Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion pal/uefi_acpi/include/pal_uefi.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** @file
* Copyright (c) 2016-2025, Arm Limited or its affiliates. All rights reserved.
* Copyright (c) 2016-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");
Expand Down Expand Up @@ -130,6 +130,19 @@ typedef struct {
PE_INFO_ENTRY pe_info[];
}PE_INFO_TABLE;

/**
@brief MMU configuration structure for secondary PE initialization
This structure holds the primary PE's MMU configuration which
is used to enable MMU/caches on secondary PEs.
**/
typedef struct {
UINT64 ttbr0; ///< Translation Table Base Register 0
UINT64 tcr; ///< Translation Control Register
UINT64 mair; ///< Memory Attribute Indirection Register
UINT64 sctlr; ///< System Control Register
UINT32 current_el; ///< Current Exception Level (1 or 2)
} PE_MMU_CONFIG;

/**
@brief Instance of smbios type 4 processor info
**/
Expand Down
93 changes: 92 additions & 1 deletion pal/uefi_acpi/src/AArch64/ModuleEntryPoint.S
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#/** @file
# Copyright (c) 2016-2018,2024-2025, Arm Limited or its affiliates. All rights reserved.
# Copyright (c) 2016-2018,2024-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");
Expand All @@ -22,12 +22,99 @@
GCC_ASM_IMPORT(ArmReadMpidr)
GCC_ASM_IMPORT(PalGetSecondaryStackBase)
GCC_ASM_IMPORT(PalGetMaxMpidr)
GCC_ASM_IMPORT(PalGetMmuConfigAddr)
GCC_ASM_EXPORT(ModuleEntryPoint)

StartupAddr: .8byte ASM_PFX(val_test_entry)
ASM_PFX(StackSize): .8byte 0x100

// PE_MMU_CONFIG structure offsets
.equ MMU_CFG_TTBR0, 0
.equ MMU_CFG_TCR, 8
.equ MMU_CFG_MAIR, 16
.equ MMU_CFG_SCTLR, 24
.equ MMU_CFG_EL, 32

// SCTLR bits
.equ SCTLR_M_BIT, (1 << 0) // MMU enable
.equ SCTLR_C_BIT, (1 << 2) // Data cache enable
.equ SCTLR_I_BIT, (1 << 12) // Instruction cache enable

ASM_PFX(ModuleEntryPoint):
//
// Enable MMU and caches using primary PE's configuration
//
_EnableMmu:
// Get the MMU configuration address from primary PE
// Note: This is a C function call, but we haven't set up stack yet
// so we use a special calling convention (just branch and link)
adr x9, MmuConfigAddr
ldr x9, [x9]
blr x9

// x0 now contains the address of PE_MMU_CONFIG structure
mov x11, x0 // Save config address in x11

