PowerPC Tutorial

Previous Chapter

Chapter 31: Block Address Translation (BATs)

Section 1: Intro

When writing a program where you need to write the boot/reset code, you are responsible for creating up Virtual Memory, and how the Program should translate the Virtual addresses. Having a Program run entirely on Physical Memory is not feasible at all due to huge performance degrades. There are two ways to translate a Physical Address to Virtual Address.

BAT Registers are simpler and faster than Page Tables. Therefore we will discuss those first.

The BAT Registers are a set of SPR's that are responsible for taking a Virtual Address and translating (converting) it to its Physical Address. BAT stands for Block Address Translation. For *most* PowerPC CPUs, there are 8 total BAT registers. 4 for instructions and 4 for data. They are as follows...

IBATs are for instructions. DBATs are for data. Some PowerPC CPUs (i.e. Broadway) have 8 extra BATs for a total of 16. Each BAT is 64 bits (double-word) in length and is split into upper 32 bit and lower 32 bit portions. Any area or region of memory that a BAT is responsible for is known as a Block. For most PowerPC programs, the BATs are configured almost immediately after boot/reset.


Section 2: MSR Bits

Back in Chapter 17, we briefly covered some bits of the Machine State Register. We need to go over 3 more bits

PR aka Privilege Level; Bit 17
This bit sets the PowerPC CPU into User Mode or Supervisor Mode. When the bit is LOW, the CPU is in Supervisor Mode. This means it has access to ALL registers. This bit is important because there are supervisor mode bits in the BAT Registers that work in conjunction with this bit.

IR aka Instruction Address Translation; Bit 26
This bit (when HIGH) allows for the IBATs (and/or Page Tables) to be used for translation.

DR aka Data Address Translation; Bit 27
This bit (when HIGH) allows for the DBATs (and/or Page Tables) to be used for translation.

IMPORTANT FYI: After a boot/reset, the MSR will have all of its bits low except for the IP bit (bit 25).


Section 3: BAT Structure Upper 32 bits

Upper Portion:

BEPI: Block Effective Page Index. This is simply the beginning Virtual Address that you choose to use for the translation. The ending Virtual Address is determined by the memory block size in the BL field.

BL: Block Length. This sets the amount (block) of Memory that the BAT will cover using BEPI as the starting address. Use the bit map below to determine the size.

BL Bit Map:

Setting an invalid bit combo will corrupt the BAT and cause undefined behavior. If the exact size you want is not listed, then you need round up, or split the sizes via multiple BATs.

Vs: Supervisor Valid Bit. If you are in Supervisor Mode (Machine State Register PR Bit Low), this bit will determine whether or not you are allowed to access that memory.

Vp: User Valid Bit. If you are in User Mode (Machine State Register PR bit High), this bit will determine whether or not you are allowed to access that memory.

Cheat Sheet for Upper Portion of BAT if using Vs & Vp are *both* set high.
XXXXzzzz

XXXX = Upper 16 bits of the starting Virtual Address (i.e. 0x8000 represents 0x80000000)
zzzz = BL + Vs&Vp combo (whenever Vs and Vp are BOTH set high)

zzzz value map (assumes Vs and Vp are BOTH set high)

Let's go over an example. Let's once again revisit the Nintendo Wii. It has the physical addresses of...
0x00000000 thru 0x017FFFFF
0x10000000 thru 0x13FFFFFF

If we wanted to write a BAT covering 8MB starting at 0x01000000 (addresses 0x01000000 thru 0x017FFFFF), with Vs & Vp both being set high, what would be the BAT's upper 32-bit value?

It would be 0x810000FF.

Btw, here's a handy memory size cheat sheet to help calculate the zzzz values. Take the end address minus the start address, then add 1. All calculations are in Hex obviously..

Example: (0x80FFFFFF - 0x80000000) + 1 = 0x01000000 (size of 16MB; is zzzz value of 01FF)


