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

Reply via email to