diff options
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | common/tf_printf.c | 43 | ||||
-rw-r--r-- | docs/diagrams/secure_sw_stack_sp.png | bin | 0 -> 34909 bytes | |||
-rw-r--r-- | docs/diagrams/secure_sw_stack_tos.png | bin | 0 -> 34202 bytes | |||
-rw-r--r-- | docs/secure-partition-manager-design.rst | 825 | ||||
-rw-r--r-- | docs/spm-user-guide.rst | 59 | ||||
-rw-r--r-- | drivers/io/io_block.c | 400 | ||||
-rw-r--r-- | plat/hisilicon/poplar/aarch64/platform_common.c | 5 | ||||
-rw-r--r-- | plat/hisilicon/poplar/bl2_plat_setup.c | 49 | ||||
-rw-r--r-- | plat/hisilicon/poplar/bl31_plat_setup.c | 32 | ||||
-rw-r--r-- | plat/hisilicon/poplar/include/hi3798cv200.h | 5 | ||||
-rw-r--r-- | plat/hisilicon/poplar/include/platform_def.h | 46 | ||||
-rw-r--r-- | plat/hisilicon/poplar/include/poplar_layout.h | 6 | ||||
-rw-r--r-- | plat/hisilicon/poplar/plat_storage.c | 9 | ||||
-rw-r--r-- | plat/hisilicon/poplar/platform.mk | 11 | ||||
-rw-r--r-- | services/std_svc/spm/spm_main.c | 50 |
16 files changed, 1316 insertions, 231 deletions
@@ -160,6 +160,13 @@ TF_CFLAGS += $(CPPFLAGS) $(TF_CFLAGS_$(ARCH)) \ -ffreestanding -fno-builtin -Wall -std=gnu99 \ -Os -ffunction-sections -fdata-sections +GCC_V_OUTPUT := $(shell $(CC) -v 2>&1) +PIE_FOUND := $(findstring --enable-default-pie,${GCC_V_OUTPUT}) + +ifeq ($(PIE_FOUND),1) +TF_CFLAGS += -fno-PIE +endif + TF_LDFLAGS += --fatal-warnings -O1 TF_LDFLAGS += --gc-sections TF_LDFLAGS += $(TF_LDFLAGS_$(ARCH)) diff --git a/common/tf_printf.c b/common/tf_printf.c index f73842acd..d40398338 100644 --- a/common/tf_printf.c +++ b/common/tf_printf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -31,7 +31,8 @@ void tf_string_print(const char *str) putchar(*str++); } -static void unsigned_num_print(unsigned long long int unum, unsigned int radix) +static void unsigned_num_print(unsigned long long int unum, unsigned int radix, + char padc, int padn) { /* Just need enough space to store 64 bit decimal integer */ unsigned char num_buf[20]; @@ -45,6 +46,12 @@ static void unsigned_num_print(unsigned long long int unum, unsigned int radix) num_buf[i++] = 'a' + (rem - 0xa); } while (unum /= radix); + if (padn > 0) { + while (i < padn--) { + putchar(padc); + } + } + while (--i >= 0) putchar(num_buf[i]); } @@ -63,6 +70,9 @@ static void unsigned_num_print(unsigned long long int unum, unsigned int radix) * %ll - long long int (64-bit on AArch64) * %z - size_t sized integer formats (64 bit on AArch64) * + * The following padding specifiers are supported by this print + * %0NN - Left-pad the number with 0s (NN is a decimal number) + * * The print exits on all other formats specifiers other than valid * combinations of the above specifiers. *******************************************************************/ @@ -72,9 +82,12 @@ void tf_vprintf(const char *fmt, va_list args) long long int num; unsigned long long int unum; char *str; + char padc = 0; /* Padding character */ + int padn; /* Number of characters to pad */ while (*fmt) { l_count = 0; + padn = 0; if (*fmt == '%') { fmt++; @@ -87,10 +100,11 @@ loop: if (num < 0) { putchar('-'); unum = (unsigned long long int)-num; + padn--; } else unum = (unsigned long long int)num; - unsigned_num_print(unum, 10); + unsigned_num_print(unum, 10, padc, padn); break; case 's': str = va_arg(args, char *); @@ -98,14 +112,16 @@ loop: break; case 'p': unum = (uintptr_t)va_arg(args, void *); - if (unum) + if (unum) { tf_string_print("0x"); + padn -= 2; + } - unsigned_num_print(unum, 16); + unsigned_num_print(unum, 16, padc, padn); break; case 'x': unum = get_unum_va_args(args, l_count); - unsigned_num_print(unum, 16); + unsigned_num_print(unum, 16, padc, padn); break; case 'z': if (sizeof(size_t) == 8) @@ -119,8 +135,21 @@ loop: goto loop; case 'u': unum = get_unum_va_args(args, l_count); - unsigned_num_print(unum, 10); + unsigned_num_print(unum, 10, padc, padn); break; + case '0': + padc = '0'; + padn = 0; + fmt++; + + while (1) { + char ch = *fmt; + if (ch < '0' || ch > '9') { + goto loop; + } + padn = (padn * 10) + (ch - '0'); + fmt++; + } default: /* Exit on any other format specifier */ return; diff --git a/docs/diagrams/secure_sw_stack_sp.png b/docs/diagrams/secure_sw_stack_sp.png Binary files differnew file mode 100644 index 000000000..5cb2ca7a2 --- /dev/null +++ b/docs/diagrams/secure_sw_stack_sp.png diff --git a/docs/diagrams/secure_sw_stack_tos.png b/docs/diagrams/secure_sw_stack_tos.png Binary files differnew file mode 100644 index 000000000..1f2d55506 --- /dev/null +++ b/docs/diagrams/secure_sw_stack_tos.png diff --git a/docs/secure-partition-manager-design.rst b/docs/secure-partition-manager-design.rst new file mode 100644 index 000000000..05d4e8bb6 --- /dev/null +++ b/docs/secure-partition-manager-design.rst @@ -0,0 +1,825 @@ +******************************* +Secure Partition Manager Design +******************************* + +.. section-numbering:: + :suffix: . + +.. contents:: + +Background +========== + +In some market segments that primarily deal with client-side devices like mobile +phones, tablets, STBs and embedded devices, a Trusted OS instantiates trusted +applications to provide security services like DRM, secure payment and +authentication. The Global Platform TEE Client API specification defines the API +used by Non-secure world applications to access these services. A Trusted OS +fulfils the requirements of a security service as described above. + +Management services are typically implemented at the highest level of privilege +in the system (i.e. EL3 in Arm Trusted Firmware). The service requirements are +fulfilled by the execution environment provided by Arm Trusted Firmware. + +The following diagram illustrates the corresponding software stack: + +|Image 1| + +In other market segments that primarily deal with server-side devices (e.g. data +centres and enterprise servers) the secure software stack typically does not +include a Global Platform Trusted OS. Security functions are accessed through +other interfaces (e.g. ACPI TCG TPM interface, UEFI runtime variable service). + +Placement of management and security functions with diverse requirements in a +privileged Exception Level (i.e. EL3 or S-EL1) makes security auditing of +firmware more difficult and does not allow isolation of unrelated services from +each other either. + +Introduction +============ + +A **Secure Partition** is a software execution environment instantiated in +S-EL0 that can be used to implement simple management and security services. +Since S-EL0 is an unprivileged Exception Level, a Secure Partition relies on +privileged firmware (i.e. Arm Trusted Firmware) to be granted access to system +and processor resources. Essentially, it is a software sandbox in the Secure +world that runs under the control of privileged software, provides one or more +services and accesses the following system resources: + +- Memory and device regions in the system address map. + +- PE system registers. + +- A range of synchronous exceptions (e.g. SMC function identifiers). + +Note that currently the Arm Trusted Firmware only supports handling one Secure +Partition. + +A Secure Partition enables Arm Trusted Firmware to implement only the essential +secure services in EL3 and instantiate the rest in a partition in S-EL0. +Furthermore, multiple Secure Partitions can be used to isolate unrelated +services from each other. + +The following diagram illustrates the place of a Secure Partition in a typical +ARMv8-A software stack. A single or multiple Secure Partitions provide secure +services to software components in the Non-secure world and other Secure +Partitions. + +|Image 2| + +The Arm Trusted Firmware build system is responsible for including the Secure +Partition image in the FIP. During boot, BL2 includes support to authenticate +and load the Secure Partition image. A BL31 component called **Secure Partition +Manager (SPM)** is responsible for managing the partition. This is semantically +similar to a hypervisor managing a virtual machine. + +The SPM is responsible for the following actions during boot: + +- Allocate resources requested by the Secure Partition. + +- Perform architectural and system setup required by the Secure Partition to + fulfil a service request. + +- Implement a standard interface that is used for initialising a Secure + Partition. + +The SPM is responsible for the following actions during runtime: + +- Implement a standard interface that is used by a Secure Partition to fulfil + service requests. + +- Implement a standard interface that is used by the Non-secure world for + accessing the services exported by a Secure Partition. A service can be + invoked through a SMC. + +Alternatively, a partition can be viewed as a thread of execution running under +the control of the SPM. Hence common programming concepts described below are +applicable to a partition. + +Description +=========== + +The previous section introduced some general aspects of the software +architecture of a Secure Partition. This section describes the specific choices +made in the current implementation of this software architecture. Subsequent +revisions of the implementation will include a richer set of features that +enable a more flexible architecture. + +Building Arm Trusted Firmware with Secure Partition support +----------------------------------------------------------- + +SPM is supported on the Arm FVP exclusively at the moment. The current +implementation supports inclusion of only a single Secure Partition in which a +service always runs to completion (e.g. the requested services cannot be +preempted to give control back to the Normal world). + +It is not currently possible for BL31 to integrate SPM support and a Secure +Payload Dispatcher (SPD) at the same time; they are mutually exclusive. In the +SPM bootflow, a Secure Partition image executing at S-EL0 replaces the Secure +Payload image executing at S-EL1 (e.g. a Trusted OS). Both are referred to as +BL32. + +A working prototype of a SP has been implemented by re-purposing the EDK2 code +and tools, leveraging the concept of the *Standalone Management Mode (MM)* in +the UEFI specification (see the PI v1.6 Volume 4: Management Mode Core +Interface). This will be referred to as the *Standalone MM Secure Partition* in +the rest of this document. + +To enable SPM support in the TF, the source code must be compiled with the build +flag ``ENABLE_SPM=1``. On Arm platforms the build option ``ARM_BL31_IN_DRAM`` +can be used to select the location of BL31, both SRAM and DRAM are supported. +Also, the location of the binary that contains the BL32 image +(``BL32=path/to/image.bin``) must be specified. + +First, build the Standalone MM Secure Partition. To build it, refer to the +`instructions in the EDK2 repository`_. + +Then build TF with SPM support and include the Standalone MM Secure Partition +image in the FIP: + +:: + + BL32=path/to/standalone/mm/sp BL33=path/to/bl33.bin \ + make PLAT=fvp ENABLE_SPM=1 fip all + +Describing Secure Partition resources +------------------------------------- + +Arm Trusted Firmware exports a porting interface that enables a platform to +specify the system resources required by the Secure Partition. Some instructions +are given below. However, this interface is under development and it may change +as new features are implemented. + +- A Secure Partition is considered a BL32 image, so the same defines that apply + to BL32 images apply to a Secure Partition: ``BL32_BASE`` and ``BL32_LIMIT``. + +- The following defines are needed to allocate space for the translation tables + used by the Secure Partition: ``PLAT_SP_IMAGE_MMAP_REGIONS`` and + ``PLAT_SP_IMAGE_MAX_XLAT_TABLES``. + +- The functions ``plat_get_secure_partition_mmap()`` and + ``plat_get_secure_partition_boot_info()`` have to be implemented. The file + ``plat/arm/board/fvp/fvp_common.c`` can be used as an example. It uses the + defines in ``include/plat/arm/common/arm_spm_def.h``. + + - ``plat_get_secure_partition_mmap()`` returns an array of mmap regions that + describe the memory regions that the SPM needs to allocate for a Secure + Partition. + + - ``plat_get_secure_partition_boot_info()`` returns a + ``secure_partition_boot_info_t`` struct that is populated by the platform + with information about the memory map of the Secure Partition. + +For an example of all the changes in context, you may refer to commit +``e29efeb1b4``, in which the port for FVP was introduced. + +Accessing Secure Partition services +----------------------------------- + +The `SMC Calling Convention`_ (*ARM DEN 0028B*) describes SMCs as a conduit for +accessing services implemented in the Secure world. The ``MM_COMMUNICATE`` +interface defined in the `Management Mode Interface Specification`_ (*ARM DEN +0060A*) is used to invoke a Secure Partition service as a Fast Call. + +The mechanism used to identify a service within the partition depends on the +service implementation. It is assumed that the caller of the service will be +able to discover this mechanism through standard platform discovery mechanisms +like ACPI and Device Trees. For example, *Volume 4: Platform Initialisation +Specification v1.6. Management Mode Core Interface* specifies that a GUID is +used to identify a management mode service. A client populates the GUID in the +``EFI_MM_COMMUNICATE_HEADER``. The header is populated in the communication +buffer shared with the Secure Partition. + +A Fast Call appears to be atomic from the perspective of the caller and returns +when the requested operation has completed. A service invoked through the +``MM_COMMUNICATE`` SMC will run to completion in the partition on a given CPU. +The SPM is responsible for guaranteeing this behaviour. This means that there +can only be a single outstanding Fast Call in a partition on a given CPU. + +Exchanging data with the Secure Partition +----------------------------------------- + +The exchange of data between the Non-secure world and the partition takes place +through a shared memory region. The location of data in the shared memory area +is passed as a parameter to the ``MM_COMMUNICATE`` SMC. The shared memory area +is statically allocated by the SPM and is expected to be either implicitly known +to the Non-secure world or discovered through a platform discovery mechanism +e.g. ACPI table or device tree. It is possible for the Non-secure world to +exchange data with a partition only if it has been populated in this shared +memory area. The shared memory area is implemented as per the guidelines +specified in Section 3.2.3 of the `Management Mode Interface Specification`_ +(*ARM DEN 0060A*). + +The format of data structures used to encapsulate data in the shared memory is +agreed between the Non-secure world and the Secure Partition. For example, in +the `Management Mode Interface specification`_ (*ARM DEN 0060A*), Section 4 +describes that the communication buffer shared between the Non-secure world and +the Management Mode (MM) in the Secure world must be of the type +``EFI_MM_COMMUNICATE_HEADER``. This data structure is defined in *Volume 4: +Platform Initialisation Specification v1.6. Management Mode Core Interface*. +Any caller of a MM service will have to use the ``EFI_MM_COMMUNICATE_HEADER`` +data structure. + +Runtime model of the Secure Partition +===================================== + +This section describes how the Secure Partition interfaces with the SPM. + +Interface with SPM +------------------ + +In order to instantiate one or more secure services in the Secure Partition in +S-EL0, the SPM should define the following types of interfaces: + +- Interfaces that enable access to privileged operations from S-EL0. These + operations typically require access to system resources that are either shared + amongst multiple software components in the Secure world or cannot be directly + accessed from an unprivileged Exception Level. + +- Interfaces that establish the control path between the SPM and the Secure + Partition. + +This section describes the APIs currently exported by the SPM that enable a +Secure Partition to initialise itself and export its services in S-EL0. These +interfaces are not accessible from the Non-secure world. + +Conduit +^^^^^^^ + +The `SMC Calling Convention`_ (*ARM DEN 0028B*) specification describes the SMC +and HVC conduits for accessing firmware services and their availability +depending on the implemented Exception levels. In S-EL0, the Supervisor Call +exception (SVC) is the only architectural mechanism available for unprivileged +software to make a request for an operation implemented in privileged software. +Hence, the SVC conduit must be used by the Secure Partition to access interfaces +implemented by the SPM. + +A SVC causes an exception to be taken to S-EL1. Arm Trusted Firmware assumes +ownership of S-EL1 and installs a simple exception vector table in S-EL1 that +relays a SVC request from a Secure Partition as a SMC request to the SPM in EL3. +Upon servicing the SMC request, Arm Trusted Firmware returns control directly to +S-EL0 through an ERET instruction. + +Calling conventions +^^^^^^^^^^^^^^^^^^^ + +The `SMC Calling Convention`_ (*ARM DEN 0028B*) specification describes the +32-bit and 64-bit calling conventions for the SMC and HVC conduits. The SVC +conduit introduces the concept of SVC32 and SVC64 calling conventions. The SVC32 +and SVC64 calling conventions are equivalent to the 32-bit (SMC32) and the +64-bit (SMC64) calling conventions respectively. + +Communication initiated by SPM +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A service request is initiated from the SPM through an exception return +instruction (ERET) to S-EL0. Later, the Secure Partition issues an SVC +instruction to signal completion of the request. Some example use cases are +given below: + +- A request to initialise the Secure Partition during system boot. + +- A request to handle a runtime service request. + +Communication initiated by Secure Partition +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A request is initiated from the Secure Partition by executing a SVC instruction. +An ERET instruction is used by Arm Trusted Firmware to return to S-EL0 with the +result of the request. + +For instance, a request to perform privileged operations on behalf of a +partition (e.g. management of memory attributes in the translation tables for +the Secure EL1&0 translation regime). + +Interfaces +^^^^^^^^^^ + +The current implementation reserves function IDs for Fast Calls in the Standard +Secure Service calls range (see `SMC Calling Convention`_ (*ARM DEN 0028B*) +specification) for each API exported by the SPM. This section defines the +function prototypes for each function ID. The function IDs specify whether one +or both of the SVC32 and SVC64 calling conventions can be used to invoke the +corresponding interface. + +Secure Partition Event Management +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Secure Partition provides an Event Management interface that is used by the +SPM to delegate service requests to the Secure Partition. The interface also +allows the Secure Partition to: + +- Register with the SPM a service that it provides. +- Indicate completion of a service request delagated by the SPM + +Miscellaneous interfaces +------------------------ + +``SPM_VERSION_AARCH32`` +^^^^^^^^^^^^^^^^^^^^^^^ + +- Description + + Returns the version of the interface exported by SPM. + +- Parameters + + - **uint32** - Function ID + + - SVC32 Version: **0x84000060** + +- Return parameters + + - **int32** - Status + + On success, the format of the value is as follows: + + - Bit [31]: Must be 0 + - Bits [30:16]: Major Version. Must be 0 for this revision of the SPM + interface. + - Bits [15:0]: Minor Version. Must be 1 for this revision of the SPM + interface. + + On error, the format of the value is as follows: + + - ``NOT_SUPPORTED``: SPM interface is not supported or not available for the + client. + +- Usage + + This function returns the version of the Secure Partition Manager + implementation. The major version is 0 and the minor version is 1. The version + number is a 31-bit unsigned integer, with the upper 15 bits denoting the major + revision, and the lower 16 bits denoting the minor revision. The following + rules apply to the version numbering: + + - Different major revision values indicate possibly incompatible functions. + + - For two revisions, A and B, for which the major revision values are + identical, if the minor revision value of revision B is greater than the + minor revision value of revision A, then every function in revision A must + work in a compatible way with revision B. However, it is possible for + revision B to have a higher function count than revision A. + +- Implementation responsibilities + + If this function returns a valid version number, all the functions that are + described subsequently must be implemented, unless it is explicitly stated + that a function is optional. + +See `Error Codes`_ for integer values that are associated with each return +code. + +Secure Partition Initialisation +------------------------------- + +The SPM is responsible for initialising the architectural execution context to +enable initialisation of a service in S-EL0. The responsibilities of the SPM are +listed below. At the end of initialisation, the partition issues a +``SP_EVENT_COMPLETE_AARCH64`` call (described later) to signal readiness for +handling requests for services implemented by the Secure Partition. The +initialisation event is executed as a Fast Call. + +Entry point invocation +^^^^^^^^^^^^^^^^^^^^^^ + +The entry point for service requests that should be handled as Fast Calls is +used as the target of the ERET instruction to start initialisation of the Secure +Partition. + +Architectural Setup +^^^^^^^^^^^^^^^^^^^ + +At cold boot, system registers accessible from S-EL0 will be in their reset +state unless otherwise specified. The SPM will perform the following +architectural setup to enable execution in S-EL0 + +MMU setup +^^^^^^^^^ + +The platform port of a Secure Partition specifies to the SPM a list of regions +that it needs access to and their attributes. The SPM validates this resource +description and initialises the Secure EL1&0 translation regime as follows. + +1. Device regions are mapped with nGnRE attributes and Execute Never + instruction access permissions. + +2. Code memory regions are mapped with RO data and Executable instruction access + permissions. + +3. Read Only data memory regions are mapped with RO data and Execute Never + instruction access permissions. + +4. Read Write data memory regions are mapped with RW data and Execute Never + instruction access permissions. + +5. If the resource description does not explicitly describe the type of memory + regions then all memory regions will be marked with Code memory region + attributes. + +6. The ``UXN`` and ``PXN`` bits are set for regions that are not executable by + S-EL0 or S-EL1. + +System Register Setup +^^^^^^^^^^^^^^^^^^^^^ + +System registers that influence software execution in S-EL0 are setup by the SPM +as follows: + +1. ``SCTLR_EL1`` + + - ``UCI=1`` + - ``EOE=0`` + - ``WXN=1`` + - ``nTWE=1`` + - ``nTWI=1`` + - ``UCT=1`` + - ``DZE=1`` + - ``I=1`` + - ``UMA=0`` + - ``SA0=1`` + - ``C=1`` + - ``A=1`` + - ``M=1`` + +2. ``CPACR_EL1`` + + - ``FPEN=b'11`` + +3. ``PSTATE`` + + - ``D,A,I,F=1`` + - ``CurrentEL=0`` (EL0) + - ``SpSel=0`` (Thread mode) + - ``NRW=0`` (AArch64) + +General Purpose Register Setup +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +SPM will invoke the entry point of a service by executing an ERET instruction. +This transition into S-EL0 is special since it is not in response to a previous +request through a SVC instruction. This is the first entry into S-EL0. The +general purpose register usage at the time of entry will be as specified in the +"Return State" column of Table 3-1 in Section 3.1 "Register use in AArch64 SMC +calls" of the `SMC Calling Convention`_ (*ARM DEN 0028B*) specification. In +addition, certain other restrictions will be applied as described below. + +1. ``SP_EL0`` + + A non-zero value will indicate that the SPM has initialised the stack pointer + for the current CPU. + + The value will be 0 otherwise. + +2. ``X4-X30`` + + The values of these registers will be 0. + +3. ``X0-X3`` + + Parameters passed by the SPM. + + - ``X0``: Virtual address of a buffer shared between EL3 and S-EL0. The + buffer will be mapped in the Secure EL1&0 translation regime with read-only + memory attributes described earlier. + + - ``X1``: Size of the buffer in bytes. + + - ``X2``: Cookie value (*IMPLEMENTATION DEFINED*). + + - ``X3``: Cookie value (*IMPLEMENTATION DEFINED*). + +Runtime Event Delegation +------------------------ + +The SPM receives requests for Secure Partition services through a synchronous +invocation (i.e. a SMC from the Non-secure world). These requests are delegated +to the partition by programming a return from the last +``SP_EVENT_COMPLETE_AARCH64`` call received from the partition. The last call +was made to signal either completion of Secure Partition initialisation or +completion of a partition service request. + +``SP_EVENT_COMPLETE_AARCH64`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Description + + Signal completion of the last SP service request. + +- Parameters + + - **uint32** - Function ID + + - SVC64 Version: **0xC4000061** + + - **int32** - Event Status Code + + Zero or a positive value indicates that the event was handled successfully. + The values depend upon the original event that was delegated to the Secure + partition. They are described as follows. + + - ``SUCCESS`` : Used to indicate that the Secure Partition was initialised + or a runtime request was handled successfully. + + - Any other value greater than 0 is used to pass a specific Event Status + code in response to a runtime event. + + A negative value indicates an error. The values of Event Status code depend + on the original event. + +- Return parameters + + - **int32** - Event ID/Return Code + + Zero or a positive value specifies the unique ID of the event being + delegated to the partition by the SPM. + + In the current implementation, this parameter contains the function ID of + the ``MM_COMMUNICATE`` SMC. This value indicates to the partition that an + event has been delegated to it in response to an ``MM_COMMUNICATE`` request + from the Non-secure world. + + A negative value indicates an error. The format of the value is as follows: + + - ``NOT_SUPPORTED``: Function was called from the Non-secure world. + + See `Error Codes`_ for integer values that are associated with each return + code. + + - **uint32** - Event Context Address + + Address of a buffer shared between the SPM and Secure Partition to pass + event specific information. The format of the data populated in the buffer + is implementation defined. + + The buffer is mapped in the Secure EL1&0 translation regime with read-only + memory attributes described earlier. + + For the SVC64 version, this parameter is a 64-bit Virtual Address (VA). + + For the SVC32 version, this parameter is a 32-bit Virtual Address (VA). + + - **uint32** - Event context size + + Size of the memory starting at Event Address. + + - **uint32/uint64** - Event Cookie + + This is an optional parameter. If unused its value is SBZ. + +- Usage + + This function signals to the SPM that the handling of the last event delegated + to a partition has completed. The partition is ready to handle its next event. + A return from this function is in response to the next event that will be + delegated to the partition. The return parameters describe the next event. + +- Caller responsibilities + + A Secure Partition must only call ``SP_EVENT_COMPLETE_AARCH64`` to signal + completion of a request that was delegated to it by the SPM. + +- Callee responsibilities + + When the SPM receives this call from a Secure Partition, the corresponding + syndrome information can be used to return control through an ERET + instruction, to the instruction immediately after the call in the Secure + Partition context. This syndrome information comprises of general purpose and + system register values when the call was made. + + The SPM must save this syndrome information and use it to delegate the next + event to the Secure Partition. The return parameters of this interface must + specify the properties of the event and be populated in ``X0-X3/W0-W3`` + registers. + +Secure Partition Memory Management +---------------------------------- + +A Secure Partition executes at S-EL0, which is an unprivileged Exception Level. +The SPM is responsible for enabling access to regions of memory in the system +address map from a Secure Partition. This is done by mapping these regions in +the Secure EL1&0 Translation regime with appropriate memory attributes. +Attributes refer to memory type, permission, cacheability and shareability +attributes used in the Translation tables. The definitions of these attributes +and their usage can be found in the `ARMv8 ARM`_ (*ARM DDI 0487*). + +All memory required by the Secure Partition is allocated upfront in the SPM, +even before handing over to the Secure Partition for the first time. The initial +access permissions of the memory regions are statically provided by the platform +port and should allow the Secure Partition to run its initialisation code. + +However, they might not suit the final needs of the Secure Partition because its +final memory layout might not be known until the Secure Partition initialises +itself. As the Secure Partition initialises its runtime environment it might, +for example, load dynamically some modules. For instance, a Secure Partition +could implement a loader for a standard executable file format (e.g. an PE-COFF +loader for loading executable files at runtime). These executable files will be +a part of the Secure Partition image. The location of various sections in an +executable file and their permission attributes (e.g. read-write data, read-only +data and code) will be known only when the file is loaded into memory. + +In this case, the Secure Partition needs a way to change the access permissions +of its memory regions. The SPM provides this feature through the +``SP_MEMORY_ATTRIBUTES_SET_AARCH64`` SVC interface. This interface is available +to the Secure Partition during a specific time window: from the first entry into +the Secure Partition up to the first ``SP_EVENT_COMPLETE`` call that signals the +Secure Partition has finished its initialisation. Once the initialisation is +complete, the SPM does not allow changes to the memory attributes. + +This section describes the standard SVC interface that is implemented by the SPM +to determine and change permission attributes of memory regions that belong to a +Secure Partition. + +``SP_MEMORY_ATTRIBUTES_GET_AARCH64`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Description + + Request the permission attributes of a memory region from S-EL0. + +- Parameters + + - **uint32** Function ID + + - SVC64 Version: **0xC4000064** + + - **uint64** Base Address + + This parameter is a 64-bit Virtual Address (VA). + + There are no alignment restrictions on the Base Address. The permission + attributes of the translation granule it lies in are returned. + +- Return parameters + + - **int32** - Memory Attributes/Return Code + + On success the format of the Return Code is as follows: + + - Bits[1:0] : Data access permission + + - b'00 : No access + - b'01 : Read-Write access + - b'10 : Reserved + - b'11 : Read-only access + + - Bit[2]: Instruction access permission + + - b'0 : Executable + - b'1 : Non-executable + + - Bit[30:3] : Reserved. SBZ. + + - Bit[31] : Must be 0 + + On failure the following error codes are returned: + + - ``INVALID_PARAMETERS``: The Secure Partition is not allowed to access the + memory region the Base Address lies in. + + - ``NOT_SUPPORTED`` : The SPM does not support retrieval of attributes of + any memory page that is accessible by the Secure Partition, or the + function was called from the Non-secure world. Also returned if it is + used after ``SP_EVENT_COMPLETE_AARCH64``. + + See `Error Codes`_ for integer values that are associated with each return + code. + +- Usage + + This function is used to request the permission attributes for S-EL0 on a + memory region accessible from a Secure Partition. The size of the memory + region is equal to the Translation Granule size used in the Secure EL1&0 + translation regime. Requests to retrieve other memory region attributes are + not currently supported. + +- Caller responsibilities + + The caller must obtain the Translation Granule Size of the Secure EL1&0 + translation regime from the SPM through an implementation defined method. + +- Callee responsibilities + + The SPM must not return the memory access controls for a page of memory that + is not accessible from a Secure Partition. + +``SP_MEMORY_ATTRIBUTES_SET_AARCH64`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Description + + Set the permission attributes of a memory region from S-EL0. + +- Parameters + + - **uint32** - Function ID + + - SVC64 Version: **0xC4000065** + + - **uint64** - Base Address + + This parameter is a 64-bit Virtual Address (VA). + + The alignment of the Base Address must be greater than or equal to the size + of the Translation Granule Size used in the Secure EL1&0 translation + regime. + + - **uint32** - Page count + + Number of pages starting from the Base Address whose memory attributes + should be changed. The page size is equal to the Translation Granule Size. + + - **uint32** - Memory Access Controls + + - Bits[1:0] : Data access permission + + - b'00 : No access + - b'01 : Read-Write access + - b'10 : Reserved + - b'11 : Read-only access + + - Bit[2] : Instruction access permission + + - b'0 : Executable + - b'1 : Non-executable + + - Bits[31:3] : Reserved. SBZ. + + A combination of attributes that mark the region with RW and Executable + permissions is prohibited. A request to mark a device memory region with + Executable permissions is prohibited. + +- Return parameters + + - **int32** - Return Code + + - ``SUCCESS``: The Memory Access Controls were changed successfully. + + - ``DENIED``: The SPM is servicing a request to change the attributes of a + memory region that overlaps with the region specified in this request. + + - ``INVALID_PARAMETER``: An invalid combination of Memory Access Controls + has been specified. The Base Address is not correctly aligned. The Secure + Partition is not allowed to access part or all of the memory region + specified in the call. + + - ``NO_MEMORY``: The SPM does not have memory resources to change the + attributes of the memory region in the translation tables. + + - ``NOT_SUPPORTED``: The SPM does not permit change of attributes of any + memory region that is accessible by the Secure Partition. Function was + called from the Non-secure world. Also returned if it is used after + ``SP_EVENT_COMPLETE_AARCH64``. + + See `Error Codes`_ for integer values that are associated with each return + code. + +- Usage + + This function is used to change the permission attributes for S-EL0 on a + memory region accessible from a Secure Partition. The size of the memory + region is equal to the Translation Granule size used in the Secure EL1&0 + translation regime. Requests to change other memory region attributes are not + currently supported. + + This function is only available at boot time. This interface is revoked after + the Secure Partition sends the first ``SP_EVENT_COMPLETE_AARCH64`` to signal + that it is initialised and ready to receive run-time requests. + +- Caller responsibilities + + The caller must obtain the Translation Granule Size of the Secure EL1&0 + translation regime from the SPM through an implementation defined method. + +- Callee responsibilities + + The SPM must preserve the original memory access controls of the region of + memory in case of an unsuccessful call. The SPM must preserve the consistency + of the S-EL1 translation regime if this function is called on different PEs + concurrently and the memory regions specified overlap. + +Error Codes +----------- + +.. csv-table:: + :header: "Name", "Value" + + ``SUCCESS``,0 + ``NOT_SUPPORTED``,-1 + ``INVALID_PARAMETER``,-2 + ``DENIED``,-3 + ``NO_MEMORY``,-5 + ``NOT_PRESENT``,-7 + +-------------- + +*Copyright (c) 2017, Arm Limited and Contributors. All rights reserved.* + +.. _ARMv8 ARM: https://developer.arm.com/docs/ddi0487/latest/arm-architecture-reference-manual-armv8-for-armv8-a-architecture-profile +.. _instructions in the EDK2 repository: https://github.com/tianocore/edk2-staging/blob/AArch64StandaloneMm/HowtoBuild.MD +.. _Management Mode Interface Specification: http://infocenter.arm.com/help/topic/com.arm.doc.den0060a/DEN0060A_ARM_MM_Interface_Specification.pdf +.. _SDEI Specification: http://infocenter.arm.com/help/topic/com.arm.doc.den0054a/ARM_DEN0054A_Software_Delegated_Exception_Interface.pdf +.. _SMC Calling Convention: http://infocenter.arm.com/help/topic/com.arm.doc.den0028b/ARM_DEN0028B_SMC_Calling_Convention.pdf + +.. |Image 1| image:: diagrams/secure_sw_stack_tos.png +.. |Image 2| image:: diagrams/secure_sw_stack_sp.png diff --git a/docs/spm-user-guide.rst b/docs/spm-user-guide.rst deleted file mode 100644 index a3b64d931..000000000 --- a/docs/spm-user-guide.rst +++ /dev/null @@ -1,59 +0,0 @@ -ARM Trusted Firmware - SPM User Guide -===================================== - -.. section-numbering:: - :suffix: . - -.. contents:: - - -This document briefly presents the Secure Partition Management (SPM) support in -the Arm Trusted Firmware (TF), specifically focusing on how to build Arm TF with -SPM support. - -Overview of the SPM software stack ----------------------------------- - -SPM is supported on the Arm FVP exclusively at the moment. - -It is not currently possible for BL31 to integrate SPM support and a Secure -Payload Dispatcher (SPD) at the same time; they are mutually exclusive. In the -SPM bootflow, a Secure Partition (SP) image executing at Secure-EL0 replaces the -Secure Payload image executing at Secure-EL1 (e.g. a Trusted OS). Both are -referred to as BL32. - -A working prototype of a SP has been implemented by repurposing the EDK2 code -and tools, leveraging the concept of the *Standalone Management Mode (MM)* in -the UEFI specification (see the PI v1.6 Volume 4: Management Mode Core -Interface). This will be referred to as the *Standalone MM Secure Partition* in -the rest of this document. - - -Building TF with SPM support ----------------------------- - -To enable SPM support in the TF, the source code must be compiled with the build -flag ``ENABLE_SPM=1``. On Arm platforms the build option ``ARM_BL31_IN_DRAM`` -can be used to select the location of BL31, both SRAM and DRAM are supported. - - -Using the Standalone MM SP -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -First, build the Standalone MM Secure Partition. To build it, refer to the -`instructions in the EDK2 repository`_. - -Then build TF with SPM support and include the Standalone MM Secure Partition -image in the FIP: - -:: - - BL32=path/to/standalone/mm/sp BL33=path/to/bl33.bin \ - make PLAT=fvp ENABLE_SPM=1 fip all - - --------------- - -*Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* - -.. _instructions in the EDK2 repository: https://github.com/tianocore/edk2-staging/blob/AArch64StandaloneMm/HowtoBuild.MD diff --git a/drivers/io/io_block.c b/drivers/io/io_block.c index 128246fed..8226554d9 100644 --- a/drivers/io/io_block.c +++ b/drivers/io/io_block.c @@ -167,15 +167,98 @@ static int block_seek(io_entity_t *entity, int mode, ssize_t offset) return 0; } +/* + * This function allows the caller to read any number of bytes + * from any position. It hides from the caller that the low level + * driver only can read aligned blocks of data. For this reason + * we need to handle the use case where the first byte to be read is not + * aligned to start of the block, the last byte to be read is also not + * aligned to the end of a block, and there are zero or more blocks-worth + * of data in between. + * + * In such a case we need to read more bytes than requested (i.e. full + * blocks) and strip-out the leading bytes (aka skip) and the trailing + * bytes (aka padding). See diagram below + * + * cur->file_pos ------------ + * | + * cur->base | + * | | + * v v<---- length ----> + * -------------------------------------------------------------- + * | | block#1 | | block#n | + * | block#0 | + | ... | + | + * | | <- skip -> + | | + <- padding ->| + * ------------------------+----------------------+-------------- + * ^ ^ + * | | + * v iteration#1 iteration#n v + * -------------------------------------------------- + * | | | | + * |<---- request ---->| ... |<----- request ---->| + * | | | | + * -------------------------------------------------- + * / / | | + * / / | | + * / / | | + * / / | | + * / / | | + * / / | | + * / / | | + * / / | | + * / / | | + * / / | | + * <---- request ------> <------ request -----> + * --------------------- ----------------------- + * | | | | | | + * |<-skip->|<-nbytes->| -------->|<-nbytes->|<-padding->| + * | | | | | | | + * --------------------- | ----------------------- + * ^ \ \ | | | + * | \ \ | | | + * | \ \ | | | + * buf->offset \ \ buf->offset | | + * \ \ | | + * \ \ | | + * \ \ | | + * \ \ | | + * \ \ | | + * \ \ | | + * \ \ | | + * -------------------------------- + * | | | | + * buffer-------------->| | ... | | + * | | | | + * -------------------------------- + * <-count#1->| | + * <---------- count#n --------> + * <---------- length ----------> + * + * Additionally, the IO driver has an underlying buffer that is at least + * one block-size and may be big enough to allow. + */ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read) { block_dev_state_t *cur; io_block_spec_t *buf; io_block_ops_t *ops; - size_t aligned_length, skip, count, left, padding, block_size; int lba; - int buffer_not_aligned; + size_t block_size, left; + size_t nbytes; /* number of bytes read in one iteration */ + size_t request; /* number of requested bytes in one iteration */ + size_t count; /* number of bytes already read */ + /* + * number of leading bytes from start of the block + * to the first byte to be read + */ + size_t skip; + + /* + * number of trailing bytes between the last byte + * to be read and the end of the block + */ + size_t padding; assert(entity->info != (uintptr_t)NULL); cur = (block_dev_state_t *)entity->info; @@ -186,102 +269,107 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, (length > 0) && (ops->read != 0)); - if ((buffer & (block_size - 1)) != 0) { + /* + * We don't know the number of bytes that we are going + * to read in every iteration, because it will depend + * on the low level driver. + */ + count = 0; + for (left = length; left > 0; left -= nbytes) { /* - * buffer isn't aligned with block size. - * Block device always relies on DMA operation. - * It's better to make the buffer as block size aligned. + * We must only request operations aligned to the block + * size. Therefore if file_pos is not block-aligned, + * we have to request the operation to start at the + * previous block boundary and skip the leading bytes. And + * similarly, the number of bytes requested must be a + * block size multiple */ - buffer_not_aligned = 1; - } else { - buffer_not_aligned = 0; - } + skip = cur->file_pos & (block_size - 1); - skip = cur->file_pos % block_size; - aligned_length = ((skip + length) + (block_size - 1)) & - ~(block_size - 1); - padding = aligned_length - (skip + length); - left = aligned_length; - do { + /* + * Calculate the block number containing file_pos + * - e.g. block 3. + */ lba = (cur->file_pos + cur->base) / block_size; - if (left >= buf->length) { + + if (skip + left > buf->length) { /* - * Since left is larger, it's impossible to padding. - * - * If buffer isn't aligned, we need to use aligned - * buffer instead. + * The underlying read buffer is too small to + * read all the required data - limit to just + * fill the buffer, and then read again. */ - if (skip || buffer_not_aligned) { - /* - * The beginning address (file_pos) isn't - * aligned with block size, we need to use - * block buffer to read block. Since block - * device is always relied on DMA operation. - */ - count = ops->read(lba, buf->offset, - buf->length); - } else { - count = ops->read(lba, buffer, buf->length); - } - assert(count == buf->length); - cur->file_pos += count - skip; - if (skip || buffer_not_aligned) { - /* - * Since there's not aligned block size caused - * by skip or not aligned buffer, block buffer - * is used to store data. - */ - memcpy((void *)buffer, - (void *)(buf->offset + skip), - count - skip); - } - left = left - (count - skip); + request = buf->length; } else { - if (skip || padding || buffer_not_aligned) { - /* - * The beginning address (file_pos) isn't - * aligned with block size, we have to read - * full block by block buffer instead. - * The size isn't aligned with block size. - * Use block buffer to avoid overflow. - * - * If buffer isn't aligned, use block buffer - * to avoid DMA error. - */ - count = ops->read(lba, buf->offset, left); - } else - count = ops->read(lba, buffer, left); - assert(count == left); - left = left - (skip + padding); - cur->file_pos += left; - if (skip || padding || buffer_not_aligned) { - /* - * Since there's not aligned block size or - * buffer, block buffer is used to store data. - */ - memcpy((void *)buffer, - (void *)(buf->offset + skip), - left); - } - /* It's already the last block operation */ - left = 0; + /* + * The underlying read buffer is big enough to + * read all the required data. Calculate the + * number of bytes to read to align with the + * block size. + */ + request = skip + left; + request = (request + (block_size - 1)) & ~(block_size - 1); } - skip = cur->file_pos % block_size; - } while (left > 0); - *length_read = length; + request = ops->read(lba, buf->offset, request); + + if (request <= skip) { + /* + * We couldn't read enough bytes to jump over + * the skip bytes, so we should have to read + * again the same block, thus generating + * the same error. + */ + return -EIO; + } + + /* + * Need to remove skip and padding bytes,if any, from + * the read data when copying to the user buffer. + */ + nbytes = request - skip; + padding = (nbytes > left) ? nbytes - left : 0; + nbytes -= padding; + + memcpy((void *)(buffer + count), + (void *)(buf->offset + skip), + nbytes); + + cur->file_pos += nbytes; + count += nbytes; + } + assert(count == length); + *length_read = count; return 0; } +/* + * This function allows the caller to write any number of bytes + * from any position. It hides from the caller that the low level + * driver only can write aligned blocks of data. + * See comments for block_read for more details. + */ static int block_write(io_entity_t *entity, const uintptr_t buffer, size_t length, size_t *length_written) { block_dev_state_t *cur; io_block_spec_t *buf; io_block_ops_t *ops; - size_t aligned_length, skip, count, left, padding, block_size; int lba; - int buffer_not_aligned; + size_t block_size, left; + size_t nbytes; /* number of bytes read in one iteration */ + size_t request; /* number of requested bytes in one iteration */ + size_t count; /* number of bytes already read */ + /* + * number of leading bytes from start of the block + * to the first byte to be read + */ + size_t skip; + + /* + * number of trailing bytes between the last byte + * to be read and the end of the block + */ + size_t padding; assert(entity->info != (uintptr_t)NULL); cur = (block_dev_state_t *)entity->info; @@ -293,75 +381,107 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, (ops->read != 0) && (ops->write != 0)); - if ((buffer & (block_size - 1)) != 0) { + /* + * We don't know the number of bytes that we are going + * to write in every iteration, because it will depend + * on the low level driver. + */ + count = 0; + for (left = length; left > 0; left -= nbytes) { /* - * buffer isn't aligned with block size. - * Block device always relies on DMA operation. - * It's better to make the buffer as block size aligned. + * We must only request operations aligned to the block + * size. Therefore if file_pos is not block-aligned, + * we have to request the operation to start at the + * previous block boundary and skip the leading bytes. And + * similarly, the number of bytes requested must be a + * block size multiple */ - buffer_not_aligned = 1; - } else { - buffer_not_aligned = 0; - } + skip = cur->file_pos & (block_size - 1); - skip = cur->file_pos % block_size; - aligned_length = ((skip + length) + (block_size - 1)) & - ~(block_size - 1); - padding = aligned_length - (skip + length); - left = aligned_length; - do { + /* + * Calculate the block number containing file_pos + * - e.g. block 3. + */ lba = (cur->file_pos + cur->base) / block_size; - if (left >= buf->length) { - /* Since left is larger, it's impossible to padding. */ - if (skip || buffer_not_aligned) { - /* - * The beginning address (file_pos) isn't - * aligned with block size or buffer isn't - * aligned, we need to use block buffer to - * write block. - */ - count = ops->read(lba, buf->offset, - buf->length); - assert(count == buf->length); - memcpy((void *)(buf->offset + skip), - (void *)buffer, - count - skip); - count = ops->write(lba, buf->offset, - buf->length); - } else - count = ops->write(lba, buffer, buf->length); - assert(count == buf->length); - cur->file_pos += count - skip; - left = left - (count - skip); + + if (skip + left > buf->length) { + /* + * The underlying read buffer is too small to + * read all the required data - limit to just + * fill the buffer, and then read again. + */ + request = buf->length; } else { - if (skip || padding || buffer_not_aligned) { + /* + * The underlying read buffer is big enough to + * read all the required data. Calculate the + * number of bytes to read to align with the + * block size. + */ + request = skip + left; + request = (request + (block_size - 1)) & ~(block_size - 1); + } + + /* + * The number of bytes that we are going to write + * from the user buffer will depend of the size + * of the current request. + */ + nbytes = request - skip; + padding = (nbytes > left) ? nbytes - left : 0; + nbytes -= padding; + + /* + * If we have skip or padding bytes then we have to preserve + * some content and it means that we have to read before + * writing + */ + if (skip > 0 || padding > 0) { + request = ops->read(lba, buf->offset, request); + /* + * The read may return size less than + * requested. Round down to the nearest block + * boundary + */ + request &= ~(block_size-1); + if (request <= skip) { /* - * The beginning address (file_pos) isn't - * aligned with block size, we need to avoid - * poluate data in the beginning. Reading and - * skipping the beginning is the only way. - * The size isn't aligned with block size. - * Use block buffer to avoid overflow. - * - * If buffer isn't aligned, use block buffer - * to avoid DMA error. + * We couldn't read enough bytes to jump over + * the skip bytes, so we should have to read + * again the same block, thus generating + * the same error. */ - count = ops->read(lba, buf->offset, left); - assert(count == left); - memcpy((void *)(buf->offset + skip), - (void *)buffer, - left - skip - padding); - count = ops->write(lba, buf->offset, left); - } else - count = ops->write(lba, buffer, left); - assert(count == left); - cur->file_pos += left - (skip + padding); - /* It's already the last block operation */ - left = 0; + return -EIO; + } + nbytes = request - skip; + padding = (nbytes > left) ? nbytes - left : 0; + nbytes -= padding; } - skip = cur->file_pos % block_size; - } while (left > 0); - *length_written = length; + + memcpy((void *)(buf->offset + skip), + (void *)(buffer + count), + nbytes); + + request = ops->write(lba, buf->offset, request); + if (request <= skip) + return -EIO; + + /* + * And the previous write operation may modify the size + * of the request, so again, we have to calculate the + * number of bytes that we consumed from the user + * buffer + */ + nbytes = request - skip; + padding = (nbytes > left) ? nbytes - left : 0; + nbytes -= padding; + + cur->file_pos += nbytes; + count += nbytes; + } + assert(count == length); + *length_written = count; + return 0; } diff --git a/plat/hisilicon/poplar/aarch64/platform_common.c b/plat/hisilicon/poplar/aarch64/platform_common.c index a7dac4fb5..762bd8467 100644 --- a/plat/hisilicon/poplar/aarch64/platform_common.c +++ b/plat/hisilicon/poplar/aarch64/platform_common.c @@ -25,9 +25,14 @@ DEVICE_SIZE, \ MT_DEVICE | MT_RW | MT_SECURE) +#define MAP_TSP_MEM MAP_REGION_FLAT(TSP_SEC_MEM_BASE, \ + TSP_SEC_MEM_SIZE, \ + MT_MEMORY | MT_RW | MT_SECURE) + static const mmap_region_t poplar_mmap[] = { MAP_DDR, MAP_DEVICE, + MAP_TSP_MEM, {0} }; diff --git a/plat/hisilicon/poplar/bl2_plat_setup.c b/plat/hisilicon/poplar/bl2_plat_setup.c index 1741475b8..db507c327 100644 --- a/plat/hisilicon/poplar/bl2_plat_setup.c +++ b/plat/hisilicon/poplar/bl2_plat_setup.c @@ -29,8 +29,10 @@ typedef struct bl2_to_bl31_params_mem { bl31_params_t bl31_params; image_info_t bl31_image_info; + image_info_t bl32_image_info; image_info_t bl33_image_info; entry_point_info_t bl33_ep_info; + entry_point_info_t bl32_ep_info; entry_point_info_t bl31_ep_info; } bl2_to_bl31_params_mem_t; @@ -61,6 +63,16 @@ bl31_params_t *bl2_plat_get_bl31_params(void) SET_PARAM_HEAD(bl2_to_bl31_params->bl31_image_info, PARAM_IMAGE_BINARY, VERSION_1, 0); + /* Fill BL3-2 related information if it exists */ +#ifdef BL32_BASE + bl2_to_bl31_params->bl32_ep_info = &bl31_params_mem.bl32_ep_info; + SET_PARAM_HEAD(bl2_to_bl31_params->bl32_ep_info, PARAM_EP, + VERSION_1, 0); + bl2_to_bl31_params->bl32_image_info = &bl31_params_mem.bl32_image_info; + SET_PARAM_HEAD(bl2_to_bl31_params->bl32_image_info, PARAM_IMAGE_BINARY, + VERSION_1, 0); +#endif + /* Fill BL3-3 related information */ bl2_to_bl31_params->bl33_ep_info = &bl31_params_mem.bl33_ep_info; SET_PARAM_HEAD(bl2_to_bl31_params->bl33_ep_info, @@ -89,6 +101,41 @@ void bl2_plat_set_bl31_ep_info(image_info_t *image, DISABLE_ALL_EXCEPTIONS); } +/******************************************************************************* + * Before calling this function BL32 is loaded in memory and its entrypoint + * is set by load_image. This is a placeholder for the platform to change + * the entrypoint of BL32 and set SPSR and security state. + * On Poplar we only set the security state of the entrypoint + ******************************************************************************/ +#ifdef BL32_BASE +void bl2_plat_set_bl32_ep_info(image_info_t *bl32_image_info, + entry_point_info_t *bl32_ep_info) +{ + SET_SECURITY_STATE(bl32_ep_info->h.attr, SECURE); + /* + * The Secure Payload Dispatcher service is responsible for + * setting the SPSR prior to entry into the BL32 image. + */ + bl32_ep_info->spsr = 0; +} + +/******************************************************************************* + * Populate the extents of memory available for loading BL32 + ******************************************************************************/ +void bl2_plat_get_bl32_meminfo(meminfo_t *bl32_meminfo) +{ + /* + * Populate the extents of memory available for loading BL32. + */ + bl32_meminfo->total_base = BL32_BASE; + bl32_meminfo->free_base = BL32_BASE; + bl32_meminfo->total_size = + (TSP_SEC_MEM_BASE + TSP_SEC_MEM_SIZE) - BL32_BASE; + bl32_meminfo->free_size = + (TSP_SEC_MEM_BASE + TSP_SEC_MEM_SIZE) - BL32_BASE; +} +#endif /* BL32_BASE */ + static uint32_t hisi_get_spsr_for_bl33_entry(void) { unsigned long el_status; @@ -159,5 +206,5 @@ void bl2_platform_setup(void) unsigned long plat_get_ns_image_entrypoint(void) { - return PLAT_ARM_NS_IMAGE_OFFSET; + return PLAT_POPLAR_NS_IMAGE_OFFSET; } diff --git a/plat/hisilicon/poplar/bl31_plat_setup.c b/plat/hisilicon/poplar/bl31_plat_setup.c index b9a0e18e7..e3a5c50f6 100644 --- a/plat/hisilicon/poplar/bl31_plat_setup.c +++ b/plat/hisilicon/poplar/bl31_plat_setup.c @@ -32,11 +32,31 @@ #define BL31_COHERENT_RAM_BASE (unsigned long)(&__COHERENT_RAM_START__) #define BL31_COHERENT_RAM_LIMIT (unsigned long)(&__COHERENT_RAM_END__) +#define TZPC_SEC_ATTR_CTRL_VALUE (0x9DB98D45) + +static entry_point_info_t bl32_image_ep_info; static entry_point_info_t bl33_image_ep_info; +static void hisi_tzpc_sec_init(void) +{ + mmio_write_32(HISI_TZPC_SEC_ATTR_CTRL, TZPC_SEC_ATTR_CTRL_VALUE); +} + entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) { - return &bl33_image_ep_info; + entry_point_info_t *next_image_info; + + assert(sec_state_is_valid(type)); + next_image_info = (type == NON_SECURE) + ? &bl33_image_ep_info : &bl32_image_ep_info; + /* + * None of the images on the ARM development platforms can have 0x0 + * as the entrypoint + */ + if (next_image_info->pc) + return next_image_info; + else + return NULL; } void bl31_early_platform_setup(bl31_params_t *from_bl2, @@ -47,6 +67,13 @@ void bl31_early_platform_setup(bl31_params_t *from_bl2, /* Init console for crash report */ plat_crash_console_init(); + + /* + * Copy BL32 (if populated by BL2) and BL33 entry point information. + * They are stored in Secure RAM, in BL2's address space. + */ + if (from_bl2->bl32_ep_info) + bl32_image_ep_info = *from_bl2->bl32_ep_info; bl33_image_ep_info = *from_bl2->bl33_ep_info; } @@ -58,6 +85,9 @@ void bl31_platform_setup(void) /* Init GIC distributor and CPU interface */ plat_arm_gic_driver_init(); plat_arm_gic_init(); + + /* Init security properties of IP blocks */ + hisi_tzpc_sec_init(); } void bl31_plat_runtime_setup(void) diff --git a/plat/hisilicon/poplar/include/hi3798cv200.h b/plat/hisilicon/poplar/include/hi3798cv200.h index 6318b9c64..540d0aa18 100644 --- a/plat/hisilicon/poplar/include/hi3798cv200.h +++ b/plat/hisilicon/poplar/include/hi3798cv200.h @@ -30,7 +30,7 @@ #define TIMER20_BGLOAD (SEC_TIMER2_BASE + 0x018) /* GPIO */ -#define GPIO_MAX (12) +#define GPIO_MAX (13) #define GPIO_BASE(x) (x != 5 ? \ 0xf820000 + x * 0x1000 : 0xf8004000) @@ -97,4 +97,7 @@ /* Watchdog */ #define HISI_WDG0_BASE (0xF8A2C000) +#define HISI_TZPC_BASE (0xF8A80000) +#define HISI_TZPC_SEC_ATTR_CTRL (HISI_TZPC_BASE + 0x10) + #endif /* __HI3798cv200_H__ */ diff --git a/plat/hisilicon/poplar/include/platform_def.h b/plat/hisilicon/poplar/include/platform_def.h index b7afe8201..3d1ad9b99 100644 --- a/plat/hisilicon/poplar/include/platform_def.h +++ b/plat/hisilicon/poplar/include/platform_def.h @@ -48,11 +48,55 @@ #define TEE_SEC_MEM_BASE (0x70000000) #define TEE_SEC_MEM_SIZE (0x10000000) +/* Memory location options for TSP */ +#define POPLAR_SRAM_ID 0 +#define POPLAR_DRAM_ID 1 + +/* + * DDR for OP-TEE (28MB from 0x02200000 -0x04000000) is divided in several + * regions: + * - Secure DDR (default is the top 16MB) used by OP-TEE + * - Non-secure DDR (4MB) reserved for OP-TEE's future use + * - Secure DDR (4MB aligned on 4MB) for OP-TEE's "Secure Data Path" feature + * - Non-secure DDR used by OP-TEE (shared memory and padding) (4MB) + * - Non-secure DDR (2MB) reserved for OP-TEE's future use + */ +#define DDR_SEC_SIZE 0x01000000 +#define DDR_SEC_BASE 0x03000000 + #define BL_MEM_BASE (BL1_RO_BASE) #define BL_MEM_LIMIT (BL31_LIMIT) #define BL_MEM_SIZE (BL_MEM_LIMIT - BL_MEM_BASE) -#define PLAT_ARM_NS_IMAGE_OFFSET 0x37000000 +/* + * BL3-2 specific defines. + */ + +/* + * The TSP currently executes from TZC secured area of DRAM. + */ +#define BL32_DRAM_BASE 0x03000000 +#define BL32_DRAM_LIMIT 0x04000000 + +#if (POPLAR_TSP_RAM_LOCATION_ID == POPLAR_DRAM_ID) +#define TSP_SEC_MEM_BASE BL32_DRAM_BASE +#define TSP_SEC_MEM_SIZE (BL32_DRAM_LIMIT - BL32_DRAM_BASE) +#define BL32_BASE BL32_DRAM_BASE +#define BL32_LIMIT BL32_DRAM_LIMIT +#elif (POPLAR_TSP_RAM_LOCATION_ID == POPLAR_SRAM_ID) +#error "SRAM storage of TSP payload is currently unsupported" +#else +#error "Currently unsupported POPLAR_TSP_LOCATION_ID value" +#endif + +/* BL32 is mandatory in AArch32 */ +#ifndef AARCH32 +#ifdef SPD_none +#undef BL32_BASE +#endif /* SPD_none */ +#endif + +#define PLAT_POPLAR_NS_IMAGE_OFFSET 0x37000000 /* Page table and MMU setup constants */ #define ADDR_SPACE_SIZE (1ull << 32) diff --git a/plat/hisilicon/poplar/include/poplar_layout.h b/plat/hisilicon/poplar/include/poplar_layout.h index 192bcb9c4..e0b5618ef 100644 --- a/plat/hisilicon/poplar/include/poplar_layout.h +++ b/plat/hisilicon/poplar/include/poplar_layout.h @@ -74,16 +74,16 @@ * "OFFSET" is an offset to the start of a region relative to the * base of the "l-loader" TEXT section (also a multiple of page size). */ -#define LLOADER_TEXT_BASE 0x00001000 /* page aligned */ +#define LLOADER_TEXT_BASE 0x02001000 /* page aligned */ #define BL1_OFFSET 0x0000D000 /* page multiple */ -#define FIP_BASE 0x00040000 +#define FIP_BASE 0x02040000 #define BL1_RO_SIZE 0x00008000 /* page multiple */ #define BL1_RW_SIZE 0x00008000 /* page multiple */ #define BL1_SIZE (BL1_RO_SIZE + BL1_RW_SIZE) #define BL2_SIZE 0x0000c000 /* page multiple */ #define BL31_SIZE 0x00014000 -#define FIP_SIZE 0x00068000 +#define FIP_SIZE 0x000c0000 /* absolute max */ /* BL1_OFFSET */ /* (Defined above) */ #define BL1_BASE (LLOADER_TEXT_BASE + BL1_OFFSET) diff --git a/plat/hisilicon/poplar/plat_storage.c b/plat/hisilicon/poplar/plat_storage.c index 623a61b77..ab94cba33 100644 --- a/plat/hisilicon/poplar/plat_storage.c +++ b/plat/hisilicon/poplar/plat_storage.c @@ -43,6 +43,10 @@ static const io_uuid_spec_t bl31_uuid_spec = { .uuid = UUID_EL3_RUNTIME_FIRMWARE_BL31, }; +static const io_uuid_spec_t bl32_uuid_spec = { + .uuid = UUID_SECURE_PAYLOAD_BL32, +}; + static const io_uuid_spec_t bl33_uuid_spec = { .uuid = UUID_NON_TRUSTED_FIRMWARE_BL33, }; @@ -69,6 +73,11 @@ static const struct plat_io_policy policies[] = { (uintptr_t)&bl31_uuid_spec, open_fip }, + [BL32_IMAGE_ID] = { + &fip_dev_handle, + (uintptr_t)&bl32_uuid_spec, + open_fip + }, [BL33_IMAGE_ID] = { &fip_dev_handle, (uintptr_t)&bl33_uuid_spec, diff --git a/plat/hisilicon/poplar/platform.mk b/plat/hisilicon/poplar/platform.mk index 28e0d1f4f..818e3115f 100644 --- a/plat/hisilicon/poplar/platform.mk +++ b/plat/hisilicon/poplar/platform.mk @@ -4,6 +4,17 @@ # SPDX-License-Identifier: BSD-3-Clause # +# On Poplar, the TSP can execute from TZC secure area in DRAM. +POPLAR_TSP_RAM_LOCATION := dram +ifeq (${POPLAR_TSP_RAM_LOCATION}, dram) + POPLAR_TSP_RAM_LOCATION_ID = POPLAR_DRAM_ID +else ifeq (${HIKEY960_TSP_RAM_LOCATION}, sram) + POPLAR_TSP_RAM_LOCATION_ID := POPLAR_SRAM_ID +else + $(error "Currently unsupported POPLAR_TSP_RAM_LOCATION value") +endif +$(eval $(call add_define,POPLAR_TSP_RAM_LOCATION_ID)) + NEED_BL33 := yes COLD_BOOT_SINGLE_CPU := 1 diff --git a/services/std_svc/spm/spm_main.c b/services/std_svc/spm/spm_main.c index 00f3a30c3..ae71c1df5 100644 --- a/services/std_svc/spm/spm_main.c +++ b/services/std_svc/spm/spm_main.c @@ -48,7 +48,7 @@ void spm_setup_next_eret_into_sel0(cpu_context_t *secure_context) * 2. Saves the current C runtime state (callee-saved registers) on the stack * frame and saves a reference to this state. * 3. Calls el3_exit() so that the EL3 system and general purpose registers - * from the sp_ctx->cpu_ctx are used to enter the secure payload image. + * from the sp_ctx->cpu_ctx are used to enter the secure partition image. ******************************************************************************/ static uint64_t spm_synchronous_sp_entry(secure_partition_context_t *sp_ctx_ptr) { @@ -75,7 +75,7 @@ static uint64_t spm_synchronous_sp_entry(secure_partition_context_t *sp_ctx_ptr) /******************************************************************************* * This function takes a Secure partition context pointer and: - * 1. Saves the S-EL1 system register context tp sp_ctx->cpu_ctx. + * 1. Saves the S-EL1 system register context to sp_ctx->cpu_ctx. * 2. Restores the current C runtime state (callee saved registers) from the * stack frame using the reference to this state saved in * spm_secure_partition_enter(). @@ -101,7 +101,7 @@ static void __dead2 spm_synchronous_sp_exit( * This function passes control to the Secure Partition image (BL32) for the * first time on the primary cpu after a cold boot. It assumes that a valid * secure context has already been created by spm_setup() which can be directly - * used. This function performs a synchronous entry into the Secure payload. + * used. This function performs a synchronous entry into the Secure partition. * The SP passes control back to this routine through a SMC. ******************************************************************************/ int32_t spm_init(void) @@ -126,7 +126,7 @@ int32_t spm_init(void) secure_partition_setup(); /* - * Arrange for an entry into the secure payload. + * Arrange for an entry into the secure partition. */ sp_init_in_progress = 1; rc = spm_synchronous_sp_entry(&sp_ctx); @@ -138,9 +138,9 @@ int32_t spm_init(void) } /******************************************************************************* - * Given a secure payload entrypoint info pointer, entry point PC & pointer to + * Given a secure partition entrypoint info pointer, entry point PC & pointer to * a context data structure, this function will initialize the SPM context and - * entry point info for the secure payload + * entry point info for the secure partition. ******************************************************************************/ void spm_init_sp_ep_state(struct entry_point_info *sp_ep_info, uint64_t pc, @@ -161,7 +161,7 @@ void spm_init_sp_ep_state(struct entry_point_info *sp_ep_info, SET_PARAM_HEAD(sp_ep_info, PARAM_EP, VERSION_1, ep_attr); sp_ep_info->pc = pc; - /* The SPM payload runs in S-EL0 */ + /* The secure partition runs in S-EL0. */ sp_ep_info->spsr = SPSR_64(MODE_EL0, MODE_SP_EL0, DISABLE_ALL_EXCEPTIONS); @@ -350,7 +350,7 @@ uint64_t spm_smc_handler(uint32_t smc_fid, switch (smc_fid) { - case SPM_VERSION_AARCH32: + case SPM_VERSION_AARCH32: SMC_RET1(handle, SPM_VERSION_COMPILED); case SP_EVENT_COMPLETE_AARCH64: @@ -414,12 +414,31 @@ uint64_t spm_smc_handler(uint32_t smc_fid, switch (smc_fid) { - case SP_VERSION_AARCH64: - case SP_VERSION_AARCH32: + case SP_VERSION_AARCH64: + case SP_VERSION_AARCH32: SMC_RET1(handle, SP_VERSION_COMPILED); case MM_COMMUNICATE_AARCH32: case MM_COMMUNICATE_AARCH64: + { + uint64_t mm_cookie = x1; + uint64_t comm_buffer_address = x2; + uint64_t comm_size_address = x3; + + /* Cookie. Reserved for future use. It must be zero. */ + if (mm_cookie != 0) { + ERROR("MM_COMMUNICATE: cookie is not zero\n"); + SMC_RET1(handle, SPM_INVALID_PARAMETER); + } + + if (comm_buffer_address == 0) { + ERROR("MM_COMMUNICATE: comm_buffer_address is zero\n"); + SMC_RET1(handle, SPM_INVALID_PARAMETER); + } + + if (comm_size_address != 0) { + VERBOSE("MM_COMMUNICATE: comm_size_address is not 0 as recommended.\n"); + } /* Save the Normal world context */ cm_el1_sysregs_context_save(NON_SECURE); @@ -432,14 +451,9 @@ uint64_t spm_smc_handler(uint32_t smc_fid, cm_el1_sysregs_context_restore(SECURE); cm_set_next_eret_context(SECURE); - /* Cookie. Reserved for future use. It must be zero. */ - assert(x1 == 0); - - if (x3 != 0) { - VERBOSE("MM_COMMUNICATE_AARCH32/64: X3 is not 0 as recommended.\n"); - } - - SMC_RET4(&sp_ctx.cpu_ctx, smc_fid, x1, x2, x3); + SMC_RET4(&sp_ctx.cpu_ctx, smc_fid, comm_buffer_address, + comm_size_address, plat_my_core_pos()); + } case SP_MEMORY_ATTRIBUTES_GET_AARCH64: case SP_MEMORY_ATTRIBUTES_SET_AARCH64: |