xref: /kvm-unit-tests/lib/riscv/setup.c (revision 6895ce6dc618079972b9b60be6aeccdcff28cb40)
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>
9*6895ce6dSAndrew Jones #include <alloc_page.h>
1022f287f4SAndrew Jones #include <alloc_phys.h>
1122f287f4SAndrew Jones #include <argv.h>
1222f287f4SAndrew Jones #include <cpumask.h>
1322f287f4SAndrew Jones #include <devicetree.h>
14*6895ce6dSAndrew Jones #include <memregions.h>
159c92b28eSAndrew Jones #include <on-cpus.h>
1622f287f4SAndrew Jones #include <asm/csr.h>
1722f287f4SAndrew Jones #include <asm/page.h>
18386561f8SAndrew Jones #include <asm/processor.h>
19bd744d46SAndrew Jones #include <asm/setup.h>
20bd744d46SAndrew Jones 
21*6895ce6dSAndrew Jones #define VA_BASE			((phys_addr_t)3 * SZ_1G)
22*6895ce6dSAndrew Jones 
23*6895ce6dSAndrew Jones #define MAX_DT_MEM_REGIONS	16
24*6895ce6dSAndrew Jones #define NR_MEM_REGIONS		(MAX_DT_MEM_REGIONS + 16)
25*6895ce6dSAndrew Jones 
2622f287f4SAndrew Jones char *initrd;
2722f287f4SAndrew Jones u32 initrd_size;
2822f287f4SAndrew Jones 
29386561f8SAndrew Jones struct thread_info cpus[NR_CPUS];
3022f287f4SAndrew Jones int nr_cpus;
3122f287f4SAndrew Jones 
32*6895ce6dSAndrew Jones static struct mem_region riscv_mem_regions[NR_MEM_REGIONS + 1];
33*6895ce6dSAndrew Jones 
3422f287f4SAndrew Jones int hartid_to_cpu(unsigned long hartid)
3522f287f4SAndrew Jones {
3622f287f4SAndrew Jones 	int cpu;
3722f287f4SAndrew Jones 
3822f287f4SAndrew Jones 	for_each_present_cpu(cpu)
39386561f8SAndrew Jones 		if (cpus[cpu].hartid == hartid)
4022f287f4SAndrew Jones 			return cpu;
4122f287f4SAndrew Jones 	return -1;
4222f287f4SAndrew Jones }
4322f287f4SAndrew Jones 
4422f287f4SAndrew Jones static void cpu_set_fdt(int fdtnode __unused, u64 regval, void *info __unused)
4522f287f4SAndrew Jones {
4622f287f4SAndrew Jones 	int cpu = nr_cpus++;
4722f287f4SAndrew Jones 
4822f287f4SAndrew Jones 	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
4922f287f4SAndrew Jones 
50386561f8SAndrew Jones 	cpus[cpu].cpu = cpu;
51386561f8SAndrew Jones 	cpus[cpu].hartid = regval;
5222f287f4SAndrew Jones 	set_cpu_present(cpu, true);
5322f287f4SAndrew Jones }
5422f287f4SAndrew Jones 
5522f287f4SAndrew Jones static void cpu_init_acpi(void)
5622f287f4SAndrew Jones {
5722f287f4SAndrew Jones 	assert_msg(false, "ACPI not available");
5822f287f4SAndrew Jones }
5922f287f4SAndrew Jones 
6022f287f4SAndrew Jones static void cpu_init(void)
6122f287f4SAndrew Jones {
6222f287f4SAndrew Jones 	int ret;
6322f287f4SAndrew Jones 
6422f287f4SAndrew Jones 	nr_cpus = 0;
6522f287f4SAndrew Jones 	if (dt_available()) {
6622f287f4SAndrew Jones 		ret = dt_for_each_cpu_node(cpu_set_fdt, NULL);
6722f287f4SAndrew Jones 		assert(ret == 0);
6822f287f4SAndrew Jones 	} else {
6922f287f4SAndrew Jones 		cpu_init_acpi();
7022f287f4SAndrew Jones 	}
7122f287f4SAndrew Jones 
7222f287f4SAndrew Jones 	set_cpu_online(hartid_to_cpu(csr_read(CSR_SSCRATCH)), true);
739c92b28eSAndrew Jones 	cpu0_calls_idle = true;
7422f287f4SAndrew Jones }
7522f287f4SAndrew Jones 
76*6895ce6dSAndrew Jones extern unsigned long _etext;
77*6895ce6dSAndrew Jones 
7822f287f4SAndrew Jones static void mem_init(phys_addr_t freemem_start)
7922f287f4SAndrew Jones {
80*6895ce6dSAndrew Jones 	struct mem_region *freemem, *code, *data;
81*6895ce6dSAndrew Jones 	phys_addr_t freemem_end, base, top;
82*6895ce6dSAndrew Jones 
83*6895ce6dSAndrew Jones 	memregions_init(riscv_mem_regions, NR_MEM_REGIONS);
84*6895ce6dSAndrew Jones 	memregions_add_dt_regions(MAX_DT_MEM_REGIONS);
85*6895ce6dSAndrew Jones 
86*6895ce6dSAndrew Jones 	/* Split the region with the code into two regions; code and data */
87*6895ce6dSAndrew Jones 	memregions_split((unsigned long)&_etext, &code, &data);
88*6895ce6dSAndrew Jones 	assert(code);
89*6895ce6dSAndrew Jones 	code->flags |= MR_F_CODE;
90*6895ce6dSAndrew Jones 
91*6895ce6dSAndrew Jones 	freemem = memregions_find(freemem_start);
92*6895ce6dSAndrew Jones 	assert(freemem && !(freemem->flags & (MR_F_IO | MR_F_CODE)));
93*6895ce6dSAndrew Jones 
94*6895ce6dSAndrew Jones 	freemem_end = freemem->end & PAGE_MASK;
95*6895ce6dSAndrew Jones 
96*6895ce6dSAndrew Jones 	/*
97*6895ce6dSAndrew Jones 	 * The assert below is mostly checking that the free memory doesn't
98*6895ce6dSAndrew Jones 	 * start in the 3G-4G range, which is reserved for virtual addresses,
99*6895ce6dSAndrew Jones 	 * but it also confirms that there is some free memory (the amount
100*6895ce6dSAndrew Jones 	 * is arbitrarily selected, but should be sufficient for a unit test)
101*6895ce6dSAndrew Jones 	 *
102*6895ce6dSAndrew Jones 	 * TODO: Allow the VA range to shrink and move.
103*6895ce6dSAndrew Jones 	 */
104*6895ce6dSAndrew Jones 	if (freemem_end > VA_BASE)
105*6895ce6dSAndrew Jones 		freemem_end = VA_BASE;
106*6895ce6dSAndrew Jones 	assert(freemem_end - freemem_start >= SZ_1M * 16);
107*6895ce6dSAndrew Jones 
108*6895ce6dSAndrew Jones 	/*
109*6895ce6dSAndrew Jones 	 * TODO: Remove the need for this phys allocator dance, since, as we
110*6895ce6dSAndrew Jones 	 * can see with the assert, we could have gone straight to the page
111*6895ce6dSAndrew Jones 	 * allocator.
112*6895ce6dSAndrew Jones 	 */
113*6895ce6dSAndrew Jones 	phys_alloc_init(freemem_start, freemem_end - freemem_start);
114*6895ce6dSAndrew Jones 	phys_alloc_set_minimum_alignment(PAGE_SIZE);
115*6895ce6dSAndrew Jones 	phys_alloc_get_unused(&base, &top);
116*6895ce6dSAndrew Jones 	assert(base == freemem_start && top == freemem_end);
117*6895ce6dSAndrew Jones 
118*6895ce6dSAndrew Jones 	page_alloc_init_area(0, freemem_start >> PAGE_SHIFT, freemem_end >> PAGE_SHIFT);
119*6895ce6dSAndrew Jones 	page_alloc_ops_enable();
12022f287f4SAndrew Jones }
12122f287f4SAndrew Jones 
12222f287f4SAndrew Jones static void banner(void)
12322f287f4SAndrew Jones {
12422f287f4SAndrew Jones 	puts("\n");
12522f287f4SAndrew Jones 	puts("##########################################################################\n");
12622f287f4SAndrew Jones 	puts("#    kvm-unit-tests\n");
12722f287f4SAndrew Jones 	puts("##########################################################################\n");
12822f287f4SAndrew Jones 	puts("\n");
12922f287f4SAndrew Jones }
13022f287f4SAndrew Jones 
131bd744d46SAndrew Jones void setup(const void *fdt, phys_addr_t freemem_start)
132bd744d46SAndrew Jones {
13322f287f4SAndrew Jones 	void *freemem;
13422f287f4SAndrew Jones 	const char *bootargs, *tmp;
13522f287f4SAndrew Jones 	u32 fdt_size;
13622f287f4SAndrew Jones 	int ret;
13722f287f4SAndrew Jones 
138*6895ce6dSAndrew Jones 	assert(sizeof(long) == 8 || freemem_start < VA_BASE);
13922f287f4SAndrew Jones 	freemem = (void *)(unsigned long)freemem_start;
14022f287f4SAndrew Jones 
14122f287f4SAndrew Jones 	/* Move the FDT to the base of free memory */
14222f287f4SAndrew Jones 	fdt_size = fdt_totalsize(fdt);
14322f287f4SAndrew Jones 	ret = fdt_move(fdt, freemem, fdt_size);
14422f287f4SAndrew Jones 	assert(ret == 0);
14522f287f4SAndrew Jones 	ret = dt_init(freemem);
14622f287f4SAndrew Jones 	assert(ret == 0);
14722f287f4SAndrew Jones 	freemem += fdt_size;
14822f287f4SAndrew Jones 
14922f287f4SAndrew Jones 	/* Move the initrd to the top of the FDT */
15022f287f4SAndrew Jones 	ret = dt_get_initrd(&tmp, &initrd_size);
15122f287f4SAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
15222f287f4SAndrew Jones 	if (ret == 0) {
15322f287f4SAndrew Jones 		initrd = freemem;
15422f287f4SAndrew Jones 		memmove(initrd, tmp, initrd_size);
15522f287f4SAndrew Jones 		freemem += initrd_size;
15622f287f4SAndrew Jones 	}
15722f287f4SAndrew Jones 
15822f287f4SAndrew Jones 	mem_init(PAGE_ALIGN((unsigned long)freemem));
15922f287f4SAndrew Jones 	cpu_init();
160386561f8SAndrew Jones 	thread_info_init();
16122f287f4SAndrew Jones 	io_init();
16222f287f4SAndrew Jones 
16322f287f4SAndrew Jones 	ret = dt_get_bootargs(&bootargs);
16422f287f4SAndrew Jones 	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
16522f287f4SAndrew Jones 	setup_args_progname(bootargs);
16622f287f4SAndrew Jones 
16722f287f4SAndrew Jones 	if (initrd) {
16822f287f4SAndrew Jones 		/* environ is currently the only file in the initrd */
16922f287f4SAndrew Jones 		char *env = malloc(initrd_size);
17022f287f4SAndrew Jones 		memcpy(env, initrd, initrd_size);
17122f287f4SAndrew Jones 		setup_env(env, initrd_size);
17222f287f4SAndrew Jones 	}
17322f287f4SAndrew Jones 
17422f287f4SAndrew Jones 	banner();
175bd744d46SAndrew Jones }
176