diff --git a/bl31/bl31.mk b/bl31/bl31.mk index 2088533ac..1fdf545da 100644 --- a/bl31/bl31.mk +++ b/bl31/bl31.mk @@ -95,6 +95,10 @@ BL31_SOURCES += lib/cpus/aarch64/wa_cve_2017_5715_bpiall.S \ lib/cpus/aarch64/wa_cve_2017_5715_mmu.S endif +ifeq ($(SMC_PCI_SUPPORT),1) +BL31_SOURCES += services/std_svc/pci_svc.c +endif + BL31_LINKERFILE := bl31/bl31.ld.S # Flag used to indicate if Crash reporting via console should be included diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst index 2c3cd6bb5..99fc21db3 100644 --- a/docs/getting_started/build-options.rst +++ b/docs/getting_started/build-options.rst @@ -579,6 +579,11 @@ Common build options ``BL31_NOBITS_LIMIT``. When the option is ``0`` (the default), NOBITS sections are placed in RAM immediately following the loaded firmware image. +- ``SMC_PCI_SUPPORT``: This option allows platforms to handle PCI configuration + access requests via a standard SMCCC defined in `DEN0115`_. When combined with + UEFI+ACPI this can provide a certain amount of OS forward compatibility + with newer platforms that aren't ECAM compliant. + - ``SPD``: Choose a Secure Payload Dispatcher component to be built into TF-A. This build option is only valid if ``ARCH=aarch64``. The value should be the path to the directory containing the SPD source, relative to @@ -849,3 +854,6 @@ commands can be used: -------------- *Copyright (c) 2019-2021, Arm Limited. All rights reserved.* + +.. _DEN0115: https://developer.arm.com/docs/den0115/latest + diff --git a/include/services/pci_svc.h b/include/services/pci_svc.h new file mode 100644 index 000000000..664a742c6 --- /dev/null +++ b/include/services/pci_svc.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PCI_SVC_H +#define PCI_SVC_H + +#include + +/* SMCCC PCI platform functions */ +#define SMC_PCI_VERSION U(0x84000130) +#define SMC_PCI_FEATURES U(0x84000131) +#define SMC_PCI_READ U(0x84000132) +#define SMC_PCI_WRITE U(0x84000133) +#define SMC_PCI_SEG_INFO U(0x84000134) + +#define is_pci_fid(_fid) (((_fid) >= SMC_PCI_VERSION) && \ + ((_fid) <= SMC_PCI_SEG_INFO)) + +uint64_t pci_smc_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, + u_register_t x3, u_register_t x4, void *cookie, + void *handle, u_register_t flags); + +#define PCI_ADDR_FUN(dev) ((dev) & U(0x7)) +#define PCI_ADDR_DEV(dev) (((dev) >> U(3)) & U(0x001F)) +#define PCI_ADDR_BUS(dev) (((dev) >> U(8)) & U(0x00FF)) +#define PCI_ADDR_SEG(dev) (((dev) >> U(16)) & U(0xFFFF)) +#define PCI_OFFSET_MASK U(0xFFF) +typedef union { + struct { + uint16_t minor; + uint16_t major; + } __packed; + uint32_t val; +} pcie_version; + +/* + * platforms are responsible for providing implementations of these + * three functions in a manner which conforms to the Arm PCI Configuration + * Space Access Firmware Interface (DEN0115) and the PCIe specification's + * sections on PCI configuration access. See the rpi4_pci_svc.c example. + */ +uint32_t pci_read_config(uint32_t addr, uint32_t off, uint32_t sz, uint32_t *val); +uint32_t pci_write_config(uint32_t addr, uint32_t off, uint32_t sz, uint32_t val); +uint32_t pci_get_bus_for_seg(uint32_t seg, uint32_t *bus_range, uint32_t *nseg); + +/* Return codes for Arm PCI Config Space Access Firmware SMC calls */ +#define SMC_PCI_CALL_SUCCESS U(0) +#define SMC_PCI_CALL_NOT_SUPPORTED -1 +#define SMC_PCI_CALL_INVAL_PARAM -2 +#define SMC_PCI_CALL_NOT_IMPL -3 + +#define SMC_PCI_SZ_8BIT U(1) +#define SMC_PCI_SZ_16BIT U(2) +#define SMC_PCI_SZ_32BIT U(4) + +#endif /* PCI_SVC_H */ diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk index 8d0cd0477..043312372 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -212,6 +212,9 @@ SDEI_SUPPORT := 0 # True Random Number firmware Interface TRNG_SUPPORT := 0 +# SMCCC PCI support +SMC_PCI_SUPPORT := 0 + # Whether code and read-only data should be put on separate memory pages. The # platform Makefile is free to override this value. SEPARATE_CODE_AND_RODATA := 0 diff --git a/services/std_svc/pci_svc.c b/services/std_svc/pci_svc.c new file mode 100644 index 000000000..a02b8a745 --- /dev/null +++ b/services/std_svc/pci_svc.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include + +static uint64_t validate_rw_addr_sz(uint32_t addr, uint64_t off, uint64_t sz) +{ + uint32_t nseg; + uint32_t ret; + uint32_t start_end_bus; + + ret = pci_get_bus_for_seg(PCI_ADDR_SEG(addr), &start_end_bus, &nseg); + + if (ret != SMC_PCI_CALL_SUCCESS) { + return SMC_PCI_CALL_INVAL_PARAM; + } + switch (sz) { + case SMC_PCI_SZ_8BIT: + case SMC_PCI_SZ_16BIT: + case SMC_PCI_SZ_32BIT: + break; + default: + return SMC_PCI_CALL_INVAL_PARAM; + } + if ((off + sz) > (PCI_OFFSET_MASK + 1U)) { + return SMC_PCI_CALL_INVAL_PARAM; + } + return SMC_PCI_CALL_SUCCESS; +} + +uint64_t pci_smc_handler(uint32_t smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + switch (smc_fid) { + case SMC_PCI_VERSION: { + pcie_version ver; + + ver.major = 1U; + ver.minor = 0U; + SMC_RET4(handle, ver.val, 0U, 0U, 0U); + } + case SMC_PCI_FEATURES: + switch (x1) { + case SMC_PCI_VERSION: + case SMC_PCI_FEATURES: + case SMC_PCI_READ: + case SMC_PCI_WRITE: + case SMC_PCI_SEG_INFO: + SMC_RET1(handle, SMC_PCI_CALL_SUCCESS); + default: + SMC_RET1(handle, SMC_PCI_CALL_NOT_SUPPORTED); + } + break; + case SMC_PCI_READ: { + uint32_t ret; + + if (validate_rw_addr_sz(x1, x2, x3) != SMC_PCI_CALL_SUCCESS) { + SMC_RET2(handle, SMC_PCI_CALL_INVAL_PARAM, 0U); + } + if (x4 != 0U) { + SMC_RET2(handle, SMC_PCI_CALL_INVAL_PARAM, 0U); + } + if (pci_read_config(x1, x2, x3, &ret) != 0U) { + SMC_RET2(handle, SMC_PCI_CALL_INVAL_PARAM, 0U); + } else { + SMC_RET2(handle, SMC_PCI_CALL_SUCCESS, ret); + } + break; + } + case SMC_PCI_WRITE: { + uint32_t ret; + + if (validate_rw_addr_sz(x1, x2, x3) != SMC_PCI_CALL_SUCCESS) { + SMC_RET1(handle, SMC_PCI_CALL_INVAL_PARAM); + } + ret = pci_write_config(x1, x2, x3, x4); + SMC_RET1(handle, ret); + break; + } + case SMC_PCI_SEG_INFO: { + uint32_t nseg; + uint32_t ret; + uint32_t start_end_bus; + + if ((x2 != 0U) || (x3 != 0U) || (x4 != 0U)) { + SMC_RET3(handle, SMC_PCI_CALL_INVAL_PARAM, 0U, 0U); + } + ret = pci_get_bus_for_seg(x1, &start_end_bus, &nseg); + SMC_RET3(handle, ret, start_end_bus, nseg); + break; + } + default: + /* should be unreachable */ + WARN("Unimplemented PCI Service Call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_PCI_CALL_NOT_SUPPORTED); + } +} diff --git a/services/std_svc/std_svc_setup.c b/services/std_svc/std_svc_setup.c index 5e2d640f5..1917d0a14 100644 --- a/services/std_svc/std_svc_setup.c +++ b/services/std_svc/std_svc_setup.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,15 @@ static uintptr_t std_svc_smc_handler(uint32_t smc_fid, void *handle, u_register_t flags) { + if (((smc_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_32) { + /* 32-bit SMC function, clear top parameter bits */ + + x1 &= UINT32_MAX; + x2 &= UINT32_MAX; + x3 &= UINT32_MAX; + x4 &= UINT32_MAX; + } + /* * Dispatch PSCI calls to PSCI SMC handler and return its return * value @@ -149,6 +159,13 @@ static uintptr_t std_svc_smc_handler(uint32_t smc_fid, } #endif +#if SMC_PCI_SUPPORT + if (is_pci_fid(smc_fid)) { + return pci_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, + flags); + } +#endif + switch (smc_fid) { case ARM_STD_SVC_CALL_COUNT: /*