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. 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";