xref: /kvm-unit-tests/lib/riscv/setup.c (revision 93bcbb0982adb2d83a524a3f8026c5a6c101e143)
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>
22bd744d46SAndrew Jones #include <asm/setup.h>
23*93bcbb09SJames Raphael Tiovalen #include <asm/timer.h>
24bd744d46SAndrew Jones 
256895ce6dSAndrew Jones #define VA_BASE			((phys_addr_t)3 * SZ_1G)
2623100d97SAndrew Jones #if __riscv_xlen == 64
2723100d97SAndrew Jones #define VA_TOP			((phys_addr_t)4 * SZ_1G)
2823100d97SAndrew Jones #else
2923100d97SAndrew Jones #define VA_TOP			((phys_addr_t)0)
3023100d97SAndrew Jones #endif
316895ce6dSAndrew Jones 
326895ce6dSAndrew Jones #define MAX_DT_MEM_REGIONS	16
336895ce6dSAndrew Jones #define NR_MEM_REGIONS		(MAX_DT_MEM_REGIONS + 16)
346895ce6dSAndrew Jones 
3531b4ef45SAndrew Jones extern unsigned long _etext;
3631b4ef45SAndrew Jones 
3722f287f4SAndrew Jones char *initrd;
3822f287f4SAndrew Jones u32 initrd_size;
3922f287f4SAndrew Jones 
40386561f8SAndrew Jones struct thread_info cpus[NR_CPUS];
4122f287f4SAndrew Jones int nr_cpus;
42*93bcbb09SJames Raphael Tiovalen uint64_t timebase_frequency;
4322f287f4SAndrew Jones 
446895ce6dSAndrew Jones static struct mem_region riscv_mem_regions[NR_MEM_REGIONS + 1];
456895ce6dSAndrew Jones 
4622f287f4SAndrew Jones int hartid_to_cpu(unsigned long hartid)
4722f287f4SAndrew Jones {
4822f287f4SAndrew Jones 	int cpu;
4922f287f4SAndrew Jones 
5022f287f4SAndrew Jones 	for_each_present_cpu(cpu)
51386561f8SAndrew Jones 		if (cpus[cpu].hartid == hartid)
5222f287f4SAndrew Jones 			return cpu;
5322f287f4SAndrew Jones 	return -1;
5422f287f4SAndrew Jones }
5522f287f4SAndrew Jones 
5622f287f4SAndrew Jones static void cpu_set_fdt(int fdtnode __unused, u64 regval, void *info __unused)
5722f287f4SAndrew Jones {
5822f287f4SAndrew Jones 	int cpu = nr_cpus++;
5922f287f4SAndrew Jones 
6022f287f4SAndrew Jones 	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
6122f287f4SAndrew Jones 
62386561f8SAndrew Jones 	cpus[cpu].cpu = cpu;
63386561f8SAndrew Jones 	cpus[cpu].hartid = regval;
6422f287f4SAndrew Jones 	set_cpu_present(cpu, true);
6522f287f4SAndrew Jones }
6622f287f4SAndrew Jones 
6722f287f4SAndrew Jones static void cpu_init_acpi(void)
6822f287f4SAndrew Jones {
6922f287f4SAndrew Jones 	assert_msg(false, "ACPI not available");
7022f287f4SAndrew Jones }
7122f287f4SAndrew Jones 
7222f287f4SAndrew Jones static void cpu_init(void)
7322f287f4SAndrew Jones {
7422f287f4SAndrew Jones 	int ret;
7522f287f4SAndrew Jones 
7622f287f4SAndrew Jones 	nr_cpus = 0;
7722f287f4SAndrew Jones 	if (dt_available()) {
7822f287f4SAndrew Jones 		ret = dt_for_each_cpu_node(cpu_set_fdt, NULL);
7922f287f4SAndrew Jones 		assert(ret == 0);
8022f287f4SAndrew Jones 	} else {
8122f287f4SAndrew Jones 		cpu_init_acpi();
8222f287f4SAndrew Jones 	}
8322f287f4SAndrew Jones 
8422f287f4SAndrew Jones 	set_cpu_online(hartid_to_cpu(csr_read(CSR_SSCRATCH)), true);
859c92b28eSAndrew Jones 	cpu0_calls_idle = true;
8622f287f4SAndrew Jones }
8722f287f4SAndrew Jones 
8831b4ef45SAndrew Jones static void mem_allocator_init(phys_addr_t freemem_start, phys_addr_t freemem_end)
8922f287f4SAndrew Jones {
9031b4ef45SAndrew Jones 	phys_addr_t base, top;
916895ce6dSAndrew Jones 
9231b4ef45SAndrew Jones 	freemem_start = PAGE_ALIGN(freemem_start);
9331b4ef45SAndrew Jones 	freemem_end &= 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 	 */
1036895ce6dSAndrew Jones 	if (freemem_end > VA_BASE)
1046895ce6dSAndrew Jones 		freemem_end = VA_BASE;
1056895ce6dSAndrew Jones 	assert(freemem_end - freemem_start >= SZ_1M * 16);
1066895ce6dSAndrew Jones 
10723100d97SAndrew Jones 	init_alloc_vpage(__va(VA_TOP));
10823100d97SAndrew Jones 
1096895ce6dSAndrew Jones 	/*
1106895ce6dSAndrew Jones 	 * TODO: Remove the need for this phys allocator dance, since, as we
1116895ce6dSAndrew Jones 	 * can see with the assert, we could have gone straight to the page
1126895ce6dSAndrew Jones 	 * allocator.
1136895ce6dSAndrew Jones 	 */
1146895ce6dSAndrew Jones 	phys_alloc_init(freemem_start, freemem_end - freemem_start);
1156895ce6dSAndrew Jones 	phys_alloc_set_minimum_alignment(PAGE_SIZE);
1166895ce6dSAndrew Jones 	phys_alloc_get_unused(&base, &top);
1176895ce6dSAndrew Jones 	assert(base == freemem_start && top == freemem_end);
1186895ce6dSAndrew Jones 
1196895ce6dSAndrew Jones 	page_alloc_init_area(0, freemem_start >> PAGE_SHIFT, freemem_end >> PAGE_SHIFT);
1206895ce6dSAndrew Jones 	page_alloc_ops_enable();
12122f287f4SAndrew Jones }
12222f287f4SAndrew Jones 
12331b4ef45SAndrew Jones static void mem_init(phys_addr_t freemem_start)
12431b4ef45SAndrew Jones {
12531b4ef45SAndrew Jones 	struct mem_region *freemem, *code, *data;
12631b4ef45SAndrew Jones 
12731b4ef45SAndrew Jones 	memregions_init(riscv_mem_regions, NR_MEM_REGIONS);
12831b4ef45SAndrew Jones 	memregions_add_dt_regions(MAX_DT_MEM_REGIONS);
12931b4ef45SAndrew Jones 
13031b4ef45SAndrew Jones 	/* Split the region with the code into two regions; code and data */
13131b4ef45SAndrew Jones 	memregions_split((unsigned long)&_etext, &code, &data);
13231b4ef45SAndrew Jones 	assert(code);
13331b4ef45SAndrew Jones 	code->flags |= MR_F_CODE;
13431b4ef45SAndrew Jones 
13531b4ef45SAndrew Jones 	freemem = memregions_find(freemem_start);
13631b4ef45SAndrew Jones 	assert(freemem && !(freemem->flags & (MR_F_IO | MR_F_CODE)));
13731b4ef45SAndrew Jones 
13831b4ef45SAndrew Jones 	mem_allocator_init(freemem_start, freemem->end);
13931b4ef45SAndrew Jones }
14031b4ef45SAndrew Jones 
14131b4ef45SAndrew Jones static void freemem_push_fdt(void **freemem, const void *fdt)
14231b4ef45SAndrew Jones {
14331b4ef45SAndrew Jones 	u32 fdt_size;
14431b4ef45SAndrew Jones 	int ret;
14531b4ef45SAndrew Jones 
14631b4ef45SAndrew Jones 	fdt_size = fdt_totalsize(fdt);
14731b4ef45SAndrew Jones 	ret = fdt_move(fdt, *freemem, fdt_size);
14831b4ef45SAndrew Jones 	assert(ret == 0);
14931b4ef45SAndrew Jones 	ret = dt_init(*freemem);
15031b4ef45SAndrew Jones 	assert(ret == 0);
15131b4ef45SAndrew Jones 	*freemem += fdt_size;
15231b4ef45SAndrew Jones }
15331b4ef45SAndrew Jones 
15431b4ef45SAndrew Jones static void freemem_push_dt_initrd(void **freemem)
15531b4ef45SAndrew Jones {
15631b4ef45SAndrew Jones 	const char *tmp;
15731b4ef45SAndrew Jones 	int ret;
15831b4ef45SAndrew Jones 
15931b4ef45SAndrew Jones 	ret = dt_get_initrd(&tmp, &initrd_size);
16031b4ef45SAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
16131b4ef45SAndrew Jones 	if (ret == 0) {
16231b4ef45SAndrew Jones 		initrd = *freemem;
16331b4ef45SAndrew Jones 		memmove(initrd, tmp, initrd_size);
16431b4ef45SAndrew Jones 		*freemem += initrd_size;
16531b4ef45SAndrew Jones 	}
16631b4ef45SAndrew Jones }
16731b4ef45SAndrew Jones 
16831b4ef45SAndrew Jones static void initrd_setup(void)
16931b4ef45SAndrew Jones {
17031b4ef45SAndrew Jones 	char *env;
17131b4ef45SAndrew Jones 
17231b4ef45SAndrew Jones 	if (!initrd)
17331b4ef45SAndrew Jones 		return;
17431b4ef45SAndrew Jones 
17531b4ef45SAndrew Jones 	/* environ is currently the only file in the initrd */
17631b4ef45SAndrew Jones 	env = malloc(initrd_size);
17731b4ef45SAndrew Jones 	memcpy(env, initrd, initrd_size);
17831b4ef45SAndrew Jones 	setup_env(env, initrd_size);
17931b4ef45SAndrew Jones }
18031b4ef45SAndrew Jones 
18122f287f4SAndrew Jones static void banner(void)
18222f287f4SAndrew Jones {
18322f287f4SAndrew Jones 	puts("\n");
18422f287f4SAndrew Jones 	puts("##########################################################################\n");
18522f287f4SAndrew Jones 	puts("#    kvm-unit-tests\n");
18622f287f4SAndrew Jones 	puts("##########################################################################\n");
18722f287f4SAndrew Jones 	puts("\n");
18822f287f4SAndrew Jones }
18922f287f4SAndrew Jones 
190bd744d46SAndrew Jones void setup(const void *fdt, phys_addr_t freemem_start)
191bd744d46SAndrew Jones {
19222f287f4SAndrew Jones 	void *freemem;
19331b4ef45SAndrew Jones 	const char *bootargs;
19422f287f4SAndrew Jones 	int ret;
19522f287f4SAndrew Jones 
1966895ce6dSAndrew Jones 	assert(sizeof(long) == 8 || freemem_start < VA_BASE);
19723100d97SAndrew Jones 	freemem = __va(freemem_start);
19822f287f4SAndrew Jones 
19931b4ef45SAndrew Jones 	freemem_push_fdt(&freemem, fdt);
20031b4ef45SAndrew Jones 	freemem_push_dt_initrd(&freemem);
20122f287f4SAndrew Jones 
20223100d97SAndrew Jones 	mem_init(PAGE_ALIGN(__pa(freemem)));
20322f287f4SAndrew Jones 	cpu_init();
204*93bcbb09SJames Raphael Tiovalen 	timer_get_frequency();
205386561f8SAndrew Jones 	thread_info_init();
20622f287f4SAndrew Jones 	io_init();
20722f287f4SAndrew Jones 
20822f287f4SAndrew Jones 	ret = dt_get_bootargs(&bootargs);
20922f287f4SAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
21022f287f4SAndrew Jones 	setup_args_progname(bootargs);
21122f287f4SAndrew Jones 
21231b4ef45SAndrew Jones 	initrd_setup();
21322f287f4SAndrew Jones 
21423100d97SAndrew Jones 	if (!(auxinfo.flags & AUXINFO_MMU_OFF))
21523100d97SAndrew Jones 		setup_vm();
216ad435a71SAndrew Jones 
21722f287f4SAndrew Jones 	banner();
218bd744d46SAndrew Jones }
2199f34c810SAndrew Jones 
2209f34c810SAndrew Jones #ifdef CONFIG_EFI
2219f34c810SAndrew Jones #include <efi.h>
2229f34c810SAndrew Jones 
2239f34c810SAndrew Jones extern unsigned long exception_vectors;
2249f34c810SAndrew Jones extern unsigned long boot_hartid;
2259f34c810SAndrew Jones 
2269f34c810SAndrew Jones static efi_status_t efi_mem_init(efi_bootinfo_t *efi_bootinfo)
2279f34c810SAndrew Jones {
2289f34c810SAndrew Jones 	struct mem_region *freemem_mr = NULL, *code, *data;
2299f34c810SAndrew Jones 	void *freemem;
2309f34c810SAndrew Jones 
2319f34c810SAndrew Jones 	memregions_init(riscv_mem_regions, NR_MEM_REGIONS);
2329f34c810SAndrew Jones 
2339f34c810SAndrew Jones 	memregions_efi_init(&efi_bootinfo->mem_map, &freemem_mr);
2349f34c810SAndrew Jones 	if (!freemem_mr)
2359f34c810SAndrew Jones 		return EFI_OUT_OF_RESOURCES;
2369f34c810SAndrew Jones 
2379f34c810SAndrew Jones 	memregions_split((unsigned long)&_etext, &code, &data);
2389f34c810SAndrew Jones 	assert(code && (code->flags & MR_F_CODE));
2399f34c810SAndrew Jones 	if (data)
2409f34c810SAndrew Jones 		data->flags &= ~MR_F_CODE;
2419f34c810SAndrew Jones 
2429f34c810SAndrew Jones 	for (struct mem_region *m = mem_regions; m->end; ++m)
2439f34c810SAndrew Jones 		assert(m == code || !(m->flags & MR_F_CODE));
2449f34c810SAndrew Jones 
2459f34c810SAndrew Jones 	freemem = (void *)PAGE_ALIGN(freemem_mr->start);
2469f34c810SAndrew Jones 
2479f34c810SAndrew Jones 	if (efi_bootinfo->fdt)
2489f34c810SAndrew Jones 		freemem_push_fdt(&freemem, efi_bootinfo->fdt);
2499f34c810SAndrew Jones 
2509f34c810SAndrew Jones 	mmu_disable();
2519f34c810SAndrew Jones 	mem_allocator_init((unsigned long)freemem, freemem_mr->end);
2529f34c810SAndrew Jones 
2539f34c810SAndrew Jones 	return EFI_SUCCESS;
2549f34c810SAndrew Jones }
2559f34c810SAndrew Jones 
2569f34c810SAndrew Jones efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
2579f34c810SAndrew Jones {
2589f34c810SAndrew Jones 	efi_status_t status;
2599f34c810SAndrew Jones 
2609f34c810SAndrew Jones 	csr_write(CSR_STVEC, (unsigned long)&exception_vectors);
2619f34c810SAndrew Jones 	csr_write(CSR_SSCRATCH, boot_hartid);
2629f34c810SAndrew Jones 
2639f34c810SAndrew Jones 	status = efi_mem_init(efi_bootinfo);
2649f34c810SAndrew Jones 	if (status != EFI_SUCCESS) {
2659f34c810SAndrew Jones 		printf("Failed to initialize memory\n");
2669f34c810SAndrew Jones 		return status;
2679f34c810SAndrew Jones 	}
2689f34c810SAndrew Jones 
2699f34c810SAndrew Jones 	cpu_init();
270*93bcbb09SJames Raphael Tiovalen 	timer_get_frequency();
2719f34c810SAndrew Jones 	thread_info_init();
2729f34c810SAndrew Jones 	io_init();
2739f34c810SAndrew Jones 	initrd_setup();
2749f34c810SAndrew Jones 
2759f34c810SAndrew Jones 	if (!(auxinfo.flags & AUXINFO_MMU_OFF))
2769f34c810SAndrew Jones 		setup_vm();
2779f34c810SAndrew Jones 
2789f34c810SAndrew Jones 	banner();
2799f34c810SAndrew Jones 
2809f34c810SAndrew Jones 	return EFI_SUCCESS;
2819f34c810SAndrew Jones }
2829f34c810SAndrew Jones #endif /* CONFIG_EFI */
283