Changes

Jump to navigation Jump to search
Start documenting the Espresso boot ROM
Line 1: Line 1:  
The [[Espresso]] includes a special boot ROM that checks all software it boots. It only boots software in the form of an [[Ancast Image]].
 
The [[Espresso]] includes a special boot ROM that checks all software it boots. It only boots software in the form of an [[Ancast Image]].
   −
== Process ==
+
== Initialization ==
:''A more visual explanation can be seen [https://fail0verflow.com/media/30c3-slides/#/18 here].''
+
The Espresso boot ROM runs from address 0x00000100 where the PPC reset vector is located. It begins by setting up SPRs, mapping memory and jumping to the main routine:
 +
// Clear MSR, TLB and TBU
 +
MSR = 0
 +
TBL = 0
 +
TBU = 0
 +
 +
// Set HID0_BHT, HID0_BTIC, HID0_SGE and HID0_SPD
 +
HID0 = 0x2A4
 +
 +
// Set HID4_SBE
 +
HID4 |= 0x2000000
 +
 +
// Set a flag in BCR when in vWii mode
 +
if ((SCR & 0x8000000) != 0 && (SCR & 0x4000000) != 0)
 +
    BCR = ((BCR & 0x1FFFFFFF) | 0x20000000)
 +
   
 +
__isync();
 +
 +
// Clear all DBATs/IBATs
 +
DBAT0U = 0
 +
DBAT1U = 0
 +
DBAT2U = 0
 +
DBAT3U = 0
 +
DBAT4U = 0
 +
DBAT5U = 0
 +
DBAT6U = 0
 +
DBAT7U = 0
 +
IBAT0U = 0
 +
IBAT1U = 0
 +
IBAT2U = 0
 +
IBAT3U = 0
 +
IBAT4U = 0
 +
IBAT5U = 0
 +
IBAT6U = 0
 +
IBAT7U = 0
 +
 +
__isync();
 +
 +
// Clear all SRs
 +
SR0 = 0
 +
SR1 = 0
 +
SR2 = 0
 +
SR3 = 0
 +
SR4 = 0
 +
SR5 = 0
 +
SR6 = 0
 +
SR7 = 0
 +
SR8 = 0
 +
SR9 = 0
 +
SR10 = 0
 +
SR11 = 0
 +
SR12 = 0
 +
SR13 = 0
 +
SR14 = 0
 +
SR15 = 0
 +
 +
__sync();
 +
 +
// Flush L2CR with a dummy read
 +
L2CR = L2CR
 +
 +
__sync();
 +
 +
// Set L2CR_L2I
 +
L2CR |= 0x200000
 +
 +
// Wait for L2CR_L2IP to be cleared
 +
while ((L2CR & 0x1) != 0);
 +
 +
// Clear L2CR_L2I
 +
L2CR &= 0xFFDFFFFF
 +
 +
// Wait for L2CR_L2IP to be cleared
 +
while ((L2CR & 0x1) != 0);
 +
 +
// Set HID0_DCFI and HID0_ICFI
 +
HID0 |= 0xC00
 +
 +
__isync();
 +
 +
// Configure DBAT0
 +
// Physical address is 0
 +
// Virtual address is 0
 +
// Mapped as read only, cache inhibit, user valid and supervisor valid
 +
DBAT0L = 0x21
 +
DBAT0U = 0x3
 +
 +
__isync();
 +
 +
// Configure IBAT0
 +
// Physical address is 0
 +
// Virtual address is 0
 +
// Mapped as read only, cache inhibit, user valid and supervisor valid
 +
IBAT0L = 0x21
 +
IBAT0U = 0x3
 +
 +
__isync();
 +
 +
// Configure DBAT1
 +
// Physical address is 0xC320000
 +
// Virtual address is 0xC320000
 +
// Mapped as read only, cache inhibit, user valid and supervisor valid
 +
DBAT1L = 0xC320021
 +
DBAT1U = 0xC320003
 +
 +
__isync();
 +
 +
// Configure DBAT2
 +
// Physical address is 0xE0000000
 +
// Virtual address is 0xE0000000
 +
// Mapped as read write, user valid and supervisor valid
 +
DBAT2L = 0xE0000002
 +
DBAT2U = 0xE0000003
 +
 +
__isync();
 +
 +
// Configure DBAT4
 +
// Physical address is 0x16C0000
 +
// Virtual address is 0x20000
 +
// Mapped as read write, user valid and supervisor valid
 +
DBAT4L = 0x16C0002
 +
DBAT4U = 0x20003
 +
 +
__isync();
 +
 +
// Configure IBAT4
 +
// Physical address is 0x16C0000
 +
// Virtual address is 0x20000
 +
// Mapped as read write, user valid and supervisor valid
 +
IBAT0L = 0x16C0002
 +
IBAT0U = 0x20003
 +
 +
__isync();
 +
 +
// Clear DBAT6
 +
DBAT6L = 0
 +
DBAT6U = 0
 +
 +
__isync();
 +
 +
// Configure DBAT7
 +
// Physical address is 0x16E0000
 +
// Virtual address is 0xC16E0000
 +
// Mapped as read write, guarded, cache inhibit, user valid and supervisor valid
 +
DBAT7L = 0x16E002A
 +
DBAT7U = 0xC16E0003
 +
 +
__isync();
 +
 +
// Set MSR_DR and MSR_IR
 +
MSR |= 0x30
 +
 +
__isync();
 +
 +
// Clear L2CR_L2TS, L2CR_L2I, L2CR_L2DO and set L2CR_L2E
 +
L2CR = ((L2CR | 0x80000000) & 0xFF9BFFFF)
 +
 +
// Set HID0_ICE and HID0_DCE
 +
HID0 |= 0xC000
 +
 +
// Set HID2_LCE
 +
HID2 |= 0x10000000
 +
 +
u32 index = 0xE0000000;
 +
u32 size = 0x200;
 +
 +
// Set to zero data cache for the 0xE0000000 region
 +
while (size > 0)
 +
{
 +
    __dcbzl(0, index);
 +
    index += 0x20;
 +
    size -= 0x1;
 +
}
 +
 +
// Configure DBAT5
 +
// Physical address is 0
 +
// Virtual address is 0xF0000000
 +
// Mapped as read write, user valid and supervisor valid
 +
DBAT5L = 0x2
 +
DBAT5U = 0xF0000003
 +
 +
__isync();
 +
 +
index = 0xF0000100;
 +
size = 0x7;
 +
 +
// Test cache flushing
 +
while (size > 0)
 +
{
 +
    *(u32 *)index = 0x48000000;
 +
   
 +
    __dcbf(0, index);
 +
    __isync();
 +
    __sync();
 +
   
 +
    if (*(u32 *)index != 0x48000000)
 +
    {
 +
        // Deadlock
 +
        sub_E0();
 +
    }
 +
       
 +
    index += 0x100;
 +
    size -= 0x1;
 +
}
 +
 +
// Clear DBAT5
 +
DBAT5L = 0
 +
DBAT5U = 0
 +
 +
__isync();
 +
 +
SRR0 = 0x758
 +
SRR1 = MSR
 +
 +
// Set LR to a deadlock
 +
LR = sub_E0
 +
 +
// Execution will jump to 0x758
 +
return;
   −
First, the MMU, caches, and registers are all cleared. The boot ROM is mapped into translated mode as well, and two other regions of memory are mapped. The boot ROM then switches to translated mode, and remains in execution due to it being mapped there.
+
== Main ==
 +
This is the main function of the Espresso boot ROM. It is located at address 0x00000758 (where the initialization routine jumps to) and is responsible for initializing registers and memory, processing the PPC ancast image and running it.
   −
At this point, the L1 and L2 caches are enabled, and the L1 cache is locked. From here, the reset vector is replaced with an infinite loop, and the L1 cache is directly initialized. The boot ROM is then copied into main memory, and the copy being flushed to L2. Now, the copy of the boot ROM is jumped to.
+
*(u32 *)0xC16FFFFC = 0;
 +
*(u32 *)0xC16FFFF8 = 0;
 +
*(u32 *)0xC16FFFF4 = 0;
 +
*(u32 *)0xC16FFFF0 = 0;
 +
 +
// Clear all GPRs and set the TOC and SDA addresses
 +
InitializeRegisters();
 +
 +
// Initialize and map virtual memory
 +
InitializeMemory();
 +
 +
// Load, verify and decrypt the ancast image
 +
u32 entry_addr = LoadImg();
 +
 +
// Cleanup and jump to the loaded image
 +
Finish(entry_addr);
 +
 +
// Execution will jump to a deadlock
 +
return;
   −
AES keys are now taken from [[Hardware/OTP|OTP]] before locking OTP, and the Ancast Image signature is validated, as well as the cipher text decrypted.
+
=== InitializeMemory ===
 +
=== LoadImg ===
 +
=== Finish ===
 +
The Espresso boot ROM cleans up and jumps to the entrypoint address returned by [[#LoadImg|LoadImg]] minus 4 bytes.
   −
The boot ROM now jumps back to its original location, and proceeds to clear the copy in RAM, as well as the L1 cache. The L1 cache is now disabled, and the memory previously used is now unmapped. The Ancast Header is now mapped into translated memory, and the L1 and L2 caches are reset.
+
// Load state from MSR
 
+
u32 state = MSR
Now, a special instruction to unmap the boot ROM is placed before the jumping point, and flushed to the L2 cache, and verified. The boot ROM then jumps to that instruction, allowing the code to begin executing.
+
 +
if (*(u32 *)0xE00007A0 != 1)
 +
    SCR &= 0xCFFFFFFF
 +
 +
// Clear 0x20000 region
 +
memset(0x20000, 0, 0x4000);
 +
 +
// Set to zero data cache for the 0x24000 region
 +
sub_5A4();
 +
 +
// Clear 0xE0000000 region
 +
memset(0xE0000000, 0, 0x4000);
 +
 +
// Invalidate cache for the 0xE0000000 region and clear HID2_LCE
 +
sub_638();
 +
 +
// Clear DBAT1, DBAT2, DBAT4 and IBAT4
 +
sub_660();
 +
 +
if (!IsVWii())
 +
{
 +
    // Configure DBAT3 and IBAT3 for WiiU mode
 +
    // Physical address is 0x8000000
 +
    // Virtual address is 0x8000000
 +
    // Mapped as read only, user valid, supervisor valid and block size is 2MB
 +
    sub_788();
 +
   
 +
    if (entry_addr != 0x8000100)
 +
    {
 +
        *(u32 *)0xC16FFFFC |= 0x31000000;
 +
        while (1);
 +
    }
 +
    else
 +
        state |= 0x40;
 +
}
 +
else
 +
{
 +
    // Configure DBAT3 and IBAT3 for vWii mode
 +
    // Physical address is 0x1000000
 +
    // Virtual address is 0x1000000
 +
    // Mapped as read only, user valid, supervisor valid and block size is 8MB
 +
    sub_7B4();
 +
   
 +
    if (entry_addr != 0x1330100)
 +
    {
 +
        *(u32 *)0xC16FFFFC |= 0x31000000;
 +
        while (1);
 +
    }
 +
}
 +
 +
// Store TB
 +
*(u32 *)0xC16FFFF0 = TB
 +
 +
// Clear TBL and TBU
 +
sub_1E8();
 +
 +
// Clear L2CR_L2TS and do a global invalidate
 +
sub_69C();
 +
 +
// Clear HID0_DCE and HID0_ICE
 +
HID0 &= 0xFFFF3FFF
 +
 +
__isync();
 +
 +
// Set L2CR_L2E and clear L2CR_L2I
 +
sub_7E0();
 +
 +
// Set HID0_DCE and HID0_ICE
 +
HID0 |= 0xFFFF3FFF
 +
 +
__isync();
 +
 +
// Set HID0_DCFI and HID0_ICFI 
 +
HID0 |= 0xC00
 +
 +
__isync();
 +
__sync();
 +
 
 +
// Write the bootrom disable instruction and flush the cache
 +
u32 index = ((entry_addr - 0x4) & 0xFFFFFFE0);
 +
*(u32 *)(entry_addr - 0x4) = 0x7C73EBA6;
 +
 
 +
__dcbf(0, index);
 +
__isync();
 +
__sync();
 +
 
 +
if (*(u32 *)(entry_addr - 0x4) != 0x7C73EBA6)
 +
{
 +
    *(u32 *)0xC16FFFFC |= 0x31000000;
 +
    while (1);
 +
}
 +
   
 +
*(u32 *)0xC16FFFFC |= 0x80000000;
 +
 +
SRR0 = (entry_addr - 0x4)
 +
SRR1 = state
 +
 +
// Copy SCR to R3 as it will be used by the bootrom disable instruction
 +
R3 = ((SCR | 0x80000000) & 0xBFFFFFFF)
 +
 +
// Execution will jump to entry_addr - 4
 +
return;

Navigation menu