UBRX - L2 cache as instruction RAM (CAiR)

Word of advice: if you want to play with CPU caches, and especially an L2 cache, don't use a Slot 1 Pentium III as your test system: L2 is disabled by default there and, because it's really some fast RAM that was added on the board that also has the CPU chip, initializing it take a little more effort than simply flipping a switch... If you want to know all about how to initialize L2 cache on Slot 1 Pentiums, you may want to have a look at the old freebios code.

But now, with L2 sorted out and as a famous robot once said: "Things are starting to look up..."

Why is L2 cache so important for UBRX you ask? Good question.
You see, our goal is to run binary code that was uploaded by the user, on a system that is considered RAM-less, therefore all we have at our disposal are the caches. Now, CAR (Cache As RAM) has been in use by coreboot for some time, but the problem with this implementation is that it only uses L1 cache. However, if you know your CPU architecture 101, you are aware that there are two L1 caches ondie: one for data and another for instructions, with the instruction one being read-only. Thus, the CAR setup method from coreboot only provides access to the L1 data cache, not the instruction one, so we can't simply upload our code into L1-Data and expect it to run.

On the other hand, the L2 cache is a unified one, which means that it works for both data and instruction. Thus, if we manage to get our code onto L2, and have all the caches in WriteBack mode, we should be able to get the CPU to fetch instructions, which we uploaded, from L2, and we're good. This is called Cache As instruction RAM (CAiR). And with L2 caches being more than 256 KB in size, we could actually run a hefty and quite complex section of code, rather than be limited to the 16 or 32 KB of L1.

To achieve that, simply upload the code you want into L1-Data (which would have been initialized as CAR), then read or write a contiguous section of data, from a different address, that is larger than your L1 cache. As L1-Data gets replaced, your code gets pushed onto L2, where it is not accessible for execution by the CPU.

So, how does it work in the UBRX console? Like this:
s/u/r/q> s
$60000010 a             # disable cache
$11e c $0 d $01043531 m # setup L2 for PIII Slot 1
$2ff c $0 d $c00 m      # fixed + var MTRRs
$268 c $06060606 d m    # C0000-C7FFF as WriteBack
! $10 a                 # flush and enable cache
$8000 c $c0000 <        # preload region to L2-Unified
# load our code in L1-Data
$c0000 d $f8ba68b0 z    # 'h'
$c0004 d $65b0ee03 z    # 'e'
$c0008 d $ee03f8ba z
$c000c d $f8ba6cb0 z    # 'l'
$c0010 d $6cb0ee03 z    # 'l'
$c0014 d $ee03f8ba z
$c0018 d $f8ba6fb0 z    # 'o'
$c001c d $0db0ee03 z    # CR
$c0020 d $ee03f8ba z
$c0024 d $f8ba0ab0 z    # LF
$c0028 d $ffcbee03 z
$8000 c $c0030 >        # flush L1-Data onto L2-Unified

$c0800 b $c0000         # stack at C8000, code at C0000
s/u/r/q> r
Now, by flashing less than 4KB of your BIOS bootblock, you are able to run ANY code you want using the UBRX the recovery console, even if you don't have any RAM installed, with the added benefit that your code can be as large as your L2 cache. Neat!

All of this and more in UBRX v0.4.

No comments:

Post a Comment