blob: 50ffb0d0751bb320fa608580f39f9e0b94d2db5e [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Initialize machine setup information and I/O.
*
* Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
*/
#include <libcflat.h>
#include <alloc.h>
#include <alloc_page.h>
#include <alloc_phys.h>
#include <argv.h>
#include <auxinfo.h>
#include <cpumask.h>
#include <devicetree.h>
#include <memregions.h>
#include <on-cpus.h>
#include <vmalloc.h>
#include <asm/csr.h>
#include <asm/mmu.h>
#include <asm/page.h>
#include <asm/processor.h>
#include <asm/setup.h>
#define VA_BASE ((phys_addr_t)3 * SZ_1G)
#if __riscv_xlen == 64
#define VA_TOP ((phys_addr_t)4 * SZ_1G)
#else
#define VA_TOP ((phys_addr_t)0)
#endif
#define MAX_DT_MEM_REGIONS 16
#define NR_MEM_REGIONS (MAX_DT_MEM_REGIONS + 16)
extern unsigned long _etext;
char *initrd;
u32 initrd_size;
struct thread_info cpus[NR_CPUS];
int nr_cpus;
static struct mem_region riscv_mem_regions[NR_MEM_REGIONS + 1];
int hartid_to_cpu(unsigned long hartid)
{
int cpu;
for_each_present_cpu(cpu)
if (cpus[cpu].hartid == hartid)
return cpu;
return -1;
}
static void cpu_set_fdt(int fdtnode __unused, u64 regval, void *info __unused)
{
int cpu = nr_cpus++;
assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
cpus[cpu].cpu = cpu;
cpus[cpu].hartid = regval;
set_cpu_present(cpu, true);
}
static void cpu_init_acpi(void)
{
assert_msg(false, "ACPI not available");
}
static void cpu_init(void)
{
int ret;
nr_cpus = 0;
if (dt_available()) {
ret = dt_for_each_cpu_node(cpu_set_fdt, NULL);
assert(ret == 0);
} else {
cpu_init_acpi();
}
set_cpu_online(hartid_to_cpu(csr_read(CSR_SSCRATCH)), true);
cpu0_calls_idle = true;
}
static void mem_allocator_init(phys_addr_t freemem_start, phys_addr_t freemem_end)
{
phys_addr_t base, top;
freemem_start = PAGE_ALIGN(freemem_start);
freemem_end &= PAGE_MASK;
/*
* The assert below is mostly checking that the free memory doesn't
* start in the 3G-4G range, which is reserved for virtual addresses,
* but it also confirms that there is some free memory (the amount
* is arbitrarily selected, but should be sufficient for a unit test)
*
* TODO: Allow the VA range to shrink and move.
*/
if (freemem_end > VA_BASE)
freemem_end = VA_BASE;
assert(freemem_end - freemem_start >= SZ_1M * 16);
init_alloc_vpage(__va(VA_TOP));
/*
* TODO: Remove the need for this phys allocator dance, since, as we
* can see with the assert, we could have gone straight to the page
* allocator.
*/
phys_alloc_init(freemem_start, freemem_end - freemem_start);
phys_alloc_set_minimum_alignment(PAGE_SIZE);
phys_alloc_get_unused(&base, &top);
assert(base == freemem_start && top == freemem_end);
page_alloc_init_area(0, freemem_start >> PAGE_SHIFT, freemem_end >> PAGE_SHIFT);
page_alloc_ops_enable();
}
static void mem_init(phys_addr_t freemem_start)
{
struct mem_region *freemem, *code, *data;
memregions_init(riscv_mem_regions, NR_MEM_REGIONS);
memregions_add_dt_regions(MAX_DT_MEM_REGIONS);
/* Split the region with the code into two regions; code and data */
memregions_split((unsigned long)&_etext, &code, &data);
assert(code);
code->flags |= MR_F_CODE;
freemem = memregions_find(freemem_start);
assert(freemem && !(freemem->flags & (MR_F_IO | MR_F_CODE)));
mem_allocator_init(freemem_start, freemem->end);
}
static void freemem_push_fdt(void **freemem, const void *fdt)
{
u32 fdt_size;
int ret;
fdt_size = fdt_totalsize(fdt);
ret = fdt_move(fdt, *freemem, fdt_size);
assert(ret == 0);
ret = dt_init(*freemem);
assert(ret == 0);
*freemem += fdt_size;
}
static void freemem_push_dt_initrd(void **freemem)
{
const char *tmp;
int ret;
ret = dt_get_initrd(&tmp, &initrd_size);
assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
if (ret == 0) {
initrd = *freemem;
memmove(initrd, tmp, initrd_size);
*freemem += initrd_size;
}
}
static void initrd_setup(void)
{
char *env;
if (!initrd)
return;
/* environ is currently the only file in the initrd */
env = malloc(initrd_size);
memcpy(env, initrd, initrd_size);
setup_env(env, initrd_size);
}
static void banner(void)
{
puts("\n");
puts("##########################################################################\n");
puts("# kvm-unit-tests\n");
puts("##########################################################################\n");
puts("\n");
}
void setup(const void *fdt, phys_addr_t freemem_start)
{
void *freemem;
const char *bootargs;
int ret;
assert(sizeof(long) == 8 || freemem_start < VA_BASE);
freemem = __va(freemem_start);
freemem_push_fdt(&freemem, fdt);
freemem_push_dt_initrd(&freemem);
mem_init(PAGE_ALIGN(__pa(freemem)));
cpu_init();
thread_info_init();
io_init();
ret = dt_get_bootargs(&bootargs);
assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
setup_args_progname(bootargs);
initrd_setup();
if (!(auxinfo.flags & AUXINFO_MMU_OFF))
setup_vm();
banner();
}
#ifdef CONFIG_EFI
#include <efi.h>
extern unsigned long exception_vectors;
extern unsigned long boot_hartid;
static efi_status_t efi_mem_init(efi_bootinfo_t *efi_bootinfo)
{
struct mem_region *freemem_mr = NULL, *code, *data;
void *freemem;
memregions_init(riscv_mem_regions, NR_MEM_REGIONS);
memregions_efi_init(&efi_bootinfo->mem_map, &freemem_mr);
if (!freemem_mr)
return EFI_OUT_OF_RESOURCES;
memregions_split((unsigned long)&_etext, &code, &data);
assert(code && (code->flags & MR_F_CODE));
if (data)
data->flags &= ~MR_F_CODE;
for (struct mem_region *m = mem_regions; m->end; ++m)
assert(m == code || !(m->flags & MR_F_CODE));
freemem = (void *)PAGE_ALIGN(freemem_mr->start);
if (efi_bootinfo->fdt)
freemem_push_fdt(&freemem, efi_bootinfo->fdt);
mmu_disable();
mem_allocator_init((unsigned long)freemem, freemem_mr->end);
return EFI_SUCCESS;
}
efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
{
efi_status_t status;
csr_write(CSR_STVEC, (unsigned long)&exception_vectors);
csr_write(CSR_SSCRATCH, boot_hartid);
status = efi_mem_init(efi_bootinfo);
if (status != EFI_SUCCESS) {
printf("Failed to initialize memory\n");
return status;
}
cpu_init();
thread_info_init();
io_init();
initrd_setup();
if (!(auxinfo.flags & AUXINFO_MMU_OFF))
setup_vm();
banner();
return EFI_SUCCESS;
}
#endif /* CONFIG_EFI */