Section 4: BAT Structure Lower 32 bits

BRPN: Block Register Page Number. This is the physical address that is used in conjunction to your Virtual Address, this must be a legit physical address.

W: Write Through. If this bit is high, any store operations to cached memory are also written to physical memory, think of this like a dcbst instruction. If a block of memory is Write-Through, using dcbst on said block is 100% useless. If the W bit is low, this is known as 'Write-Back'. In Write-Back mode, when store operations update cached memory, they do not instantly update physical memory.

I: Cache-Inhibited. If this bit is high, no cache-ing of any kind shall occur for the specified block of memory. Blocks of memory that include access to something such as an I/O device should have the I bit set high.

M: Memory coherence: When this bit is high, other devices or processors will be notified whenever the specified region of memory is accessed. Think of this like a "global broadcast". All broadcast-able instructions automatically broadcast.

G: Guarded: This bit should be high if there are missing gaps in the specified memory block. This bit should also be set high for any memory that is not "well behaved". "Not Well behaved" simply means if the block of memory includes access to something such as an I/O device. I'm not sure on this but you may also need the G bit high if you had to roundup the BL bits. That would technically cause gaps in the memory block. Also G bit should be set high if you want to stop out-of-order operations (i.e loads, fetches, etc). Fyi, stores on Broadway are never done out-of-order in relation to each other. Finally, Store Gathering is automatically disabled (regardless of SGE bit value in HID0) whenever G bit is high. Meaning the use of eieio after a store instruction on Guarded memory is 100% useless.

NOTE: Setting any of the WIMG bits high will degrade performance.

PP: Page Protection. Responsible for allowing access to the block of memory. Think of it like a firewall. See bit map below.

PP bit map:

If you are wanting a block of memory to allow full access in any circumstance, set both the Vs and Vp bits high in the upper portion of the BAT, and then set the PP bits to 10 for the lower portion.

Here's an example of a BAT with its lower portion using a physical start address 0x01000000 with WIMG all low, and PP bits set to 10. We will use the upper portion example from Section 2 that used Virtual Start Address 0x81000000. The BAT will have Vs+Vp high with PP as 10 which means both user and supervisor have read+write access to the block of memory.

BAT value: 0x810000FF 01000002

Virtual Start Address: 0x81000000
Block Length: 8MB
Vs and Vp high
Physical Start Address: 0x01000000
WIMG all low
PP = 10 (0x2); Read & Write

IMPORTANT NOTE's: The IBATs can NEVER have the W and/or G bit set high. Doing so will corrupt the IBAT. Undefined behavior will occur. Also, regarding DBATs, whenever I bit is high, there is zero need to set W bit high. Having both W and I bit high in a DBAT is considered as an invalid combo, and should not be used. If you want to set a DBAT to be a basic copy of Physical Memory (but with the ability to use it Virtually), set I and G bits high, with W low.


Section 5: Getting In & Out of Real Mode

You *should* always be in Real Mode when doing any modifications to the BATs. Also, interrupts *must* be disabled the entire time you are modifying the BATs.

Here is an example code snippet to go into Real Mode for the purpose of BAT modifications. This code is designed to run on a Nintendo Wii as the Virtual to Physical ptr changing uses known Wii BAT Mapping.
#Disable Int's in MSR
mfmsr r3
rlwinm r3, r3, 0, 17, 15
mtmsr r3

#Get Program Counter
bl get_pc
get_pc:
mflr r4

#Handy Assembler Directive
margin = real_mode - get_pc

#Place Real Mode ptr in srr0
addi r4, r4, margin
clrlwi r4, r4, 1 #Change address to physical
mtspr srr0, r4

#Place to-be-new MSR value in srr1
rlwinm r3, r3, 0, 28, 25 #Flip off IR and DR bits 
mtspr srr1, r3
rfi #Go into real mode

#Label for start of real mode
real_mode:

