Mixed-Mode Translation in AArch64 Kernel with AArch32 User Space
The core issue revolves around the complexities of mixed-mode translation in an ARMv8 architecture, where an AArch64 kernel (running at EL1) needs to manage AArch32 user-space applications (running at EL0). Specifically, the challenge lies in configuring the translation tables to allow the AArch64 kernel to access user-space data using PL0 (EL0) bindings while maintaining the separation between kernel and user-space memory regions. This scenario requires careful handling of the Translation Table Base Registers (TTBR0 and TTBR1) and the page table formats (AArch32 short/long descriptors vs. AArch64 descriptors).
In ARMv8, the memory management unit (MMU) supports two distinct translation table formats: AArch32 and AArch64. AArch32 uses either short descriptors (32-bit) or long descriptors (40-bit), while AArch64 employs a 64-bit descriptor format. The kernel typically uses TTBR1 for its own address space (kernel space) and TTBR0 for the user-space address space. However, when the kernel runs in AArch64 mode and the user-space applications run in AArch32 mode, the translation tables must be configured to support both formats simultaneously.
The primary challenge is ensuring that TTBR0, which points to the user-space page tables, can use AArch32 long descriptors while TTBR1 uses AArch64 descriptors for the kernel. This mixed-mode configuration is not explicitly documented in the ARM Architecture Reference Manual (ARM ARM), leading to ambiguity and potential implementation pitfalls. The key question is whether an AArch64 kernel can point TTBR0 to an AArch32 long descriptor table and still access user-space data using PL0 bindings.
ARMv8 Translation Table Configuration and Mixed-Mode Constraints
The ARMv8 architecture allows for flexible configuration of translation tables, but this flexibility comes with constraints that must be carefully considered. According to the ARM ARM (Section G4.2.3), translation stages used in AArch32 state can be configured from a higher exception level, regardless of whether that level is using AArch32 or AArch64. This means that an AArch64 kernel at EL1 can configure the translation tables for AArch32 user-space applications at EL0.
However, there are specific constraints to be aware of:
- Exception Level Hierarchy: A higher exception level (e.g., EL1) can configure translation tables for a lower exception level (e.g., EL0), but not vice versa. This ensures that the kernel retains control over memory management.
- Secure vs. Non-Secure State: A non-secure exception level cannot configure translation tables used in the secure state. This separation is critical for maintaining security boundaries.
- Descriptor Compatibility: While AArch64 and AArch32 descriptors are not directly compatible, the ARMv8 MMU can handle mixed-mode translation by using separate TTBRs for kernel and user-space.
The ARM ARM also clarifies that if non-secure EL0 is the only non-secure exception level using AArch32, the non-secure PL1&0 stage of address translation can be configured from non-secure EL1 using AArch64. This implies that an AArch64 kernel can indeed configure AArch32 page tables for user-space applications.
Despite this flexibility, there are potential pitfalls:
- Descriptor Format Mismatch: If the kernel attempts to use AArch64 descriptors for user-space memory, it will result in translation faults because the MMU expects AArch32 descriptors for EL0 in this scenario.
- Cache Coherency Issues: Mixed-mode translation can lead to cache coherency problems if the kernel and user-space use different descriptor formats. Proper cache maintenance operations are required to ensure data consistency.
- Performance Overhead: Managing mixed-mode translation tables can introduce additional overhead, particularly if frequent context switches occur between AArch64 and AArch32 modes.
Implementing Mixed-Mode Translation with AArch32 Page Tables in AArch64 Kernel
To successfully implement mixed-mode translation in an AArch64 kernel with AArch32 user-space applications, follow these steps:
Step 1: Configure TTBR0 and TTBR1 for Mixed-Mode Translation
The first step is to configure TTBR0 to point to an AArch32 long descriptor table for user-space and TTBR1 to point to an AArch64 descriptor table for the kernel. This ensures that the MMU uses the correct descriptor format for each address space.
// Example: Configuring TTBR0 and TTBR1 in AArch64 assembly
MOV X0, #USER_SPACE_TABLE_BASE // Load base address of AArch32 user-space table
MSR TTBR0_EL1, X0 // Set TTBR0 to point to user-space table
MOV X1, #KERNEL_TABLE_BASE // Load base address of AArch64 kernel table
MSR TTBR1_EL1, X1 // Set TTBR1 to point to kernel table
ISB // Ensure the changes take effect
Step 2: Validate Descriptor Compatibility
Ensure that the AArch32 long descriptors in the user-space table are compatible with the AArch64 kernel’s expectations. This includes verifying that the memory attributes (e.g., cacheability, shareability) and access permissions are correctly configured.
Step 3: Handle Cache Coherency
Mixed-mode translation can lead to cache coherency issues, particularly when the kernel accesses user-space data. Use data synchronization barriers (DSB) and instruction synchronization barriers (ISB) to ensure that the MMU and cache are in a consistent state.
// Example: Using DSB and ISB for cache coherency
DSB SY // Ensure all memory operations are complete
ISB // Ensure the instruction stream is synchronized
Step 4: Implement Context Switching Logic
When switching between AArch64 and AArch32 modes, ensure that the translation tables are correctly updated. This includes saving and restoring TTBR0 and TTBR1 during context switches.
// Example: Context switching logic
MRS X0, TTBR0_EL1 // Save TTBR0
MRS X1, TTBR1_EL1 // Save TTBR1
STR X0, [X2, #TTBR0_SAVE_OFFSET] // Store TTBR0 in context structure
STR X1, [X2, #TTBR1_SAVE_OFFSET] // Store TTBR1 in context structure
// Restore TTBR0 and TTBR1 during context switch
LDR X0, [X2, #TTBR0_SAVE_OFFSET] // Load TTBR0 from context structure
LDR X1, [X2, #TTBR1_SAVE_OFFSET] // Load TTBR1 from context structure
MSR TTBR0_EL1, X0 // Restore TTBR0
MSR TTBR1_EL1, X1 // Restore TTBR1
ISB // Ensure the changes take effect
Step 5: Debugging and Validation
Use hardware debugging tools (e.g., JTAG) and software debugging techniques (e.g., logging) to validate the mixed-mode translation configuration. Check for translation faults, cache coherency issues, and performance bottlenecks.
Step 6: Optimize Performance
Mixed-mode translation can introduce performance overhead. Optimize the translation table walk process by using large pages (e.g., 2MB or 1GB) where possible and minimizing the frequency of context switches.
By following these steps, you can successfully implement mixed-mode translation in an AArch64 kernel with AArch32 user-space applications, ensuring robust and efficient memory management across different descriptor formats and exception levels.