4.21. Granule Protection Tables Library
This document describes the design of the granule protection tables (GPT) library used by Trusted Firmware-A (TF-A). This library provides the APIs needed to initialize the GPTs based on a data structure containing information about the systems memory layout, configure the system registers to enable granule protection checks based on these tables, and transition granules between different PAS (physical address spaces) at runtime.
Arm CCA adds two new security states for a total of four: root, realm, secure, and non-secure. In addition to new security states, corresponding physical address spaces have been added to control memory access for each state. The PAS access allowed to each security state can be seen in the table below.
The GPT can function as either a 1 level or 2 level lookup depending on how a PAS region is configured. The first step is the level 0 table, each entry in the level 0 table controls access to a relatively large region in memory (block descriptor), and the entire region can belong to a single PAS when a one step mapping is used, or a level 0 entry can link to a level 1 table where relatively small regions (granules) of memory can be assigned to different PAS with a 2 step mapping. The type of mapping used for each PAS is determined by the user when setting up the configuration structure.
4.21.1. Design Concepts and Interfaces
This section covers some important concepts and data structures used in the GPT library.
There are three main parameters that determine how the tables are organized and function: the PPS (protected physical space) which is the total amount of protected physical address space in the system, PGS (physical granule size) which is how large each level 1 granule is, and L0GPTSZ (level 0 GPT size) which determines how much physical memory is governed by each level 0 entry. A granule is the smallest unit of memory that can be independently assigned to a PAS.
L0GPTSZ is determined by the hardware and is read from the GPCCR_EL3 register. PPS and PGS are passed into the APIs at runtime and can be determined in whatever way is best for a given platform, either through some algorithm or hard coded in the firmware.
GPT setup is split into two parts: table creation and runtime initialization. In the table creation step, a data structure containing information about the desired PAS regions is passed into the library which validates the mappings, creates the tables in memory, and enables granule protection checks. In the runtime initialization step, the runtime firmware locates the existing tables in memory using the GPT register configuration and saves important data to a structure used by the granule transition service which will be covered more below.
In the reference implementation for FVP models, you can find an example of PAS
region definitions in the file
Table creation API calls can be found in
runtime initialization API calls can be seen in
188.8.131.52. Defining PAS regions
pas_region_t structure is a way to represent a physical address space and
its attributes that can be used by the GPT library to initialize the tables.
This structure is composed of the following:
The base physical address
The region size
The desired attributes of this memory region (mapping type, PAS type)
pas_region_t type in
The programmer should provide the API with an array containing
structures, then the library will check the desired memory access layout for
validity and create tables to implement it.
pas_region_t is a public type, however it is recommended that the macros
GPT_MAP_REGION_GRANULE be used to populate
these structures instead of doing it manually to reduce the risk of future
compatibility issues. These macros take the base physical address, region size,
and PAS type as arguments to generate the pas_region_t structure. As the names
GPT_MAP_REGION_BLOCK creates a region using only L0 mapping while
GPT_MAP_REGION_GRANULE creates a region using L0 and L1 mappings.
184.108.40.206. Level 0 and Level 1 Tables
The GPT initialization APIs require memory to be passed in for the tables to be
gpt_init_l0_tables takes a memory address and size for building
the level 0 tables and
gpt_init_pas_l1_tables takes an address and size for
building the level 1 tables which are linked from level 0 descriptors. The
tables should have PAS type
GPT_GPI_ROOT and a typical system might place
its level 0 table in SRAM and its level 1 table(s) in DRAM.
220.127.116.11. Granule Transition Service
The Granule Transition Service allows memory mapped with GPT_MAP_REGION_GRANULE ownership to be changed using SMC calls. Non-secure granules can be transitioned to either realm or secure space, and realm and secure granules can be transitioned back to non-secure. This library only allows memory mapped as granules to be transitioned, memory mapped as blocks have their GPIs fixed after table creation.
4.21.2. Library APIs
The public APIs and types can be found in
include/lib/gpt_rme/gpt_rme.h and this
section is intended to provide additional details and clarifications.
To create the GPTs and enable granule protection checks the APIs need to be called in the correct order and at the correct time during the system boot process.
Firmware must enable the MMU.
Firmware must call
gpt_init_l0_tablesto initialize the level 0 tables to a default state, that is, initializing all of the L0 descriptors to allow all accesses to all memory. The PPS is provided to this function as an argument.
DDR discovery and initialization by the system, the discovered DDR region(s) are then added to the L1 PAS regions to be initialized in the next step and used by the GTSI at runtime.
Firmware must call
gpt_init_pas_l1_tableswith a pointer to an array of
pas_region_tstructures containing the desired memory access layout. The PGS is provided to this function as an argument.
Firmware must call
gpt_enableto enable granule protection checks by setting the correct register values.
In systems that make use of the granule transition service, runtime firmware must call
gpt_runtime_initto set up the data structures needed by the GTSI to find the tables and transition granules between PAS types.
18.104.22.168. API Constraints
The values allowed by the API for PPS and PGS are enumerated types
defined in the file
Allowable values for PPS along with their corresponding size.
GPCCR_PPS_4GB(4GB protected space, 0x100000000 bytes)
GPCCR_PPS_64GB(64GB protected space, 0x1000000000 bytes)
GPCCR_PPS_1TB(1TB protected space, 0x10000000000 bytes)
GPCCR_PPS_4TB(4TB protected space, 0x40000000000 bytes)
GPCCR_PPS_16TB(16TB protected space, 0x100000000000 bytes)
GPCCR_PPS_256TB(256TB protected space, 0x1000000000000 bytes)
GPCCR_PPS_4PB(4PB protected space, 0x10000000000000 bytes)
Allowable values for PGS along with their corresponding size.
GPCCR_PGS_4K(4KB granules, 0x1000 bytes)
GPCCR_PGS_16K(16KB granules, 0x4000 bytes)
GPCCR_PGS_64K(64KB granules, 0x10000 bytes)
Allowable values for L0GPTSZ along with the corresponding size.
GPCCR_L0GPTSZ_30BITS(1GB regions, 0x40000000 bytes)
GPCCR_L0GPTSZ_34BITS(16GB regions, 0x400000000 bytes)
GPCCR_L0GPTSZ_36BITS(64GB regions, 0x1000000000 bytes)
GPCCR_L0GPTSZ_39BITS(512GB regions, 0x8000000000 bytes)
Note that the value of the PPS, PGS, and L0GPTSZ definitions is an encoded value corresponding to the size, not the size itself. The decoded hex representations of the sizes have been provided for convenience.
The L0 table memory has some constraints that must be taken into account.
The L0 table must be aligned to either the table size or 4096 bytes, whichever is greater. L0 table size is the total protected space (PPS) divided by the size of each L0 region (L0GPTSZ) multiplied by the size of each L0 descriptor (8 bytes). ((PPS / L0GPTSZ) * 8)
The L0 memory size must be greater than or equal to the table size.
The L0 memory must fall within a PAS of type GPT_GPI_ROOT.
The L1 memory also has some constraints.
The L1 tables must be aligned to their size. The size of each L1 table is the size of each L0 region (L0GPTSZ) divided by the granule size (PGS) divided by the granules controlled in each byte (2). ((L0GPTSZ / PGS) / 2)
There must be enough L1 memory supplied to build all requested L1 tables.
The L1 memory must fall within a PAS of type GPT_GPI_ROOT.
If an invalid combination of parameters is supplied, the APIs will print an error message and return a negative value. The return values of APIs should be checked to ensure successful configuration.
22.214.171.124. Sample Calculation for L0 memory size and alignment
Let PPS=GPCCR_PPS_4GB and L0GPTSZ=GPCCR_L0GPTSZ_30BITS
We can find the total L0 table size with ((PPS / L0GPTSZ) * 8)
Substitute values to get this: ((0x100000000 / 0x40000000) * 8)
And solve to get 32 bytes. In this case, 4096 is greater than 32, so the L0 tables must be aligned to 4096 bytes.
126.96.36.199. Sample calculation for L1 table size and alignment
Let PGS=GPCCR_PGS_4K and L0GPTSZ=GPCCR_L0GPTSZ_30BITS
We can find the size of each L1 table with ((L0GPTSZ / PGS) / 2).
Substitute values: ((0x40000000 / 0x1000) / 2)
And solve to get 0x20000 bytes per L1 table.