#Real mode code would sit here

And here's the an example code for going into Virtual Mode after completing BAT Modifications

bl get_pc #Get Program Counter
get_pc:
mflr r4

margin = virtual_mode - get_pc

mfmsr r3 #Get MSR
ori r3, r3, 0x8030 #Flip IR and DR bits high, turn back on Interrupts
mtspr srr1, r3 #Place updated MSR into srr1
addi r3, r4, margin #Points to instruction right after rfi
oris r3, r3, 0x8000 #Change address to Virtual, Wii virtual mem80 used for this example
mtspr srr0, r3 #Place virtual address into srr0
rfi #End real mode

virtual_mode:

Section 6: Invalidating BATs, Assembler Assistance

To modify a BAT, it MUST be invalidated first. When a PowerPC CPU is powered on, or experiences a reset, the BATs are in an unknown state. All BATs should be marked invalid before configuring them. To invalidate a BAT, both Vs and Vp bits of the Upper Portion must be set low. The simplest way to invalidate a BAT is to write zero to the upper 32 bits. Like this...

li rX, 0
mtspr ZZZU, rX #Copy rX to ZZZ Upper BAT

ZZZ = the SPR number of the BAT
U = reminding you the write needs to be done the the UPPER portion

The above code is assuming you are using the raw SPR number for the mtspr/mfspr instruction. There are simplified mnemonics available. Here are all the simplified mnemonic BAT-related instructions~

X = 0 thru 3

These simplified mnemonics only cover IBATs & DBATs 0 thru 3. If you are on a PowerPC system that uses the 8 extra BATS, you will need to use the standard mnemonic with the BAT's raw SPR Number. Here's a handy Assembler Directive that set all the BAT SPR's..

.set IBAT0U, 528
.set IBAT0L, 529
.set IBAT1U, 530
.set IBAT1L, 531
.set IBAT2U, 532
.set IBAT2L, 533
.set IBAT3U, 534
.set IBAT3L, 535
.set IBAT4U, 560
.set IBAT4L, 561
.set IBAT5U, 562
.set IBAT5L, 563
.set IBAT6U, 564
.set IBAT6L, 565
.set IBAT7U, 566
.set IBAT7L, 567
.set DBAT0U, 536
.set DBAT0L, 537
.set DBAT1U, 538
.set DBAT1L, 539
.set DBAT2U, 540
.set DBAT2L, 541
.set DBAT3U, 542
.set DBAT3L, 543
.set DBAT4U, 568
.set DBAT4L, 569
.set DBAT5U, 570
.set DBAT5L, 571
.set DBAT6U, 572
.set DBAT6L, 573
.set DBAT7U, 574
.set DBAT7L, 585

With the above assembler directive, you can then use a BAT instruction like so...

#Invalid DBAT7
li r3, 0
mtspr DBAT7U, r3

Any modification/invalidation to an IBAT requires an isync instruction AFTERWARDS (Reference: PowerPC Microprocessor Family: The Programming Environments, table 2-22 (page 2-42))

Any modification/invalidation to a DBAT requires isync instructions BEFORE and AFTER (Reference: PowerPC Microprocessor Family: The Programming Environments, table 2-23 (page 2-43))

If you are invalidating a group of IBATS consecutively, or group of DBATS consecutively, you do *NOT* need an isync after every single individual BAT invalidation instruction.

Example: Invalidate DBATS 2 & 3 only~

li r0, 0
isync
mtdbatu 2, r0
mtdbatu 3, r0
isync

Since IBAT invalidation doesn't require an isync beforehand, if you are writing a boot/reset sequence code, you can invalidate all BATs (both I & D) simply like this...

#Invalid IBATs first
li r0, 0
mtibatu 0, r0
mtibatu 1, r0
mtibatu 2, r0
mtibatu 3, r0
isync

#Now invalidate DBATs
mtdbatu 0, r0
mtdbatu 1, r0
mtdbatu 2, r0
mtdbatu 3, r0
isync

