1bd744d46SAndrew Jones // SPDX-License-Identifier: GPL-2.0-only
2bd744d46SAndrew Jones /*
3bd744d46SAndrew Jones * Initialize machine setup information and I/O.
4bd744d46SAndrew Jones *
5bd744d46SAndrew Jones * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
6bd744d46SAndrew Jones */
7bd744d46SAndrew Jones #include <libcflat.h>
822f287f4SAndrew Jones #include <alloc.h>
96895ce6dSAndrew Jones #include <alloc_page.h>
1022f287f4SAndrew Jones #include <alloc_phys.h>
1122f287f4SAndrew Jones #include <argv.h>
1223100d97SAndrew Jones #include <auxinfo.h>
1322f287f4SAndrew Jones #include <cpumask.h>
1422f287f4SAndrew Jones #include <devicetree.h>
156895ce6dSAndrew Jones #include <memregions.h>
169c92b28eSAndrew Jones #include <on-cpus.h>
1723100d97SAndrew Jones #include <vmalloc.h>
1822f287f4SAndrew Jones #include <asm/csr.h>
19ad435a71SAndrew Jones #include <asm/mmu.h>
2022f287f4SAndrew Jones #include <asm/page.h>
21386561f8SAndrew Jones #include <asm/processor.h>
22d4827ec0SAndrew Jones #include <asm/sbi.h>
23bd744d46SAndrew Jones #include <asm/setup.h>
2493bcbb09SJames Raphael Tiovalen #include <asm/timer.h>
25bd744d46SAndrew Jones
266895ce6dSAndrew Jones #define VA_BASE ((phys_addr_t)3 * SZ_1G)
2723100d97SAndrew Jones #if __riscv_xlen == 64
2823100d97SAndrew Jones #define VA_TOP ((phys_addr_t)4 * SZ_1G)
2923100d97SAndrew Jones #else
3023100d97SAndrew Jones #define VA_TOP ((phys_addr_t)0)
3123100d97SAndrew Jones #endif
326895ce6dSAndrew Jones
336895ce6dSAndrew Jones #define MAX_DT_MEM_REGIONS 16
34*f35fe0eeSAndrew Jones #ifdef CONFIG_EFI
35*f35fe0eeSAndrew Jones #define NR_MEM_REGIONS (MAX_DT_MEM_REGIONS + 128)
36*f35fe0eeSAndrew Jones #else
376895ce6dSAndrew Jones #define NR_MEM_REGIONS (MAX_DT_MEM_REGIONS + 16)
38*f35fe0eeSAndrew Jones #endif
396895ce6dSAndrew Jones
4031b4ef45SAndrew Jones extern unsigned long _etext;
4131b4ef45SAndrew Jones
4222f287f4SAndrew Jones char *initrd;
4322f287f4SAndrew Jones u32 initrd_size;
4422f287f4SAndrew Jones
45386561f8SAndrew Jones struct thread_info cpus[NR_CPUS];
4622f287f4SAndrew Jones int nr_cpus;
4793bcbb09SJames Raphael Tiovalen uint64_t timebase_frequency;
4822f287f4SAndrew Jones
496895ce6dSAndrew Jones static struct mem_region riscv_mem_regions[NR_MEM_REGIONS + 1];
506895ce6dSAndrew Jones
cpu_set_fdt(int fdtnode __unused,u64 regval,void * info __unused)5122f287f4SAndrew Jones static void cpu_set_fdt(int fdtnode __unused, u64 regval, void *info __unused)
5222f287f4SAndrew Jones {
5322f287f4SAndrew Jones int cpu = nr_cpus++;
5422f287f4SAndrew Jones
5522f287f4SAndrew Jones assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
5622f287f4SAndrew Jones
57386561f8SAndrew Jones cpus[cpu].cpu = cpu;
58386561f8SAndrew Jones cpus[cpu].hartid = regval;
59d4827ec0SAndrew Jones
60d4827ec0SAndrew Jones if (!sbi_hart_get_status(cpus[cpu].hartid).error)
6122f287f4SAndrew Jones set_cpu_present(cpu, true);
6222f287f4SAndrew Jones }
6322f287f4SAndrew Jones
cpu_init_acpi(void)6422f287f4SAndrew Jones static void cpu_init_acpi(void)
6522f287f4SAndrew Jones {
6622f287f4SAndrew Jones assert_msg(false, "ACPI not available");
6722f287f4SAndrew Jones }
6822f287f4SAndrew Jones
cpu_init(void)6922f287f4SAndrew Jones static void cpu_init(void)
7022f287f4SAndrew Jones {
71d4827ec0SAndrew Jones int ret, me;
7222f287f4SAndrew Jones
7322f287f4SAndrew Jones nr_cpus = 0;
7422f287f4SAndrew Jones if (dt_available()) {
7522f287f4SAndrew Jones ret = dt_for_each_cpu_node(cpu_set_fdt, NULL);
7622f287f4SAndrew Jones assert(ret == 0);
7722f287f4SAndrew Jones } else {
7822f287f4SAndrew Jones cpu_init_acpi();
7922f287f4SAndrew Jones }
8022f287f4SAndrew Jones
81d4827ec0SAndrew Jones me = hartid_to_cpu(csr_read(CSR_SSCRATCH));
82d4827ec0SAndrew Jones assert(cpu_present(me));
83d4827ec0SAndrew Jones set_cpu_online(me, true);
849c92b28eSAndrew Jones cpu0_calls_idle = true;
8522f287f4SAndrew Jones }
8622f287f4SAndrew Jones
mem_allocator_init(struct mem_region * freemem,phys_addr_t freemem_start)873d83840dSAndrew Jones static void mem_allocator_init(struct mem_region *freemem, phys_addr_t freemem_start)
8822f287f4SAndrew Jones {
893d83840dSAndrew Jones phys_addr_t freemem_end = freemem->end;
9031b4ef45SAndrew Jones phys_addr_t base, top;
916895ce6dSAndrew Jones
9231b4ef45SAndrew Jones freemem_start = PAGE_ALIGN(freemem_start);
9388f594c8SAndrew Jones freemem_end &= PHYS_PAGE_MASK;
946895ce6dSAndrew Jones
956895ce6dSAndrew Jones /*
966895ce6dSAndrew Jones * The assert below is mostly checking that the free memory doesn't
976895ce6dSAndrew Jones * start in the 3G-4G range, which is reserved for virtual addresses,
986895ce6dSAndrew Jones * but it also confirms that there is some free memory (the amount
996895ce6dSAndrew Jones * is arbitrarily selected, but should be sufficient for a unit test)
1006895ce6dSAndrew Jones *
1016895ce6dSAndrew Jones * TODO: Allow the VA range to shrink and move.
1026895ce6dSAndrew Jones */
1033d83840dSAndrew Jones if (freemem_end > VA_BASE) {
1043d83840dSAndrew Jones struct mem_region *curr, *rest;
1056895ce6dSAndrew Jones freemem_end = VA_BASE;
1063d83840dSAndrew Jones memregions_split(VA_BASE, &curr, &rest);
1073d83840dSAndrew Jones assert(curr == freemem);
1083d83840dSAndrew Jones if (rest)
1093d83840dSAndrew Jones rest->flags = MR_F_UNUSED;
1103d83840dSAndrew Jones }
1116895ce6dSAndrew Jones assert(freemem_end - freemem_start >= SZ_1M * 16);
1126895ce6dSAndrew Jones
11323100d97SAndrew Jones init_alloc_vpage(__va(VA_TOP));
11423100d97SAndrew Jones
1156895ce6dSAndrew Jones /*
1166895ce6dSAndrew Jones * TODO: Remove the need for this phys allocator dance, since, as we
1176895ce6dSAndrew Jones * can see with the assert, we could have gone straight to the page
1186895ce6dSAndrew Jones * allocator.
1196895ce6dSAndrew Jones */
1206895ce6dSAndrew Jones phys_alloc_init(freemem_start, freemem_end - freemem_start);
1216895ce6dSAndrew Jones phys_alloc_set_minimum_alignment(PAGE_SIZE);
1226895ce6dSAndrew Jones phys_alloc_get_unused(&base, &top);
1236895ce6dSAndrew Jones assert(base == freemem_start && top == freemem_end);
1246895ce6dSAndrew Jones
1256895ce6dSAndrew Jones page_alloc_init_area(0, freemem_start >> PAGE_SHIFT, freemem_end >> PAGE_SHIFT);
1266895ce6dSAndrew Jones page_alloc_ops_enable();
12722f287f4SAndrew Jones }
12822f287f4SAndrew Jones
mem_init(phys_addr_t freemem_start)12931b4ef45SAndrew Jones static void mem_init(phys_addr_t freemem_start)
13031b4ef45SAndrew Jones {
13131b4ef45SAndrew Jones struct mem_region *freemem, *code, *data;
13231b4ef45SAndrew Jones
13331b4ef45SAndrew Jones memregions_init(riscv_mem_regions, NR_MEM_REGIONS);
13431b4ef45SAndrew Jones memregions_add_dt_regions(MAX_DT_MEM_REGIONS);
13531b4ef45SAndrew Jones
13631b4ef45SAndrew Jones /* Split the region with the code into two regions; code and data */
13731b4ef45SAndrew Jones memregions_split((unsigned long)&_etext, &code, &data);
13831b4ef45SAndrew Jones assert(code);
13931b4ef45SAndrew Jones code->flags |= MR_F_CODE;
14031b4ef45SAndrew Jones
14131b4ef45SAndrew Jones freemem = memregions_find(freemem_start);
14231b4ef45SAndrew Jones assert(freemem && !(freemem->flags & (MR_F_IO | MR_F_CODE)));
14331b4ef45SAndrew Jones
1443d83840dSAndrew Jones mem_allocator_init(freemem, freemem_start);
14531b4ef45SAndrew Jones }
14631b4ef45SAndrew Jones
freemem_push_fdt(void ** freemem,const void * fdt)14731b4ef45SAndrew Jones static void freemem_push_fdt(void **freemem, const void *fdt)
14831b4ef45SAndrew Jones {
14931b4ef45SAndrew Jones u32 fdt_size;
15031b4ef45SAndrew Jones int ret;
15131b4ef45SAndrew Jones
15231b4ef45SAndrew Jones fdt_size = fdt_totalsize(fdt);
15331b4ef45SAndrew Jones ret = fdt_move(fdt, *freemem, fdt_size);
15431b4ef45SAndrew Jones assert(ret == 0);
15531b4ef45SAndrew Jones ret = dt_init(*freemem);
15631b4ef45SAndrew Jones assert(ret == 0);
15731b4ef45SAndrew Jones *freemem += fdt_size;
15831b4ef45SAndrew Jones }
15931b4ef45SAndrew Jones
freemem_push_dt_initrd(void ** freemem)16031b4ef45SAndrew Jones static void freemem_push_dt_initrd(void **freemem)
16131b4ef45SAndrew Jones {
16231b4ef45SAndrew Jones const char *tmp;
16331b4ef45SAndrew Jones int ret;
16431b4ef45SAndrew Jones
16531b4ef45SAndrew Jones ret = dt_get_initrd(&tmp, &initrd_size);
16631b4ef45SAndrew Jones assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
16731b4ef45SAndrew Jones if (ret == 0) {
16831b4ef45SAndrew Jones initrd = *freemem;
16931b4ef45SAndrew Jones memmove(initrd, tmp, initrd_size);
17031b4ef45SAndrew Jones *freemem += initrd_size;
17131b4ef45SAndrew Jones }
17231b4ef45SAndrew Jones }
17331b4ef45SAndrew Jones
initrd_setup(void)17431b4ef45SAndrew Jones static void initrd_setup(void)
17531b4ef45SAndrew Jones {
17631b4ef45SAndrew Jones char *env;
17731b4ef45SAndrew Jones
17831b4ef45SAndrew Jones if (!initrd)
17931b4ef45SAndrew Jones return;
18031b4ef45SAndrew Jones
18131b4ef45SAndrew Jones /* environ is currently the only file in the initrd */
18231b4ef45SAndrew Jones env = malloc(initrd_size);
18331b4ef45SAndrew Jones memcpy(env, initrd, initrd_size);
18431b4ef45SAndrew Jones setup_env(env, initrd_size);
18531b4ef45SAndrew Jones }
18631b4ef45SAndrew Jones
banner(void)18722f287f4SAndrew Jones static void banner(void)
18822f287f4SAndrew Jones {
18922f287f4SAndrew Jones puts("\n");
19022f287f4SAndrew Jones puts("##########################################################################\n");
19122f287f4SAndrew Jones puts("# kvm-unit-tests\n");
19222f287f4SAndrew Jones puts("##########################################################################\n");
19322f287f4SAndrew Jones puts("\n");
19422f287f4SAndrew Jones }
19522f287f4SAndrew Jones
setup(const void * fdt,phys_addr_t freemem_start)196bd744d46SAndrew Jones void setup(const void *fdt, phys_addr_t freemem_start)
197bd744d46SAndrew Jones {
19822f287f4SAndrew Jones void *freemem;
19931b4ef45SAndrew Jones const char *bootargs;
20022f287f4SAndrew Jones int ret;
20122f287f4SAndrew Jones
20274492717SAndrew Jones assert(freemem_start < VA_BASE);
20323100d97SAndrew Jones freemem = __va(freemem_start);
20422f287f4SAndrew Jones
20531b4ef45SAndrew Jones freemem_push_fdt(&freemem, fdt);
20631b4ef45SAndrew Jones freemem_push_dt_initrd(&freemem);
20722f287f4SAndrew Jones
20823100d97SAndrew Jones mem_init(PAGE_ALIGN(__pa(freemem)));
20922f287f4SAndrew Jones cpu_init();
21093bcbb09SJames Raphael Tiovalen timer_get_frequency();
211386561f8SAndrew Jones thread_info_init();
21294ca1aafSAndrew Jones local_hart_init();
21322f287f4SAndrew Jones io_init();
21422f287f4SAndrew Jones
21522f287f4SAndrew Jones ret = dt_get_bootargs(&bootargs);
21622f287f4SAndrew Jones assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
21722f287f4SAndrew Jones setup_args_progname(bootargs);
21822f287f4SAndrew Jones
21931b4ef45SAndrew Jones initrd_setup();
22022f287f4SAndrew Jones
22123100d97SAndrew Jones if (!(auxinfo.flags & AUXINFO_MMU_OFF))
22223100d97SAndrew Jones setup_vm();
223ad435a71SAndrew Jones
22422f287f4SAndrew Jones banner();
225bd744d46SAndrew Jones }
2269f34c810SAndrew Jones
2279f34c810SAndrew Jones #ifdef CONFIG_EFI
2289f34c810SAndrew Jones #include <efi.h>
2299f34c810SAndrew Jones
2309f34c810SAndrew Jones extern unsigned long exception_vectors;
2319f34c810SAndrew Jones extern unsigned long boot_hartid;
2329f34c810SAndrew Jones
efi_mem_init(efi_bootinfo_t * efi_bootinfo)2339f34c810SAndrew Jones static efi_status_t efi_mem_init(efi_bootinfo_t *efi_bootinfo)
2349f34c810SAndrew Jones {
2359f34c810SAndrew Jones struct mem_region *freemem_mr = NULL, *code, *data;
2369f34c810SAndrew Jones void *freemem;
2379f34c810SAndrew Jones
2389f34c810SAndrew Jones memregions_init(riscv_mem_regions, NR_MEM_REGIONS);
2399f34c810SAndrew Jones
2409f34c810SAndrew Jones memregions_efi_init(&efi_bootinfo->mem_map, &freemem_mr);
2419f34c810SAndrew Jones if (!freemem_mr)
2429f34c810SAndrew Jones return EFI_OUT_OF_RESOURCES;
2439f34c810SAndrew Jones
2449f34c810SAndrew Jones memregions_split((unsigned long)&_etext, &code, &data);
2459f34c810SAndrew Jones assert(code && (code->flags & MR_F_CODE));
2469f34c810SAndrew Jones if (data)
2479f34c810SAndrew Jones data->flags &= ~MR_F_CODE;
2489f34c810SAndrew Jones
2499f34c810SAndrew Jones for (struct mem_region *m = mem_regions; m->end; ++m)
2509f34c810SAndrew Jones assert(m == code || !(m->flags & MR_F_CODE));
2519f34c810SAndrew Jones
2529f34c810SAndrew Jones freemem = (void *)PAGE_ALIGN(freemem_mr->start);
2539f34c810SAndrew Jones
2549f34c810SAndrew Jones if (efi_bootinfo->fdt)
2559f34c810SAndrew Jones freemem_push_fdt(&freemem, efi_bootinfo->fdt);
2569f34c810SAndrew Jones
2579f34c810SAndrew Jones mmu_disable();
2583d83840dSAndrew Jones mem_allocator_init(freemem_mr, (unsigned long)freemem);
2599f34c810SAndrew Jones
2609f34c810SAndrew Jones return EFI_SUCCESS;
2619f34c810SAndrew Jones }
2629f34c810SAndrew Jones
setup_efi(efi_bootinfo_t * efi_bootinfo)2639f34c810SAndrew Jones efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
2649f34c810SAndrew Jones {
2659f34c810SAndrew Jones efi_status_t status;
2669f34c810SAndrew Jones
2679f34c810SAndrew Jones csr_write(CSR_STVEC, (unsigned long)&exception_vectors);
2689f34c810SAndrew Jones csr_write(CSR_SSCRATCH, boot_hartid);
2699f34c810SAndrew Jones
2709f34c810SAndrew Jones status = efi_mem_init(efi_bootinfo);
2719f34c810SAndrew Jones if (status != EFI_SUCCESS) {
2729f34c810SAndrew Jones printf("Failed to initialize memory\n");
2739f34c810SAndrew Jones return status;
2749f34c810SAndrew Jones }
2759f34c810SAndrew Jones
2769f34c810SAndrew Jones cpu_init();
27793bcbb09SJames Raphael Tiovalen timer_get_frequency();
2789f34c810SAndrew Jones thread_info_init();
27994ca1aafSAndrew Jones local_hart_init();
2809f34c810SAndrew Jones io_init();
2819f34c810SAndrew Jones initrd_setup();
2829f34c810SAndrew Jones
2839f34c810SAndrew Jones if (!(auxinfo.flags & AUXINFO_MMU_OFF))
2849f34c810SAndrew Jones setup_vm();
2859f34c810SAndrew Jones
2869f34c810SAndrew Jones banner();
2879f34c810SAndrew Jones
2889f34c810SAndrew Jones return EFI_SUCCESS;
2899f34c810SAndrew Jones }
2909f34c810SAndrew Jones #endif /* CONFIG_EFI */
291