This introduction is particularly biased by my interest in the 3/80 bootrom. The bootrom disassembly is my only real life example of the sun3x MMU being used, so there you go.
You get these resources as part of the CPU:
CRP - 64 bit cpu root pointer SRP - 64 bit system root pointer TC - 32 bit "translation control" register MMUSR - 16 bit mmu status (never used in bootrom) TT0 - 32 bit transparent translation register (never used in bootrom) TT1 - 32 bit transparent translation register (never used in bootrom)The "pmove" instruction is used to access these registers.
pmove %srp,%a0@ pmovefd %a0@,%srp pmove %tc,%a0@ pmovefd %a0@,%tc pmove %psr,%a0@ pmove %a0@,%psrThe "fd" variant says "flush disable" and disables the flushing of the ATC as would normally take place. The pmove instruction did not exist on the 68020.
What is the %psr? How is it different than %sr? It took some time, but I figured it out. It is the MMUSR. The fact that it is accessed via the PMOVE instruction should make this clear.
Dump MMU Exception 0x2C at 0x00004040.Indeed, the offending instruction is:
pmove %tc,%d0What exactly is exception 0x2c?
I try something different and now get:
Dump MMU Error (PSR = 0x203) Timeout Bus Error: Vaddr: 80D07660, Paddr: 00001660, Read, FC 5, Size 1 at 0x000048A0.This comes from deep inside printf(). But I am distracted by some bonus information in the above. First we are running with FC=5, which is no great surprise. It also says that the PSR is 0x203. If we can believe this and the upper 4 bits are 0, we are running in user state, not supervisor state (as I would hope and expect).
It turns out the above error is due to a stupid typo. (%s rather than %x in the format) When I fix that, I get:
Dump MMU TC = 80D07660 CRP = 7FFF0003 00FFA000 SRP = 7FFF0003 00FFA000This uses the following code to read the TC (as well as CRP and SRP
get_tc: moveal %sp@(4), %a0 pmove %tc, %a0@ rtsDid my first attempt fail due to pmove not allowing the %d0 register? So it would seem, and the bootrom never does that. It is odd that the assembler did not complain.
The fact that we can now execute the PMOVE instruction suggests that we are not in user mode. In fact we dump some more registers to investigate:
SR = 00002704 TC = 80D07660 PSR = 00030000 CRP = 7FFF0003 00FFA000 SRP = 7FFF0003 00FFA000Indeed, just being able to read the %sr register proves we are not in user mode, and the value we read confirms that. Note that the PSR is only 16 bits, so only the value 0003 is valid.
80 = bits E---_--SF E = translation enabled S = (off) SRP is disabled F = (off) FC lookup disabled D0 = pppp_iiii pppp = 0xD = 1101 = page size 8K iiii = 0x0 = 0000 = initial shift 76 = aaaa_bbbb aaaa = 0x7 = 7 bits in level A bbbb = 0x6 = 6 bits in level B 60 = cccc_dddd cccc = 0x6 = 6 bits in level C dddd = 0 = no level DSo, a virtual address is split 7:6:6:13 with 13 bits for an 8K page.
Note above that the CRP and SRP are set identically, but the SRP is disabled.
CRP + 7FFF0003 00FFA000This is a 64 bit object.
7FFF is a special value saying "no upper or lower limit" 0003 indicates that long (8 byte) descriptors are used. 00FFA000 is the table address.So, we have an array of 8 byte objects at 0x00FFA000.
A naive attempt to access the table address of 0x00FFA000 nets us:
Error (PSR = 0x203) Timeout Bus Error: Vaddr: 00FFA000, Paddr: 00000000, Read, FC 5, Size 4 at 0x0000458E.This is a fine opportunity to consider the value in the PSR (mmu status) of 0x203.
0200 = modified 0003 = 3 levelsThe modified bit doesn't tell us much, but the 3 levels is consistent with the setup of A,B,C, but not D in the TC register. The value in %psr is not set by a fault, but is set when the ptest instruction is used to investigate an access.
My machine currently has 16M of physical RAM, so a single entry in the A table would map it, using only half of the B entries.
^r TC: (0x80D07660) CRP: (0x7FFF0003) (0xFFA000) SYS ENA: (0x8000) INT: (0x81) BERR: (0x20) VBR: (0xFEF72000) MEM ERR CNTRL: (0x50505050) MEM ERR ADDR: (0xFFE9A0)Also consider these:
>m a TIA Map 00000000 [00FFA000] = 00FFA400? TIA Map 02000000 [00FFA000] = 00000000? TIA Map 04000000 [00FFA000] = 00000000? TIA Map 06000000 [00FFA000] = 00000000? TIA Map 08000000 [00FFA000] = 00000000? . >m b TIB Map 00000000 [00FFA400] = 00FFA60A? TIB Map 00080000 [00FFA400] = 00FFA70A? TIB Map 00100000 [00FFA400] = 00FFA80A? TIB Map 00180000 [00FFA400] = 00FFA902? TIB Map 00200000 [00FFA400] = 00FFAA02? .One happy thing about these is that they confirm values I found my reading the registers.
Now consider 16M of physical memory. This would be at addresses:
0000_0000 to 00ff_ffffSo, it would appear that the bootrom located the page tables in the top end of physical ram.
I experiment, seeing what address ranges I can read from. After some trial and error, I see the following:
Poke 00000000 = 80D07660 Poke 00080000 = 00000000 Poke 00100000 = 00000000 Poke 00200000 = 00000000 Poke 00300000 = 00000000 Poke 00400000 = 00000000 Poke 00600000 = 00000000 Poke 00700000 = 00000000 Poke 00780000 = 00000000 Poke 007FFFF0 = 00000000 Error (PSR = 0x203) Timeout Bus Error: Vaddr: 00800000, Paddr: 7FFFE000, Read, FC 5, Size 4 at 0x000044BA.So, it looks like, for whatever reason, I can only access the first 8M of memory.
Error (PSR = 0x203) Timeout Bus Error: Vaddr: 0FE02004, Paddr: 00FFA004, Read, FC 5, Size 1 at 0x00004ABC.Let's use the monitor "m" command to see what it thinks the A and B mappings for that address might be:
m a 0fe02004 TIA Map 0FE02004 [00FFA000] = 00000000? TIA Map 11E02004 [00FFA000] = 00000000? . >m b 0fe02004 TIB Map 0FE02004 [00000000] = 00000000?That seems pretty definite. These addresses aren't mapped.
>m a 0 TIA Map 00000000 [00FFA000] = 00FFA400? TIA Map 02000000 [00FFA000] = 00000000? TIA Map 04000000 [00FFA000] = 00000000? TIA Map 06000000 [00FFA000] = 00000000? ... TIA Map FC000000 [00FFA000] = 00000000? TIA Map FE000000 [00FFA000] = 00FFA500? TIA Map 00000000 [00FFA000] = 00FFA400? TIA Map 02000000 [00FFA000] = 00000000?The "m" command just loops when we get past fe000000. But there is a zone of mapped addresses at the top of the virtual address space.
Let's look at the B table for the 0 zone. We expect it just to map the 8M of ram we discovered.
m b 0 TIB Map 00000000 [00FFA400] = 00FFA60A? TIB Map 00080000 [00FFA400] = 00FFA70A? TIB Map 00100000 [00FFA400] = 00FFA80A? TIB Map 00180000 [00FFA400] = 00FFA902? TIB Map 00200000 [00FFA400] = 00FFAA0A? TIB Map 00280000 [00FFA400] = 00FFAB02? TIB Map 00300000 [00FFA400] = 00FFAC0A? TIB Map 00380000 [00FFA400] = 00FFAD02? TIB Map 00400000 [00FFA400] = 00FFAE0A? TIB Map 00480000 [00FFA400] = 00FFAF02? TIB Map 00500000 [00FFA400] = 00FFB002? TIB Map 00580000 [00FFA400] = 00FFB102? TIB Map 00600000 [00FFA400] = 00FFB20A? TIB Map 00680000 [00FFA400] = 00FFB302? TIB Map 00700000 [00FFA400] = 00FFB40A? TIB Map 00780000 [00FFA400] = 00FFB50A? TIB Map 00800000 [00FFA400] = 00000000? TIB Map 00880000 [00FFA400] = 00000000? ... TIB Map 01E80000 [00FFA400] = 00000000? TIB Map 01F00000 [00FFA400] = 00000000? TIB Map 01F80000 [00FFA400] = 00000000?Indeed, each B entry maps 0.5M and the mapping continues for 8M.
m b FE000000 TIB Map FE000000 [00FFA500] = 00000000? TIB Map FE080000 [00FFA500] = 00000000? TIB Map FE100000 [00FFA500] = 00000000? TIB Map FE180000 [00FFA500] = 00000000? ..... TIB Map FED80000 [00FFA500] = 00000000? TIB Map FEE00000 [00FFA500] = 00FFB602? TIB Map FEE80000 [00FFA500] = 00FFB702? TIB Map FEF00000 [00FFA500] = 00FFB80A? TIB Map FEF80000 [00FFA500] = 00FFB90A? TIB Map FF000000 [00FFA500] = 00000000? ..... TIB Map FFE00000 [00FFA500] = 00000000? TIB Map FFE80000 [00FFA500] = 00000000? TIB Map FFF00000 [00FFA500] = 00FFBA0A? TIB Map FFF80000 [00FFA500] = 00FFBB02?That is interesting! A 2M zone at FEE0_0000 and a 1M zone at FFF0_0000.
^a Devcie: Virtual Address: Physical Address: Keybrd Mouse 0xFEF00000: 0x62000000: Serial Port 0xFEF02000: 0x62002000: EEPROM 0xFEF04000: 0x64000000: TOD 0xFEF06000: 0x640007F8: Mem Err Reg 0xFEF09000: 0x61001000: Int Reg 0xFEF0B400: 0x61001400: le Ethernet 0xFEF0E000: 0x65002000: Sys ENA Reg 0xFEF16000: 0x61000000: Bus ERR Reg 0xFEF18400: 0x61000400: IDPROM 0xFEF047D8: 0x640007D8: VIDEOMEM_BASE 0xFEF20000: 0x50400000: P4 Video Reg 0xFEF1E000: 0x50300000: P4 DAC 0xFEF0C000: 0x50200000: P4 Overlay 0xFEF20000: 0x50400000: P4 ENA Plane 0xFEF40000: 0x50600000: IOMAPPER 0xFEF66000: 0x60000000: FDC 0xFEF7A000: 0x6E000000: FDC_CNTRL_REG 0xFEF7A400: 0x6E000400: FDC_VEC_REG 0xFEF7A800: 0x6E000800: PRINTER_PORT 0xFEF8003C: 0x6F00003C:Now I realize my error with the serial port! I need to make this change:
// #define SCC_BASE 0x0fe02000 #define SCC_BASE 0xfef02000I just tried to use the address from the 3/160 system, and only glanced at the virtual address table above.
Indeed! I can write to the serial port at these proper addresses.
The most civilized way would be to set up a new set of page tables at the top of the 8M mapped ram, then switch the MMU to use those. Those could map the entire 16M and let me do anything.
Perhaps if I put proper values into the NVRAM, it would map the entire 16M? I note these messages:
Sun Workstation, Model Sun-3/80 Series. ROM Rev 3.0.2, 16MB memory installed, Serial #1193046. Ethernet address 8:0:20:1:DB:2, Host ID 42123456. Testing 0 Megabytes of Memory ... Completed.Even though it at first recognizes the proper 16M of ram, it later says it is only testing 0 -- given that, who knows what is decided to initialize virtual addresses for only 8M
The CRP itself is a long format table descriptor.
There are actually 4 types of long descriptors:
Normal table descriptor Early termination page descriptor Page descriptor Invalid descriptorThe manual shows examples where long and short descriptors are mixed.
p 0 PageMap 00000000 [00FFA600] = 00000049? PageMap 00002000 [00FFA600] = 00002059? PageMap 00004000 [00FFA600] = 00004059? PageMap 00006000 [00FFA600] = 00006041? PageMap 00008000 [00FFA600] = 00008041? .This is perhaps the level 3 "C" map and consists of page descriptors (8K each).
Tom's Computer Info / tom@mmto.org