Section 7: Modifying BATs Correctly

Assuming you have invalidated a BAT correctly, you can now modify it. The Lower 32-bits of the BAT MUST ALWAYS BE WRITTEN FIRST! Another rule is that you can only modify one BAT at a time.

Example: Configure the 5th IBAT to have virtual address 0x80000000 represent physical address 0x00000000. 0x80000000 thru 0x80FFFFFF is 16MB in total size which is a perfect size match for setting the BL bits. Vs, Vp both High. PP = 10 (0x2). WIMG is all low.

#Pretend the IBAT was properly invalidated some time ago. Fyi, no isync required beforehand since this is an IBAT modification
#Set r0 as lower portion
li r0, 2

#Set r3 as upper portion
lis r3, 0x8000
ori r3, r3, 0x01FF

#Write lower portion
mtspr IBAT5L, r0

#Write upper portion
mtspr IBAT5U, r0

#isync required now
isync

One more example, set one BAT (DBAT2) to cover Virtual Uncached Mem2 (aka mem9). Mem9's full size is 64MB which is a perfect size match once again for the BL bits. Assume DBAT2 was properly invalidated beforehand.

#W = low
#I = high! (cache must be blocked)
#M = low
#G = high! (prevent out-of-order operations)
#PP = 10 (read & write)
lis r0, 0x1000
ori r0, r0, 0x002A

#Upper Portion config
#Virtual Start Addr = 0xD0000000
#Block size = 64MB
#Vs and Vp both set high
lis r3, 0xD000
ori r3, r3, 0x07FF

#Modify the BAT
isync
mtspr DBAT2L, r0
mtspr DBAT2L, r3
isync

Section 8: More Rules to Follow

You cannot have multiple BATs (for the BAT same type; instruction or data), that do a translation to the same physical address.

Example: You have IBAT0 translate 0x80000000 to physical 0x00000000, and IBAT6 translate 0x70000000 to physical 0x00000000. That's a no-no.

Also, you cannot have memory blocks overlap from multiple BATs (for the same BAT type: instruction or data).

