The Cell architecture uses a technique known as Logical Partioning to execute and isolate different OS regions to prevent them from interfering with one another. Testing using Hypervisor calls such as lv1_allocate_memory and lv1_map_device_mmio_region has revealed the algorithm used to calculate the addresses of logical partition regions. This knowledge could prove useful when examining regions allocated by the Hypervisor as the logical partition address can be used to determine the size of the allocated region and indicate with respect to the uptime of the OS as to when the allocation took place. More information on the lpar is also available from lv1_query_logical_partition_address_region_info.
A logical partition address is derived from the requested region size and a counter that is incremented with each allocation. The address itself comprises two parts, the Base and the Counter.
Address = 0xbbccccccc000, where bb = Base and ccccccc = Counter
Allocating consecutive regions of the same size returns a consistent pattern of addresses - each is offset from the previous by the size of the region rounded up to the nearest power of two. Furthermore, allocating regions of differing sizes results in addresses that indicate that the allocation algorithm is based upon an incrementing counter multiplied by the rounded region size. This counter is common for all allocations, independent of their size.
Addresses are not (initially) reused, as freeing a region before the next allocation of the same size returns the next address in the sequence. Interestingly, when the upper limit of an address region is reached (e.g. 0x33fffffff000 for 0×1000 byte allocations) the address rolls back to the initial base address (e.g. 0×300000000000), though the addresses of any regions that have not been released are NOT reused, suggesting that the algorithm also keeps track of allocated regions.
The address base is determined by the size of the allocated region:
| Region Size | Base |
|---|---|
| 0×1000 | 0×30 - 0×33 |
| 0×2000 | 0×34 - 0×37 |
| 0×3000 - 0×4000 | 0×38 - 0x3b |
| 0×5000 - 0×8000 | 0x3c - 0x3f |
| 0×9000 - 0×10000 | 0×40 - 0×43 |
| 0×11000 - 0×20000 | 0×44 - 0×47 |
| 0×21000 - 0×40000 | 0×48 - 0x4b |
| 0×41000 - 0×80000 | 0x4c - 0x4f |
| 0×81000 - 0×100000 | 0×50 - 0×53 |
| 0×101000 - 0×200000 | 0×54 - 0×57 |
| 0×201000 - 0×400000 | 0×58 - 0x5b |
| 0×401000 - 0×800000 | 0x5c - 0x5f |
| 0×801000 - 0×1000000 | 0×60 - 0×63 |
| 0×1001000 - 0×2000000 | 0×64 - 0×67 |
| 0×2001000 - 0×4000000 | 0×68 - 0x6b |
| 0×4001000 - 0×8000000 | 0x6c - 0x6f |
| 0×8001000 - 0×10000000 | 0×70 - 0×73 |
The allocator also appears to be memory agnostic - addresses for DDR allocation are in sequence with XDR allocations.
Here is a simple calculator that can convert between size and counter values to addresses and vice versa.
Note that the calculator does not perform any error checking for invalid values and will generate incorrect values for rolled over addresses (though is not very likely that these will occur in regular usage). The code is slightly convoluted as Javascript does not handle 64 bit numbers well.
The value of the allocation counter on a fully boot Linux system is relatively small (depending on which drivers are enabled). By constructing lpars for counter value from 0 to current, and for each power of two of region size (12-28), and calling lv1_query_logical_partition_address_region_info on the contructed lpar, it is possible to determine all the lpars allocated so far by the hypervisor and their characteritics. Indeed, calls to lv1_query_logical_partition_address_region_info on unallocated or freed lpars will return LV1_NO_ENTRY (-6).
This algorithm was used a minimalistic Linux system, with network only and hotplug memory restricted to 96MB. Then device drivers were loaded one at a time (USB, storage, lpm, sound, graphics). It reveals the following lpars are used:
| order | lpar/start | size | rights | page size | flag | read access time | comments |
|---|---|---|---|---|---|---|---|
| 0 (-) | 0×000000000000 | 134217728 (128MB) | 3 (RW) | 27 (128MB) | 0×8 | 10 | real memory |
| 27 (128MB) | 0x6c0058000000 | 100663296 (96MB) | 3 (RW) | 24 (16MB) | 0×4 | 10 | hotplug memory |
| 19 (512kB) | 0x4c0000600000 | 524288 (512kB) | 3 (RW) | 12 (4kB) | 0xa000000000000000 | 220 | SPU0 local store, problem state and privilege 2 region |
| 12 (4kB) | 0x30000000d000 | 4096 (4kB) | 1 (RO) | 12 (4kB) | 0xa000000000000000 | 10 | SPU0 shadow registers |
| 19 (512kB) | 0x4c0000700000 | 524288 (512kB) | 3 (RW) | 12 (4kB) | 0xa000000000000000 | 218 | SPU1 local store, problem state and privilege 2 region |
| 12 (4kB) | 0x30000000f000 | 4096 (4kB) | 1 (RO) | 12 (4kB) | 0xa000000000000000 | 10 | SPU1 shadow registers |
| 19 (512kB) | 0x4c0000800000 | 524288 (512kB) | 3 (RW) | 12 (4kB) | 0xa000000000000000 | 216 | SPU2 local store, problem state and privilege 2 region |
| 12 (4kB) | 0×300000011000 | 4096 (4kB) | 1 (RO) | 12 (4kB) | 0xa000000000000000 | 10 | SPU2 shadow registers |
| 19 (512kB) | 0x4c0000900000 | 524288 (512kB) | 3 (RW) | 12 (4kB) | 0xa000000000000000 | 212 | SPU3 local store, problem state and privilege 2 region |
| 12 (4kB) | 0×300000013000 | 4096 (4kB) | 1 (RO) | 12 (4kB) | 0xa000000000000000 | 10 | SPU3 shadow registers |
| 19 (512kB) | 0x4c0000a00000 | 524288 (512kB) | 3 (RW) | 12 (4kB) | 0xa000000000000000 | 214 | SPU4 local store, problem state and privilege 2 region |
| 12 (4kB) | 0×300000015000 | 4096 (4kB) | 1 (RO) | 12 (4kB) | 0xa000000000000000 | 10 | SPU4 shadow registers |
| 19 (512kB) | 0x4c0000b00000 | 524288 (512kB) | 3 (RW) | 12 (4kB) | 0xa000000000000000 | 216 | SPU5 local store, problem state and privilege 2 region |
| 12 (4kB) | 0×300000017000 | 4096 (4kB) | 1 (RO) | 12 (4kB) | 0xa000000000000000 | 10 | SPU5 shadow registers |
| 16 (64kB) | 0×400000300000 | 65536 (64kB) | 3 (RW) | 12 (4kB) | 0×8000000000000000 | 1693 | USB EHCI-0 MMIO (created by lv1_map_device_mmio_region) |
| 16 (64kB) | 0×400000310000 | 65536 (64kB) | 3 (RW) | 12 (4kB) | 0×8000000000000000 | 1693 | USB EHCI-1 MMIO (created by lv1_map_device_mmio_region) |
| 16 (64kB) | 0x4000003e0000 | 65536 (64kB) | 3 (RW) | 12 (4kB) | 0×8000000000000000 | 2399 | USB OHCI-0 MMIO (created by lv1_map_device_mmio_region) |
| 16 (64kB) | 0x4000003f0000 | 65536 (64kB) | 3 (RW) | 12 (4kB) | 0×8000000000000000 | 2398 | USB OHCI-1 MMIO (created by lv1_map_device_mmio_region) |
| 28 (256MB) | 0x700ae0000000 | 266338304 (254MB) | 3 (RW) | 20 (1MB) | 0×8000000000000000 | 1571 | GPU video RAM (created by lv1_gpu_open) |
| 16 (64kB) | 0x400000af0000 | 49152 (48kB) | 3 (RW) | 12 (4kB) | 0×8000000000000000 | 10 | GPU driver info region, see also lv1_gpu_context_allocate (created by lv1_gpu_open) |
| 18 (256kB) | 0x480002c00000 | 262144 (256kB) | 3 (RW) | 12 (4kB) | 0×8000000000000000 | 1571 | GPU reports region in video RAM, see also lv1_gpu_context_allocate (created by lv1_gpu_open) |
| 17 (128kB) | 0×440001620000 | 131072 (128kB) | 3 (RW) | 12 (4kB) | 0×8000000000000000 | 951 | GPU FIFO control registers, see also lv1_gpu_context_allocate (created by lv1_gpu_open) |
| 15 (32kB) | 0x3c00001d0000 | 32768 (32kB) | 3 (RW) | 12 (4kB) | 0×8000000000000000 | 1015 | audio registers (created by lv1_gpu_device_map) |
The SPU shadow registers are accessed read-only by the Linux kernel, and the lpar access_right have a value of 1 instead of 3 for all other lpars tested. It suggests the access_right field is a bitfield, with bit 0 for read-access and bit 1 for write-access.
Read access time is in CPU cycles (from mftb() * 40), measured on the first dword of the lpar, averaged over 1 million loops.
Allocating via lv1_allocate_memory creates additional entries, with flags reflecting the flags parameter, though only flags 2**0-3 are accepted for this call.