.text .global start start: ! We have been loaded into low RAM, at an address chosen by ! OBP, offset by booter. Figure out our actual location. 1: call 2f nop 2: ! The instruction we think of as being at 1 is really at %o7. ! Compute the actual address of database and save it in %g1. set 1b - database, %o5 sub %o7, %o5, %g1 ! Set up a stack set stackend - database, %o5 add %o5, %g1, %o5 andn %o5, 7, %sp ! Create a stack frame so we can call the PROM save %sp, -96, %sp ! PROM vector was passed in %o0, now %i0. Save it in %g2. mov %i0, %g2 ! %g3 holds global flags. mov %g0, %g3 ! Check PROM interface version ! XXX do we want to check magic number? ld [%g2+4], %l0 ! pv_romvec_vers tst %l0 beq rom_v0 nop subcc %l0,2, %g0 beq rom_v2 nop subcc %l0,3, %g0 beq rom_v3 nop halt: ld [%g2+0x74], %l0 call %l0 nop b halt nop rom_v0: ld [%g2+0x54], %l0 call %l0 add %g0, 0x56, %o0 call %l0 add %g0, 0x30, %o0 call %l0 add %g0, 0x0d, %o0 call %l0 add %g0, 0x0a, %o0 b halt nop print_string: ! String in %o0, no other args ! Depends on %g2. andcc %g3, 1, %g0 beq 9f nop retl nop 9: save %sp, -96, %sp mov %g0, %o2 ! inlined strlen 1: ldub [%i0+%o2], %o3 tst %o3 bne 1b inc %o2 dec %o2 mov %i0, %o1 ld [%g2+0x94], %o0 ! v2_fd1 ld [%g2+0xb8], %o4 ! v2_write mov %o2, %i0 call %o4 ld [%o0], %o0 ! *v2_fd1 ret restore print_onechar: ! Char in %o0, no other args ! Depends on %g1 and %g2. add %g1, numbuf-database, %o1 stb %o0, [%o1] stb %g0, [%o1+1] b print_string ! tail call mov %o1, %o0 print_hex8: ! Number in %o0, no other args ! Depends on %g1 and %g2. add %g1, numbuf-database, %o1 add %g1, digits-database, %o2 set 7, %o5 1: and %o0, 15, %o3 ldub [%o2+%o3], %o4 stb %o4, [%o1+%o5] deccc %o5 bge 1b srl %o0, 4, %o0 stb %g0, [%o1+8] b print_string ! tail call mov %o1, %o0 print_hex2: ! Number in %o0, no other args ! Depends on %g1 and %g2. Uses %o1, %o2, %o3, %o4. add %g1, numbuf-database, %o1 add %g1, digits-database, %o2 and %o0, 15, %o3 ldub [%o2+%o3], %o4 stb %o4, [%o1+1] srl %o0, 4, %o3 and %o3, 15, %o3 ldub [%o2+%o3], %o4 stb %o4, [%o1] stb %g0, [%o1+2] b print_string ! tail call mov %o1, %o0 print_dec: ! Number in %o0, no other args ! Depends on %g1 and %g2. save %sp, -96, %sp add %g1, numbuf-database, %i1 add %g1, digits-database, %i2 tst %i0 bge 1f mov %i1, %i4 set 0x2d, %i3 stb %i3, [%i1] inc %i1 sub %g0, %i0, %i0 1: mov %i1, %l2 mov %i0, %o0 1: call udiv add %g0, 10, %o1 ldub [%i2+%o1], %l1 stb %l1, [%i1] tst %o0 bne 1b inc %i1 stb %g0, [%i1] 1: dec %i1 cmp %i1,%l2 ble 1f nop ldub [%i1], %l3 ldub [%l2], %l4 stb %l3, [%l2] stb %l4, [%i1] b 1b inc %l2 1: mov %i4, %i0 b print_string ! tail call restore udiv: ! Unsigned divide %o0 by %o1, quotient in %o0, remainder in %o1 ! Uses %o2, %o3, %o4. ! If %o1 is zero, division by zero. tst %o1 bne 1f nop call print_string add %g1, divby0-database, %o0 b halt nop 1: ! If %o1 > %o0, easy. (This includes %o0 being zero.) cmp %o1, %o0 bleu 1f nop mov %o0, %o1 retl mov %g0, %o0 1: ! Since %o1 <= %o0, %o1's high bit must be no higher than ! %o0's. Copy %o1 to %o2, then shift %o2 left until its high ! bit matches %o0's; then we can start the divide loop. But ! this means figuring out where %o0's high bit is. ! Annoyingly, there is no srlcc instruction; we could save a ! register (and two instructions per iteration) if there were. set 16, %o4 mov %o4, %o3 2: srl %o0, %o3, %o2 tst %o2 be,a 1f sub %o3, %o4, %o3 1: srl %o4, 1, %o4 tst %o4 bne,a 2b add %o3, %o4, %o3 ! %o3 is now the largest amount %o0 can be shifted right by ! without shifting the high bit away. add %g0, 1, %o2 sll %o2, %o3, %o3 ! %o3 is now %o0's high bit. Copy %o1 to %o2 and shift it left ! until its high bit hits %o3. mov %o1, %o2 1: andcc %o2, %o3, %g0 be,a 1b sll %o2, 1, %o2 ! We can now do the divide loop. We develop the quotient in ! %o3 and the remainder in %o0. mov %g0, %o3 2: cmp %o2, %o0 bgu 1f sll %o3, 1, %o3 sub %o0, %o2, %o0 inc %o3 1: srl %o2, 1, %o2 cmp %o2, %o1 bge 2b nop mov %o0, %o1 retl mov %o3, %o0 umul: ! Unsigned multiply %o0 by %o1. Product in %o0 (low) and %o1 (high). ! Uses %o2, %o3, %o4, %o5. ! Could use mulscc instead. Likely faster, especially for large ! numbers, but definitely more code space needed. mov %g0, %o2 mov %g0, %o3 mov %g0, %o4 2: set 1, %o5 andcc %o1, %o5, %g0 be 1f srl %o1, 1, %o1 addcc %o2, %o0, %o2 addx %o3, %o4, %o3 1: sll %o4, 1, %o4 srl %o0, 31, %o5 or %o4, %o5, %o4 tst %o1 bne 2b sll %o0, 1, %o0 mov %o2, %o0 retl mov %o3, %o1 rom_v2: rom_v3: ! Banner. call print_string add %g1, starting-database, %o0 ! CRC ourselves. Do this before we try to print any numbers, ! because that changes numbuf. print_string itself doesn't ! change anything, so we can banner before this. ! Unfortunately, this won't catch memory errors that don't ! change our data but that matter, like a stuck-at-0 bit in ! what we call propbuf, but we can't catch *everything*. We ! could even, say, get hit with a memory error that changes ! the conditional branch in the checksum test to ! unconditional, which of course would break the check. And, ! of course, checksumming always has *some* chance of a false ! negative (ie, a corrupted version checksumming OK). add %g1, start-database, %l0 add %g1, checksum-database, %l1 set 0xedb88320, %l2 mov %g0, %l4 3: ldub [%l0], %l3 xor %l4, %l3, %l4 set 8, %l5 2: andcc %l4, 1, %g0 srl %l4, 1, %l4 bne,a 1f xor %l4, %l2, %l4 1: deccc %l5 bgu 2b nop inc %l0 cmp %l0, %l1 bne 3b nop ld [%l0], %l5 cmp %l4, %l5 beq 1f nop call print_string add %g1, checksum_fail1-database, %o0 call print_hex8 mov %l4, %o0 call print_string add %g1, checksum_fail2-database, %o0 add %g1, checksum-database, %o0 call print_hex8 ld [%o0], %o0 call print_string add %g1, crlf-database, %o0 b halt nop 1: ! Walk the children of the root node, looking for one with a ! `name' property with value "memory". ! First, get the root with no_nextnode applied to 0. ld [%g2+0x1c], %l1 ! pv_nodeops ld [%l1], %l0 ! no_nextnode call %l0 mov %g0, %o0 ! Get the root's first child. ld [%l1+0x04], %l0 ! no_child call %l0 nop ! arg already in %o0 ! Loop, checking `name' properties. mov %o0, %l7 4: ld [%l1+0x08], %l0 ! no_proplen add %g1, name_str-database, %o1 call %l0 mov %l7, %o0 ! If length is <6 or >7, isn't "memory". ("not found" comes ! back to us as a length of -1.) The reason we allow both 6 ! and 7 is that we want to catch both "memory" and "memory\0". sub %o0, 6, %o1 cmp %o1, 1 bgu 1f mov %o0, %l6 ! Fetch the value ld [%l1+0x0c], %l0 ! no_getprop add %g1, propbuf-database, %o2 add %g1, name_str-database, %o1 call %l0 mov %l7, %o0 ! Is it "memory"/"memory\0"? add %g1, propbuf-database, %o0 add %g1, memory_str-database, %o1 mov %g0, %o2 3: ldub [%o0+%o2], %o3 ldub [%o1+%o2], %o4 cmp %o3, %o4 bne 2f inc %o2 cmp %o2, %l6 bl 3b nop ! Found it! ! Fetch the `available' prop. ld [%l1+0x08], %l0 ! no_proplen add %g1, available_str-database, %o1 call %l0 mov %l7, %o0 ! Is it even there? tst %o0 bgu,a 3f ! If so, skip over loop bottom cmp %o0, epropbuf-propbuf call print_string add %g1, missing_available-database, %o0 b halt nop 2: ! Value isn't "memory"/"memory\0". 1: ! Length isn't 7. ! Fetch the next child and, if we haven't run out, try it. ld [%l1], %l0 ! no_nextnode call %l0 mov %l7, %o0 orcc %o0, %g0, %l7 bne 4b nop ! Ran out of nodes. call print_string add %g1, no_mem_node-database, %o0 b halt nop 3: ! Come here if the `available' property exists. ! Is it too long? ble 1f mov %o0, %l2 call print_string add %g1, too_fragmented-database, %o0 call print_dec mov %l2, %o0 call print_string add %g1, crlf-database, %o0 b halt nop 1: ! It fits! Fetch the value. ld [%l1+0x0c], %l0 ! no_getprop add %g1, propbuf-database, %o2 add %g1, available_str-database, %o1 call %l0 mov %l7, %o0 ! See how many memory segments we have. mov %l2, %o0 call udiv add %g0, 12, %o1 ! If there's a remainder, something's weird. tst %o1 be,a 1f mov %o0, %l2 call print_string add %g1, bad_length-database, %o0 call print_dec mov %l2, %o0 call print_string add %g1, crlf-database, %o0 b halt nop 1: ! Save the number of memory segments. st %l2, [%g1+(nsegs-database)] ! Compact them to 8 bytes each, removing the initial 4 bytes of ! zeroes from each one. This simplifes addressing later. ! This code depends on propbuf being aligned. set 4, %l3 ! src offset mov %g0, %l4 ! dst offset add %g1, propbuf-database, %l5 ! base mov %g0, %l6 ! counter 1: ld [%l5+%l3], %l7 add %l3, 4, %l3 st %l7, [%l5+%l4] add %l4, 4, %l4 ld [%l5+%l3], %l7 add %l3, 8, %l3 st %l7, [%l5+%l4] inc %l6 cmp %l6, %l2 blu 1b add %l4, 4, %l4 tst %l2 bg 1f nop call print_string add %g1, no_memory-database, %o0 b halt nop 1: ! Report the number of memory segments. call print_string add %g1, memseg_count-database, %o0 call print_dec mov %l2, %o0 call print_string add %g1, crlf-database, %o0 ! Print the memory segments. ! This depends on propbuf being aligned. mov %g0, %l3 add %g1, propbuf-database, %l5 1: sll %l3, 3, %o0 add %l5, %o0, %l4 call print_hex8 ld [%l4], %o0 call print_string add %g1, spc2-database, %o0 call print_hex8 ld [%l4+4], %o0 call print_string add %g1, crlf-database, %o0 inc %l3 cmp %l3, %l2 bl 1b nop ! We want two places we can relocate ourselves to in physical memory. ! While our memory can conceptually be split into multiple pieces, ! most notably text versus data, we run with no memory protection, so ! the distinctions between these pieces are irrelevant. We *could*, ! say, relocate data separately from text, but we are small enough ! there's little point. ! ! At this writing, we are about 2K - half a page - in size. I'm ! allocating space enough to handle growth up to a megabyte. A ! megabyte of 4k pages is 256 pages. ! ! We don't care which physical pages we use, as long as (a) they don't ! conflict with the PROM (which is why we use memory/available rather ! than memory/reg) and (b) they don't conflict with the memory we ! currently occupy. (We could, I suppose, use our current location as ! one of our relocation positions. Since we don't control how we're ! aligned, that strikes me as more difficult than necessary.) ! We're entered with the MMU enabled. set 0x200, %l0 ! Context lda [%l0]4, %l1 call print_dec mov %l1, %o0 call print_string add %g1, crlf-database, %o0 set 0x100, %l0 ! Context table pointer lda [%l0]4, %l2 sll %l1, 2, %o0 add %o0, %l2, %l4 sll %l4, 4, %l4 ! PA of context table entry call print_hex8 mov %l4, %o0 call print_string add %g1, crlf-database, %o0 lda [%l4]0x20, %l3 ! Entry in context table call print_hex8 mov %l3, %o0 call print_string add %g1, crlf-database, %o0 ! Is the context table entry a PTD? and %l3, 3, %o0 cmp %o0, 1 beq 1f nop ! Not a PTD? Handling this is too messy. call print_string add %g1, ctx_not_ptd-database, %o0 b halt nop 1: st %g0, [%g1+(foundphys-database)] call print_string add %g1, pre_cleanup-database, %o0 mov %l4, %o2 add %g1, dump_pte-database, %o1 call walk_tables mov %l3, %o0 ! Walk the page tables looking for memory. mov %l4, %o2 add %g1, save_pte-database, %o1 call walk_tables mov %l3, %o0 ld [%g1+(foundphys-database)], %l0 cmp %l0, (ephyspages-physpages)/4 beq 1f nop call print_string add %g1, physfail-database, %o0 call print_dec mov %l0, %o0 call print_string add %g1, crlf-database, %o0 b halt nop 1: call print_string add %g1, 9f-database, %o0 b 8f nop 9: .ascii "Disabling VM...\r\n\0" .align 4 8: ! We shut off all VM, except for (a) the VM we occupy and (b) VM ! belonging to the PROM (which we identify by its mapping to a ! physical address not in any of the physical address segments). mov %l4, %o2 add %g1, disable_most_vm-database, %o1 call walk_tables mov %l3, %o0 ! Dump out the page tables to verify. call print_string add %g1, post_cleanup-database, %o0 mov %l4, %o2 add %g1, dump_pte-database, %o1 call walk_tables mov %l3, %o0 ! We flush the whole PDC, copy ourselves to one of our relocation ! locations in physical memory (by using ASI 0x20), flush the PDC ! again (it shouldn't need it; this is paranoia), then set up VM ! covering our new location, then switch to using the new VM and tear ! down our old VM. ! Print the PTEs saved during the table walk. call print_string add %g1, nsaved-database, %o0 call print_dec ld [%g1+(foundphys-database)], %o0 call print_string add %g1, crlf-database, %o0 ld [%g1+(foundphys-database)], %l0 add %g1, physpages-database, %l3 mov %g0, %l1 1: sll %l1, 2, %l2 call print_hex8 ld [%l2+%l3], %o0 call print_string add %g1, crlf-database, %o0 inc %l1 cmp %l1, %l0 blu 1b nop b halt nop walk_tables: ! %o0 - PTD from context table ! %o1 - function to call when a PTE is found ! %o2 - physical address %i0 was loaded from mov %o2, %o4 mov %o1, %o3 mov %g0, %o2 mov %g0, %o1 ! and fall through into.... walk_tables_: ! (these are %o before the initial save, %i after it) ! %i0 - PTE/PTD from next level up ! %i1 - level (0-3) times 4 (ie, 0, 4, 8, or 12) ! %i2 - first VA mapped by this object ! %i3 - function to call when a PTE is found ! %i4 - physical address %i0 was loaded from ! ! When %i3 is called, it is called with ! %o0 - PTE ! %o1 - level (%i1 on entry) ! %o2 - first VA mapped by PTE ! %o3 - physical address PTE stored at ! ! We assume all physical memory (or at least all physical memory used ! for PROM-set-up page tables) has high address bits 0000, so we can ! just use ASI 0x20 instead of having to compute an ASI and either (a) ! generate an instruction on the fly or (b) have a table of ! instructions with all possible ASIs in them. ! ! :-þ on SPARC for not providing a way to generate an ASI at runtime. save %sp, -96, %sp andcc %i0, 3, %o0 beq invalid deccc %o0 beq ptd deccc %o0 beq pte nop ! Reserved last two bits call print_string add %g1, ptx_reserved-database, %o0 printva: call print_hex8 mov %i0, %o0 call print_string add %g1, crlf-database, %o0 b halt nop invalid: ! Nothing to do here. It can't give us physical pages for our ! use and it can't describe where we currently are. ret restore ptd: cmp %i1, 3*4 blu 1f nop ! PTD in L3 page table? Error; cough and die. call print_string add %g1, l3ptd-database, %o0 b printva nop 1: add %g1, vaincs-database, %o0 ld [%o0+%i1], %i5 andn %i0, 3, %i0 sll %i0, 4, %i0 add %g1, mmusizes-database, %o0 ld [%o0+%i1], %l0 sll %l0, 2, %l0 ! entry count -> byte count mov %g0, %l1 1: add %i0, %l1, %l2 lda [%l2]0x20, %o0 mov %l2, %o4 mov %i3, %o3 mov %i2, %o2 call walk_tables_ add %i1, 4, %o1 add %l1, 4, %l1 cmp %l1, %l0 blu 1b add %i2, %i5, %i2 ret restore pte: cmp %i1, 3*4 beq 1f nop ! PTE in other than L3 page table? Cough and die. call print_string add %g1, nonl3pte-database, %o0 b printva nop 1: mov %i4, %o3 mov %i2, %o2 mov %i1, %o1 call %i3 mov %i0, %o0 ret restore dump_pte: ! %o0 = PTE found ! %o1 = level (0-3) times 4 (ie, 0, 4, 8, or 12) ! %o2 = first VA mapped by this PTE ! %o3 = physical address PTE loaded from (we don't care about this) save %sp, -96, %sp call print_hex8 mov %i2, %o0 call print_string add %g1, spc-database, %o0 call print_hex8 mov %i0, %o0 call print_string add %g1, spc-database, %o0 call print_hex8 andn %i0, 0xff, %o0 call print_string add %g1, 2f-database, %o0 srl %i0, 7, %o0 call print_dec and %o0, 1, %o0 srl %i0, 6, %o0 call print_dec and %o0, 1, %o0 srl %i0, 5, %o0 call print_dec and %o0, 1, %o0 call print_string add %g1, spc-database, %o0 call print_dec and %i0, 7, %o0 call print_string add %g1, crlf-database, %o0 ret restore 2: .ascii "0 \0" .align 4 save_pte: ! %o0 = PTE found ! %o1 = level (0-3) times 4 (ie, 0, 4, 8, or 12) ! %o2 = first VA mapped by this PTE ! %o3 = physical address PTE loaded from (we don't care about this) ! Does it overlap with the VA we're using now? ! Overlap occurs iff pagemin < ourend and ourmin < pageend; ! to put the same test negatively, no overlap iff ! pagemin >= ourend or ourmin >= pageend. Coming in to this ! code, pagemin is in %o2 and the others need computing. ! ! We're simple enough to run as a leaf routine. set stackend-database, %o5 ! too large for immediate data add %g1, %o5, %o5 ! ourend cmp %o2, %o5 bge 1f add %g1, start-database, %o4 ! ourmin add %g1, (vaincs-4)-database, %o3 ld [%o3+%o1], %o3 add %o3, %o2, %o3 ! pageend cmp %o4, %o3 bge 1f nop ! It overlaps with the VM we're using. Ignore it. retl nop 1: ! No overlap. If we haven't found all the physical pages we ! want yet, save it (in the form of its PTE). ld [%g1+(foundphys-database)], %o3 cmp %o3, (ephyspages-physpages)/4 bge 1f sll %o3, 2, %o5 add %g1, physpages-database, %o4 st %o0, [%o5+%o4] inc %o3 st %o3, [%g1+(foundphys-database)] 1: retl nop disable_most_vm: ! %o0 = PTE found ! %o1 = level (0-3) times 4 (ie, 0, 4, 8, or 12) ! %o2 = first VA mapped by this PTE ! %o3 = address PTE loaded from ! Disable VM, except for (a) VM we occupy and (b) the PROM's ! VM. We identify the latter by its mapping to a physical ! address not in any of the physical address segments. ! PA outside low 4G of physical space? Don't meddle. save %sp, -96, %sp call print_string add %g1, 9f-database, %o0 call print_hex8 mov %i0, %o0 call print_string add %g1, 7f-database, %o0 call print_hex8 mov %i2, %o0 call print_string add %g1, 6f-database, %o0 call print_hex8 mov %i3, %o0 call print_string add %g1, 5f-database, %o0 b 1f nop 9: .ascii "PTE=\0" 7: .ascii " V=\0" 6: .ascii " F=\0" 5: .ascii ": \0" .align 4 1: set 0xf0000000, %o4 andcc %o4, %i0, %g0 bne 1f nop ! Is the VA ours? add %g1, (vaincs-4)-database, %o4 ld [%o4+%i1], %o4 add %o4, %i2, %o4 ! page V end add %g1, start-database, %o5 ! our start cmp %o4, %o5 bleu 2f set stackend-database, %o5 add %g1, %o5, %o5 ! our end cmp %i2, %o5 bgeu 2f nop ! Our VA. Don't meddle. call print_string add %g1, 9f-database, %o0 ret restore ! Non-low-4G PA. Don't meddle. 1: call print_string add %g1, 8f-database, %o0 ret restore 9: .ascii "ours\r\n\0" 8: .ascii "dev\r\n\0" .align 4 2: ! Not our VM. Look for the PA in the memory segments. Note ! that we want to count a PTE whose space is even partially ! outside all segments as outside. (I haven't seen that ! happen and I'm not sure it can, but I'm also not sure it ! can't.) We do this by taking the physical space ! corresponding to the <%o0,%o1> pair and subtracting each ! memory segment in turn from it, until either we run out of ! segments or we have an empty region left. To subtract [a,b) ! from [c,d), we do ! if ((a<=d) && (b>c)) c = b; ! if ((b>=c) && (a=d after this. (This is not ! theoretically ideal; if a>c and bc)) c = b; cmp %l6, %l2 bgu 2f cmp %l7, %i0 bgu,a 2f mov %l7, %i0 2: ! if ((b>=c) && (a