| // SPDX-License-Identifier: BSD-2-Clause |
| /* |
| * Copyright (c) 2016, Linaro Limited |
| * Copyright (c) 2014, STMicroelectronics International N.V. |
| */ |
| |
| #include <arm.h> |
| #include <console.h> |
| #include <drivers/gic.h> |
| #include <drivers/pl011.h> |
| #include <drivers/tzc400.h> |
| #include <initcall.h> |
| #include <keep.h> |
| #include <kernel/generic_boot.h> |
| #include <kernel/interrupt.h> |
| #include <kernel/misc.h> |
| #include <kernel/panic.h> |
| #include <kernel/pm_stubs.h> |
| #include <kernel/tee_time.h> |
| #include <mm/core_memprot.h> |
| #include <mm/core_mmu.h> |
| #include <platform_config.h> |
| #include <sm/psci.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <tee/entry_fast.h> |
| #include <tee/entry_std.h> |
| #include <trace.h> |
| |
| static const struct thread_handlers handlers = { |
| #if defined(CFG_WITH_ARM_TRUSTED_FW) |
| .cpu_on = cpu_on_handler, |
| .cpu_off = pm_do_nothing, |
| .cpu_suspend = pm_do_nothing, |
| .cpu_resume = pm_do_nothing, |
| .system_off = pm_do_nothing, |
| .system_reset = pm_do_nothing, |
| #else |
| .cpu_on = pm_panic, |
| .cpu_off = pm_panic, |
| .cpu_suspend = pm_panic, |
| .cpu_resume = pm_panic, |
| .system_off = pm_panic, |
| .system_reset = pm_panic, |
| #endif |
| }; |
| |
| static struct gic_data gic_data __nex_bss; |
| static struct pl011_data console_data __nex_bss; |
| |
| register_phys_mem_pgdir(MEM_AREA_IO_SEC, CONSOLE_UART_BASE, PL011_REG_SIZE); |
| #if defined(PLATFORM_FLAVOR_fvp) |
| register_phys_mem(MEM_AREA_RAM_SEC, TZCDRAM_BASE, TZCDRAM_SIZE); |
| #endif |
| #if defined(PLATFORM_FLAVOR_qemu_virt) |
| register_phys_mem_pgdir(MEM_AREA_IO_SEC, SECRAM_BASE, SECRAM_COHERENT_SIZE); |
| #endif |
| #ifdef DRAM0_BASE |
| register_ddr(DRAM0_BASE, DRAM0_SIZE); |
| #endif |
| #ifdef DRAM1_BASE |
| register_ddr(DRAM1_BASE, DRAM1_SIZE); |
| #endif |
| |
| const struct thread_handlers *generic_boot_get_handlers(void) |
| { |
| return &handlers; |
| } |
| |
| #ifdef GIC_BASE |
| |
| register_phys_mem_pgdir(MEM_AREA_IO_SEC, GICD_BASE, GIC_DIST_REG_SIZE); |
| register_phys_mem_pgdir(MEM_AREA_IO_SEC, GICC_BASE, GIC_DIST_REG_SIZE); |
| |
| void main_init_gic(void) |
| { |
| vaddr_t gicc_base; |
| vaddr_t gicd_base; |
| |
| gicc_base = (vaddr_t)phys_to_virt(GIC_BASE + GICC_OFFSET, |
| MEM_AREA_IO_SEC); |
| gicd_base = (vaddr_t)phys_to_virt(GIC_BASE + GICD_OFFSET, |
| MEM_AREA_IO_SEC); |
| if (!gicc_base || !gicd_base) |
| panic(); |
| |
| #if defined(CFG_WITH_ARM_TRUSTED_FW) |
| /* On ARMv8, GIC configuration is initialized in ARM-TF */ |
| gic_init_base_addr(&gic_data, gicc_base, gicd_base); |
| #else |
| /* Initialize GIC */ |
| gic_init(&gic_data, gicc_base, gicd_base); |
| #endif |
| itr_init(&gic_data.chip); |
| } |
| |
| #if !defined(CFG_WITH_ARM_TRUSTED_FW) |
| void main_secondary_init_gic(void) |
| { |
| gic_cpu_init(&gic_data); |
| } |
| #endif |
| |
| #endif |
| |
| void itr_core_handler(void) |
| { |
| gic_it_handle(&gic_data); |
| } |
| |
| void console_init(void) |
| { |
| pl011_init(&console_data, CONSOLE_UART_BASE, CONSOLE_UART_CLK_IN_HZ, |
| CONSOLE_BAUDRATE); |
| register_serial_console(&console_data.chip); |
| } |
| |
| #if defined(IT_CONSOLE_UART) && \ |
| !(defined(CFG_WITH_ARM_TRUSTED_FW) && defined(CFG_ARM_GICV3)) |
| /* |
| * This cannot be enabled with TF-A and GICv3 because TF-A then need to |
| * assign the interrupt number of the UART to OP-TEE (S-EL1). Currently |
| * there's no way of TF-A to know which interrupts that OP-TEE will serve. |
| * If TF-A doesn't assign the interrupt we're enabling below to OP-TEE it |
| * will hang in EL3 since the interrupt will just be delivered again and |
| * again. |
| */ |
| static enum itr_return console_itr_cb(struct itr_handler *h __unused) |
| { |
| struct serial_chip *cons = &console_data.chip; |
| |
| while (cons->ops->have_rx_data(cons)) { |
| int ch __maybe_unused = cons->ops->getchar(cons); |
| |
| DMSG("cpu %zu: got 0x%x", get_core_pos(), ch); |
| } |
| return ITRR_HANDLED; |
| } |
| |
| static struct itr_handler console_itr = { |
| .it = IT_CONSOLE_UART, |
| .flags = ITRF_TRIGGER_LEVEL, |
| .handler = console_itr_cb, |
| }; |
| KEEP_PAGER(console_itr); |
| |
| static TEE_Result init_console_itr(void) |
| { |
| itr_add(&console_itr); |
| itr_enable(IT_CONSOLE_UART); |
| return TEE_SUCCESS; |
| } |
| driver_init(init_console_itr); |
| #endif |
| |
| #ifdef CFG_TZC400 |
| register_phys_mem_pgdir(MEM_AREA_IO_SEC, TZC400_BASE, TZC400_REG_SIZE); |
| |
| static TEE_Result init_tzc400(void) |
| { |
| void *va; |
| |
| DMSG("Initializing TZC400"); |
| |
| va = phys_to_virt(TZC400_BASE, MEM_AREA_IO_SEC); |
| if (!va) { |
| EMSG("TZC400 not mapped"); |
| panic(); |
| } |
| |
| tzc_init((vaddr_t)va); |
| tzc_dump_state(); |
| |
| return TEE_SUCCESS; |
| } |
| |
| service_init(init_tzc400); |
| #endif /*CFG_TZC400*/ |
| |
| #if defined(PLATFORM_FLAVOR_qemu_virt) |
| static void release_secondary_early_hpen(size_t pos) |
| { |
| struct mailbox { |
| uint64_t ep; |
| uint64_t hpen[]; |
| } *mailbox; |
| |
| if (cpu_mmu_enabled()) |
| mailbox = phys_to_virt(SECRAM_BASE, MEM_AREA_IO_SEC); |
| else |
| mailbox = (void *)SECRAM_BASE; |
| |
| if (!mailbox) |
| panic(); |
| |
| mailbox->ep = TEE_LOAD_ADDR; |
| dsb_ishst(); |
| mailbox->hpen[pos] = 1; |
| dsb_ishst(); |
| sev(); |
| } |
| |
| int psci_cpu_on(uint32_t core_id, uint32_t entry, uint32_t context_id) |
| { |
| size_t pos = get_core_pos_mpidr(core_id); |
| static bool core_is_released[CFG_TEE_CORE_NB_CORE]; |
| |
| if (!pos || pos >= CFG_TEE_CORE_NB_CORE) |
| return PSCI_RET_INVALID_PARAMETERS; |
| |
| DMSG("core pos: %zu: ns_entry %#" PRIx32, pos, entry); |
| |
| if (core_is_released[pos]) { |
| EMSG("core %zu already released", pos); |
| return PSCI_RET_DENIED; |
| } |
| core_is_released[pos] = true; |
| |
| generic_boot_set_core_ns_entry(pos, entry, context_id); |
| release_secondary_early_hpen(pos); |
| |
| return PSCI_RET_SUCCESS; |
| } |
| #endif /*PLATFORM_FLAVOR_qemu_virt*/ |