summaryrefslogtreecommitdiff
path: root/sys/arch/sparc/stand/boot/boot.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/sparc/stand/boot/boot.c')
-rw-r--r--sys/arch/sparc/stand/boot/boot.c270
1 files changed, 254 insertions, 16 deletions
diff --git a/sys/arch/sparc/stand/boot/boot.c b/sys/arch/sparc/stand/boot/boot.c
index 078ae4a9e35..306b8513e01 100644
--- a/sys/arch/sparc/stand/boot/boot.c
+++ b/sys/arch/sparc/stand/boot/boot.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: boot.c,v 1.6 2003/08/14 17:13:57 deraadt Exp $ */
+/* $OpenBSD: boot.c,v 1.7 2010/06/29 21:33:54 miod Exp $ */
/* $NetBSD: boot.c,v 1.2 1997/09/14 19:27:21 pk Exp $ */
/*-
@@ -34,16 +34,16 @@
#include <sys/param.h>
#include <sys/reboot.h>
-#include <a.out.h>
+#include <lib/libsa/loadfile.h>
#include <lib/libsa/stand.h>
#include <sparc/stand/common/promdev.h>
-void copyunix(int, char *);
-void promsyms(int, struct exec *);
int debug;
int netif_debug;
+#define FOURMB 0x400000
+
/*
* Boot device is derived from ROM provided information.
*/
@@ -53,19 +53,254 @@ extern char *version;
extern vaddr_t esym;
char fbuf[80], dbuf[128];
-typedef void (*entry_t)(caddr_t, int, int, int, long, long);
-int loadfile(int, vaddr_t *);
+paddr_t bstart, bend; /* physical start & end address of the boot program */
+u_long loadaddrmask = -1UL;
+int compat = 1; /* try to load in compat mode */
+
+typedef void (*entry_t)(u_long, int, int, int, long, long);
+
+int fdloadfile(int, u_long *, int);
+
+static paddr_t
+getphysmem(u_long size)
+{
+ struct memarr *pmemarr; /* physical memory regions */
+ u_int npmemarr; /* number of entries in pmemarr */
+ struct memarr *mp;
+ int i;
+#ifdef DEBUG
+ static int arrdpy;
+#endif
+
+ /*
+ * Get available physical memory from the prom.
+ */
+ npmemarr = prom_makememarr(NULL, 0, MEMARR_AVAILPHYS);
+ pmemarr = alloc(npmemarr*sizeof(struct memarr));
+ if (pmemarr == NULL)
+ return ((paddr_t)-1);
+ npmemarr = prom_makememarr(pmemarr, npmemarr, MEMARR_AVAILPHYS);
+
+#ifdef DEBUG
+ if (arrdpy == 0) {
+ arrdpy = 1;
+ printf("Available physical memory:\n");
+ for (mp = pmemarr, i = (int)npmemarr; --i >= 0; mp++) {
+ uint64_t addr;
+ addr = pmemarr[i].addr_hi;
+ addr <<= 32;
+ addr |= pmemarr[i].addr_lo;
+ printf("%p at 0x%llx\n", pmemarr[i].len, addr);
+ }
+ }
+#endif
+
+ /*
+ * Find a suitable loading address.
+ */
+ for (mp = pmemarr, i = (int)npmemarr; --i >= 0; mp++) {
+ paddr_t pa;
+ u_long len;
+
+ /* Skip memory ranges the kernel can't use yet on sun4d */
+ if (pmemarr[i].addr_hi != 0)
+ continue;
+ pa = (paddr_t)pmemarr[i].addr_lo;
+ if (pa >= 0x80000000)
+ continue;
+ len = (u_long)pmemarr[i].len;
+ if (len >= 0x80000000)
+ len = 0x80000000;
+ if (pa + len > 0x80000000)
+ len = 0x80000000 - pa;
+
+ if (len < size)
+ continue;
+
+ /* Check whether it will fit in front of us */
+ if (pa < bstart && len >= size && (bstart - pa) >= size)
+ return (pa);
+
+ /* Skip the boot program memory */
+ if (pa < bend) {
+ if (len < bend - pa)
+ /* Not large enough */
+ continue;
+
+ /* Shrink this segment */
+ len -= bend - pa;
+ pa = bend;
+ }
+
+ /* Does it fit in the remainder of this segment? */
+ if (len >= size)
+ return (pa);
+ }
+ return ((paddr_t)-1);
+}
+
+static int
+loadk(char *file, u_long *marks)
+{
+ int fd, error, flags;
+ vaddr_t va;
+ paddr_t pa;
+ u_long minsize, size;
+
+ if ((fd = open(file, 0)) < 0)
+ return (errno ? errno : ENOENT);
+
+ /*
+ * We need to know whether we are booting off a tape or not,
+ * because we can not seek backwards off tapes.
+ */
+
+ if (files[fd].f_flags & F_RAW) {
+ flags = (COUNT_KERNEL & ~COUNT_SYM) | (LOAD_KERNEL & ~LOAD_SYM);
+ minsize = RELOC + 0x40000; /* RELOC2 */
+ va = 0xf8000000; /* KERNBASE */
+#ifdef DEBUG
+ printf("Tape boot: expecting a bsd.rd kernel smaller than %p\n",
+ minsize);
+#endif
+ /* compensate for extra room below */
+ minsize -= 512 * 1024;
+ } else {
+ flags = LOAD_KERNEL;
+ marks[MARK_START] = 0;
+
+ /*
+ * Even though we just have opened the file, the gzip code
+ * has tried to read from it. Be sure to reset position in
+ * case the file is not compressed (transparent mode isn't
+ * so transparent...)
+ */
+ if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
+ error = errno;
+ goto out;
+ }
+
+ if ((error = fdloadfile(fd, marks, COUNT_KERNEL)) != 0)
+ goto out;
+ /* rewind file for the actual load operation later */
+ if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
+ error = errno;
+ goto out;
+ }
+
+ minsize = marks[MARK_END] - marks[MARK_START];
+
+ /* We want that leading 16K in front of the kernel image */
+ minsize += PROM_LOADADDR;
+ va = marks[MARK_START] - PROM_LOADADDR;
+ }
+
+ /*
+ * If the kernel would entirely fit under the boot code, and the
+ * boot code has been loaded 1:1, we do not need to allocate
+ * breathing room after it.
+ */
+ if (compat != 0) {
+ if (minsize < bstart)
+ size = minsize;
+ else
+ compat = 0;
+ }
+
+ /*
+ * If we are not loading the kernel in low physical addresses,
+ * we need to make sure it has enough valid space to use during
+ * pmap_bootstrap; 512KB ought to be enough.
+ */
+ if (compat == 0) {
+ size = minsize + 512 * 1024;
+#ifdef DEBUG
+ printf("kernel footprint %p, requesting %p\n", minsize, size);
+#endif
+ }
+
+ /* Get a physical load address */
+ pa = getphysmem(size);
+ if (pa == (paddr_t)-1) {
+ /*
+ * That 512KB extra might have been too much, if physical
+ * memory doesn't have any contiguous large chunks (e.g.
+ * on sun4c systems with 4MB regions).
+ * If that 512KB increase caused us to cross a 4MB
+ * boundary, try to limit ourselves to a 4MB multiple.
+ */
+ if (compat == 0 && size / FOURMB != minsize / FOURMB) {
+ size = roundup(minsize, FOURMB);
+#ifdef DEBUG
+ printf("now trying %p\n", size);
+#endif
+ pa = getphysmem(size);
+ }
+ if (pa == (paddr_t)-1) {
+ error = EFBIG;
+ goto out;
+ }
+ }
+
+ printf("Loading at physical address %lx\n", pa);
+ if (pmap_map(va, pa, size) != 0) {
+ error = EFAULT;
+ goto out;
+ }
+
+ /* try and double-map at VA 0 for compatibility */
+ if (pa + size >= bstart) {
+ printf("WARNING: %s is too large for compat mode.\n"
+ "If your kernel is too old, it will not run correctly.\n",
+ file);
+ } else {
+ if (pa != 0 && pmap_map(0, pa, size) != 0) {
+ error = EFAULT;
+ goto out;
+ }
+ loadaddrmask = 0x07ffffffUL;
+ }
+
+ marks[MARK_START] = 0;
+ error = fdloadfile(fd, marks, flags);
+out:
+ close(fd);
+ return (error);
+}
+
+int
main(int argc, char *argv[])
{
- int io;
+ int error;
char *file;
- entry_t entry;
+ u_long marks[MARK_MAX];
+ extern char start[]; /* top of stack (see srt0.S) */
+ vaddr_t bstart_va;
prom_init();
+ mmu_init();
printf(">> OpenBSD BOOT %s\n", version);
+ /*
+ * Find the physical memory area that's in use by the boot loader.
+ * Our stack grows down from label `start'; assume we need no more
+ * than 16K of stack space.
+ * The top of the boot loader is the next 4MB boundary.
+ */
+ bstart_va = (vaddr_t)start - 16 * 1024;
+ if (pmap_extract(bstart_va, &bstart) != 0)
+ panic("can't figure out where we have been loaded");
+
+ if (bstart != bstart_va)
+ compat = 0;
+
+ bend = roundup(bstart, FOURMB);
+#ifdef DEBUG
+ printf("bstart %p bend %p\n", bstart, bend);
+#endif
+
file = prom_bootfile;
if (file == 0 || *file == 0)
file = DEFAULT_KERNEL;
@@ -81,18 +316,21 @@ main(int argc, char *argv[])
if (fbuf[0])
file = fbuf;
}
- if ((io = open(file, 0)) >= 0)
+
+ printf("Booting %s\n", file);
+ if ((error = loadk(file, marks)) == 0)
break;
- printf("open: %s: %s\n", file, strerror(errno));
- prom_boothow |= RB_ASKNAME;
- }
- printf("Booting %s @ 0x%x\n", file, LOADADDR);
- loadfile(io, (vaddr_t *)&entry);
+ if (error != ENOENT) {
+ printf("Cannot load %s: error=%d\n", file, error);
+ prom_boothow |= RB_ASKNAME;
+ }
+ }
/* Note: args 2-4 not used due to conflicts with SunOS loaders */
- (*entry)(cputyp == CPU_SUN4 ? LOADADDR : (caddr_t)promvec,
- 0, 0, 0, esym, DDB_MAGIC1);
+ (*(entry_t)marks[MARK_ENTRY])(cputyp == CPU_SUN4 ?
+ PROM_LOADADDR : (u_long)promvec, 0, 0, 0,
+ marks[MARK_END] & loadaddrmask, DDB_MAGIC1);
_rtt();
}