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