Example: You have DBAT2 for 0x90000000 using block-length of 64MB (so this covers all of mem9 which is 0x90000000 thru 0x93FFFFFF. You then also have DBAT4 for 0x93000000 using block length of 16MB (so this covers just 0x93000000 thru 0x93FFFFFF). As a result memory addresses 0x93000000 thru 0x93FFFFFF are both part of DBAT2 and DBAT4 which is invalid.

Whenever BATs are incorrectly invalidated and/or modified, the processor may enter into what is known as a Checkstop Condition. In simple terms, the processor halts without any Exception Routine being taken. It's a hardware halt.


Section 9: Other Notes

All BAT M bits (for instruction fetching) can be overridden by setting bit 23 (IFEM) low on HID0.

Please note that the I bit setting in a BAT will override the ICE, DCE, ILOCK, & DLOCK bits of HID0!


Section 10: Example Full BAT Source

Below is a code snippet for properly setting up the BATs of the Nintendo Wii (Broadway). While this isn't suitable for general PPC applications, this will give you enough insight on how you can write your own BAT-config code for your PPC system.

#Directives
.set IBAT0U, 528
.set IBAT0L, 529
.set IBAT1U, 530
.set IBAT1L, 531
.set IBAT2U, 532
.set IBAT2L, 533
.set IBAT3U, 534
.set IBAT3L, 535
.set IBAT4U, 560
.set IBAT4L, 561
.set IBAT5U, 562
.set IBAT5L, 563
.set IBAT6U, 564
.set IBAT6L, 565
.set IBAT7U, 566
.set IBAT7L, 567
.set DBAT0U, 536
.set DBAT0L, 537
.set DBAT1U, 538
.set DBAT1L, 539
.set DBAT2U, 540
.set DBAT2L, 541
.set DBAT3U, 542
.set DBAT3L, 543
.set DBAT4U, 568
.set DBAT4L, 569
.set DBAT5U, 570
.set DBAT5L, 571
.set DBAT6U, 572
.set DBAT6L, 573
.set DBAT7U, 574
.set DBAT7L, 585

#Invalidate all BATs
li r0, 0
mtspr IBAT0U, r0
mtspr IBAT1U, r0
mtspr IBAT2U, r0
mtspr IBAT3U, r0
mtspr IBAT4U, r0
mtspr IBAT5U, r0
mtspr IBAT6U, r0
mtspr IBAT7U, r0
isync
mtspr DBAT0U, r0
mtspr DBAT1U, r0
mtspr DBAT2U, r0
mtspr DBAT3U, r0
mtspr DBAT4U, r0
mtspr DBAT5U, r0
mtspr DBAT6U, r0
mtspr DBAT7U, r0
isync

#Setup BATs, all BATs will be enabled read+write for both user+supervisor

#Setup IBATs for...
#Cached Mem80; 16MB; WIMG 0000
#Cached Mem81; 8MB; WIMG 0000
#Cached Mem9; 64MB; WIMG 0000
#Uncached Mem80 (MemC); 16MB; WIMG 0101
#Uncached Mem81 (MemC1); 8 MB; WIMG 0101
#Uncached Mem9 (MemD); 64MB; WIMG 0101

#Setup DBATs for...
#Cached Mem80; 16MB; WIMG 0000
#Cached Mem81; 8MB; WIMG 0000
#Cached Mem9; 64MB; WIMG 0000
#Uncached Mem80; Mem81; Mem9; Hardware Memory (all of MemC)); 256MB; WIMG 0101
#Guarded Locked Cache (MemE); 256KB; WIMG 0001

#First 16MB of Cache MEM1; WIMG 0000
lis r0, 0x8000
ori r0, r0, 0x01FF
li r3, 0x0002
mtspr IBAT0L, r3
mtspr IBAT0U, r0
isync
mtspr DBAT0L, r3
mtspr DBAT0U, r0
isync
   
#Second 8MB of Cache MEM1; WIMG 0000
lis r0, 0x8100
ori r0, r0, 0x00FF
#Change 0x00000002 to 0x01000002
oris r3, r3, 0x0100
mtspr IBAT2L, r3
mtspr IBAT2U, r0
isync
mtspr DBAT2L, r3
mtspr DBAT2U, r0
isync
   
#64MB of Cache MEM2; WIMG 0000
lis r0, 0x9000
ori r0, r0, 0x07FF
lis r3, 0x1000
ori r3, r3, 0x0002
mtspr IBAT4L, r3
mtspr IBAT4U, r0
isync
mtspr DBAT4L, r3
mtspr DBAT4U, r0
isync
   
#64MB of Uncache MEM2; WIMG 0101 (Cache Inhibited, Guarded)
#Change 0x900007FF to 0xD00007FF
oris r0, r0, 0xD000
ori r3, r3, 0x002A
mtspr DBAT5L, r3
mtspr DBAT5U, r0
isync
   
#Uncache MEM1 & Hardware Memory; WIMG 0101 (Cache Inhibited, Guarded)
#Lower 32-bits already present in r3 (0x0000002A)
lis r0, 0xC000
ori r0, r0, 0x1FFF
li r3, 0x002A
mtspr DBAT1L, r3
mtspr DBAT1U, r0
isync
   
#DBAT for the Locked Cache (identical translation used)
lis r0, 0xE000 #Virtual Addr is same as Physical Addr
ori r0, r0, 0x0003 #Locked Cache is 16KB in size; set usage by both User and Supervisor
lis r3, 0xE000
ori r3, r3, 0x0002
mtspr DBAT3L, r3
mtspr DBAT3U, r0
isync

Next Chapter

Tutorial Index