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