diff --git a/apps/uefi/Bsa.inf b/apps/uefi/Bsa.inf index 123bbadf..8f07bee3 100644 --- a/apps/uefi/Bsa.inf +++ b/apps/uefi/Bsa.inf @@ -93,6 +93,7 @@ ../../test_pool/peripherals/d002.c ../../test_pool/peripherals/d003.c ../../test_pool/peripherals/d005.c + ../../test_pool/peripherals/d004.c ../../test_pool/memory_map/m001.c ../../test_pool/memory_map/m002.c ../../test_pool/memory_map/m003.c diff --git a/apps/uefi/Sbsa.inf b/apps/uefi/Sbsa.inf index 885d738a..082b463b 100644 --- a/apps/uefi/Sbsa.inf +++ b/apps/uefi/Sbsa.inf @@ -96,6 +96,7 @@ ../../test_pool/peripherals/d002.c ../../test_pool/peripherals/d003.c ../../test_pool/peripherals/d005.c + ../../test_pool/peripherals/d004.c ../../test_pool/memory_map/m001.c ../../test_pool/memory_map/m002.c ../../test_pool/memory_map/m003.c diff --git a/apps/uefi/Vbsa.inf b/apps/uefi/Vbsa.inf index 4ce9b482..2354f79c 100644 --- a/apps/uefi/Vbsa.inf +++ b/apps/uefi/Vbsa.inf @@ -95,6 +95,7 @@ ../../test_pool/peripherals/d002.c ../../test_pool/peripherals/d003.c ../../test_pool/peripherals/d005.c + ../../test_pool/peripherals/d004.c ../../test_pool/memory_map/m001.c ../../test_pool/memory_map/m002.c ../../test_pool/memory_map/m003.c diff --git a/apps/uefi/acs.h b/apps/uefi/acs.h index fbd97972..8a1dd8e8 100644 --- a/apps/uefi/acs.h +++ b/apps/uefi/acs.h @@ -101,6 +101,12 @@ extern char8_t *module_name_string[MODULE_ID_SENTINEL]; /* UEFI-only declarations */ void HelpMsg(VOID); +uint32_t createPeInfoTable(void); +uint32_t createGicInfoTable(void); +uint32_t createRasInfoTable(void); +void createTimerInfoTable(void); +void createWatchdogInfoTable(void); +void createDmaInfoTable(void); void createPcieVirtInfoTable(void); void print_selection_summary(void); void FlushImage(void); diff --git a/apps/uefi/acs_helpers.c b/apps/uefi/acs_helpers.c index 64809f71..e2ef04e6 100644 --- a/apps/uefi/acs_helpers.c +++ b/apps/uefi/acs_helpers.c @@ -1208,6 +1208,16 @@ createCxlInfoTable( val_cxl_create_info_table(CxlInfoTable); } +void +createDmaInfoTable( +) +{ + UINT64 *DmaInfoTable; + + DmaInfoTable = val_aligned_alloc(SIZE_4K, sizeof(DMA_INFO_TABLE) + + sizeof(DMA_INFO_BLOCK)); + val_dma_create_info_table(DmaInfoTable); +} VOID createPeripheralInfoTable( ) diff --git a/apps/uefi/bsa_main.c b/apps/uefi/bsa_main.c index 6f473d91..73fb5f02 100644 --- a/apps/uefi/bsa_main.c +++ b/apps/uefi/bsa_main.c @@ -116,6 +116,7 @@ freeAcsMem() val_pcie_free_info_table(); val_iovirt_free_info_table(); val_peripheral_free_info_table(); + val_dma_free_info_table(); val_smbios_free_info_table(); } @@ -197,6 +198,7 @@ execute_tests() createPcieVirtInfoTable(); createPeripheralInfoTable(); createSmbiosInfoTable(); + createDmaInfoTable(); val_allocate_shared_mem(); FlushImage(); diff --git a/apps/uefi/pc_bsa.inf b/apps/uefi/pc_bsa.inf index 0d2b9cd0..985b56ad 100644 --- a/apps/uefi/pc_bsa.inf +++ b/apps/uefi/pc_bsa.inf @@ -96,6 +96,7 @@ ../../test_pool/peripherals/d002.c ../../test_pool/peripherals/d003.c ../../test_pool/peripherals/d005.c + ../../test_pool/peripherals/d004.c ../../test_pool/memory_map/m001.c ../../test_pool/memory_map/m002.c ../../test_pool/memory_map/m003.c diff --git a/apps/uefi/pc_bsa_main.c b/apps/uefi/pc_bsa_main.c index 22c43d06..0205c14c 100644 --- a/apps/uefi/pc_bsa_main.c +++ b/apps/uefi/pc_bsa_main.c @@ -98,6 +98,7 @@ freeAcsMem() val_pcie_free_info_table(); val_iovirt_free_info_table(); val_peripheral_free_info_table(); + val_dma_free_info_table(); val_tpm2_free_info_table(); val_free_shared_mem(); val_srat_free_info_table(); @@ -181,6 +182,7 @@ execute_tests() createWatchdogInfoTable(); createPcieVirtInfoTable(); createPeripheralInfoTable(); + createDmaInfoTable(); createTpm2InfoTable(); createSratInfoTable(); val_drtm_create_info_table(); diff --git a/apps/uefi/sbsa_main.c b/apps/uefi/sbsa_main.c index 9beb1885..82f963b9 100644 --- a/apps/uefi/sbsa_main.c +++ b/apps/uefi/sbsa_main.c @@ -110,6 +110,7 @@ freeAcsMem() val_cxl_free_info_table(); val_iovirt_free_info_table(); val_peripheral_free_info_table(); + val_dma_free_info_table(); val_smbios_free_info_table(); val_pmu_free_info_table(); val_cache_free_info_table(); @@ -203,6 +204,7 @@ execute_tests() createPcieVirtInfoTable(); createCxlInfoTable(); createPeripheralInfoTable(); + createDmaInfoTable(); createSmbiosInfoTable(); createCacheInfoTable(); createPccInfoTable(); diff --git a/apps/uefi/vbsa_main.c b/apps/uefi/vbsa_main.c index 93a53dc2..e9fffa84 100644 --- a/apps/uefi/vbsa_main.c +++ b/apps/uefi/vbsa_main.c @@ -103,6 +103,7 @@ freeAcsMem() val_pcie_free_info_table(); val_iovirt_free_info_table(); val_peripheral_free_info_table(); + val_dma_free_info_table(); val_smbios_free_info_table(); } @@ -181,6 +182,7 @@ execute_tests() createWatchdogInfoTable(); createPcieVirtInfoTable(); createPeripheralInfoTable(); + createDmaInfoTable(); createSmbiosInfoTable(); val_allocate_shared_mem(); diff --git a/apps/uefi/xbsa_acpi.inf b/apps/uefi/xbsa_acpi.inf index d6371b70..5a976138 100644 --- a/apps/uefi/xbsa_acpi.inf +++ b/apps/uefi/xbsa_acpi.inf @@ -92,6 +92,7 @@ ../../test_pool/peripherals/d002.c ../../test_pool/peripherals/d003.c ../../test_pool/peripherals/d005.c + ../../test_pool/peripherals/d004.c ../../test_pool/memory_map/m001.c ../../test_pool/memory_map/m002.c ../../test_pool/memory_map/m003.c diff --git a/apps/uefi/xbsa_main.c b/apps/uefi/xbsa_main.c index 9fcd1ab5..714b58ba 100644 --- a/apps/uefi/xbsa_main.c +++ b/apps/uefi/xbsa_main.c @@ -138,6 +138,7 @@ freeAcsMem(void) val_cxl_free_info_table(); val_iovirt_free_info_table(); val_peripheral_free_info_table(); + val_dma_free_info_table(); val_smbios_free_info_table(); val_pmu_free_info_table(); val_cache_free_info_table(); @@ -238,6 +239,7 @@ execute_tests() createPcieVirtInfoTable(); createCxlInfoTable(); createPeripheralInfoTable(); + createDmaInfoTable(); createSmbiosInfoTable(); createCacheInfoTable(); createPccInfoTable(); diff --git a/pal/uefi_acpi/PalLib.inf b/pal/uefi_acpi/PalLib.inf index 5a392fd1..99534843 100644 --- a/pal/uefi_acpi/PalLib.inf +++ b/pal/uefi_acpi/PalLib.inf @@ -50,6 +50,7 @@ src/pal_pcc.c src/pal_tpm2.c src/pal_cxl.c + src/pal_dma.c [Packages] ArmPkg/ArmPkg.dec diff --git a/pal/uefi_acpi/include/pal_uefi.h b/pal/uefi_acpi/include/pal_uefi.h index 7b78c992..39896e12 100644 --- a/pal/uefi_acpi/include/pal_uefi.h +++ b/pal/uefi_acpi/include/pal_uefi.h @@ -28,6 +28,8 @@ #include "Include/IndustryStandard/Tpm20.h" #include "Include/IndustryStandard/Tpm2Acpi.h" +typedef struct _EFI_PCI_IO_PROTOCOL EFI_PCI_IO_PROTOCOL; + #define PLATFORM_TIMEOUT_MEDIUM 0x1000 UINT64 pal_get_acpi_table_ptr(UINT32 table_signature); @@ -992,6 +994,33 @@ typedef struct { #pragma pack() +typedef enum { + DMA_TYPE_USB = 0x2000, + DMA_TYPE_SATA, + DMA_TYPE_OTHER, +} DMA_INFO_TYPE_e; + +typedef struct { + VOID *CpuAddr; + VOID *Mapping; + EFI_PCI_IO_PROTOCOL *PciIo; + UINTN Pages; + BOOLEAN UsingPci; +} PAL_DMA_MAP; + +typedef struct { + DMA_INFO_TYPE_e type; + void *target; ///< Implementation-specific target pointer. + void *port; + void *host; // It will be used only by PAL. hence void. + UINT32 flags; +} DMA_INFO_BLOCK; + +typedef struct { + UINT32 num_dma_ctrls; + DMA_INFO_BLOCK info[]; ///< Array of information blocks - per DMA controller +} DMA_INFO_TABLE; + /** @brief TPM2 Info Table **/ typedef struct { diff --git a/pal/uefi_acpi/src/pal_dma.c b/pal/uefi_acpi/src/pal_dma.c new file mode 100644 index 00000000..14dfc631 --- /dev/null +++ b/pal/uefi_acpi/src/pal_dma.c @@ -0,0 +1,241 @@ +/** @file + * Copyright (c) 2026, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +#include + +#include +#include +#include +#include + +#include "pal_uefi.h" +#include "pal_status.h" + +#define DMA_COHERENT 0x1 +#define DMA_NOT_COHERENT 0x2 + +#define PAL_MAX_DMA_MAPS 8 + +static PAL_DMA_MAP gPalDmaMaps[PAL_MAX_DMA_MAPS]; +static EFI_PCI_IO_PROTOCOL *gPalDmaPciIo; + +static PAL_DMA_MAP * +pal_dma_alloc_slot(void) +{ + UINTN i; + + for (i = 0; i < PAL_MAX_DMA_MAPS; i++) { + if (gPalDmaMaps[i].CpuAddr == NULL) + return &gPalDmaMaps[i]; + } + return NULL; +} + +static PAL_DMA_MAP * +pal_dma_find_slot(void *cpu_addr) +{ + UINTN i; + + for (i = 0; i < PAL_MAX_DMA_MAPS; i++) { + if (gPalDmaMaps[i].CpuAddr == cpu_addr) + return &gPalDmaMaps[i]; + } + return NULL; +} + +/** + @brief Populate DMA_INFO_TABLE with the information of DMA Controllers + in the system. + + @param dma_info_table Pointer to the DMA_INFO_TABLE data structure + + @return None +**/ +void +pal_dma_create_info_table(DMA_INFO_TABLE *dma_info_table) +{ + EFI_STATUS Status; + EFI_HANDLE *Handles = NULL; + UINTN HandleCount = 0; + EFI_PCI_IO_PROTOCOL *PciIo = NULL; + UINT64 Attr = 0; + UINTN i; + + dma_info_table->num_dma_ctrls = 0; + gPalDmaPciIo = NULL; + + /* Discover one PCI I/O device to use for DMA buffer allocation/mapping. */ + Status = gBS->LocateHandleBuffer(ByProtocol, + &gEfiPciIoProtocolGuid, + NULL, + &HandleCount, + &Handles); + + if (!EFI_ERROR(Status) && HandleCount > 0) { + for (i = 0; i < HandleCount; i++) { + Status = gBS->HandleProtocol(Handles[i], + &gEfiPciIoProtocolGuid, + (VOID **)&PciIo); + if (EFI_ERROR(Status)) { + continue; + } + + Status = PciIo->Attributes(PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Attr); + if (EFI_ERROR(Status)) { + continue; + } + + if (Attr & EFI_PCI_IO_ATTRIBUTE_BUS_MASTER) { + gPalDmaPciIo = PciIo; + break; + } + } + } + (void)(gPalDmaPciIo); + + if (Handles != NULL) { + FreePool(Handles); + } + + if (gPalDmaPciIo == NULL) { + return; + } + + /* Expose a single coherent DMA-capable instance so the test can run. */ + dma_info_table->num_dma_ctrls = 1; + dma_info_table->info[0].host = NULL; + dma_info_table->info[0].port = gPalDmaPciIo; + dma_info_table->info[0].target = NULL; + dma_info_table->info[0].flags = DMA_COHERENT; + dma_info_table->info[0].type = DMA_TYPE_OTHER; +} + +/** + @brief Allocate DMA capable memory and return the mapped DMA address + + @param buffer Pointer to store CPU accessible buffer address + @param length Size of the buffer to allocate + @param dev Device handle used for DMA mapping + @param flags Attribute flags for the allocation + @param dma_addr Pointer to store the DMA address + + @return PAL_STATUS_SUCCESS on success, error code otherwise +**/ +uint64_t +pal_dma_mem_alloc(void **buffer, uint32_t length, void *dev, uint32_t flags, UINT64 *dma_addr) +{ + EFI_STATUS Status; + UINTN Pages; + EFI_PCI_IO_PROTOCOL *PciIo = (EFI_PCI_IO_PROTOCOL *)dev; + PAL_DMA_MAP *Entry; + EFI_PCI_IO_PROTOCOL_OPERATION Operation; + UINTN NumberOfBytes; + + if (PciIo == NULL) { + return PAL_STATUS_NO_RESOURCE; + } + + Pages = EFI_SIZE_TO_PAGES(length); + Entry = pal_dma_alloc_slot(); + if (Entry == NULL) { + return PAL_STATUS_NO_RESOURCE; + } + + Entry->UsingPci = TRUE; + Entry->PciIo = PciIo; + Entry->Pages = Pages; + + Status = PciIo->AllocateBuffer(PciIo, + AllocateAnyPages, + EfiBootServicesData, + Pages, + &Entry->CpuAddr, + 0); + if (EFI_ERROR(Status)) { + return PAL_STATUS_NO_RESOURCE; + } + + ZeroMem(Entry->CpuAddr, length); + + if (flags & DMA_COHERENT) + Operation = EfiPciIoOperationBusMasterCommonBuffer; + else + Operation = EfiPciIoOperationBusMasterRead; + + NumberOfBytes = length; + Status = PciIo->Map(PciIo, + Operation, + Entry->CpuAddr, + &NumberOfBytes, + (EFI_PHYSICAL_ADDRESS *)dma_addr, + &Entry->Mapping); + if (EFI_ERROR(Status)) { + PciIo->FreeBuffer(PciIo, Pages, Entry->CpuAddr); + ZeroMem(Entry, sizeof(*Entry)); + return PAL_STATUS_NO_RESOURCE; + } + + *buffer = Entry->CpuAddr; + return PAL_STATUS_SUCCESS; +} + +/** + @brief Free DMA capable memory allocated by pal_dma_mem_alloc + + @param buffer CPU accessible buffer address to be freed + @param mem_dma DMA address corresponding to the buffer + @param length Size of the buffer + @param port Device handle used for DMA mapping + @param flags Attribute flags used for the allocation + + @return None +**/ +void +pal_dma_mem_free(void *buffer, UINT64 mem_dma, unsigned int length, void *port, unsigned int flags) +{ + PAL_DMA_MAP *Entry; + UINTN Pages; + + (void)mem_dma; + (void)port; + (void)flags; + + if (buffer == NULL) { + return; + } + + Entry = pal_dma_find_slot(buffer); + if (Entry == NULL) { + return; + } + + Pages = Entry->Pages ? Entry->Pages : EFI_SIZE_TO_PAGES(length); + + if (Entry->UsingPci && Entry->PciIo != NULL) { + if (Entry->Mapping != NULL) { + Entry->PciIo->Unmap(Entry->PciIo, Entry->Mapping); + } + Entry->PciIo->FreeBuffer(Entry->PciIo, Pages, Entry->CpuAddr); + } else { + gBS->FreePages((EFI_PHYSICAL_ADDRESS)(UINTN)Entry->CpuAddr, Pages); + } + + ZeroMem(Entry, sizeof(*Entry)); +} diff --git a/pal/uefi_dt/PalLib.inf b/pal/uefi_dt/PalLib.inf index b060e345..8143ac0a 100644 --- a/pal/uefi_dt/PalLib.inf +++ b/pal/uefi_dt/PalLib.inf @@ -45,6 +45,7 @@ src/pal_dt.c src/pal_dt_debug.c src/pal_pfdi.c + src/pal_dma.c [Packages] StdLib/StdLib.dec diff --git a/pal/uefi_dt/include/pal_uefi.h b/pal/uefi_dt/include/pal_uefi.h index 830503ab..0a9a2463 100644 --- a/pal/uefi_dt/include/pal_uefi.h +++ b/pal/uefi_dt/include/pal_uefi.h @@ -26,6 +26,8 @@ extern UINT32 g_curr_module; extern UINT32 g_enable_module; VOID pal_warn_not_implemented(const CHAR8 *api_name); +typedef struct _EFI_PCI_IO_PROTOCOL EFI_PCI_IO_PROTOCOL; + #define PCIE_SUCCESS 0x00000000 /* Operation completed successfully */ #define PCIE_NO_MAPPING 0x10000001 /* A mapping to a Function does not exist */ #define PCIE_CAP_NOT_FOUND 0x10000010 /* The specified capability was not found */ @@ -490,7 +492,32 @@ typedef struct { UINT64 dram_size; MEM_INFO_BLOCK info[]; } MEMORY_INFO_TABLE; +typedef enum { + DMA_TYPE_USB = 0x2000, + DMA_TYPE_SATA, + DMA_TYPE_OTHER, +} DMA_INFO_TYPE_e; + +typedef struct { + VOID *CpuAddr; + VOID *Mapping; + EFI_PCI_IO_PROTOCOL *PciIo; + UINTN Pages; + BOOLEAN UsingPci; +} PAL_DMA_MAP; +typedef struct { + DMA_INFO_TYPE_e type; + void *target; ///< Implementation-specific target pointer. + void *port; + void *host; // It will be used only by PAL. hence void. + UINT32 flags; +} DMA_INFO_BLOCK; + +typedef struct { + UINT32 num_dma_ctrls; + DMA_INFO_BLOCK info[]; ///< Array of information blocks - per DMA controller +} DMA_INFO_TABLE; VOID pal_memory_create_info_table(MEMORY_INFO_TABLE *memoryInfoTable); diff --git a/pal/uefi_dt/src/pal_dma.c b/pal/uefi_dt/src/pal_dma.c new file mode 100644 index 00000000..a043ba47 --- /dev/null +++ b/pal/uefi_dt/src/pal_dma.c @@ -0,0 +1,237 @@ +/** @file + * Copyright (c) 2026, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +#include +#include +#include +#include +#include + +#include "pal_uefi.h" +#include "pal_status.h" + +#define DMA_COHERENT 0x1 +#define DMA_NOT_COHERENT 0x2 + +#define PAL_MAX_DMA_MAPS 8 + +static PAL_DMA_MAP gPalDmaMaps[PAL_MAX_DMA_MAPS]; +static EFI_PCI_IO_PROTOCOL *gPalDmaPciIo; + +static PAL_DMA_MAP * +pal_dma_alloc_slot(void) +{ + UINTN i; + + for (i = 0; i < PAL_MAX_DMA_MAPS; i++) { + if (gPalDmaMaps[i].CpuAddr == NULL) + return &gPalDmaMaps[i]; + } + return NULL; +} + +static PAL_DMA_MAP * +pal_dma_find_slot(void *cpu_addr) +{ + UINTN i; + + for (i = 0; i < PAL_MAX_DMA_MAPS; i++) { + if (gPalDmaMaps[i].CpuAddr == cpu_addr) + return &gPalDmaMaps[i]; + } + return NULL; +} + +/** + @brief Populate DMA_INFO_TABLE with the information of DMA Controllers + in the system. + + @param dma_info_table Pointer to the DMA_INFO_TABLE data structure + + @return None +**/ +void +pal_dma_create_info_table(DMA_INFO_TABLE *dma_info_table) +{ + EFI_STATUS Status; + EFI_HANDLE *Handles = NULL; + UINTN HandleCount = 0; + EFI_PCI_IO_PROTOCOL *PciIo = NULL; + UINT64 Attr = 0; + UINTN i; + dma_info_table->num_dma_ctrls = 0; + gPalDmaPciIo = NULL; + + /* Discover one PCI I/O device to use for DMA buffer allocation/mapping. */ + Status = gBS->LocateHandleBuffer(ByProtocol, + &gEfiPciIoProtocolGuid, + NULL, + &HandleCount, + &Handles); + if (!EFI_ERROR(Status) && HandleCount > 0) { + for (i = 0; i < HandleCount; i++) { + Status = gBS->HandleProtocol(Handles[i], + &gEfiPciIoProtocolGuid, + (VOID **)&PciIo); + if (EFI_ERROR(Status)) { + continue; + } + + Status = PciIo->Attributes(PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Attr); + if (EFI_ERROR(Status)) { + continue; + } + + if (Attr & EFI_PCI_IO_ATTRIBUTE_BUS_MASTER) { + gPalDmaPciIo = PciIo; + break; + } + } + } + + if (Handles != NULL) { + FreePool(Handles); + } + + if (gPalDmaPciIo == NULL) { + return; + } + + /* Expose a single coherent DMA-capable instance so the test can run. */ + dma_info_table->num_dma_ctrls = 1; + dma_info_table->info[0].host = NULL; + dma_info_table->info[0].port = gPalDmaPciIo; + dma_info_table->info[0].target = NULL; + dma_info_table->info[0].flags = DMA_COHERENT; + dma_info_table->info[0].type = DMA_TYPE_OTHER; +} + +/** + @brief Allocate DMA capable memory and return the mapped DMA address + + @param buffer Pointer to store CPU accessible buffer address + @param length Size of the buffer to allocate + @param dev Device handle used for DMA mapping + @param flags Attribute flags for the allocation + @param dma_addr Pointer to store the DMA address + + @return PAL_STATUS_SUCCESS on success, error code otherwise +**/ +uint64_t +pal_dma_mem_alloc(void **buffer, uint32_t length, void *dev, uint32_t flags, UINT64 *dma_addr) +{ + EFI_STATUS Status; + UINTN Pages; + EFI_PCI_IO_PROTOCOL *PciIo = (EFI_PCI_IO_PROTOCOL *)dev; + PAL_DMA_MAP *Entry; + EFI_PCI_IO_PROTOCOL_OPERATION Operation; + UINTN NumberOfBytes; + + if (PciIo == NULL) { + return PAL_STATUS_NO_RESOURCE; + } + + Pages = EFI_SIZE_TO_PAGES(length); + Entry = pal_dma_alloc_slot(); + if (Entry == NULL) { + return PAL_STATUS_NO_RESOURCE; + } + + Entry->UsingPci = TRUE; + Entry->PciIo = PciIo; + Entry->Pages = Pages; + + Status = PciIo->AllocateBuffer(PciIo, + AllocateAnyPages, + EfiBootServicesData, + Pages, + &Entry->CpuAddr, + 0); + if (EFI_ERROR(Status)) { + return PAL_STATUS_NO_RESOURCE; + } + + ZeroMem(Entry->CpuAddr, length); + + if (flags & DMA_COHERENT) + Operation = EfiPciIoOperationBusMasterCommonBuffer; + else + Operation = EfiPciIoOperationBusMasterRead; + + NumberOfBytes = length; + Status = PciIo->Map(PciIo, + Operation, + Entry->CpuAddr, + &NumberOfBytes, + (EFI_PHYSICAL_ADDRESS *)dma_addr, + &Entry->Mapping); + if (EFI_ERROR(Status)) { + PciIo->FreeBuffer(PciIo, Pages, Entry->CpuAddr); + ZeroMem(Entry, sizeof(*Entry)); + return PAL_STATUS_NO_RESOURCE; + } + + *buffer = Entry->CpuAddr; + return PAL_STATUS_SUCCESS; +} + +/** + @brief Free DMA capable memory allocated by pal_dma_mem_alloc + + @param buffer CPU accessible buffer address to be freed + @param mem_dma DMA address corresponding to the buffer + @param length Size of the buffer + @param port Device handle used for DMA mapping + @param flags Attribute flags used for the allocation + + @return None +**/ +void +pal_dma_mem_free(void *buffer, UINT64 mem_dma, unsigned int length, void *port, unsigned int flags) +{ + PAL_DMA_MAP *Entry; + UINTN Pages; + + (void)mem_dma; + (void)port; + (void)flags; + + if (buffer == NULL) { + return; + } + + Entry = pal_dma_find_slot(buffer); + if (Entry == NULL) { + return; + } + + Pages = Entry->Pages ? Entry->Pages : EFI_SIZE_TO_PAGES(length); + + if (Entry->UsingPci && Entry->PciIo != NULL) { + if (Entry->Mapping != NULL) { + Entry->PciIo->Unmap(Entry->PciIo, Entry->Mapping); + } + Entry->PciIo->FreeBuffer(Entry->PciIo, Pages, Entry->CpuAddr); + } else { + gBS->FreePages((EFI_PHYSICAL_ADDRESS)(UINTN)Entry->CpuAddr, Pages); + } + + ZeroMem(Entry, sizeof(*Entry)); +} diff --git a/test_pool/peripherals/d004.c b/test_pool/peripherals/d004.c index e03ab29e..76f79570 100644 --- a/test_pool/peripherals/d004.c +++ b/test_pool/peripherals/d004.c @@ -41,13 +41,14 @@ payload_check_dma_mem_attribute(void) { uint32_t target_dev_index; void *buffer; - uint32_t attr, sh; + uint32_t attr = 0, sh = 0; int ret; bool flag_fail = 0; uint32_t index = val_pe_get_index_mpid(val_pe_get_mpid()); target_dev_index = val_dma_get_info(DMA_NUM_CTRL, 0); addr_t dma_addr = 0; uint32_t status; + uint32_t dma_flags; if (!target_dev_index) { @@ -65,27 +66,43 @@ payload_check_dma_mem_attribute(void) /* Allocate DMA memory based on coherency */ if (val_dma_get_info(DMA_HOST_COHERENT, target_dev_index)) { + dma_flags = DMA_COHERENT; status = val_dma_mem_alloc(&buffer, 512, target_dev_index, DMA_COHERENT, &dma_addr); if (status == ACS_STATUS_PAL_NOT_IMPLEMENTED) { goto test_warn_unimplemented; } + else if (status) { + val_print(WARN, "\n Error during DMA alloc. %x", status); + val_set_status(index, RESULT_FAIL(1)); + flag_fail = 1; + continue; + } } else { + dma_flags = DMA_NOT_COHERENT; status = val_dma_mem_alloc(&buffer, 512, target_dev_index, DMA_NOT_COHERENT, &dma_addr); if (status == ACS_STATUS_PAL_NOT_IMPLEMENTED) { goto test_warn_unimplemented; } + else if (status) { + val_print(WARN, "\n Error during DMA alloc."); + val_set_status(index, RESULT_FAIL(1)); + flag_fail = 1; + continue; + } } ret = val_dma_mem_get_attrs(buffer, &attr, &sh); if (ret) { if (ret == ACS_STATUS_PAL_NOT_IMPLEMENTED) { + val_dma_mem_free(buffer, dma_addr, 512, target_dev_index, dma_flags); goto test_warn_unimplemented; } val_print(ERROR, "\n DMA controller %d: Failed to get memory attributes", target_dev_index); - val_set_status(index, RESULT_FAIL(1)); + val_set_status(index, RESULT_FAIL(2)); flag_fail = 1; + val_dma_mem_free(buffer, dma_addr, 512, target_dev_index, dma_flags); continue; } @@ -97,9 +114,10 @@ payload_check_dma_mem_attribute(void) val_print(TRACE, "\n DMA controller %d: DMA memory must be inner/outer writeback inner shareable, inner/outer non-cacheable, or device type", target_dev_index); - val_set_status(index, RESULT_FAIL(2)); + val_set_status(index, RESULT_FAIL(3)); flag_fail = 1; } + val_dma_mem_free(buffer, dma_addr, 512, target_dev_index, dma_flags); } /* PASS the test if no fail conditions hit */ if (!flag_fail) diff --git a/val/include/acs_dma.h b/val/include/acs_dma.h index 6dd76830..d383b1f2 100644 --- a/val/include/acs_dma.h +++ b/val/include/acs_dma.h @@ -33,6 +33,11 @@ #define PCI_EP 0x100 #define PCI_EP_MASK 0xF00 +#define TTBR_SEL_MAX 2 +#define PGT_DESC_ATTR_INDEX(desc_attr) (((desc_attr) >> 2) & 0x7) +#define PGT_DESC_SH(desc_attr) (((desc_attr) >> 8) & 0x3) +#define MAIR_ATTR_VALUE(mair, idx) (((mair) >> ((idx) * 8)) & 0xFF) + void val_dma_free_info_table(void); uint64_t val_dma_mem_alloc(void **buffer, uint32_t size, uint32_t dev_index, uint32_t flags, diff --git a/val/include/val_interface.h b/val/include/val_interface.h index a4e8689d..ad00763c 100644 --- a/val/include/val_interface.h +++ b/val/include/val_interface.h @@ -409,6 +409,7 @@ typedef enum { } DMA_INFO_e; void val_dma_create_info_table(uint64_t *dma_info_ptr); +void val_dma_free_info_table(void); uint64_t val_dma_get_info(DMA_INFO_e type, uint32_t index); uint32_t val_dma_start_from_device(void *buffer, uint32_t length, uint32_t index); uint32_t val_dma_iommu_check_iova(uint32_t ctrl_index, addr_t dma_addr, addr_t cpu_addr); diff --git a/val/src/acs_dma.c b/val/src/acs_dma.c index dbfcf0f2..029679d9 100644 --- a/val/src/acs_dma.c +++ b/val/src/acs_dma.c @@ -20,6 +20,8 @@ #include "acs_dma.h" #include "pal_interface.h" #include "val_interface.h" +#include "acs_pgt.h" +#include "acs_pe.h" DMA_INFO_TABLE *g_dma_info_table; @@ -35,7 +37,7 @@ val_dma_free_info_table(void) { if (g_dma_info_table != NULL) { - pal_mem_free((void *)g_dma_info_table); + pal_mem_free_aligned((void *)g_dma_info_table); g_dma_info_table = NULL; } else { @@ -215,5 +217,41 @@ val_dma_device_get_dma_addr(uint32_t ctrl_index, uint64_t *dma_addr, uint32_t *c int val_dma_mem_get_attrs(void *buf, uint32_t *attr, uint32_t *sh) { +#if defined(TARGET_BAREMETAL) || defined(TARGET_UEFI) + pgt_descriptor_t pgt_desc; + uint64_t ttbr; + uint64_t desc_attr; + uint32_t ttbr_sel; + + if (buf == NULL || attr == NULL || sh == NULL) + return 1; + + for (ttbr_sel = 0; ttbr_sel < TTBR_SEL_MAX; ttbr_sel++) { + val_memory_set(&pgt_desc, sizeof(pgt_desc), 0); + pgt_desc.stage = PGT_STAGE1; + + if (val_pe_reg_read_tcr(ttbr_sel, &pgt_desc.tcr)) + continue; + + if (val_pe_reg_read_ttbr(ttbr_sel, &ttbr)) + continue; + + pgt_desc.pgt_base = (ttbr & AARCH64_TTBR_ADDR_MASK); + pgt_desc.mair = val_pe_reg_read(MAIR_ELx); + + if (!pgt_desc.pgt_base) + continue; + + if (val_pgt_get_attributes(pgt_desc, (uint64_t)buf, &desc_attr)) + continue; + + *attr = (uint32_t)MAIR_ATTR_VALUE(pgt_desc.mair, PGT_DESC_ATTR_INDEX(desc_attr)); + *sh = (uint32_t)PGT_DESC_SH(desc_attr); + return 0; + } + + return 1; +#else return pal_dma_mem_get_attrs(buf, attr, sh); +#endif } diff --git a/val/src/rule_metadata.c b/val/src/rule_metadata.c index 60bb9a70..6e0e89f3 100644 --- a/val/src/rule_metadata.c +++ b/val/src/rule_metadata.c @@ -832,7 +832,7 @@ rule_test_map_t rule_test_map[RULE_ID_SENTINEL] = { .test_entry_id = D004_ENTRY, .module_id = PERIPHERAL, .rule_desc = "Check Memory Attributes of DMA", - .platform_bitmask = PLATFORM_BAREMETAL | PLATFORM_LINUX, + .platform_bitmask = PLATFORM_BAREMETAL | PLATFORM_UEFI | PLATFORM_LINUX, .flag = BASE_RULE, .test_num = ACS_PER_TEST_NUM_BASE + 4, }, @@ -3510,6 +3510,7 @@ test_entry_fn_t test_entry_func_table[TEST_ENTRY_SENTINEL] = { [D005_ENTRY] = d005_entry, [D006_ENTRY] = d006_entry, [D008_ENTRY] = d008_entry, + [D004_ENTRY] = d004_entry, [E001_ENTRY] = e001_entry, // used in wrapper. [E002_ENTRY] = e002_entry, // used in wrapper. [E003_ENTRY] = e003_entry,