GIC Memory Map Misconfiguration Leading to System Hangs

In bare-metal ARM development, particularly when working with ARM Cortex-A55 and Cortex-A75 cores, the configuration of the Generic Interrupt Controller (GIC) memory map is critical for proper system operation. The GIC is responsible for managing interrupts, and its memory map must be correctly set up to ensure that the processor can access the GIC registers. A common issue arises when developers attempt to relocate the GIC memory map from its default address range (e.g., 0x2F000000) to a custom address range (e.g., 0x52800000). When this relocation is performed without proper consideration of the Memory Management Unit (MMU) settings, the system may hang or become unresponsive.

The root cause of this issue often lies in the interaction between the GIC memory map and the MMU translation tables. The MMU is responsible for translating virtual addresses to physical addresses, and if the MMU is not configured correctly, the processor may fail to access the GIC registers, leading to a system hang. This is particularly problematic when the GIC is relocated to a higher memory address range (e.g., above 0x40000000), as the default MMU settings may not account for this change.

The symptoms of this issue include the system hanging immediately after writing to the TCR_EL1 register, which controls the MMU translation table behavior. Additionally, the debugger may lose connection with the target, making it difficult to diagnose the problem. This behavior suggests that the MMU is either misconfigured or that the translation tables are not properly set up to handle the new GIC memory map.


Incorrect TCR_EL1 Configuration and Translation Table Setup

The primary cause of the system hang when relocating the GIC memory map is an incorrect configuration of the TCR_EL1 register and the associated translation tables. The TCR_EL1 register controls the size and organization of the translation tables, and it must be configured to match the physical address size and memory map of the system. In this case, the developer needed to support a 40-bit physical address space (1TB), which requires specific settings in the TCR_EL1 register.

The TCR_EL1 register includes several fields that control the translation table behavior, such as the translation granule size, the size of the address space, and the attributes of the memory regions. If these fields are not set correctly, the MMU may fail to translate addresses properly, leading to access faults or system hangs. For example, the developer in the discussion set the TCR_EL1 register as follows:

r = (0b00LL << 37) |
    (0b010LL << 32) |
    (0b00LL << 30) |
    (0b00LL << 28) |
    (0b00LL << 26) |
    (0b00LL << 24) |
    (0b1LL  << 23) |
    (00LL  << 16) |
    (0b00LL << 14) |
    (0b10LL << 12) |
    (0b01LL << 10) |
    (0b01LL << 8) |
    (0b0LL  << 7) |
    (24LL  << 0);
asm volatile ("msr TCR_EL1, %0" : : "r" (r));

This configuration sets the translation granule size to 4KB, the physical address size to 40 bits, and the memory region attributes to default values. However, if the translation tables are not properly initialized to match this configuration, the MMU will fail to translate addresses correctly.

Another potential cause of the issue is the initialization of the translation tables themselves. The translation tables must be set up to map the new GIC memory map address range (e.g., 0x52800000) to the correct physical addresses. If the translation tables are not updated to reflect the new GIC memory map, the MMU will generate translation faults when the processor attempts to access the GIC registers.


Correcting MMU Configuration and Translation Table Initialization

To resolve the issue of the system hanging after relocating the GIC memory map, the developer must ensure that the MMU is properly configured and that the translation tables are correctly initialized. The following steps outline the process for correcting the MMU configuration and translation table setup:

Step 1: Verify TCR_EL1 Configuration

The first step is to verify that the TCR_EL1 register is configured correctly for the desired physical address size and translation granule size. In this case, the developer needed to support a 40-bit physical address space, which requires setting the appropriate fields in the TCR_EL1 register. The following table summarizes the key fields in the TCR_EL1 register and their recommended settings for a 40-bit physical address space:

Field Description Recommended Setting
T0SZ (bits 0-5) Size offset for the address space 24
TG0 (bits 14-15) Granule size for the address space 0b01 (4KB)
IPS (bits 32-34) Physical address size 0b010 (40 bits)
SH0 (bits 12-13) Shareability attribute 0b10 (Outer Shareable)
ORGN0 (bits 10-11) Outer cacheability attribute 0b01 (Write-Back)
IRGN0 (bits 8-9) Inner cacheability attribute 0b01 (Write-Back)

Step 2: Initialize Translation Tables

The next step is to initialize the translation tables to map the new GIC memory map address range (e.g., 0x52800000) to the correct physical addresses. The translation tables must be set up to reflect the new memory map, and the MMU must be enabled to use these tables. The following code snippet demonstrates how to initialize the translation tables for a 40-bit physical address space:

#include "mmu.h"
#pragma asm
extern uint64_t Image$$TTB0_L2_PERIPH$$ZI$$Base;

void mmu_init(void) {
    uint64_t r;
    uint64_t i;
    unsigned long* BaseAddress = &Image$$TTB0_L2_PERIPH$$ZI$$Base;

    printf("MMU table setting\n");
    for(i = 0; i < 508; i++) {
        mem_write((unsigned long)BaseAddress + 0x00 + 0x20 * i, 0x00000F25 + 0x100000000 * i);
        mem_write((unsigned long)BaseAddress + 0x08 + 0x20 * i, 0x40000F25 + 0x100000000 * i);
        mem_write((unsigned long)BaseAddress + 0x10 + 0x20 * i, 0x80000F25 + 0x100000000 * i);
        mem_write((unsigned long)BaseAddress + 0x18 + 0x20 * i, 0xC0000F25 + 0x100000000 * i);
    }
    asm volatile ("isb");

    printf("TCR_EL setting\n");
    r = (0b00LL << 37) |
        (0b010LL << 32) |
        (0b00LL << 30) |
        (0b00LL << 28) |
        (0b00LL << 26) |
        (0b00LL << 24) |
        (0b1LL  << 23) |
        (00LL  << 16) |
        (0b00LL << 14) |
        (0b10LL << 12) |
        (0b01LL << 10) |
        (0b01LL << 8) |
        (0b0LL  << 7) |
        (24LL  << 0);
    asm volatile ("msr TCR_EL1, %0" : : "r" (r));
    asm volatile ("isb");
}

Step 3: Enable the MMU

Once the TCR_EL1 register and translation tables are properly configured, the MMU can be enabled. This is typically done by setting the M bit in the SCTLR_EL1 register. The following code snippet demonstrates how to enable the MMU:

asm volatile ("mrs %0, SCTLR_EL1" : "=r" (r));
r |= (1 << 0); // Set the M bit to enable the MMU
asm volatile ("msr SCTLR_EL1, %0" : : "r" (r));
asm volatile ("isb");

Step 4: Verify GIC Access

After enabling the MMU, the developer should verify that the processor can access the GIC registers at the new memory map address. This can be done by reading and writing to the GIC registers and checking for expected behavior. If the system operates normally and interrupts are handled correctly, the GIC memory map relocation has been successful.

Step 5: Debugging and Troubleshooting

If the system still hangs or fails to operate correctly, the developer should use a debugger to inspect the MMU configuration and translation tables. The debugger can be used to step through the initialization code and verify that the TCR_EL1 register and translation tables are set up correctly. Additionally, the developer should check for any translation faults or access violations that may indicate a problem with the MMU configuration.


By following these steps, developers can successfully relocate the GIC memory map in a bare-metal ARM development environment and ensure that the system operates correctly. Proper configuration of the MMU and translation tables is critical for handling the new memory map and avoiding system hangs or other issues.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *