| 1 | /* |
| 2 | * Copyright (c) 2021 Apple Inc. All rights reserved. |
| 3 | */ |
| 4 | |
| 5 | #include <stdint.h> |
| 6 | #include <arm64/proc_reg.h> |
| 7 | #include <kern/clock.h> |
| 8 | #include <mach/mach_time.h> |
| 9 | #include <machine/atomic.h> |
| 10 | #include <machine/machine_routines.h> |
| 11 | #include <pexpert/device_tree.h> |
| 12 | #include <pexpert/arm64/board_config.h> |
| 13 | |
| 14 | |
| 15 | #if HAS_GIC_V3 |
| 16 | #define GICR_WAKE_TIMEOUT_NS (1000000000ULL) // timeout for redistributor wakeup (default 1s) |
| 17 | MACHINE_TIMEOUT(gicr_wake_timeout_ns, "gicr-wake-timeout" , GICR_WAKE_TIMEOUT_NS, MACHINE_TIMEOUT_UNIT_NSEC, NULL); |
| 18 | |
| 19 | static vm_offset_t gicd_base; |
| 20 | static vm_offset_t gicr_base; |
| 21 | static vm_offset_t gicr_size; |
| 22 | |
| 23 | static uint32_t |
| 24 | _gic_read32(vm_offset_t addr) |
| 25 | { |
| 26 | return *((volatile uint32_t *) addr); |
| 27 | } |
| 28 | |
| 29 | static uint64_t |
| 30 | _gic_read64(vm_offset_t addr) |
| 31 | { |
| 32 | return *((volatile uint64_t *) addr); |
| 33 | } |
| 34 | |
| 35 | static void |
| 36 | _gic_write32(vm_offset_t addr, uint32_t value) |
| 37 | { |
| 38 | *((volatile uint32_t *) addr) = value; |
| 39 | } |
| 40 | |
| 41 | #define gicd_read32(offset) (_gic_read32(gicd_base + (offset))) |
| 42 | #define gicd_write32(offset, data) (_gic_write32(gicd_base + (offset), (data))) |
| 43 | #define gicr_read32(offset) (_gic_read32(gicr_pe_base + (offset))) |
| 44 | #define gicr_write32(offset, data) (_gic_write32(gicr_pe_base + (offset), (data))) |
| 45 | #define gicr_read64(offset) (_gic_read64(gicr_pe_base + (offset))) |
| 46 | |
| 47 | static vm_offset_t |
| 48 | find_gicr_pe_base() |
| 49 | { |
| 50 | // We only care about aff1 and aff0 |
| 51 | uint32_t phys_id = __builtin_arm_rsr64("MPIDR_EL1" ) & (MPIDR_AFF1_MASK | MPIDR_AFF0_MASK); |
| 52 | |
| 53 | for (vm_offset_t offset = 0; offset < gicr_size; offset += GICR_PE_SIZE) { |
| 54 | vm_offset_t gicr_pe_base = gicr_base + offset; |
| 55 | uint64_t gicr_typer = gicr_read64(GICR_TYPER); |
| 56 | uint32_t aff_value = (uint32_t) (gicr_typer >> GICR_TYPER_AFFINITY_VALUE_SHIFT) & (MPIDR_AFF1_MASK | MPIDR_AFF0_MASK); |
| 57 | |
| 58 | if (phys_id == aff_value) { |
| 59 | return gicr_pe_base; |
| 60 | } |
| 61 | |
| 62 | if (gicr_typer & GICR_TYPER_LAST) { |
| 63 | break; |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | panic("%s: cannot find GICR base for core %u" , __func__, ml_get_cpu_number(phys_id)); |
| 68 | } |
| 69 | |
| 70 | void |
| 71 | pe_init_fiq() |
| 72 | { |
| 73 | int error; |
| 74 | DTEntry entry; |
| 75 | |
| 76 | // gicd_base is 0x0 only before it's initialized by the boot processor. |
| 77 | // Avoid calling SecureDT* routines on secondary processors to avoid |
| 78 | // race conditions because they are not thread-safe. |
| 79 | if (!gicd_base) { |
| 80 | // Find GIC DT node |
| 81 | error = SecureDTLookupEntry(NULL, pathName: "/arm-io/gic" , foundEntry: &entry); |
| 82 | if (error != kSuccess) { |
| 83 | panic("%s: cannot find GIC node in DT" , __func__); |
| 84 | } |
| 85 | |
| 86 | // Find "reg" property |
| 87 | void const *prop; |
| 88 | unsigned int prop_size; |
| 89 | error = SecureDTGetProperty(entry, propertyName: "reg" , propertyValue: &prop, propertySize: &prop_size); |
| 90 | if (error != kSuccess) { |
| 91 | panic("%s: cannot find GIC MMIO regions in DT" , __func__); |
| 92 | } |
| 93 | |
| 94 | // Need at least GICD base, GICD size, GICR base and GICR size |
| 95 | if (prop_size < 4 * sizeof(uint64_t)) { |
| 96 | panic("%s: incorrect reg property size in GIC DT node; expecting 32 bytes but got %u bytes" , __func__, prop_size); |
| 97 | } |
| 98 | |
| 99 | vm_offset_t soc_base_phys = pe_arm_get_soc_base_phys(); |
| 100 | |
| 101 | uint64_t const gicd_base_prop = ((uint64_t const *) prop)[0]; |
| 102 | uint64_t const gicd_size_prop = ((uint64_t const *) prop)[1]; |
| 103 | uint64_t const gicr_base_prop = ((uint64_t const *) prop)[2]; |
| 104 | uint64_t const gicr_size_prop = ((uint64_t const *) prop)[3]; |
| 105 | |
| 106 | // Find GICD base address |
| 107 | gicd_base = ml_io_map(phys_addr: soc_base_phys + gicd_base_prop, size: gicd_size_prop); |
| 108 | |
| 109 | if (!gicd_base) { |
| 110 | panic("%s: cannot map GICD region" , __func__); |
| 111 | } |
| 112 | |
| 113 | // Find GICR base address |
| 114 | gicr_base = ml_io_map(phys_addr: soc_base_phys + gicr_base_prop, size: gicr_size_prop); |
| 115 | |
| 116 | if (!gicr_base) { |
| 117 | panic("%s: cannot map GICR region" , __func__); |
| 118 | } |
| 119 | |
| 120 | gicr_size = gicr_size_prop; |
| 121 | } |
| 122 | |
| 123 | // Find the redistributor for this processor |
| 124 | vm_offset_t gicr_pe_base = find_gicr_pe_base(); |
| 125 | |
| 126 | // Mark this PE to be awake |
| 127 | uint32_t gicr_waker = gicr_read32(GICR_WAKER); |
| 128 | if (gicr_waker & GICR_WAKER_CHILDRENASLEEP) { |
| 129 | gicr_waker &= ~GICR_WAKER_PROCESSORSLEEP; |
| 130 | |
| 131 | gicr_write32(GICR_WAKER, gicr_waker); |
| 132 | |
| 133 | uint64_t gicr_wake_deadline; |
| 134 | nanoseconds_to_deadline(os_atomic_load(&gicr_wake_timeout_ns, relaxed), result: &gicr_wake_deadline); |
| 135 | while (gicr_read32(GICR_WAKER) & GICR_WAKER_CHILDRENASLEEP) { |
| 136 | // Spin |
| 137 | if (gicr_wake_timeout_ns > 0 && mach_absolute_time() > gicr_wake_deadline) { |
| 138 | panic("%s: core %u timed out waiting for redistributor to wake up" , |
| 139 | __func__, ml_get_cpu_number_local()); |
| 140 | } |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | // Configure timers and legacy FIQ to be group 0 |
| 145 | gicr_write32(GICR_IGROUPR0, 0x81FFFFFF); |
| 146 | |
| 147 | // Enable PPI 27 |
| 148 | gicr_write32(GICR_ISENABLER0, (1 << 27)); |
| 149 | |
| 150 | // Enable system register access |
| 151 | uint64_t icc_sre = __builtin_arm_rsr64("ICC_SRE_EL1" ); |
| 152 | icc_sre |= ICC_SRE_SRE; |
| 153 | __builtin_arm_wsr64("ICC_SRE_EL1" , icc_sre); |
| 154 | __builtin_arm_isb(ISB_SY); |
| 155 | |
| 156 | // Set priority masks and binary point for group 0 |
| 157 | __builtin_arm_wsr64("ICC_BPR0_EL1" , 0); |
| 158 | __builtin_arm_wsr64("ICC_PMR_EL1" , 0xFF); |
| 159 | |
| 160 | // Set EOI mode of this processor |
| 161 | uint64_t icc_ctlr = __builtin_arm_rsr64("ICC_CTLR_EL1" ); |
| 162 | icc_ctlr &= ~ICC_CTLR_EOIMODE; |
| 163 | __builtin_arm_wsr64("ICC_CTLR_EL1" , icc_ctlr); |
| 164 | |
| 165 | // Enable the forwarding of the vtimer interrupt |
| 166 | uint32_t gicd_ctlr = gicd_read32(GICD_CTLR); |
| 167 | gicd_ctlr |= GICD_CTLR_ENABLEGRP0; |
| 168 | gicd_write32(GICD_CTLR, gicd_ctlr); |
| 169 | __builtin_arm_wsr64("ICC_IGRPEN0_EL1" , 1); |
| 170 | __builtin_arm_isb(ISB_SY); |
| 171 | } |
| 172 | #else |
| 173 | void |
| 174 | pe_init_fiq() |
| 175 | { |
| 176 | } |
| 177 | #endif |
| 178 | |