#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sched.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/reboot.h>
#include <sys/mount.h>
#include <sys/exec.h>
#include <sys/user.h>
#include <sys/conf.h>
#include <sys/kcore.h>
#include <sys/msgbuf.h>
#include <sys/buf.h>
#include <sys/sensors.h>
#include <sys/malloc.h>
#include <sys/syscallargs.h>
#include <sys/pledge.h>
#include <net/if.h>
#include <uvm/uvm_extern.h>
#include <dev/cons.h>
#include <dev/ofw/fdt.h>
#include <dev/ofw/openfirm.h>
#include <machine/param.h>
#include <machine/bootconfig.h>
#include <machine/bus.h>
#include <machine/sbi.h>
#include <machine/sysarch.h>
#include <machine/db_machdep.h>
#include <ddb/db_extern.h>
#include <dev/efi/efi.h>
#include "softraid.h"
#if NSOFTRAID > 0
#include <dev/softraidvar.h>
#endif
extern vaddr_t virtual_avail;
extern uint64_t esym;
extern char _start[];
char *boot_args = NULL;
uint8_t *bootmac = NULL;
int stdout_node;
int stdout_speed;
void (*cpuresetfn)(void);
void (*powerdownfn)(void);
int cold = 1;
struct vm_map *exec_map = NULL;
struct vm_map *phys_map = NULL;
int physmem;
caddr_t msgbufaddr;
paddr_t msgbufphys;
struct user *proc0paddr;
struct uvm_constraint_range dma_constraint = { 0x0, (paddr_t)-1 };
struct uvm_constraint_range *uvm_md_constraints[] = {
&dma_constraint,
NULL,
};
char machine[] = MACHINE;
int safepri = 0;
uint32_t boot_hart;
struct cpu_info cpu_info_primary;
struct cpu_info *cpu_info[MAXCPUS] = { &cpu_info_primary };
uint64_t tb_freq = 1000000;
struct fdt_reg memreg[VM_PHYSSEG_MAX];
int nmemreg;
void memreg_add(const struct fdt_reg *);
void memreg_remove(const struct fdt_reg *);
static int
atoi(const char *s)
{
int n, neg;
n = 0;
neg = 0;
while (*s == '-') {
s++;
neg = !neg;
}
while (*s != '\0') {
if (*s < '0' || *s > '9')
break;
n = (10 * n) + (*s - '0');
s++;
}
return (neg ? -n : n);
}
void *
fdt_find_cons(const char *name)
{
char *alias = "serial0";
char buf[128];
char *stdout = NULL;
char *p;
void *node;
node = fdt_find_node("/chosen");
if (node) {
if (fdt_node_property(node, "stdout-path", &stdout) > 0) {
if (strchr(stdout, ':') != NULL) {
strlcpy(buf, stdout, sizeof(buf));
if ((p = strchr(buf, ':')) != NULL) {
*p++ = '\0';
stdout_speed = atoi(p);
}
stdout = buf;
}
if (stdout[0] != '/') {
alias = stdout;
stdout = NULL;
}
}
}
if (stdout == NULL) {
node = fdt_find_node("/aliases");
if (node)
fdt_node_property(node, alias, &stdout);
}
if (stdout) {
node = fdt_find_node(stdout);
if (node && fdt_is_compatible(node, name)) {
stdout_node = OF_finddevice(stdout);
return (node);
}
}
return (NULL);
}
void com_fdt_init_cons(void);
void sfuart_init_cons(void);
void
consinit(void)
{
static int consinit_called = 0;
if (consinit_called != 0)
return;
consinit_called = 1;
com_fdt_init_cons();
sfuart_init_cons();
}
void
cpu_idle_cycle(void)
{
intr_enable();
__asm volatile("wfi");
}
struct trapframe proc0tf;
void
cpu_startup(void)
{
u_int loop;
paddr_t minaddr;
paddr_t maxaddr;
proc0.p_addr = proc0paddr;
pmap_postinit();
for (loop = 0; loop < atop(MSGBUFSIZE); ++loop)
pmap_kenter_pa((vaddr_t)msgbufaddr + loop * PAGE_SIZE,
msgbufphys + loop * PAGE_SIZE, PROT_READ | PROT_WRITE);
pmap_update(pmap_kernel());
initmsgbuf(msgbufaddr, round_page(MSGBUFSIZE));
printf("%s", version);
printf("real mem = %lu (%luMB)\n", ptoa(physmem),
ptoa(physmem) / 1024 / 1024);
minaddr = vm_map_min(kernel_map);
exec_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
16 * NCARGS, VM_MAP_PAGEABLE, FALSE, NULL);
phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
VM_PHYS_SIZE, 0, FALSE, NULL);
bufinit();
printf("avail mem = %lu (%luMB)\n", ptoa(uvmexp.free),
ptoa(uvmexp.free) / 1024 / 1024);
sbi_print_version();
curpcb = &proc0.p_addr->u_pcb;
curpcb->pcb_flags = 0;
curpcb->pcb_tf = &proc0tf;
if (boothowto & RB_CONFIG) {
#ifdef BOOT_CONFIG
user_config();
#else
printf("kernel does not support -c; continuing..\n");
#endif
}
}
void cpu_switchto_asm(struct proc *, struct proc *);
void
cpu_switchto(struct proc *old, struct proc *new)
{
if (old) {
struct pcb *pcb = &old->p_addr->u_pcb;
struct trapframe *tf = pcb->pcb_tf;
if (pcb->pcb_flags & PCB_FPU)
fpu_save(old, tf);
tf->tf_sstatus &= ~SSTATUS_FS_MASK;
tf->tf_sstatus |= SSTATUS_FS_OFF;
}
cpu_switchto_asm(old, new);
}
int
cpu_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen, struct proc *p)
{
char *compatible;
int node, len, error;
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case CPU_COMPATIBLE:
node = OF_finddevice("/");
len = OF_getproplen(node, "compatible");
if (len <= 0)
return (EOPNOTSUPP);
compatible = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
OF_getprop(node, "compatible", compatible, len);
compatible[len - 1] = 0;
error = sysctl_rdstring(oldp, oldlenp, newp, compatible);
free(compatible, M_TEMP, len);
return error;
default:
return (EOPNOTSUPP);
}
}
int waittime = -1;
__dead void
boot(int howto)
{
if ((howto & RB_RESET) != 0)
goto doreset;
if (cold) {
if ((howto & RB_USERREQ) == 0)
howto |= RB_HALT;
goto haltsys;
}
boothowto = howto;
if ((howto & RB_NOSYNC) == 0 && waittime < 0) {
waittime = 0;
vfs_shutdown(curproc);
if ((howto & RB_TIMEBAD) == 0) {
resettodr();
} else {
printf("WARNING: not updating battery clock\n");
}
}
if_downall();
uvm_shutdown();
splhigh();
cold = 1;
if ((howto & RB_DUMP) != 0)
printf("no dump so far\n");
haltsys:
config_suspend_all(DVACT_POWERDOWN);
if ((howto & RB_HALT) != 0) {
if ((howto & RB_POWERDOWN) != 0) {
printf("\nAttempting to power down...\n");
delay(500000);
if (powerdownfn)
(*powerdownfn)();
}
printf("\n");
printf("The operating system has halted.\n");
printf("Please press any key to reboot.\n\n");
cngetc();
}
doreset:
printf("rebooting...\n");
delay(500000);
if (cpuresetfn)
(*cpuresetfn)();
printf("reboot failed; spinning\n");
for (;;)
continue;
}
void
setregs(struct proc *p, struct exec_package *pack, u_long stack,
struct ps_strings *arginfo)
{
struct trapframe *tf = p->p_addr->u_pcb.pcb_tf;
struct pcb *pcb = &p->p_addr->u_pcb;
pcb->pcb_flags &= ~PCB_FPU;
tf->tf_sstatus &= ~SSTATUS_FS_MASK;
tf->tf_sstatus |= SSTATUS_FS_OFF;
memset(tf, 0, sizeof *tf);
tf->tf_sp = STACKALIGN(stack);
tf->tf_ra = pack->ep_entry;
tf->tf_sepc = pack->ep_entry;
}
void
need_resched(struct cpu_info *ci)
{
ci->ci_want_resched = 1;
if (ci->ci_curproc) {
aston(ci->ci_curproc);
cpu_kick(ci);
}
}
phys_ram_seg_t mem_clusters[VM_PHYSSEG_MAX];
int mem_cluster_cnt;
int
cpu_dumpsize(void)
{
int size;
size = ALIGN(sizeof(kcore_seg_t)) +
ALIGN(mem_cluster_cnt * sizeof(phys_ram_seg_t));
if (roundup(size, dbtob(1)) != dbtob(1))
return (-1);
return (1);
}
u_long
cpu_dump_mempagecnt(void)
{
return 0;
}
u_long dumpmag = 0x8fca0101;
int dumpsize = 0;
long dumplo = 0;
void
dumpconf(void)
{
int nblks, dumpblks;
if (dumpdev == NODEV ||
(nblks = (bdevsw[major(dumpdev)].d_psize)(dumpdev)) == 0)
return;
if (nblks <= ctod(1))
return;
dumpblks = cpu_dumpsize();
if (dumpblks < 0)
return;
dumpblks += ctod(cpu_dump_mempagecnt());
if (dumpblks > (nblks - ctod(1)))
return;
dumplo = nblks - dumpblks;
dumpsize = cpu_dump_mempagecnt();
}
int
sys_sysarch(struct proc *p, void *v, register_t *retval)
{
struct sys_sysarch_args
*uap = v;
struct riscv_sync_icache_args args;
int error = 0;
int op = SCARG(uap, op);
if ((p->p_p->ps_flags & PS_PLEDGE) && op != RISCV_SYNC_ICACHE)
return pledge_fail(p, EINVAL, 0);
switch (op) {
case RISCV_SYNC_ICACHE:
if (SCARG(uap, parms) != NULL)
error = copyin(SCARG(uap, parms), &args, sizeof(args));
if (error)
break;
pmap_proc_iflush(p->p_p, (vaddr_t)args.addr, args.len);
break;
default:
error = EINVAL;
break;
}
return (error);
}
uint64_t mmap_start;
uint32_t mmap_size;
uint32_t mmap_desc_size;
uint32_t mmap_desc_ver;
EFI_MEMORY_DESCRIPTOR *mmap;
void collect_kernel_args(const char *);
void process_kernel_args(void);
int pmap_bootstrap_bs_map(bus_space_tag_t, bus_addr_t,
bus_size_t, int, bus_space_handle_t *);
void
initriscv(struct riscv_bootparams *rbp)
{
paddr_t memstart, memend;
paddr_t startpa, endpa, pa;
vaddr_t vstart, va;
struct fdt_head *fh;
void *config = (void *)rbp->dtbp_phys;
void *fdt = NULL;
struct fdt_reg reg;
void *node;
EFI_PHYSICAL_ADDRESS system_table = 0;
int (*map_func_save)(bus_space_tag_t, bus_addr_t, bus_size_t, int,
bus_space_handle_t *);
int i;
__asm volatile("mv tp, %0" :: "r"(&cpu_info_primary));
sbi_init();
memstart = rbp->kern_phys;
memend = memstart + 64 * 1024 * 1024;
vstart = pmap_bootstrap(rbp->kern_phys - KERNBASE, rbp->kern_l1pt,
KERNBASE, esym, memstart, memend);
va = vstart;
startpa = trunc_page((paddr_t)config);
endpa = round_page((paddr_t)config + sizeof(struct fdt_head));
for (pa = startpa; pa < endpa; pa += PAGE_SIZE, va += PAGE_SIZE)
pmap_kenter_cache(va, pa, PROT_READ | PROT_WRITE, PMAP_CACHE_WB);
fh = (void *)(vstart + ((paddr_t)config - startpa));
if (betoh32(fh->fh_magic) != FDT_MAGIC || betoh32(fh->fh_size) == 0)
panic("%s: no FDT", __func__);
endpa = round_page((paddr_t)config + betoh32(fh->fh_size));
for (; pa < endpa; pa += PAGE_SIZE, va += PAGE_SIZE)
pmap_kenter_cache(va, pa, PROT_READ | PROT_WRITE, PMAP_CACHE_WB);
config = (void *)(vstart + ((paddr_t)config - startpa));
vstart = va;
if (!fdt_init(config))
panic("%s: corrupt FDT", __func__);
node = fdt_find_node("/cpus");
if (node != NULL) {
char *prop;
int len;
len = fdt_node_property(node, "timebase-frequency", &prop);
if (len == sizeof(uint32_t))
tb_freq = bemtoh32((uint32_t *)prop);
}
node = fdt_find_node("/chosen");
if (node != NULL) {
char *prop;
int len;
static uint8_t lladdr[6];
len = fdt_node_property(node, "boot-hartid", &prop);
if (len == sizeof(boot_hart))
boot_hart = bemtoh32((uint32_t *)prop);
len = fdt_node_property(node, "bootargs", &prop);
if (len > 0)
collect_kernel_args(prop);
len = fdt_node_property(node, "openbsd,boothowto", &prop);
if (len == sizeof(boothowto))
boothowto = bemtoh32((uint32_t *)prop);
len = fdt_node_property(node, "openbsd,bootduid", &prop);
if (len == sizeof(bootduid))
memcpy(bootduid, prop, sizeof(bootduid));
len = fdt_node_property(node, "openbsd,bootmac", &prop);
if (len == sizeof(lladdr)) {
memcpy(lladdr, prop, sizeof(lladdr));
bootmac = lladdr;
}
len = fdt_node_property(node, "openbsd,sr-bootuuid", &prop);
#if NSOFTRAID > 0
if (len == sizeof(sr_bootuuid))
memcpy(&sr_bootuuid, prop, sizeof(sr_bootuuid));
#endif
if (len > 0)
explicit_bzero(prop, len);
len = fdt_node_property(node, "openbsd,sr-bootkey", &prop);
#if NSOFTRAID > 0
if (len == sizeof(sr_bootkey))
memcpy(&sr_bootkey, prop, sizeof(sr_bootkey));
#endif
if (len > 0)
explicit_bzero(prop, len);
len = fdt_node_property(node, "openbsd,uefi-mmap-start", &prop);
if (len == sizeof(mmap_start))
mmap_start = bemtoh64((uint64_t *)prop);
len = fdt_node_property(node, "openbsd,uefi-mmap-size", &prop);
if (len == sizeof(mmap_size))
mmap_size = bemtoh32((uint32_t *)prop);
len = fdt_node_property(node, "openbsd,uefi-mmap-desc-size", &prop);
if (len == sizeof(mmap_desc_size))
mmap_desc_size = bemtoh32((uint32_t *)prop);
len = fdt_node_property(node, "openbsd,uefi-mmap-desc-ver", &prop);
if (len == sizeof(mmap_desc_ver))
mmap_desc_ver = bemtoh32((uint32_t *)prop);
len = fdt_node_property(node, "openbsd,uefi-system-table", &prop);
if (len == sizeof(system_table))
system_table = bemtoh64((uint64_t *)prop);
len = fdt_node_property(node, "openbsd,dma-constraint", &prop);
if (len == sizeof(dma_constraint)) {
dma_constraint.ucr_low = bemtoh64((uint64_t *)prop);
dma_constraint.ucr_high = bemtoh64((uint64_t *)prop + 1);
}
}
process_kernel_args();
proc0paddr = (struct user *)rbp->kern_stack;
msgbufaddr = (caddr_t)vstart;
msgbufphys = pmap_steal_avail(round_page(MSGBUFSIZE), PAGE_SIZE, NULL);
vstart += round_page(MSGBUFSIZE);
zero_page = vstart;
vstart += MAXCPUS * PAGE_SIZE;
copy_src_page = vstart;
vstart += MAXCPUS * PAGE_SIZE;
copy_dst_page = vstart;
vstart += MAXCPUS * PAGE_SIZE;
if (fdt_get_size(config) != 0) {
uint32_t csize, size = round_page(fdt_get_size(config));
paddr_t pa;
vaddr_t va;
pa = pmap_steal_avail(size, PAGE_SIZE, NULL);
memcpy((void *)PHYS_TO_DMAP(pa), config, size);
for (va = vstart, csize = size; csize > 0;
csize -= PAGE_SIZE, va += PAGE_SIZE, pa += PAGE_SIZE)
pmap_kenter_cache(va, pa, PROT_READ, PMAP_CACHE_WB);
fdt = (void *)vstart;
vstart += size;
}
if (mmap_start != 0) {
uint32_t csize, size = round_page(mmap_size);
paddr_t pa, startpa, endpa;
vaddr_t va;
startpa = trunc_page(mmap_start);
endpa = round_page(mmap_start + mmap_size);
for (pa = startpa, va = vstart; pa < endpa;
pa += PAGE_SIZE, va += PAGE_SIZE)
pmap_kenter_cache(va, pa, PROT_READ, PMAP_CACHE_WB);
pa = pmap_steal_avail(size, PAGE_SIZE, NULL);
memcpy((void *)PHYS_TO_DMAP(pa),
(caddr_t)vstart + (mmap_start - startpa), mmap_size);
pmap_kremove(vstart, endpa - startpa);
for (va = vstart, csize = size; csize > 0;
csize -= PAGE_SIZE, va += PAGE_SIZE, pa += PAGE_SIZE)
pmap_kenter_cache(va, pa, PROT_READ | PROT_WRITE, PMAP_CACHE_WB);
mmap = (void *)vstart;
vstart += size;
}
virtual_avail = vstart;
if (fdt)
fdt_init(fdt);
map_func_save = riscv64_bs_tag._space_map;
riscv64_bs_tag._space_map = pmap_bootstrap_bs_map;
consinit();
riscv64_bs_tag._space_map = map_func_save;
pmap_avail_fixup();
uvmexp.pagesize = PAGE_SIZE;
uvm_setpagesize();
pmap_physload_avail();
if (mmap && mmap_desc_ver == EFI_MEMORY_DESCRIPTOR_VERSION) {
EFI_MEMORY_DESCRIPTOR *desc = mmap;
for (i = 0; i < mmap_size / mmap_desc_size; i++) {
printf("type 0x%x pa 0x%llx va 0x%llx pages 0x%llx attr 0x%llx\n",
desc->Type, desc->PhysicalStart,
desc->VirtualStart, desc->NumberOfPages,
desc->Attribute);
if ((desc->Type == EfiConventionalMemory ||
desc->Type == EfiBootServicesCode ||
desc->Type == EfiBootServicesData) &&
desc->NumberOfPages >= 16) {
reg.addr = desc->PhysicalStart;
reg.size = ptoa(desc->NumberOfPages);
memreg_add(®);
}
desc = NextMemoryDescriptor(desc, mmap_desc_size);
}
} else {
node = fdt_find_node("/memory");
if (node == NULL)
panic("%s: no memory specified", __func__);
for (i = 0; nmemreg < nitems(memreg); i++) {
if (fdt_get_reg(node, i, ®))
break;
if (reg.size == 0)
continue;
memreg_add(®);
}
}
node = fdt_find_node("/reserved-memory");
if (node) {
for (node = fdt_child_node(node); node;
node = fdt_next_node(node)) {
if (fdt_get_reg(node, 0, ®))
continue;
if (reg.size == 0)
continue;
memreg_remove(®);
}
}
reg.addr = memstart;
reg.size = memend - memstart;
memreg_remove(®);
for (i = 0; i < nmemreg; i++) {
paddr_t start = memreg[i].addr;
paddr_t end = start + memreg[i].size;
uvm_page_physload(atop(start), atop(end),
atop(start), atop(end), 0);
}
node = fdt_find_node("/memory");
if (node) {
while (node) {
const char *s = fdt_node_name(node);
if (strncmp(s, "memory", 6) == 0 &&
(s[6] == '\0' || s[6] == '@')) {
for (i = 0; i < VM_PHYSSEG_MAX; i++) {
if (fdt_get_reg(node, i, ®))
break;
if (reg.size == 0)
continue;
physmem += atop(reg.size);
}
}
node = fdt_next_node(node);
}
} else {
for (i = 0; i < nmemreg; i++)
physmem += atop(memreg[i].size);
}
kmeminit_nkmempages();
pmap_growkernel(VM_MIN_KERNEL_ADDRESS + 1024 * 1024 * 1024 +
physmem * sizeof(struct vm_page) + ptoa(nkmempages));
#ifdef DDB
db_machine_init();
ddb_init();
if (boothowto & RB_KDB)
db_enter();
#endif
softintr_init();
splraise(IPL_IPI);
}
char bootargs[256];
void
collect_kernel_args(const char *args)
{
strlcpy(bootargs, args, sizeof(bootargs));
}
void
process_kernel_args(void)
{
char *cp = bootargs;
if (*cp == 0)
return;
while (*cp != ' ' && *cp != 0)
cp++;
if (*cp != 0)
*cp++ = 0;
while (*cp == ' ')
cp++;
boot_args = cp;
printf("bootargs: %s\n", boot_args);
while (*cp != '-')
if (*cp++ == '\0')
return;
while (*++cp != 0) {
switch (*cp) {
case 'a':
boothowto |= RB_ASKNAME;
break;
case 'c':
boothowto |= RB_CONFIG;
break;
case 'd':
boothowto |= RB_KDB;
break;
case 's':
boothowto |= RB_SINGLE;
break;
default:
printf("unknown option `%c'\n", *cp);
break;
}
}
}
int
pmap_bootstrap_bs_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size,
int flags, bus_space_handle_t *bshp)
{
u_long startpa, pa, endpa;
vaddr_t va;
va = virtual_avail;
startpa = trunc_page(bpa);
endpa = round_page((bpa + size));
*bshp = (bus_space_handle_t)(va + (bpa - startpa));
for (pa = startpa; pa < endpa; pa += PAGE_SIZE, va += PAGE_SIZE)
pmap_kenter_cache(va, pa, PROT_READ | PROT_WRITE,
PMAP_CACHE_DEV);
virtual_avail = va;
return 0;
}
void
memreg_add(const struct fdt_reg *reg)
{
int i;
for (i = 0; i < nmemreg; i++) {
if (reg->addr == memreg[i].addr + memreg[i].size) {
memreg[i].size += reg->size;
return;
}
if (reg->addr + reg->size == memreg[i].addr) {
memreg[i].addr = reg->addr;
memreg[i].size += reg->size;
return;
}
}
if (nmemreg >= nitems(memreg))
return;
memreg[nmemreg++] = *reg;
}
void
memreg_remove(const struct fdt_reg *reg)
{
uint64_t start = reg->addr;
uint64_t end = reg->addr + reg->size;
int i, j;
for (i = 0; i < nmemreg; i++) {
uint64_t memstart = memreg[i].addr;
uint64_t memend = memreg[i].addr + memreg[i].size;
if (end <= memstart)
continue;
if (start >= memend)
continue;
if (start <= memstart)
memstart = MIN(end, memend);
if (end >= memend)
memend = MAX(start, memstart);
if (start > memstart && end < memend) {
if (nmemreg < nitems(memreg)) {
memreg[nmemreg].addr = end;
memreg[nmemreg].size = memend - end;
nmemreg++;
}
memend = start;
}
memreg[i].addr = memstart;
memreg[i].size = memend - memstart;
}
for (i = nmemreg - 1; i >= 0; i--) {
if (memreg[i].size == 0) {
for (j = i; (j + 1) < nmemreg; j++)
memreg[j] = memreg[j + 1];
nmemreg--;
}
}
}