diff options
Diffstat (limited to 'sys/arch/sparc/stand/boot/boot.c')
-rw-r--r-- | sys/arch/sparc/stand/boot/boot.c | 270 |
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(); } |