diff options
author | 2019-07-17 14:36:31 +0000 | |
---|---|---|
committer | 2019-07-17 14:36:31 +0000 | |
commit | 3a62b615e8a99ae852ef9000d03e4dc04e13301f (patch) | |
tree | cadfa3d385a95b6e9cb03896016cd3eb54111d91 | |
parent | Update appstest.sh (diff) | |
download | wireguard-openbsd-3a62b615e8a99ae852ef9000d03e4dc04e13301f.tar.xz wireguard-openbsd-3a62b615e8a99ae852ef9000d03e4dc04e13301f.zip |
Add a bootloader for octeon.
The firmware on OCTEON machines usually does not provide an interface
for accessing devices, which has made it tricky to implement an OpenBSD
bootloader. To solve this device access problem, this new loader has
been built on top of a small kernel. The kernel provides all the
necessary devices drivers, while most of the usual bootloader logic
is in a userspace program in a ramdisk.
The loader program is accompanied by a special device, octboot(4).
The main purpose of this device is to implement a mechanism for
loading and launching kernels. The mechanism has been inspired by Linux'
kexec(2) system call.
The bootloader will be enabled later when it is ready for general use.
Discussed with deraadt@
-rw-r--r-- | distrib/octeon/boot/Makefile | 65 | ||||
-rw-r--r-- | distrib/octeon/boot/list | 9 | ||||
-rw-r--r-- | etc/etc.octeon/MAKEDEV.md | 6 | ||||
-rw-r--r-- | sys/arch/octeon/compile/BOOT/Makefile | 1 | ||||
-rw-r--r-- | sys/arch/octeon/conf/BOOT | 70 | ||||
-rw-r--r-- | sys/arch/octeon/conf/files.octeon | 5 | ||||
-rw-r--r-- | sys/arch/octeon/dev/octboot.c | 387 | ||||
-rw-r--r-- | sys/arch/octeon/include/conf.h | 10 | ||||
-rw-r--r-- | sys/arch/octeon/include/octboot.h | 35 | ||||
-rw-r--r-- | sys/arch/octeon/include/octeonvar.h | 9 | ||||
-rw-r--r-- | sys/arch/octeon/octeon/conf.c | 5 | ||||
-rw-r--r-- | sys/arch/octeon/octeon/locore.S | 14 | ||||
-rw-r--r-- | sys/arch/octeon/octeon/machdep.c | 243 | ||||
-rw-r--r-- | sys/arch/octeon/stand/rdboot/Makefile | 16 | ||||
-rw-r--r-- | sys/arch/octeon/stand/rdboot/cmd.c | 503 | ||||
-rw-r--r-- | sys/arch/octeon/stand/rdboot/cmd.h | 65 | ||||
-rw-r--r-- | sys/arch/octeon/stand/rdboot/disk.c | 207 | ||||
-rw-r--r-- | sys/arch/octeon/stand/rdboot/disk.h | 21 | ||||
-rw-r--r-- | sys/arch/octeon/stand/rdboot/rdboot.c | 192 | ||||
-rw-r--r-- | sys/arch/octeon/stand/rdboot/vars.c | 203 |
20 files changed, 2056 insertions, 10 deletions
diff --git a/distrib/octeon/boot/Makefile b/distrib/octeon/boot/Makefile new file mode 100644 index 00000000000..9d9e619bb65 --- /dev/null +++ b/distrib/octeon/boot/Makefile @@ -0,0 +1,65 @@ +# $OpenBSD: Makefile,v 1.1 2019/07/17 14:36:32 visa Exp $ + +FS= miniroot${OSrev}.fs +FSSIZE= 24576 +FSDISKTYPE= miniroot +MOUNT_POINT= /mnt +MTREE= ${UTILS}/mtree.conf +RAMDISK= BOOT + +LISTS= ${.CURDIR}/list +UTILS= ${.CURDIR}/../../miniroot + +MRDISKTYPE= rdroot +MRMAKEFSARGS= -o disklabel=${MRDISKTYPE},minfree=0,density=4096 + +all: bsd.rd + +bsd.rd: mr.fs bsd + cp bsd bsd.rd + rdsetroot bsd.rd mr.fs + +bsd: + cd ${.CURDIR}/../../../sys/arch/${MACHINE}/compile/${RAMDISK} && \ + su ${BUILDUSER} -c '${MAKE} config && ${MAKE} clean && exec ${MAKE}' + cp -p ${.CURDIR}/../../../sys/arch/${MACHINE}/compile/${RAMDISK}/obj/bsd bsd + +mr.fs: rdboot + rm -rf $@.d + install -d -o root -g wheel $@.d + mtree -def ${MTREE} -p $@.d -u + CURDIR=${.CURDIR} OBJDIR=${.OBJDIR} OSrev=${OSrev} \ + TARGDIR=$@.d UTILS=${UTILS} RELEASEDIR=${RELEASEDIR} \ + sh ${UTILS}/runlist.sh ${LISTS} + makefs ${MRMAKEFSARGS} $@ $@.d + +instbin.mk instbin.cache instbin.c: instbin.conf + crunchgen -E -M -D ${.CURDIR}/../../.. -L ${DESTDIR}/usr/lib \ + -c instbin.c -e instbin -m instbin.mk instbin.conf + +instbin: instbin.mk instbin.cache instbin.c + ${MAKE} -f instbin.mk SRCLIBDIR=${.CURDIR}/../../../lib all + +instbin.conf: ${LISTS} + awk -f ${UTILS}/makeconf.awk ${LISTS} > instbin.conf + +rdboot: + cp -p ${.CURDIR}/../../../sys/arch/${MACHINE}/stand/rdboot/obj/rdboot rdboot + strip rdboot + +unconfig: + -umount -f ${MOUNT_POINT} + -[ -f vnd ] && vnconfig -u `cat vnd` && rm -f vnd + +.ifdef RELEASEDIR +install: + cp bsd.rd ${RELEASEDIR}/boot + chmod a+r ${RELEASEDIR}/boot +.endif + +clean cleandir: + rm -f *.core mr.fs instbin instbin.mk instbin.cache \ + lib*.a lib*.olist instbin.map *.o *.lo *.c bsd bsd.rd rdboot + rm -rf cd-dir mr.fs.d + +.include <bsd.obj.mk> diff --git a/distrib/octeon/boot/list b/distrib/octeon/boot/list new file mode 100644 index 00000000000..8bc8325c46e --- /dev/null +++ b/distrib/octeon/boot/list @@ -0,0 +1,9 @@ +# $OpenBSD: list,v 1.1 2019/07/17 14:36:32 visa Exp $ + +SRCDIRS distrib/special + +COPY ${OBJDIR}/rdboot sbin/init + +# copy the MAKEDEV script and make some devices +SCRIPT ${DESTDIR}/dev/MAKEDEV dev/MAKEDEV +SPECIAL cd dev; sh MAKEDEV boot diff --git a/etc/etc.octeon/MAKEDEV.md b/etc/etc.octeon/MAKEDEV.md index d5f61418c60..173fb01913d 100644 --- a/etc/etc.octeon/MAKEDEV.md +++ b/etc/etc.octeon/MAKEDEV.md @@ -1,6 +1,6 @@ define(MACHINE,octeon)dnl vers(__file__, - {-$OpenBSD: MAKEDEV.md,v 1.13 2016/09/04 15:38:59 naddy Exp $-}, + {-$OpenBSD: MAKEDEV.md,v 1.14 2019/07/17 14:36:31 visa Exp $-}, etc.MACHINE)dnl dnl dnl Copyright (c) 2001-2006 Todd T. Fries <todd@OpenBSD.org> @@ -86,6 +86,10 @@ _DEV(switch, 75) dnl divert(__mddivert)dnl dnl +boot) + _recurse ramdisk random + M octboot c 21 0 600 + ;; _std(2, 3, 35, 6) M openprom c 20 0 600 ;; diff --git a/sys/arch/octeon/compile/BOOT/Makefile b/sys/arch/octeon/compile/BOOT/Makefile new file mode 100644 index 00000000000..01b5f23410c --- /dev/null +++ b/sys/arch/octeon/compile/BOOT/Makefile @@ -0,0 +1 @@ +.include "../Makefile.inc" diff --git a/sys/arch/octeon/conf/BOOT b/sys/arch/octeon/conf/BOOT new file mode 100644 index 00000000000..fc1516290e6 --- /dev/null +++ b/sys/arch/octeon/conf/BOOT @@ -0,0 +1,70 @@ +# $OpenBSD: BOOT,v 1.1 2019/07/17 14:36:32 visa Exp $ + +machine octeon mips64 +maxusers 4 + +option TIMEZONE=0 +option DST=0 +option SMALL_KERNEL +option NO_PROPOLICE + +option RAMDISK_HOOKS +option MINIROOTSIZE=10240 + +option PCIVERBOSE +option USBVERBOSE + +option FFS +option FFS2 + +option CPU_MIPS64R2 +option CPU_OCTEON +option FPUEMUL +makeoption LINK_ADDRESS="0xffffffff82000000" + +config bsd root on rd0a swap on rd0b + +mainbus0 at root +cpu0 at mainbus0 +clock0 at mainbus0 +iobus0 at mainbus0 +octcf0 at iobus0 +amdcf0 at iobus0 + +simplebus* at fdt? +simplebus* at iobus? + +com* at fdt_octeon? +octcib* at fdt? # Interrupt controller +octcit* at fdt? # Interrupt controller v3 +octciu* at fdt? # Interrupt controller v1 +octmmc* at fdt? # MMC host controller +sdmmc* at octmmc? # SD/MMC bus + +# AHCI controllers +octsctl* at fdt? +ahci* at octsctl? + +dwctwo0 at iobus0 irq 56 +octuctl* at fdt? +octxctl* at fdt? +ehci0 at octuctl? +ohci0 at octuctl? +xhci* at octxctl? + +usb* at dwctwo? +usb* at ehci? +usb* at ohci? +usb* at xhci? + +uhub* at usb? +uhub* at uhub? +umass* at uhub? + +scsibus* at scsi? +sd* at scsibus? + +pseudo-device etherip # pulls ether in kernel +pseudo-device octboot 1 +pseudo-device rd 1 +pseudo-device wsmux 2 diff --git a/sys/arch/octeon/conf/files.octeon b/sys/arch/octeon/conf/files.octeon index 90cf58cedb3..36a195257d7 100644 --- a/sys/arch/octeon/conf/files.octeon +++ b/sys/arch/octeon/conf/files.octeon @@ -1,4 +1,4 @@ -# $OpenBSD: files.octeon,v 1.50 2019/04/23 13:53:46 visa Exp $ +# $OpenBSD: files.octeon,v 1.51 2019/07/17 14:36:32 visa Exp $ # Standard stanzas config(8) can't run without maxpartitions 16 @@ -165,5 +165,8 @@ device octxctl: fdt attach octxctl at fdt file arch/octeon/dev/octxctl.c octxctl +pseudo-device octboot +file arch/octeon/dev/octboot.c octboot needs-flag + pseudo-device openprom file arch/octeon/octeon/openprom.c openprom needs-flag diff --git a/sys/arch/octeon/dev/octboot.c b/sys/arch/octeon/dev/octboot.c new file mode 100644 index 00000000000..659b165e632 --- /dev/null +++ b/sys/arch/octeon/dev/octboot.c @@ -0,0 +1,387 @@ +/* $OpenBSD: octboot.c,v 1.1 2019/07/17 14:36:32 visa Exp $ */ + +/* + * Copyright (c) 2019 Visa Hankala + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/exec_elf.h> +#include <sys/malloc.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/vnode.h> + +#include <uvm/uvm_extern.h> + +#include <mips64/memconf.h> + +#include <machine/autoconf.h> +#include <machine/octboot.h> +#include <machine/octeonvar.h> + +typedef void (*kentry)(register_t, register_t, register_t, register_t); +#define PRIMARY 1 + +int octboot_kexec(struct octboot_kexec_args *, struct proc *); +int octboot_read(struct proc *, struct vnode *, void *, size_t, off_t); + +uint64_t octeon_boot_entry; +uint32_t octeon_boot_ready; + +void +octbootattach(int num) +{ +} + +int +octbootopen(dev_t dev, int flags, int mode, struct proc *p) +{ + return (0); +} + +int +octbootclose(dev_t dev, int flags, int mode, struct proc *p) +{ + return (0); +} + +int +octbootioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) +{ + int error = 0; + + switch (cmd) { + case OBIOC_GETROOTDEV: + if (strlen(uboot_rootdev) == 0) { + error = ENOENT; + break; + } + strlcpy((char *)data, uboot_rootdev, PATH_MAX); + break; + + case OBIOC_KEXEC: + error = suser(p); + if (error != 0) + break; + error = octboot_kexec((struct octboot_kexec_args *)data, p); + break; + + default: + error = ENOTTY; + break; + } + + return error; +} + +int +octboot_kexec(struct octboot_kexec_args *kargs, struct proc *p) +{ + extern char start[], end[]; + Elf_Ehdr eh; + Elf_Phdr *ph = NULL; + Elf_Shdr *sh = NULL; + struct nameidata nid; + struct vattr va; + paddr_t ekern = 0, elfp, maxp = 0, off, pa, shp; + size_t len, phsize, shsize, shstrsize, size; + char *argbuf = NULL, *argptr; + char *shstr = NULL; + int argc = 0, error, havesyms = 0, i, nalloc = 0; + + NDINIT(&nid, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, kargs->path, p); + error = namei(&nid); + if (error != 0) + return error; + error = VOP_GETATTR(nid.ni_vp, &va, p->p_ucred, p); + if (error != 0) + goto fail; + if (nid.ni_vp->v_type != VREG || va.va_size == 0) { + error = EINVAL; + goto fail; + } + error = VOP_ACCESS(nid.ni_vp, VREAD, p->p_ucred, p); + if (error != 0) + goto fail; + + /* + * Load kernel arguments into a temporary buffer. + * This also translates the userspace argv pointers to kernel pointers. + */ + argbuf = malloc(PAGE_SIZE, M_TEMP, M_NOWAIT); + if (argbuf == NULL) { + error = ENOMEM; + goto fail; + } + argptr = argbuf; + for (i = 0; i < OCTBOOT_MAX_ARGS && kargs->argv[i] != NULL; i++) { + len = argbuf + PAGE_SIZE - argptr; + error = copyinstr(kargs->argv[i], argptr, len, &len); + if (error != 0) + goto fail; + kargs->argv[i] = argptr; + argptr += len; + argc++; + } + + /* + * Read the headers and validate them. + */ + error = octboot_read(p, nid.ni_vp, &eh, sizeof(eh), 0); + if (error != 0) + goto fail; + + /* Load program headers. */ + ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_NOWAIT); + if (ph == NULL) { + error = ENOMEM; + goto fail; + } + phsize = eh.e_phnum * sizeof(Elf_Phdr); + error = octboot_read(p, nid.ni_vp, ph, phsize, eh.e_phoff); + if (error != 0) + goto fail; + + /* Load section headers. */ + sh = mallocarray(eh.e_shnum, sizeof(Elf_Shdr), M_TEMP, M_NOWAIT); + if (sh == NULL) { + error = ENOMEM; + goto fail; + } + shsize = eh.e_shnum * sizeof(Elf_Shdr); + error = octboot_read(p, nid.ni_vp, sh, shsize, eh.e_shoff); + if (error != 0) + goto fail; + + /* Sanity-check addresses. */ + for (i = 0; i < eh.e_phnum; i++) { + if (ph[i].p_type != PT_LOAD && + ph[i].p_type != PT_OPENBSD_RANDOMIZE) + continue; + if (ph[i].p_paddr < CKSEG0_BASE || + ph[i].p_paddr + ph[i].p_memsz >= CKSEG0_BASE + CKSEG_SIZE) { + error = ENOEXEC; + goto fail; + } + } + + /* + * Allocate physical memory and load the segments. + */ + + for (i = 0; i < eh.e_phnum; i++) { + if (ph[i].p_type != PT_LOAD) + continue; + pa = CKSEG0_TO_PHYS(ph[i].p_paddr); + size = roundup(ph[i].p_memsz, BOOTMEM_BLOCK_ALIGN); + if (bootmem_alloc_region(pa, size) != 0) { + printf("kexec: failed to allocate segment " + "0x%lx @ 0x%lx\n", size, pa); + error = ENOMEM; + goto fail; + } + if (maxp < pa + size) + maxp = pa + size; + nalloc++; + } + + for (i = 0; i < eh.e_phnum; i++) { + if (ph[i].p_type == PT_OPENBSD_RANDOMIZE) { + /* Assume that the segment is inside a LOAD segment. */ + arc4random_buf((caddr_t)ph[i].p_paddr, ph[i].p_filesz); + continue; + } + + if (ph[i].p_type != PT_LOAD) + continue; + + error = octboot_read(p, nid.ni_vp, (caddr_t)ph[i].p_paddr, + ph[i].p_filesz, ph[i].p_offset); + if (error != 0) + goto fail; + + /* Clear any BSS. */ + if (ph[i].p_memsz > ph[i].p_filesz) { + memset((caddr_t)ph[i].p_paddr + ph[i].p_filesz, + 0, ph[i].p_memsz - ph[i].p_filesz); + } + } + ekern = maxp; + + for (i = 0; i < eh.e_shnum; i++) { + if (sh[i].sh_type == SHT_SYMTAB) { + havesyms = 1; + break; + } + } + + if (havesyms) { + /* Reserve space for ssym and esym pointers. */ + maxp += sizeof(int32_t) * 2; + + elfp = roundup(maxp, sizeof(Elf_Addr)); + maxp = elfp + sizeof(Elf_Ehdr); + shp = maxp; + maxp = shp + roundup(shsize, sizeof(Elf_Addr)); + maxp = roundup(maxp, BOOTMEM_BLOCK_ALIGN); + if (bootmem_alloc_region(ekern, maxp - ekern) != 0) { + printf("kexec: failed to allocate %zu bytes for ELF " + "and section headers\n", maxp - ekern); + error = ENOMEM; + goto fail; + } + + shstrsize = sh[eh.e_shstrndx].sh_size; + shstr = malloc(shstrsize, M_TEMP, M_NOWAIT); + if (shstr == NULL) { + error = ENOMEM; + goto fail; + } + error = octboot_read(p, nid.ni_vp, shstr, shstrsize, + sh[eh.e_shstrndx].sh_offset); + if (error != 0) + goto fail; + + off = roundup(sizeof(Elf_Ehdr) + shsize, sizeof(Elf_Addr)); + off = maxp - elfp; + for (i = 0; i < eh.e_shnum; i++) { + if (sh[i].sh_type == SHT_STRTAB || + sh[i].sh_type == SHT_SYMTAB || + strcmp(shstr + sh[i].sh_name, ELF_CTF) == 0 || + strcmp(shstr + sh[i].sh_name, ".debug_line") == 0) { + size_t bsize = roundup(sh[i].sh_size, + BOOTMEM_BLOCK_ALIGN); + + if (bootmem_alloc_region(maxp, bsize) != 0) { + error = ENOMEM; + goto fail; + } + error = octboot_read(p, nid.ni_vp, + (caddr_t)PHYS_TO_CKSEG0(maxp), + sh[i].sh_size, sh[i].sh_offset); + maxp += bsize; + if (error != 0) + goto fail; + sh[i].sh_offset = off; + sh[i].sh_flags |= SHF_ALLOC; + off += bsize; + } + } + + eh.e_phoff = 0; + eh.e_shoff = sizeof(eh); + eh.e_phentsize = 0; + eh.e_phnum = 0; + memcpy((caddr_t)PHYS_TO_CKSEG0(elfp), &eh, sizeof(eh)); + memcpy((caddr_t)PHYS_TO_CKSEG0(shp), sh, shsize); + + *(int32_t *)PHYS_TO_CKSEG0(ekern) = PHYS_TO_CKSEG0(elfp); + *((int32_t *)PHYS_TO_CKSEG0(ekern) + 1) = PHYS_TO_CKSEG0(maxp); + } + + /* + * Put kernel arguments in place. + */ + octeon_boot_desc->argc = 0; + for (i = 0; i < OCTEON_ARGV_MAX; i++) + octeon_boot_desc->argv[i] = 0; + if (argptr > argbuf) { + size = roundup(argptr - argbuf, BOOTMEM_BLOCK_ALIGN); + if (bootmem_alloc_region(maxp, size) != 0) { + error = ENOMEM; + goto fail; + } + memcpy((caddr_t)PHYS_TO_CKSEG0(maxp), argbuf, argptr - argbuf); + for (i = 0; i < argc; i++) { + KASSERT(kargs->argv[i] >= argbuf); + KASSERT(kargs->argv[i] < argbuf + PAGE_SIZE); + octeon_boot_desc->argv[i] = kargs->argv[i] - argbuf + + maxp; + } + octeon_boot_desc->argc = argc; + maxp += size; + } + + printf("launching kernel\n"); + + config_suspend_all(DVACT_POWERDOWN); + + intr_disable(); + + /* Put UVM memory back to the free list. */ + for (i = 0; mem_layout[i].mem_last_page != 0; i++) { + uint64_t fp = mem_layout[i].mem_first_page; + uint64_t lp = mem_layout[i].mem_last_page; + + bootmem_free(ptoa(fp), ptoa(lp) - ptoa(fp)); + } + + /* + * Release the memory of the bootloader kernel. + * This may overwrite a tiny region at the start of the running image. + */ + bootmem_free(CKSEG0_TO_PHYS((vaddr_t)start), end - start); + + /* Let secondary cores proceed to the new kernel. */ + octeon_boot_entry = eh.e_entry; + octeon_syncw(); /* Order writes. */ + octeon_boot_ready = 1; /* Open the gate. */ + octeon_syncw(); /* Flush writes. */ + delay(1000); /* Give secondary cores a lead. */ + + __asm__ volatile ( + " cache 1, 0($0)\n" /* Flush and invalidate dcache. */ + " cache 0, 0($0)\n" /* Invalidate icache. */ + ::: "memory"); + + (*(kentry)eh.e_entry)(0, 0, PRIMARY, (register_t)octeon_boot_desc); + + for (;;) + continue; + +fail: + if (ekern != 0) + bootmem_free(ekern, maxp - ekern); + for (i = 0; i < eh.e_phnum && nalloc > 0; i++) { + if (ph[i].p_type == PT_LOAD) { + pa = CKSEG0_TO_PHYS(ph[i].p_paddr); + bootmem_free(pa, ph[i].p_memsz); + nalloc--; + } + } + free(shstr, M_TEMP, shstrsize); + free(sh, M_TEMP, shsize); + free(ph, M_TEMP, phsize); + free(argbuf, M_TEMP, PAGE_SIZE); + vput(nid.ni_vp); + return error; +} + +int +octboot_read(struct proc *p, struct vnode *vp, void *buf, size_t size, + off_t off) +{ + size_t resid; + int error; + + error = vn_rdwr(UIO_READ, vp, buf, size, off, UIO_SYSSPACE, 0, + p->p_ucred, &resid, p); + if (error != 0) + return error; + if (resid != 0) + return ENOEXEC; + return 0; +} diff --git a/sys/arch/octeon/include/conf.h b/sys/arch/octeon/include/conf.h index e84bc9ea15a..a954a3fa714 100644 --- a/sys/arch/octeon/include/conf.h +++ b/sys/arch/octeon/include/conf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.h,v 1.2 2016/07/05 12:53:40 visa Exp $ */ +/* $OpenBSD: conf.h,v 1.3 2019/07/17 14:36:32 visa Exp $ */ /* $NetBSD: conf.h,v 1.2 1996/05/05 19:28:34 christos Exp $ */ /* @@ -36,12 +36,20 @@ #include <sys/conf.h> /* open, close, ioctl */ +#define cdev_octboot_init(c,n) { \ + dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \ + (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \ + (dev_type_stop((*))) nullop, 0, selfalse, \ + (dev_type_mmap((*))) enodev } + +/* open, close, ioctl */ #define cdev_openprom_init(c,n) { \ dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \ (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \ (dev_type_stop((*))) nullop, 0, selfalse, \ (dev_type_mmap((*))) enodev } +cdev_decl(octboot); cdev_decl(openprom); #endif /* _OCTEON_CONF_H_ */ diff --git a/sys/arch/octeon/include/octboot.h b/sys/arch/octeon/include/octboot.h new file mode 100644 index 00000000000..60c9f300169 --- /dev/null +++ b/sys/arch/octeon/include/octboot.h @@ -0,0 +1,35 @@ +/* $OpenBSD: octboot.h,v 1.1 2019/07/17 14:36:32 visa Exp $ */ + +/* + * Copyright (c) 2019 Visa Hankala + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OCTEON_OCTBOOT_H_ +#define _OCTEON_OCTBOOT_H_ + +#include <sys/ioccom.h> + +#define OCTBOOT_MAX_ARGS 8 /* maximum number of boot arguments */ + +struct octboot_kexec_args { + const char *path; /* kernel path */ + char *argv[OCTBOOT_MAX_ARGS]; + /* kernel boot arguments */ +}; + +#define OBIOC_KEXEC _IOW('O', 1, struct octboot_kexec_args) +#define OBIOC_GETROOTDEV _IOR('O', 2, char[PATH_MAX]) + +#endif /* !_OCTEON_OCTBOOT_H_ */ diff --git a/sys/arch/octeon/include/octeonvar.h b/sys/arch/octeon/include/octeonvar.h index 6e645788059..1e2c221dd02 100644 --- a/sys/arch/octeon/include/octeonvar.h +++ b/sys/arch/octeon/include/octeonvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: octeonvar.h,v 1.45 2018/04/09 13:46:15 visa Exp $ */ +/* $OpenBSD: octeonvar.h,v 1.46 2019/07/17 14:36:32 visa Exp $ */ /* $NetBSD: maltavar.h,v 1.3 2002/03/18 10:10:16 simonb Exp $ */ /*- @@ -272,6 +272,13 @@ extern struct boot_info *octeon_boot_info; #define BOOTINFO_CFG_FLAG_DEBUG (1ull << 2) #define BOOTINFO_CFG_FLAG_NO_MAGIC (1ull << 3) +#define BOOTMEM_BLOCK_ALIGN 16 +#define BOOTMEM_BLOCK_MASK (BOOTMEM_BLOCK_ALIGN - 1) +#define BOOTMEM_BLOCK_MIN_SIZE 16 + +int bootmem_alloc_region(paddr_t, size_t); +void bootmem_free(paddr_t, size_t); + int octeon_ioclock_speed(void); #endif /* _KERNEL */ diff --git a/sys/arch/octeon/octeon/conf.c b/sys/arch/octeon/octeon/conf.c index bdf9353e534..6b96db3daab 100644 --- a/sys/arch/octeon/octeon/conf.c +++ b/sys/arch/octeon/octeon/conf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.c,v 1.20 2016/09/04 10:51:24 naddy Exp $ */ +/* $OpenBSD: conf.c,v 1.21 2019/07/17 14:36:32 visa Exp $ */ /* * Copyright (c) 1992, 1993 @@ -140,6 +140,7 @@ cdev_decl(pci); #include "vscsi.h" #include "pppx.h" #include "fuse.h" +#include "octboot.h" #include "openprom.h" #include "switch.h" @@ -170,7 +171,7 @@ struct cdevsw cdevsw[] = cdev_disk_init(NWD,wd), /* 18: ST506/ESDI/IDE disk */ cdev_disk_init(NAMDCF,amdcf), /* 19: CF disk */ cdev_openprom_init(NOPENPROM,openprom), /* 20: /dev/openprom */ - cdev_notdef(), /* 21: */ + cdev_octboot_init(NOCTBOOT,octboot), /* 21: /dev/octboot */ cdev_disk_init(NRD,rd), /* 22: ramdisk device */ cdev_notdef(), /* 23: was: concatenated disk driver */ cdev_notdef(), /* 24: */ diff --git a/sys/arch/octeon/octeon/locore.S b/sys/arch/octeon/octeon/locore.S index 32667171a6b..96e6b7534bf 100644 --- a/sys/arch/octeon/octeon/locore.S +++ b/sys/arch/octeon/octeon/locore.S @@ -1,4 +1,4 @@ -/* $OpenBSD: locore.S,v 1.19 2019/05/06 17:13:22 visa Exp $ */ +/* $OpenBSD: locore.S,v 1.20 2019/07/17 14:36:32 visa Exp $ */ /* * Copyright (c) 2001-2004 Opsycon AB (www.opsycon.se / www.opsycon.com) @@ -37,6 +37,7 @@ #include <octeon/dev/cn30xxcorereg.h> #include "assym.h" +#include "octboot.h" #define RNG_CONTROL_ADDR 0x9001180040000000 #define RNG_CONTROL_ENABLE 0x3 @@ -88,6 +89,17 @@ locore_start: nop j hw_cpu_spinup_trampoline nop +#elif NOCTBOOT > 0 + LA t0, octeon_boot_ready +1: lw t1, (t0) + beq t1, zero, 1b + nop + + LA t0, octeon_boot_entry + ld t0, (t0) # get entry point + cache 1, 0(zero) # flush and invalidate dcache + jr t0 + cache 0, 0(zero) # invalidate icache #else /* Halt extra cores on single-processor kernel. */ 1: wait diff --git a/sys/arch/octeon/octeon/machdep.c b/sys/arch/octeon/octeon/machdep.c index 2adb012c0c5..ef27fb00932 100644 --- a/sys/arch/octeon/octeon/machdep.c +++ b/sys/arch/octeon/octeon/machdep.c @@ -1,7 +1,8 @@ -/* $OpenBSD: machdep.c,v 1.114 2019/07/12 03:09:23 visa Exp $ */ +/* $OpenBSD: machdep.c,v 1.115 2019/07/17 14:36:32 visa Exp $ */ /* * Copyright (c) 2009, 2010 Miodrag Vallat. + * Copyright (c) 2019 Visa Hankala. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -86,6 +87,8 @@ #include <machine/octeonvar.h> #include <machine/octeon_model.h> +#include "octboot.h" + /* The following is used externally (sysctl_hw) */ char machine[] = MACHINE; /* Machine "architecture" */ char cpu_model[64]; @@ -152,6 +155,12 @@ struct timecounter ioclock_timecounter = { * determined at runtime */ }; +static struct octeon_bootmem_block * +pa_to_block(paddr_t addr) +{ + return (struct octeon_bootmem_block *)PHYS_TO_XKPHYS(addr, CCA_CACHED); +} + void octeon_memory_init(struct boot_info *boot_info) { @@ -178,11 +187,23 @@ octeon_memory_init(struct boot_info *boot_info) if (blockaddr == 0) panic("bootmem list is empty"); for (i = 0; i < MAXMEMSEGS && blockaddr != 0; blockaddr = block->next) { - block = (struct octeon_bootmem_block *)PHYS_TO_XKPHYS( - blockaddr, CCA_CACHED); + block = pa_to_block(blockaddr); printf("avail phys mem 0x%016lx - 0x%016lx\n", blockaddr, (paddr_t)(blockaddr + block->size)); +#if NOCTBOOT > 0 + /* + * Reserve the physical memory below the boot kernel + * for loading the actual kernel. + */ + extern char start[]; + if (blockaddr < CKSEG_SIZE && + PHYS_TO_CKSEG0(blockaddr) < (vaddr_t)start) { + printf("skipped\n"); + continue; + } +#endif + fp = atop(round_page(blockaddr)); lp = atop(trunc_page(blockaddr + block->size)); @@ -209,6 +230,13 @@ octeon_memory_init(struct boot_info *boot_info) for (i = 0; mem_layout[i].mem_last_page; i++) { printf("mem_layout[%d] page 0x%016llX -> 0x%016llX\n", i, mem_layout[i].mem_first_page, mem_layout[i].mem_last_page); + +#if NOCTBOOT > 0 + fp = mem_layout[i].mem_first_page; + lp = mem_layout[i].mem_last_page; + if (bootmem_alloc_region(ptoa(fp), ptoa(lp) - ptoa(fp)) != 0) + panic("%s: bootmem allocation failed", __func__); +#endif } } @@ -898,6 +926,215 @@ ioclock_get_timecount(struct timecounter *tc) return octeon_xkphys_read_8(reg); } +#if NOCTBOOT > 0 +static uint64_t +size_trunc(uint64_t size) +{ + return (size & ~BOOTMEM_BLOCK_MASK); +} + +void +bootmem_dump(void) +{ + struct octeon_bootmem_desc *memdesc = (struct octeon_bootmem_desc *) + PHYS_TO_XKPHYS(octeon_boot_info->phys_mem_desc_addr, CCA_CACHED); + struct octeon_bootmem_block *block; + paddr_t pa; + + pa = memdesc->head_addr; + while (pa != 0) { + block = pa_to_block(pa); + printf("free 0x%lx - 0x%lx\n", pa, pa + (size_t)block->size); + pa = block->next; + } +} + +/* + * Allocate the given region from the free memory list. + */ +int +bootmem_alloc_region(paddr_t pa, size_t size) +{ + struct octeon_bootmem_desc *memdesc = (struct octeon_bootmem_desc *) + PHYS_TO_XKPHYS(octeon_boot_info->phys_mem_desc_addr, CCA_CACHED); + struct octeon_bootmem_block *block, *next, nblock; + paddr_t bpa; + + if (pa == 0 || size < BOOTMEM_BLOCK_MIN_SIZE || + (pa & BOOTMEM_BLOCK_MASK) != 0 || + (size & BOOTMEM_BLOCK_MASK) != 0) + return EINVAL; + + if (memdesc->head_addr == 0 || pa < memdesc->head_addr) + return ENOMEM; + + /* Check if the region is at the head of the free list. */ + if (pa == memdesc->head_addr) { + block = pa_to_block(memdesc->head_addr); + if (block->size < size) + return ENOMEM; + if (size_trunc(block->size) == size) { + memdesc->head_addr = block->next; + } else { + KASSERT(block->size > size); + nblock.next = block->next; + nblock.size = block->size - size; + KASSERT(nblock.size >= BOOTMEM_BLOCK_MIN_SIZE); + memdesc->head_addr += size; + *pa_to_block(memdesc->head_addr) = nblock; + } + return 0; + } + + /* Find the block that immediately precedes or is at `pa'. */ + bpa = memdesc->head_addr; + block = pa_to_block(bpa); + while (block->next != 0 && block->next < pa) { + bpa = block->next; + block = pa_to_block(bpa); + } + + /* Refuse to play if the block is not properly aligned. */ + if ((bpa & BOOTMEM_BLOCK_MASK) != 0) + return ENOMEM; + + if (block->next == pa) { + next = pa_to_block(block->next); + if (next->size < size) + return ENOMEM; + if (size_trunc(next->size) == size) { + block->next = next->next; + } else { + KASSERT(next->size > size); + nblock.next = next->next; + nblock.size = next->size - size; + KASSERT(nblock.size >= BOOTMEM_BLOCK_MIN_SIZE); + block->next += size; + *pa_to_block(block->next) = nblock; + } + } else { + KASSERT(bpa < pa); + KASSERT(block->next == 0 || block->next > pa); + + if (bpa + block->size < pa + size) + return ENOMEM; + if (bpa + size_trunc(block->size) == pa + size) { + block->size = pa - bpa; + } else { + KASSERT(bpa + block->size > pa + size); + nblock.next = block->next; + nblock.size = block->size - (pa - bpa) - size; + KASSERT(nblock.size >= BOOTMEM_BLOCK_MIN_SIZE); + block->next = pa + size; + block->size = pa - bpa; + *pa_to_block(block->next) = nblock; + } + } + + return 0; +} + +/* + * Release the given region to the free memory list. + */ +void +bootmem_free(paddr_t pa, size_t size) +{ + struct octeon_bootmem_desc *memdesc = (struct octeon_bootmem_desc *) + PHYS_TO_XKPHYS(octeon_boot_info->phys_mem_desc_addr, CCA_CACHED); + struct octeon_bootmem_block *block, *next, *prev; + paddr_t prevpa; + + if (pa == 0 || size < BOOTMEM_BLOCK_MIN_SIZE || + (pa & BOOTMEM_BLOCK_MASK) != 0 || + (size & BOOTMEM_BLOCK_MASK) != 0) + panic("%s: invalid block 0x%lx @ 0x%lx", __func__, size, pa); + + /* If the list is empty, insert at the head. */ + if (memdesc->head_addr == 0) { + block = pa_to_block(pa); + block->next = 0; + block->size = size; + memdesc->head_addr = pa; + return; + } + + /* If the block precedes the current head, insert before, or merge. */ + if (pa <= memdesc->head_addr) { + block = pa_to_block(pa); + if (pa + size < memdesc->head_addr) { + block->next = memdesc->head_addr; + block->size = size; + memdesc->head_addr = pa; + } else if (pa + size == memdesc->head_addr) { + next = pa_to_block(memdesc->head_addr); + block->next = next->next; + block->size = next->size + size; + memdesc->head_addr = pa; + } else { + panic("%s: overlap 1: 0x%lx @ 0x%lx / 0x%llx @ 0x%llx", + __func__, size, pa, + pa_to_block(memdesc->head_addr)->size, + memdesc->head_addr); + } + return; + } + + /* Find the immediate predecessor. */ + prevpa = memdesc->head_addr; + prev = pa_to_block(prevpa); + while (prev->next != 0 && prev->next < pa) { + prevpa = prev->next; + prev = pa_to_block(prevpa); + } + if (prevpa + prev->size > pa) { + panic("%s: overlap 2: 0x%llx @ 0x%lx / 0x%lx @ 0x%lx", + __func__, prev->size, prevpa, size, pa); + } + + /* Merge with or insert after the predecessor. */ + if (prevpa + prev->size == pa) { + if (prev->next == 0) { + prev->size += size; + return; + } + next = pa_to_block(prev->next); + if (prevpa + prev->size + size < prev->next) { + prev->size += size; + } else if (prevpa + prev->size + size == prev->next) { + prev->next = next->next; + prev->size += size + next->size; + } else { + panic("%s: overlap 3: 0x%llx @ 0x%lx / 0x%lx @ 0x%lx / " + "0x%llx @ 0x%llx", __func__, + prev->size, prevpa, size, pa, + next->size, prev->next); + } + } else { + /* The block is disjoint with prev. */ + KASSERT(prevpa + prev->size < pa); + + block = pa_to_block(pa); + if (pa + size < prev->next || prev->next == 0) { + block->next = prev->next; + block->size = size; + prev->next = pa; + } else if (pa + size == prev->next) { + next = pa_to_block(prev->next); + block->next = next->next; + block->size = next->size + size; + prev->next = pa; + } else { + next = pa_to_block(prev->next); + panic("%s: overlap 4: 0x%llx @ 0x%lx / " + "0x%lx @ 0x%lx / 0x%llx @ 0x%llx", + __func__, prev->size, prevpa, size, pa, + next->size, prev->next); + } + } +} +#endif /* NOCTBOOT > 0 */ + #ifdef MULTIPROCESSOR uint32_t cpu_spinup_mask = 0; uint64_t cpu_spinup_a0, cpu_spinup_sp; diff --git a/sys/arch/octeon/stand/rdboot/Makefile b/sys/arch/octeon/stand/rdboot/Makefile new file mode 100644 index 00000000000..38f01e78ef6 --- /dev/null +++ b/sys/arch/octeon/stand/rdboot/Makefile @@ -0,0 +1,16 @@ +# $OpenBSD: Makefile,v 1.1 2019/07/17 14:36:32 visa Exp $ + +NOMAN= + +.if ${MACHINE} == "octeon" +PROG= rdboot +SRCS= cmd.c disk.c rdboot.c vars.c +LDADD+= -lutil +LDSTATIC+= -static +.else +NOPROG= +.endif + +install: + +.include <bsd.prog.mk> diff --git a/sys/arch/octeon/stand/rdboot/cmd.c b/sys/arch/octeon/stand/rdboot/cmd.c new file mode 100644 index 00000000000..85c8a28e238 --- /dev/null +++ b/sys/arch/octeon/stand/rdboot/cmd.c @@ -0,0 +1,503 @@ +/* $OpenBSD: cmd.c,v 1.1 2019/07/17 14:36:32 visa Exp $ */ + +/* + * Copyright (c) 1997-1999 Michael Shalayeff + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/reboot.h> +#include <sys/select.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include "cmd.h" +#include "disk.h" + +static int Xboot(void); +static int Xecho(void); +static int Xhelp(void); +static int Xls(void); +static int Xnop(void); +static int Xreboot(void); +#ifdef MACHINE_CMD +static int Xmachine(void); +extern const struct cmd_table MACHINE_CMD[]; +#endif +extern int Xset(void); + +#ifdef CHECK_SKIP_CONF +extern int CHECK_SKIP_CONF(void); +#endif + +extern const struct cmd_table cmd_set[]; +const struct cmd_table cmd_table[] = { + {"#", CMDT_CMD, Xnop}, /* XXX must be first */ + {"boot", CMDT_CMD, Xboot}, + {"echo", CMDT_CMD, Xecho}, + {"help", CMDT_CMD, Xhelp}, + {"ls", CMDT_CMD, Xls}, +#ifdef MACHINE_CMD + {"machine",CMDT_MDC, Xmachine}, +#endif + {"reboot", CMDT_CMD, Xreboot}, + {"set", CMDT_SET, Xset}, + {NULL, 0}, +}; + +static void ls(const char *, struct stat *); +static int readline(char *, size_t, int); +char *nextword(char *); +static char *whatcmd(const struct cmd_table **ct, char *); +static char *qualify(char *); + +char cmd_buf[CMD_BUFF_SIZE]; + +int +getcmd(void) +{ + cmd.cmd = NULL; + + if (!readline(cmd_buf, sizeof(cmd_buf), cmd.timeout)) + cmd.cmd = cmd_table; + + return docmd(); +} + +int +read_conf(void) +{ + struct stat sb; + const char *path; + int fd, rc = 0; + +#ifdef CHECK_SKIP_CONF + if (CHECK_SKIP_CONF()) { + printf("boot.conf processing skipped at operator request\n"); + cmd.timeout = 0; + return -1; /* Pretend file wasn't found */ + } +#endif + + path = disk_open(qualify(cmd.conf)); + if (path == NULL) { + fprintf(stderr, "cannot open device for reading %s: %s\n", + cmd.conf, strerror(errno)); + return -1; + } + if ((fd = open(path, O_RDONLY)) == -1) { + if (errno != ENOENT && errno != ENXIO) { + fprintf(stderr, "%s: open(%s): %s\n", __func__, + cmd.path, strerror(errno)); + rc = 0; + } else + rc = -1; + goto out; + } + + (void) fstat(fd, &sb); + if (sb.st_uid || (sb.st_mode & 2)) { + fprintf(stderr, "non-secure %s, will not proceed\n", cmd.path); + rc = -1; + goto out; + } + + do { + char *p = cmd_buf; + + cmd.cmd = NULL; + do { + rc = read(fd, p, 1); + } while (rc > 0 && *p++ != '\n' && + (p-cmd_buf) < sizeof(cmd_buf)); + + if (rc < 0) { /* Error from read() */ + fprintf(stderr, "%s: %s\n", cmd.path, strerror(errno)); + break; + } + + if (rc == 0) { /* eof from read() */ + if (p != cmd_buf) { /* Line w/o trailing \n */ + *p = '\0'; + rc = docmd(); + break; + } + } else { /* rc > 0, read a char */ + p--; /* Get back to last character */ + + if (*p != '\n') { /* Line was too long */ + fprintf(stderr, "%s: line too long\n", + cmd.path); + + /* Don't want to run the truncated command */ + rc = -1; + } + *p = '\0'; + } + } while (rc > 0 && !(rc = docmd())); + +out: + if (fd != -1) + close(fd); + disk_close(); + return rc; +} + +int +docmd(void) +{ + char *p = NULL; + const struct cmd_table *ct = cmd_table, *cs; + + cmd.argc = 1; + if (cmd.cmd == NULL) { + + /* command */ + for (p = cmd_buf; *p == ' ' || *p == '\t'; p++) + ; + if (*p == '#' || *p == '\0') { /* comment or empty string */ +#ifdef DEBUG + printf("rem\n"); +#endif + return 0; + } + ct = cmd_table; + cs = NULL; + cmd.argv[cmd.argc] = p; /* in case it's shortcut boot */ + p = whatcmd(&ct, p); + if (ct == NULL) { + cmd.argc++; + ct = cmd_table; + } else if (ct->cmd_type == CMDT_SET && p != NULL) { + cs = cmd_set; +#ifdef MACHINE_CMD + } else if (ct->cmd_type == CMDT_MDC && p != NULL) { + cs = MACHINE_CMD; +#endif + } + + if (cs != NULL) { + p = whatcmd(&cs, p); + if (cs == NULL) { + printf("%s: syntax error\n", ct->cmd_name); + return 0; + } + ct = cs; + } + cmd.cmd = ct; + } + + cmd.argv[0] = ct->cmd_name; + while (p && cmd.argc+1 < sizeof(cmd.argv) / sizeof(cmd.argv[0])) { + cmd.argv[cmd.argc++] = p; + p = nextword(p); + } + cmd.argv[cmd.argc] = NULL; + + return (*cmd.cmd->cmd_exec)(); +} + +static char * +whatcmd(const struct cmd_table **ct, char *p) +{ + char *q; + int l; + + q = nextword(p); + + for (l = 0; p[l]; l++) + ; + + while ((*ct)->cmd_name != NULL && strncmp(p, (*ct)->cmd_name, l)) + (*ct)++; + + if ((*ct)->cmd_name == NULL) + *ct = NULL; + + return q; +} + +static int +readline(char *buf, size_t n, int to) +{ + struct termios saved_tio, tio; + struct timeval tv; + fd_set fdset; + int timed_out = 0; +#ifdef DEBUG + extern int debug; +#endif + + /* Only do timeout if greater than 0 */ + if (to > 0) { + /* Switch to non-canonical mode for timeout detection. */ + tcgetattr(STDIN_FILENO, &saved_tio); + tio = saved_tio; + tio.c_lflag &= ~(ECHO | ICANON); + tcsetattr(STDIN_FILENO, TCSANOW, &tio); + + FD_ZERO(&fdset); + FD_SET(STDIN_FILENO, &fdset); + tv.tv_sec = to; + tv.tv_usec = 0; + if (select(STDIN_FILENO + 1, &fdset, NULL, NULL, &tv) == 0) + timed_out = 1; + + /* Restore canonical mode. */ + tcsetattr(STDIN_FILENO, TCSANOW, &saved_tio); + + if (timed_out) { + strlcpy(buf, "boot", 5); + putchar('\n'); + return strlen(buf); + } + } + + /* User has typed something. Turn off timeouts. */ + cmd.timeout = 0; + + if (fgets(buf, n, stdin) == NULL) + return 0; + + /* Strip trailing newline. */ + strtok(buf, "\n"); + + return strlen(buf); +} + +/* + * Search for spaces/tabs after the current word. If found, \0 the + * first one. Then pass a pointer to the first character of the + * next word, or NULL if there is no next word. + */ +char * +nextword(char *p) +{ + /* skip blanks */ + while (*p && *p != '\t' && *p != ' ') + p++; + if (*p) { + *p++ = '\0'; + while (*p == '\t' || *p == ' ') + p++; + } + if (*p == '\0') + p = NULL; + return p; +} + +static void +print_help(const struct cmd_table *ct) +{ + for (; ct->cmd_name != NULL; ct++) + printf(" %s", ct->cmd_name); + putchar('\n'); +} + +static int +Xhelp(void) +{ + printf("commands:"); + print_help(cmd_table); +#ifdef MACHINE_CMD + return Xmachine(); +#else + return 0; +#endif +} + +#ifdef MACHINE_CMD +static int +Xmachine(void) +{ + printf("machine:"); + print_help(MACHINE_CMD); + return 0; +} +#endif + +static int +Xecho(void) +{ + int i; + + for (i = 1; i < cmd.argc; i++) + printf("%s ", cmd.argv[i]); + putchar('\n'); + return 0; +} + +static int +Xls(void) +{ + struct stat sb; + const char *path; + DIR *dir; + struct dirent *dent; + int dirfd, oldcwd; + + path = disk_open(qualify(cmd.argv[1] ? cmd.argv[1] : "/.")); + if (path == NULL) + return 0; + + if (stat(path, &sb) < 0) { + printf("stat(%s): %s\n", cmd.path, strerror(errno)); + goto out; + } + + if ((sb.st_mode & S_IFMT) != S_IFDIR) + ls(path, &sb); + else { + oldcwd = open(".", O_RDONLY); + + dirfd = open(path, O_RDONLY); + if (dirfd < 0) { + printf("opendir(%s): %s\n", cmd.path, strerror(errno)); + close(oldcwd); + goto out; + } + if ((dir = fdopendir(dirfd)) < 0) { + printf("opendir(%s): %s\n", cmd.path, strerror(errno)); + close(dirfd); + close(oldcwd); + goto out; + } + fchdir(dirfd); + while ((dent = readdir(dir)) != NULL) { + if (fstatat(dirfd, dent->d_name, &sb, + AT_SYMLINK_NOFOLLOW) < 0) + printf("stat(%s): %s\n", dent->d_name, + strerror(errno)); + else + ls(dent->d_name, &sb); + } + closedir(dir); + + fchdir(oldcwd); + } + +out: + disk_close(); + return 0; +} + +#define lsrwx(mode,s) \ + putchar ((mode) & S_IROTH? 'r' : '-'); \ + putchar ((mode) & S_IWOTH? 'w' : '-'); \ + putchar ((mode) & S_IXOTH? *(s): (s)[1]); + +static void +ls(const char *name, struct stat *sb) +{ + putchar("-fc-d-b---l-s-w-"[(sb->st_mode & S_IFMT) >> 12]); + lsrwx(sb->st_mode >> 6, (sb->st_mode & S_ISUID? "sS" : "x-")); + lsrwx(sb->st_mode >> 3, (sb->st_mode & S_ISGID? "sS" : "x-")); + lsrwx(sb->st_mode , (sb->st_mode & S_ISTXT? "tT" : "x-")); + + printf (" %u,%u\t%lu\t%s\n", sb->st_uid, sb->st_gid, + (u_long)sb->st_size, name); +} +#undef lsrwx + +int doboot = 1; + +static int +Xnop(void) +{ + if (doboot) { + doboot = 0; + return (Xboot()); + } + + return 0; +} + +static int +Xboot(void) +{ + if (cmd.argc > 1 && cmd.argv[1][0] != '-') { + qualify((cmd.argv[1]? cmd.argv[1]: cmd.image)); + if (bootparse(2)) + return 0; + } else { + if (bootparse(1)) + return 0; + snprintf(cmd.path, sizeof cmd.path, "%s:%s", + cmd.bootdev, cmd.image); + } + + return 1; +} + +/* + * Qualifies the path adding necessary dev + */ + +static char * +qualify(char *name) +{ + char *p; + + for (p = name; *p; p++) + if (*p == ':') + break; + if (*p == ':') + strlcpy(cmd.path, name, sizeof(cmd.path)); + else + snprintf(cmd.path, sizeof cmd.path, "%s:%s", + cmd.bootdev, name); + return cmd.path; +} + +static int +Xreboot(void) +{ + printf("Rebooting...\n"); + reboot(0); + return 0; /* just in case */ +} + +int +upgrade(void) +{ + struct stat sb; + const char *path; + int ret = 0; + + path = disk_open(qualify("/bsd.upgrade")); + if (path == NULL) + return 0; + if (stat(path, &sb) == 0 && S_ISREG(sb.st_mode)) + ret = 1; + disk_close(); + + return ret; +} diff --git a/sys/arch/octeon/stand/rdboot/cmd.h b/sys/arch/octeon/stand/rdboot/cmd.h new file mode 100644 index 00000000000..1ffafde59ef --- /dev/null +++ b/sys/arch/octeon/stand/rdboot/cmd.h @@ -0,0 +1,65 @@ +/* $OpenBSD: cmd.h,v 1.1 2019/07/17 14:36:32 visa Exp $ */ + +/* + * Copyright (c) 1997 Michael Shalayeff + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define CMD_BUFF_SIZE 133 +#define BOOTDEVLEN 1024 + +struct cmd_table { + char *cmd_name; + char cmd_type; +#define CMDT_CMD 0 +#define CMDT_VAR 1 +#define CMDT_SET 2 +#define CMDT_MDC 3 + int (*cmd_exec)(void); +}; + +struct cmd_state { + char bootdev[BOOTDEVLEN]; /* device */ + char image[MAXPATHLEN - 16]; /* image */ + unsigned char bootduid[8]; /* duid of root disk */ + int boothowto; /* howto */ + int hasduid; + char *conf; /* /etc/boot.conf normally */ + int timeout; + + char path[MAXPATHLEN]; /* buffer for pathname compose */ + const struct cmd_table *cmd; + int argc; + char *argv[8]; /* XXX i hope this is enough */ +}; +extern struct cmd_state cmd; + +int getcmd(void); +int read_conf(void); +int bootparse(int); +void boot(dev_t); + +int upgrade(void); +int docmd(void); /* No longer static: needed by regress test */ diff --git a/sys/arch/octeon/stand/rdboot/disk.c b/sys/arch/octeon/stand/rdboot/disk.c new file mode 100644 index 00000000000..cc943444c73 --- /dev/null +++ b/sys/arch/octeon/stand/rdboot/disk.c @@ -0,0 +1,207 @@ +/* $OpenBSD: disk.c,v 1.1 2019/07/17 14:36:32 visa Exp $ */ + +/* + * Copyright (c) 2019 Visa Hankala + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/disklabel.h> +#include <sys/dkio.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/sysctl.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <util.h> + +#include "cmd.h" + +int disk_proberoot(const char *); + +int mounted = 0; +int rdroot = -1; /* fd that points to the root of the ramdisk */ + +void +disk_init(void) +{ + char rootdevs[1024]; + char *devname, *disknames, *ptr; + size_t size; + int mib[2]; + + rdroot = open("/", O_RDONLY); + if (rdroot == -1) + err(1, "failed to open root directory fd"); + + if (strlen(cmd.bootdev) != 0) + return; + + mib[0] = CTL_HW; + mib[1] = HW_DISKNAMES; + size = 0; + if (sysctl(mib, 2, NULL, &size, NULL, 0) == -1) { + fprintf(stderr, "%s: cannot get hw.disknames: %s\n", __func__, + strerror(errno)); + return; + } + disknames = malloc(size); + if (disknames == NULL) { + fprintf(stderr, "%s: out of memory\n", __func__); + return; + } + if (sysctl(mib, 2, disknames, &size, NULL, 0) == -1) { + fprintf(stderr, "%s: cannot get hw.disknames: %s\n", __func__, + strerror(errno)); + free(disknames); + return; + } + + printf("probing disks\n"); + rootdevs[0] = '\0'; + ptr = disknames; + while ((devname = strsep(&ptr, ",")) != NULL) { + char *duid; + + duid = strchr(devname, ':'); + if (duid == NULL) + continue; + *duid++ = '\0'; + + /* Disk without a duid cannot be a root device. */ + if (strlen(duid) == 0) + continue; + + if (disk_proberoot(devname)) { + if (strlen(cmd.bootdev) == 0) { + snprintf(cmd.bootdev, sizeof(cmd.bootdev), + "%sa", devname); + } + (void)strlcat(rootdevs, " ", sizeof(rootdevs)); + (void)strlcat(rootdevs, devname, sizeof(rootdevs)); + } + } + if (strlen(rootdevs) != 0) + printf("available root devices:%s\n", rootdevs); + else + printf("no root devices found\n"); +} + +int +disk_proberoot(const char *devname) +{ + static const char *const names[] = { + "bin", "dev", "etc", "home", "mnt", "root", "sbin", "tmp", + "usr", "var", NULL + }; + struct ufs_args ffs_args; + struct stat st; + char path[32]; + int i, is_root = 1; + + snprintf(path, sizeof(path), "/dev/%sa", devname); + memset(&ffs_args, 0, sizeof(ffs_args)); + ffs_args.fspec = path; + if (mount(MOUNT_FFS, "/mnt", MNT_RDONLY, &ffs_args) == -1) + return 0; + for (i = 0; names[i] != NULL; i++) { + snprintf(path, sizeof(path), "/mnt/%s", names[i]); + if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) { + is_root = 0; + break; + } + } + (void)unmount("/mnt", 0); + + return is_root; +} + +const char * +disk_open(const char *path) +{ + struct ufs_args ffs_args; + struct disklabel label; + char devname[32]; + char *devpath; + const char *ptr; + int fd; + + if (mounted) { + fprintf(stderr, "%s: cannot nest\n", __func__); + return NULL; + } + + ptr = strchr(path, ':'); + if (ptr != NULL) { + snprintf(devname, sizeof(devname), "%.*s", + (int)(ptr - path), path); + ptr++; /* skip ':' */ + } else { + strlcpy(devname, cmd.bootdev, sizeof(devname)); + ptr = path; + } + if (strlen(devname) == 0) { + fprintf(stderr, "no device specified\n"); + return NULL; + } + + cmd.hasduid = 0; + fd = opendev(devname, O_RDONLY, OPENDEV_BLCK, &devpath); + if (fd != -1) { + if (ioctl(fd, DIOCGDINFO, &label) != -1) { + memcpy(cmd.bootduid, label.d_uid, 8); + cmd.hasduid = 1; + } + close(fd); + } else { + fprintf(stderr, "failed to open device %s: %s\n", devname, + strerror(errno)); + return NULL; + } + + memset(&ffs_args, 0, sizeof(ffs_args)); + ffs_args.fspec = devpath; + if (mount(MOUNT_FFS, "/mnt", MNT_RDONLY, &ffs_args) == -1) { + fprintf(stderr, "failed to mount %s: %s\n", devpath, + strerror(errno)); + return NULL; + } + if (chroot("/mnt") == -1) { + fprintf(stderr, "failed to chroot: %s\n", strerror(errno)); + (void)unmount("/mnt", 0); + return NULL; + } + mounted = 1; + + return ptr; +} + +void +disk_close(void) +{ + if (mounted) { + (void)fchdir(rdroot); + (void)chroot("."); + mounted = 0; + (void)unmount("/mnt", 0); + } +} diff --git a/sys/arch/octeon/stand/rdboot/disk.h b/sys/arch/octeon/stand/rdboot/disk.h new file mode 100644 index 00000000000..ac0d1879bb0 --- /dev/null +++ b/sys/arch/octeon/stand/rdboot/disk.h @@ -0,0 +1,21 @@ +/* $OpenBSD: disk.h,v 1.1 2019/07/17 14:36:32 visa Exp $ */ + +/* + * Copyright (c) 2019 Visa Hankala + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +void disk_init(void); +const char *disk_open(const char *); +void disk_close(void); diff --git a/sys/arch/octeon/stand/rdboot/rdboot.c b/sys/arch/octeon/stand/rdboot/rdboot.c new file mode 100644 index 00000000000..0b66162142e --- /dev/null +++ b/sys/arch/octeon/stand/rdboot/rdboot.c @@ -0,0 +1,192 @@ +/* $OpenBSD: rdboot.c,v 1.1 2019/07/17 14:36:32 visa Exp $ */ + +/* + * Copyright (c) 2019 Visa Hankala + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/reboot.h> +#include <sys/select.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <stdio.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <util.h> + +#include <machine/octboot.h> +#include <machine/param.h> + +#include "cmd.h" +#include "disk.h" + +#define DEVRANDOM "/dev/random" +#define BOOTRANDOM "/etc/random.seed" +#define BOOTRANDOM_MAX 512 +#define KERNEL "/bsd" + +void loadrandom(void); +void kexec(void); + +struct cmd_state cmd; +int octbootfd = -1; +const char version[] = "1.0"; + +int +main(void) +{ + char rootdev[PATH_MAX]; + int fd, hasboot; + + fd = open(_PATH_CONSOLE, O_RDWR); + login_tty(fd); + + /* Keep stdout unbuffered to mimic ordinary bootblocks. */ + setvbuf(stdout, NULL, _IONBF, 0); + + printf(">> OpenBSD/" MACHINE " BOOT %s\n", version); + + octbootfd = open("/dev/octboot", O_WRONLY); + if (octbootfd == -1) + err(1, "cannot open boot control device"); + + memset(&cmd, 0, sizeof(cmd)); + cmd.boothowto = 0; + cmd.conf = "/etc/boot.conf"; + strlcpy(cmd.image, KERNEL, sizeof(cmd.image)); + cmd.timeout = 5; + + if (ioctl(octbootfd, OBIOC_GETROOTDEV, rootdev) == -1) { + if (errno != ENOENT) + fprintf(stderr, "cannot get rootdev from kernel: %s\n", + strerror(errno)); + } else { + snprintf(cmd.bootdev, sizeof(cmd.bootdev), "%s%sa", + rootdev, isduid(rootdev, OPENDEV_PART) ? "." : ""); + } + + disk_init(); + + if (upgrade()) { + strlcpy(cmd.image, "/bsd.upgrade", sizeof(cmd.image)); + printf("upgrade detected: switching to %s\n", cmd.image); + } + + hasboot = read_conf(); + + for (;;) { + if (hasboot <= 0) { + do { + printf("boot> "); + } while (!getcmd()); + } + + loadrandom(); + kexec(); + + hasboot = 0; + strlcpy(cmd.image, KERNEL, sizeof(cmd.image)); + printf("will try %s\n", cmd.image); + } + + return 0; +} + +void +loadrandom(void) +{ + char buf[BOOTRANDOM_MAX]; + int fd; + + /* Read the file from the device specified by the kernel path. */ + if (disk_open(cmd.path) == NULL) + return; + fd = open(BOOTRANDOM, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "%s: cannot open %s: %s", __func__, BOOTRANDOM, + strerror(errno)); + disk_close(); + return; + } + read(fd, buf, sizeof(buf)); + close(fd); + disk_close(); + + /* + * Push the whole buffer to the entropy pool. + * The kernel will use the entropy on kexec(). + * It does not matter if some of the buffer content is uninitialized. + */ + fd = open(DEVRANDOM, O_WRONLY); + if (fd == -1) { + fprintf(stderr, "%s: cannot open %s: %s", __func__, + DEVRANDOM, strerror(errno)); + return; + } + write(fd, buf, sizeof(buf)); + close(fd); +} + +void +kexec(void) +{ + struct octboot_kexec_args kargs; + char kernelflags[8]; + char rootdev[32]; + const char *path; + int argc, ret; + + path = disk_open(cmd.path); + if (path == NULL) + return; + + memset(&kargs, 0, sizeof(kargs)); + kargs.path = path; + argc = 0; + if (cmd.boothowto != 0) { + snprintf(kernelflags, sizeof(kernelflags), "-%s%s%s%s", + (cmd.boothowto & RB_ASKNAME) ? "a" : "", + (cmd.boothowto & RB_CONFIG) ? "c" : "", + (cmd.boothowto & RB_KDB) ? "d" : "", + (cmd.boothowto & RB_SINGLE) ? "s" : ""); + kargs.argv[argc++] = kernelflags; + } + if (cmd.hasduid) { + snprintf(rootdev, sizeof(rootdev), + "rootdev=%02x%02x%02x%02x%02x%02x%02x%02x", + cmd.bootduid[0], cmd.bootduid[1], + cmd.bootduid[2], cmd.bootduid[3], + cmd.bootduid[4], cmd.bootduid[5], + cmd.bootduid[6], cmd.bootduid[7]); + kargs.argv[argc++] = rootdev; + } + + printf("booting %s\n", cmd.path); + ret = ioctl(octbootfd, OBIOC_KEXEC, &kargs); + if (ret == -1) + fprintf(stderr, "failed to execute kernel %s: %s\n", + cmd.path, strerror(errno)); + else + fprintf(stderr, "kexec() returned unexpectedly\n"); + + disk_close(); +} diff --git a/sys/arch/octeon/stand/rdboot/vars.c b/sys/arch/octeon/stand/rdboot/vars.c new file mode 100644 index 00000000000..926c8a6cb16 --- /dev/null +++ b/sys/arch/octeon/stand/rdboot/vars.c @@ -0,0 +1,203 @@ +/* $OpenBSD: vars.c,v 1.1 2019/07/17 14:36:32 visa Exp $ */ + +/* + * Copyright (c) 1998-2000 Michael Shalayeff + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/param.h> +#include <sys/reboot.h> + +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "cmd.h" + +extern int debug; +int db_console = -1; + +static int Xdevice(void); +#ifdef DEBUG +static int Xdebug(void); +#endif +static int Xdb_console(void); +static int Ximage(void); +static int Xhowto(void); +static int Xtimeout(void); +int Xset(void); + +const struct cmd_table cmd_set[] = { + {"howto", CMDT_VAR, Xhowto}, +#ifdef DEBUG + {"debug", CMDT_VAR, Xdebug}, +#endif + {"device", CMDT_VAR, Xdevice}, + {"image", CMDT_VAR, Ximage}, + {"timeout",CMDT_VAR, Xtimeout}, + {"db_console", CMDT_VAR, Xdb_console}, + {NULL,0} +}; + +#ifdef DEBUG +static int +Xdebug(void) +{ + if (cmd.argc != 2) + printf( "o%s\n", debug? "n": "ff" ); + else + debug = (cmd.argv[1][0] == '0' || + (cmd.argv[1][0] == 'o' && cmd.argv[1][1] == 'f'))? + 0: 1; + return 0; +} +#endif + +int +Xdb_console(void) +{ + if (cmd.argc != 2) { + switch (db_console) { + case 0: + printf("off\n"); + break; + case 1: + printf("on\n"); + break; + default: + printf("unset\n"); + break; + } + } else { + if (strcmp(cmd.argv[1], "0") == 0 || + strcmp(cmd.argv[1], "off") == 0) + db_console = 0; + else if (strcmp(cmd.argv[1], "1") == 0 || + strcmp(cmd.argv[1], "on") == 0) + db_console = 1; + } + + return (0); +} + +static int +Xtimeout(void) +{ + if (cmd.argc != 2) + printf( "%d\n", cmd.timeout ); + else + cmd.timeout = (int)strtol( cmd.argv[1], (char **)NULL, 0 ); + return 0; +} + +/* called only w/ no arguments */ +int +Xset(void) +{ + const struct cmd_table *ct; + + printf("boot\n"); + for (ct = cmd_set; ct->cmd_name != NULL; ct++) { + printf("%s\t ", ct->cmd_name); + (*ct->cmd_exec)(); + } + return 0; +} + +static int +Xdevice(void) +{ + if (cmd.argc != 2) + printf("%s\n", cmd.bootdev); + else + strlcpy(cmd.bootdev, cmd.argv[1], sizeof(cmd.bootdev)); + return 0; +} + +static int +Ximage(void) +{ + if (cmd.argc != 2) + printf("%s\n", cmd.image); + else + strlcpy(cmd.image, cmd.argv[1], sizeof(cmd.image)); + return 0; +} + +static int +Xhowto(void) +{ + if (cmd.argc == 1) { + if (cmd.boothowto) { + putchar('-'); + if (cmd.boothowto & RB_ASKNAME) + putchar('a'); + if (cmd.boothowto & RB_CONFIG) + putchar('c'); + if (cmd.boothowto & RB_SINGLE) + putchar('s'); + if (cmd.boothowto & RB_KDB) + putchar('d'); + } + putchar('\n'); + } else + bootparse(1); + return 0; +} + +int +bootparse(int i) +{ + char *cp; + int howto = cmd.boothowto; + + for (; i < cmd.argc; i++) { + cp = cmd.argv[i]; + if (*cp == '-') { + while (*++cp) { + switch (*cp) { + case 'a': + howto |= RB_ASKNAME; + break; + case 'c': + howto |= RB_CONFIG; + break; + case 's': + howto |= RB_SINGLE; + break; + case 'd': + howto |= RB_KDB; + break; + default: + printf("howto: bad option: %c\n", *cp); + return 1; + } + } + } + } + cmd.boothowto = howto; + return 0; +} |