diff --git a/pal/baremetal/base/include/pal_common_support.h b/pal/baremetal/base/include/pal_common_support.h index ea9a47b3..c4dad3ff 100644 --- a/pal/baremetal/base/include/pal_common_support.h +++ b/pal/baremetal/base/include/pal_common_support.h @@ -1116,8 +1116,8 @@ typedef struct { void pal_hmat_create_info_table(HMAT_INFO_TABLE *HmatTable); /* Platform Communication Channel (PCC) info table */ -#ifndef GAS_STRUCT -#define GAS_STRUCT +/* Set 1B alignment to prevent padding before the address field */ +#pragma pack(push, 1) typedef struct { uint8_t addr_space_id; uint8_t reg_bit_width; @@ -1125,13 +1125,15 @@ typedef struct { uint8_t access_size; uint64_t addr; } GENERIC_ADDRESS_STRUCTURE; -#endif +#pragma pack(pop) typedef struct { uint64_t base_addr; /* base addr of shared mem-region */ + uint32_t memory_length; /* length of shared mem-region */ GENERIC_ADDRESS_STRUCTURE doorbell_reg; /* doorbell register */ uint64_t doorbell_preserve; /* doorbell register preserve mask */ uint64_t doorbell_write; /* doorbell register set mask */ + uint32_t nominal_latency_usec; /* expected command latency */ uint32_t min_req_turnaround_usec; /* minimum request turnaround time */ GENERIC_ADDRESS_STRUCTURE cmd_complete_chk_reg; /* command complete check register */ uint64_t cmd_complete_chk_mask; /* command complete check mask */ @@ -1179,8 +1181,8 @@ typedef struct { typedef struct { uint32_t msc_id; /* Identifier of the MSC */ uint32_t flags; /* Reserved, must be zero */ - uint32_t val; /* value to be written to the register */ uint32_t offset; /* MPAM register offset to write */ + uint32_t val; /* value to be written to the register */ } PCC_MPAM_MSC_WRITE_CMD_PARA; typedef struct { @@ -1204,6 +1206,8 @@ typedef struct { #define MPAM_PCC_CMD_SUCCESS 0x0 #define MPAM_PCC_SAFE_RETURN 0x0 #define RETURN_FAILURE 0xFFFFFFFF +#define PCC_TY3_FLAGS_OFFSET 4 +#define PCC_TY3_LENGTH_OFFSET 8 #define PCC_TY3_CMD_OFFSET 12 #define PCC_TY3_COMM_SPACE 16 #define PCCT_SUBSPACE_TYPE_3_EXTENDED_PCC 0x03 diff --git a/pal/baremetal/base/src/pal_mpam.c b/pal/baremetal/base/src/pal_mpam.c index a8c6f3e2..b5065b4b 100644 --- a/pal/baremetal/base/src/pal_mpam.c +++ b/pal/baremetal/base/src/pal_mpam.c @@ -47,6 +47,9 @@ pal_mpam_dump_table(MPAM_INFO_TABLE *MpamTable) pal_print_msg(ACS_PRINT_INFO, "\nMSC node Index :%d ", i); + pal_print_msg(ACS_PRINT_INFO, + "\nInterface type :%lx ", + curr_entry->intrf_type); pal_print_msg(ACS_PRINT_INFO, "\nMSC base addr :%llx ", curr_entry->msc_base_addr); diff --git a/pal/baremetal/base/src/pal_pcc.c b/pal/baremetal/base/src/pal_pcc.c index 05594aae..52e93b26 100644 --- a/pal/baremetal/base/src/pal_pcc.c +++ b/pal/baremetal/base/src/pal_pcc.c @@ -61,6 +61,9 @@ pal_pcc_dump_info_table(PCC_INFO_TABLE *PccInfoTable) pal_print_msg(ACS_PRINT_INFO, "\n Base address : 0x%lx", ptr_pcc_ss_type_3->base_addr); + pal_print_msg(ACS_PRINT_INFO, + "\n Memory length : 0x%x", + ptr_pcc_ss_type_3->memory_length); pal_print_msg(ACS_PRINT_INFO, "\n Doorbell Register addr : 0x%lx", ptr_pcc_ss_type_3->doorbell_reg.addr); @@ -70,6 +73,9 @@ pal_pcc_dump_info_table(PCC_INFO_TABLE *PccInfoTable) pal_print_msg(ACS_PRINT_INFO, "\n Doorbell write Mask : 0x%lx", ptr_pcc_ss_type_3->doorbell_write); + pal_print_msg(ACS_PRINT_INFO, + "\n Nominal latency (us) : 0x%x", + ptr_pcc_ss_type_3->nominal_latency_usec); pal_print_msg(ACS_PRINT_INFO, "\n Min req turnaround time (us) : 0x%x", ptr_pcc_ss_type_3->min_req_turnaround_usec); @@ -125,6 +131,8 @@ pal_pcc_create_info_table(PCC_INFO_TABLE *PccInfoTable) if (curr_entry->subspace_type == PCCT_SUBSPACE_TYPE_3_EXTENDED_PCC) { curr_entry->type_spec_info.pcc_ss_type_3.base_addr = platform_pcc_cfg.pcc_info[i].type_spec_info.pcc_ss_type_3.base_addr; + curr_entry->type_spec_info.pcc_ss_type_3.memory_length + = platform_pcc_cfg.pcc_info[i].type_spec_info.pcc_ss_type_3.memory_length; curr_entry->type_spec_info.pcc_ss_type_3.cmd_complete_chk_mask = platform_pcc_cfg.pcc_info[i].type_spec_info.pcc_ss_type_3.cmd_complete_chk_mask; curr_entry->type_spec_info.pcc_ss_type_3.cmd_complete_chk_reg @@ -141,6 +149,8 @@ pal_pcc_create_info_table(PCC_INFO_TABLE *PccInfoTable) = platform_pcc_cfg.pcc_info[i].type_spec_info.pcc_ss_type_3.doorbell_reg; curr_entry->type_spec_info.pcc_ss_type_3.doorbell_write = platform_pcc_cfg.pcc_info[i].type_spec_info.pcc_ss_type_3.doorbell_write; + curr_entry->type_spec_info.pcc_ss_type_3.nominal_latency_usec + = platform_pcc_cfg.pcc_info[i].type_spec_info.pcc_ss_type_3.nominal_latency_usec; curr_entry->type_spec_info.pcc_ss_type_3.min_req_turnaround_usec = platform_pcc_cfg.pcc_info[i].type_spec_info.pcc_ss_type_3.min_req_turnaround_usec; } diff --git a/pal/baremetal/target/RDN2/include/platform_override_fvp.h b/pal/baremetal/target/RDN2/include/platform_override_fvp.h index d25ccddd..600a9da1 100644 --- a/pal/baremetal/target/RDN2/include/platform_override_fvp.h +++ b/pal/baremetal/target/RDN2/include/platform_override_fvp.h @@ -1575,8 +1575,10 @@ #define PLATFORM_PCC_SUBSPACE0_INDEX 0x0 #define PLATFORM_PCC_SUBSPACE0_TYPE 0x3 #define PLATFORM_PCC_SUBSPACE0_BASE 0x0 +#define PLATFORM_PCC_SUBSPACE0_MEMORY_LENGTH 0x20 #define PLATFORM_PCC_SUBSPACE0_DOORBELL_PRESERVE 0x0 #define PLATFORM_PCC_SUBSPACE0_DOORBELL_WRITE 0x0 +#define PLATFORM_PCC_SUBSPACE0_NOMINAL_LATENCY 0x0 #define PLATFORM_PCC_SUBSPACE0_MIN_REQ_TURN_TIME 0x0 #define PLATFORM_PCC_SUBSPACE0_CMD_COMPLETE_CHK_MASK 0x0 #define PLATFORM_PCC_SUBSPACE0_CMD_UPDATE_PRESERVE 0x0 diff --git a/pal/baremetal/target/RDN2/include/platform_override_struct.h b/pal/baremetal/target/RDN2/include/platform_override_struct.h index 79d6713c..c993f00f 100644 --- a/pal/baremetal/target/RDN2/include/platform_override_struct.h +++ b/pal/baremetal/target/RDN2/include/platform_override_struct.h @@ -17,6 +17,7 @@ #include #include "acs_stdint.h" +#include "pal_common_support.h" #include "platform_override_fvp.h" #define MAX_CS_COMP_LENGTH 256 @@ -451,22 +452,13 @@ typedef struct { /* Platform Communication Channel (PCC) info table */ -#ifndef GAS_STRUCT -#define GAS_STRUCT -typedef struct { - uint8_t addr_space_id; - uint8_t reg_bit_width; - uint8_t reg_bit_offset; - uint8_t access_size; - uint64_t addr; -} GENERIC_ADDRESS_STRUCTURE; -#endif - typedef struct { uint64_t base_addr; /* base addr of shared mem-region */ + uint32_t memory_length; /* length of shared mem-region */ GENERIC_ADDRESS_STRUCTURE doorbell_reg; /* doorbell register */ uint64_t doorbell_preserve; /* doorbell register preserve mask */ uint64_t doorbell_write; /* doorbell register set mask */ + uint32_t nominal_latency_usec; /* expected command latency */ uint32_t min_req_turnaround_usec; /* minimum request turnaround time */ GENERIC_ADDRESS_STRUCTURE cmd_complete_chk_reg; /* command complete check register */ uint64_t cmd_complete_chk_mask; /* command complete check mask */ diff --git a/pal/baremetal/target/RDN2/src/platform_cfg_fvp.c b/pal/baremetal/target/RDN2/src/platform_cfg_fvp.c index 1f5f3f91..709b8a86 100644 --- a/pal/baremetal/target/RDN2/src/platform_cfg_fvp.c +++ b/pal/baremetal/target/RDN2/src/platform_cfg_fvp.c @@ -1676,12 +1676,16 @@ const PLATFORM_OVERRIDE_PCC_INFO_TABLE platform_pcc_cfg = { .pcc_info[0].type_spec_info.pcc_ss_type_3.base_addr = PLATFORM_PCC_SUBSPACE0_BASE, + .pcc_info[0].type_spec_info.pcc_ss_type_3.memory_length + = PLATFORM_PCC_SUBSPACE0_MEMORY_LENGTH, .pcc_info[0].type_spec_info.pcc_ss_type_3.doorbell_reg = PLATFORM_PCC_SUBSPACE0_DOORBELL_REG, .pcc_info[0].type_spec_info.pcc_ss_type_3.doorbell_preserve = PLATFORM_PCC_SUBSPACE0_DOORBELL_PRESERVE, .pcc_info[0].type_spec_info.pcc_ss_type_3.doorbell_write = PLATFORM_PCC_SUBSPACE0_DOORBELL_WRITE, + .pcc_info[0].type_spec_info.pcc_ss_type_3.nominal_latency_usec + = PLATFORM_PCC_SUBSPACE0_NOMINAL_LATENCY, .pcc_info[0].type_spec_info.pcc_ss_type_3.min_req_turnaround_usec = PLATFORM_PCC_SUBSPACE0_MIN_REQ_TURN_TIME, .pcc_info[0].type_spec_info.pcc_ss_type_3.cmd_complete_chk_reg diff --git a/pal/baremetal/target/RDV3/include/platform_override_fvp.h b/pal/baremetal/target/RDV3/include/platform_override_fvp.h index 9287fabb..c4f19500 100644 --- a/pal/baremetal/target/RDV3/include/platform_override_fvp.h +++ b/pal/baremetal/target/RDV3/include/platform_override_fvp.h @@ -1424,8 +1424,10 @@ #define PLATFORM_PCC_SUBSPACE0_INDEX 0x0 #define PLATFORM_PCC_SUBSPACE0_TYPE 0x3 #define PLATFORM_PCC_SUBSPACE0_BASE 0x0 +#define PLATFORM_PCC_SUBSPACE0_MEMORY_LENGTH 0x20 #define PLATFORM_PCC_SUBSPACE0_DOORBELL_PRESERVE 0x0 #define PLATFORM_PCC_SUBSPACE0_DOORBELL_WRITE 0x0 +#define PLATFORM_PCC_SUBSPACE0_NOMINAL_LATENCY 0x0 #define PLATFORM_PCC_SUBSPACE0_MIN_REQ_TURN_TIME 0x0 #define PLATFORM_PCC_SUBSPACE0_CMD_COMPLETE_CHK_MASK 0x0 #define PLATFORM_PCC_SUBSPACE0_CMD_UPDATE_PRESERVE 0x0 diff --git a/pal/baremetal/target/RDV3/include/platform_override_struct.h b/pal/baremetal/target/RDV3/include/platform_override_struct.h index 88f65083..ec9192ab 100644 --- a/pal/baremetal/target/RDV3/include/platform_override_struct.h +++ b/pal/baremetal/target/RDV3/include/platform_override_struct.h @@ -17,6 +17,7 @@ #include #include "acs_stdint.h" +#include "pal_common_support.h" #include "platform_override_fvp.h" #define MAX_CS_COMP_LENGTH 256 @@ -451,22 +452,13 @@ typedef struct { /* Platform Communication Channel (PCC) info table */ -#ifndef GAS_STRUCT -#define GAS_STRUCT -typedef struct { - uint8_t addr_space_id; - uint8_t reg_bit_width; - uint8_t reg_bit_offset; - uint8_t access_size; - uint64_t addr; -} GENERIC_ADDRESS_STRUCTURE; -#endif - typedef struct { uint64_t base_addr; /* base addr of shared mem-region */ + uint32_t memory_length; /* length of shared mem-region */ GENERIC_ADDRESS_STRUCTURE doorbell_reg; /* doorbell register */ uint64_t doorbell_preserve; /* doorbell register preserve mask */ uint64_t doorbell_write; /* doorbell register set mask */ + uint32_t nominal_latency_usec; /* expected command latency */ uint32_t min_req_turnaround_usec; /* minimum request turnaround time */ GENERIC_ADDRESS_STRUCTURE cmd_complete_chk_reg; /* command complete check register */ uint64_t cmd_complete_chk_mask; /* command complete check mask */ diff --git a/pal/baremetal/target/RDV3/src/platform_cfg_fvp.c b/pal/baremetal/target/RDV3/src/platform_cfg_fvp.c index fd555b0c..a73e7770 100644 --- a/pal/baremetal/target/RDV3/src/platform_cfg_fvp.c +++ b/pal/baremetal/target/RDV3/src/platform_cfg_fvp.c @@ -1523,12 +1523,16 @@ const PLATFORM_OVERRIDE_PCC_INFO_TABLE platform_pcc_cfg = { .pcc_info[0].type_spec_info.pcc_ss_type_3.base_addr = PLATFORM_PCC_SUBSPACE0_BASE, + .pcc_info[0].type_spec_info.pcc_ss_type_3.memory_length + = PLATFORM_PCC_SUBSPACE0_MEMORY_LENGTH, .pcc_info[0].type_spec_info.pcc_ss_type_3.doorbell_reg = PLATFORM_PCC_SUBSPACE0_DOORBELL_REG, .pcc_info[0].type_spec_info.pcc_ss_type_3.doorbell_preserve = PLATFORM_PCC_SUBSPACE0_DOORBELL_PRESERVE, .pcc_info[0].type_spec_info.pcc_ss_type_3.doorbell_write = PLATFORM_PCC_SUBSPACE0_DOORBELL_WRITE, + .pcc_info[0].type_spec_info.pcc_ss_type_3.nominal_latency_usec + = PLATFORM_PCC_SUBSPACE0_NOMINAL_LATENCY, .pcc_info[0].type_spec_info.pcc_ss_type_3.min_req_turnaround_usec = PLATFORM_PCC_SUBSPACE0_MIN_REQ_TURN_TIME, .pcc_info[0].type_spec_info.pcc_ss_type_3.cmd_complete_chk_reg diff --git a/pal/baremetal/target/RDV3CFG1/include/platform_override_fvp.h b/pal/baremetal/target/RDV3CFG1/include/platform_override_fvp.h index 78d946bc..a58d391a 100644 --- a/pal/baremetal/target/RDV3CFG1/include/platform_override_fvp.h +++ b/pal/baremetal/target/RDV3CFG1/include/platform_override_fvp.h @@ -1250,8 +1250,10 @@ #define PLATFORM_PCC_SUBSPACE0_INDEX 0x0 #define PLATFORM_PCC_SUBSPACE0_TYPE 0x3 #define PLATFORM_PCC_SUBSPACE0_BASE 0x0 +#define PLATFORM_PCC_SUBSPACE0_MEMORY_LENGTH 0x20 #define PLATFORM_PCC_SUBSPACE0_DOORBELL_PRESERVE 0x0 #define PLATFORM_PCC_SUBSPACE0_DOORBELL_WRITE 0x0 +#define PLATFORM_PCC_SUBSPACE0_NOMINAL_LATENCY 0x0 #define PLATFORM_PCC_SUBSPACE0_MIN_REQ_TURN_TIME 0x0 #define PLATFORM_PCC_SUBSPACE0_CMD_COMPLETE_CHK_MASK 0x0 #define PLATFORM_PCC_SUBSPACE0_CMD_UPDATE_PRESERVE 0x0 diff --git a/pal/baremetal/target/RDV3CFG1/include/platform_override_struct.h b/pal/baremetal/target/RDV3CFG1/include/platform_override_struct.h index 88f65083..ec9192ab 100644 --- a/pal/baremetal/target/RDV3CFG1/include/platform_override_struct.h +++ b/pal/baremetal/target/RDV3CFG1/include/platform_override_struct.h @@ -17,6 +17,7 @@ #include #include "acs_stdint.h" +#include "pal_common_support.h" #include "platform_override_fvp.h" #define MAX_CS_COMP_LENGTH 256 @@ -451,22 +452,13 @@ typedef struct { /* Platform Communication Channel (PCC) info table */ -#ifndef GAS_STRUCT -#define GAS_STRUCT -typedef struct { - uint8_t addr_space_id; - uint8_t reg_bit_width; - uint8_t reg_bit_offset; - uint8_t access_size; - uint64_t addr; -} GENERIC_ADDRESS_STRUCTURE; -#endif - typedef struct { uint64_t base_addr; /* base addr of shared mem-region */ + uint32_t memory_length; /* length of shared mem-region */ GENERIC_ADDRESS_STRUCTURE doorbell_reg; /* doorbell register */ uint64_t doorbell_preserve; /* doorbell register preserve mask */ uint64_t doorbell_write; /* doorbell register set mask */ + uint32_t nominal_latency_usec; /* expected command latency */ uint32_t min_req_turnaround_usec; /* minimum request turnaround time */ GENERIC_ADDRESS_STRUCTURE cmd_complete_chk_reg; /* command complete check register */ uint64_t cmd_complete_chk_mask; /* command complete check mask */ diff --git a/pal/baremetal/target/RDV3CFG1/src/platform_cfg_fvp.c b/pal/baremetal/target/RDV3CFG1/src/platform_cfg_fvp.c index d9d2f7aa..fa9ab17d 100644 --- a/pal/baremetal/target/RDV3CFG1/src/platform_cfg_fvp.c +++ b/pal/baremetal/target/RDV3CFG1/src/platform_cfg_fvp.c @@ -1336,12 +1336,16 @@ const PLATFORM_OVERRIDE_PCC_INFO_TABLE platform_pcc_cfg = { .pcc_info[0].type_spec_info.pcc_ss_type_3.base_addr = PLATFORM_PCC_SUBSPACE0_BASE, + .pcc_info[0].type_spec_info.pcc_ss_type_3.memory_length + = PLATFORM_PCC_SUBSPACE0_MEMORY_LENGTH, .pcc_info[0].type_spec_info.pcc_ss_type_3.doorbell_reg = PLATFORM_PCC_SUBSPACE0_DOORBELL_REG, .pcc_info[0].type_spec_info.pcc_ss_type_3.doorbell_preserve = PLATFORM_PCC_SUBSPACE0_DOORBELL_PRESERVE, .pcc_info[0].type_spec_info.pcc_ss_type_3.doorbell_write = PLATFORM_PCC_SUBSPACE0_DOORBELL_WRITE, + .pcc_info[0].type_spec_info.pcc_ss_type_3.nominal_latency_usec + = PLATFORM_PCC_SUBSPACE0_NOMINAL_LATENCY, .pcc_info[0].type_spec_info.pcc_ss_type_3.min_req_turnaround_usec = PLATFORM_PCC_SUBSPACE0_MIN_REQ_TURN_TIME, .pcc_info[0].type_spec_info.pcc_ss_type_3.cmd_complete_chk_reg diff --git a/pal/uefi_acpi/include/pal_uefi.h b/pal/uefi_acpi/include/pal_uefi.h index 97272899..7b78c992 100644 --- a/pal/uefi_acpi/include/pal_uefi.h +++ b/pal/uefi_acpi/include/pal_uefi.h @@ -659,10 +659,12 @@ UINT32 pal_mpam_parse_dsdt_info(MPAM_INFO_TABLE *MpamTable); /* Platform Communication Channel (PCC) info table */ typedef struct { UINT64 base_addr; /* base addr of shared mem-region */ + UINT32 memory_length; /* length of shared mem-region */ EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE doorbell_reg; /* doorbell register */ UINT64 doorbell_preserve; /* doorbell register preserve mask */ UINT64 doorbell_write; /* doorbell register set mask */ + UINT32 nominal_latency_usec; /* expected command latency */ UINT32 min_req_turnaround_usec; /* minimum request turnaround time */ EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE cmd_complete_chk_reg; /* command complete check register */ @@ -711,8 +713,8 @@ typedef struct { typedef struct { UINT32 msc_id; /* Identifier of the MSC */ UINT32 flags; /* Reserved, must be zero */ - UINT32 val; /* value to be written to the register */ UINT32 offset; /* MPAM register offset to write */ + UINT32 val; /* value to be written to the register */ } PCC_MPAM_MSC_WRITE_CMD_PARA; typedef struct { @@ -725,6 +727,8 @@ typedef struct { #define MPAM_MSC_WRITE_CMD_ID 0x5 #define MPAM_PCC_CMD_SUCCESS 0x0 #define RETURN_FAILURE 0xFFFFFFFF +#define PCC_TY3_FLAGS_OFFSET 4 +#define PCC_TY3_LENGTH_OFFSET 8 #define PCC_TY3_CMD_OFFSET 12 #define PCC_TY3_COMM_SPACE 16 diff --git a/pal/uefi_acpi/src/pal_pcc.c b/pal/uefi_acpi/src/pal_pcc.c index 868857cb..ed6ae41d 100644 --- a/pal/uefi_acpi/src/pal_pcc.c +++ b/pal/uefi_acpi/src/pal_pcc.c @@ -63,29 +63,86 @@ VOID pal_pcc_store_info(UINT32 subspace_idx) { EFI_ACPI_6_5_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER *pcct; - EFI_ACPI_6_5_PCCT_SUBSPACE_GENERIC *pcct_subspace, *pcct_end; + EFI_ACPI_6_5_PCCT_SUBSPACE_HEADER *pcct_subspace; EFI_ACPI_6_5_PCCT_SUBSPACE_3_EXTENDED_PCC *pcct_type_3; PCC_SUBSPACE_TYPE_3 *ptr_to_pcc_ss_type_3; + UINT8 *pcct_end; + UINTN remaining; UINT32 index = 0; /* get pointer to PCCT ACPI table*/ pcct = (EFI_ACPI_6_5_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER *) pal_get_acpi_table_ptr(EFI_ACPI_6_5_PLATFORM_COMMUNICATIONS_CHANNEL_TABLE_SIGNATURE); + if (pcct == NULL) { + pal_print_msg(ACS_PRINT_ERR, "\n PCC: PCCT table not found", 0); + return; + } + + /* Multiple MSCs may reference the same PCCT subspace. Store it only once. */ + while (index < g_pcc_info_table->subspace_cnt) { + if (g_pcc_info_table->pcc_info[index].subspace_idx == subspace_idx) { + pal_print_msg(ACS_PRINT_DEBUG, + "\n PCC: subspace=%u is already stored", subspace_idx); + return; + } + index++; + } + index = 0; + + pal_print_msg(ACS_PRINT_DEBUG, "\n PCC: parse PCCT=0x%llx length=0x%x requested subspace=%u", + (UINT64)pcct, pcct->Header.Length, subspace_idx); + + /* The table length must include the fixed header before subspaces can be walked. */ + if (pcct->Header.Length < + sizeof(EFI_ACPI_6_5_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER)) { + pal_print_msg(ACS_PRINT_ERR, "\n PCC: PCCT length 0x%x is smaller than its header", + pcct->Header.Length); + return; + } /* pointer to start of PCC subspace structure entries */ - pcct_subspace = ADD_PTR(EFI_ACPI_6_5_PCCT_SUBSPACE_GENERIC, pcct, + pcct_subspace = ADD_PTR(EFI_ACPI_6_5_PCCT_SUBSPACE_HEADER, pcct, sizeof(EFI_ACPI_6_5_PLATFORM_COMMUNICATION_CHANNEL_TABLE_HEADER)); - pcct_end = ADD_PTR(EFI_ACPI_6_5_PCCT_SUBSPACE_GENERIC, pcct, - pcct->Header.Length); - while (pcct_subspace < pcct_end) { + pcct_end = ADD_PTR(UINT8, pcct, pcct->Header.Length); + + while ((UINT8 *)pcct_subspace < pcct_end) { + remaining = (UINTN)(pcct_end - (UINT8 *)pcct_subspace); + + /* Ensure Type and Length are both within the declared PCCT table. */ + if (remaining < sizeof(EFI_ACPI_6_5_PCCT_SUBSPACE_HEADER)) { + pal_print_msg(ACS_PRINT_ERR, + "\n PCC: truncated PCCT subspace header at index %u", + index); + return; + } + + /* Reject lengths that cannot make progress or that overrun the table. */ + if ((pcct_subspace->Length < sizeof(EFI_ACPI_6_5_PCCT_SUBSPACE_HEADER)) || + (pcct_subspace->Length > remaining)) { + pal_print_msg(ACS_PRINT_ERR, + "\n PCC: invalid PCCT subspace length 0x%x at index %u", + pcct_subspace->Length, index); + return; + } + if (index == subspace_idx) { - /* this API only supports parsing of type 3 PCC structure info */ + /* MPAM firmware-based communication uses a Type 3 Extended PCC subspace. */ if (pcct_subspace->Type != EFI_ACPI_6_5_PCCT_SUBSPACE_TYPE_3_EXTENDED_PCC) { pal_print_msg(ACS_PRINT_ERR, "\n %a API doesn't support PCC structure type : 0x%x", __func__, pcct_subspace->Type); + return; + } + + /* All fields copied below must be present in the selected Type 3 entry. */ + if (pcct_subspace->Length < + sizeof(EFI_ACPI_6_5_PCCT_SUBSPACE_3_EXTENDED_PCC)) { + pal_print_msg(ACS_PRINT_ERR, + "\n PCC: Type 3 subspace at index %u is truncated", + index); + return; } /* parse PCC structure type 3 */ @@ -103,9 +160,12 @@ pal_pcc_store_info(UINT32 subspace_idx) = pcct_type_3->CommandCompleteUpdateRegister; ptr_to_pcc_ss_type_3->cmd_complete_update_preserve = pcct_type_3->CommandCompleteUpdatePreserve; + ptr_to_pcc_ss_type_3->nominal_latency_usec + = pcct_type_3->NominalLatency; ptr_to_pcc_ss_type_3->min_req_turnaround_usec = pcct_type_3->MinimumRequestTurnaroundTime; ptr_to_pcc_ss_type_3->base_addr = pcct_type_3->BaseAddress; + ptr_to_pcc_ss_type_3->memory_length = pcct_type_3->AddressLength; ptr_to_pcc_ss_type_3->doorbell_preserve = pcct_type_3->DoorbellPreserve; ptr_to_pcc_ss_type_3->doorbell_write = pcct_type_3->DoorbellWrite; ptr_to_pcc_ss_type_3->cmd_complete_chk_mask = pcct_type_3->CommandCompleteCheckMask; @@ -115,7 +175,7 @@ pal_pcc_store_info(UINT32 subspace_idx) break; } /* point to next PCC subspace entry */ - pcct_subspace = ADD_PTR(EFI_ACPI_6_5_PCCT_SUBSPACE_GENERIC, pcct_subspace, + pcct_subspace = ADD_PTR(EFI_ACPI_6_5_PCCT_SUBSPACE_HEADER, pcct_subspace, pcct_subspace->Length); index++; } diff --git a/test_pool/mpam/partition001.c b/test_pool/mpam/partition001.c index 8c57d2fe..c1770ef7 100644 --- a/test_pool/mpam/partition001.c +++ b/test_pool/mpam/partition001.c @@ -220,6 +220,13 @@ static void payload(void) end_count = val_mpam_read_csumon(msc_index); val_print(DEBUG, "\n End Count = 0x%lx", end_count); + /* A partitioning result is not valid unless the monitor observed traffic. */ + if (end_count <= start_count) { + val_print(ERROR, "\n CSU monitor did not advance for msc_index : %d", + msc_index); + test_fail++; + } + /* Disable CSU MON */ val_mpam_csumon_disable(msc_index); diff --git a/test_pool/mpam/partition002.c b/test_pool/mpam/partition002.c index a0fb8e82..530d5c8b 100644 --- a/test_pool/mpam/partition002.c +++ b/test_pool/mpam/partition002.c @@ -221,6 +221,13 @@ static void payload(void) end_count = val_mpam_read_csumon(msc_index); val_print(DEBUG, "\n End Count = 0x%lx", end_count); + /* A partitioning result is not valid unless the monitor observed traffic. */ + if (end_count <= start_count) { + val_print(ERROR, "\n CSU monitor did not advance for msc_index : %d", + msc_index); + test_fail++; + } + /* Disable CSU MON */ val_mpam_csumon_disable(msc_index); diff --git a/test_pool/mpam/partition003.c b/test_pool/mpam/partition003.c index a342db2b..8bd5ce87 100644 --- a/test_pool/mpam/partition003.c +++ b/test_pool/mpam/partition003.c @@ -226,6 +226,13 @@ static void payload(void) end_count = val_mpam_read_csumon(msc_index); val_print(DEBUG, "\n End Count = 0x%lx", end_count); + /* A partitioning result is not valid unless the monitor observed traffic. */ + if (end_count <= start_count) { + val_print(ERROR, "\n CSU monitor did not advance for msc_index : %d", + msc_index); + test_fail++; + } + /* Disable CSU MON */ val_mpam_csumon_disable(msc_index); diff --git a/val/include/pal_interface.h b/val/include/pal_interface.h index 3f4f5f63..d41ba8b2 100644 --- a/val/include/pal_interface.h +++ b/val/include/pal_interface.h @@ -1207,6 +1207,8 @@ void *pal_mem_alloc_at_address(uint64_t mem_base, uint64_t size); void pal_mem_free_at_address(uint64_t mem_base, uint64_t size); /* Platform Communication Channel (PCC) info table */ +/* Set 1B alignment to prevent padding before the address field */ +#pragma pack(push, 1) typedef struct { uint8_t addr_space_id; uint8_t reg_bit_width; @@ -1214,12 +1216,15 @@ typedef struct { uint8_t access_size; uint64_t addr; } ACPI_GENERIC_ADDRESS_STRUCTURE; +#pragma pack(pop) typedef struct { uint64_t base_addr; /* base addr of shared mem-region */ + uint32_t memory_length; /* length of shared mem-region */ ACPI_GENERIC_ADDRESS_STRUCTURE doorbell_reg; /* doorbell register */ uint64_t doorbell_preserve; /* doorbell register preserve mask */ uint64_t doorbell_write; /* doorbell register set mask */ + uint32_t nominal_latency_usec; /* expected command latency */ uint32_t min_req_turnaround_usec; /* minimum request turnaround time */ ACPI_GENERIC_ADDRESS_STRUCTURE cmd_complete_chk_reg; /* command complete check register */ uint64_t cmd_complete_chk_mask; /* command complete check mask */ @@ -1267,23 +1272,53 @@ typedef struct { typedef struct { uint32_t msc_id; /* Identifier of the MSC */ uint32_t flags; /* Reserved, must be zero */ - uint32_t val; /* value to be written to the register */ uint32_t offset; /* MPAM register offset to write */ + uint32_t val; /* value to be written to the register */ } PCC_MPAM_MSC_WRITE_CMD_PARA; typedef struct { int32_t status; /* command response status code */ } PCC_MPAM_MSC_WRITE_RESP_PARA; -#define MPAM_FB_PROTOCOL_ID 0x1A -#define MPAM_MSG_TYPE_CMD 0x0 -#define MPAM_MSC_READ_CMD_ID 0x4 -#define MPAM_MSC_WRITE_CMD_ID 0x5 -#define MPAM_PCC_CMD_SUCCESS 0x0 -#define MPAM_PCC_SAFE_RETURN 0x0 -#define RETURN_FAILURE 0xFFFFFFFF -#define PCC_TY3_CMD_OFFSET 12 -#define PCC_TY3_COMM_SPACE 16 +typedef union { + PCC_MPAM_MSC_READ_CMD_PARA read; + PCC_MPAM_MSC_WRITE_CMD_PARA write; +} MPAM_FB_CMD_PAYLOAD; + +#define MPAM_FB_PROTOCOL_ID 0x1A +#define MPAM_MSG_TYPE_CMD 0x0 +#define MPAM_MSC_READ_CMD_ID 0x4 +#define MPAM_MSC_WRITE_CMD_ID 0x5 +#define MPAM_PCC_CMD_SUCCESS 0x0 +#define MPAM_PCC_SAFE_RETURN 0x0 +#define MPAM_FB_MESSAGE_ID_SHIFT 0U +#define MPAM_FB_MESSAGE_ID_MASK 0xFFU +#define MPAM_FB_MESSAGE_TYPE_SHIFT 8U +#define MPAM_FB_MESSAGE_TYPE_MASK 0x3U +#define MPAM_FB_PROTOCOL_ID_SHIFT 10U +#define MPAM_FB_PROTOCOL_ID_MASK 0xFFU +#define MPAM_FB_TOKEN_SHIFT 18U +#define MPAM_FB_TOKEN_MASK 0x3FFU +#define RETURN_FAILURE 0xFFFFFFFF + +/* ACPI Generic Address Structure encodings used by PCC registers. */ +#define PCC_GAS_SYSTEM_MEMORY 0x00U +#define PCC_GAS_ACCESS_SIZE_MIN 1U +#define PCC_GAS_ACCESS_SIZE_MAX 4U +#define PCC_BITS_PER_BYTE 8U + +/* PCC Type 3 shared-memory transport layout. */ +#define PCC_TY3_FLAGS_OFFSET 4U +#define PCC_TY3_LENGTH_OFFSET 8U +#define PCC_TY3_CMD_OFFSET 12U +#define PCC_TY3_COMM_SPACE 16U +#define PCC_SCMI_TRANSPORT_FLAGS 0U +#define PCC_SCMI_HEADER_SIZE ((uint32_t)sizeof(uint32_t)) + +/* VAL polling policy for PCC channel ownership and command completion. */ +#define PCC_COMMAND_TIMEOUT_USEC 100000U +#define PCC_POLL_INTERVAL_USEC 1000U +#define PCC_STATUS_SUCCESS 0U void pal_pcc_create_info_table(PCC_INFO_TABLE *PccInfoTable); void pal_pcc_store_info(uint32_t subspace_idx); diff --git a/val/include/val_interface.h b/val/include/val_interface.h index d2103efe..a4e8689d 100644 --- a/val/include/val_interface.h +++ b/val/include/val_interface.h @@ -693,6 +693,7 @@ uint32_t val_pcbsa_tpm2_execute_tests(uint32_t level, uint32_t num_pe); void val_pcc_create_info_table(uint64_t *pcc_info_table); void *val_pcc_cmd_response(uint32_t subspace_id, uint32_t command, void *data, uint32_t data_size); uint32_t val_pcc_get_ss_info_idx(uint32_t subspace_id); +uint32_t val_pcc_map_registers(uint32_t subspace_id); void val_pcc_free_info_table(void); typedef enum { diff --git a/val/src/acs_mpam.c b/val/src/acs_mpam.c index 2447f628..1a727431 100644 --- a/val/src/acs_mpam.c +++ b/val/src/acs_mpam.c @@ -987,12 +987,24 @@ void memory_map_msc(void) { uint32_t msc_index; + uint32_t intrf_type; uint64_t msc_base; uint32_t msc_node_cnt = val_mpam_get_msc_count(); for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { - msc_base = val_mpam_get_info(MPAM_MSC_BASE_ADDR, msc_index, 0); - val_mmu_update_entry(msc_base, MPAM_MSC_REGISTER_SPACE, DEVICE_nGnRnE); + msc_base = val_mpam_get_info(MPAM_MSC_BASE_ADDR, msc_index, 0); + intrf_type = val_mpam_get_info(MPAM_MSC_INTERFACE_TYPE, msc_index, 0); + + /* If interface is MMIO, make sure the MSC registers are mapped in PE MMU */ + if (intrf_type == MPAM_INTERFACE_TYPE_MMIO) { + val_mmu_update_entry(msc_base, MPAM_MSC_REGISTER_SPACE, DEVICE_nGnRnE); + + /* If interface is PCC, make sure the Doorbell registers, etc. are mapped */ + } else if (intrf_type == MPAM_INTERFACE_TYPE_PCC) { + if (val_pcc_map_registers((uint32_t)msc_base)) + val_print(ERROR, "\n Failed to map PCC registers for MPAM MSC index %u", + msc_index); + } } return; @@ -1022,7 +1034,6 @@ val_mpam_create_info_table(uint64_t *mpam_info_table) "\n MPAM_INFO: Number of MSC nodes : %d", g_mpam_info_table->msc_count); val_print(DEBUG, "\n Memory mapping MSC nodes"); - /* TODO - Check if MSC memory mapping requires a flag/ cmdline option */ memory_map_msc(); #endif } @@ -1604,8 +1615,10 @@ val_mpam_configure_csu_mon(uint32_t msc_index, uint16_t partid, uint8_t pmg, uin /*Disable the monitor */ val_mpam_csumon_disable(msc_index); - /* Configure the CSU monitor control register to match input PARTID & PMG */ - data = BITFIELD_SET(CSU_CTL_MATCH_PARTID, 1) | BITFIELD_SET(CSU_CTL_MATCH_PMG, 1); + /* Preserve the monitor TYPE/SUBTYPE and other implementation-defined fields. */ + data = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_CSU_CTL); + data = BITFIELD_WRITE(data, CSU_CTL_MATCH_PARTID, 1); + data = BITFIELD_WRITE(data, CSU_CTL_MATCH_PMG, 1); val_mpam_mmr_write(msc_index, REG_MSMON_CFG_CSU_CTL, data); /* Reset CSU Monitor Value */ @@ -1708,7 +1721,7 @@ val_mpam_mmr_read(uint32_t msc_index, uint32_t reg_offset) return value; } else if (intrf_type == MPAM_INTERFACE_TYPE_PCC) { value = val_mpam_pcc_read(msc_index, reg_offset); - MPAM_PRINT_REG("Read", reg_offset, value); + MPAM_PRINT_REG("Read PCC", reg_offset, value); return value; } else { val_print(ERROR, @@ -1741,11 +1754,15 @@ val_mpam_mmr_read64(uint32_t msc_index, uint32_t reg_offset) MPAM_PRINT_REG("Read", reg_offset, value); return value; } else if (intrf_type == MPAM_INTERFACE_TYPE_PCC) { - /* PCC supports only supports 32 bit read at a time, hence reading twice - and concating */ - value = ((uint64_t)val_mpam_pcc_read(msc_index, reg_offset + 4) << 32) - | val_mpam_pcc_read(msc_index, reg_offset); - MPAM_PRINT_REG("Read", reg_offset, value); + uint32_t value_low; + uint32_t value_high; + + /* The MPAM Fb protocol transfers one 32-bit register + value per command. Read both halves for a 64-bit register. */ + value_low = val_mpam_pcc_read(msc_index, reg_offset); + value_high = val_mpam_pcc_read(msc_index, reg_offset + sizeof(uint32_t)); + value = ((uint64_t)value_high << 32) | value_low; + MPAM_PRINT_REG("Read PCC", reg_offset, value); return value; } else { val_print(ERROR, @@ -1778,7 +1795,7 @@ val_mpam_mmr_write(uint32_t msc_index, uint32_t reg_offset, uint32_t data) MPAM_PRINT_REG("Write", reg_offset, data); } else if (intrf_type == MPAM_INTERFACE_TYPE_PCC) { val_mpam_pcc_write(msc_index, reg_offset, data); - MPAM_PRINT_REG("Write", reg_offset, data); + MPAM_PRINT_REG("Write PCC", reg_offset, data); } else { val_print(ERROR, "\n Invalid interface type reported for MPAM MSC index = %x", msc_index); @@ -1809,9 +1826,12 @@ val_mpam_mmr_write64(uint32_t msc_index, uint32_t reg_offset, uint64_t data) val_mmio_write64(base_addr + reg_offset, data); MPAM_PRINT_REG("Write", reg_offset, data); } else if (intrf_type == MPAM_INTERFACE_TYPE_PCC) { - val_mpam_pcc_write(msc_index, reg_offset, (uint32_t)(data & 0xFFFFFFFF)); - val_mpam_pcc_write(msc_index, reg_offset + 4, (uint32_t)(data >> 32)); - MPAM_PRINT_REG("Write", reg_offset, data); + /* The MPAM firmware-based protocol transfers one 32-bit register + value per command. Write both halves for a 64-bit register. */ + val_mpam_pcc_write(msc_index, reg_offset, (uint32_t)data); + val_mpam_pcc_write(msc_index, reg_offset + sizeof(uint32_t), + (uint32_t)(data >> 32)); + MPAM_PRINT_REG("Write PCC", reg_offset, data); } else { val_print(ERROR, "\n Invalid interface type reported for MPAM MSC index = %x", msc_index); @@ -1819,6 +1839,64 @@ val_mpam_mmr_write64(uint32_t msc_index, uint32_t reg_offset, uint64_t data) val_mem_issue_dsb(); } +/** + @brief Encode an MPAM firmware-based protocol message header. + + @param message_id - Message identifier. + @param message_type - Message type. + @param protocol_id - Protocol identifier. + @param token - Caller-defined transaction token. + + @return Encoded 32-bit MPAM firmware-based protocol message header. +**/ +static uint32_t +val_mpam_fb_header(uint32_t message_id, uint32_t message_type, + uint32_t protocol_id, uint32_t token) +{ + return BITFIELD_SET(MPAM_FB_MESSAGE_ID, message_id) + | BITFIELD_SET(MPAM_FB_MESSAGE_TYPE, message_type) + | BITFIELD_SET(MPAM_FB_PROTOCOL_ID, protocol_id) + | BITFIELD_SET(MPAM_FB_TOKEN, token); +} + +/** + @brief Construct an MPAM firmware-based protocol command payload. + + @param message_id - Message identifier that selects the payload format. + @param msc_id - Identifier of the target MSC. + @param reg_offset - MPAM register offset. + @param data - Register value for commands that carry write data. + + @return Constructed command payload. +**/ +static MPAM_FB_CMD_PAYLOAD +val_mpam_fb_payload(uint32_t message_id, uint32_t msc_id, + uint32_t reg_offset, uint32_t data) +{ + MPAM_FB_CMD_PAYLOAD payload = {0}; + + switch (message_id) { + case MPAM_MSC_READ_CMD_ID: + payload.read.msc_id = msc_id; + payload.read.flags = 0U; + payload.read.offset = reg_offset; + break; + + case MPAM_MSC_WRITE_CMD_ID: + payload.write.msc_id = msc_id; + payload.write.flags = 0U; + payload.write.offset = reg_offset; + payload.write.val = data; + break; + + default: + val_print(ERROR, "\n Unsupported MPAM firmware-based message ID: 0x%x", + message_id); + } + + return payload; +} + /** @brief This API constructs header and parameter for the MPAM_MSC_READ PCC command and calls doorbell protocol. @@ -1831,30 +1909,31 @@ val_mpam_mmr_write64(uint32_t msc_index, uint32_t reg_offset, uint64_t data) uint32_t val_mpam_pcc_read(uint32_t msc_index, uint32_t reg_offset) { - SCMI_PROTOCOL_MESSAGE_HEADER header; - PCC_MPAM_MSC_READ_CMD_PARA parameter; - PCC_MPAM_MSC_READ_RESP_PARA *response; + uint32_t header; + uint32_t msc_id; uint32_t subspace_id; + MPAM_FB_CMD_PAYLOAD payload; + PCC_MPAM_MSC_READ_RESP_PARA *response; /* if MSC interface type is PCC (0x0A), the Base address field captures index to PCCT ACPI structure */ subspace_id = (uint32_t)val_mpam_get_info(MPAM_MSC_BASE_ADDR, msc_index, 0); - /* construct the message header */ - header.reserved = 0; - header.protocol_id = MPAM_FB_PROTOCOL_ID; - header.message_type = MPAM_MSG_TYPE_CMD; - header.message_id = MPAM_MSC_READ_CMD_ID; - /* token is user defined value for book keeping */ - header.token = 1; + /* Construct the MPAM Fb protocol header; token is caller-defined. */ + header = val_mpam_fb_header(MPAM_MSC_READ_CMD_ID, MPAM_MSG_TYPE_CMD, + MPAM_FB_PROTOCOL_ID, 1U); + + /* Construct the MPAM Fb protocol payload with msc_id as input */ + msc_id = (uint32_t)val_mpam_get_info(MPAM_MSC_ID, msc_index, 0); + payload = val_mpam_fb_payload(MPAM_MSC_READ_CMD_ID, msc_id, reg_offset, 0U); - /* construct parameter payload */ - parameter.msc_id = val_mpam_get_info(MPAM_MSC_ID, msc_index, 0); - parameter.flags = 0; - parameter.offset = reg_offset; + val_print(TRACE, + "\n MPAM PCC read: msc_id=0x%x subspace=%u offset=0x%x header=0x%x", + payload.read.msc_id, subspace_id, payload.read.offset, header); + /* Submit the header and payload to the PCC channel and get the response back from the platform */ response = (PCC_MPAM_MSC_READ_RESP_PARA *) val_pcc_cmd_response( - (uint32_t)subspace_id, *(uint32_t *)&header, (void *)¶meter, sizeof(parameter)); + (uint32_t)subspace_id, header, (void *)&payload.read, sizeof(payload.read)); if (response == NULL || response->status != MPAM_PCC_CMD_SUCCESS) { val_print(ERROR, @@ -1881,35 +1960,35 @@ val_mpam_pcc_read(uint32_t msc_index, uint32_t reg_offset) void val_mpam_pcc_write(uint32_t msc_index, uint32_t reg_offset, uint32_t data) { - SCMI_PROTOCOL_MESSAGE_HEADER header; - PCC_MPAM_MSC_WRITE_CMD_PARA parameter; - PCC_MPAM_MSC_WRITE_RESP_PARA *response; + uint32_t header; + uint32_t msc_id; uint32_t subspace_id; + MPAM_FB_CMD_PAYLOAD payload; + PCC_MPAM_MSC_WRITE_RESP_PARA *response; /* if MSC interface type is PCC (0x0A), the Base address field captures index to PCCT ACPI structure */ subspace_id = val_mpam_get_info(MPAM_MSC_BASE_ADDR, msc_index, 0); - /* construct the message header */ - header.reserved = 0; - header.protocol_id = MPAM_FB_PROTOCOL_ID; - header.message_type = MPAM_MSG_TYPE_CMD; - header.message_id = MPAM_MSC_WRITE_CMD_ID; - /* token is user defined value for book keeping */ - header.token = 1; + /* Construct the MPAM firmware-based protocol header; token is caller-defined. */ + header = val_mpam_fb_header(MPAM_MSC_WRITE_CMD_ID, MPAM_MSG_TYPE_CMD, + MPAM_FB_PROTOCOL_ID, 1U); - /* construct parameter payload */ - parameter.msc_id = val_mpam_get_info(MPAM_MSC_ID, msc_index, 0); - parameter.flags = 0; - parameter.val = data; - parameter.offset = reg_offset; + /* Construct the MPAM Fb protocol payload with msc_id as input */ + msc_id = (uint32_t)val_mpam_get_info(MPAM_MSC_ID, msc_index, 0); + payload = val_mpam_fb_payload(MPAM_MSC_WRITE_CMD_ID, msc_id, reg_offset, data); + val_print(TRACE, + "\n MPAM PCC write: msc_id=0x%x subspace=%u offset=0x%x value=0x%x header=0x%x", + payload.write.msc_id, subspace_id, payload.write.offset, data, header); + + /* Submit the header and payload to the PCC channel and get the response back from the platform */ response = (PCC_MPAM_MSC_WRITE_RESP_PARA *) val_pcc_cmd_response( - (uint32_t)subspace_id, *(uint32_t *)&header, (void *)¶meter, sizeof(parameter)); + (uint32_t)subspace_id, header, (void *)&payload.write, sizeof(payload.write)); if (response == NULL || response->status != MPAM_PCC_CMD_SUCCESS) { val_print(ERROR, - "\n Failed to read MPAM register with offset (0x%x) via PCC", reg_offset); + "\n Failed to write MPAM register with offset (0x%x) via PCC", reg_offset); val_print(ERROR, " for MSC index = 0x%x", msc_index); if (response != NULL) { val_print(ERROR, "\n PCC command response code = 0x%x", response->status); diff --git a/val/src/acs_pcc.c b/val/src/acs_pcc.c index a26b6288..3aa04fd0 100644 --- a/val/src/acs_pcc.c +++ b/val/src/acs_pcc.c @@ -19,179 +19,425 @@ #include "acs_val.h" #include "acs_common.h" +#include "acs_memory.h" +#include "acs_mmu.h" #include "val_interface.h" static PCC_INFO_TABLE *g_pcc_info_table; -/* PCCT related APIs */ +/* Doorbell registers, Command complete update/check registers are represented in GAS format. + Each GAS format describes it's own access_size. Perform read/write based on the access_size */ +static uint64_t +val_pcc_read_gas_register(const ACPI_GENERIC_ADDRESS_STRUCTURE *gas) +{ + if (gas->access_size == 1U) + return val_mmio_read8(gas->addr); + + if (gas->access_size == 2U) + return val_mmio_read16(gas->addr); + + if ((gas->access_size == 4U) || + ((gas->access_size == 0U) && + ((gas->reg_bit_offset + gas->reg_bit_width) > 32U))) + return val_mmio_read64(gas->addr); + + return val_mmio_read(gas->addr); +} + +static void +val_pcc_write_gas_register(const ACPI_GENERIC_ADDRESS_STRUCTURE *gas, uint64_t value) +{ + if (gas->access_size == 1U) { + val_mmio_write8(gas->addr, (uint8_t)value); + return; + } + + if (gas->access_size == 2U) { + val_mmio_write16(gas->addr, (uint16_t)value); + return; + } + + if ((gas->access_size == 4U) || + ((gas->access_size == 0U) && + ((gas->reg_bit_offset + gas->reg_bit_width) > 32U))) { + val_mmio_write64(gas->addr, value); + return; + } + + val_mmio_write(gas->addr, (uint32_t)value); +} /** - @brief This API will call PAL layer to initialise PCC table information - into the g_pcc_info_table pointer. - 1. Caller - Application layer. - 2. Prerequisite - Memory allocated and passed as argument. - @param pcc_info_table pre-allocated memory pointer for pcc info. - @return None + @brief Map the pages containing a PCC Generic Address Structure register. + + @param name Register name used in diagnostic messages. + @param gas Generic Address Structure describing the register. + + @return PCC_STATUS_SUCCESS on success, RETURN_FAILURE on failure. **/ -void -val_pcc_create_info_table(uint64_t *pcc_info_table) +static uint32_t +val_pcc_map_gas_register(const char *name, const ACPI_GENERIC_ADDRESS_STRUCTURE *gas) { - /* store pointer to pcc info table */ - g_pcc_info_table = (PCC_INFO_TABLE *)pcc_info_table; + uint32_t access_bytes; + uint32_t field_bits; + uint32_t map_size; + uint64_t page_base; + uint64_t page_offset; + + if (gas->addr_space_id != PCC_GAS_SYSTEM_MEMORY) { + val_print(ERROR, "\n PCC: %a GAS uses unsupported address space 0x%x", + name, gas->addr_space_id); + return RETURN_FAILURE; + } + + if (gas->addr == 0U) { + val_print(ERROR, "\n PCC: %a GAS has a null address", name); + return RETURN_FAILURE; + } + + /* GAS AccessSize values 1 through 4 encode byte, word, dword, and qword. */ + field_bits = gas->reg_bit_offset + gas->reg_bit_width; + if ((gas->access_size >= PCC_GAS_ACCESS_SIZE_MIN) && + (gas->access_size <= PCC_GAS_ACCESS_SIZE_MAX)) + access_bytes = 1U << (gas->access_size - PCC_GAS_ACCESS_SIZE_MIN); + else if (gas->access_size == 0U) + access_bytes = (field_bits > 32U) ? sizeof(uint64_t) : sizeof(uint32_t); + else { + val_print(ERROR, "\n PCC: %a GAS has unsupported AccessSize 0x%x", + name, gas->access_size); + return RETURN_FAILURE; + } + + if (field_bits > (access_bytes * PCC_BITS_PER_BYTE)) { + val_print(ERROR, "\n PCC: %a GAS field exceeds its access width", name); + return RETURN_FAILURE; + } + + page_base = gas->addr & ~((uint64_t)SIZE_4KB - 1U); + page_offset = gas->addr - page_base; - pal_pcc_create_info_table(g_pcc_info_table); + /* A GAS register is at most eight bytes, so it can span no more than two pages. */ + map_size = (page_offset + access_bytes > SIZE_4KB) ? (2U * SIZE_4KB) : SIZE_4KB; - return; + val_print(DEBUG, "\n PCC: mapping %a GAS page=0x%llx size=0x%x address=0x%llx", + name, page_base, map_size, gas->addr); + return val_mmu_update_entry(page_base, map_size, DEVICE_nGnRnE); } /** - @brief This API return index to PCC info block in PCC info table for - corresponding subspace id input. + @brief Map the PCC shared communication region as Device memory. - @param subspace_idx - Subspace id, used to index PCCT array. + @param subspace PCC Type 3 subspace describing the communication region. - @return index of the pcc info. + @return PCC_STATUS_SUCCESS on success, RETURN_FAILURE on failure. +**/ +static uint32_t +val_pcc_map_shared_memory(const PCC_SUBSPACE_TYPE_3 *subspace) +{ + uint64_t last_addr; + uint64_t page_addr; + uint64_t last_page; + + if ((subspace->base_addr == 0U) || (subspace->memory_length == 0U)) { + val_print(ERROR, "\n PCC: shared memory has an invalid base or length"); + return RETURN_FAILURE; + } + + /* Check the inclusive end-address calculation before aligning the range. */ + if (subspace->base_addr > (~0ULL - ((uint64_t)subspace->memory_length - 1U))) { + val_print(ERROR, "\n PCC: shared memory range overflows the address space"); + return RETURN_FAILURE; + } + + last_addr = subspace->base_addr + subspace->memory_length - 1U; + page_addr = subspace->base_addr & ~((uint64_t)SIZE_4KB - 1U); + last_page = last_addr & ~((uint64_t)SIZE_4KB - 1U); + + val_print(DEBUG, + "\n PCC: mapping shared memory base=0x%llx length=0x%x", + subspace->base_addr, subspace->memory_length); + + /* Check every page because an earlier mapping may cover only part of the region. */ + while (page_addr <= last_page) { + if (val_mmu_update_entry(page_addr, SIZE_4KB, DEVICE_nGnRnE) != + PCC_STATUS_SUCCESS) { + val_print(ERROR, "\n PCC: failed to map shared memory page 0x%llx", + page_addr); + return RETURN_FAILURE; + } + + if (page_addr == last_page) + break; + + page_addr += SIZE_4KB; + } + + return PCC_STATUS_SUCCESS; +} + +/** + @brief Wait for the platform to return ownership of a PCC channel. + + @param subspace PCC subspace containing the completion register. + @param phase Text used to identify the poll in debug output. + @param initial_delay_usec Delay before the first completion check. + + @return PCC_STATUS_SUCCESS when command complete is set, or + RETURN_FAILURE after the timeout. +**/ +static uint32_t +val_pcc_wait_for_completion(const PCC_SUBSPACE_TYPE_3 *subspace, const char *phase, + uint32_t delay_usec) +{ + uint64_t cmd_complete; + uint64_t cmd_complete_raw; + uint32_t remaining_usec; + uint32_t waited_usec = 0U; + + /* Keep the advisory initial delay within the overall polling timeout. */ + if (delay_usec > PCC_COMMAND_TIMEOUT_USEC) { + delay_usec = PCC_COMMAND_TIMEOUT_USEC; + } + + /* Initial delay before the agent can probe for command completion. */ + if (delay_usec != 0U) { + val_time_delay_ms(delay_usec); + waited_usec = delay_usec; + } + + /* Probe the command complete check register to verify that the platform has processed the command + and agent can read the response back */ + while (waited_usec <= PCC_COMMAND_TIMEOUT_USEC) { + cmd_complete_raw = val_pcc_read_gas_register(&subspace->cmd_complete_chk_reg); + cmd_complete = cmd_complete_raw & subspace->cmd_complete_chk_mask; + val_print(DEBUG, + "\n PCC: %a poll raw=0x%llx masked=0x%llx waited=%u us", + phase, cmd_complete_raw, cmd_complete, waited_usec); + + if (cmd_complete != 0U) { + return PCC_STATUS_SUCCESS; + } + + if (waited_usec >= PCC_COMMAND_TIMEOUT_USEC) { + break; + } + + /* Poll at a fixed interval without extending the overall timeout. */ + remaining_usec = PCC_COMMAND_TIMEOUT_USEC - waited_usec; + delay_usec = (remaining_usec < PCC_POLL_INTERVAL_USEC) ? + remaining_usec : PCC_POLL_INTERVAL_USEC; + val_time_delay_ms(delay_usec); + waited_usec += delay_usec; + } + + return RETURN_FAILURE; +} + +/** + @brief Initialize the PCC information table through PAL. + @param pcc_info_table Caller-allocated storage for the PCC information. +**/ +void +val_pcc_create_info_table(uint64_t *pcc_info_table) +{ + if (pcc_info_table == NULL) { + val_print(ERROR, "\n PCC: cannot create info table from a NULL buffer"); + return; + } + + /* PAL populates the caller-owned buffer with the platform's PCCT data. */ + g_pcc_info_table = (PCC_INFO_TABLE *)pcc_info_table; + pal_pcc_create_info_table(g_pcc_info_table); + + val_print(DEBUG, "\n PCC: info table=0x%llx subspace count=%u", + (uint64_t)g_pcc_info_table, g_pcc_info_table->subspace_cnt); +} + +/** + @brief Find the PCC information-table entry for a subspace ID. + @param subspace_id Subspace ID from the PCCT. + @return Information-table index, or RETURN_FAILURE when not found. **/ uint32_t val_pcc_get_ss_info_idx(uint32_t subspace_id) { - - PCC_INFO *entry; uint32_t i; - entry = g_pcc_info_table->pcc_info; + if (g_pcc_info_table == NULL) { + val_print(ERROR, "\n PCC: info table is NULL while looking up subspace %u", subspace_id); + return RETURN_FAILURE; + } + /* looking up PCC subspace in available subspace entries */ for (i = 0; i < g_pcc_info_table->subspace_cnt; i++) { - if (entry->subspace_idx == subspace_id) { + if (g_pcc_info_table->pcc_info[i].subspace_idx == subspace_id) { + val_print(DEBUG, "\n PCC: subspace=%u found at table index=%u type=%u", + subspace_id, i, g_pcc_info_table->pcc_info[i].subspace_type); return i; } - entry++; } + val_print(ERROR, "\n PCC: subspace=%u is not present in the info table", subspace_id); return RETURN_FAILURE; } /** - @brief This API implements ACPI Doorbell protocol. + @brief Map the shared memory and registers for a PCC subspace. + @param subspace_id Subspace ID used to index the PCCT array. + @return PCC_STATUS_SUCCESS on success, RETURN_FAILURE on failure. +**/ +uint32_t +val_pcc_map_registers(uint32_t subspace_id) +{ + uint32_t pcc_idx; + PCC_SUBSPACE_TYPE_3 *subspace; + + pcc_idx = val_pcc_get_ss_info_idx(subspace_id); + if (pcc_idx == RETURN_FAILURE) { + val_print(ERROR, "\n PCC: cannot map registers for unknown subspace %u", subspace_id); + return RETURN_FAILURE; + } + + subspace = &g_pcc_info_table->pcc_info[pcc_idx].type_spec_info.pcc_ss_type_3; + + if (val_pcc_map_shared_memory(subspace) != PCC_STATUS_SUCCESS) + return RETURN_FAILURE; + + if (val_pcc_map_gas_register("doorbell", &subspace->doorbell_reg) != PCC_STATUS_SUCCESS) + return RETURN_FAILURE; + + if (val_pcc_map_gas_register("command complete check", + &subspace->cmd_complete_chk_reg) != PCC_STATUS_SUCCESS) + return RETURN_FAILURE; + + if (val_pcc_map_gas_register("command complete update", + &subspace->cmd_complete_update_reg) != PCC_STATUS_SUCCESS) + return RETURN_FAILURE; + + return PCC_STATUS_SUCCESS; +} + +/** + @brief Submit a command using the ACPI PCC doorbell protocol. - @param subspace_idx - Subspace id, used to index PCCT array. - @param command - PCC command header - @param data - pointer to data to be written to communication - subspace. - @param data_size - size of data to be written to subspace + @param subspace_id Subspace ID from the PCCT. + @param command Protocol message header. + @param data Payload copied into the shared communication region. + @param data_size Payload size in bytes. - @return pointer to communication subspace with response. + @return Pointer to the response payload in shared memory, or NULL on failure. **/ void *val_pcc_cmd_response(uint32_t subspace_id, uint32_t command, void *data, uint32_t data_size) { - uint32_t pcc_idx; - uint32_t loop_cnt; - uint32_t cmd_complete; uint64_t shared_mem_addr; - uint64_t cmd_complete_upd_reg; + uint64_t response_addr; + uint64_t cmd_complete_update_val; uint64_t doorbell_val; - PCC_SUBSPACE_TYPE_3 *ptr_to_pcc_ss_type_3; + PCC_SUBSPACE_TYPE_3 *subspace; + if ((data == NULL) && (data_size != 0U)) { + val_print(ERROR, "\n PCC: command payload is NULL but its size is %u", data_size); + return NULL; + } - /* get pcc info block index */ + /* Resolve the PCCT subspace ID to its cached Type 3 descriptor. */ pcc_idx = val_pcc_get_ss_info_idx(subspace_id); - - /* return if failed to get index */ if (pcc_idx == RETURN_FAILURE) { return NULL; } - /* pointer to PCC info */ - ptr_to_pcc_ss_type_3 = &(g_pcc_info_table->pcc_info[pcc_idx].type_spec_info.pcc_ss_type_3); + subspace = &g_pcc_info_table->pcc_info[pcc_idx].type_spec_info.pcc_ss_type_3; - /* Note : For information on Doorbell Protocol refer ACPI 6.5 specification; section 14.5 */ + /* Verify that the channel can hold the PCC header and command payload. */ + if ((subspace->memory_length < PCC_TY3_COMM_SPACE) || + (data_size > (subspace->memory_length - PCC_TY3_COMM_SPACE))) { + val_print(ERROR, + "\n PCC: payload size %u exceeds channel memory length %u for subspace %u", + data_size, subspace->memory_length, subspace_id); + return NULL; + } - /* ensuring command complete check is set, indicating shared memory - exclusively owned by OSPM */ - loop_cnt = 3; - do { - /* wait for minimum request turnaround time * 3 to provide time for platform */ - val_time_delay_ms(ptr_to_pcc_ss_type_3->min_req_turnaround_usec); - /* read command complete check register */ - cmd_complete = val_mmio_read(ptr_to_pcc_ss_type_3->cmd_complete_chk_reg.addr) & - ptr_to_pcc_ss_type_3->cmd_complete_chk_mask; - loop_cnt--; - } while (cmd_complete == 0 || loop_cnt != 0); - - /* if platform not setting complete, return with failure */ - if (loop_cnt == 0) { + /* Note : For information on Doorbell Protocol refer ACPI 6.5 specification; section 14.5 */ + /* OSPM checks that there is no command pending completion and the subspace is free to use. + A set completion bit means OSPM owns the shared-memory channel. */ + if (val_pcc_wait_for_completion(subspace, "pre-command", + subspace->min_req_turnaround_usec) != PCC_STATUS_SUCCESS) { val_print(ERROR, - "\n Platform fails to set command complete reg for PCC subspace id : 0x%x", - subspace_id); + "\n PCC: channel remained busy for %u us for subspace id : 0x%x", + PCC_COMMAND_TIMEOUT_USEC, subspace_id); return NULL; } - /* write command and parameters to PCC shared memory region */ - shared_mem_addr = ptr_to_pcc_ss_type_3->base_addr; - /* write command */ + shared_mem_addr = subspace->base_addr; + + /* The OSPM places a command into the shared memory of the subspace + to update the flags, length, command and payload fields */ + /* Flags: 0 = Disable interrupt on command completion */ + val_mmio_write(shared_mem_addr + PCC_TY3_FLAGS_OFFSET, PCC_SCMI_TRANSPORT_FLAGS); + + /* Length: Length of the payload being transmitted including the command */ + val_mmio_write(shared_mem_addr + PCC_TY3_LENGTH_OFFSET, + PCC_SCMI_HEADER_SIZE + data_size); + + /* Command: Command being sent over the subspace. SCMI Header in case of MPAM Fb profile */ val_mmio_write(shared_mem_addr + PCC_TY3_CMD_OFFSET, command); - /* write parameters */ - val_memcpy((void *)(shared_mem_addr + PCC_TY3_COMM_SPACE), data, data_size); - - /* clear command complete indicating platform to process the command - using command complete update register */ - cmd_complete_upd_reg = val_mmio_read(ptr_to_pcc_ss_type_3->cmd_complete_update_reg.addr); - /* modify data as specified in doorbell protocol */ - cmd_complete_upd_reg = - (cmd_complete_upd_reg & ptr_to_pcc_ss_type_3->cmd_complete_update_preserve) - | ptr_to_pcc_ss_type_3->cmd_complete_update_set; - /* write command complete update register to clear the complete bit */ - val_mmio_write(ptr_to_pcc_ss_type_3->cmd_complete_update_reg.addr, cmd_complete_upd_reg); - - /* ring doorbell by performing read/modify/write cycle */ - doorbell_val = val_mmio_read(ptr_to_pcc_ss_type_3->doorbell_reg.addr); - doorbell_val = (doorbell_val & ptr_to_pcc_ss_type_3->doorbell_preserve) - | ptr_to_pcc_ss_type_3->doorbell_write; - val_mmio_write(ptr_to_pcc_ss_type_3->doorbell_reg.addr, doorbell_val); - - /* wait for minimum request turnaround time and poll on the command complete bit for set */ - loop_cnt = 3; - do { - /* wait for minimum request turnaround time * 3 to provide time for platform */ - val_time_delay_ms(ptr_to_pcc_ss_type_3->min_req_turnaround_usec); - /* read command complete check register */ - cmd_complete = val_mmio_read(ptr_to_pcc_ss_type_3->cmd_complete_chk_reg.addr) & - ptr_to_pcc_ss_type_3->cmd_complete_chk_mask; - loop_cnt--; - } while (cmd_complete == 0 || loop_cnt != 0); - - /* if platform not setting complete, return with failure */ - if (loop_cnt == 0) { + + /* Payload: Copy the payload to the shared memory */ + if (data_size != 0U) { + val_memcpy((void *)(shared_mem_addr + PCC_TY3_COMM_SPACE), data, data_size); + } + + /* Make the complete request visible before transferring ownership. */ + val_mem_issue_dsb(); + + /* Clear completion with the platform-provided preserve and set masks. */ + cmd_complete_update_val = val_pcc_read_gas_register(&subspace->cmd_complete_update_reg); + cmd_complete_update_val = + (cmd_complete_update_val & subspace->cmd_complete_update_preserve) | + subspace->cmd_complete_update_set; + val_pcc_write_gas_register(&subspace->cmd_complete_update_reg, cmd_complete_update_val); + + val_print(DEBUG, "\n PCC: complete update write value=0x%llx", + cmd_complete_update_val); + + /* Ring the doorbell with its independent preserve and write masks. */ + doorbell_val = val_pcc_read_gas_register(&subspace->doorbell_reg); + doorbell_val = (doorbell_val & subspace->doorbell_preserve) | + subspace->doorbell_write; + val_pcc_write_gas_register(&subspace->doorbell_reg, doorbell_val); + + val_print(DEBUG, "\n PCC: doorbell write value=0x%llx", doorbell_val); + + /* NominalLatency delays the first check; the ACS timeout remains the limit. */ + if (val_pcc_wait_for_completion(subspace, "post-command", + subspace->nominal_latency_usec) != PCC_STATUS_SUCCESS) { val_print(ERROR, - "\n Platform fails to set command complete, post command for PCC subspace id : 0x%x", - subspace_id); + "\n PCC: command did not complete within %u us for subspace id : 0x%x", + PCC_COMMAND_TIMEOUT_USEC, subspace_id); return NULL; } - /* process response from platform */ - /* return pointer to communication subspace with response data */ - return (void *)(shared_mem_addr + PCC_TY3_COMM_SPACE); + /* Completion transfers ownership back to OSPM; order response reads after it. */ + val_mem_issue_dsb(); + + response_addr = shared_mem_addr + PCC_TY3_COMM_SPACE; + val_print(DEBUG, "\n PCC: command complete response=0x%llx", response_addr); + return (void *)response_addr; } /** - @brief Free the memory allocated for the pcc_info_table - - @param None - - @return None + @brief Free the memory allocated for the PCC information table. **/ void val_pcc_free_info_table(void) { - - if (g_pcc_info_table != NULL) { - pal_mem_free_aligned((void *)g_pcc_info_table); - g_pcc_info_table = NULL; - } - else { + if (g_pcc_info_table != NULL) { + pal_mem_free_aligned((void *)g_pcc_info_table); + g_pcc_info_table = NULL; + } else { val_print(ERROR, - "\n WARNING: g_pcc_info_table pointer is already NULL"); - } + "\n WARNING: g_pcc_info_table pointer is already NULL"); + } }