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