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/sbi.h> 23 #include <asm/setup.h> 24 #include <asm/timer.h> 25 26 #define VA_BASE ((phys_addr_t)3 * SZ_1G) 27 #if __riscv_xlen == 64 28 #define VA_TOP ((phys_addr_t)4 * SZ_1G) 29 #else 30 #define VA_TOP ((phys_addr_t)0) 31 #endif 32 33 #define MAX_DT_MEM_REGIONS 16 34 #ifdef CONFIG_EFI 35 #define NR_MEM_REGIONS (MAX_DT_MEM_REGIONS + 128) 36 #else 37 #define NR_MEM_REGIONS (MAX_DT_MEM_REGIONS + 16) 38 #endif 39 40 extern unsigned long _etext; 41 42 char *initrd; 43 u32 initrd_size; 44 45 struct thread_info cpus[NR_CPUS]; 46 int nr_cpus; 47 uint64_t timebase_frequency; 48 49 static struct mem_region riscv_mem_regions[NR_MEM_REGIONS + 1]; 50 51 static void cpu_set_fdt(int fdtnode __unused, u64 regval, void *info __unused) 52 { 53 int cpu = nr_cpus++; 54 55 assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS); 56 57 cpus[cpu].cpu = cpu; 58 cpus[cpu].hartid = regval; 59 60 if (!sbi_hart_get_status(cpus[cpu].hartid).error) 61 set_cpu_present(cpu, true); 62 } 63 64 static void cpu_init_acpi(void) 65 { 66 assert_msg(false, "ACPI not available"); 67 } 68 69 static void cpu_init(void) 70 { 71 int ret, me; 72 73 nr_cpus = 0; 74 if (dt_available()) { 75 ret = dt_for_each_cpu_node(cpu_set_fdt, NULL); 76 assert(ret == 0); 77 } else { 78 cpu_init_acpi(); 79 } 80 81 me = hartid_to_cpu(csr_read(CSR_SSCRATCH)); 82 assert(cpu_present(me)); 83 set_cpu_online(me, true); 84 cpu0_calls_idle = true; 85 } 86 87 static void mem_allocator_init(struct mem_region *freemem, phys_addr_t freemem_start) 88 { 89 phys_addr_t freemem_end = freemem->end; 90 phys_addr_t base, top; 91 92 freemem_start = PAGE_ALIGN(freemem_start); 93 freemem_end &= PHYS_PAGE_MASK; 94 95 /* 96 * The assert below is mostly checking that the free memory doesn't 97 * start in the 3G-4G range, which is reserved for virtual addresses, 98 * but it also confirms that there is some free memory (the amount 99 * is arbitrarily selected, but should be sufficient for a unit test) 100 * 101 * TODO: Allow the VA range to shrink and move. 102 */ 103 if (freemem_end > VA_BASE) { 104 struct mem_region *curr, *rest; 105 freemem_end = VA_BASE; 106 memregions_split(VA_BASE, &curr, &rest); 107 assert(curr == freemem); 108 if (rest) 109 rest->flags = MR_F_UNUSED; 110 } 111 assert(freemem_end - freemem_start >= SZ_1M * 16); 112 113 init_alloc_vpage(__va(VA_TOP)); 114 115 /* 116 * TODO: Remove the need for this phys allocator dance, since, as we 117 * can see with the assert, we could have gone straight to the page 118 * allocator. 119 */ 120 phys_alloc_init(freemem_start, freemem_end - freemem_start); 121 phys_alloc_set_minimum_alignment(PAGE_SIZE); 122 phys_alloc_get_unused(&base, &top); 123 assert(base == freemem_start && top == freemem_end); 124 125 page_alloc_init_area(0, freemem_start >> PAGE_SHIFT, freemem_end >> PAGE_SHIFT); 126 page_alloc_ops_enable(); 127 } 128 129 static void mem_init(phys_addr_t freemem_start) 130 { 131 struct mem_region *freemem, *code, *data; 132 133 memregions_init(riscv_mem_regions, NR_MEM_REGIONS); 134 memregions_add_dt_regions(MAX_DT_MEM_REGIONS); 135 136 /* Split the region with the code into two regions; code and data */ 137 memregions_split((unsigned long)&_etext, &code, &data); 138 assert(code); 139 code->flags |= MR_F_CODE; 140 141 freemem = memregions_find(freemem_start); 142 assert(freemem && !(freemem->flags & (MR_F_IO | MR_F_CODE))); 143 144 mem_allocator_init(freemem, freemem_start); 145 } 146 147 static void freemem_push_fdt(void **freemem, const void *fdt) 148 { 149 u32 fdt_size; 150 int ret; 151 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 160 static void freemem_push_dt_initrd(void **freemem) 161 { 162 const char *tmp; 163 int ret; 164 165 ret = dt_get_initrd(&tmp, &initrd_size); 166 assert(ret == 0 || ret == -FDT_ERR_NOTFOUND); 167 if (ret == 0) { 168 initrd = *freemem; 169 memmove(initrd, tmp, initrd_size); 170 *freemem += initrd_size; 171 } 172 } 173 174 static void initrd_setup(void) 175 { 176 char *env; 177 178 if (!initrd) 179 return; 180 181 /* environ is currently the only file in the initrd */ 182 env = malloc(initrd_size); 183 memcpy(env, initrd, initrd_size); 184 setup_env(env, initrd_size); 185 } 186 187 static void banner(void) 188 { 189 puts("\n"); 190 puts("##########################################################################\n"); 191 puts("# kvm-unit-tests\n"); 192 puts("##########################################################################\n"); 193 puts("\n"); 194 } 195 196 void setup(const void *fdt, phys_addr_t freemem_start) 197 { 198 void *freemem; 199 const char *bootargs; 200 int ret; 201 202 assert(freemem_start < VA_BASE); 203 freemem = __va(freemem_start); 204 205 freemem_push_fdt(&freemem, fdt); 206 freemem_push_dt_initrd(&freemem); 207 208 mem_init(PAGE_ALIGN(__pa(freemem))); 209 cpu_init(); 210 timer_get_frequency(); 211 thread_info_init(); 212 local_hart_init(); 213 io_init(); 214 215 ret = dt_get_bootargs(&bootargs); 216 assert(ret == 0 || ret == -FDT_ERR_NOTFOUND); 217 setup_args_progname(bootargs); 218 219 initrd_setup(); 220 221 if (!(auxinfo.flags & AUXINFO_MMU_OFF)) 222 setup_vm(); 223 224 banner(); 225 } 226 227 #ifdef CONFIG_EFI 228 #include <efi.h> 229 230 extern unsigned long exception_vectors; 231 extern unsigned long boot_hartid; 232 233 static efi_status_t efi_mem_init(efi_bootinfo_t *efi_bootinfo) 234 { 235 struct mem_region *freemem_mr = NULL, *code, *data; 236 void *freemem; 237 238 memregions_init(riscv_mem_regions, NR_MEM_REGIONS); 239 240 memregions_efi_init(&efi_bootinfo->mem_map, &freemem_mr); 241 if (!freemem_mr) 242 return EFI_OUT_OF_RESOURCES; 243 244 memregions_split((unsigned long)&_etext, &code, &data); 245 assert(code && (code->flags & MR_F_CODE)); 246 if (data) 247 data->flags &= ~MR_F_CODE; 248 249 for (struct mem_region *m = mem_regions; m->end; ++m) 250 assert(m == code || !(m->flags & MR_F_CODE)); 251 252 freemem = (void *)PAGE_ALIGN(freemem_mr->start); 253 254 if (efi_bootinfo->fdt) 255 freemem_push_fdt(&freemem, efi_bootinfo->fdt); 256 257 mmu_disable(); 258 mem_allocator_init(freemem_mr, (unsigned long)freemem); 259 260 return EFI_SUCCESS; 261 } 262 263 efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo) 264 { 265 efi_status_t status; 266 267 csr_write(CSR_STVEC, (unsigned long)&exception_vectors); 268 csr_write(CSR_SSCRATCH, boot_hartid); 269 270 status = efi_mem_init(efi_bootinfo); 271 if (status != EFI_SUCCESS) { 272 printf("Failed to initialize memory\n"); 273 return status; 274 } 275 276 cpu_init(); 277 timer_get_frequency(); 278 thread_info_init(); 279 local_hart_init(); 280 io_init(); 281 initrd_setup(); 282 283 if (!(auxinfo.flags & AUXINFO_MMU_OFF)) 284 setup_vm(); 285 286 banner(); 287 288 return EFI_SUCCESS; 289 } 290 #endif /* CONFIG_EFI */ 291