1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Initialize machine setup information and I/O. 4 * 5 * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com> 6 */ 7 #include <libcflat.h> 8 #include <alloc.h> 9 #include <alloc_page.h> 10 #include <alloc_phys.h> 11 #include <argv.h> 12 #include <auxinfo.h> 13 #include <cpumask.h> 14 #include <devicetree.h> 15 #include <memregions.h> 16 #include <on-cpus.h> 17 #include <vmalloc.h> 18 #include <asm/csr.h> 19 #include <asm/mmu.h> 20 #include <asm/page.h> 21 #include <asm/processor.h> 22 #include <asm/setup.h> 23 24 #define VA_BASE ((phys_addr_t)3 * SZ_1G) 25 #if __riscv_xlen == 64 26 #define VA_TOP ((phys_addr_t)4 * SZ_1G) 27 #else 28 #define VA_TOP ((phys_addr_t)0) 29 #endif 30 31 #define MAX_DT_MEM_REGIONS 16 32 #define NR_MEM_REGIONS (MAX_DT_MEM_REGIONS + 16) 33 34 char *initrd; 35 u32 initrd_size; 36 37 struct thread_info cpus[NR_CPUS]; 38 int nr_cpus; 39 40 static struct mem_region riscv_mem_regions[NR_MEM_REGIONS + 1]; 41 42 int hartid_to_cpu(unsigned long hartid) 43 { 44 int cpu; 45 46 for_each_present_cpu(cpu) 47 if (cpus[cpu].hartid == hartid) 48 return cpu; 49 return -1; 50 } 51 52 static void cpu_set_fdt(int fdtnode __unused, u64 regval, void *info __unused) 53 { 54 int cpu = nr_cpus++; 55 56 assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS); 57 58 cpus[cpu].cpu = cpu; 59 cpus[cpu].hartid = regval; 60 set_cpu_present(cpu, true); 61 } 62 63 static void cpu_init_acpi(void) 64 { 65 assert_msg(false, "ACPI not available"); 66 } 67 68 static void cpu_init(void) 69 { 70 int ret; 71 72 nr_cpus = 0; 73 if (dt_available()) { 74 ret = dt_for_each_cpu_node(cpu_set_fdt, NULL); 75 assert(ret == 0); 76 } else { 77 cpu_init_acpi(); 78 } 79 80 set_cpu_online(hartid_to_cpu(csr_read(CSR_SSCRATCH)), true); 81 cpu0_calls_idle = true; 82 } 83 84 extern unsigned long _etext; 85 86 static void mem_init(phys_addr_t freemem_start) 87 { 88 struct mem_region *freemem, *code, *data; 89 phys_addr_t freemem_end, base, top; 90 91 memregions_init(riscv_mem_regions, NR_MEM_REGIONS); 92 memregions_add_dt_regions(MAX_DT_MEM_REGIONS); 93 94 /* Split the region with the code into two regions; code and data */ 95 memregions_split((unsigned long)&_etext, &code, &data); 96 assert(code); 97 code->flags |= MR_F_CODE; 98 99 freemem = memregions_find(freemem_start); 100 assert(freemem && !(freemem->flags & (MR_F_IO | MR_F_CODE))); 101 102 freemem_end = freemem->end & PAGE_MASK; 103 104 /* 105 * The assert below is mostly checking that the free memory doesn't 106 * start in the 3G-4G range, which is reserved for virtual addresses, 107 * but it also confirms that there is some free memory (the amount 108 * is arbitrarily selected, but should be sufficient for a unit test) 109 * 110 * TODO: Allow the VA range to shrink and move. 111 */ 112 if (freemem_end > VA_BASE) 113 freemem_end = VA_BASE; 114 assert(freemem_end - freemem_start >= SZ_1M * 16); 115 116 init_alloc_vpage(__va(VA_TOP)); 117 118 /* 119 * TODO: Remove the need for this phys allocator dance, since, as we 120 * can see with the assert, we could have gone straight to the page 121 * allocator. 122 */ 123 phys_alloc_init(freemem_start, freemem_end - freemem_start); 124 phys_alloc_set_minimum_alignment(PAGE_SIZE); 125 phys_alloc_get_unused(&base, &top); 126 assert(base == freemem_start && top == freemem_end); 127 128 page_alloc_init_area(0, freemem_start >> PAGE_SHIFT, freemem_end >> PAGE_SHIFT); 129 page_alloc_ops_enable(); 130 } 131 132 static void banner(void) 133 { 134 puts("\n"); 135 puts("##########################################################################\n"); 136 puts("# kvm-unit-tests\n"); 137 puts("##########################################################################\n"); 138 puts("\n"); 139 } 140 141 void setup(const void *fdt, phys_addr_t freemem_start) 142 { 143 void *freemem; 144 const char *bootargs, *tmp; 145 u32 fdt_size; 146 int ret; 147 148 assert(sizeof(long) == 8 || freemem_start < VA_BASE); 149 freemem = __va(freemem_start); 150 151 /* Move the FDT to the base of free memory */ 152 fdt_size = fdt_totalsize(fdt); 153 ret = fdt_move(fdt, freemem, fdt_size); 154 assert(ret == 0); 155 ret = dt_init(freemem); 156 assert(ret == 0); 157 freemem += fdt_size; 158 159 /* Move the initrd to the top of the FDT */ 160 ret = dt_get_initrd(&tmp, &initrd_size); 161 assert(ret == 0 || ret == -FDT_ERR_NOTFOUND); 162 if (ret == 0) { 163 initrd = freemem; 164 memmove(initrd, tmp, initrd_size); 165 freemem += initrd_size; 166 } 167 168 mem_init(PAGE_ALIGN(__pa(freemem))); 169 cpu_init(); 170 thread_info_init(); 171 io_init(); 172 173 ret = dt_get_bootargs(&bootargs); 174 assert(ret == 0 || ret == -FDT_ERR_NOTFOUND); 175 setup_args_progname(bootargs); 176 177 if (initrd) { 178 /* environ is currently the only file in the initrd */ 179 char *env = malloc(initrd_size); 180 memcpy(env, initrd, initrd_size); 181 setup_env(env, initrd_size); 182 } 183 184 if (!(auxinfo.flags & AUXINFO_MMU_OFF)) 185 setup_vm(); 186 187 banner(); 188 } 189