On Tue, Oct 04, 2016 at 08:08:32PM +0000, Miod Vallat wrote:
> The sgi boot blocks use the PROM (ARCBios or ARCS) for its I/O routines.
> When using a disk-based path, these routines are using the partition
> table found in the ``volume header''.
>
> In order to be able to use 16 partitions per disk, the OpenBSD port only
> claims one volume header partition, #0, as the OpenBSD area, and puts
> its own label in there.
>
> Unfortunately, this means that `sd0a', the OpenBSD partition on which
> the kernel is found, may not actually start at the same location as the
> volume header partition #0, even though this is the case in most setups.
>
> When reinstalling an O2 some time ago, I did not pay attention to this
> during install:
>
> Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout? [a] c
> [...]
> Label editor (enter '?' for help at any prompt)
> > p
> OpenBSD area: 0-17773524; size: 17773524; free: 0
> # size offset fstype [fsize bsize cpg]
> a: 17770389 3135 4.2BSD 1024 8192 16
> c: 17773524 0 unused
> p: 3135 0 unknown
> > d a
> > a
> partition: [a]
> offset: [3135]
> size: [17770389] 400M
> Rounding size to cylinder (3360 sectors): 816705
> FS type: [4.2BSD]
> mount point: [none] /
> Rounding offset to bsize (32 sectors): 3136
> Rounding size to bsize (32 sectors): 816704
>
> And `sd0a' ended up starting one sector after the beginning of the
> volume header partition #0.
>
> Of course, rebooting afterwards did not work as expected:
>
> OpenBSD/sgi-IP32 ARCBios boot version 1.7
> arg 0: pci(0)scsi(0)disk(1)rdisk(0)partition(8)/boot
> arg 1: OSLoadOptions=auto
> arg 2: ConsoleIn=serial(0)
> arg 3: ConsoleOut=serial(0)
> arg 4: SystemPartition=pci(0)scsi(0)disk(1)rdisk(0)partition(8)
> arg 5: OSLoader=boot
> arg 6: OSLoadPartition=pci(0)scsi(0)disk(1)rdisk(0)partition(0)
> arg 7: OSLoadFilename=bsd
> Boot: pci(0)scsi(0)disk(1)rdisk(0)partition(0)bsd
> cannot open pci(0)scsi(0)disk(1)rdisk(0)partition(0)/etc/random.seed:
> Invalid argument
> open pci(0)scsi(0)disk(1)rdisk(0)partition(0)bsd: Invalid argument
> Boot FAILED!
>
> The proper way to fix this is to make the boot blocks read the real
> OpenBSD label instead of assuming sd0a spans volume header partition #0
> in all cases.
>
> This is achieved by the diff below, which will attempt to use the raw
> disk device (volume header partition #10, equivalent to sd0c) for disk
> accesses and will read both the volume header (to know where the OpenBSD
> disklabel lies) and then the OpenBSD label (to know where sd0a really
> is).
>
> Of course, this has to cope with the two valid disk syntaxes (true
> ARCBios used on older sgi systems, and ARCS dksc() syntax used on Origin
> and later systems).
>
> This diff has been tested on O2 (IP32) and Origin 350 (IP27). It is
> written in a conservative way, in order to revert to the existing
> behaviour if anything fails (invalid volume header, no OpenBSD label
> yet...) and should not cause any regression.
>
> Note that network boots are not affected by these changes.
I tested this also on Octane (IP30), and it works fine. The diff is now
committed. Thanks!
> Index: Makefile32.inc
> ===================================================================
> RCS file: /OpenBSD/src/sys/arch/sgi/stand/Makefile32.inc,v
> retrieving revision 1.5
> diff -u -p -r1.5 Makefile32.inc
> --- Makefile32.inc 19 Oct 2012 13:51:59 -0000 1.5
> +++ Makefile32.inc 30 Sep 2016 09:19:15 -0000
> @@ -18,6 +18,7 @@ AS+= -32
> LD?= ld
> LD+= -m elf32btsmip
> LIBSA_CPPFLAGS=
> +CFLAGS+= -DLIBSA_LONGLONG_PRINTF
> .endif
>
> ### Figure out what to use for libsa and libz
> Index: boot/Makefile
> ===================================================================
> RCS file: /OpenBSD/src/sys/arch/sgi/stand/boot/Makefile,v
> retrieving revision 1.16
> diff -u -p -r1.16 Makefile
> --- boot/Makefile 30 Jul 2016 03:25:49 -0000 1.16
> +++ boot/Makefile 30 Sep 2016 09:19:15 -0000
> @@ -13,14 +13,14 @@ AFLAGS+= ${SAABI}
>
> S= ${.CURDIR}/../../../..
> SRCS= start.S arcbios.c boot.c conf.c diskio.c filesystem.c \
> - netfs.c netio.c strchr.c strstr.c
> + netfs.c netio.c strstr.c
>
> .PATH: ${S}/lib/libsa
> SRCS+= loadfile.c
>
> .PATH: ${S}/lib/libkern/arch/mips64 ${S}/lib/libkern
> -SRCS+= strlcpy.c memcpy.c strlen.c strrchr.c strlcat.c
> strncmp.c \
> - strcmp.S
> +SRCS+= memcpy.c strchr.c strcmp.S strlcat.c strlcpy.c strlen.c
> \
> + strncmp.c strrchr.c
>
> CLEANFILES+= machine mips64
>
> Index: boot/diskio.c
> ===================================================================
> RCS file: /OpenBSD/src/sys/arch/sgi/stand/boot/diskio.c,v
> retrieving revision 1.10
> diff -u -p -r1.10 diskio.c
> --- boot/diskio.c 30 Sep 2015 22:45:57 -0000 1.10
> +++ boot/diskio.c 30 Sep 2016 09:19:15 -0000
> @@ -1,6 +1,21 @@
> /* $OpenBSD: diskio.c,v 1.10 2015/09/30 22:45:57 krw Exp $ */
>
> /*
> + * Copyright (c) 2016 Miodrag Vallat.
> + *
> + * Permission to use, copy, modify, and 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.
> + */
> +/*
> * Copyright (c) 2000 Opsycon AB (www.opsycon.se)
> * Copyright (c) 2000 Rtmx, Inc (www.rtmx.com)
> *
> @@ -33,11 +48,15 @@
> *
> */
>
> -#include <stand.h>
> #include <sys/param.h>
> +#include <lib/libkern/libkern.h>
> +#include <stand.h>
> +
> #include <sys/disklabel.h>
> #include <mips64/arcbios.h>
>
> +char *strstr(char *, const char *); /* strstr.c */
> +
> struct dio_softc {
> int sc_fd; /* PROM file ID */
> int sc_part; /* Disk partition number. */
> @@ -50,18 +69,20 @@ diostrategy(void *devdata, int rw, daddr
> {
> struct dio_softc *sc = (struct dio_softc *)devdata;
> struct partition *pp = &sc->sc_label.d_partitions[sc->sc_part];
> + uint64_t blkoffset;
> arc_quad_t offset;
> long result;
>
> - offset.hi = 0;
> - offset.lo = (pp->p_offset + bn) * DEV_BSIZE;
> -
> - if ((Bios_Seek(sc->sc_fd, &offset, 0) < 0) ||
> - (Bios_Read(sc->sc_fd, addr, reqcnt, &result) < 0))
> - return (EIO);
> + blkoffset = (DL_GETPOFFSET(pp) + bn) * DEV_BSIZE;
> + offset.hi = blkoffset >> 32;
> + offset.lo = blkoffset;
> +
> + if (Bios_Seek(sc->sc_fd, &offset, 0) < 0 ||
> + Bios_Read(sc->sc_fd, addr, reqcnt, &result) < 0)
> + return EIO;
>
> *cnt = result;
> - return (0);
> + return 0;
> }
>
> int
> @@ -69,11 +90,18 @@ dioopen(struct open_file *f, ...)
> {
> char *ctlr;
> int partition;
> -
> struct dio_softc *sc;
> struct disklabel *lp;
> + struct sgilabel *sl;
> long fd;
> + /* XXX getdisklabel() assumes DEV_BSIZE bytes available */
> + char buf[DEV_BSIZE + LABELOFFSET];
> + arc_quad_t offset;
> + daddr_t native_offset;
> + long result;
> va_list ap;
> + char rawctlr[1 + MAXPATHLEN];
> + char *partptr;
>
> va_start(ap, f);
> ctlr = va_arg(ap, char *);
> @@ -81,26 +109,130 @@ dioopen(struct open_file *f, ...)
> va_end(ap);
>
> if (partition >= MAXPARTITIONS)
> - return (ENXIO);
> + return ENXIO;
>
> - if (Bios_Open(ctlr, 0, &fd) < 0)
> - return (ENXIO);
> + /*
> + * If booting from disk, `ctlr` is something like
> + * whatever()partition(0)
> + * or
> + * dksc(whatever,0)
> + * where 0 is the volume header #0 partition, which is the
> + * OpenBSD area, where the OpenBSD disklabel can be found.
> + *
> + * However, the OpenBSD `a' partition, where the kernel is to be
> + * found, may not start at the same offset.
> + *
> + * In order to be able to correctly load any file from the OpenBSD
> + * partitions, we need to access the volume header partition table
> + * and the OpenBSD label.
> + *
> + * Therefore, make sure we replace `partition(*)' with `partition(10)'
> + * before reaching ARCBios, in order to access the raw disk.
> + *
> + * We could use partition #8 and use the value of SystemPartition in
> + * the environment to avoid doing this, but this would prevent us
> + * from being able to boot from a different disk than the one
> + * pointed to by SystemPartition.
> + */
> +
> + strlcpy(rawctlr, ctlr, sizeof rawctlr);
> + partptr = strstr(rawctlr, "partition(");
> + if (partptr != NULL) {
> + strlcpy(partptr, "partition(10)",
> + sizeof rawctlr - (partptr - rawctlr));
> + } else {
> + if ((partptr = strstr(rawctlr, "dksc(")) != NULL) {
> + partptr = strstr(partptr, ",0)");
> + if (partptr != NULL && partptr[3] == '\0')
> + strlcpy(partptr, ",10)",
> + sizeof rawctlr - (partptr - rawctlr));
> + }
> + }
> +
> + sl = NULL; /* no volume header found yet */
> + if (partptr != NULL) {
> + if (Bios_Open(rawctlr, 0, &fd) < 0)
> + return ENXIO;
> +
> + /*
> + * Read the volume header.
> + */
> + offset.hi = offset.lo = 0;
> + if (Bios_Seek(fd, &offset, 0) < 0 ||
> + Bios_Read(fd, buf, DEV_BSIZE, &result) < 0 ||
> + result != DEV_BSIZE)
> + return EIO;
> +
> + sl = (struct sgilabel *)buf;
> + if (sl->magic != SGILABEL_MAGIC) {
> +#ifdef DEBUG
> + printf("Invalid volume header magic %x\n", sl->magic);
> +#endif
> + Bios_Close(fd);
> + sl = NULL;
> + }
> + }
> +
> + if (sl == NULL) {
> + if (Bios_Open(ctlr, 0, &fd) < 0)
> + return ENXIO;
> + }
>
> sc = alloc(sizeof(struct dio_softc));
> bzero(sc, sizeof(struct dio_softc));
> f->f_devdata = (void *)sc;
> + lp = &sc->sc_label;
>
> sc->sc_fd = fd;
> sc->sc_part = partition;
>
> - lp = &sc->sc_label;
> - lp->d_secsize = DEV_BSIZE;
> - lp->d_secpercyl = 1;
> - lp->d_npartitions = MAXPARTITIONS;
> - lp->d_partitions[partition].p_offset = 0;
> - lp->d_partitions[0].p_size = 0x7fff0000;
> + if (sl != NULL) {
> + native_offset = sl->partitions[0].first;
> + } else {
> + /*
> + * We could not read the volume header, or there isn't any.
> + * Stick to the device we were given, and assume the
> + * OpenBSD disklabel can be found at the beginning.
> + */
> + native_offset = 0;
> + }
> +
> + /*
> + * Read the native OpenBSD label.
> + */
> +#ifdef DEBUG
> + printf("OpenBSD label @%lld\n", native_offset + LABELSECTOR);
> +#endif
> + offset.hi = ((native_offset + LABELSECTOR) * DEV_BSIZE) >> 32;
> + offset.lo = (native_offset + LABELSECTOR) * DEV_BSIZE;
> +
> + if (Bios_Seek(fd, &offset, 0) < 0 ||
> + Bios_Read(fd, buf, DEV_BSIZE, &result) < 0 ||
> + result != DEV_BSIZE)
> + return EIO;
> +
> + if (getdisklabel(buf + LABELOFFSET, lp) == NULL) {
> +#ifdef DEBUG
> + printf("Found native disklabel, "
> + "partition %c starts at %lld\n",
> + 'a' + partition,
> + DL_GETPOFFSET(&lp->d_partitions[partition]));
> +#endif
> + } else {
> + /*
> + * Assume the OpenBSD partition spans the whole device.
> + */
> +#ifdef DEBUG
> + printf("No native disklabel found\n");
> +#endif
> + lp->d_secsize = DEV_BSIZE;
> + lp->d_secpercyl = 1;
> + lp->d_npartitions = MAXPARTITIONS;
> + DL_SETPOFFSET(&lp->d_partitions[partition], native_offset);
> + DL_SETPSIZE(&lp->d_partitions[partition], -1ULL);
> + }
>
> - return (0);
> + return 0;
> }
>
> int
> Index: boot/version
> ===================================================================
> RCS file: /OpenBSD/src/sys/arch/sgi/stand/boot/version,v
> retrieving revision 1.8
> diff -u -p -r1.8 version
> --- boot/version 13 Sep 2016 18:27:49 -0000 1.8
> +++ boot/version 30 Sep 2016 09:19:15 -0000
> @@ -34,6 +34,10 @@ No version strings up to 2012
> 1.7
> Loadfile support for .SUNW_ctf section
>
> +1.8
> + Use the OpenBSD disklabel instead of assuming OpenBSD partition `a`
> + starts at the beginning of the volume header partition #0.
> +
> #endif
>
> -static const char version[] = "1.7";
> +static const char version[] = "1.8";
>