HV Syscall Reference: Difference between revisions
m (Upto syscall 212 ported) |
m (Upto syscall 221 ported) |
||
Line 4,942: | Line 4,942: | ||
dev_id 8 appears to be a mapping of video RAM at offset 0x0ff10000. This region of video memory is referenced by RSX DMA objects but its purpose is unknown. | dev_id 8 appears to be a mapping of video RAM at offset 0x0ff10000. This region of video memory is referenced by RSX DMA objects but its purpose is unknown. | ||
---- | ---- | ||
=== lv1_gpu_device_unmap === | |||
Unmaps the device from logical address space. | |||
===== kboot Call ===== | |||
lv1_gpu_device_unmap( /*IN*/ dev_id ); | |||
===== Parameters ===== | |||
{| class="wikitable" | |||
|- | |||
! colspan="2" | Inputs | |||
|- | |||
!Register | |||
!Description | |||
|- | |||
|R3 | |||
|dev_id - device id (see [[HV_Syscall_Reference#lv1_gpu_device_map|lv1_gpu_device_map]]) | |||
|- | |||
! colspan="2" | Outputs | |||
|- | |||
!Register | |||
!Description | |||
|- | |||
|R3 | |||
|Status? | |||
|- | |||
|} | |||
Notes: | |||
Info taken from kboot-10\dl\linux-2.6.16\sound\powerpc\snd_ps3pf.c (kboot-20061208) | |||
---- | |||
=== lv1_gpu_memory_allocate === | |||
Allocate GPU memory. | |||
===== Kernel Call ===== | |||
status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0, &memory_handle, &ddr_lpar); | |||
===== Parameters ===== | |||
{| class="wikitable" | |||
|- | |||
! colspan="2" | Inputs | |||
|- | |||
!Register | |||
!Description | |||
|- | |||
|R3 | |||
|ddr_size - amount of DDR to reserve? (see notes) | |||
|- | |||
|R4 | |||
|0 - Unknown. Max value 512*1024. | |||
|- | |||
|R5 | |||
|0 - Unknown. Max value 3075*1024. | |||
|- | |||
|R6 | |||
|0 - Unknown. Max value 15. | |||
|- | |||
|R7 | |||
|0 - Unknown. Max value 8. | |||
|- | |||
! colspan="2" | Outputs | |||
|- | |||
!Register | |||
!Description | |||
|- | |||
|R3 | |||
|status: 0 = LV1_SUCCESS, -17 = LV1_ILLEGAL_PARAMETER_VALUE | |||
|- | |||
|R4 | |||
|memory_handle - used by [[HV_Syscall_Reference#lv1_gpu_context_allocate|lv1_gpu_context_allocate]] and [[HV_Syscall_Reference#lv1_gpu_memory_free|lv1_gpu_memory_free]]?? | |||
|- | |||
|R5 | |||
|ddr_lpar - remappable address of allocated video memory, unused by Kernel. | |||
|- | |||
|} | |||
Notes: | |||
ddr_size accepts values (0..252) * 2^20 - values not divisible by 2^20 (1 MB) and above the range result in a return value of -17 (LV1_ILLEGAL_PARAMETER_VALUE). [Verified by Strontium Dog on an AU PS3 V1.5 firmware] | |||
Bits 52-63 of ddr_size seem to be ignored or correspond to flags. The lower bits correspond to the amount of allocated video RAM. A call to [[HV_Syscall_Reference#lv1_gpu_context_allocate|lv1_gpu_context_allocate]] using the returned memory_handle will create a RSX DMA object handle 0xfeed0000 corresponding to the region of allocated memory. Multiple calls to [[HV_Syscall_Reference#lv1_gpu_memory_allocate|lv1_gpu_memory_allocate]] with non-zero ddr_size will change the start of this region. The DMA object limit is set to ddr_size - 1. Before FW 2.1, a ddr_size of 0 was accepted, in which case a DMA object starting at zero and of limit 0xffffffff was created. Note that in this case, the start of this region is always zero even if previous calls to [[HV_Syscall_Reference#lv1_gpu_memory_allocate|lv1_gpu_memory_allocate]] with non-zero ddr_size were performed. As of FW 2.1 and above, a zero ddr_size is not accepted anymore. | |||
Parameters r4-r7 are unknown. Maximum values for these parameters are respectively 512kB, 3075kB, 15 and 8. They refer to shared scarce resources, as allocations are retained across multiple calls to [[HV_Syscall_Reference#lv1_gpu_memory_allocate|lv1_gpu_memory_allocate]]. When attached to a context during [[HV_Syscall_Reference#lv1_gpu_context_allocate|lv1_gpu_context_allocate]], the values of these parameters are reported in the lpar_driver_info structure of the context. | |||
Cell separates multiple OS into Logical Partitions (lpar) described at http://research.scea.com/research/html/CellGDC05/13.html. PS3 GPU memory is referred to as DDR (or GDDR) whereas system memory is XDR. 256 MB of each are installed in PS3 though only XDR is currently available for use by OtherOS. | |||
To make use of the allocated DDR ddr_lpar needs to be transformed into a usable address using: | |||
ddr_address = ioremap_flags(ddr_lpar, ddr_size, _PAGE_NO_CACHE); | |||
Be aware that the memory that holds the physical GPU frame buffer is not allocated by the Kernel, just used. So on the first call to this, some or all of the memory you request (depending on now much you request) may be actually used as the frame buffer. You will know this, because your writes to memory will mysteriously disappear up to 20ms after you perform them. Note that direct access to video ram is very slow (~10MB/s). | |||
===== Test Results ===== | |||
{| class="wikitable" | |||
|- | |||
!Register | |||
!Hex | |||
!Decimal | |||
!Comments | |||
|- | |||
|R3 | |||
|0x000000000fc00000 | |||
|(264241152) | |||
|252 MB | |||
|- | |||
|R4 | |||
|0×0000000000000000 | |||
|(0) | |||
| | |||
|- | |||
|R5 | |||
|0×0000000000000000 | |||
|(0) | |||
| | |||
|- | |||
|R6 | |||
|0×0000000000000000 | |||
|(0) | |||
| | |||
|- | |||
|R7 | |||
|0×0000000000000000 | |||
|(0) | |||
| | |||
|- | |||
! colspan="4" | Outputs | |||
|- | |||
|R3 | |||
|0×0000000000000000 | |||
|(0) | |||
|LV1_SUCCESS | |||
|- | |||
|R4 | |||
|0x000000005a5a5a5b | |||
|(...) | |||
|memory handle | |||
|- | |||
|R5 | |||
|0x00007001a0000000 | |||
|(...) | |||
|ddr logical partition address | |||
|- | |||
|} | |||
---- | |||
=== lv1_gpu_memory_free === | |||
Free memory handle returned by [[HV_Syscall_Reference#lv1_gpu_memory_allocate|lv1_gpu_memory_allocate]]. Must be called to dispose of the handle returned by [[HV_Syscall_Reference#lv1_gpu_memory_allocate|lv1_gpu_memory_allocate]]. | |||
===== Kernel Call ===== | |||
status = lv1_gpu_memory_free(ps3fb.memory_handle); | |||
===== Parameters ===== | |||
{| class="wikitable" | |||
|- | |||
! colspan="2" | Inputs | |||
|- | |||
!Register | |||
!Description | |||
|- | |||
|R3 | |||
|Memory handle returned by [[HV_Syscall_Reference#lv1_gpu_memory_allocate|lv1_gpu_memory_allocate]] | |||
|- | |||
! colspan="2" | Outputs | |||
|- | |||
!Register | |||
!Description | |||
|- | |||
|R3 | |||
|Status - 0 = OK, Other values are unknown, but indicate failure. | |||
|- | |||
|} | |||
---- | |||
=== lv1_gpu_context_iomap === | |||
Map system RAM address to GPU through the Cell FlexIO interface. | |||
===== Kernel Call ===== | |||
status = lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, | |||
xdr_lpar, ps3fb_videomemory.size, 0); | |||
===== Parameters ===== | |||
{| class="wikitable" | |||
|- | |||
! colspan="2" | Inputs | |||
|- | |||
!Register | |||
!Description | |||
|- | |||
|R3 | |||
|ps3fb.context_handle as allocated by [[HV_Syscall_Reference#lv1_gpu_context_allocate|lv1_gpu_context_allocate]] | |||
|- | |||
|R4 | |||
|GPU_IOIF = 0x0d000000UL - GPU address where the system RAM virtual framebuffer is remapped | |||
|- | |||
|R5 | |||
|xdr_lpar - lpar version of the physical address of the virtual frame buffer in local memory. (Note: the lpar version = the physical address in the PS3) | |||
|- | |||
|R6 | |||
|ps3fb_videomemory.size = The size of the virtual frame buffer | |||
|- | |||
|R7 | |||
|0 - IOPTE flags - bitfield describing protection, coherency and ordering of the I/O mapping. Any combination of 0 or 2^{11 (cache hint),59 (read ordering),60 (write ordering),61 (coherency),62 (read protection) ,63 (write protection)} seems valid | |||
|- | |||
! colspan="2" | Outputs | |||
|- | |||
!Register | |||
!Description | |||
|- | |||
|R3 | |||
|Status - 0 = OK, LV1_TYPE_MISMATCH (-8) if R5 set to a DDR lpar address, LV1_ILLEGAL_PARAMETER_VALUE (-17) if any other bit set in R7 than described above | |||
|- | |||
|} | |||
This function creates a mapping in GPU address space so that the RSX can access system RAM. The Kernel uses it to associate the virtual framebuffer residing in system RAM to the GPU, so that so that transfers can be initiated by the RSX from the system RAM to the video RAM using the [[HV_Syscall_Reference#lv1_gpu_context_attribute|lv1_gpu_context_attribute]]:fb_blit() call. | |||
Comments: | |||
It was previously suspected that GPU_IOIF was the address of GPU MMIO registers. However GPU_IOIF is a GPU address, not a lpar address, and therefore has no meaning on the Linux side, and cannot be directly mapped from the Linux address space. Reserving the memory block (using request_mem_region) and mapping (using ioremap) results in a block of memory that is used by Linux (nothing resembling IO registers was observed whilst single frame stepping a gfx demo). [was: As you’ve previously discovered that the top of RAM is 0x0e00 0000, GPU_IOIF here is 16Mb below that. That’s typically the size of the a graphics card PCI IO region on a PC, so given the name, I’d strongly suggest it’s not GPU memory that’s being mapped but the GPU IO registers. Although why this address range would overlap with RAM is a mystery.] | |||
GPU_IOIF was successfully set to other values (0×00000000, 0×02000000, 0×04000000) with Linux booting and displaying correctly. A value of 0x0f000000 causes the PS3 to hang (need to retest 0x0e0000000) | |||
Although it looks like GPU_IOIF would overlap video RAM, the RSX differentiates between the two by associating different DMA objects to the source and destination of the blit. The source is associated with DMA object handle 0xfeed0001 which targets system memory, while the destination is associated with DMA object handle 0xfeed0000 which targets video memory. This has been observed by analysing the FIFO commands sent to the GPU by the hypervisor during the [[HV_Syscall_Reference#lv1_gpu_context_attribute|lv1_gpu_context_attribute]]:fb_setup() and [[HV_Syscall_Reference#lv1_gpu_context_attribute|lv1_gpu_context_attribute]]:fb_blit() calls. | |||
It seems a call to [[HV_Syscall_Reference#lv1_gpu_context_iomap|lv1_gpu_context_iomap]](handle, bus_addr, xdr_lpar, size, flags) is equivalent to a series of call to [[HV_Syscall_Reference#lv1_put_iopte|lv1_put_iopte]]: | |||
int i; | |||
int context_number; /* derived from handle, 1st allocated context 0, 2nd allocated context 1, etc... */ | |||
for (i = 0; i < size; i += 1024 * 1024) { | |||
lv1_put_iopte(0, /* IO ASID */ | |||
((0x08ULL + context_number) << 28) | bus_addr) + i, /* IO address */ | |||
xdr_lpar + i, /* Logical Partition address */ | |||
1, /* PS3_AUDIO_IOID, actually RSX IOID */ | |||
flags); | |||
} | |||
Tested by replacing the call to [[HV_Syscall_Reference#lv1_gpu_context_iomap|lv1_gpu_context_iomap]] with the code above in ps3fb.c. |
Revision as of 03:53, 19 May 2011
HV Syscalls
NOTE: Update with graf's work. It'll probably fill in some of the undocumented functions.
lv1_allocate_memory
Create a memory region in the Hypervisor Virtual Address Space (vas)
Kernel Call
result = lv1_allocate_memory( /*IN*/ size, page_size_exp, 0, flags, /*OUT*/ &addr, &muid );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | size - of the region to allocate, must be a multiple of page_size |
R4 | page_size_exp - where required page_size = 2 ^ page_size_exp |
R5 | 0 - Unknown, see notes |
R6 | flags - (from linux/include/asm-powerpc/lv1call.h) bit 63: transferability: TF_NO = 0×00, TF_YES = 0×01 |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, LV1_RESOURCE_SHORTAGE (-2), LV1_NO_ENTRY (-6), LV1_DUPLICATE_ENTRY (-7) |
R4 | addr - LPAR Address of region |
R5 | muid - Unknown, unused by Kernel |
Notes:
page_size_exp takes values of 12 (page_size = 4K) to 21 (page_size = 2M) before LV1_RESOURCE_SHORTAGE (-2) is returned under a fully booted Linux OS. Higher values (24, page_size = 16M) can be found in the actual kernel source and can presumably be made before the OS has fully booted. page_size_exp values below 12 cause a return status of LV1_ILLEGAL_PARAMETER_VALUE (-17).
Input R5 was speculated to be the initialization value for the allocated region, but appears not to be the case. Values other than 0 or 1 appear to return a status of LV1_NO_ENTRY (-6), though a valid value of page_size_exp appears to be checked first (-17 is returned for invalid values of page_size_exp, regardless of the value of R5).
Allocations with flags = 0×00, 0×01, 0×02, 0×03 and 0×04 were successful though the effects of the flags could not be tested at this point. Allocations with flags >= 0×400 return LV1_ILLEGAL_PARAMETER_VALUE.
Initial tests allocating memory with flags = 0×08 (ADDR_0, presumably request physical address rather than logical partition address) result in a status of LV1_DUPLICATE_ENTRY (-7). This and the previous return value of -6 suggest an association with a database of some kind (repository values or memory maps?). It appears that some form of allocation may be taking place as LV1_ILLEGAL_PARAMETER_VALUE and LV1_RESOURCE_SHORTAGE are reported for invalid input parameters, rather than LV1_DUPLICATE_ENTRY.
For all successful allocations so far, output muid (R5) = 1
lv1_write_htab_entry
Write an entry to the hash page table.
Kernel Call
result = lv1_write_htab_entry( /*IN*/ vas_id, slot, va, pa );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | vas_id - virtual address space id (0 for current) |
R4 | slot - table slot to write entry to |
R5 | va - first half of PTE |
R6 | pa - second half of PTE, except RPN is replaced with LPAR address |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_construct_virtual_address_space
Construct a PPE virtual address space.
Kernel Call
result = lv1_construct_virtual_address_space( /*IN*/ htab_size, number_of_sizes, page_sizes, /*OUT*/ &vas_id, &act_htab_size );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | htab_size - must be 18, 19 or 20 (256KB, 512KB or 1MB) |
R4 | number_of_sizes - How many page sizes are specified in page_sizes |
R5 | page_sizes - see notes |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | vas_id - virtual address space id |
R5 | act_htab_size - actual hash table size? |
Notes:
Page sizes are specified as the power of two for the desired sizes. Each power of two is stored as an 8 bit field in page_sizes, starting from the MSB.
The “pages_sizes” parameter is set in “mm.c” using the following function:
page_sizes = make_page_sizes(PAGE_SHIFT_16M, PAGE_SHIFT_64K); static unsigned long make_page_sizes(unsigned long a, unsigned long b) { return (a << 56) | (b << 48); }
lv1_invalidate_htab_entries
Not used in current kernel.
Abstract Call
result = lv1_invalidate_htab_entries( /*IN*/ p1, p2, p3, p4, p5 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
R5 | p3 - Unknown |
R6 | p4 - Unknown |
R7 | p5 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
lv1_get_virtual_address_space_id_of_ppe
Returns the virtual address space id of the PPE.
Kernel Call
result = lv1_get_virtual_address_space_id_of_ppe( /*IN*/ ppe_id , /*OUT*/ &vas_id );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | PPE id |
Outputs | |
Register | Description |
R3 | status: 0 = LV1_SUCCESS |
R4 | vas_id - virtual address space id of the PPE |
Notes:
Regardless of the ppe_id, when called from kernel module init function, vas_id always seems to be 11.
lv1_query_logical_partition_address_region_info
Retrieve address region information for the specified logical partition address region.
Kernel Call
result = lv1_query_logical_partition_address_region_info( /*IN*/ 0, /*OUT*/ &start_address, &size, &access_right, &max_page_size, &flags);
Parameters
Inputs | |
---|---|
Register | Description |
R3 | 0 - logical partition address region (lpar) |
Outputs | |
Register | Description |
R3 | status: 0 = LV1_SUCCESS |
R4 | start_address - start address of logical partition address region |
R5 | size - size of logical partition address region |
R6 | access_right - ? |
R7 | max_page_size - maximum page size of logical partition address region? or order of the allocation? |
R8 | flags - ? |
Notes:
Only the “max_page_size” parameter is currently used by the Kernel, in “mm.c”
Test Results
Register | Hex | Decimal | Comment |
---|---|---|---|
R3 | 0x00000000 | (0) | value does not seem to effect result |
Outputs | |||
R3 | 0×00000000 | (0) | LV1_SUCCESS |
R4 | 0×00000000 | (0) | start_address |
R5 | 0×08000000 | (134217728) | size - 128 Mb |
R6 | 0×00000003 | (3) | access_right |
R7 | 0x0000001b | (27) | max_page_size |
R8 | 0×00000008 | (8) | flags |
This suggests lpar 0 is a special lpar representing the first 128MB of RAM that are always available at boot time. In this case, max_page_size seems to correspond to the order of the allocation (2**27 = 128 MB). The meaning of access_right and flags is unknown.
Also works on a lpar obtained from lv1_allocate_memory, for example
lv1_allocate_memory(4096 /* size */, 12 /* page size */, 0, 0, &lpar, &muid); lv1_query_logical_partition_address_region_info(lpar, &start_address, &size, &access_right, &max_page_size, &flags);
returns:
Register | Hex | Decimal | Comment |
---|---|---|---|
R3 | 0x30000001f00 | (3298534891264) | lpar obtained from lv1_allocate_memory |
Outputs | |||
R3 | 0×00000000 | (0) | LV1_SUCCESS |
R4 | 0×30000001f00 | (0) | start_address (same as input lpar) |
R5 | 0×00001000 | (4096) | size - 4kB |
R6 | 0×00000003 | (3) | access_right |
R7 | 0x0000000c | (12) | max_page_size |
R8 | 0×00000000 | (0) | flags |
lv1_select_virtual_address_space
Select an alternative virtual address space.
Kernel Call
result = lv1_select_virtual_address_space( /*IN*/ vas_id );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | vas_id - virtual address space id |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
Notes:
In “mm.c” When destructing a virtual address space, a call to select address space 0 (default?) is performed first.
Calling lv1_select_virtual_address_space(0) from a kernel module init function causes the PS3 to hang.
lv1_undocumented_function_8
Returns current HV uptime. (NOTE: Use graf's work here)
lv1_pause
Called during the Kernel idle loop - puts the PPE thread into an inactive state.
Kernel Call
result = lv1_pause( /*IN*/ mode );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | mode: 0 = wake on DEC interrupt, 1 = ignore DEC interrupt |
Outputs | |
Register | Description |
R3 | status: 0 = LV1_SUCCESS, -17 = LV1_ILLEGAL_PARAMETER_VALUE |
Notes:
LV1_ILLEGAL_PARAMETER_VALUE is returned for values of “mode” other than 0 or 1.
Comment from setup.c
/* * lv1_pause() puts the PPE thread into inactive state until an * irq on an unmasked plug exists. MSR[EE] has no effect. * flags: 0 = wake on DEC interrupt, 1 = ignore DEC interrupt. */
lv1_destruct_virtual_address_space
Destruct a virtual address space.
Kernel Call
lv1_destruct_virtual_address_space( /*IN*/ vas_id );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | vas_id - virtual address space id |
Outputs | |
Register | Description |
R3 | Status? |
Notes:
Called with 0 in R3 crashes my PS3. Light turns red, appears to be off
lv1_configure_irq_state_bitmap
Register the address of a HV plug-outlet bitmap with the Hypervisor.
Kernel Call
result = lv1_configure_irq_state_bitmap( /*IN*/ ppe_id, cpu_id, bmp_addr );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | ppe_id - PPE id |
R4 | cpu_id - PPE CPU id |
R5 | bmp_addr - lpar address of state bitmap |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
Notes:
Comment from interrupt.c:
/** * The HV mantains per SMT thread mappings of HV outlet to HV plug on * behalf of the guest. These mappings are implemented as 256 bit guest * supplied bitmaps indexed by plug number. The addresses of the bitmaps * are registered with the HV through lv1_configure_irq_state_bitmap(). * The HV requires that the 512 bits of status + mask not cross a page * boundary. PS3_BMP_MINALIGN is used to define this minimal 64 byte * alignment. * * The HV supports 256 plugs per thread, assigned as {0..255}, for a total * of 512 plugs supported on a processor. To simplify the logic this * implementation equates HV plug value to Linux virq value, constrains each * interrupt to have a system wide unique plug number, and limits the range * of the plug values to map into the first dword of the bitmaps. This * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note * that there is no constraint on how many in this set an individual thread * can acquire. */
lv1_connect_irq_plug_ext
Connect a HV outlet to a CPU and virtual irq.
Kernel Call
result = lv1_connect_irq_plug_ext( /*IN*/ ppe_id, cpu_id, virq, outlet, 0 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | ppe_id - PPE id |
R4 | cpu_id - PPE CPU id |
R5 | virq - virtual irq |
R6 | outlet - HV outlet |
R7 | 0 - unknown |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_release_memory
Releases a previously allocated memory region. Return code is not checked.
Kernel Call
lv1_release_memory( /*IN*/ base );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | base - base address of memory region |
Outputs | |
Register | Description |
R3 | Status?? |
lv1_put_iopte
Put an io page table entry.
Kernel Call
result = lv1_put_iopte( /*IN*/ ioas_id, ioif_addr, lpar_addr, io_id, flags );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | ioas_id - io address space id |
R4 | ioif_addr - io interface address |
R5 | lpar_addr - logical partition address |
R6 | io_id - io id |
R7 | flags - see notes |
Outputs | |
Register | Description |
R3 | Status?? |
Notes:
Code taken from kboot-10\dl\linux-2.6.16\sound\powerpc\snd_ps3pf.c (kboot-20061208)
ret64 = lv1_put_iopte(0, /* io address space id */ ioif_map_info_array[current_segment].ioif_addr + current_page * IO_PAGESIZE, /* ioif addr */ p_to_lp(current_paddr), /* lpar addr */ PS3PF_AUDIO_IOID, IOPTE_READONLY | IOPTE_COHERENT | IOPTE_STRICT_ORDER);
lv1_disconnect_irq_plug_ext
Disconnect a virtual irq from its HV outlet.
Kernel Call
lv1_disconnect_irq_plug_ext( /*IN*/ ppe_id, cpu_id, virq );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | ppe_id - PPE id |
R4 | cpu_id - PPE CPU id |
R5 | virq - virtual irq |
R7 | flags - see notes |
Outputs | |
Register | Description |
R3 | Status? |
lv1_construct_event_receive_port
Creates an outlet that can be used with a virtual irq to receive system events.
Kernel Call
result = lv1_construct_event_receive_port( /*OUT*/ &outlet );
Parameters
Outputs | |
---|---|
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | outlet - event outlet |
lv1_destruct_event_receive_port
Destruct a previously constructed event receiving port.
Kernel Call
result = lv1_destruct_event_receive_port( /*IN*/ outlet );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | outlet - event outlet |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_send_event_locally
Signal the specified event.
Kernel Call
result = lv1_send_event_locally( /*IN*/ outlet );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | outlet - event outlet |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_detect_pending_interrupts
Not used in current kernel.
Abstract Call
result = lv1_detect_pending_interrupts( /*IN*/ p1, /*OUT*/ &v1, &v2, &v3, &v4 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
R5 | v2 - Unknown |
R6 | v3 - Unknown |
R7 | v4 - Unknown |
Notes:
Info taken from kboot-10\dl\linux-2.6.16\include\asm-powerpc\lv1calltab.h (kboot-20061208)
lv1_end_of_interrupt
Indicate the end of an interrupt handler has been reached.
kboot Call
result = lv1_end_of_interrupt( /*IN*/ irq );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | irq - interrupt number |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
Notes:
Comment in kboot-10\dl\linux-2.6.16\arch\powerpc\platforms\ps3pf\pic.c (kboot-20061208)
/* lv1_end_of_interrupt must be called at end_irq. Some lv1 drivers clear irq status in it. */
lv1_connect_irq_plug
Bind a virtual interrupt to a CPU.
kboot Call
result = lv1_connect_irq_plug( /*IN*/ virq, hwirq );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | virq - virtual interrupt |
R4 | hwirq - hardware interrupt |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
Notes:
Info taken from kboot-10/dl/linux-2.6.16/patches/cell-support/2.6.19-rc6-arnd1/ps3-support/ps3-interrupt.patch (kboot-20061208)
lv1_disconnect_irq_plug
Unbind a virtual interrupt from a CPU.
kboot Call
lv1_disconnect_irq_plug( /*IN*/ virq );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | virq - virtual interrupt |
Outputs | |
Register | Description |
R3 | Status? |
Notes:
Info taken from kboot-10/dl/linux-2.6.16/patches/cell-support/2.6.19-rc6-arnd1/ps3-support/ps3-interrupt.patch (kboot-20061208)
lv1_end_of_interrupt_ext
Indicate that the end of an interrupt handler has been reached.
Kernel Call
lv1_end_of_interrupt_ext( /*IN*/ ppe_id, cpu_id, virq );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | ppe_id - PPE id |
R4 | cpu_id - PPE CPU id |
R5 | virq - virtual irq |
Outputs | |
Register | Description |
R3 | Status? |
lv1_did_update_interrupt_mask
Indicate that CPU interrupt mask has been updated.
Kernel Call
lv1_did_update_interrupt_mask( /*IN*/ ppe_id, cpu_id );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | ppe_id - PPE id |
R4 | cpu_id - PPE CPU id |
Outputs | |
Register | Description |
R3 | Status? |
lv1_shutdown_logical_partition
Not used in current kernel.
Abstract Call
result = lv1_shutdown_logical_partition( /*IN*/ p1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - shutdown command (see notes) |
Outputs | |
Register | Description |
R3 | Status? |
Notes:
Comment from kboot-10/dl/linux-2.6.16/include/asm-powerpc/lv1call.h (kboot-20061208)
/* values for lv1_shutdown_logical_partition */ #define LV1_SHUTDOWN_LP_HALT 1 #define LV1_SHUTDOWN_LP_POWER_OFF 2 #define LV1_SHUTDOWN_LP_RESTART 3
lv1_destruct_logical_spe
Destructs a logical SPE.
Kernel Call
result = lv1_destruct_logical_spe( /*IN*/ spe_id );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | spe_id - spe id |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_construct_logical_spe
Constructs a logical SPE.
Kernel Call
status = lv1_construct_logical_spe( /*IN*/ PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, vas_id, SPE_TYPE_LOGICAL, /*OUT*/ &priv2_addr, &problem_phys, &local_store_phys, &unused, &shadow_addr, &spe_id );
Parameters
R3Inputs | |
---|---|
Register | Description |
R3 | PAGE_SHIFT |
R4 | PAGE_SHIFT |
R5 | PAGE_SHIFT |
R6 | PAGE_SHIFT |
R7 | PAGE_SHIFT |
R8 | vas_id - virtual address space id |
R9 | SPE_TYPE_LOGICAL (0) |
Outputs | |
Register | Description |
Status - 0 = OK, Other values are unknown, but indicate failure. | |
R4 | priv2_addr - lpar address of spe priv2 area |
R5 | problem_phys - lpar address of spu_problem area |
R6 | local_store_phys - lpar address of spu local storage |
R7 | unused |
R8 | shadow_addr - lpar address of spe register shadow area |
R9 | spe_id - logical spe id |
Notes:
R7 out parameter was referred to in a previous codebase as “ctxt_addr”.
Test Results
priv2_addr | problem_phys | local_store_phys | unused | shadow_addr | spe_id |
---|---|---|---|---|---|
0x4c0000660000 | 0x4c0000640000 | 0x4c0000600000 | 0×0 | 0x30000000d000 | 11 |
0x4c0000760000 | 0x4c0000740000 | 0x4c0000700000 | 0×0 | 0x30000000f000 | 15 |
0x4c0000860000 | 0x4c0000840000 | 0x4c0000800000 | 0×0 | 0×300000011000 | 19 |
0x4c0000960000 | 0x4c0000940000 | 0x4c0000900000 | 0×0 | 0×300000013000 | 23 |
0x4c0000a60000 | 0x4c0000a40000 | 0x4c0000a00000 | 0×0 | 0×300000015000 | 27 |
0x4c0000b60000 | 0x4c0000b40000 | 0x4c0000b00000 | 0×0 | 0×300000017000 | 31 |
For each entry, only two separate lpar are allocated by the hypervisor. Indeed, calling lv1_query_logical_partition_address_region_info on priv2_addr, problem_phys, local_store_phys and shadow_addr for the first entry returns the following info:
0x4c0000660000lpar | start_address | size | access_right | max_page_size | flags | comment |
---|---|---|---|---|---|---|
0x4c0000600000 | 524288 | 0×3 (RW) | 12 | 0xa000000000000000 | SPU MMIO lpar, privilege state 2 region (128kB) | |
0x4c0000640000 | 0x4c0000600000 | 524288 | 0×3 (RW) | 12 | 0xa000000000000000 | SPU MMIO lpar, problem state region (128kB) |
0x4c0000600000 | 0x4c0000600000 | 524288 | 0×3 (RW) | 12 | 0xa000000000000000 | SPU MMIO lpar, local store region (256kB) |
0x30000000d000 | 0x30000000d000 | 4096 | 0×1 (RO) | 12 | 0xa000000000000000 | SPU shadow registers lpar (4kB, read-only) |
From this info we see a single lpar is used for all the SPU MMIO region (see figure 5-1 CBE Memory Map of the Cell Broadband Engine Programming Handbook) and another lpar is used for the SPE registers.
lv1_set_spe_interrupt_mask
Set the interrupt mask of a specific spe.
Kernel Call
lv1_set_spe_interrupt_mask( /*IN*/ spe_id, class, mask );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | spe_id - spe id |
R4 | class - spe interrupt class |
R5 | mask - spe interrupt mask |
Outputs | |
Register | Description |
R3 | Status? |
lv1_undocumented_function_62
Exists in PAL 1.7; Returned -4 (LV1_DENIED_BY_POLICY) when passed 0 in R3 to R10.
lv1_undocumented_function_63
Does not exist in 3.15.
Exists in PAL 1.7; Returned -4 (LV1_DENIED_BY_POLICY) when passed 0 in R3 to R10.
lv1_set_spe_transition_notifier
Not used in current kernel.
Abstract Call
result = lv1_set_spe_transition_notifier( /*IN*/ p1, p2, p3 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | spe_id - logical spe id |
R4 | 0 - Unknown usage |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_disable_logical_spe
Disables a logical SPE.
Kernel Call
result = lv1_disable_logical_spe( /*IN*/ spe_id, 0 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | spe_id - logical spe id |
R4 | 0 - Unknown usage |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_clear_spe_interrupt_status
Clear the interrupt status of a specific spe.
Kernel Call
lv1_clear_spe_interrupt_status( /*IN*/ spe_id, class, stat, 0 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | spe_id - logical spe id |
R4 | class - spe interrupt class |
R5 | stat - new interrupt status? |
R6 | 0 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
lv1_get_spe_interrupt_status
Get the interrupt status of a specific spe.
Kernel Call
lv1_get_spe_interrupt_status( /*IN*/ spe_id, class, /*OUT*/ &stat );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | spe_id - logical spe id |
R4 | class - spe interrupt class |
Outputs | |
Register | Description |
R3 | Status? |
R4 | stat - interrupt status |
lv1_get_logical_ppe_id
Returns the logical PPE id.
Kernel Call
status = lv1_get_logical_ppe_id( /*OUT*/ &ppe_id );
Parameters
Outputs | |
---|---|
Register | Description |
R3 | Status? |
R4 | logical PPE id |
Notes:
When called from kernel module init function, ppe_id always seem to be 1.
lv1_set_interrupt_mask
Not used in current kernel.
Abstract Call
result = lv1_set_interrupt_mask( /*IN*/ p1, p2, p3, p4, p5 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
R5 | p3 - Unknown |
R6 | p4 - Unknown |
R7 | p5 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
lv1_get_logical_partition_id
Called during Kernel setup.
The single output parameter, logical partition id, is later used as a parameter to call other Hypervisor functions.
Kernel Call
result = lv1_get_logical_partition_id(/*OUT*/ &lp_id );
Parameters
Outputs | |
---|---|
register | Description |
R3 | result: 0 = LV1_SUCCESS |
R4 | lp_id - logical partition id |
Notes:
When called from kernel module init function, lp_id always seems to be 2.
The “read_node” function contained in “repository.c” is passed a parameter, lpar_id (logical partition id). If lpar_id is equal to PS3_LPAR_ID_CURRENT (0) then lv1_get_logical_partition_id is called to retrieve the current logical partition id. Any other value for lpar_id is passed directly to the following HV calls, though the only other value in use appears to be PS3_LPAR_ID_PME (1)
lv1_undocumented_function_75
Exists in PAL 1.7; Returned 0 (LV1_SUCCESS) when passed 0 in R3 to R10.
lv1_configure_execution_time_variable
Not used in current kernel.
Abstract Call
result = lv1_configure_execution_time_variable( /*IN*/ p1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
lv1_get_spe_irq_outlet
Get an IRQ outlet of a certain class from the specified SPE.
Kernel Call
result = lv1_get_spe_irq_outlet( /*IN*/ spe_id, class, /*OUT*/ &outlet );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | spe_id - logical spe id |
R4 | class - spe interrupt class (0, 1 or 2) |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | outlet - irq outlet |
SPE Interrupt Class:
Class | Description | Examples |
---|---|---|
0 | Errors | SPE errors, DMA errors, DMA alignment errors |
1 | DMA translation exceptions | MFC page faults, segment faults |
2 | Application events | SPE stop and signal, DMA completion interrupt, mailbox interrupts |
lv1_set_spe_privilege_state_area_1_register
Sets a register in SPE privilege area 1.
Kernel Call
lv1_set_spe_privilege_state_area_1_register( /*IN*/ spe_id, offset, value );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | spe_id - spe id |
R4 | offset - register offset, 0, 0x600, 0x710, 0x808, 0x820 |
R5 | value - register value |
Outputs | |
Register | Description |
R3 | Status? |
lv1_undocumented_function_89
Exists in PAL 1.7; Returned -6 (LV1_NO_ENTRY) when passed 0 in R3 to R10.
lv1_create_repository_node
The repository appears to be an area of storage for use by the Hypervisor.
Abstract Call
status = lv1_create_repository_node(/*IN*/ n1, n2, n3, n4, v1, v2 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | top level key |
R4 | 2nd level key |
R5 | 3rd level key |
R6 | 4th level key |
R7 | value 1 |
R8 | value 2 |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
Notes:
This call is not used within the current Kernel, so the parameter list is based on speculation. It will still return 0 if the specified node already exists, and it will not change current data.
See “lv1_get_repository_node_value” for an example of actual key/value usage within the Kernel.
lv1_get_repository_node_value
The repository appears to be an area of storage for use by the Hypervisor. 256-bit keys are used to reference 128-bit values stored in the repository. Functions exist to create, get, modify and remove repository nodes.
See notes below for usage speculation.
Kernel Call
status = lv1_get_repository_node_value(/*IN*/ lpar_id, n1, n2, n3, n4 /*OUT*/ &v1, &v2 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | logical partion id |
R4 | top level key |
R5 | 2nd level key |
R6 | 3rd level key |
R7 | 4th level key |
Outputs | |
Register | Description |
R3 | Status: 0 = OK, -6 = LV1_NO_ENTRY |
R4 | value 1 |
R5 | value 2 |
Notes:
The logical partition id is also required as an input parameter to get a repository node value, but this parameter does not appear to be passed when creating, modifying or removing nodes.
There are instances in the Kernel code where the keys are constructed from string values, which appear to follow a 4-level key/subkey hierarchy.
Test Results
Calling lv1_get_repository_node_value for the node ‘bi.pu.#1.rm_size’ produces the following result:
bi.pu.(1).rm_size - Real mode memory size | |||
---|---|---|---|
Register | Hex Value | Value | Comment |
R3 | 0000000000000002 | (2) | PS3_LPAR_ID_CURRENT |
R4 | 0000000062690000 | ....bi.. | boot info? |
R5 | 7075000000000000 | pu...... | processor unit |
R6 | 0000000000000001 | ........ | pu id (1) |
R7 | 726d5f73697a6500 | rm_size. | real mode size |
Outputs | |||
R3 | 0000000000000000 | (0) | LV1_SUCCESS |
R4 | 0000000008000000 | (134217728) | 128 Mb |
R5 | 0000000000000000 | (0) |
Other nodes referenced in “repository.c”:
lpar_id = PS3_LPAR_ID_CURRENT (0×2) | |||
---|---|---|---|
Node | v1 | v2 | Comment |
bi.spun.#0.#0 | 0000000000000006 (6) | 0 (0) | number of physical spus reserved |
bi.spursvn.#0.#0 | 0000000000000006 (6) | 0 (0) | number of spu resource reservations |
bi.spursv.(0..5).#0 | 8000000000000000 (PS3_SPU_RESOURCE_TYPE_EXCLUSIVE) | (0..5) | spu resource reservation id value |
bi.boot_dat.address.#0 | 0000000007fff000 (134213632) | 0 (0) | boot data address |
bi.boot_dat.size.#0 | 0000000000000800 (2048) | 0 (0) | boot data size |
bi.pu.(1).rm_size | 0000000008000000 (134217728) | 0 (0) | real mode limit |
bi.rgntotal.#0.#0 | 000000000f800000 (260046848) | 0 (0) | max memory size |
lpar_id = PS3_LPAR_ID_PME (0×1) | |||
---|---|---|---|
Node | v1 | v2 | Comment |
ben.#0.#0.#0 | 0000000000000001 (1) | 0000000000000000 (0) | Unused - in function “ps3_repository_read_num_be” |
be(0).#0.#0.#0 | 0000000000000000 (0) | 0000000000000000 (0) | be node id (used to retrieve clock freq) |
be.(0).clock.#0 | 0000000004c1a6c0 (79800000) | 0000000000000000 (0) | decrementer frequency (3.2 Ghz / 40) |
lpar_id = PS3_LPAR_ID_PME (0×1) - FW 3.15 PAL 60GB PS3, First Generation (Fat) | |||
---|---|---|---|
Node | v1 | v2 | Comment |
sys.flash.fmt.#0 | 00000000000001 (1) | 00000000000000 (0) | Flash format. v1: 1 = NAND , 2 = VFLASH? |
sys.hw.config.#0 | 20000000fffffeff | 00000000000000 (0) | 0×20000000000000 mask + something |
sys.flash.ext.#0 | 000000000000ff (255) | 00000000000000 (0) | – |
plat.id.#0.#0 | 436f6b4231300000 (ASCII‘CokB10’) | 00000000000000 (0) | Platform ID |
sys.ac.sd.#0 | 00000000000000 (0) | 00000000000000 (0) | – |
sys.flash.boot.#0 | 00000000000000 (0) | 00000000000000 (0) | – |
be.0.lpm.priv | 00000000000002 (2) | 00000000780101 (7864577) | – |
rsx.rdcy.1.#0 | ffff0d020a02ffff | 00000000000000 (0) | – |
rsx.rdcy.2.#0 | ffffffffffffffff | 00000000000000 (0) | – |
rsx.rdcy.3.#0 | 00000000000000 (0) | 00000000000000 (0) | – |
rsx.rdcy.4.#0 | 00000000000000 (0) | 00000000000000 (0) | – |
rsx.rdcy.5.#0 | 00000000000000 (0) | 00000000000000 (0) | – |
rsx.rdcy.6.#0 | 00000000000000 (0) | 00000000000000 (0) | – |
rsx.rdcy.7.#0 | 00000000000000 (0) | 00000000000000 (0) | – |
rsx.rdcy.8.#0 | 00000000000000 (0) | 00000000000000 (0) | – |
mu.1.size.#0 | 00000010000000 (268435456) | 00000000000000 (0) | – |
be.0.tb_clk.#0 | 00000004c1a6c0 (79800000) | 00000000000000 (0) | – |
be.0.nclk.#0 | 000000be420e00 (3192000000) | 00000000000000 (0) | – |
ios.net.eurus.lpar | 00000000000000 (0) | 00000000000000 (0) | – |
sys.syscon.pversion.#0 | 00000000000001 (1) | 00000000000000 (0) | – |
sys.param.load.rom1st | 00000000000001 (1) | 00000000000000 (0) | – |
lv1.maxplgid.#0.#0 | 00000000000100 (256) | 00000000000000 (0) | – |
lv1.specver.#0.#0 | 00000300010005 | 00000000000000 (0) | Matches firmware 3.1.5 |
lv1.buildid.#0.#0 | 00000000000000 (0) | 00000000000000 (0) | – |
lv1.ts.size.#0 | 00000000000000 (0) | 00000000000000 (0) | – |
lv1.ts.start.#0 | 00000010000000 (268435456) | 00000000000000 (0) | – |
lv1_modify_repository_node_value
The repository appears to be an area of storage for use by the Hypervisor.
Abstract Call
status = lv1_modify_repository_node_value(/*IN*/ n1, n2, n3, n4, v1, v2 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | top level key |
R4 | 2nd level key |
R5 | 3rd level key |
R6 | 4th level key |
R7 | value 1 |
R8 | value 2 |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
Parameter list based on speculation. Not used in current Kernel.
lv1_remove_repository_node
The repository appears to be an area of storage for use by the Hypervisor.
Abstract Call
status = lv1_remove_repository_node( /*IN*/ n1, n2, n3, n4 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | top level key |
R4 | 2nd level key |
R5 | 3rd level key |
R6 | 4th level key |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
Parameter list based on speculation. Not used in current Kernel.
lv1_read_htab_entries
Not used in current Kernel.
Abstract Call
result = lv1_read_htab_entries( /*IN*/ p1, p2, /*OUT*/ &v1, &v2, &v3, &v4, &v5 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - vas_id |
R4 | p2 - offset |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - unknown |
R5 | v2 - unknown |
R6 | v3 - unknown |
R7 | v4 - unknown |
R8 | v5 - unknown |
Notes: offset is 64-bit indexed. r4-r7 seem to be right from the htab. r8 is 16-bit indexed reading from something 0xFFFB is max offset
lv1_set_dabr
Sets dabr (data address breakpoint register) - an exception should be thrown upon access to data at this address (range?)
Kernel Call
result = lv1_set_dabr( /*IN*/ dabr, DABR_KERNEL | DABR_USER);
Parameters
Inputs | |
---|---|
Register | Description |
R3 | dabr - data address |
R4 | DABR_USER) - see notes |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
Notes:
DABR_KERNEL and DABR_USER are defined in “setup.c” as follows
enum {DABR_USER = 1, DABR_KERNEL = 2,};
lv1_set_vmx_graphics_mode
Set the Single Precision mode of the vmx graphics units in the PPU
Abstract Call
result = lv1_set_vmx_graphics_mode( mode );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | mode - 0 = IEEE 754-1985 SIMD rounding mode for VMX/Altivec Instructions |
mode - 1 = SPE-compatible SIMD graphics-rounding mode for VMX/Altivec Instructions | |
Outputs | |
Register | Description |
R3 | result: 0 = LV1_SUCCESS, -17 = LV1_ILLEGAL_PARAMETER_VALUE |
Notes:
This is a call to set the single precision rounding mode of the VMX units in the Cell PPUs. The AltiVec/VMX SIMD/Vector processing unit has 2 rounding modes for dealing with Single-precision floating-point. IEEE 754-1985 and SPE-compatible SIMD graphics-rounding mode.
The Cell Broadband Engine Programming Handbook has this to say on the subject: The first implementation of the Cell Broadband Engine Architecture (CBEA) (the CBE processor) supports instructions with a graphics rounding mode. This mode allows programs written with vector/SIMD multimedia extension instructions to produce floating-point results that are equivalent in precision to those written in the SPU instruction set. In this mode, as in the SPU environment, the default rounding mode is round to zero, denormals are treated as zero, and there are no infinities or NaNs.
To change this mode, bit 12 in the HID1 register (known as grap_md or grap_mode in various documents). HID1 is a HV privileged resource, hence to change the mode from Supervisor mode requires a HV call.
Reference Documents: Cell Broadband Engine Programming Handbook V1.1 Cell Broadband Engine Registers V1.5
Tests:
Info taken from kboot-10\dl\linux-2.6.16\include\asm-powerpc\lv1calltab.h (kboot-20061208)
When recompiled into Kernel module init function, accepts values of 0 and 1 for p1. All other values return -17 (LV1_ILLEGAL_PARAMETER_VALUE)
lv1_set_thread_switch_control_register
Not used in current Kernel.
Abstract Call
result = lv1_set_thread_switch_control_register( /*IN*/ p1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
Notes:
Info taken from kboot-10\dl\linux-2.6.16\include\asm-powerpc\lv1calltab.h (kboot-20061208)
lv1_undocumented_function_99
Exists in PAL 1.7; Returned -4 (LV1_DENIED_BY_POLICY) when passed 0 in R3 to R10.
lv1_undocumented_function_102
Exists in PAL 1.7; Returned 0 (LV1_SUCCESS) and R4 = 0x692F5D1E7h when passed 0 in R3 to R10.
lv1_get_total_execution_time
Not used in current kernel. Does not exist in 3.15 HV dump.
Abstract Call
result = lv1_get_total_execution_time( /*IN*/ p1, p2, /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
Notes:
I always get LV1_NOT_IMPLEMENTED
lv1_undocumented_function_105
Exists in PAL 1.7. Returns -17 (LV1_ILLEGAL_PARAMETER_VALUE, 0, 0, C000000000537EF8h, F09B89AF5001h, 6C0061D8E190h, C0000000008DF718h, F09B89AF5001h, when passed 0 in R3 to R10.
lv1_undocumented_function_106
Exists in PAL 1.7. Returns the same as lv1_undocumented_function_105.
lv1_undocumented_function_107
Exists in PAL 1.7. Returns the same as lv1_undocumented_function_105.
lv1_undocumented_function_108
Exists in PAL 1.7. Returns the same as lv1_undocumented_function_105.
lv1_undocumented_function_109
Exists in PAL 1.7. Returns the same as lv1_undocumented_function_105.
lv1_undocumented_function_110
Exists in PAL 1.7. Returns -6 (LV1_NO_ENTRY) in R3, rest same as lv1_undocumented_function_105.
lv1_undocumented_function_111
Exists in PAL 1.7. Returns -6 (LV1_NO_ENTRY) in R3, rest same as lv1_undocumented_function_105.
lv1_undocumented_function_112
Exists in PAL 1.7. Returns -6 (LV1_NO_ENTRY) in R3, rest same as lv1_undocumented_function_105.
lv1_undocumented_function_114
Exists in PAL 1.7. Returns -17 (LV1_ILLEGAL_PARAMETER_VALUE) in R3, 0 in R4-R10. Passed 0 in R3 to R10.
lv1_undocumented_function_115
Exists in PAL 1.7. Returns -8 (LV1_TYPE_MISMATCH) in R3, 0 in R4-R10. Passed 0 in R3 to R10.
lv1_allocate_io_segment
Allocate an io segment.
kboot Call
result = lv1_allocate_io_segment( /*IN*/ ioas_id, segment_size, io_page_size, /*OUT*/ &ioif_addr );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | ioas_id - io address space id |
R4 | segment_size - io segment size |
R5 | io_page_size - io page size, 0xC, 0×10, 0×14 |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | ioif_addr - io interface address |
Notes:
Code taken from kboot-10\dl\linux-2.6.16\sound\powerpc\snd_ps3pf.c (kboot-20061208)
ret64 = lv1_allocate_io_segment(0, /* io space */ IO_SEGMENTSIZE, /* segment size */ IO_PAGESIZE_SHIFT, /* io page size */ &(ioif_map_info_array[current_segment].ioif_addr));
lv1_release_io_segment
Release an io segment.
kboot Call
result = lv1_release_io_segment( /*IN*/ ioas_id, ioif_addr );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | ioas_id - io address space id |
R4 | ioif_addr - io interface address |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
Notes:
Code taken from kboot-10\dl\linux-2.6.16\sound\powerpc\snd_ps3pf.c (kboot-20061208)
ret64 = lv1_release_io_segment(0, /* io space */ ioif_map_info_array[current_segment].ioif_addr);
lv1_allocate_ioid
Not used in current kernel.
Abstract Call
result = lv1_allocate_ioid( /*IN*/ p1, /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | SBZ |
Outputs | |
Register | Description |
R3 | Status? |
R4 | the ioid |
Notes:
Info taken from kboot-10\dl\linux-2.6.16\include\asm-powerpc\lv1calltab.h (kboot-20061208)
lv1_release_ioid
Not used in current Kernel.
Abstract Call
result = lv1_release_ioid( /*IN*/ p1, p2 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
Notes:
Info taken from kboot-10\dl\linux-2.6.16\include\asm-powerpc\lv1calltab.h (kboot-20061208)
lv1_construct_io_irq_outlet
Construct an outlet for a non-virtualized device interrupt.
Kernel Call
result = lv1_construct_io_irq_outlet( /*IN*/ interrupt_id, /*OUT*/ &outlet );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | interrupt_id - interrupt id |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | outlet - interrupt outlet |
lv1_destruct_io_irq_outlet
Destruct a previously constructed device interrupt outlet.
Kernel Call
result = lv1_destruct_io_irq_outlet( /*IN*/ outlet );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | outlet - interrupt outlet |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_map_htab
Map the hash page table.
Kernel Call
result = lv1_map_htab( /*IN*/ 0, /*OUT*/ &htab_addr );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | 0 - Unknown (lpid?) |
Outputs | |
Register | Description |
R3 | Status? |
R4 | htab_addr - hash page table address |
Notes:
In “htab.c” return code is assigned, but not used. Must be translated and ioremapped before it can be used in the kernel. It’s 1MB long
lv1_unmap_htab
Unmap the hash page table.
Kernel Call
lv1_unmap_htab( /*IN*/ htab_addr );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | htab_addr - hash page table address |
Outputs | |
Register | Description |
R3 | Status? |
lv1_undocumented_function_124
Exists in PAL 1.7. Returns -6 (LV1_NO_ENTRY) in R3, rest same as lv1_undocumented_function_105.
lv1_undocumented_function_125
Exists in PAL 1.7. Returns -6 (LV1_NO_ENTRY) in R3, rest same as lv1_undocumented_function_105.
lv1_undocumented_function_126
Exists in PAL 1.7. Returns -6 (LV1_NO_ENTRY) in R3, rest same as lv1_undocumented_function_105.
lv1_get_version_info
Returns PS3 firmware version information.
Kernel Call
result = lv1_get_version_info( /*OUT*/ &raw );
Parameters
Outputs | |
---|---|
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | raw - firmware data (see notes) |
Notes:
The firmware information is accessed using the following union:
union ps3_firmware_version { u64 raw; struct { u16 pad; u16 major; u16 minor; u16 rev; }; };
lv1_undocumented_function_134
Exists in PAL 1.7. Returns 0 when passed R3-R10=0.
lv1_undocumented_function_135
Exists in PAL 1.7. Returns -6 (LV1_NO_ENTRY) in R3, rest same as lv1_undocumented_function_105.
lv1_undocumented_function_136
Exists in PAL 1.7. Returns -6 (LV1_NO_ENTRY) in R3, rest same as lv1_undocumented_function_105.
lv1_undocumented_function_137
Exists in PAL 1.7. Returns -4 (LV1_DENIED_BY_POLICY) when passed 0 in R3-R10.
lv1_undocumented_function_138
Exists in PAL 1.7. Returns -4 (LV1_DENIED_BY_POLICY) when passed 0 in R3-R10.
lv1_construct_lpm
Not used in current kernel.
Abstract Call
result = lv1_construct_lpm( /*IN*/ p1, p2, p3, p4, p5, p6, /*OUT*/ &v1, &v2, &v3 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - node_id, this is the node id of the processor, 0 is only valid value |
R4 | p2 - tb_type, 0 is none, 1 is internal |
R5 | p3 - Unknown, is 0 |
R6 | p4 - Unknown, is 0 |
R7 | p5 - tb_cache in lpar, 128 byte aligned |
R8 | p6 - tb_cache_size |
Outputs | |
Register | Description |
R3 | Status |
R4 | v1 - lpm_id |
R5 | v2 - outlet_id |
R6 | v3 - tb_size |
lv1_destruct_lpm
Not used in current kernel.
Abstract Call
result = lv1_destruct_lpm( /*IN*/ p1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - lpm_id |
Outputs | |
Register | Description |
R3 | Status? |
lv1_start_lpm
Not used in current kernel.
Abstract Call
result = lv1_start_lpm( /*IN*/ p1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - lpm_id |
Outputs | |
Register | Description |
R3 | Status? |
lv1_stop_lpm
Not used in current kernel.
Abstract Call
result = lv1_stop_lpm( /*IN*/ p1, /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
lv1_copy_lpm_trace_buffer
Not used in current kernel.
Abstract Call
result = lv1_copy_lpm_trace_buffer( /*IN*/ p1, p2, p3 /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - lpm_id |
R3 | p2 - offset |
R3 | p3 - request |
Outputs | |
Register | Description |
R3 | Status |
R4 | v1 - tmp |
lv1_add_lpm_event_bookmark
Not in current kernel.
Abstract Call
result = lv1_add_lpm_event_bookmark( /*IN*/ p1, p2, p3, p4, p5 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R3 | p2 - Unknown |
R3 | p3 - Unknown |
R6 | p4 - Unknown |
R7 | p5 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
lv1_delete_lpm_event_bookmark
Not used in current kernel.
Abstract Call
result = lv1_delete_lpm_event_bookmark( /*IN*/ p1, p2, p3 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R3 | p2 - Unknown |
R3 | p3 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
lv1_set_lpm_interrupt_mask
Not used in current kernel.
Abstract Call
result = lv1_set_lpm_interrupt_mask( /*IN*/ p1, p2, p3, /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R3 | p2 - Unknown |
R3 | p3 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
lv1_get_lpm_interrupt_status
Not used in current kernel.
Abstract Call
result = lv1_get_lpm_interrupt_status( /*IN*/ p1, /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
lv1_set_lpm_general_control
Not used in current kernel.
Abstract Call
result = lv1_set_lpm_general_control( /*IN*/ p1, p2, p3, p4, p5, /*OUT*/ &v1, &v2 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
R5 | p3 - Unknown |
R6 | p4 - Unknown |
R7 | p5 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
R5 | v2 - Unknown |
lv1_set_lpm_interval
Not used in current kernel.
Abstract Call
result = lv1_set_lpm_interval( /*IN*/ p1, p2, p3, /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
R5 | p3 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
lv1_set_lpm_trigger_control
Not used in current kernel.
Abstract Call
result = lv1_set_lpm_trigger_control( /*IN*/ p1, p2, p3, /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
R5 | p3 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
lv1_set_lpm_counter_control
Not used in current kernel.
Abstract Call
result = lv1_set_lpm_counter_control( /*IN*/ p1, p2, p3, p4, /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
R5 | p3 - Unknown |
R6 | p4 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
lv1_set_lpm_group_control
Not used in current kernel.
Abstract Call
result = lv1_set_lpm_group_control( /*IN*/ p1, p2, p3, /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
R5 | p3 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
lv1_set_lpm_debug_bus_control
Not used in current kernel.
Abstract Call
result = lv1_set_lpm_debug_bus_control( /*IN*/ p1, p2, p3, /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
R5 | p3 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
lv1_set_lpm_counter
Not used in current kernel.
Abstract Call
result = lv1_set_lpm_counter( /*IN*/ p1, p2, p3, p4, p5, /*OUT*/ &v1, &v2 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
R5 | p3 - Unknown |
R6 | p4 - Unknown |
R7 | p5 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
R5 | v2 - Unknown |
lv1_set_lpm_signal
Not used in current kernel.
Abstract Call
result = lv1_set_lpm_signal( /*IN*/ p1, p2, p3, p4, p5, p6, p7 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
R5 | p3 - Unknown |
R6 | p4 - Unknown |
R7 | p5 - Unknown |
R8 | p6 - Unknown |
R9 | p7 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
lv1_set_lpm_spr_trigger
Not used in current kernel.
Abstract Call
result = lv1_set_lpm_spr_trigger( /*IN*/ p1, p2 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
lv1_insert_htab_entry
Used in current kernel.
Kernel Call
result = lv1_insert_htab_entry( /*IN*/ p1, p2, p3, p4, p5, p6, /*OUT*/ &v1, &v2, &v3 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - htab ID, 0 or returns -6 |
R4 | p2 - hpte_group, 0 or returns -17 |
R5 | p3 - hpte_v |
R6 | p4 - hpte_r |
R7 | p5 - Bolted flag |
R8 | p6 - flags? |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - inserted_index |
R5 | v2 - evicted_v |
R6 | v3 - evicted_r |
Notes: Kernel usage:
result = lv1_insert_htab_entry(PS3_LPAR_VAS_ID_CURRENT, hpte_group, hpte_v, hpte_r, HPTE_V_BOLTED, 0, &inserted_index, &evicted_v, &evicted_r);
lv1_read_virtual_uart
Read data from a VUART port into a provided buffer.
Kernel Call
result = lv1_read_virtual_uart( /*IN*/ port_number, buffer, bytes, /*OUT*/ bytes_read );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | port_number - VUART port number |
R4 | buffer - pointer to buffer (must be address in lpar) |
R5 | bytes - buffer size? |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | bytes_read - number of bytes read into buffer |
lv1_write_virtual_uart
Write a buffer of data to a VUART port.
Kernel Call
result = lv1_write_virtual_uart( /*IN*/ port_number, buffer, bytes, /*OUT*/ bytes_written );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | port_number - VUART port number |
R4 | buffer - pointer to buffer (must be address in lpar) |
R5 | bytes - buffer size? |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | bytes_written - number of bytes written to VUART |
lv1_set_virtual_uart_param
Set a parameter for a port on the VUART.
Kernel Call
result = lv1_set_virtual_uart_param( /*IN*/ port_number, param_id, param_value );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | port_number - VUART port number |
R4 | param_id - id of parameter to set (see notes) |
R5 | param_value - parameter value |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
See lv1_get_virtual_uart_param for parameter information.
lv1_get_virtual_uart_param
Get a parameter for a port on the VUART.
Kernel Call
result = lv1_get_virtual_uart_param( /*IN*/ port_number, param_id, /*OUT*/ ¶m_value );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | port_number - VUART port number |
R4 | param_id - id of parameter to set (see notes) |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | param_value - parameter value |
VUART parameters defined in vuart.c:
Parameter | param_id | Notes |
---|---|---|
PARAM_TX_TRIGGER | 0 | |
PARAM_RX_TRIGGER | 1 | |
PARAM_INTERRUPT_MASK | 2 | |
PARAM_RX_BUF_SIZE | 3 | read only |
PARAM_RX_BYTES | 4 | read only |
PARAM_TX_BUF_SIZE | 5 | read only, not referenced in current Kernel |
PARAM_TX_BYTES | 6 | read only, not referenced in current Kernel |
PARAM_INTERRUPT_STATUS | 7 | read only |
lv1_configure_virtual_uart_irq
Configure the VUART IRQ.
Kernel Call
result = lv1_configure_virtual_uart_irq( /*IN*/ lpar_addr, /*OUT*/ &outlet );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | lpar_addr - logical partition address of virtual uart interrupt bitmap |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | outlet - irq outlet |
Notes:
Comment from interrupt.c:
/** * The system supports only a single virtual uart, so multiple calls without * freeing the interrupt will return a wrong state error. */
lv1_undocumented_function_167
Exists on PAL 1.7. Returns -4 (LV1_DENIED_BY_POLICY) when passed 0 in R3 to R10.
lv1_undocumented_function_168
Exists on PAL 1.7. Returns -4 (LV1_DENIED_BY_POLICY) when passed 0 in R3 to R10.
lv1_open_device
Open the device for a given bus and device id.
Kernel Call
result = lv1_open_device( /*IN*/ bus_id, dev_id, 0 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | 0 - Unknown |
Outputs | |
Register | Description |
R3 | Status: LV1_SUCCESS (0) - OK
LV1_NO_ENTRY (-6) - invalid dev_id LV1_BUSY (-9) - device already open LV1_ILLEGAL_PARAMETER_VALUE (-17) - invalid bus_id |
Notes:
Valid values for bus_id and dev_id can be obtained from the repository (see lv1_get_repository_node_value).
Once open, functions such as lv1_map_device_mmio_region and lv1_allocate_device_dma_region can be used to map the device into memory.
The value of R5 does not seem to affect the outcome of the call (powers of 2 in the 64 bit range were tested). One reason for the flag could be to indicate whether to open the device in shared mode or not (and thus prevent the LV1_BUSY return code).
lv1_close_device
Close the device for a given bus and device id.
Kernel Call
result = lv1_close_device( /*IN*/ bus_id, dev_id );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_map_device_mmio_region
Map an MMIO region to the specified device.
Kernel Call
result = lv1_map_device_mmio_region( /*IN*/ bus_id, dev_id, bus_addr, size, page_size, /*OUT*/ &lpar_addr );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | bus_addr - address of the region |
R6 | size - size of the region |
R7 | page_size - page size of the region |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | lpar_addr - logical partition address of the mapped region |
lv1_unmap_device_mmio_region
Unmap an MMIO region from the specified device.
Kernel Call
result = lv1_unmap_device_mmio_region( /*IN*/ bus_id, dev_id, lpar_addr );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | lpar_addr - logical partition address of the mapped region |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_allocate_device_dma_region
Allocate a DMA region for the specified device.
Kernel Call
result = lv1_allocate_device_dma_region( /*IN*/ bus_id, dev_id, io_size, io_pagesize, flag, &dma_region );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | io_size - region size |
R6 | io_pagesize - specified as a PAGE_SHIFT (i.e. n where pagesize = 2^n) |
R7 | flag - 0=32 bit mode, 2=8 bit mode |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | dma_region - dma region address |
Notes: When the device is little endian, the mode must be set to 8 bit for 8 bit DMA to work as expected, otherwise the bytes will be read/written in the wrong order. OTOH, this mode requires that 16 and 32-bit values are byte-swapped by the CPU since they will appear as little endian in memory.
lv1_free_device_dma_region
Free a previously allocated DMA region for the specified device.
Kernel Call
lv1_free_device_dma_region( /*IN*/ bus_id, dev_id, dma_region );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | dma_region - dma region address |
Outputs | |
Register | Description |
R3 | Status? |
lv1_map_device_dma_region
Map a DMA region for the specified device.
Kernel Call
result = lv1_map_device_dma_region( /*IN*/ bus_id, dev_id, lpar_addr, dma_region, size, flags );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | lpar_addr - logical partition address |
R6 | dma_region - dma region address |
R7 | size - region size |
R8 | flags - see notes |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
Notes:
In “mm.c” and “ps3_storage.c”, flags parameter is always specified as 0xf800000000000000UL. They are the same flags as the IOPTE entry ones.
lv1_unmap_device_dma_region
Unmap a DMA region for the specified device.
Kernel Call
result = lv1_unmap_device_dma_region( /*IN*/ bus_id, dev_id, dma_region, size );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | dma_region - dma region address |
R6 | size - region size |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_read_pci_config
Read external PCI config. PS3tool only?
kboot Call
result = lv1_read_pci_config( /*IN*/ bus_id, pci_bus_id, dev_id, func_id, offset, size, /*OUT*/ &config_data );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - ps3 bus id |
R4 | pci_bus_id - pci bus id |
R5 | dev_id - pci device id |
R6 | func_id - pci function |
R7 | offset - offset to store data within config_data |
R8 | size - size of config_data |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | config_data - pci config data |
Notes:
Code taken from kboot-10\dl\linux-2.6.16\arch\powerpc\platforms\ps3pf\pci.c (kboot-20061208)
status = lv1_read_pci_config(p->bus_id, p->bus_pci_bus, p->bus_pci_dev, p->bus_pci_func, (uint64_t)offset, size, &data);
lv1_write_pci_config
Write external PCI config. PS3tool only?
kboot Call
result = lv1_write_pci_config( /*IN*/ bus_id, pci_bus_id, dev_id, func_id, offset, size, config_data );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - ps3 bus id |
R4 | pci_bus_id - pci bus id |
R5 | dev_id - pci device id |
R6 | func_id - pci function |
R7 | offset - offset to store data within config_data |
R8 | size - size of config_data |
R9 | config_data - pci config data |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
Notes:
Code taken from kboot-10\dl\linux-2.6.16\arch\powerpc\platforms\ps3pf\pci.c (kboot-20061208)
status = lv1_write_pci_config(p->bus_id, p->bus_pci_bus, p->bus_pci_dev, p->bus_pci_func, (uint64_t)offset, size, (uint64_t)val); if(status) { /* lv1_write_pci_config can't write reg from 0x10 to 0x3f */ }
lv1_read_pci_io
Not used in current kernel.
Abstract Call
result = lv1_read_pci_io( /*IN*/ p1, p2, p3, p4, /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
R5 | p3 - Unknown |
R6 | p4 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
Notes:
Info taken from kboot-10\dl\linux-2.6.16\include\asm-powerpc\lv1calltab.h (kboot-20061208)
lv1_write_pci_io
Not used in current kernel.
Abstract Call
result = lv1_write_pci_io( /*IN*/ p1, p2, p3, p4, p5 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
R4 | p2 - Unknown |
R5 | p3 - Unknown |
R6 | p4 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
Notes:
Info taken from kboot-10\dl\linux-2.6.16\include\asm-powerpc\lv1calltab.h (kboot-20061208)
lv1_undocumented_function_182
Exists in PAL 3.15. Nothing known so far. Note: probably a function that allocates or frees memory.
lv1_undocumented_function_183
Exists in PAL 3.15. Nothing known so far. Note: probably a function that allocates or frees memory.
lv1_net_add_multicast_address
Add multicast address to the specified network device.
Kernel Call
result = lv1_net_add_multicast_address( /*IN*/ bus_id, dev_id, addr, flag );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | addr - see notes |
R6 | flag - see notes |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
Notes:
From “gelic_net.c”, addr can also take special values of 0 and GELIC_NET_BROADCAST_ADDR (0xffffffff) and flag is assigned values of 0 and 1.
lv1_net_remove_multicast_address
Remove multicast address on the specified network device.
Kernel Call
result = lv1_net_remove_multicast_address( /*IN*/ bus_id, dev_id, 0, 1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | 0 - Unknown |
R6 | 1 - Unknown |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_net_start_tx_dma
Start DMA transmit on the specified network device.
Kernel Call
result = lv1_net_start_tx_dma( /*IN*/ bus_id, dev_id, bus_addr, 0 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | bus_addr - dma address? |
R6 | 0 - Unknown |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_net_stop_tx_dma
Stop DMA transmit on the specified network device.
Kernel Call
result = lv1_net_stop_tx_dma( /*IN*/ bus_id, dev_id, 0 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | 0 - Unknown |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_net_start_rx_dma
Start DMA receive on the specified network device.
Kernel Call
result = lv1_net_start_rx_dma( /*IN*/ bus_id, dev_id, bus_addr, 0 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | bus_addr - dma address? |
R6 | 0 - Unknown |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_net_stop_rx_dma
Stop DMA receive on the specified network device.
Kernel Call
result = lv1_net_stop_rx_dma( /*IN*/ bus_id, dev_id, 0 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | 0 - Unknown |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_net_set_interrupt_status_indicator
Set the interrupt status indicator for the specified network device.
Kernel Call
result = lv1_net_set_interrupt_status_indicator( /*IN*/ bus_id, dev_id, irq_status_addr, 0 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | irq_status_addr - lpar address of the irq status indicator |
R6 | 0 - Unknown |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_net_set_interrupt_mask
Sets the interrupt mask for specified network device.
Kernel Call
result = lv1_net_set_interrupt_mask( /*IN*/ bus_id, dev_id, mask, 0 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | mask - interrupt mask |
R6 | 0 - Unknown |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_net_control
Send a control command to the specified network device.
Kernel Call
result = lv1_net_control( /*IN*/ bus_id, dev_id, p1, p2, p3, p4, /*OUT*/ &v1, &v2 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - bus id |
R4 | dev_id - device id |
R5 | p1 - command dependent input parameter |
R6 | p2 - command dependent input parameter |
R7 | p3 - command dependent input parameter |
R8 | p4 - command dependent input parameter |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | v1 - command dependent output parameter |
R5 | v2 - command dependent output parameter |
lv1_undocumented_function_195
Exists in PAL 1.7. Returns -3 (LV1_NO_PRIVILEGE) when passed 0 in R3 to R10.
lv1_undocumented_function_196
Exists in PAL 1.7. Returns -3 (LV1_NO_PRIVILEGE) when passed 0 in R3 to R10.
lv1_connect_interrupt_event_receive_port
Assign a virtual interrupt to a system bus device.
Kernel Call
result = lv1_connect_interrupt_event_receive_port( /*IN*/ bus_id, dev_id, outlet, interrupt_id );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - device bus id |
R4 | dev_id - device id |
R5 | outlet - interrupt outlet |
R6 | interrupt_id - interrupt id |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_disconnect_interrupt_event_receive_port
Disconnect a virtual interrupt from a system bus device.
Kernel Call
result = lv1_disconnect_interrupt_event_receive_port( /*IN*/ bus_id, dev_id, outlet, interrupt_id );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | bus_id - device bus id |
R4 | dev_id - device id |
R5 | outlet - interrupt outlet |
R6 | interrupt_id - interrupt id |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_get_spe_all_interrupt_statuses
Not used in current kernel.
Abstract Call
result = lv1_get_spe_all_interrupt_statuses( /*IN*/ p1, /*OUT*/ &v1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown |
Outputs | |
Register | Description |
R3 | Status? |
R4 | v1 - Unknown |
lv1_undocumented_function_200
Exists in PAL 1.7. Returns -4 (LV1_DENIED_BY_POLICY) when passed 0 in R3 to R10.
lv1_undocumented_function_201
Exists in PAL 1.7. Returns -4 (LV1_DENIED_BY_POLICY) when passed 0 in R3 to R10.
lv1_deconfigure_virtual_uart_irq
Deconfigure the VUART IRQ.
Kernel Call
result = lv1_deconfigure_virtual_uart_irq();
Parameters
Outputs | |
---|---|
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_enable_logical_spe
Enables a logical SPE.
Kernel Call
status = lv1_enable_logical_spe( /*IN*/ spe_id, resource_id );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | spe_id - logical spe id |
R4 | resource_id - spe resource id (prevously retrieved from Kernel repository) |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_undocumented_function_209
Exists in PAL 1.7. Returns -4 (LV1_DENIED_BY_POLICY) when passed 0 in R3 to R10.
lv1_gpu_open
Open the GPU. Call lv1_gpu_close to close the GPU.
Kernel Call
status = lv1_gpu_open( /*IN*/ p1 );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | p1 - Unknown, Kernel only ever passes 0, though other randomly chosen values seem to succeed. |
Outputs | |
Register | Description |
R3 | Status: 0 = LV1_SUCCESS, -6 = LV1_NO_ENTRY |
Notes:
When called from Kernel module init function, if GPU is already open, subsequent calls to lv1_gpu_open return LV1_NO_ENTRY (-6). Closing the GPU and re-opening succeeds.
lv1_gpu_close
Closes the GPU. Must be called once for every call to lv1_gpu_open.
Kernel Call
status = lv1_gpu_close();
Parameters
Outputs | |
---|---|
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_gpu_device_map
Map a device into logical address space. Address needs to be ioremapped before use.
kboot Call
result = lv1_gpu_device_map( /*IN*/ dev_id, /*OUT*/ &lpar_addr, &lpar_size );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | dev_id - device id (see notes) |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
R4 | lpar_addr - logical partition address of device block |
R5 | lpar_size - size of device block |
Notes:
Info taken from kboot-10\dl\linux-2.6.16\sound\powerpc\snd_ps3pf.c (kboot-20061208) where the audio front-end registers are mapped into memory. Two calls to lv1_gpu_device_map are performed, the first when dev_id = 1, to obtain the audio interrupt (Audio IRQ Outlet) and a second time when dev_id = 2 to obtain a mapping of the register block (Audio Registers).
lpar_addr is a virtual address, created by the Hypervisor. Multiple calls to lv1_gpu_device_map with the same dev_id return successfully, but the lpar_address returned for each is different (if there have been no intermediary lv1_gpu_device_unmap calls). These various virtual lpar_addr values all alias the same physical location in memory.
Test Results
lv1_gpu_device_map | ||||
---|---|---|---|---|
dev_id | result | lpar_addr | lpar_size | Comment |
0 | ffffffffffffffef | 98d2f7d44da1ceae | 0100000000000000 | result = LV1_ILLEGAL_PARAMETER_VALUE |
1 | 0000000000000000 | 0000300000022000 | 0000000000001000 | Audio IRQ Outlet (map1_dump) |
2 | 0000000000000000 | 00003c0000128000 | 0000000000008000 | Audio_Registers (map2_dump) |
3 | 0000000000000000 | 0000300000026000 | 0000000000001000 | ??? - Any attempt to dereference this lpar_addr locks up the PS3 |
4 | ffffffffffffffef | 0000300000026000 | 0000000000001000 | result = LV1_ILLEGAL_PARAMETER_VALUE |
5 | 0000000000000000 | 0000300000028000 | 0000000000001000 | ??? - (map5_dump) |
6 | 0000000000000000 | 0000300000029000 | 0000000000001000 | ??? - (map6_dump) |
7 | 0000000000000000 | 00003000002A0000 | 0000000000010000 | ??? - (map7_dump) |
8 | 0000000000000000 | 000030000002B000 | 0000000000001000 | video RAM at offset 0x0ff10000- (map8_dump) |
9-255 | -20 | ??? | ??? | result = LV1_NOT_IMPLEMENTED |
It is interesting that when dev_id = 4, LV1_ILLEGAL_PARAMETER_VALUE is returned with lpar_addr and lpar_size set to the values returned from the previous call - for the first call when dev_id = 0, values also appear to be set (though these could be garbage values)
Devices 1 & 2
dev_id 1 gives a location used to process IRQ’s from the audio and dev_id 2 gives the base address of the Audio Hardware registers. From published Sony documents (http://www.watch.impress.co.jp/game/docs/20060329/3dps303.htm), Audio is believed to be on the RSX, this call seems to confirm that. Access to the audio after this mapping call (it would appear) bypasses the Hypervisor and occurs directly on the RSX hardware. 3,5,6,7,8 are currently unknown. Presumably 0 and 4 are otherwise valid parameters blocked by the Hypervisor for OtherOS (ie, they may function for Games) otherwise I would have expected a return result of -20 for them. So rather than just being a bridge for audio into HDMI, it is for all audio.
Also interesting is that the GPU version number returned by lv1_gpu_context_allocate appears at address 0×10 in the device 1 dump, though this of course may be complete coincidence.
Device 7
dev_id 7 appears to be a video device. It is not possible to map the entire reported memory space (0×10000), only areas 0×0000 to 0x0fff and 0×2000 to 0x2fff (discovered via laborious trial and error testing, mapping other areas causes the PS3 to hang without warning). In both mappable areas, the current screen resolution can be seen at locations 0×200 and 0×210. Changes to the ps3 video mode (e.g. using the ps3videomode utility) can be observed in the mapped areas, but modifying the values directly does not affect the screen resolution. Although both areas contain different values, there appear to be parts in common, as changing the values at 0×200 and 0×210 directly in one area also causes the same values in the other area to change.
Given the screen resolution connection, it could be possible that this device is a mapping of the GPU display heads:
- Out of 16Kb, only two areas are mappable (= number of accessible display heads)
- Mappable areas are 2Kb apart → 8 total display heads (= size of display heads array returned by lv1_gpu_context_allocate)
Device 8
dev_id 8 appears to be a mapping of video RAM at offset 0x0ff10000. This region of video memory is referenced by RSX DMA objects but its purpose is unknown.
lv1_gpu_device_unmap
Unmaps the device from logical address space.
kboot Call
lv1_gpu_device_unmap( /*IN*/ dev_id );
Parameters
Inputs | |
---|---|
Register | Description |
R3 | dev_id - device id (see lv1_gpu_device_map) |
Outputs | |
Register | Description |
R3 | Status? |
Notes:
Info taken from kboot-10\dl\linux-2.6.16\sound\powerpc\snd_ps3pf.c (kboot-20061208)
lv1_gpu_memory_allocate
Allocate GPU memory.
Kernel Call
status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0, &memory_handle, &ddr_lpar);
Parameters
Inputs | |
---|---|
Register | Description |
R3 | ddr_size - amount of DDR to reserve? (see notes) |
R4 | 0 - Unknown. Max value 512*1024. |
R5 | 0 - Unknown. Max value 3075*1024. |
R6 | 0 - Unknown. Max value 15. |
R7 | 0 - Unknown. Max value 8. |
Outputs | |
Register | Description |
R3 | status: 0 = LV1_SUCCESS, -17 = LV1_ILLEGAL_PARAMETER_VALUE |
R4 | memory_handle - used by lv1_gpu_context_allocate and lv1_gpu_memory_free?? |
R5 | ddr_lpar - remappable address of allocated video memory, unused by Kernel. |
Notes:
ddr_size accepts values (0..252) * 2^20 - values not divisible by 2^20 (1 MB) and above the range result in a return value of -17 (LV1_ILLEGAL_PARAMETER_VALUE). [Verified by Strontium Dog on an AU PS3 V1.5 firmware]
Bits 52-63 of ddr_size seem to be ignored or correspond to flags. The lower bits correspond to the amount of allocated video RAM. A call to lv1_gpu_context_allocate using the returned memory_handle will create a RSX DMA object handle 0xfeed0000 corresponding to the region of allocated memory. Multiple calls to lv1_gpu_memory_allocate with non-zero ddr_size will change the start of this region. The DMA object limit is set to ddr_size - 1. Before FW 2.1, a ddr_size of 0 was accepted, in which case a DMA object starting at zero and of limit 0xffffffff was created. Note that in this case, the start of this region is always zero even if previous calls to lv1_gpu_memory_allocate with non-zero ddr_size were performed. As of FW 2.1 and above, a zero ddr_size is not accepted anymore.
Parameters r4-r7 are unknown. Maximum values for these parameters are respectively 512kB, 3075kB, 15 and 8. They refer to shared scarce resources, as allocations are retained across multiple calls to lv1_gpu_memory_allocate. When attached to a context during lv1_gpu_context_allocate, the values of these parameters are reported in the lpar_driver_info structure of the context.
Cell separates multiple OS into Logical Partitions (lpar) described at http://research.scea.com/research/html/CellGDC05/13.html. PS3 GPU memory is referred to as DDR (or GDDR) whereas system memory is XDR. 256 MB of each are installed in PS3 though only XDR is currently available for use by OtherOS.
To make use of the allocated DDR ddr_lpar needs to be transformed into a usable address using:
ddr_address = ioremap_flags(ddr_lpar, ddr_size, _PAGE_NO_CACHE);
Be aware that the memory that holds the physical GPU frame buffer is not allocated by the Kernel, just used. So on the first call to this, some or all of the memory you request (depending on now much you request) may be actually used as the frame buffer. You will know this, because your writes to memory will mysteriously disappear up to 20ms after you perform them. Note that direct access to video ram is very slow (~10MB/s).
Test Results
Register | Hex | Decimal | Comments |
---|---|---|---|
R3 | 0x000000000fc00000 | (264241152) | 252 MB |
R4 | 0×0000000000000000 | (0) | |
R5 | 0×0000000000000000 | (0) | |
R6 | 0×0000000000000000 | (0) | |
R7 | 0×0000000000000000 | (0) | |
Outputs | |||
R3 | 0×0000000000000000 | (0) | LV1_SUCCESS |
R4 | 0x000000005a5a5a5b | (...) | memory handle |
R5 | 0x00007001a0000000 | (...) | ddr logical partition address |
lv1_gpu_memory_free
Free memory handle returned by lv1_gpu_memory_allocate. Must be called to dispose of the handle returned by lv1_gpu_memory_allocate.
Kernel Call
status = lv1_gpu_memory_free(ps3fb.memory_handle);
Parameters
Inputs | |
---|---|
Register | Description |
R3 | Memory handle returned by lv1_gpu_memory_allocate |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, Other values are unknown, but indicate failure. |
lv1_gpu_context_iomap
Map system RAM address to GPU through the Cell FlexIO interface.
Kernel Call
status = lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar, ps3fb_videomemory.size, 0);
Parameters
Inputs | |
---|---|
Register | Description |
R3 | ps3fb.context_handle as allocated by lv1_gpu_context_allocate |
R4 | GPU_IOIF = 0x0d000000UL - GPU address where the system RAM virtual framebuffer is remapped |
R5 | xdr_lpar - lpar version of the physical address of the virtual frame buffer in local memory. (Note: the lpar version = the physical address in the PS3) |
R6 | ps3fb_videomemory.size = The size of the virtual frame buffer |
R7 | 0 - IOPTE flags - bitfield describing protection, coherency and ordering of the I/O mapping. Any combination of 0 or 2^{11 (cache hint),59 (read ordering),60 (write ordering),61 (coherency),62 (read protection) ,63 (write protection)} seems valid |
Outputs | |
Register | Description |
R3 | Status - 0 = OK, LV1_TYPE_MISMATCH (-8) if R5 set to a DDR lpar address, LV1_ILLEGAL_PARAMETER_VALUE (-17) if any other bit set in R7 than described above |
This function creates a mapping in GPU address space so that the RSX can access system RAM. The Kernel uses it to associate the virtual framebuffer residing in system RAM to the GPU, so that so that transfers can be initiated by the RSX from the system RAM to the video RAM using the lv1_gpu_context_attribute:fb_blit() call.
Comments:
It was previously suspected that GPU_IOIF was the address of GPU MMIO registers. However GPU_IOIF is a GPU address, not a lpar address, and therefore has no meaning on the Linux side, and cannot be directly mapped from the Linux address space. Reserving the memory block (using request_mem_region) and mapping (using ioremap) results in a block of memory that is used by Linux (nothing resembling IO registers was observed whilst single frame stepping a gfx demo). [was: As you’ve previously discovered that the top of RAM is 0x0e00 0000, GPU_IOIF here is 16Mb below that. That’s typically the size of the a graphics card PCI IO region on a PC, so given the name, I’d strongly suggest it’s not GPU memory that’s being mapped but the GPU IO registers. Although why this address range would overlap with RAM is a mystery.]
GPU_IOIF was successfully set to other values (0×00000000, 0×02000000, 0×04000000) with Linux booting and displaying correctly. A value of 0x0f000000 causes the PS3 to hang (need to retest 0x0e0000000)
Although it looks like GPU_IOIF would overlap video RAM, the RSX differentiates between the two by associating different DMA objects to the source and destination of the blit. The source is associated with DMA object handle 0xfeed0001 which targets system memory, while the destination is associated with DMA object handle 0xfeed0000 which targets video memory. This has been observed by analysing the FIFO commands sent to the GPU by the hypervisor during the lv1_gpu_context_attribute:fb_setup() and lv1_gpu_context_attribute:fb_blit() calls.
It seems a call to lv1_gpu_context_iomap(handle, bus_addr, xdr_lpar, size, flags) is equivalent to a series of call to lv1_put_iopte:
int i; int context_number; /* derived from handle, 1st allocated context 0, 2nd allocated context 1, etc... */ for (i = 0; i < size; i += 1024 * 1024) { lv1_put_iopte(0, /* IO ASID */ ((0x08ULL + context_number) << 28) | bus_addr) + i, /* IO address */ xdr_lpar + i, /* Logical Partition address */ 1, /* PS3_AUDIO_IOID, actually RSX IOID */ flags); }
Tested by replacing the call to lv1_gpu_context_iomap with the code above in ps3fb.c.