diff --git a/bsp/qemu-virt64-aarch64/drivers/board.c b/bsp/qemu-virt64-aarch64/drivers/board.c index 4bd5e1d2b6d5cbd8bb54cbf2c18a114b84683fd0..9096b6b3802649063519d33f8388952a87365c76 100644 --- a/bsp/qemu-virt64-aarch64/drivers/board.c +++ b/bsp/qemu-virt64-aarch64/drivers/board.c @@ -20,6 +20,12 @@ #endif #include "board.h" +#ifdef RT_USING_FDT +#include "interrupt.h" +#include "dtb_node.h" +#include +#endif + #ifdef RT_USING_USERSPACE struct mem_desc platform_mem_desc[] = { {KERNEL_VADDR_START, KERNEL_VADDR_START + 0x0fffffff, KERNEL_VADDR_START + PV_OFFSET, NORMAL_MEM} @@ -74,9 +80,19 @@ void rt_hw_board_init(void) /* initialize system heap */ rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END); - rt_components_board_init(); + /* support debug feature before components init */ + rt_hw_uart_init(); rt_console_set_device(RT_CONSOLE_DEVICE_NAME); +#if defined(RT_USING_FDT) && defined(RT_USING_SMP) + // TODO 0x44000000 should be replace by a variable + void * fdt_start = (void *)0x44000000 - PV_OFFSET; + device_tree_setup(fdt_start); + rt_hw_cpu_init(); +#endif + + rt_components_board_init(); + rt_thread_idle_sethook(idle_wfi); #ifdef RT_USING_SMP diff --git a/bsp/qemu-virt64-aarch64/drivers/board.h b/bsp/qemu-virt64-aarch64/drivers/board.h index ef7ae4c7dd1012da4c009535558d0b97cc0bbc1c..4f27cc37ac6fff5eee3004b36c3236fb137beb14 100644 --- a/bsp/qemu-virt64-aarch64/drivers/board.h +++ b/bsp/qemu-virt64-aarch64/drivers/board.h @@ -21,7 +21,7 @@ extern unsigned char __bss_end; #ifdef RT_USING_USERSPACE #define HEAP_END (rt_size_t)(KERNEL_VADDR_START + 64 * 1024 * 1024) -#define PAGE_START HEAP_END +#define PAGE_START HEAP_END + 1 * 1024 * 1024 #define PAGE_END ((rt_size_t)KERNEL_VADDR_START + 128 * 1024 * 1024) #else #define HEAP_END ((void *)HEAP_BEGIN + 64 * 1024 * 1024) @@ -29,4 +29,6 @@ extern unsigned char __bss_end; void rt_hw_board_init(void); +int rt_hw_uart_init(void); + #endif diff --git a/bsp/qemu-virt64-aarch64/drivers/drv_uart.c b/bsp/qemu-virt64-aarch64/drivers/drv_uart.c index 804d3afc998fb0f1a169d52044e3fbe5f93d014d..57d9b653eb59bb4a017dd1b1fd921d735f034a40 100644 --- a/bsp/qemu-virt64-aarch64/drivers/drv_uart.c +++ b/bsp/qemu-virt64-aarch64/drivers/drv_uart.c @@ -141,4 +141,3 @@ int rt_hw_uart_init(void) return 0; } -INIT_BOARD_EXPORT(rt_hw_uart_init); diff --git a/bsp/qemu-virt64-aarch64/drivers/secondary_cpu.c b/bsp/qemu-virt64-aarch64/drivers/secondary_cpu.c new file mode 100644 index 0000000000000000000000000000000000000000..c9602c6432f690db1612bfffd6265dafc07db14f --- /dev/null +++ b/bsp/qemu-virt64-aarch64/drivers/secondary_cpu.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#include +#include +#include +#include "gic.h" +#include "interrupt.h" +#include "mmu.h" + +#ifdef RT_USING_SMP + +extern unsigned long MMUTable[]; + +void rt_hw_secondary_cpu_bsp_start(void) +{ + rt_hw_spin_lock(&_cpus_lock); + + kernel_mmu_switch((unsigned long)MMUTable); + + // interrupt init + rt_hw_vector_init(); + + arm_gic_cpu_init(0, 0); + + // local timer init + + rt_system_scheduler_start(); +} + +#endif // SMP \ No newline at end of file diff --git a/bsp/qemu-virt64-aarch64/qemu.sh b/bsp/qemu-virt64-aarch64/qemu.sh index 40eeabc551003becfa8bcf04a4f78d3d46d362c3..1130c5429b693bf4d65a34a40f6a26bffcca16da 100755 --- a/bsp/qemu-virt64-aarch64/qemu.sh +++ b/bsp/qemu-virt64-aarch64/qemu.sh @@ -1,7 +1,7 @@ if [ ! -f "sd.bin" ]; then dd if=/dev/zero of=sd.bin bs=1024 count=65536 fi -qemu-system-aarch64 -M virt,gic-version=2 -cpu cortex-a53 -smp 4 -kernel rtthread.bin -nographic \ +qemu-system-aarch64 -M virt,gic-version=2,virtualization=on,secure=on -cpu cortex-a53 -smp 4 -kernel rtthread.bin -nographic -drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \ -netdev user,id=net0 -device virtio-net-device,netdev=net0,bus=virtio-mmio-bus.1 \ -device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0 diff --git a/bsp/qemu-virt64-aarch64/rtconfig.h b/bsp/qemu-virt64-aarch64/rtconfig.h index 8c5a491a7b79bf87102384b00e7badb5ce1d6955..376a55344518d7911684b1131a9c7753ed844bec 100644 --- a/bsp/qemu-virt64-aarch64/rtconfig.h +++ b/bsp/qemu-virt64-aarch64/rtconfig.h @@ -8,6 +8,8 @@ #define RT_NAME_MAX 16 #define RT_USING_SMART +#define RT_USING_SMP +#define RT_CPUS_NR 4 #define RT_ALIGN_SIZE 4 #define RT_THREAD_PRIORITY_32 #define RT_THREAD_PRIORITY_MAX 32 @@ -17,6 +19,7 @@ #define RT_USING_IDLE_HOOK #define RT_IDLE_HOOK_LIST_SIZE 4 #define IDLE_THREAD_STACK_SIZE 8192 +#define SYSTEM_THREAD_STACK_SIZE 8192 #define RT_USING_TIMER_SOFT #define RT_TIMER_THREAD_PRIO 4 #define RT_TIMER_THREAD_STACK_SIZE 8192 @@ -119,6 +122,8 @@ #define RT_SERIAL_RB_BUFSZ 256 #define RT_USING_TTY #define RT_USING_PIN +#define RT_USING_FDT +#define RT_USING_FDTLIB #define RT_USING_RTC #define RT_USING_VIRTIO #define RT_USING_VIRTIO10 diff --git a/components/drivers/rtc/rtc.c b/components/drivers/rtc/rtc.c index f8e8a43690f7843f8bbe49c73e4d05139191b50d..08a08c496fc457adca54098fbb2d0156b639f409 100644 --- a/components/drivers/rtc/rtc.c +++ b/components/drivers/rtc/rtc.c @@ -17,6 +17,7 @@ #include #include +#include /* Using NTP auto sync RTC time */ #ifdef RTC_SYNC_USING_NTP diff --git a/libcpu/aarch64/common/cpu.c b/libcpu/aarch64/common/cpu.c index 2467c96773a7d87ba5fc1f628e8ca0c2186157fe..724da69353e9430d293e44392e9ee31edb5cd1df 100644 --- a/libcpu/aarch64/common/cpu.c +++ b/libcpu/aarch64/common/cpu.c @@ -14,13 +14,19 @@ #include #include "cp15.h" +#define DBG_TAG "libcpu.aarch64.cpu" +#define DBG_LVL DBG_INFO +#include +#include +#include "cpu.h" + #ifdef RT_USING_SMP void rt_hw_spin_lock_init(rt_hw_spinlock_t *lock) { lock->slock = 0; } -#define TICKET_SHIFT 16 +#define TICKET_SHIFT 16 void rt_hw_spin_lock(rt_hw_spinlock_t *lock) { unsigned int tmp; @@ -62,6 +68,256 @@ void rt_hw_spin_unlock(rt_hw_spinlock_t *lock) : "r"(lock->tickets.owner + 1) : "memory"); } + +/** + * cpu_ops_tbl contains cpu_ops_t for each cpu kernel observed, + * given cpu logical id 'i', its cpu_ops_t is 'cpu_ops_tbl[i]' + */ +struct cpu_ops_t *cpu_ops_tbl[RT_CPUS_NR]; + +// _id_to_mpidr is a table translate logical id to mpid, which is a 64-bit value +rt_uint64_t rt_cpu_mpidr_early[RT_CPUS_NR] RT_WEAK = {[0 ... RT_CPUS_NR - 1] = ID_ERROR}; + +#ifdef RT_USING_FDT +#include "dtb_node.h" +struct dtb_node *_cpu_node[RT_CPUS_NR]; +#endif /* RT_USING_FDT */ + +#define MPIDR_AFF_MASK 0x000000FF00FFFFFFul +#define REPORT_ERR(retval) LOG_E("got error code %d in %s(), %s:%d", (retval), __func__, __FILE__, __LINE__) +#define CHECK_RETVAL(retval) if (retval) {REPORT_ERR(retval);} + +static int _cpus_init_data_hardcoded(int num_cpus, rt_uint64_t *cpu_hw_ids, struct cpu_ops_t *cpu_ops[]) +{ + // load in cpu_hw_ids in cpuid_to_hwid, + // cpu_ops to cpu_ops_tbl + if (num_cpus > RT_CPUS_NR) + { + LOG_W("num_cpus (%d) greater than RT_CPUS_NR (%d)\n", num_cpus, RT_CPUS_NR); + num_cpus = RT_CPUS_NR; + } + + for (int i = 0; i < num_cpus; i++) + { + set_hwid(i, cpu_hw_ids[i]); + cpu_ops_tbl[i] = cpu_ops[i]; + } + return 0; +} + +#ifdef RT_USING_FDT + +/** read ('size' * 4) bytes number from start, big-endian format */ +static rt_uint64_t _read_be_number(void *start, int size) +{ + rt_uint64_t buf = 0; + for (; size > 0; size--) + buf = (buf << 32) | fdt32_to_cpu(*(uint32_t *)start++); + return buf; +} + +/** check device-type of the node, */ +static bool _node_is_cpu(struct dtb_node *node) +{ + char *device_type = dtb_node_get_dtb_node_property_value(node, "device_type", NULL); + if (device_type) + { + return !strcmp(device_type, "cpu"); + } + return false; +} + +static int _read_and_set_hwid(struct dtb_node *cpu, int *id_pool, int *pcpuid) +{ + // size/address_cells is number of elements in reg array + int size; + static int address_cells, size_cells; + if (!address_cells && !size_cells) + dtb_node_get_dtb_node_cells(cpu, &address_cells, &size_cells); + + void *id_start = dtb_node_get_dtb_node_property_value(cpu, "reg", &size); + rt_uint64_t mpid = _read_be_number(id_start, address_cells); + + *pcpuid = *id_pool; + *id_pool = *id_pool + 1; + set_hwid(*pcpuid, mpid); + + LOG_I("Using MPID 0x%lx as cpu %d", mpid, *pcpuid); + + // setting _cpu_node for cpu_init use + _cpu_node[*pcpuid] = cpu; + + return 0; +} + +static int _read_and_set_cpuops(struct dtb_node *cpu, int cpuid) +{ + char *method = dtb_node_get_dtb_node_property_value(cpu, "enable-method", NULL); + if (!method) + { + LOG_E("Cannot read method from cpu node"); + return -1; + } + + struct cpu_ops_t *cpu_ops; + if (!strcmp(method, cpu_ops_psci.method)) + { + cpu_ops = &cpu_ops_psci; + } + else if (!strcmp(method, cpu_ops_spin_tbl.method)) + { + cpu_ops = &cpu_ops_spin_tbl; + } + else + { + cpu_ops = RT_NULL; + LOG_E("Not supported cpu_ops: %s", method); + } + cpu_ops_tbl[cpuid] = cpu_ops; + + LOG_D("Using boot method [%s] for cpu %d", cpu_ops->method, cpuid); + return 0; +} + +static int _cpus_init_data_fdt() +{ + // cpuid_to_hwid and cpu_ops_tbl with fdt + void *root = get_dtb_node_head(); + int id_pool = 0; + int cpuid; + struct dtb_node *cpus = dtb_node_get_dtb_node_by_path(root, "/cpus"); + + // for each cpu node (device-type is cpu), read its mpid and set its cpuid_to_hwid + for_each_node_child(cpus) + { + if (!_node_is_cpu(cpus)) + { + continue; + } + + if (id_pool > RT_CPUS_NR) + { + LOG_W("Reading more cpus from FDT than RT_CPUS_NR" + "\n Parsing will not continue and only %d cpus will be used.", RT_CPUS_NR); + break; + } + + _read_and_set_hwid(cpus, &id_pool, &cpuid); + + _read_and_set_cpuops(cpus, cpuid); + } + return 0; +} + +#endif /* RT_USING_FDT */ + +/** init cpu with hardcoded infomation or parsing from FDT */ +static int _cpus_init(int num_cpus, rt_uint64_t *cpu_hw_ids, struct cpu_ops_t *cpu_ops[]) +{ + int retval; + + // first setup cpu_ops_tbl and cpuid_to_hwid + if (num_cpus > 0) + retval = _cpus_init_data_hardcoded(num_cpus, cpu_hw_ids, cpu_ops); + else + { + retval = -1; +#ifdef RT_USING_FDT + retval = _cpus_init_data_fdt(); +#endif + } + + if (retval) + return retval; + + // using cpuid_to_hwid and cpu_ops_tbl to call method_init and cpu_init + // assuming that cpuid 0 has already init + for (int i = 1; i < RT_CPUS_NR; i++) + { + if (cpuid_to_hwid(i) == ID_ERROR) + { + LOG_E("Failed to find hardware id of CPU %d", i); + continue; + } + + if (cpu_ops_tbl[i] && cpu_ops_tbl[i]->cpu_init) + { + retval = cpu_ops_tbl[i]->cpu_init(i); + CHECK_RETVAL(retval); + } + else + { + LOG_E("Failed to find cpu_init for cpu %d with cpu_ops[%p], cpu_ops->cpu_init[%p]" + , cpuid_to_hwid(i), cpu_ops_tbl[i], cpu_ops_tbl[i] ? cpu_ops_tbl[i]->cpu_init : NULL); + } + } + return 0; +} + +static void _boot_secondary(void) +{ + for (int i = 1; i < RT_CPUS_NR; i++) + { + int retval = -0xbad0; // mark no support operation + if (cpu_ops_tbl[i] && cpu_ops_tbl[i]->cpu_boot) + retval = cpu_ops_tbl[i]->cpu_boot(i); + if (retval) + { + LOG_E("Failed to boot secondary CPU %d, error code %d", i, retval); + } else { + LOG_I("Secondary CPU %d booted", i); + } + } +} + +RT_WEAK void rt_hw_secondary_cpu_up(void) +{ + _boot_secondary(); +} + +/** + * @brief boot cpu with hardcoded data + * + * @param num_cpus number of cpus + * @param cpu_hw_ids each element represents a hwid of cpu[i] + * @param cpu_ops each element represents a pointer to cpu_ops of cpu[i] + * @return int 0 on success, + */ +int rt_hw_cpu_boot_secondary(int num_cpus, rt_uint64_t *cpu_hw_ids, struct cpu_ops_t *cpu_ops[]) +{ + int retval = 0; + if (num_cpus < 1 || !cpu_hw_ids || !cpu_ops) + return -1; + + retval = _cpus_init(num_cpus, cpu_hw_ids, cpu_ops); + CHECK_RETVAL(retval); + + return retval; +} + +#define CPU_INIT_USING_FDT 0,0,0 + +/** + * @brief Initialize cpu infomation from fdt + * + * @return int + */ +int rt_hw_cpu_init() +{ +#ifdef RT_USING_FDT + return _cpus_init(CPU_INIT_USING_FDT); +#else + LOG_E("CPU init failed since RT_USING_FDT was not defined"); + return -0xa; /* no fdt support */ +#endif /* RT_USING_FDT */ +} + +RT_WEAK void rt_hw_secondary_cpu_idle_exec(void) +{ + asm volatile("wfe" :: + : "memory", "cc"); +} + #endif /*RT_USING_SMP*/ /** diff --git a/libcpu/aarch64/common/cpu.h b/libcpu/aarch64/common/cpu.h new file mode 100644 index 0000000000000000000000000000000000000000..2bc3d0dc362a8948d2c89a84c6c65457529f723a --- /dev/null +++ b/libcpu/aarch64/common/cpu.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006-2019, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#ifndef __RT_HW_CPU_H__ +#define __RT_HW_CPU_H__ + +#include +#include +#include + +struct cpu_ops_t +{ + const char *method; + int (*cpu_init)(rt_uint32_t id); + int (*cpu_boot)(rt_uint32_t id); + void (*cpu_shutdown)(void); +}; + +/** + * Identifier to mark a wrong CPU MPID. + * All elements in rt_cpu_mpidr_early[] should be initialized with this value + */ +#define ID_ERROR __INT64_MAX__ + +extern rt_uint64_t rt_cpu_mpidr_early[]; +extern struct dtb_node *_cpu_node[]; + +#define cpuid_to_hwid(cpuid) \ + ((((cpuid) >= 0) && ((cpuid) < RT_CPUS_NR)) ? rt_cpu_mpidr_early[cpuid] : ID_ERROR) +#define set_hwid(cpuid, hwid) \ + ((((cpuid) >= 0) && ((cpuid) < RT_CPUS_NR)) ? (rt_cpu_mpidr_early[cpuid] = (hwid)) : ID_ERROR) +#define get_cpu_node(cpuid) \ + ((((cpuid) >= 0) && ((cpuid) < RT_CPUS_NR)) ? _cpu_node[cpuid] : NULL) +#define set_cpu_node(cpuid, node) \ + ((((cpuid) >= 0) && ((cpuid) < RT_CPUS_NR)) ? (_cpu_node[cpuid] = node) : NULL) + +extern void rt_hw_cpu_shutdown(void); + +extern int rt_hw_cpu_init(); + +extern int rt_hw_cpu_boot_secondary(int num_cpus, rt_uint64_t *cpu_hw_ids, struct cpu_ops_t *cpu_ops[]); + +extern void rt_hw_secondary_cpu_idle_exec(void); + +extern struct cpu_ops_t cpu_ops_psci; + +extern struct cpu_ops_t cpu_ops_spin_tbl; + +#endif /* __RT_HW_CPU_H__ */ \ No newline at end of file diff --git a/libcpu/aarch64/common/cpu_ops_common.h b/libcpu/aarch64/common/cpu_ops_common.h new file mode 100644 index 0000000000000000000000000000000000000000..3ac0a363c39b95811cf1716ce2207aa8b1ff74d6 --- /dev/null +++ b/libcpu/aarch64/common/cpu_ops_common.h @@ -0,0 +1,21 @@ +#ifndef __CPU_OPS_COMMON_H__ +#define __CPU_OPS_COMMON_H__ + +#include +#include +#include +#include "entry_point.h" + +static inline rt_uint64_t get_secondary_entry_pa(void) +{ + rt_uint64_t secondary_entry_pa = (rt_uint64_t)rt_hw_mmu_v2p(&mmu_info, _secondary_cpu_entry); + + if (!secondary_entry_pa) + { + LOG_E("Failed to translate 'secondary_entry_pa' to physical address"); + return 0; + } + return secondary_entry_pa; +} + +#endif /* __CPU_OPS_COMMON_H__ */ \ No newline at end of file diff --git a/libcpu/aarch64/common/cpu_psci.c b/libcpu/aarch64/common/cpu_psci.c new file mode 100644 index 0000000000000000000000000000000000000000..8bdfcc95c38721c04bbe38a0fac75ebb5be543a8 --- /dev/null +++ b/libcpu/aarch64/common/cpu_psci.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2006-2019, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#include +#include +#include + +#define DBG_TAG "libcpu.aarch64.cpu_psci" +#define DBG_LVL DBG_INFO +#include +#include "cpu_ops_common.h" + +#include "cpu.h" +#include "errno.h" +#include "psci.h" +#include "psci_api.h" + +static int (*_psci_init)(void) = psci_init; + +static int __call_method_init() +{ + int (*init)(void) = _psci_init; + _psci_init = RT_NULL; + + return init(); +} + +/** return 0 on success, otherwise failed */ +#define _call_method_init() ((_psci_init) ? __call_method_init() : 0); + +static int cpu_psci_cpu_init(rt_uint32_t cpuid) +{ + // init psci only once + return _call_method_init(); +} + +static int cpu_psci_cpu_boot(rt_uint32_t cpuid) +{ + rt_uint64_t secondary_entry_pa = get_secondary_entry_pa(); + + if (!secondary_entry_pa) + return -1; + + if (!psci_ops.cpu_on) { + LOG_E("Uninitialized psci operation"); + return -1; + } + return psci_ops.cpu_on(cpuid_to_hwid(cpuid), secondary_entry_pa); +} + +struct cpu_ops_t cpu_ops_psci = { + .method = "psci", + .cpu_boot = cpu_psci_cpu_boot, + .cpu_init = cpu_psci_cpu_init, + .cpu_shutdown = RT_NULL +}; diff --git a/libcpu/aarch64/common/cpu_spin_table.c b/libcpu/aarch64/common/cpu_spin_table.c new file mode 100644 index 0000000000000000000000000000000000000000..26ddb58fede00a173f88efa600df2312b4364770 --- /dev/null +++ b/libcpu/aarch64/common/cpu_spin_table.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#include +#include +#include +#include "cpu.h" + +#define DBG_TAG "libcpu.aarch64.cpu_spin_table" +#define DBG_LVL DBG_INFO +#include +#include "cpu_ops_common.h" + +#ifdef RT_USING_FDT +#include + +static rt_uint64_t cpu_release_addr[RT_CPUS_NR]; + +static int spin_table_cpu_init(rt_uint32_t cpuid) +{ + struct dtb_node *cpu = get_cpu_node(cpuid); + if (!cpu) + return -1; /* uninitialized cpu node in fdt */ + + int size; + rt_uint64_t *phead = (rt_uint64_t*)dtb_node_get_dtb_node_property_value(cpu, "cpu-release-addr", &size); + cpu_release_addr[cpuid] = fdt64_to_cpu(*phead); + + LOG_D("Using release address 0x%p for CPU %d", cpu_release_addr[cpuid], cpuid); + return 0; +} + +static int spin_table_cpu_boot(rt_uint32_t cpuid) +{ + rt_uint64_t secondary_entry_pa = get_secondary_entry_pa(); + if (!secondary_entry_pa) + return -1; + + // map release_addr to addressable place + void *rel_va = rt_ioremap((void *)cpu_release_addr[cpuid], sizeof(cpu_release_addr[0])); + + if (!rel_va) + { + LOG_E("IO remap failing"); + return -1; + } + + __asm__ volatile("str %0, [%1]" ::"rZ"(secondary_entry_pa), "r"(rel_va)); + __asm__ volatile("dsb sy"); + __asm__ volatile("sev"); + rt_iounmap(rel_va); + return 0; +} +#endif /* RT_USING_FDT */ + +struct cpu_ops_t cpu_ops_spin_tbl = { + .method = "spin-table", +#ifdef RT_USING_FDT + .cpu_init = spin_table_cpu_init, + .cpu_boot = spin_table_cpu_boot, +#endif +}; diff --git a/libcpu/aarch64/common/entry_point.h b/libcpu/aarch64/common/entry_point.h new file mode 100644 index 0000000000000000000000000000000000000000..fc3e6c061f1289f412f77e4091b9e539bd877c29 --- /dev/null +++ b/libcpu/aarch64/common/entry_point.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#ifndef __ENTRY_POINT_H__ +#define __ENTRY_POINT_H__ + +extern void _secondary_cpu_entry(void); +#endif /* __ENTRY_POINT_H__ */ \ No newline at end of file diff --git a/libcpu/aarch64/common/mmu.h b/libcpu/aarch64/common/mmu.h index 81bba3a43e80e2be22528805eccaf33f237ba14b..abd8191f1d1cb24762cccab1cb708c7af8fe4b4a 100644 --- a/libcpu/aarch64/common/mmu.h +++ b/libcpu/aarch64/common/mmu.h @@ -135,4 +135,8 @@ void *rt_hw_mmu_v2p(rt_mmu_info *mmu_info, void* v_addr); void rt_mm_lock(void); void rt_mm_unlock(void); +void kernel_mmu_switch(unsigned long tbl); + +extern rt_mmu_info mmu_info; + #endif diff --git a/libcpu/aarch64/common/psci.c b/libcpu/aarch64/common/psci.c new file mode 100644 index 0000000000000000000000000000000000000000..8fc23a673fc925ce5719ff45ef18780037728280 --- /dev/null +++ b/libcpu/aarch64/common/psci.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#include +#include +#include +#include +#include +#include "psci.h" +#include "psci_api.h" +#include "smccc.h" + +#define DBG_TAG "libcpu.aarch64.psci" +#define DBG_LVL DBG_INFO +#include + +/** template for creating 4 PSCI ops: SUSPEND, OFF, ON, MIGRATE */ +#define COMMON_PSCI_OPS_TEMPLATE(VER, SUSPEND, OFF, ON, MIGRATE) \ + static int psci_##VER##_cpu_suspend(uint32_t state, unsigned long entry_point) \ + { \ + return psci_call((SUSPEND), state, entry_point, 0); \ + } \ + static int psci_##VER##_cpu_off(uint32_t state) \ + { \ + return psci_call((OFF), state, 0, 0); \ + } \ + static int psci_##VER##_cpu_on(unsigned long cpuid, unsigned long entry_point) \ + { \ + return psci_call((ON), cpuid, entry_point, 0); \ + } \ + static int psci_##VER##_migrate(unsigned long cpuid) \ + { \ + return psci_call((MIGRATE), cpuid, 0, 0); \ + } + +#ifdef RT_USING_FDT +#include "dtb_node.h" + +struct psci_ops_t psci_ops; + +#if __SIZE_WIDTH__ == 64 +#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name +#else +#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name +#endif + +/** + * SMCCC can use either smc or hvc method + * smccc_call will be init to proper interface when psci_init() was executed + */ +static void (*smccc_call)(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + unsigned long a6, unsigned long a7, struct arm_smccc_res_t *res, + struct arm_smccc_quirk_t *quirk); + +static rt_uint32_t psci_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3) +{ + struct arm_smccc_res_t res; + smccc_call(a0, a1, a2, a3, 0, 0, 0, 0, &res, (void *)0); + return res.a0; +} + +static int _psci_probe_version(char *version, int *major, int *minor); +static int _psci_init_with_version(int major, int minor); + +static struct dtb_node *psci_node; + +static int psci_ver_major; +static int psci_ver_minor; + +/** + * @brief init psci operations. + * using device tree to probe version and psci-method, + * setup psci ops for future use + * + * @return int 0 on success + */ +int psci_init() +{ + void *root = get_dtb_node_head(); + psci_node = dtb_node_get_dtb_node_by_path(root, "/psci"); + if (!psci_node) + { + return -1; + } + char *compatible = dtb_node_get_dtb_node_property_value(psci_node, "compatible", NULL); + char *method = dtb_node_get_dtb_node_property_value(psci_node, "method", NULL); + + int retval = 0; + + // setup psci-method + if (!strcmp("hvc", method)) + { + smccc_call = arm_smccc_hvc; + } + else if (!strcmp("smc", method)) + { + smccc_call = arm_smccc_smc; + } + else + { + LOG_E("Unknown PSCI method: %s", method); + return -1; + } + + retval = _psci_probe_version(compatible, &psci_ver_major, &psci_ver_minor); + if (retval != 0) + return retval; + + // init psci_ops with specified psci version + retval = _psci_init_with_version(psci_ver_major, psci_ver_minor); + + return retval; +} + +/* function id of PSCI v0.1 should be probed in FDT, they are implementation defined value */ +static rt_uint32_t cpu_suspend_0_1; +static rt_uint32_t cpu_off_0_1; +static rt_uint32_t cpu_on_0_1; +static rt_uint32_t migrate_0_1; + +/* basic operations TEMPLATE for API since 0.1 version */ +COMMON_PSCI_OPS_TEMPLATE(0_1, cpu_suspend_0_1, cpu_off_0_1, cpu_on_0_1, migrate_0_1); + +/* used for v0.1 only, rely on FDT to probe function id */ +#define PROBE_AND_SET(FUNC_NAME) \ + do \ + { \ + int num_of_elem; \ + funcid = \ + dtb_node_get_dtb_node_property_value(psci_node, #FUNC_NAME, &num_of_elem); \ + if (num_of_elem != 4 || funcid == 0 || *funcid == 0) \ + { \ + LOG_E("Failed to probe " #FUNC_NAME " in FDT"); \ + } \ + else \ + { \ + FUNC_NAME##_0_1 = (rt_uint32_t)fdt32_to_cpu(*funcid); \ + psci_ops.FUNC_NAME = psci_0_1_##FUNC_NAME; \ + } \ + } while (0) + +static int psci_0_1_init() +{ + // reading function id from fdt + rt_uint32_t *funcid; + PROBE_AND_SET(cpu_suspend); + PROBE_AND_SET(cpu_off); + PROBE_AND_SET(cpu_on); + PROBE_AND_SET(migrate); + return 0; +} + +COMMON_PSCI_OPS_TEMPLATE(0_2, PSCI_FN_NATIVE(0_2, CPU_SUSPEND), PSCI_0_2_FN_CPU_OFF, PSCI_FN_NATIVE(0_2, CPU_ON), PSCI_FN_NATIVE(0_2, MIGRATE)); + +static rt_uint32_t psci_0_2_get_version(void) +{ + return psci_call(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); +} + +static void psci_0_2_set_basic_ops() +{ + psci_ops = (struct psci_ops_t){ + .get_version = psci_0_2_get_version, + + // followings API are v0.1 compatible + .cpu_suspend = psci_0_2_cpu_suspend, + .cpu_off = psci_0_2_cpu_off, + .cpu_on = psci_0_2_cpu_on, + .migrate = psci_0_2_migrate, + }; +} + +static int psci_0_2_init() +{ + psci_0_2_set_basic_ops(); + + // TODO init other version 0.2 features... + return 0; +} + +static int psci_1_0_init() +{ + psci_0_2_init(); + + // TODO init other version 1.0 features... + return 0; +} + +/* probe psci version from fdt or SMC call */ +static int _psci_probe_version(char *version, int *major, int *minor) +{ + int retval = 0; + // if strcmp compatible 'arm,psci-0.1' + if (!strcmp(version, "arm,psci")) + { + *major = 0; + *minor = 1; + } + else if (!strncmp(version, "arm,psci-", 8)) + { + // since psci-0.2, using psci call to probe version + rt_uint32_t ret = psci_0_2_get_version(); + *major = PSCI_VERSION_MAJOR(ret); + *minor = PSCI_VERSION_MINOR(ret); + } + else + { + LOG_E("[%s] was not a proper PSCI version", version); + retval = -1; + } + LOG_D("Using PSCI v%d.%d", *major, *minor); + return retval; +} + +/* init psci ops with version info */ +static int _psci_init_with_version(int major, int minor) +{ + int retval = -0xbeef; // mark unsupported + if (major == 0) + { + // for v0.1, psci function id was provided fdt + if (minor == 1) + { + retval = psci_0_1_init(); + } + else if (minor == 2) + { + retval = psci_0_2_init(); + } + } + else if (major == 1) + { + // psci_1_0_init is a base setup for version after v1.0 + retval = psci_1_0_init(); + } + + if (retval == -0xbeef) + { + LOG_E("PSCI init with incompatible version %d.%d", major, minor); + } + return retval; +} + +#endif /* RT_USING_FDT */ \ No newline at end of file diff --git a/libcpu/aarch64/common/psci.h b/libcpu/aarch64/common/psci.h new file mode 100644 index 0000000000000000000000000000000000000000..3cce66211f0a7b499f4093bc4d63a567f613e512 --- /dev/null +++ b/libcpu/aarch64/common/psci.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#ifndef __PSCI_H__ +#define __PSCI_H__ + +/** + * PSCI protocol content + * For PSCI v0.1, only return values below are protocol defined + */ + +/* PSCI v0.2 interface */ +#define PSCI_0_2_FN_BASE 0x84000000 +#define PSCI_0_2_FN(n) (PSCI_0_2_FN_BASE + (n)) +#define PSCI_0_2_64BIT 0x40000000 +#define PSCI_0_2_FN64_BASE (PSCI_0_2_FN_BASE + PSCI_0_2_64BIT) +#define PSCI_0_2_FN64(n) (PSCI_0_2_FN64_BASE + (n)) + +#define PSCI_0_2_FN_PSCI_VERSION PSCI_0_2_FN(0) +#define PSCI_0_2_FN_CPU_SUSPEND PSCI_0_2_FN(1) +#define PSCI_0_2_FN_CPU_OFF PSCI_0_2_FN(2) +#define PSCI_0_2_FN_CPU_ON PSCI_0_2_FN(3) +#define PSCI_0_2_FN_AFFINITY_INFO PSCI_0_2_FN(4) +#define PSCI_0_2_FN_MIGRATE PSCI_0_2_FN(5) +#define PSCI_0_2_FN_MIGRATE_INFO_TYPE PSCI_0_2_FN(6) +#define PSCI_0_2_FN_MIGRATE_INFO_UP_CPU PSCI_0_2_FN(7) +#define PSCI_0_2_FN_SYSTEM_OFF PSCI_0_2_FN(8) +#define PSCI_0_2_FN_SYSTEM_RESET PSCI_0_2_FN(9) + +#define PSCI_0_2_FN64_CPU_SUSPEND PSCI_0_2_FN64(1) +#define PSCI_0_2_FN64_CPU_ON PSCI_0_2_FN64(3) +#define PSCI_0_2_FN64_AFFINITY_INFO PSCI_0_2_FN64(4) +#define PSCI_0_2_FN64_MIGRATE (5) +#define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU PSCI_0_2_FN64(7) + +#define PSCI_1_0_FN_PSCI_FEATURES PSCI_0_2_FN(10) +#define PSCI_1_0_FN_SYSTEM_SUSPEND PSCI_0_2_FN(14) +#define PSCI_1_0_FN_SET_SUSPEND_MODE PSCI_0_2_FN(15) +#define PSCI_1_1_FN_SYSTEM_RESET2 PSCI_0_2_FN(18) + +#define PSCI_1_0_FN64_SYSTEM_SUSPEND PSCI_0_2_FN64(14) +#define PSCI_1_1_FN64_SYSTEM_RESET2 PSCI_0_2_FN64(18) + +/* PSCI v0.2 power state encoding for CPU_SUSPEND function */ +#define PSCI_0_2_POWER_STATE_ID_MASK 0xffff +#define PSCI_0_2_POWER_STATE_ID_SHIFT 0 +#define PSCI_0_2_POWER_STATE_TYPE_SHIFT 16 +#define PSCI_0_2_POWER_STATE_TYPE_MASK (0x1 << PSCI_0_2_POWER_STATE_TYPE_SHIFT) +#define PSCI_0_2_POWER_STATE_AFFL_SHIFT 24 +#define PSCI_0_2_POWER_STATE_AFFL_MASK (0x3 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) + +/* PSCI extended power state encoding for CPU_SUSPEND function */ +#define PSCI_1_0_EXT_POWER_STATE_ID_MASK 0xfffffff +#define PSCI_1_0_EXT_POWER_STATE_ID_SHIFT 0 +#define PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT 30 +#define PSCI_1_0_EXT_POWER_STATE_TYPE_MASK (0x1 << PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT) + +/* PSCI v0.2 affinity level state returned by AFFINITY_INFO */ +#define PSCI_0_2_AFFINITY_LEVEL_ON 0 +#define PSCI_0_2_AFFINITY_LEVEL_OFF 1 +#define PSCI_0_2_AFFINITY_LEVEL_ON_PENDING 2 + +/* PSCI v0.2 multicore support in Trusted OS returned by MIGRATE_INFO_TYPE */ +#define PSCI_0_2_TOS_UP_MIGRATE 0 +#define PSCI_0_2_TOS_UP_NO_MIGRATE 1 +#define PSCI_0_2_TOS_MP 2 + +/* PSCI version decoding (independent of PSCI version) */ +#define PSCI_VERSION_MAJOR_SHIFT 16 +#define PSCI_VERSION_MINOR_MASK ((1U << PSCI_VERSION_MAJOR_SHIFT) - 1) +#define PSCI_VERSION_MAJOR_MASK ~PSCI_VERSION_MINOR_MASK +#define PSCI_VERSION_MAJOR(ver) (((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT) +#define PSCI_VERSION_MINOR(ver) ((ver) & PSCI_VERSION_MINOR_MASK) +#define PSCI_VERSION(maj, min) \ + ((((maj) << PSCI_VERSION_MAJOR_SHIFT) & PSCI_VERSION_MAJOR_MASK) | \ + ((min) & PSCI_VERSION_MINOR_MASK)) + +/* PSCI features decoding (>=1.0) */ +#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT 1 +#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK (0x1 << PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT) + +#define PSCI_1_0_OS_INITIATED BIT(0) +#define PSCI_1_0_SUSPEND_MODE_PC 0 +#define PSCI_1_0_SUSPEND_MODE_OSI 1 + +/* PSCI return values (inclusive of all PSCI versions) */ +#define PSCI_RET_SUCCESS 0 +#define PSCI_RET_NOT_SUPPORTED -1 +#define PSCI_RET_INVALID_PARAMS -2 +#define PSCI_RET_DENIED -3 +#define PSCI_RET_ALREADY_ON -4 +#define PSCI_RET_ON_PENDING -5 +#define PSCI_RET_INTERNAL_FAILURE -6 +#define PSCI_RET_NOT_PRESENT -7 +#define PSCI_RET_DISABLED -8 +#define PSCI_RET_INVALID_ADDRESS -9 + +#endif /*__PSCI_H__*/ diff --git a/libcpu/aarch64/common/psci_api.h b/libcpu/aarch64/common/psci_api.h new file mode 100644 index 0000000000000000000000000000000000000000..8c939f3abc838adae792644b081cfd4b740e6ab0 --- /dev/null +++ b/libcpu/aarch64/common/psci_api.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#ifndef __PSCI_API_H__ +#define __PSCI_API_H__ + +#include +#include +#include +#include "psci_api.h" + +/** generic psci ops supported v0.1 v0.2 v1.0 v1.1 */ +struct psci_ops_t +{ + uint32_t (*get_version)(void); + int32_t (*cpu_suspend)(uint32_t state, unsigned long entry_point); + int32_t (*cpu_off)(uint32_t state); + int32_t (*cpu_on)(unsigned long cpuid, unsigned long entry_point); + int32_t (*migrate)(unsigned long cpuid); +}; + +extern struct psci_ops_t psci_ops; + +extern int psci_init(void); + +#endif // __PSCI_API_H__ diff --git a/libcpu/aarch64/common/smccc.S b/libcpu/aarch64/common/smccc.S new file mode 100644 index 0000000000000000000000000000000000000000..6428bc988be4f408d0e1314fd76876858b1e6f59 --- /dev/null +++ b/libcpu/aarch64/common/smccc.S @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +/** + * SMCCC v0.2 + * ARM DEN0028E chapter 2.6 + */ + .macro SMCCC instr + stp x29, x30, [sp, #-16]! + mov x29, sp + \instr #0 + // store in arm_smccc_res + ldr x4, [sp, #16] + stp x0, x1, [x4, #0] + stp x2, x3, [x4, #16] +1: + ldp x29, x30, [sp], #16 + ret + .endm + +.global arm_smccc_smc +arm_smccc_smc: + SMCCC smc + +.global arm_smccc_hvc +arm_smccc_hvc: + SMCCC hvc diff --git a/libcpu/aarch64/common/smccc.h b/libcpu/aarch64/common/smccc.h new file mode 100644 index 0000000000000000000000000000000000000000..3b2283ac507b94246479a1277b35f28a990d8fd6 --- /dev/null +++ b/libcpu/aarch64/common/smccc.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2006-2019, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#ifndef __SMCCC_H__ +#define __SMCCC_H__ + +/** + * result from SMC/HVC call + * ARM DEN0028E chapter 5, + */ +typedef struct arm_smccc_res_t +{ + unsigned long a0; + // reserved for ARM SMC and HVC Fast Call services + unsigned long a1; + unsigned long a2; + unsigned long a3; +} arm_smccc_res_t; + +/** + * quirk is a structure contains vendor specified information, + * it just a placeholder currently + */ +struct arm_smccc_quirk_t +{ +}; + +/* smccc version 0.2 */ + +void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + unsigned long a6, unsigned long a7, struct arm_smccc_res_t *res, + struct arm_smccc_quirk_t *quirk); + +void arm_smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + unsigned long a6, unsigned long a7, struct arm_smccc_res_t *res, + struct arm_smccc_quirk_t *quirk); + +#endif /* __SMCCC_H__ */