Author: stepan Date: 2007-03-09 01:59:05 +0100 (Fri, 09 Mar 2007) New Revision: 114
Modified: openbios-devel/arch/sparc32/entry.S openbios-devel/arch/sparc32/romvec.c openbios-devel/drivers/esp.c openbios-devel/drivers/iommu.c openbios-devel/drivers/obio.c openbios-devel/include/sparc32/io.h Log: Patch for SunOS compatibility from pjcreath+openbios@gmail.com:
I've been trying to get old versions of SunOS to load under qemu. In doing so, I've encountered a number of bugs in OBP. I'm not always certain of the best fix, but I can at least provide a quick hack that will get people farther along.
1) Error message: "kmem_alloc failed, nbytes 680"
Bug: obp_dumb_memalloc is a bit too dumb. It needs to pick an address if passed a null address. (According to the comment in the allocator in OpenSolaris prom_alloc.c (see http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/psm/promif/ieee1275/sun4/prom_alloc.c), "If virthint is zero, a suitable virt is chosen.")
Quick fix: If passed a null address, start doling out addresses at 10MB and increment by size.
Shortcomings: The quick fix ignores the issue of free() and doesn't remove memory from the virtual-memory/available node.
After the quick fix, the boot gets farther, leading us to:
2) Error message: "Unhandled Exception 0x00000080"
Bug: Trap 0 (entry 0x80 in the table, i.e. syscall_trap_4x) is undefined. This is because the SunOS bootloader installs the trap by writing code in the trap table, but the trap table is in the .text section of OpenBIOS. Thus the trap 0 handler simply jumps to "bug".
Quick fix: Move the trap table to the .data section. Insert a "b entry; nop; nop; nop;" before "bug:".
Shortcomings: Requires the extra "b entry" code. Allows the only VM copy of the trap table to be permanently changed. OpenBIOS should copy the read-only trap table to read-write memory (and update %tbr) upon reset/entry.
3) #2 above actually exposes another bug. The write to the read-only trap table does not cause an access violation -- instead, it silently fails. The "std" instruction at 0x403e6c in the bootloader has no effect.
Bug: Uncertain. It could be a systemic bug in qemu, but it appears that the VM's MMU believes that the page is writable. That means that the VM's MMU is not having the access protection flags set for pages mapped to ROM. It thinks everything is rwx.
Fix?: The VM's MMU should have the access protection flags properly set for each ROM section. This should probably be done within OpenBIOS. E.g., .text should be r-x, .data should probably be rwx, etc.
This is the one fix I'm really not sure how to implement. Any suggestions? This may be a problem that only affects this bootloader, so fixing #2 above may be all that's strictly necessary. But I'm not positive that this bug doesn't have other ill effects I haven't found yet.
At any rate, fixing #2 gets us still further, to:
4) Error messages: "obp_devopen(sd(0,0,0):d) = 0xffd8e270 obp_inst2pkg(fd 0xffd8e270) = 0xffd57f44 obp_getprop(0xffd57f44, device_type) (not found)"
Bug: The OpenBIOS "interpose" implementation is not transparent to non-interposition-aware code (in violation of the interposition spec). The inst2pkg call in this sequence returns the phandle for /packages/misc-files, instead of the proper phandle.
Quick fix: Comment out the "interpose disk-label" lines in ob_sd_open.
Shortcomings: It disables disk-label. The correct fix is to fix the underlying problem with interposition, but I'm not sure exactly what it is. Could someone help?
Fixing #4 gets us quite a bit further, until:
5) Error message: "Unhandled Exception 0x00000009 PC = 0xf0138b20 NPC = 0xf0138b24 Stopping execution"
Bug: The instruction is trying to read from 0xfd020000+4, which is an invalid address. This address isn't mapped by OBP by default on Sun hardware, so the bootloader must be trying to (a) map this address and failing silently or (b) skipping the mapping for some reason. The instruction is hard-coded to look at this absolute address.
Fix: Unknown. This may be another instance of writes silently failing, hence my interest in #3 above. It could also be a side-effect of the quick fix for #4.
6) Error message: "BAD TRAP: cpu=0 type=9 rp=fd008f0c addr=feff8008 mmu_fsr=3a6 rw=2 MMU sfsr=3a6: Invalid Address on supv data store at level 3 regs at fd008f0c: psr=4400fc7 pc=f00053f4 npc=f00053f8 ..."
Bug: Real sun4m hardware registers 4 CPU-specific interrupts followed by a system-wide interrupt, regardless of the number of CPUs installed. The same is true of counters. SunOS looks at the 5th interrupt for the system-wide interrupt. OBP, since there's only one CPU, just sets up one CPU-specific interrupt followed by the system-wide interrupt, so there is no 5th interrupt. See the comment on "NCPU" at http://stuff.mit.edu/afs/athena/astaff/project/opssrc/sys.sunos/sun4m/devaddr.h.
Fix: in obp_interrupt_init() and obp_counter_init() register 4 CPU-specific interrupts before allocating the system-wide interrupt. The kernel will then map the 5th interrupt to the system-wide interrupt.
7) Error message: "BAD TRAP: cpu=0 type=9 rp=fd008d8c addr=7ff000 mmu_fsr=126 rw=1 MMU sfsr=126: Invalid Address on supv data fetch at level 1 regs at fd008d8c: psr=4000cc4 pc=f01339a4 npc=f01339a8 ..."
Bug: The command-line arguments passed to the kernel are fixed at address 0x7FF000 (CMDLINE_ADDR, passed from qemu via nv_info.cmdline), which is no longer mapped by the time the kernel looks at the boot arguments. A regular Sun boot ROM will copy this into mapped memory.
Fix: Copy the string in nv_info.cmdline to a OpenBIOS global (since OpenBIOS continues to be mapped) in ob_nvram_init().
8) Error message: "BAD TRAP: cpu=0 type=9 rp=fd008dec addr=1019000 mmu_fsr=126 rw=1 MMU sfsr=126: Invalid Address on supv data fetch at level 1 regs at fd008dec: psr=4400cc5 pc=f0131680 npc=f0131684 ..."
Bug: The dumb memory allocator from bug #1 was allocating a range that the SunOS 4 kernel doesn't like.
Fix: Mimic the Sun boot ROM allocator: the top of the heap should be a 0xFFEDA000 and allocations should return descending addresses. So, for example, if asking for 0x1000 bytes, the first returned pointer should be 0xFFED9000.
9) Error message: "BAD TRAP: cpu=0 type=9 rp=fd008d2c addr=b1b91000 mmu_fsr=126 rw=1 MMU sfsr=126: Invalid Address on supv data fetch at level 1 regs at fd008d2c: psr=4900cc3 pc=f0142c04 npc=f0142c08 ..."
Bug: The precise underlying cause isn't clear. The bug appears due to a variation between OBP's behavior and stock Sun behavior.
Fix: Add the "cache-physical?" property to the CPU node in ob_nvram_init() and bump the "mmu-nctx" property up to 4096 (from 256).
Modified: openbios-devel/arch/sparc32/entry.S =================================================================== --- openbios-devel/arch/sparc32/entry.S 2006-10-30 23:06:43 UTC (rev 113) +++ openbios-devel/arch/sparc32/entry.S 2007-03-09 00:59:05 UTC (rev 114) @@ -60,15 +60,15 @@ sta %g1, [%g0] ASI_M_MMUREGS ! enable mmu
first_cpu: + /* Create temporary page tables and map the ROM area to end of + RAM. This will be done properly in iommu.c later. */ set PHYS_JJ_EEPROM + 0x30, %g1 lda [%g1] ASI_M_BYPASS, %g1 - ! map PROLDATA to PROLBASE+PROLSIZE to end of ram - !set PROLSIZE+0x1000-PROLDATA+PROLBASE, %g2 ! add 0x1000 for temp tables set _end, %g3 set 0xfff, %g2 add %g3, %g2, %g3 andn %g3, %g2, %g3 - set _data, %g2 + set _start, %g2 sub %g3, %g2, %g3 set 0x1000, %g4 ! add 0x1000 for page tables add %g4, %g3, %g2 @@ -128,45 +128,17 @@ sta %g3, [%g2] ASI_M_BYPASS add %g2, 0xa00-0x9e0, %g2 ! s+0xa00
- !set (PROLDATA-PROLBASE)/0x1000, %g6 ! # of .text pages - set _start, %g3 - set _data, %g6 - set 0x1000, %g5 - sub %g6, %g3, %g6 - srl %g6, 12, %g6 ! # of .text pages -1: srl %g3, 0x4, %g4 - or %g4, ((7 << 2) | 2), %g4 ! 4 = U: --X S: --X (rom, execute only) - sta %g4, [%g2] ASI_M_BYPASS - add %g2, 4, %g2 - add %g3, %g5, %g3 - deccc %g6 - bne 1b - nop -#if 0 - !set (PROLDATA-PROLRODATA)/0x1000, %g6 ! # of .rodata pages - set _data, %g6 - set _rodata, %g4 - sub %g6, %g4, %g6 - srl %g6, 12, %g6 ! # of .bss pages -1: srl %g3, 0x4, %g4 - or %g4, ((0 << 2) | 2), %g4 ! 0 = U: R-- S: R-- (rom, read only) - sta %g4, [%g2] ASI_M_BYPASS - add %g2, 4, %g2 - add %g3, %g5, %g3 - deccc %g6 - bne 1b - nop -#endif - !set (PROLBASE+PROLSIZE-PROLDATA)/0x1000, %g6 ! # of .bss pages + /* Use end of ram for code, rodata, data, and bss + sections. SunOS wants to write to trap table... */ set _end, %g6 - set _data, %g4 + set _start, %g4 sub %g6, %g4, %g6 - srl %g6, 12, %g6 ! # of .bss pages - set 0x1000, %g4 + srl %g6, 12, %g6 ! # of all pages + set 0x1000, %g5 sll %g7, 0x4, %g3 - add %g4, %g3, %g3 + add %g5, %g3, %g3 ! ctx table + 0x1000 1: srl %g3, 0x4, %g4 - or %g4, ((7 << 2) | 2), %g4 ! 5 = U: R-- S: RW- (data area, read/write) + or %g4, ((7 << 2) | 2), %g4 ! 7 = U: --- S: RWX sta %g4, [%g2] ASI_M_BYPASS add %g2, 4, %g2 add %g3, %g5, %g3 @@ -174,8 +146,26 @@ bne 1b nop
- mov %g1, %g3 + mov %g1, %g6 ! %g6 = memory size
+ /* Copy the code, rodata and data sections from ROM. */ + set 0x1000 - 4, %g4 + sll %g7, 0x4, %g3 + add %g4, %g3, %g3 ! ctx table + 0x1000 - 4 + set _start - 4, %g4 ! First address of TEXT + set _bss, %g5 ! Last address of DATA + ba 2f + nop +1: + lda [%g4] ASI_M_BYPASS, %g1 + sta %g1, [%g3] ASI_M_BYPASS +2: + cmp %g4, %g5 + add %g3, 0x4, %g3 + bl 1b + add %g4, 0x4, %g4 + + set AC_M_CTPR, %g2 sta %g7, [%g2] ASI_M_MMUREGS ! set ctx table ptr set 1, %g1 @@ -196,19 +186,6 @@ wr %g2, 0x0, %psr WRITE_PAUSE
- /* Copy the DATA section from ROM. */ - set _data - 4, %o0 ! First address of DATA - set _bss, %o1 ! Last address of DATA - ba 2f - nop -1: - lda [%o0] ASI_M_BYPASS, %g1 - st %g1, [%o0] -2: - subcc %o0, %o1, %g0 - bl 1b - add %o0, 0x4, %o0 - /* Zero out our BSS section. */ set _bss - 4, %o0 ! First address of BSS set _estack, %o1 ! Last address of BSS @@ -229,10 +206,10 @@ wr %g1, 0x0, %tbr
set qemu_mem_size, %g1 - st %g3, [%g1] + st %g6, [%g1]
sll %g7, 4, %g7 ! Store va->pa conversion factor - set _data - 0x1000, %g1 + set _start - 0x1000, %g1 sub %g1, %g7, %g7 set va_shift, %g1 st %g7, [%g1]
Modified: openbios-devel/arch/sparc32/romvec.c =================================================================== --- openbios-devel/arch/sparc32/romvec.c 2006-10-30 23:06:43 UTC (rev 113) +++ openbios-devel/arch/sparc32/romvec.c 2007-03-09 00:59:05 UTC (rev 114) @@ -108,6 +108,21 @@ } }
+#if CONFIG_DEBUG_OBP +static int looks_like_string(char *str, int len) +{ + int i; + int ret = (str[len-1] == '\0'); + for (i = 0; i < len-1 && ret; i++) + { + int ch = str[i] & 0xFF; + if (ch < 0x20 || ch > 0x7F) + ret = 0; + } + return ret; +} +#endif + static int obp_getprop(int node, char *name, char *value) { int notfound, found; @@ -148,7 +163,19 @@ else str = "NULL";
- DPRINTF("obp_getprop(0x%x, %s) = %s\n", node, name, str); +#if CONFIG_DEBUG_OBP + if (looks_like_string(str, len)) { + DPRINTF("obp_getprop(0x%x, %s) = %s\n", node, name, str); + } else { + int i; + DPRINTF("obp_getprop(0x%x, %s) = ", node, name); + for (i = 0; i < len; i++) { + DPRINTF("%02x%s", str[i] & 0xFF, + (len == 4 || i == len-1) ? "" : " "); + } + DPRINTF("\n"); + } +#endif
return len; } @@ -378,8 +405,22 @@
static char * obp_dumb_memalloc(char *va, unsigned int size) { + static unsigned int next_free_address = 0xFFEDA000; + free_ram -= size; DPRINTF("obp_dumb_memalloc req 0x%x of %d at 0x%x\n", va, size, free_ram); + + // If va is null, the allocator is supposed to pick a "suitable" address. + // (See OpenSolaric prom_alloc.c) There's not any real guidance as + // to what might be "suitable". So we mimic the behavior of a Sun boot + // ROM. + + if (va == NULL) { + va = next_free_address - size; + next_free_address -= size; + DPRINTF("obp_dumb_memalloc req null -> 0x%x\n", va); + } + obp_dumb_mmap(va, 1, free_ram, size);
return va; @@ -447,7 +488,7 @@ void * init_openprom(unsigned long memsize) { - free_ram = va2pa((int)&_data) - PAGE_SIZE; + free_ram = va2pa((int)&_start) - PAGE_SIZE;
ptphys = totphys; ptmap = totmap;
Modified: openbios-devel/drivers/esp.c =================================================================== --- openbios-devel/drivers/esp.c 2006-10-30 23:06:43 UTC (rev 113) +++ openbios-devel/drivers/esp.c 2007-03-09 00:59:05 UTC (rev 114) @@ -285,12 +285,24 @@
selfword("open-deblocker");
+#if WHEN_INTERPOSE_IS_FIXED + // Buggy implementation of "interpose" breaks SunOS 4. + // inst2pkg of the device returned by open() should return + // the same phandle regardless of whether any interposition + // has happened. Interpose currently causes inst2pkg to + // return the phandle of /packages/misc-files. + // + // Removing interpose isn't the ideal solution, but it + // allows SunOS 4 to load the kernel. Interpose should + // be fixed, then re-enabled. + /* interpose disk-label */ ph = find_dev("/packages/disk-label"); fword("my-args"); PUSH_ph( ph ); fword("interpose"); - +#endif + RET ( -ret ); }
Modified: openbios-devel/drivers/iommu.c =================================================================== --- openbios-devel/drivers/iommu.c 2006-10-30 23:06:43 UTC (rev 113) +++ openbios-devel/drivers/iommu.c 2007-03-09 00:59:05 UTC (rev 114) @@ -222,7 +222,7 @@ PUSH(0); fword("encode-int"); fword("encode+"); - PUSH(va2pa((unsigned long)&_data) - PAGE_SIZE); + PUSH(va2pa((unsigned long)&_start) - PAGE_SIZE); fword("encode-int"); fword("encode+"); push_str("available"); @@ -295,14 +295,8 @@ l1[i] = SRMMU_ET_INVALID; }
- // 1:1 mapping for ROM - pa = va = (unsigned long)&_start; - for (; va < (unsigned long)&_data; va += PAGE_SIZE, pa += PAGE_SIZE) { - map_page(va, pa, 0); - } - - // data & bss mapped to end of RAM - va = (unsigned long)&_data; + // text, rodata, data, and bss mapped to end of RAM + va = (unsigned long)&_start; for (; va < (unsigned long)&_end; va += PAGE_SIZE) { pa = va2pa(va); map_page(va, pa, 0);
Modified: openbios-devel/drivers/obio.c =================================================================== --- openbios-devel/drivers/obio.c 2006-10-30 23:06:43 UTC (rev 113) +++ openbios-devel/drivers/obio.c 2007-03-09 00:59:05 UTC (rev 114) @@ -37,6 +37,20 @@ #define PROMDEV_SCREEN 0 /* output to screen */ #define PROMDEV_TTYA 1 /* in/out to ttya */
+/* "NCPU" is an historical name that's now a bit of a misnomer. The sun4m + * architecture registers NCPU CPU-specific interrupts along with one + * system-wide interrupt, regardless of the number of actual CPUs installed. + * See the comment on "NCPU" at <http://stuff.mit.edu/afs/athena/astaff/ + * project/opssrc/sys.sunos/sun4m/devaddr.h>. + */ +#define SUN4M_NCPU 4 + +/* The kernel may want to examine the arguments, so hold a copy in OBP's + * mapped memory. + */ +#define OBIO_CMDLINE_MAX 256 +char obio_cmdline[OBIO_CMDLINE_MAX]; + /* DECLARE data structures for the nodes. */ DECLARE_UNNAMED_NODE( ob_obio, INSTALL_OPEN, sizeof(int) );
@@ -306,6 +320,7 @@ const char *stdin, *stdout; unsigned int i; char nographic; + uint32_t size;
ob_new_obio_device("eeprom", NULL);
@@ -325,8 +340,14 @@ } kernel_image = nv_info.kernel_image; kernel_size = nv_info.kernel_size; - cmdline = nv_info.cmdline; - cmdline_size = nv_info.cmdline_size; + + size = cmdline_size; + if (size > OBIO_CMDLINE_MAX) size = OBIO_CMDLINE_MAX; + memcpy(obio_cmdline, nv_info.cmdline, size); + obio_cmdline[size-1] = '\0'; + cmdline = obio_cmdline; + cmdline_size = size; + boot_device = nv_info.boot_device; nographic = nv_info.nographic;
@@ -369,6 +390,10 @@ push_str("version"); fword("property");
+ push_str(""); + push_str("cache-physical?"); + fword("property"); + PUSH(32); fword("encode-int"); push_str("cache-line-size"); @@ -435,7 +460,7 @@ push_str("ncaches"); fword("property");
- PUSH(256); + PUSH(4096); fword("encode-int"); push_str("mmu-nctx"); fword("property"); @@ -539,26 +564,32 @@ ob_counter_init(unsigned long base, unsigned long offset) { volatile struct sun4m_timer_regs *regs; + int i;
ob_new_obio_device("counter", NULL);
+ for (i = 0; i < SUN4M_NCPU; i++) { + PUSH(0); + fword("encode-int"); + if (i != 0) fword("encode+"); + PUSH(offset + (i * PAGE_SIZE)); + fword("encode-int"); + fword("encode+"); + PUSH(COUNTER_REGS); + fword("encode-int"); + fword("encode+"); + } + PUSH(0); fword("encode-int"); - PUSH(offset); - fword("encode-int"); fword("encode+"); - PUSH(COUNTER_REGS); - fword("encode-int"); - fword("encode+"); - PUSH(0); - fword("encode-int"); - fword("encode+"); PUSH(offset + 0x10000); fword("encode-int"); fword("encode+"); PUSH(COUNTER_REGS); fword("encode-int"); fword("encode+"); + push_str("reg"); fword("property");
@@ -580,26 +611,32 @@ static void ob_interrupt_init(unsigned long base, unsigned long offset) { + int i;
ob_new_obio_device("interrupt", NULL);
+ for (i = 0; i < SUN4M_NCPU; i++) { + PUSH(0); + fword("encode-int"); + if (i != 0) fword("encode+"); + PUSH(offset + (i * PAGE_SIZE)); + fword("encode-int"); + fword("encode+"); + PUSH(INTERRUPT_REGS); + fword("encode-int"); + fword("encode+"); + } + PUSH(0); fword("encode-int"); - PUSH(offset); - fword("encode-int"); fword("encode+"); - PUSH(INTERRUPT_REGS); - fword("encode-int"); - fword("encode+"); - PUSH(0); - fword("encode-int"); - fword("encode+"); PUSH(offset + 0x10000); fword("encode-int"); fword("encode+"); PUSH(INTERRUPT_REGS); fword("encode-int"); fword("encode+"); + push_str("reg"); fword("property");
Modified: openbios-devel/include/sparc32/io.h =================================================================== --- openbios-devel/include/sparc32/io.h 2006-10-30 23:06:43 UTC (rev 113) +++ openbios-devel/include/sparc32/io.h 2007-03-09 00:59:05 UTC (rev 114) @@ -10,7 +10,7 @@ static inline unsigned long va2pa(unsigned long va) { - if ((va >= (unsigned long)&_data) && + if ((va >= (unsigned long)&_start) && (va < (unsigned long)&_end)) return va - va_shift; else @@ -20,8 +20,8 @@ static inline unsigned long pa2va(unsigned long pa) { - if ((pa + va_shift >= (unsigned long)&_data) && - (pa + va_shift< (unsigned long)&_end)) + if ((pa + va_shift >= (unsigned long)&_start) && + (pa + va_shift < (unsigned long)&_end)) return pa + va_shift; else return pa;