// Load all config values from the structure
ldr x12, [x11, #MMU_CFG_TTBR0] // TTBR0
ldr x13, [x11, #MMU_CFG_TCR] // TCR
ldr x14, [x11, #MMU_CFG_MAIR] // MAIR
ldr x15, [x11, #MMU_CFG_SCTLR] // SCTLR
ldr w16, [x11, #MMU_CFG_EL] // Current EL (32-bit)

// Determine current exception level
mrs x17, CurrentEL
lsr x17, x17, 2
and x17, x17, 0x3

// Check if we're at EL2
cmp x17, #2
b.eq _SetupMmuEl2

_SetupMmuEl1:
// Invalidate TLB for EL1
tlbi vmalle1
dsb sy
isb

// Set MAIR, TCR, TTBR0 for EL1
msr mair_el1, x14
isb
msr tcr_el1, x13
isb
msr ttbr0_el1, x12
isb

// Enable MMU and caches for EL1
// Use the same SCTLR value as primary PE (which has M, C, I bits set)
msr sctlr_el1, x15
isb
b _MmuEnabled

_SetupMmuEl2:
// Invalidate TLB for EL2
tlbi alle2
dsb sy
isb

// Set MAIR, TCR, TTBR0 for EL2
msr mair_el2, x14
isb
msr tcr_el2, x13
isb
msr ttbr0_el2, x12
isb

// Enable MMU and caches for EL2
// Use the same SCTLR value as primary PE (which has M, C, I bits set)
msr sctlr_el2, x15
isb

_MmuEnabled:
//
// MMU and caches are now enabled, proceed with original entry code
//

// Get ID of this CPU in Multicore system
bl ASM_PFX(ArmReadMpidr)
// Keep a copy of the MpId register value
Expand Down Expand Up @@ -80,3 +167,7 @@ _PrepareArguments:

_NeverReturn:
b _NeverReturn

// Function pointer for PalGetMmuConfigAddr
.align 3
MmuConfigAddr: .8byte ASM_PFX(PalGetMmuConfigAddr)
79 changes: 78 additions & 1 deletion pal/uefi_acpi/src/pal_pe.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** @file
* Copyright (c) 2016-2025, Arm Limited or its affiliates. All rights reserved.
* Copyright (c) 2016-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");
Expand Down Expand Up @@ -33,6 +33,24 @@ UINT64 gMpidrMax;
static UINT32 g_num_pe;
extern INT32 gPsciConduit;

/**
Global MMU configuration structure for secondary PE initialization.
This is populated by the primary PE and used by secondary PEs to
enable MMU/caches with the same page table configuration.
**/
static PE_MMU_CONFIG gMmuConfig __attribute__((aligned(64)));

/* External assembly functions for reading MMU registers */
UINT64 AA64ReadCurrentEL(VOID);
UINT64 AA64ReadTtbr0El1(VOID);
UINT64 AA64ReadTtbr0El2(VOID);
UINT64 AA64ReadTcr1(VOID);
UINT64 AA64ReadTcr2(VOID);
UINT64 AA64ReadMair1(VOID);
UINT64 AA64ReadMair2(VOID);
UINT64 AA64ReadSctlr1(VOID);
UINT64 AA64ReadSctlr2(VOID);

#define MAX_NUM_OF_SMBIOS_SLOTS_SUPPORTED 1024
#define SIZE_STACK_SECONDARY_PE 0x100 //256 bytes per core
#define UPDATE_AFF_MAX(src,dest,mask) ((dest & mask) > (src & mask) ? (dest & mask) : (src & mask))
Expand Down Expand Up @@ -204,6 +222,62 @@ PalGetMaxMpidr()
return gMpidrMax;
}

/**
@brief Captures the primary PE's MMU configuration for use by secondary PEs.
This function reads TTBR0, TCR, MAIR, and SCTLR from the current EL
and stores them in a global structure that secondary PEs can access.

@param None
@return None
**/
STATIC
VOID
PalCaptureMmuConfig(VOID)
{
UINT64 CurrentEl;

/* Read current exception level */
CurrentEl = (AA64ReadCurrentEL() >> 2) & 0x3;
gMmuConfig.current_el = (UINT32)CurrentEl;

/* Read MMU configuration registers based on current EL */
if (CurrentEl == 2) {
gMmuConfig.ttbr0 = AA64ReadTtbr0El2();
gMmuConfig.tcr = AA64ReadTcr2();
gMmuConfig.mair = AA64ReadMair2();
gMmuConfig.sctlr = AA64ReadSctlr2();
} else {
/* Assume EL1 */
gMmuConfig.ttbr0 = AA64ReadTtbr0El1();
gMmuConfig.tcr = AA64ReadTcr1();
gMmuConfig.mair = AA64ReadMair1();
gMmuConfig.sctlr = AA64ReadSctlr1();
}

acs_print(ACS_PRINT_INFO, L" MMU Config captured at EL%d\n", gMmuConfig.current_el);
acs_print(ACS_PRINT_DEBUG, L" TTBR0: 0x%lx\n", gMmuConfig.ttbr0);
acs_print(ACS_PRINT_DEBUG, L" TCR: 0x%lx\n", gMmuConfig.tcr);
acs_print(ACS_PRINT_DEBUG, L" MAIR: 0x%lx\n", gMmuConfig.mair);
acs_print(ACS_PRINT_DEBUG, L" SCTLR: 0x%lx\n", gMmuConfig.sctlr);

/* Clean cache to ensure secondary PEs see the config */
pal_pe_data_cache_ops_by_va((UINT64)&gMmuConfig, CLEAN_AND_INVALIDATE);
}

/**
@brief Returns the address of the MMU configuration structure.
This function is called by secondary PE entry code to get
the primary PE's MMU configuration.

@param None
@return Address of PE_MMU_CONFIG structure
**/
UINT64
PalGetMmuConfigAddr(VOID)
{
return (UINT64)&gMmuConfig;
}

/**
@brief Allocate memory region for secondary PE stack use. SIZE of stack for each PE
is a #define
Expand Down Expand Up @@ -354,6 +428,9 @@ pal_pe_create_info_table(PE_INFO_TABLE *PeTable)
pal_pe_data_cache_ops_by_va((UINT64)&gMpidrMax, CLEAN_AND_INVALIDATE);
PalAllocateSecondaryStack(gMpidrMax);

/* Capture primary PE's MMU configuration for secondary PE initialization */
PalCaptureMmuConfig();

}

/**
Expand Down
15 changes: 14 additions & 1 deletion pal/uefi_dt/include/pal_uefi.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** @file
* Copyright (c) 2016-2025, Arm Limited or its affiliates. All rights reserved.
* Copyright (c) 2016-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");
Expand Down Expand Up @@ -118,6 +118,19 @@ typedef struct {
PE_INFO_ENTRY pe_info[];
}PE_INFO_TABLE;

/**
@brief MMU configuration structure for secondary PE initialization
This structure holds the primary PE's MMU configuration which
is used to enable MMU/caches on secondary PEs.
**/
typedef struct {
UINT64 ttbr0; ///< Translation Table Base Register 0
UINT64 tcr; ///< Translation Control Register
UINT64 mair; ///< Memory Attribute Indirection Register
UINT64 sctlr; ///< System Control Register
UINT32 current_el; ///< Current Exception Level (1 or 2)
} PE_MMU_CONFIG;

/**
@brief Instance of smbios type 4 processor info
**/
Expand Down
93 changes: 92 additions & 1 deletion pal/uefi_dt/src/AArch64/ModuleEntryPoint.S
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#/** @file
# Copyright (c) 2016-2018,2024-2025, Arm Limited or its affiliates. All rights reserved.
# Copyright (c) 2016-2018,2024-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");
Expand All @@ -22,12 +22,99 @@
GCC_ASM_IMPORT(ArmReadMpidr)
GCC_ASM_IMPORT(PalGetSecondaryStackBase)
GCC_ASM_IMPORT(PalGetMaxMpidr)
GCC_ASM_IMPORT(PalGetMmuConfigAddr)
GCC_ASM_EXPORT(ModuleEntryPoint)

StartupAddr: .8byte ASM_PFX(val_test_entry)
ASM_PFX(StackSize): .8byte 0x100

// PE_MMU_CONFIG structure offsets
.equ MMU_CFG_TTBR0, 0
.equ MMU_CFG_TCR, 8
.equ MMU_CFG_MAIR, 16
.equ MMU_CFG_SCTLR, 24
.equ MMU_CFG_EL, 32

// SCTLR bits
.equ SCTLR_M_BIT, (1 << 0) // MMU enable
.equ SCTLR_C_BIT, (1 << 2) // Data cache enable
.equ SCTLR_I_BIT, (1 << 12) // Instruction cache enable

ASM_PFX(ModuleEntryPoint):
//
// Enable MMU and caches using primary PE's configuration
//
_EnableMmu:
// Get the MMU configuration address from primary PE
// Note: This is a C function call, but we haven't set up stack yet
// so we use a special calling convention (just branch and link)
adr x9, MmuConfigAddr
ldr x9, [x9]
blr x9

// x0 now contains the address of PE_MMU_CONFIG structure
mov x11, x0 // Save config address in x11

// Load all config values from the structure
ldr x12, [x11, #MMU_CFG_TTBR0] // TTBR0
ldr x13, [x11, #MMU_CFG_TCR] // TCR
ldr x14, [x11, #MMU_CFG_MAIR] // MAIR
ldr x15, [x11, #MMU_CFG_SCTLR] // SCTLR
ldr w16, [x11, #MMU_CFG_EL] // Current EL (32-bit)

// Determine current exception level
mrs x17, CurrentEL
lsr x17, x17, 2
and x17, x17, 0x3

// Check if we're at EL2
cmp x17, #2
b.eq _SetupMmuEl2

_SetupMmuEl1:
// Invalidate TLB for EL1
tlbi vmalle1
dsb sy
isb

// Set MAIR, TCR, TTBR0 for EL1
msr mair_el1, x14
isb
msr tcr_el1, x13
isb
msr ttbr0_el1, x12
isb

// Enable MMU and caches for EL1
// Use the same SCTLR value as primary PE (which has M, C, I bits set)
msr sctlr_el1, x15
isb
b _MmuEnabled

_SetupMmuEl2:
// Invalidate TLB for EL2
tlbi alle2
dsb sy
isb

// Set MAIR, TCR, TTBR0 for EL2
msr mair_el2, x14
isb
msr tcr_el2, x13
isb
msr ttbr0_el2, x12
isb

// Enable MMU and caches for EL2
// Use the same SCTLR value as primary PE (which has M, C, I bits set)
msr sctlr_el2, x15
isb

_MmuEnabled:
//
// MMU and caches are now enabled, proceed with original entry code
//

// Get ID of this CPU in Multicore system
bl ASM_PFX(ArmReadMpidr)
// Keep a copy of the MpId register value
Expand Down Expand Up @@ -80,3 +167,7 @@ _PrepareArguments:

_NeverReturn:
b _NeverReturn

// Function pointer for PalGetMmuConfigAddr
.align 3
MmuConfigAddr: .8byte ASM_PFX(PalGetMmuConfigAddr)
Loading