Hypervisor Reverse Engineering: Difference between revisions

From PS3 Developer wiki
Jump to navigation Jump to search
 
(129 intermediate revisions by 23 users not shown)
Line 1: Line 1:
[[Category:Software]]
<span style="background:red; color:#ffffff;">Warning, this page is way too long and is voted to be split into seperate sections</span>
 
----
----
= Difference between Debug Firmware HV and Retail HV =
= Difference between Debug Firmware HV and Retail HV =


There is no difference between debug firmware lv1.self and retail firmware lv1.self
There is no difference between debug firmware lv1.self and retail firmware lv1.self
The differences reside on the repository nodes loaded because of policies/flags.  
The differences reside on the repository nodes loaded because of policies/flags.
 
[http://www.mirrorcreator.com/files/1DKLUPMC/160_192_341_355_--CEX_DECR_-_LV1.rar_links DECR/Tool + CEX/Retail LV1.self 1.60 1.92 3.41 3.55]


= HSPRG  =
= HSPRG  =
Line 16: Line 19:
LPAR = Logical Partition  
LPAR = Logical Partition  


lpar1 starts at 0x&lt;unknown&gt;, and its belived to be the memory space where lv1 stores its variables, flags and other data.  
lpar1 starts at 0x&lt;unknown&gt;, and it's believed to be the memory space where lv1 stores its variables, flags and other data.  


lpar2 starts at 0x80000000000 and it's belived to be the memory space where lv2 stores its variables, flags and other data.  
lpar2 starts at 0x80000000000 and it's believed to be the memory space where lv2 stores its variables, flags and other data.  


<br>
<br>
Line 177: Line 180:
There are 2 system call tables in HV. The first one stores system calls 0 - 36. The second one stores system calls 0x10000 - 0x100FF.  
There are 2 system call tables in HV. The first one stores system calls 0 - 36. The second one stores system calls 0x10000 - 0x100FF.  


== System call table 0 - 36  ==
== UX System call table 0 - 36  ==


0x0035FAE8 (3.15)  
0x0035FAE8 (3.15)  
Line 184: Line 187:


=== System call numbers  ===
=== System call numbers  ===
0x0 - void eosh(void) //end_of_signal_handling(void)


0x1 - getpid(void)  
0x1 - pid_t getpid(void)  


0x2 - getppid(void)  
0x2 - pid_t getppid(void)  


0x3 - fork(void)  
0x3 - pid_t fork(void)  


0x4 - exit  
0x4 - void exit(int status)


0x5 - exec(filename)  
0x5 - void execv(const char *path, char *const argv[])  


0x6 - wait(status)  
0x6 - void wait(int *status)  


0x7 - open(filename)  
0x7 - int open(const char *path, int flags)  


0x8 - close(fd)  
0x8 - void close(int fd)  


0x9 - read  
0x9 - ssize_t read(int fd, void *buf, unsigned int nbyte)


0xA - write  
0xA - ssize_t write(int fd, const void *buf, unsigned int nbyte)


0xB - seek
0xB - void lseek(int fd, long offset, int whence)


0xC - unlink(filename)  
0xC - unlink(const char *path)  


0xD - signal  
0xD - void signal(int sig, void *func(int sig))


0xE - kill(pid, signal type)  
0xE - int kill(int pid, int signal_type)  


0xF - brk  
0xF - int brk(void *addr)


0x10 - socket(af, type, protocol) (supports only address family 0x1F, type 0x0 and protocol 0x0)  
0x10 - int socket(int af, int type, int protocol) (supports only address family 0x1F, type 0x0 and protocol 0x0)  


0x11 - bind  
0x11 - int bind(int sockfd , const sockaddr *addr, unsigned int addrlen)


0x12 - listen(fd, backlog)  
0x12 - int listen(int sockfd, int backlog)  


0x13 - accept  
0x13 - int accept(int sockfd, sockaddr *addr, unsigned int *addrlen)


0x14 - connect  
0x14 - int connect(int sockfd, const sockaddr *serv_addr, unsigned int addrlen)


0x15 -&nbsp;?
0x15 - void putchar(int c)


0x16 - pause(void)  
0x16 - int pause(void)  


0x17 - sleep(seconds)  
0x17 - int sleep(unsigned int seconds)  


0x18 - mmap(addr, size, prot, flags, fd, offset)  
0x18 - int mmap(void *addr, unsigned long size, int prot, int flags, int fd, long offset, void *mapped_addr)  


0x19 - munmap  
0x19 - int munmap (void *addr, unsigned long size)


0x1A - some fs func for directories, perhaps readdir
0x1A - int chdir(const char *path)


0x1B -&nbsp;?
0x1B - void getchar(char *c)


0x1C - map_pages (used for alloc)  
0x1C - map_pages(...) (used for alloc)  


0x1D - unmap_pages (used for free)  
0x1D - unmap_pages(...) (used for free)  


0x1E - select  
0x1E - int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)


0x1F - getcwd  
0x1F - getcwd(...)


0x20 -&nbsp;?
0x20 - Not used


0x21 - alarm  
0x21 - unsigned int alarm(unsigned int seconds)


0x22 - ioctl  
0x22 - int ioctl(int fd, unsigned __int64 request, ...)


0x23 - _map_pages
0x23 - pme_memalign(...)


0x24 - _unmap_pages
0x24 - ?


== System call table 0x10000 - 0x100FF  ==
== PMI System call table 0x10000 - 0x100FF  ==


0x0035DE78 (3.15)  
0x0035DE78 (3.15)  
Line 265: Line 269:
=== System call numbers  ===
=== System call numbers  ===


0x10000 - allocate_memory_region(LPAR id, size, log2 of page size,&nbsp;?,&nbsp;?)  
0x10000 - allocate_memory(LPAR id, size, log2 of page size,&nbsp;?,&nbsp;?) / construct_memory_segment


0x10001 - lpar_query_address_region_info
0x10001 - query_logical_partition_address_region_info


0x10002 - lpar_memory_addr_to_phys_addr(LPAR id, LPAR address, physical addr)  
0x10002 - translate_logical_partition_to_physical_address(LPAR id, LPAR address, physical addr)  
 
0x10003 - map_physical_address_region
 
0x10004 - unmap_physical_address_region


0x10005 - construct_logical_pu  
0x10005 - construct_logical_pu  
0x10006 - destruct_logical_pu


0x10007 - activate_logical_pu(LPAR id, PPE id)  
0x10007 - activate_logical_pu(LPAR id, PPE id)  


0x10009 - construct_logical_partition(0, LPAR id, outlet)  
0x10009 - construct_logical_partition(0, LPAR id, outlet)  
0x1000A - get_logical_console_info
0x1000B - get_remote_file_size
0x1000C - read_remote_file
0x1000D - write_remote_file


0x1000E - release_memory_region(LPAR id, memory region address)  
0x1000E - release_memory_region(LPAR id, memory region address)  


0x1001A - construct_event_receive_port  
0x1001A - construct_event_receive_port  
0x1001B - destruct_event_receive_port
0x1001C - request_to_connect_event_ports
0x1001D - connect_event_ports
0x1001E - destruct_event_send_port
0x1001F - send_event_externally
0x10020 - get_status_of_event_send_port
0x10021 - get_event_port_connection_request
0x10022 - end_of_control_signal_processing


0x10024 - shutdown_logical_partition(LPAR id, shutdown command)  
0x10024 - shutdown_logical_partition(LPAR id, shutdown command)  
Line 286: Line 320:


0x10026 - get_logical_partition_info  
0x10026 - get_logical_partition_info  
0x10027 - read_privilege_set
0x10028 - modify_privilege_set
0x10029 - get_remote_file_size_long_name
0x1002A - read_remote_file_long_name
0x1002B - write_remote_file_long_name


0x1002C - construct_scheduling_table  
0x1002C - construct_scheduling_table  
Line 291: Line 335:
0x1002D - set_scheduling_slot  
0x1002D - set_scheduling_slot  


0x1002E - ?
0x1002E - load_scheduling_table
 
0x10032 - poweroff


0x10032 - accesses system console
0x10033 - get_remote_file_name


0x10034 - ?
0x10034 - allocate_cp_channel


0x10035 - ?
0x10035 - release_cp_channel


0x10036 - accesses system console
0x10036 - power_down


0x10037 - ?
0x10037 - ?
Line 307: Line 353:
0x10039 - ?
0x10039 - ?


0x10040 - construct_spe_type_1(SPE id, shaddow_addr)  
0x10040 - construct_spe_type_1(SPE id, shaddow_addr) / construct_logical_spu


0x10041 - destruct_spe(SPE id)  
0x10041 - destruct_spe(SPE id) / destruct_logical_spu


0x10042 - decrypt_lv2_self(spe id, LPAR auth id, SELF file image ptr, LPAR memory address)  
0x10042 - decrypt_lv2_self(spe id, LPAR auth id, SELF file image ptr, LPAR memory address)  
Line 317: Line 363:
0x10044 - disable_spe_execution  
0x10044 - disable_spe_execution  


0x10045 - set_spe_interrupt_mask
0x10045 - read_spu_puint_mb(unsigned long spu_id, unsigned long msg)
 
0x10046 - read_spe_problem_state_register(spe id, register offset, value) / read_spu_problem_state_area_register


0x10046 - read_spe_problem_state_register(spe id, register offset, value)  
0x10047 - write_spe_problem_state_register(spe id, register offset, value) / write_spu_problem_state_area_register


0x10047 - write_spe_problem_state_register(spe id, register offset, value)
0x1004A - install_revoke_list


0x1004B - disable_spe_loading  
0x1004B - disable_spe_loading  
0x1004C - install_access_control_table?
0x1004D - get_storage_status?
0x1004E - get_region_table_bits?
0x1004F - commit_region_update?
0x10050 - abort_region_update?
0x10051 - set_storage_tampered?


0x10053 - pmi_set_guest_os_mode  
0x10053 - pmi_set_guest_os_mode  


0x10081 - accesses system console
0x1007F - pause
 
0x10080 - get_total_execution_time
 
0x10081 - reset
 
0x10083 - construct_logical_rsx


0x10084 - construct_virtual_uart(LPAR id, VUART id, VUART data buffer size)  
0x10084 - construct_virtual_uart(LPAR id, VUART id, VUART data buffer size)  


0x10085 - destruct_virtual_uart(LPAR id, VUART id)  
0x10085 - destruct_virtual_uart(LPAR id, VUART id)  
0x10086 - establish_virtual_uart_channel


0x10088 - RSX_syscall_10088(LPAR id)  
0x10088 - RSX_syscall_10088(LPAR id)  
Line 347: Line 415:
0x100C2 - modify_repository_node_value(LPAR id)  
0x100C2 - modify_repository_node_value(LPAR id)  


0x100C3 - remove_repository_node_value(LPAR id)
0x100C3 - remove_repository_node(LPAR id)


= Process  =
= Process  =
Line 355: Line 423:
HV supports only 32 processes simultaneously. The number of processes currently running in HV is stored at address 0x0035EA54 (3.15) and 0x00357E3C (2.60).  
HV supports only 32 processes simultaneously. The number of processes currently running in HV is stored at address 0x0035EA54 (3.15) and 0x00357E3C (2.60).  


The process table is an array of 32 process table entries.  
The process table is an array of 32 process table entries.
 
0x0036C930 (4.30)
 
0x0036C8B0 (4.21)
 
0x00365458 (4.11)


0x0035F8D0 (3.55)
0x0035F8D0 (3.55)
Line 496: Line 570:
*0x000A9870 (PID 6)  
*0x000A9870 (PID 6)  
*0x00084B80 (PID 9)
*0x00084B80 (PID 9)
In JIG 2.43:
*(PID3) <- ss_server3
*(PID4) <- ss_sc_init_pu
*(PID5) <- ss_server2
*(PID6) <- ss_server1
*(PID7) <- factory_data_mngr_server
*(PID8) <- updater_frontend
(see [http://pastie.org/pastes/9407461/text?key=f6bk5lof0g4bgeu6xrn5ua this])


= PThread  =
= PThread  =
Line 587: Line 671:
== Member variables  ==
== Member variables  ==


offset 0x8 - pointer to previous AddressProtectionDomain object  
offset 0x0 - pointer to previous AddressProtectionDomain object  


offset 0x10 - pointer to next AddressProtectionDomain object  
offset 0x8 - pointer to next AddressProtectionDomain object  


offset 0x18 - poiinter to pointer to SLB entries  
offset 0x10 - poiinter to pointer to SLB entries  


offset 0x20 - pointer to AddressSpace object that owns this object  
offset 0x18 - pointer to AddressSpace object that owns this object  


offset 0x34 - pointer to previous ProtectionPage  
offset 0x2A - pointer to previous ProtectionPage  


offset 0x3C - pointer to next ProtectionPage  
offset 0x34 - pointer to next ProtectionPage  


offset 0x48 - Mutex object  
offset 0x40 - Mutex object


= ProtectionPage  =
= ProtectionPage  =
Line 756: Line 840:
=== vtable  ===
=== vtable  ===


0x003569F8 (3.15)  
0x003569F8 (3.15)


== IOIF device file objects  ==
== IOIF device file objects  ==
Line 942: Line 1,026:
=== vtable  ===
=== vtable  ===


0x000x352308 (3.15)  
0x352308 (3.15)


=== Member variables  ===
=== Member variables  ===
Line 977: Line 1,061:


interrupt index = 8  
interrupt index = 8  
The Gelic device is similar to the spider_net device from Toshiba. There are some differences with mmio initialization values within LV1 in comparison to the spider_net.c linux driver.
Gelic defines:
{| class="wikitable sortable"
|-
! DEFINE !! Value
|-
| GELIC_CKRCTRL_REGISTER || 0xFF0
|-
| GELIC_CKRCTRL_STOP_VALUE || 0x00000105
|-
| GELIC_CKRCTRL_RUN_VALUE || 0x1D7F0105
|-
| GELIC_MACADDR_HIGH_REG || 0x500
|-
| GELIC_MACADDR_LOW_REG || 0x504
|-
|}


=== MMIO regions  ===
=== MMIO regions  ===
Line 1,602: Line 1,705:


*Before a storage region is accessed, HV checks access rights of the caller.  
*Before a storage region is accessed, HV checks access rights of the caller.  
*Repository node '''ss.laid''' (LPAR authentication id) is evaluated for this purpose.  
*Repository node '''ss.laid''' ([[Authority ID|LPAR Authority ID]]) is evaluated for this purpose.  
*If LPAR has a repository node '''ios.ata.region0.access''' (value doesn't matter) then the access rights check never fails. After System Manager sets ATA keys it removes this repository node from LPAR 1. If we add this repository node again or patch System Manager so it's not removed then we will be able to access all storage regions of all storage devices.
*If LPAR has a repository node '''ios.ata.region0.access''' (value doesn't matter) then the access rights check never fails. After System Manager sets ATA keys it removes this repository node from LPAR 1. If we add this repository node again or patch System Manager so it's not removed then we will be able to access all storage regions of all storage devices.
*'''ALL storage accesses from LPAR 1 are allowed'''  
*'''ALL storage accesses from LPAR 1 are allowed'''  
Line 1,655: Line 1,758:


*The storage subsystem is a storage device itself.  
*The storage subsystem is a storage device itself.  
*It's a psuedo device used to notify a LPAR when storage devices become e.g. ready.  
*It's a pseudo device used to notify a LPAR when storage devices become e.g. ready.  
*Linux implements a loop and reads from this device and process notifications (adds new devices dynamically).
*Linux implements a loop and reads from this device and process notifications (adds new devices dynamically).


Line 1,733: Line 1,836:


*The commands can be used with HV call '''lv1_storage_send_device_command'''.  
*The commands can be used with HV call '''lv1_storage_send_device_command'''.  
*However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node '''ss.laid''' or also called '''LPAR authentication ID'''. If this test fails then the command is NOT executed.
*However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node '''ss.laid''' or also called '''[[Authority ID|LPAR Authority ID]]'''. If this test fails then the command is NOT executed.


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
Line 1,986: Line 2,089:


*The commands can be used with HV call '''lv1_storage_send_device_command'''.  
*The commands can be used with HV call '''lv1_storage_send_device_command'''.  
*However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node '''ss.laid''' or also called '''LPAR authentication ID'''. If this test fails then the command is NOT executed.
*However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node '''ss.laid''' or also called '''[[Authority ID|LPAR Authority ID]]'''. If this test fails then the command is NOT executed.


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
Line 2,586: Line 2,689:


*The commands can be used with HV call '''lv1_storage_send_device_command'''.  
*The commands can be used with HV call '''lv1_storage_send_device_command'''.  
*However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node '''ss.laid''' or also called '''LPAR authentication ID'''. If this test fails then the command is NOT executed.
*However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node '''ss.laid''' or also called '''[[Authority ID|LPAR Authority ID]]'''. If this test fails then the command is NOT executed.


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
Line 2,639: Line 2,742:
block size = 512  
block size = 512  


*It's a psuedo device.  
*It's a pseudo device.  
*'''This storage device redirects all requests to the region 1 of HDD storage device&nbsp;!!!'''
*'''This storage device redirects all requests to the region 1 of HDD storage device&nbsp;!!!'''


Line 3,372: Line 3,475:
! Address of Data in HV Dump  
! Address of Data in HV Dump  
! Size of Data
! Size of Data
! Entry Id
|-
|-
| 0  
| 0  
| -
| lv1ldr
| 0x0C150000  
| 0x0C150000  
| 0x1E5CC
| 0x1E5CC
| 0x01
|-
|-
| 1  
| 1  
Line 3,382: Line 3,487:
| 0x00011000  
| 0x00011000  
| 0xE8D0
| 0xE8D0
| 0x00
|-
|-
| 2  
| 2  
Line 3,387: Line 3,493:
| 0x00020000  
| 0x00020000  
| 0x16DA0
| 0x16DA0
| 0x02
|-
|-
| 3  
| 3  
Line 3,392: Line 3,499:
| 0x00055000  
| 0x00055000  
| 0x12E44
| 0x12E44
| 0x04
|-
|-
| 4  
| 4  
Line 3,397: Line 3,505:
| 0x00037000  
| 0x00037000  
| 0x1DAE4
| 0x1DAE4
| 0x03
|-
|-
| 5  
| 5  
Line 3,402: Line 3,511:
| 0x00068000  
| 0x00068000  
| 0x860
| 0x860
| 0x0C
|-
|-
| 6  
| 6  
| -
| QA Flag
| 0x00069010  
| 0x00069010  
| 0x8
| 0x8
| 0x0F
|-
|-
| 7  
| 7  
| -
| QA Flag Token
| 0x00069020  
| 0x00069020  
| 0x50
| 0x50
| 0x10
|-
|-
| 8  
| 8  
| -
| Trace Level
| 0x00069070  
| 0x00069070  
| 0x8
| 0x8
| 0x11
|}
|}


Line 3,480: Line 3,593:
=== appldr  ===
=== appldr  ===


*'''appldr''' is used for decryption of SELFs  
*'''appldr''' is used for decryption of SELFs or EDATs
*HV call '''lv1_authenticate_program_segment''' loads '''appldr'''
*HV call '''lv1_authenticate_program_segment''' loads '''appldr'''


Line 3,489: Line 3,602:
==== Loading appldr  ====
==== Loading appldr  ====


*64 bit memory address of '''isoldr''' is written into 32 bit SPU register '''SPU_In_Mbox'''  
*64 bit memory address of '''appldr''' is written into 32 bit SPU register '''SPU_In_Mbox'''  
*'''metldr''' is loaded
*'''metldr''' is loaded


Line 3,829: Line 3,942:


= AV Manager  =
= AV Manager  =
Crossreference: [http://portal.gitbrew.org/wikibrew/PS3:HvReverseEngineering#AV_Manager gitbrew.org::AV Manager] <br />
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#AV_Manager gitbrew.org::AV Manager] <br />


* AV Manager is running in Process 9 of HV.  
* AV Manager is running in Process 9 of HV.  
Line 3,876: Line 3,989:
offset 0x90 - LPAR image path  
offset 0x90 - LPAR image path  


offset 0x1C0 - LPAR ability (8 bytes)  
offset 0x1C0 - LPAR ability (8 bytes)


=== Types of System Manager  ===
=== Types of System Manager  ===
Line 4,336: Line 4,449:
| 0xA  
| 0xA  
| 0x1B6  
| 0x1B6  
| Makes a double beep
| Makes a triple beep
|-
|-
| 0x29  
| 0x29  
Line 4,348: Line 4,461:
| Makes a continuous beep
| Makes a continuous beep
|}
|}
field 1 seems relative to beep tone, as 0x25 sounds different


=== Active System Managers in HV dump 3.15  ===
=== Active System Managers in HV dump 3.15  ===
Line 4,523: Line 4,637:
| 0x23  
| 0x23  
| 0x2001 - 0x2017  
| 0x2001 - 0x2017  
| Virtual TRM Manager
| [[Virtual_TRM_Manager|Virtual TRM Manager]]
|-
|-
| 0x3000  
| 0x3000  
| 0x24  
| 0x24  
| 0x3001 - 0x3003  
| 0x3001 - 0x3003  
| Secure RTC
| [[Secure_RTC_Manager|Secure RTC]]
|-
|-
| 0x5000  
| 0x5000  
| 0x23  
| 0x23  
| 0x5001 - 0x500A  
| 0x5001 - 0x500A  
| Storage Manager
| [[Storage_Manager|Storage Manager]]
|-
|-
| 0x6000  
| 0x6000  
| 0x23  
| 0x23  
| 0x6001 - 0x6011  
| 0x6001 - 0x6011  
| Update Manager
| [[Update_Manager|Update Manager]]
|-
| 0x8000
| 8
| 0x8001 - 0x8005
| [[Updater_Frontend|Updater Frontend]]
|-
|-
| 0x9000  
| 0x9000  
| 0x24  
| 0x24  
| 0x9001 - 0x9016  
| 0x9001 - 0x9016  
| SC Manager
| [[SC_Manager|SC Manager]]
|-
|-
| 0x10000  
| 0x10000  
| 0x23  
| 0x23  
| -  
| 0x10001-0x10007
| -
| [[SB_Manager|SBM (South Bridge Manager)]]
|-
|-
| 0x11000  
| 0x11000  
| 0x25  
| 0x25  
| 0x11001 - 0x11002  
| 0x11001 - 0x11002  
| SPM (Security Policy Manager)
| [[Security_Policy_Manager|SPM (Security Policy Manager)]]
|-
|-
| 0x14000  
| 0x14000  
| 0x25  
| 0x25  
| 0x14004 - 0x14005  
| 0x14004 - 0x14005  
| SLL (Secure LPAR Loader)
| [[Secure_LPAR_Loader|SLL (Secure LPAR Loader)]]
|-
|-
| 0x15000  
| 0x15000  
| 0x24  
| 0x24  
| 0x15001, 0x15003, 0x15009  
| 0x15001, 0x15003, 0x15009  
| SPL (Secure Profile Loader)
| [[Secure_Profile_Loader|SPL (Secure Profile Loader)]]
|-
|-
| 0x17000  
| 0x17000  
| 0x24  
| 0x24  
| 0x17001 - 0x17017  
| 0x17001 - 0x17017  
| Indi Info Manager
| [[Indi_Info_Manager|Indi Info Manager]]
|-
|-
| 0x18000  
| 0x18000  
| 0x25  
| 0x25  
| 0x18001, 0x18002, 0x18004  
| 0x18001, 0x18002, 0x18004  
| Dispatcher Manager
| [[Dispatcher_Manager|Dispatcher Manager]]
|-
|-
| 0x19000  
| 0x19000  
| 0x24  
| 0x24  
| 0x19002 - 0x19005  
| 0x19002 - 0x19005  
| AIM
| [[AIM_Manager|AIM]]
|-
| 0x22000
| 0x16
| 0x22001 - 0x22004
| [[Factory_Data_Manager|Factory Data Manager]]
|-
|-
| 0x24000  
| 0x24000  
| 0x23  
| 0x23  
| 0x24001 - 0x24002  
| 0x24001 - 0x24002  
| USB Dongle Authenticator
| [[USB_Dongle_Authenticator|USB Dongle Authenticator]]
|-
|-
| 0x25000  
| 0x25000  
| 0x23  
| 0x23  
| 0x25001 - 0x25002  
| 0x25001 - 0x25002  
| User Token Manager
| [[User_Token_Manager|User Token Manager]]
|}
|}


Line 4,616: Line 4,740:
     uint32_t retval;
     uint32_t retval;
     uint8_t res[4];
     uint8_t res[4];
     uint64_t laid;            /* LPAR authority id */
     uint64_t laid;            /* LPAR Authority ID */
     uint64_t paid;            /* Program authority id */
     uint64_t paid;            /* Program Authority ID */
}
}
</pre>
</pre>
Line 4,648: Line 4,772:
*The size of the body depends on a used service.
*The size of the body depends on a used service.


== 0x2000 - Virtual TRM Manager ==
= LPAR Memory Management =


{| class="wikitable FCK__ShowTableBorders"
== Memory Region class ==
|-
! Packet ID
! Description
|-
| 0x2001
| Init
|-
| 0x2002
| Status
|-
| 0x2003
| Store
|-
| 0x2004
| Store
|-
| 0x2005
| Retrieve
|-
| 0x2006
| Free
|-
| 0x200A
| Encrypt
|-
| 0x200B
| Decrypt
|-
| 0x200C
| Encrypt With Portability
|-
| 0x200D
| Decrypt With Portability
|-
| 0x200E
| Decrypt Master
|-
| 0x200F
| Restart?
|-
| 0x2010
| Unknown
|-
| 0x2011
| Unknown
|-
| 0x2012
| Backup Flash
|-
| 0x2013
| Restore Flash
|-
| 0x2014
| Backup SRK SRH
|-
| 0x2015
| Restore SRK SRH
|-
| 0x2016
| Flash Address Size
|-
| 0x2017
| Force Restart
|}


=== 0x200E - Decrypt Master  ===
This class is the base class for different memory region types.


*This service is e.g. used in Process 6 by '''USB Dongle Authenticator''' to decrypt '''USB Dongle Master Key'''
=== vtable  ===
*GameOS uses this service e.g. in syscall '''SYS_SS_AD_SIGN'''
*'''syscall 862''' uses Virtual TRM Manager services.


== 0x3000 - Secure RTC  ==
0x003578B0 (3.15)


{| class="wikitable FCK__ShowTableBorders"
=== Member variables  ===
|-
! Packet ID
! Description
|-
| 0x3001
| Set RTC
|-
| 0x3002
| Get Time
|-
| 0x3003
| Set Time
|}


*Secure RTC reads LAIDs and PAIDs that are allowed to access Secure RTC service from '''DEFAULT.SPP''' segment '''SCE_CELLOS_SS_SECURE_RTC'''.
offset 0x40 - pointer to LPAR object that owns this memory region
*vsh uses syscall 0x362 (866) to communicate.


=== 0x3001 - Set RTC  ===
offset 0x48 - type of memory region (8 bytes)


*This service uses '''SC Manager Set RTC (0x9008)''' service.
offset 0x50 - LPAR start address of memory region


=== 0x3002 - Get Time  ===
offset 0x58 - size of memory region (8 bytes)


*This service uses '''SC Manager Get Time (0x9009)''' service.
offset 0x60 - flags (8 bytes)  


=== 0x3003 - Set Time  ===
offset 0xA0 - log2 of page size


*This service uses '''SC Manager Set Time (0x900A)''' service.
=== Generating New LPAR Memory Region Addresses ===


== 0x5000 - Storage Manager  ==
generate_new_lpar_mem_region_address(?, memory region size, log2(page size), ?, ?) - 002C82E8 (3.15)


{| class="wikitable FCK__ShowTableBorders"
generate_new_lpar_mem_region_address - 002C6570 (3.41)
|-
! Packet ID
! Description
|-
| 0x5001
| Set Encdec Key
|-
| 0x5002
| Set/Delete ATA (Encdec) Key
|-
| 0x5003
| Get Random Number
|-
| 0x5004
| Authenticate BD Drive
|-
| 0x5005
| Authenticate PS2 Disc
|-
| 0x5006
| Get Secure Firmware Version
|-
| 0x5007
| HW disc auth emu
|-
| 0x5008
| HW mc
|-
| 0x5009
| HW me auth header
|-
| 0x500A
| HW me dec block
|}


*Storage Manager service is used e.g. by '''syscall 864''' and '''syscall SYS_SS_MEDIA_ID'''
*The function returns a new LPAR memory region address.
*GameOS's VSH uses '''syscall 864'''
*This method is used e.g. in all HV calls which create any kind of memory regions, e.g. '''lv1_allocate_memory''', '''lv1_map_htab''', '''lv1_undocumented_function_114''', '''lv1_construct_logical_spe''', '''lv1_map_device_mmio_region''' or '''syscall 0x10040'''.
*Storage Manager executes SPU module '''sb_iso_spu_module.self'''  
*Storage Manager communicates with devices '''/dev/encdec0''' and '''/dev/rbd0''' from LPAR 1
*2nd value from repository node '''bus1.id''' is used by Storage Manager
*Storage Manager communicates with '''sb_iso_spu_module.self''' through a shared DMA memory buffer and SPU MBox
*'''EID4''' data is passed to '''sb_iso_spu_module.self''' module.


==== SB Isolation DMA Buffer Header  ====
==== Encoding LPAR Memory Region Start Addresses and Sizes ====
<pre>struct sb_iso_header
{
    u32 seqno;
    u32 mbmsg;
    u32 cmd;
    u32 cmd_size;
    u8 cmd_data[0];
}
</pre>
*seqno has values 0x03 to 0x08. It is incremented when sending and receiving data from the spu.


=== 0x5001 - Set Encdec Key  ===
*Size of LPAR memory region is encoded in the LPAR memory region start address.
*That is why e.g. the LPAR Memory Region Start Addresses of LPAR Memory Region of size 4096 byte begin with '''0x300000000000''', '''0x300000000000 >> 42 = 0xC = log2(4096)'''.
*Each LPAR has a counter (8 bytes) which is incremented by 1 every time a new LPAR Memory Region is created.
*Before incrementing, the counter is shifted left by '''log2(LPAR Memory Region Size)''' and ored with '''log2(LPAR Memory Region Size) << 42'''.


* This service allows you to set ENCDEC keys with index '''0xC - 0xF'''
LPAR Memory Region Start Address >> 42 = log2(LPAR Memory Region Size)
* '''By patching HV process 6 it would be possible to set default ENCDEC key (used for HDD encryption) to a value different from the default one !!! It means we could encrypt our HDDs with a key we want !!!'''
* The service accepts 2 parameters: a key (max 24 bytes) and a key length (in bits)
* Valid key length values: '''0x40''', '''0x80''' and '''0xC0'''
* The service returns the ENCDEC key index used for the key
* '''ENCDEC supports upto 16 keys !!!'''
* Storage Manager in HV process 6 has a bit mask of size 2 bytes which indicates which keys are used currently.
Per default, keys with index 0x0 - 0xB are not free. But we could patch it also.


=== 0x5002 - Set/Delete ATA (Encdec) Key  ===
LPAR Memory Region Start Address = (log2(LPAR Memory Region Size) << 42) |
    (counter << log2(LPAR Memory Region Size))


*Sets/Deletes ATA (Encdec) Key
===== LPAR Memory Region Address Counter =====
*The service has only one parameter of size 8 bytes: '''0x100 - Set ATA Key''' and '''0x110 - Delete ATA Key'''.
*This service is used e.g. by '''System Manager''' in HV Process 9 during LPAR booting.
*SPM doesn't allow GameOS to use this service.
*3 possible key lengths: 0x40, 0x80 and 0xC0
*This service communicates with '''/dev/encdec0''' device.
*The service uses ENCDEC device commands '''EdecKgen1 (0x81)''', '''EdecKgen2 (0x82)''', '''EdecKset (0x83)''' and '''EdecKgenFlash (0x84)'''.
*This service communicates also with '''/dev/rbd0''' device.
*I guess that the ATA key is stored encrypted in '''EID4''' data.
*This service is used by LPAR Manager in HV Process 9 during LPAR 2 loading.
* I tested this service on Linux with '''ps3dm-utils''' and after deleting ATA key the sectors on VFLASH or HDD were NOT decrypted by HV
* After setting ATA key again, the sectors were encrypted/decrypted by HV again
* '''Deleting an ENCDEC key is nothing more than setting key with all bytes set to 0x0 !!!'''
* On old PS3s which didn't use HDD for VFLASH, HV uses 2 ENCDEC keys, one for HDD (key index 1) and one for VFLASH (key index 0). On new PS3s which use HDD for VFLASH, only one ENCDEC key is used (key index 1).


==== Service Parameter Table ====
*LPAR Memory Region Address Counter is stored at address: '''0x38(LPAR ptr) + 0x9E8'''
*LPAR1's Memory Region Address Counter is at address '''0x00677A48''' in HV dump 3.15
*LPAR2's Memory Region Address Counter is at address '''0x007632D8''' in HV dump 3.15
*LPAR1's Memory Region Address Counter is at address '''0x00677A48''' in HV dump 3.41
*LPAR2's Memory Region Address Counter is at address '''0x00161E68''' in HV dump 3.41


{| class="wikitable FCK__ShowTableBorders"
== Physical Memory Region class ==
|-
! Service Parameter
! Description
|-
| 0xC - 0xF
| Delete Encdec Key
|-
| 0x10*
| Set ATA Key (index 1)
|-
| 0x11*
| Delete ATA Key (index 1)
|}


=== 0x5003 - Get Random Number  ===
This type of memory region is created e.g. in '''lv1_allocate_memory''' HV call or in '''syscall 0x10000'''.


*I have got access to Get Random Number service through DM and tested it with PSGroove
=== vtable  ===
*The service returns 192-bit random numbers
*It has no input parameters except those in SS packet header
*Storage Manager communicates with device '''/dev/encdec0'''.
*This service is used e.g. by USB Dongle Authenticator to generate the body of a challenge or by GameOS to generate hardware random numbers.


=== 0x5004 - Authenticate BD Drive  ===
0x00357D08 (3.15)


*Used by LPAR Manager in HV Process 9 during LPAR 2 loading and unloading.
=== Member variables  ===
*Used by SLL Load GOS service (0x14004) in HV Process 3 during PS2EMU loading and by SLL Unload GOS service (0x14005) during PS2EMU unloading.
*The service expects one additional parameter.
*The service is used during loading of LPAR 2 to authenticate BD drive and during unloading LPAR 2 to reset BD drive.
*The service uses isolated SPU module '''sv_iso_spu_module.self''' for BD drive authentication.
*The service communicates with LPAR 1 device '''/dev/rbd0''' through ATAPI interface.


==== Service Parameter Table ====
offset 0xB0 - pointer to object that stores a list of addresses of physical pages owned by this memory region


{| class="wikitable FCK__ShowTableBorders"
offset 0xB8 - pointer to LPAR object that owns this memory region
|-
! Service Parameter
! Description
|-
| 0x02
| Used by SLL service 0x14004 during PS2EMU loading
|-
| 0x1E
| Used by SLL service 0x14005 during PS2EMU unloading
|-
| 0x29
| Reset BD Drive
|-
| 0x46
| Authenticate BD Drive
|-
| 0x52
| Authenticate PS2 Disc Insert
|}


=== 0x5005 - PS2 Disc Authenticate  ===
offset 0xC0 - reference counter (8 bytes)


=== 0x5006 - Get Version ===
=== Objects ===


* By default not accessible from GameOS. But it can be enabled by patching Dispatcher Manager.
Here is the list of physical memory region objects i found in HV 3.15.  
 
=== 0x5007 - Control BD Drive  ===
 
*Used by GameOS to authenticate discs and for BD emulation.
 
==== Service Parameter Table ====


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Service Parameter
! Address in HV dump
! Description
! LPAR id
! LPAR Start Address
! Size
! Flags
! log2(Page Size)
! Physical Page Addresses
|-
|-
| 0x0D
| 0x006B5510
| -
| 1
| 0x300000001000
| 0x1000
| 0x0
| 0xC
| 0x672000
|-
|-
| 0x3F
| 0x006B5E50
| -
| 1
| 0x440000040000
| 0x20000
| 0x0
| 0x11
| 0x6C0000
|-
|-
| 0x41
| 0x006B6980
| -
| 1
| 0x440000060000
| 0x20000
| 0x0
| 0x11
| 0x6E0000
|-
|-
| 0x43
| 0x006B7F00
| -
| 1
| 0x400000040000
| 0x10000
| 0x0
| 0x10
| 0x100000
|-
|-
| 0x46
| 0x003A80F0
| -
| 2
| 0x6C0058000000
| 0x7000000
| 0x4
| 0x18
| 0x1000000 - 0x7000000
|-
|-
| 0x51
| 0x003BE800
| -
| 2
| 0x300000047000
| 0x1000
| 0x0
| 0xC
| 0x1FA000
|-
|-
| 0x52
| 0x006BDAA0
| -
| 2
|-
| 0x0
| 0x53
| 0x8000000
| PS3 Disc Insert
| 0x8
|-
| 0x1B (single huge page)
| 0xA3
| 0x8000000
| BD emu
|-
| 0xA5
| BD emu
|-
| 0xA7
| BD emu
|-
| 0xAA
| BD emu
|}
|}


=== 0x5008 - HW mc ===
So, Linux kernel should be located at physical address 0x8000000 and Linux syscall handler at 0x8000C00. Too bad that the HV dump is not large enough.
 
=== GameOS Physical Memory Regions ===
 
*GameOS allocates nearly all physical memory of PS3 for itself&nbsp;!!! That is why new HV calls '''lv1_allocate_memory''' with large memory region sizes will fail.
*So when someone wants a large piece of physical memory, he can borrow it from GameOS's LPAR memory region that starts at '''0x700020000000'''. It can be used for example to send update packages to Update Manager which are very large.


==== Service Parameter Table ====
Here is the list of physical memory regions of GameOS i found in HV 3.41:


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Service Parameter
! Start Address
! Description
! Size
|-
! Access Right
| 0x01
! Max Page Size
|  
! Flags
! Real Addresses
|-
| 0x0
| 0x1000000
| 0x3
| 0x18
| 0x8
| 0x1000000 - 0x1FFF000
|-
|-
| 0x02
| 0x500000300000
|  
| 0xA0000
| 0x3
| 0x10
| 0x8
| 0x380000 - 0x38F000, 0x3B0000 - 0x3BF000, 0x1E0000 - 0x1FF000, 0x3C0000 - 0x3FF000, 0xFF00000 - 0xFF1F000
|-
|-
| 0x700020000000
| 0xE900000 (huge memory region)
| 0x3
| 0x14
| 0x0
| 0x400000 - 0x5FF000, 0x800000 - 0xFFF000, 0x2000000 - 0xFEFF000
|}
|}


== 0x6000 - Update Manager ==
== HTAB Memory Region class ==


{| class="wikitable FCK__ShowTableBorders"
This memory region is created when a HTAB is mapped into LPAR's address space. It's created in '''lv1_map_htab''' HV call.
|-
! Packet ID
! Description
|-
| 0x6001
| Update Package Tophalf
|-
| 0x6002
| Inspect Package Tophalf
|-
| 0x6003
| Get Package Info
|-
| 0x6004
| Get Fix Instruction
|-
| 0x6005
| Extract Package Tophalf
|-
| 0x6006
| Get Extract Package
|-
| 0x6007
| Check Integrity too
|-
| 0x6008
| Check Integrity too
|-
| 0x6009
| Get Token Seed
|-
| 0x600A
| Set Token
|-
| 0x600B
| Read EPROM
|-
| 0x600C
| Write EPROM
|-
| 0x600D
| Get Status
|-
| 0x600E
| Allocate Buffer
|-
| 0x600F
| Release Buffer
|-
| 0x6010
| Check Integrity
|-
| 0x6011
| Get Applicable Version
|-
| 0x6012
| Unknown
|}


*Update Manager service is accessed by GameOS '''syscall 863'''
=== vtable  ===


=== 0x6001 - Update Package Tophalf  ===
0x00357C98 (3.15)


*The result of the request can be checked by reading the value of repository node '''ss.update.request.&lt;Request ID&gt;''' periodically
=== Member variables  ===


=== 0x6002 - Inspect Package Tophalf  ===
offset 0xB0 - pointer to VAS object that owns the HTAB


*I have got access to this service through DM and tested it with PSGroove
=== Objects  ===
*This service can tell you if a package can be installed or not, the service just checks a package but does not install it
*'''Packages can be updated without GameOS&nbsp;!!! I'm using only HV calls and communicate directly with Dispatcher Manager and Update Manager'''
*I just sent a whole SCE package to GameOS through network, created a LPAR memory region and stored the file there
*It expects a SCE package that can be easily extracted from '''PUP file'''
*The data of SCE package can be passed either in SS packet itself or through LPAR memory of requester
*When the data of SCE package is too large for SS packet (SS packets are sent through DM, GameOS and DM communicate through VUART that has only 0x800 bytes buffer) then the data of SCE package has to be passed through GameOS LPAR memory. The requester sends a vector of LPAR memory addresses where the data of SCE package is stored and Update Manager maps it into the address space of Process 6
*E.g. '''Revoke List''' packages can be sent in SS packets because they are small (about 0x200 bytes). All other packages are too big to sent them in SS packets
*The service is actually split into 2 halfs: '''Top-Half''' and '''Bottom-Half'''
*The '''Top-Half''' is executed synchronously with service request and it sends a reply to the requester
*In the reply sent by '''Top-Half''' a '''Request ID''' (8 bytes) is returned to the requester
*'''Request ID''' is calculated by using '''SHA-1'''
*After the '''Top-Half''' is done, a reply is sent to the requester but the service just checked some input parameter upto now and the passed SCE package was not really checked yet
*The '''Bottom-Half''' is called asynchronously to the request, it does the real job, it checks the passed SCE package.
*The result of the request can be checked by reading the value of repository node '''ss.inspect.request.&lt;Request ID&gt;''' periodically
*I successfully tested this service with '''RL_FOR_PROGRAM.img''' from '''3.50 PUP file''' and the service returned '''Success''', so theoretically i could install this package on my PS3. But of course i want to downgrade and NOT to upgrade.


==== Inspect Package Tophalf Return Values  ====
Here is the list of HTAB memory region objects i found in HV 3.15.


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Error Code
! Address in HV dump
! Description
! LPAR id
! VAS id
! LPAR Start Address
! Size
! Flags
! log2(Page Size)
|-
|-
| 0x00000000
| 0x001FE0F0
| Success
| 2
| 3
| 0x500000C00000
| 0x100000
| 0xC000000000000000
| 0x14
|-
|-
| 0x00000013
| 0x003BD850
| Same Version/Older Version
| 2
|-
| 3
| 0x00000014
| 0x500004300000
| -
| 0x100000
|}
| 0xC000000000000000
 
| 0x14
=== 0x6003 - Get Package Info  ===
 
*I have got access to this service through DM and tested it with PSGroove
*The service expects one additional parameter: package type (valid values are 1-9)
*The service returns the version (8 bytes) of a package type installed
 
Here are the versions of packages installed on my PS3:
 
{| class="wikitable FCK__ShowTableBorders"
|-
! Package Type
! Returned Version
! Description
! Package Name in PUP File
|-
| 1
| 0x0003004100000000
| Core OS Package
| CORE_OS_PACKAGE.pkg
|-
|-
| 0x003BDEA0
| 2  
| 2  
| 0x0003004100000000
| Revoke List Package for Program
| RL_FOR_PROGRAM.img
|-
| 3  
| 3  
| 0x0002003000000000
| 0x500004500000
| Revoke List Package for Package
| 0x100000
| RL_FOR_PACKAGE.img
| 0xC000000000000000
|-
| 0x14
| 4
| 0xDEADBEAFFACEBABE
| -
| -
|-
| 5
| 0xDEADBEAFFACEBABE
| -
| -
|-
| 6
| 0x0003004000000000
| BD Firmware Package
| BDIT_FIRMWARE_PACKAGE.pkg, BDPT_FIRMWARE_PACKAGE_*.pkg
|-
| 7
| Invalid Parameter
| Bluetooth Firmware, dev_flash tarballs
| BLUETOOTH_FIRMWARE.pkg, dev_flash, dev_flash3
|-
| 8
| Invalid Parameter
| -
| -
|-
| 9
| Invalid Parameter
| SC Firmware Package
| SYS_CON_FIRMWARE_*.pkg
|}
|}


==== Decrypting and Extracting Packages with spu_pkg_rvk_verifier.self ====
=== GameOS HTAB ===


*I have managed to decrypt and extract '''Revoke List Packages 3.41 and 3.50''' by using SPE HV calls and '''spu_pkg_rvk_verifier.self'''  
*HTAB of GameOS is already mapped into address space of GameOS so that is why HV call '''lv1_map_htab''' will fail until you unmap it with '''lv1_unmap_htab'''  
*Important: Parameters to SPU module shuold be aligned, i used cache line alignment, don't know exactly alignment requerements. Or else some very strange things could happen. E.g SYSCON firmware was only partially decrypted when i used no cache line alignment.
*Effective address of GameOS HTAB is '''0x800000000F000000'''  
*I have also managed to decrypt and extract '''Core OS Packages 1.10, 1.18 Debug, 2.40, 2.80, 3.15, 3.41 and 3.50''' by using SPE HV calls and '''spu_pkg_rvk_verifier.self''' but it's compressed with '''zlib'''.Update Manager in Process 6 from 3.15 uses '''zlib 1.2.3 inflate''' to decompress it after it was decrypted and then it stores the data to flash memory.
*Virtual address of GameOS HTAB is '''0xF000000'''  
*I decompressed the decrypted Core OS Packages with zlib.
*Size of GameOS HTAB is '''0x40000'''  
*I am able now to decrypt and decompress all Core OS Packages
*GameOS HTAB supports large pages of size '''64K''' and '''1M'''  
*'''The decrypted and decompressed package CORE_OS_PACKAGE.pkg looks exactly like it's stored on flash.'''  
*GameOS HTAB can be easily dumped by reading 0x40000 bytes at EA 0x800000000F000000
*I also decrypted BD Firmwares '''BDIT_FIRMWARE_PACKAGE.pkg''' and '''BDPT_FIRMWARE_PACKAGE.pkg''' successfully. The firmware is not compressed.
*I also decrypted Bluetooth Firmware '''BLUETOOTH_FIRMWARE.pkg''' successfully. The firmware is encrypted and compressed.
*I also managed to decrypt System Controller Firmware '''SYS_CON_FIRMWARE_01050101.pkg''' from 3.41.
*Core OS Package 3.50 contains a new isolated SPU module that is not contained in older versions. The SPU module is '''manu_info_spu_module.self'''.
*Here links to PS3 Firmwares: [http://forums.penhacks.net/Thread-ALL-PS3-Firmware-to-date] and [http://www.ps3-hacks.com/category/3]


===== RL_FOR_PROGRAM.img 3.41 =====
=== GameOS SLB ===
<pre>Offset      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F


00000200  00 00 00 04 00 00 00 01  00 03 00 41 00 00 00 00  ...........A....
Here is the dump of SLB entries from GameOS 3.41:
00000210  00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00  ................
<pre>0x8000000008000000 0x0000000000000500
00000220  00 00 00 03 00 00 00 01 00 03 00 41 00 00 00 00  ...........A....
0x8000000208000000 0x0000000000020500
00000230  00 00 00 00 00 00 00 02 FF FF FF FF FF FF FF FF  ........ÿÿÿÿÿÿÿÿ
0x8000000300000000 0x0000000000030510
00000240  00 00 00 04 00 00 00 01 00 03 00 41 00 00 00 00  ...........A....
0x0000000000000000 0x0000000000000000
00000250  10 70 00 05 FF 00 00 01 FF FF FF FF FF FF FF FF  .p..ÿ...ÿÿÿÿÿÿÿÿ
0x0000000080000000 0x0000000000038C00
00000260  00 00 00 04 00 00 00 01 00 03 00 41 00 00 00 00  ...........A....
0x00000000A0000000 0x000000000003AC00
00000270  10 70 00 05 FE 00 00 01 FF FF FF FF FF FF FF FF  .p..þ...ÿÿÿÿÿÿÿÿ
0x00000000C0000000 0x000000000003CC00
00000280  00 00 00 04 00 00 00 01 00 03 00 41 00 00 00 00  ...........A....
0x0000000000000000 0x0000000000000000
00000290  10 70 00 05 FD 00 00 01 FF FF FF FF FF FF FF FF  .p..ý...ÿÿÿÿÿÿÿÿ
0x0000000000000000 0x0000000000000000
000002A0  00 00 00 04 00 00 00 01 00 03 00 41 00 00 00 00  ...........A....
0x0000000000000000 0x0000000000000000
000002B0  10 70 00 05 FC 00 00 01 FF FF FF FF FF FF FF FF  .p..ü...ÿÿÿÿÿÿÿÿ
0x0000000000000000 0x0000000000000000
000002C0  00 00 00 04 00 00 00 03 00 01 00 00 00 00 00 00  ................
0x0000000000000000 0x0000000000000000
000002D0  10 70 00 04 00 00 00 01 FF FF FF FF FF FF FF FF  .p......ÿÿÿÿÿÿÿÿ
0x0000000000000000 0x0000000000000000
</pre>
0x0000000000000000  0x0000000000000000
===== RL_FOR_PROGRAM.img 3.50 =====
0x0000000000000000 0x0000000000000000
<pre>Offset      0 1 2 3 4 5 6 7  8 9 A B C D E F
0x0000000000000000 0x0000000000000000
 
0x0000000000000000 0x0000000000000000
00000200  00 00 00 04 00 00 00 01 00 03 00 50 00 00 00 00  ...........P....
0x0000000000000000 0x0000000000000000
00000210  00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x0000000000000000 0x0000000000000000
00000220  00 00 00 03 00 00 00 01 00 03 00 50 00 00 00 00  ...........P....
0x0000000000000000 0x0000000000000000
00000230  00 00 00 00 00 00 00 02 FF FF FF FF FF FF FF FF  ........ÿÿÿÿÿÿÿÿ
0x0000000000000000 0x0000000000000000
00000240  00 00 00 04 00 00 00 01 00 03 00 50 00 00 00 00  ...........P....
0x0000000000000000 0x0000000000000000
00000250  10 70 00 05 FF 00 00 01 FF FF FF FF FF FF FF FF  .p..ÿ...ÿÿÿÿÿÿÿÿ
0x0000000000000000 0x0000000000000000
00000260  00 00 00 04 00 00 00 01 00 03 00 50 00 00 00 00  ...........P....
0x0000000000000000 0x0000000000000000
00000270  10 70 00 05 FE 00 00 01 FF FF FF FF FF FF FF FF  .p..þ...ÿÿÿÿÿÿÿÿ
0x0000000000000000 0x0000000000000000
00000280  00 00 00 04 00 00 00 01 00 03 00 50 00 00 00 00  ...........P....
0x0000000000000000 0x0000000000000000
00000290  10 70 00 05 FD 00 00 01 FF FF FF FF FF FF FF FF  .p..ý...ÿÿÿÿÿÿÿÿ
0x0000000000000000 0x0000000000000000
000002A0  00 00 00 04 00 00 00 01 00 03 00 50 00 00 00 00  ...........P....
0x0000000000000000 0x0000000000000000
000002B0  10 70 00 05 FC 00 00 01 FF FF FF FF FF FF FF FF  .p..ü...ÿÿÿÿÿÿÿÿ
0x0000000000000000 0x0000000000000000
000002C0  00 00 00 04 00 00 00 03 00 01 00 00 00 00 00 00  ................
0x0000000000000000  0x0000000000000000
000002D0  10 70 00 04 00 00 00 01 FF FF FF FF FF FF FF FF  .p......ÿÿÿÿÿÿÿÿ
0x0000000000000000 0x0000000000000000
</pre>
0x0000000000000000 0x0000000000000000
===== RL_FOR_PACKAGE.img 3.41 =====
0x8000000010057960 0x8000000000313E78
<pre>Offset      0 1 2 3 4 5 6 7  8 9 A B C D E F
0x8000000010057940 0x0000000000000000
 
0x800000000001B698 0x0000000000000000
00000200  00 00 00 03 00 00 00 02 00 01 00 00 00 00 00 00  ................
0x8000000010057930 0x8000000000490708
00000210  00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x80000000002B6C68 0x80000000003DE928
00000220  00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 02  ................
0x8000000010057EC0 0x80000000003DE920
00000230  00 00 00 08 00 05 00 00 00 00 00 00 00 00 00 00  ................
0x0000000000000000 0x8000000000309810
0x80000000004B3000 0x0000000000000000
0x8000000010057CC0 0x0000000000000000
0x80000000004AF000 0x80000000004E1F00
0x80000000100579C8 0x80000000100579C0
0x80000000100579E0 0x2400002200000000
0x80000000004CF5B0  0x8000000200012000
0x80000000100579F8 0x80000000100579F0
0x8000000010057A10 0x80000000004A3A00
0x80000000004CF5B0 0x80000000004C8D00
0x800000000001BF6C 0x80000000004CD400
0x800000000001B698 0x80000000004C8100
0x80000000100579D0 0x80000000004B48C0
0x0000000000001C08 0x0000000000000000
0x8000000010057A78 0x8000000010057A70
0x8000000010057A90 0x0000000000000000
0x80000000004CF90C 0x0000000000000000
0x0000000000000000 0x8000000010057A80
0x8000000010057A90 0x8000000000309810
0x80000000004CF62C 0x0000000000000000
0x8000000010057CC0 0x0000000000000000
0x80000000004AF000 0x80000000004B48C0
0x00004000001C0000 0x0000000000000001
0x00000000D0000000 0x0000A8E3EE7D10DA
0x0000000000000000 0x0000000000000000
0x80000000004D8088 0x80000000004D9000
</pre>
</pre>
===== RL_FOR_PACKAGE.img 3.50 =====
== SPE MMIO Memory Region class ==
<pre>Offset      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F


00000200  00 00 00 03 00 00 00 02  00 01 00 00 00 00 00 00  ................
This type of memory region represents MMIO memory region of a SPE. It's created e.g. in '''lv1_construct_logical_spe''' or in '''syscall 0x10040'''.  
00000210  00 00 00 01 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000220  00 00 00 01 00 00 00 00  00 00 00 01 00 00 00 02  ................
00000230  00 00 00 08 00 05 00 00  00 00 00 00 00 00 00 00  ................
</pre>
===== CORE_OS_PACKAGE.pkg 3.15  =====


Here is a piece of data from decrypted and decompressed package.
=== vtable ===
<pre>Offset      0 1  2  3  4  5  6  7  8  9  A  B  C  D  E  F


00000000  00 00 00 01 00 00 00 17  00 00 00 00 00 6F FF E0  .............oÿà
0x003583F8 (3.15)
00000010  00 00 00 00 00 00 04 60  00 00 00 00 00 04 00 00  .......`........
00000020  63 72 65 73 65 72 76 65  64 5F 30 00 00 00 00 00  creserved_0.....
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000040  00 00 00 00 00 04 04 60  00 00 00 00 00 00 00 08  .......`........
00000050  73 64 6B 5F 76 65 72 73  69 6F 6E 00 00 00 00 00  sdk_version.....
00000060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000070  00 00 00 00 00 04 04 80  00 00 00 00 00 01 E5 CC  .......€......åÌ
00000080  6C 76 31 6C 64 72 00 00  00 00 00 00 00 00 00 00  lv1ldr..........
00000090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000A0  00 00 00 00 00 05 EA 80  00 00 00 00 00 01 6D A0  ......ê€......m&nbsp;
000000B0  6C 76 32 6C 64 72 00 00  00 00 00 00 00 00 00 00  lv2ldr..........
000000C0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000D0  00 00 00 00 00 07 58 80  00 00 00 00 00 01 2E 44  ......X€.......D
000000E0  69 73 6F 6C 64 72 00 00  00 00 00 00 00 00 00 00  isoldr..........
000000F0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000100  00 00 00 00 00 08 87 00  00 00 00 00 00 01 DA E4  ......‡.......Úä
00000110  61 70 70 6C 64 72 00 00  00 00 00 00 00 00 00 00  appldr..........
00000120  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000130  00 00 00 00 00 0A 61 E4  00 00 00 00 00 00 FA CC  ......aä......úÌ
00000140  73 70 75 5F 70 6B 67 5F  72 76 6B 5F 76 65 72 69  spu_pkg_rvk_veri
00000150  66 69 65 72 2E 73 65 6C  66 00 00 00 00 00 00 00  fier.self.......
00000160  00 00 00 00 00 0B 5C B0  00 00 00 00 00 00 5C 94  ......\°......\”
00000170  73 70 75 5F 74 6F 6B 65  6E 5F 70 72 6F 63 65 73  spu_token_proces
00000180  73 6F 72 2E 73 65 6C 66  00 00 00 00 00 00 00 00  sor.self........
00000190  00 00 00 00 00 0B B9 44  00 00 00 00 00 00 65 D0  ......¹D......eÐ
000001A0  73 70 75 5F 75 74 6F 6B  65 6E 5F 70 72 6F 63 65  spu_utoken_proce
000001B0  73 73 6F 72 2E 73 65 6C  66 00 00 00 00 00 00 00  ssor.self.......
000001C0  00 00 00 00 00 0C 1F 14  00 00 00 00 00 01 53 2C  ..............S,
000001D0  73 63 5F 69 73 6F 2E 73  65 6C 66 00 00 00 00 00  sc_iso.self.....
000001E0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000001F0  00 00 00 00 00 0D 72 40  00 00 00 00 00 00 44 98  [email protected]˜
00000200  61 69 6D 5F 73 70 75 5F  6D 6F 64 75 6C 65 2E 73  aim_spu_module.s
00000210  65 6C 66 00 00 00 00 00  00 00 00 00 00 00 00 00  elf.............
00000220  00 00 00 00 00 0D B6 D8  00 00 00 00 00 00 D7 F0  ......¶Ø......×ð
00000230  73 70 70 5F 76 65 72 69  66 69 65 72 2E 73 65 6C  spp_verifier.sel
00000240  66 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  f...............
00000250  00 00 00 00 00 0E 8E C8  00 00 00 00 00 00 80 8C  ......ŽÈ......€Œ
00000260  6D 63 5F 69 73 6F 5F 73  70 75 5F 6D 6F 64 75 6C  mc_iso_spu_modul
00000270  65 2E 73 65 6C 66 00 00  00 00 00 00 00 00 00 00  e.self..........
00000280  00 00 00 00 00 0F 0F 54  00 00 00 00 00 00 88 B8  .......T......ˆ¸
00000290  6D 65 5F 69 73 6F 5F 73  70 75 5F 6D 6F 64 75 6C  me_iso_spu_modul
000002A0  65 2E 73 65 6C 66 00 00  00 00 00 00 00 00 00 00  e.self..........
000002B0  00 00 00 00 00 0F 98 0C  00 00 00 00 00 00 C0 78  ......˜.......Àx
000002C0  73 76 5F 69 73 6F 5F 73  70 75 5F 6D 6F 64 75 6C  sv_iso_spu_modul
000002D0  65 2E 73 65 6C 66 00 00  00 00 00 00 00 00 00 00  e.self..........
000002E0  00 00 00 00 00 10 58 84  00 00 00 00 00 00 5D B0  ......X„......]°
000002F0  73 62 5F 69 73 6F 5F 73  70 75 5F 6D 6F 64 75 6C  sb_iso_spu_modul
00000300  65 2E 73 65 6C 66 00 00  00 00 00 00 00 00 00 00  e.self..........
00000310  00 00 00 00 00 10 B6 34  00 00 00 00 00 00 22 A0  ......¶4......"&nbsp;
00000320  64 65 66 61 75 6C 74 2E  73 70 70 00 00 00 00 00  default.spp.....
00000330  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000340  00 00 00 00 00 10 D9 00  00 00 00 00 00 12 B1 70  ......Ù.......±p
00000350  6C 76 31 2E 73 65 6C 66  00 00 00 00 00 00 00 00  lv1.self........
00000360  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000370  00 00 00 00 00 23 8A 80  00 00 00 00 00 03 E8 28  .....#Š€......è(
00000380  6C 76 30 00 00 00 00 00  00 00 00 00 00 00 00 00  lv0.............
00000390  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000003A0  00 00 00 00 00 27 72 A8  00 00 00 00 00 16 EE B8  .....'r¨......î¸
000003B0  6C 76 32 5F 6B 65 72 6E  65 6C 2E 73 65 6C 66 00  lv2_kernel.self.
000003C0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000003D0  00 00 00 00 00 3E 61 60  00 00 00 00 00 07 0F 94  .....&gt;a`.......”
000003E0  65 75 72 75 73 5F 66 77  2E 62 69 6E 00 00 00 00  eurus_fw.bin....
000003F0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000400  00 00 00 00 00 45 70 F4  00 00 00 00 00 07 FC 48  .....Epô......üH
00000410  65 6D 65 72 5F 69 6E 69  74 2E 73 65 6C 66 00 00  emer_init.self..
00000420  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000430  00 00 00 00 00 4D 6D 3C  00 00 00 00 00 06 16 00  .....Mm&lt;........
00000440  68 64 64 5F 63 6F 70 79  2E 73 65 6C 66 00 00 00  hdd_copy.self...
00000450  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................


00040460  33 31 35 2E 30 30 30 0A  00 00 00 00 00 00 00 00  315.000.........
=== Member variables ===
00040470  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
</pre>
===== BDIT_FIRMWARE_PACKAGE.pkg 3.50 =====


Here is a piece of data from decrypted package.
=== Objects ===
<pre>Offset      0 1  2  3  4  5  6  7  8  9  A  B  C  D  E  F


00000300  43 6F 70 79 72 69 67 68  74 28 43 29 20 32 30 30  Copyright(C) 200
Here is the list of SPE memory region objects i found in HV 3.15.
00000310  35 2D 32 30 30 36 2C 20  53 6F 6E 79 20 43 6F 6D  5-2006, Sony Com
00000320  70 75 74 65 72 20 45 6E  74 65 72 74 61 69 6E 6D  puter Entertainm
00000330  65 6E 74 20 49 6E 63 2E  1A 00 00 00 00 00 00 00  ent Inc.........
00000340  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000350  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000360  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000370  41 96 18 D3 2D 8F 0F 68  11 4D A7 09 E4 1F A7 6F  A–.Ó-.h.M§.ä.§o
00000380  EF 29 48 A0 E9 F2 A8 F0  CC 4B F3 4D E0 4A B0 17  ï)H&nbsp;éò¨ðÌKóMàJ°.
00000390  C2 DA 07 5F 96 B3 C8 8D  E1 06 2E 3A 1D A7 FD 20  ÂÚ._–³Èá..:.§ý
</pre>
===== BDPT_FIRMWARE_PACKAGE_301R.pkg 3.50  =====
 
Here is a piece of data from decrypted package.
<pre>Offset      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
 
00000300  43 6F 70 79 72 69 67 68  74 28 43 29 20 32 30 30  Copyright(C) 200
00000310  35 2D 32 30 30 39 2C 20  53 6F 6E 79 20 43 6F 6D  5-2009, Sony Com
00000320  70 75 74 65 72 20 45 6E  74 65 72 74 61 69 6E 6D  puter Entertainm
00000330  65 6E 74 20 49 6E 63 2E  1A 00 00 00 00 00 00 00  ent Inc.........
00000340  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000350  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000360  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000370  80 18 D2 E4 22 AA 2B D7  85 47 F4 40 53 9A 04 0C  €.Òä"ª+×…Gô@Sš..
00000380  D0 B8 A5 04 20 51 9E 90  09 4F 2E 78 BA 32 C0 EA  и¥. Qž.O.xº2Àê
00000390  E9 61 96 ED D8 2A 70 C0  59 68 4E B2 47 25 9C 97  éa–íØ*pÀYhN²G%œ—
</pre>
===== BLUETOOTH_FIRMWARE.pkg 3.41  =====
<pre>Offset      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
 
00000000  52 43 32 39 5F 66 69 72  6D 77 61 72 65 5F 66 6F  RC29_firmware_fo
00000010  6F 74 65 72 2E 64 66 75  00 00 00 00 00 00 00 00  oter.dfu........
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000060  00 00 00 00 30 30 30 30  36 34 34 00 30 30 30 30  ....0000644.0000
00000070  30 30 30 00 30 30 30 30  30 30 30 00 30 30 30 30  000.0000000.0000
00000080  31 35 36 36 33 30 30 00  31 31 30 36 34 33 34 36  1566300.11064346
00000090  33 30 36 00 30 31 35 34  36 33 00 20 30 00 00 00  306.015463. 0...
000000A0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000B0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000C0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000D0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000E0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000F0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000100  00 75 73 74 61 72 20 20  00 72 6F 6F 74 00 00 00  .ustar  .root...
00000110  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000120  00 00 00 00 00 00 00 00  00 72 6F 6F 74 00 00 00  .........root...
00000130  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000140  00 00 00 00 00 00 00 00  00 30 30 30 30 30 30 30  .........0000000
00000150  00 30 30 30 30 30 30 30  00 00 00 00 00 00 00 00  .0000000........
 
 
000A5950  84 1B 00 C0 94 04 00 00  74 06 00 00 45 75 72 75  „..À”...t...Euru
000A5960  73 5F 50 72 69 6D 61 72  79 5F 50 68 79 00 00 00  s_Primary_Phy...
000A5970  4D 61 72 76 65 6C 6C 5F  41 50 00 00 94 BB 01 C0  Marvell_AP..”».À
 
 
000B7CC0  00 00 00 00 01 10 60 23  4D 61 72 76 65 6C 6C 20  ......`#Marvell
000B7CD0  46 69 72 6D 77 61 72 65  20 53 44 4B 20 56 65 72  Firmware SDK Ver
000B7CE0  73 69 6F 6E 20 32 2E 33  2E 30 54 74 5D 04 02 2B  sion 2.3.0Tt]..+
000B7CF0  0F 14 E1 36 04 32 0A 1A  FD 08 32 1A 1A C1 08 02  ..á6.2..ý.2..Á..
 
 
000F42B0  44 6F 53 68 61 72 65 64  4B 65 79 53 65 71 31 3A  DoSharedKeySeq1:
000F42C0  20 45 6E 74 65 72 65 64  20 2D 2D 2D 20 72 73 70    Entered --- rsp
000F42D0  4D 61 63 20 3D 20 25 30  32 78 3A 25 30 32 78 3A  Mac =&nbsp;%02x:%02x:
000F42E0  25 30 32 78 3A 25 30 32  78 3A 25 30 32 78 3A 25  &nbsp;%02x:%02x:%02x:%
000F42F0  30 32 78 0A 00 00 00 00  6D 6C 6D 65 41 75 74 68  02x.....mlmeAuth
000F4300  44 6F 53 68 61 72 65 64  4B 65 79 53 65 71 31 3A  DoSharedKeySeq1:
000F4310  20 56 61 6C 69 64 61 74  69 6F 6E 20 66 61 69 6C    Validation fail
000F4320  65 64 20 2D 2D 2D 20 72  73 70 4D 61 63 20 3D 20  ed --- rspMac =
000F4330  25 30 32 78 3A 25 30 32  78 3A 25 30 32 78 0A 00  &nbsp;%02x:%02x:%02x..
000F4340  6D 6C 6D 65 41 75 74 68  44 6F 53 68 61 72 65 64  mlmeAuthDoShared
000F4350  4B 65 79 53 65 71 33 3A  20 76 61 6C 69 64 61 74  KeySeq3: validat
000F4360  69 6F 6E 20 66 61 69 6C  65 64 21 20 2D 2D 2D 20  ion failed! ---
000F4370  72 73 70 4D 61 63 20 3D  20 25 30 32 78 3A 25 30  rspMac =&nbsp;%02x:%0
000F4380  32 78 3A 25 30 32 78 0A  00 65 65 70 72 6F 6D 00  2x:%02x..eeprom.
000F4390  62 74 5F 68 63 69 00 62  74 5F 75 61 72 74 00 75  bt_hci.bt_uart.u
000F43A0  73 62 30 00 75 73 62 31  00 4F 53 41 00 77 6C 61  sb0.usb1.OSA.wla
000F43B0  F3 B8 E9 70 01 00 00 00  1C 6B 03 00 00 02 00 00  ó¸ép.....k......
</pre>
===== SYS_CON_FIRMWARE_01050101.pkg 3.41  =====
<pre>Offset      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
 
00000300  1B 2D 70 0F AB 5E B3 99  68 20 FE 3D E1 80 6A 1D  .-p.«^³™h þ=á€j.
00000310  B8 FD 37 CF CD 45 85 AB  51 F7 05 E3 EA 32 A5 EA  ¸ý7ÏÍE…«Q÷.ãê2¥ê
00000320  67 45 F9 48 00 00 00 00  00 10 00 00 C0 0F 00 00  gEùH........À...
00000330  8B 04 07 F9 9B A2 90 3A  75 89 F1 42 12 59 DA 0D  ‹..ù›¢:u‰ñB.YÚ.
00000340  21 7C A2 C3 5A E4 78 00  10 8D 4B F7 A2 73 9C 63  &nbsp;!|¢ÃZäx..K÷¢sœc
00000350  5D 8D 5D 49 16 C7 6F 2C  AD 33 FE 1F D3 6C A1 CA  ]]I.Ço,­3þ.Ól¡Ê
00000360  BA AD 2B FE 8F 33 71 D7  C5 E6 5C FF BF 77 6C 80  º­+þ3q×Åæ\ÿ¿wl€
00000370  F2 BE 11 BB 3C 52 52 DC  A9 68 E5 24 AD 4F F3 48  ò¾.»&lt;RRÜ©hå$­OóH
</pre>
=== 0x6005 - Extract Package Tophalf  ===
 
*The result of the request can be checked by reading the value of repository node '''ss.extract.request.&lt;Request ID&gt;''' periodically
 
=== 0x600B - Read EEPROM  ===
 
*I have got read access to EEPROM of Update Manager through DM and tested it with PSGroove
*I read PRODUCT_MODE from it successfully, PRODUCT_MODE = 0x000000FF
*The service expects one additional parameter: offset (4 bytes)
*The service accepts only some predefined offsets
*The service returns the specified offset and the value at this offset
 
==== EEPROM Offset Table  ====
 
Here is the table of EEPROM offsets that can be accessed through Update Manager (3.15):


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Offset
! Address in HV dump
! LPAR id
! SPE
! LPAR Start Address
! Size  
! Size  
! Description
! Physical Address
! Flags
! log2(Page Size)
|-
|-
| 0x2FF8
| 0x003ABC20
| 2
| 1  
| 1  
| Factory Bit
| 0x4C0000880000
| 0x80000
| 0x20000080000
| 0xA000000000000000
| 0xC
|-
|-
| 0x48C02
| 0x003AAD70
| 1
| 2
| unknown
| 2
| 0x4C0000980000
| 0x80000
| 0x20000100000
| 0xA000000000000000
| 0xC
|-
|-
| 0x48C06
| 0x003A8880
| 1
| 2
| FSELF Control Flag
| 3
| 0x4C0000780000
| 0x80000
| 0x20000180000
| 0xA000000000000000
| 0xC
|-
|-
| 0x48C07
| 0x003B4F70
| 1
| 2
| Product Mode (UM allows to read this offset, it can be also written but only when already in product mode)
| 4
| 0x4C0000A80000
| 0x80000
| 0x20000200000
| 0xA000000000000000
| 0xC
|-
|-
| 0x48C0A
| 0x003AB700
| 1
| 2
| QA Flag
| 5
| 0x4C0000680000
| 0x80000
| 0x20000280000
| 0xA000000000000000
| 0xC
|-
|-
| 0x48C13
| 0x003B5BE0
| 1
| 2
| Device Type
| 6
|-
| 0x4C0000B80000
| 0x48C30
| 0x80000
| 1
| 0x20000300000
| SPE number Usally 0x06, can be set to 0x07 to enable the 8 SPE
| 0xA000000000000000
|-
| 0xC
| 0x48C42
| 1
| HDD Copy Mode
|-
| 0x48C50
| 0x10
| Debug Support Flag
|-
| 0x48C60
| 1
| Update Status
|-
| 0x48C61
| 1
| Recover Mode Flag
|-
| 0x48D3E
| 0x50
| QA Token (UM doesn't allow access to this offset but SC Manager can read/write it)
|}
|}


=== 0x600C - Write EEPROM ===
== SPE Shadow Registers Memory Region class ==


*Writting to EEPROM of Update Manager is also possible through DM
This type of memory region represents shadow registers memory region of a SPE. It's created e.g. in '''lv1_construct_logical_spe''' or in '''syscall 0x10040'''.
*Tested this service successfully with QA flag


=== 0x6010 - Check Integrity ===
=== vtable ===


*This service checks integrity of important files stored on '''/dev/rflash1''', e.g. '''lv0''' or '''lv1'''
0x00358448 (3.15)
*The service is used e.g. by System Manager
*When '''product mode''' is NOT '''0xFF''' then check is skipped&nbsp;!!!


=== 0x6011 - Get Applicable Version ===
=== Objects ===


*I have got access to this service through DM and PSGroove and tested it
Here is the list of SPE Shadow Registers memory region objects i found in HV 3.15.
*The service expects one additional unknown parameter of size 4 bytes, it has to be 0x00000001 or else the service fails<br> (sc863(0x6011,1,out:uint64_t,0,0,0,0,0))
 
Here is the return value:
<pre>00 00 00 01 00 00 00 00 00 03 00 20 00 00 00 00 00 00 00 00 00 00 00 01
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01
</pre>
 
=== BD Firmware Update  ===
 
*Update Manager in HV Process 6 updates BD firmware through '''ATAPI Interface''' of '''/dev/rbd0''' device.
*BD firmware is sent to BD drive by using '''ATAPI Write Buffer (0x3B)''' command with '''Mode 0x07 (Download microcode with offsets and save)''' and '''Buffer ID 0x00'''.
*The current BD drive firmware version and hash is also stored by and retrieved from SYSCON by using '''SC Manager Get/Set Region Data (0x9006/0x9007)''' service. After successfull BD firmware update, Update Manager sends the new firmware version and hash to SYSCON.
*BD firmware package is decrypted, SCE header size + 0x80 bytes are skipped and data beginning with copyright message is sent to BD drive.
*BD firmware is sent packet wise, one packet is at most 0x8000 bytes.
*After each sent packet, Update Manager checks the result by using '''ATAPI Request Sense (0x3)''' command.
*Theoretically, BD firmware update can be done also from GameOS by using ATAPI interface of the BD drive.
 
==== Detecting BD Drive Type, Generation and Revision  ====
 
*To detect BD drive type, Update Manager uses '''ATAPI Inquiry''' command.
*To detect BD drive generation, Update Manager uses '''ATAPI Mode Sense 10''' command.
 
===== BD Drive Type Table  =====
 
Here is the BD Drive Type Table extracted from HV Process 6 (3.15):


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Index
! Address in HV dump
! Vendor Identification String
! LPAR id
! Drive Type
! SPE
|-
! LPAR Start Address
| 0
! Size
| <pre>"SONY    EmerFlashROM"</pre>
! Physical Address
| 0x2100000000000001
! Flags
! log2(Page Size)
|-
|-
| 0x003ABDA0
| 2
| 1  
| 1  
| <pre>"SONY    PS-EMBOOT  300R"</pre>
| 0x300000012000
| 0x2100000000000001
| 0x1000
| -  
| 0xA000000000000000
| 0xC
|-
|-
| 0x003B4290
| 2
| 2  
| 2  
| <pre>"SONY    BDRW AQUAM(BDIT)"</pre>
| 0x300000014000
| 0x1100000000000001
| 0x1000
| -
| 0xA000000000000000
| 0xC
|-
|-
| 0x003A8A00
| 2
| 3  
| 3  
| <pre>"SONY    PS-SYSTEM  300R"</pre>
| 0x300000010000
| 0x1100000000000001
| 0x1000
| -  
| 0xA000000000000000
| 0xC
|-
|-
| 0x003B50F0
| 2
| 4  
| 4  
| <pre>"SONY    PS-SYSTEM  V300"</pre>
| 0x300000016000
| 0x1100000000000001
| 0x1000
| -  
| 0xA000000000000000
| 0xC
|-
|-
| 0x001FFC90
| 2
| 5  
| 5  
| <pre>"SCEI    EMER-FLASH-8"</pre>
| 0x30000000E000
| 0x2200000000000002
| 0x1000
| -  
| 0xA000000000000000
| 0xC
|-
|-
| 0x003AE5B0
| 2
| 6  
| 6  
| <pre>"SONY    PS-EMBOOT  301R"</pre>
| 0x300000018000
| 0x2200000000000002
| 0x1000
|-
| -  
| 7
| 0xA000000000000000
| <pre>"SONY    PS-SYSTEM  301R"</pre>
| 0xC
| 0x1200000000000002
|}
|-
 
| 8
== Device MMIO Memory Region class  ==
| <pre>"SONY    PS-EMBOOT  302R"</pre>
 
| 0x2200000000000003
This type of memory region is created when a device MMIO region is mapped into LPAR address space, e.g. in '''lv1_map_device_mmio_region'''.
|-
 
| 9
=== vtable  ===
| <pre>"SONY    PS-SYSTEM  302R"</pre>
 
| 0x1200000000000003
0x00352468 (3.15)
|-
 
| 10
=== Member variables  ===
| <pre>"SONY    PS-EMBOOT  303R"</pre>
 
| 0x2200000000000004
offset 0xA8 - physical address where the device MMIO region is mapped to
|-
 
| 11
=== Objects  ===
| <pre>"SONY    PS-SYSTEM  303R"</pre>
 
| 0x1200000000000004
Here is the list of Device MMIO memory region objects i found in HV 3.15.
 
{| class="wikitable FCK__ShowTableBorders"
|-
|-
| 12
! Address in HV dump
| <pre>"SONY    PS-EMBOOT  304R"</pre>
! LPAR id
| 0x2200000000000005
! LPAR Start Address
! Size
! Flags
! log2(Page Size)
! Physical Address
! Device
|-
| 0x001FDF00
| 2
| 0x4000001D0000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003010000
| USB controller
|-
|-
| 13
| 0x003B3850
| <pre>"SONY    PS-SYSTEM  304R"</pre>
| 2
| 0x1200000000000005
| 0x400000200000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003020000
| USB controller
|-
|-
| 14
| 0x003B6E50
| <pre>"SONY    PS-EMBOOT  306R"</pre>
| 2
| 0x2200000000000007
| 0x4000001E0000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003810000
| USB controller
|-
|-
| 15
| 0x003B9950
| <pre>"SONY    PS-SYSTEM  306R"</pre>
| 2
| 0x1200000000000007
| 0x4000001F0000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003820000
| USB controller
|}
|}


==== Methods (HV Process 6) ====
== GPU Device Memory Region class ==


update_manager_update_bd_firmware - 0x800064BC (3.15)
This type of memory region is created e.g. in '''lv1_gpu_open''', '''lv1_gpu_device_map''' and '''lv1_undocumented_function_114'''.  


bd_updater_prepare_drive - 0x80011A88 (3.15)
=== vtable  ===


bd_updater_send_firmware - 0x80011544 (3.15)  
0x00357C48 (3.15)  


bd_updater_disable_reqsense - 0x80010410 (3.15)
=== Member variables  ===


bd_updater_enable_reqsense - 0x800104D8 (3.15)
offset 0xA8 - physical address


send_atp_command - 0x80023B10 (3.15)
=== Objects  ===


== 0x9000 - SC Manager  ==
Here is the list of Device GPU memory region objects i found in HV 3.15.  
 
*SC Manager cannot be accessed directly by using DM unfortunately (DM discards all requests) but it's used by other services that are accessable through DM
*E.g. Update Manager services "Read EEPROM" and "Write EEPROM" send requests to SC Manager services "Read EEPROM" and "Write EEPROM"
*SC Manager runs '''sc_iso.self'''
* With full HV rights you could patch Dispatcher Manager and enable access to SC Manager from GameOS.


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Packet ID
! Address in HV dump
! Description
! LPAR id
! LPAR Start Address
! Size
! Flags
! log2(Page Size)
! Physical Address
|-
|-
| 0x9001
| 0x003AF380
| Get SRH
| 2
| 0x700190000000
| 0xFE00000
| 0x8000000000000000
| 0x14
| 0x28080000000
|-
|-
| 0x9002
| 0x003AF500
| Set SRH
| 2
| 0x4000001A0000
| 0xC000
| 0x8000000000000000
| 0xC
| 0x3C0000
|-
|-
| 0x9003
| 0x003AF680
| Encrypt
| 2
| 0x4800006C0000
| 0x40000
| 0x8000000000000000
| 0xC
| 0x2808FE00000
|-
|-
| 0x9004
| 0x003AFC30
| Decrypt
| 2
| 0x440000380000
| 0x20000
| 0x8000000000000000
| 0xC
| 0x28000C00000
|-
|-
| 0x9005
| 0x003BB420
| Init For VTRM
| 2
|-
| 0x3C0000108000
| 0x9006
| 0x8000
| Get Region Data
| 0x8000000000000000
|-
| 0xC
| 0x9007
| 0x28000080100
| Set Region Data
|-
| 0x9008
| Set RTC
|-
| 0x9009
| Get Time
|-
| 0x900A
| Set Time
|-
| 0x900B
| Read EPROM
|-
| 0x900C
| Write EPROM
|-
| 0x900D
| Init For Updater
|-
| 0x900E
| Get SC Status
|-
| 0x9011
| SC Binary Patch
|-
| 0x9012
| SC RTC Factory
|-
| 0x9013
| Correct RTC Factory
|-
| 0x9014
| Set SC Status
|-
| 0x9015
| Backup Root Info
|-
| 0x9016
| Restore Root Info
|}
|}


=== 0x9001 - SC Get SRH  ===
== Direct Map Memory Region class ==


<pre>
This type of memory region is created in HV call '''lv1_undocumented_function_114'''.
struct ss_sc_mgr_get_srh
'''lv1_undocumented_function_114''' allows you to map any memory address into LPAR's memory address.
{
    u8 field0[20];
    u8 res1[4];
    u8 field18[20];
    u8 res2[4];
};
</pre>


=== 0x9003 - SC Encrypt  ===
* The HV call '''lv1_undocumented_function_115''' destroys a memory region of this type.
* HV allows GameOS to create objects of this type of size 0 only !!! But it can be exploited with a dangling HTAB entry.


*There are 5 different types/kinds of encryption: 1 - 5.
=== vtable  ===


<pre>
0x00357C48 (3.15)
struct ss_sc_mgr_encrypt
{
    u32 type;              /* 1 - 5 */
    u8 res[4];
    u8 field8[16];
    u8 field18[16];
    u64 field28;
};
</pre>


=== 0x9004 - SC Decrypt ===
=== Member variables ===


*There are 5 different types/kinds of decryption: 1 - 5.
offset 0xA8 - physical address
*'''Virtual TRM Decrypt Master (0x200E)''' service uses e.g. decryption type 4.


=== 0x9006 - SC Get Region Data  ===
=== Exploiting HV with memory glitching and HV call lv1_undocumented_function_114 ===


*This service expects an ID. The valid range of ID is 0 - 15.
Here is a short description of the method i used to exploit HV from GameOS 3.15 and 3.41.
*E.g. Update Manager uses this service to retrieve hash and version of some SELFs and firmwares, e.g. '''lv0''' and '''lv1'''.


<pre>
* First i used the Geohot's method to create a dangling HTAB entry.
struct ss_sc_mgr_get_region_data
* Making memory glitch work on GameOS was the largest of my obstacles but i solved it and i'm able to create a dangling HTAB entry from GameOS within 1-3 minutes.
{
* Then i created many '''Direct Map Memory Region''' objects of size 0 with HV call '''lv1_undocumented_function_114''' and checked if they are within the page to which the dangling HTAB entry points to.
    u64 id;
* When i found one such '''Direct Map Memory Region''' object i patched the size of this object to 0x1000. Then i pointed this memory region object to the code of HV call '''lv1_undocumented_function_114''' and patched 4 bytes in this HV call which allows me to create any '''Direct Map Memory Region''' objects without any restrictions.
    u64 data_size;    /* max 0x30 bytes */
* Function '''LPAR_construct_direct_mapping_mem_region''' which is used by HV call '''lv1_undocumented_function_114''' has a parameter (register %r9) and when this parameter is not 0 then HV will allow you to create any '''Direct Map Memory Region''' objects without restrictions, but unfortunately the HV call '''lv1_undocumented_function_114''' passes 0 in this parameter, so i just patched it.
    u8 data[0];
* Then i mapped whole HV memory range with the patched HV call '''lv1_undocumented_function_114''' into the address space of GameOS.
};
* And now you have read/write access to the whole HV.
</pre>
* $ONY could fix this exploit by disallowing creating of '''Direct Map Memory Region''' objects of size 0, but i know tons of other HV C++ classes which will allow me to exploit the HV in a similar way, so it wouldn't bring $ONY anything :-) And they have to change member variable offsets in those objects to make sure that i cannot patch them easily :-)
 
== Methods  ==
 
LPAR_get_memory_region_by_start_address - 0x002C7C40 (3.15)


==== Update Package Type - ID Mapping Table ====
LPAR_get_memory_region_by_address - 0x002C7DA8 (3.15)


{| class="wikitable FCK__ShowTableBorders"
LPAR_mem_addr_to_phys_addr(LPAR id, LPAR address, phys_addr) - 0x002FB8F0 (3.15)
|-
! Update Package Type
! ID
|-
| 1
| 0
|-
| 2
| 2
|-
| 3
| 4
|-
| 4
| 6
|-
| 5
| 7
|-
| 6
| 8
|}


=== 0x9007 - SC Set Region Data  ===
LPAR_construct_direct_mapping_mem_region - 0x002D4D04 (3.15)


*This service expects an ID. The valid range of ID is 0 - 15.
= Network Devices  =
*E.g. Update Manager uses this service to store hash and version of some SELFs and firmwares, e.g. '''lv0''' and '''lv1'''.


<pre>
== Ethernet Gelic Device  ==
struct ss_sc_mgr_set_region_data
{
    u64 id;
    u64 data_size;    /* max 0x30 bytes */
    u8 data[0];
};
</pre>


=== 0x900B - SC Read EPROM  ===
device id = 0


* There are 2 ways to access SC EPROM: '''NVS Service''' and '''Device Access Service'''.
MAC Address: 00:1F:A7:C6:2A:C5
* '''NVS Service''' uses '''Block ID''' and '''Block Offset'''.
* Not all EPROM offsets can be accessed through SC Manager.


<pre>
device memory base address = 0x24003004000 (size = 0x1000)
struct ss_sc_mgr_read_eprom
{
    u32 offset;
    u8 res1[4];
    u32 nread;              /* max 0x100 bytes */
    u8 res2[4];
    u64 buf_size;
    u8 buf[0];
    /* here follows buf */
};
</pre>


==== EPROM Offset - Block ID and Block Offset Mapping Table (NVS Service) ====
== WLAN Gelic Device  ==


{| class="wikitable FCK__ShowTableBorders"
device id = 0
|-
 
! EPROM Offset
MAC Address: 02:1F:A7:C6:2A:C5 (locally administered)
! Block ID
! Block Offset
! Notes
|-
| 0x48000 - 0x480FF
| 0x00
| 0x48000 - 0x480FF
|
|-
| 0x48800 - 0x488FF
| 0x01
| 0x48800 - 0x488FF
|
|-
| 0x48C00 - 0x48CFF
| 0x02
| 0x48C00 - 0x48CFF
|
|-
| 0x48D00 - 0x48DFF
| 0x03
| 0x48D00 - 0x48DFF
|
|-
| 0x2F00 - 0x2FFF
| 0x10
| 0x2F00 - 0x2FFF
| "Industry Area" aka OS Version Area
|-
| 0x3000 - 0x30FF
| 0x20
| 0x3000 - 0x30FF
| "CS Area"
|-
| All other offsets
| Invalid
| Invalid
|}


=== 0x900C - SC Write EPROM ===
=== Net Manager ===


<pre>
*Net Manager runs in Process 9
struct ss_sc_mgr_write_eprom
*It sends commands to '''/dev/sc1''' to reset WLAN Gelic device
{
*It opens '''/dev/net0''', sets MAC address and writes device firmware '''eurus_fw.bin''' to WLAN device by using '''ioctl''' syscall
    u32 offset;
    u8 res1[4];
    u32 nwrite;
    u8 res2[4];
    u64 buf_size;
    u8 buf[0];
    /* here follows buf */
};
</pre>


=== 0x900E - SC Get Status ===
=== /dev/net0  ===


Here is what the service returned on my fat PS3:
The device supports 3 ioctl commands:  
<pre>
0x00 0x00 0x00 0x03 0x00 0x00 0x00 0x00 0xC0 0x00 0x00 0xFF 0x00 0x00 0x00 0x00
</pre>


So, '''version''' is '''0x00000003''' and '''mode''' is '''0xC00000FF'''.
*0 - 0x002AC10C (3.15)
*1 - 0x002AC250 (3.15)
*2 - EURUS_STAT 0x002AC320 (3.15)


<pre>
=== Methods  ===
struct ss_sc_mgr_get_sc_status
{
    u32 version;
    u8 res1[4];
    u32 mode;
    u8 res2[4];
};
</pre>


=== 0x9011 - SC Binary Patch  ===
net_control_cmd_GELIC_LV1_POST_WLAN_CMD - 0x0024A55C (3.15)


*This service is used by Update Manager to send a new SC firmware version to SYSCON.
net_control_wlan_cmd_GELIC_EURUS_CMD_ASSOC - 0x00246C78 (3.15)


==== SC Isolation DMA Buffer Header  ====
net_control_wlan_cmd_GELIC_EURUS_CMD_START_SCAN - 0x00248A14 (3.15)
<pre>struct sc_iso_header
{
    u32 seqno;
    u32 mbmsg;
    u32 cmd;
    u32 cmd_size;
    u8 cmd_data[0];
};
</pre>


== 0x11000 - SPM (Security Policy Manager) ==
net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WEP_CFG - 0x00249F24 (3.15)  


*Packet ID is mapped to '''SS id'''
net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WPA_CFG - 0x002497B8 (3.15)
*SS id value range is 0x0 - 0x84


{| class="wikitable FCK__ShowTableBorders"
= Event Notification  =
|-
! Packet ID
! Description
|-
| 0x11001
| Request
|-
| 0x11002
| Load Additional Policy
|}


== 0x14000 - SLL (Secure LPAR Loader)  ==
*Event Notfication is used e.g. to notify a LPAR about some event, e.g. device interrupt or notify a LPAR about destruction of another LPAR.
*For example Process 9 is notified through Event Notification when LPAR 2 is destructed.
*During LPAR construction, Process 9 creates an Outlet object with '''syscall 0x1001A''' and then passes the outlet ID to the '''syscall 0x10009''' that constructs the LINUX LPAR. In this way Process 9 is notified when LINUX LPAR is destructed.


*SLL opens '''lv2_kernel.self''', parses ELF header and determines the size of initial memory region for GameOS LPAR
== Outlet class  ==
*SLL creates a memory region for GameOS LPAR by using '''syscall 0x10000'''.
*SLL opens '''/proc/partitions/&lt;LPAR id&gt;/mem''' file and maps it with mmap syscall into it's address space.
*Then it authenticates, decrypts and copies the SELF file of GameOS to LPAR's memory region by using '''SPE syscalls 0x10040 and 0x10042'''.
*Linux is not loaded by SLL, it's loaded in Process 9 by Linux System Manager
*GameOS file image '''lv2_kernel.self''' is stored on '''/dev/rflash1'''


{| class="wikitable FCK__ShowTableBorders"
This is the base Outlet class. There are different types of Outlet and they derive from this base class.
|-
! Packet ID
! Description
|-
| 0x14004
| Load GOS
|-
| 0x14005
| Unload GOS
|}


== 0x15000 - SPL (Secure Profile Loader) ==
=== vtable ===


*DEFAULT.SPP file is stored on '''/dev/rflash1'''
0x00357DC0 (3.15)


{| class="wikitable FCK__ShowTableBorders"
=== Member variables  ===
|-
! Packet ID
! Description
|-
| 0x15001
| Get LPAR Parameter Size/Get LPAR Parameter
|-
| 0x15003
| Get Contents Size/Get Contents
|-
| 0x15009
| Get Component
|}


=== SPP File  ===
offset 0x30 - type (8 bytes)


*The file is encrypted but can be read by using 0x15003 service of SPL
offset 0x38 - pointer to LPAR that owns this Outlet object
*SPL reads SPP file, parses SPP header and checks some fields
*SPP file is verified and decrypted by SPU module '''spp_verifier.self''' that cab be executed with HV SPE calls
*Even old default.spp from PS3 Firmware 1.10 can be decrypted with spp_verifier.self from PS3 Firmware 3.41
*Header format version should be '''5''' or else the header check fails
*If (SPP header size&nbsp;% 256&nbsp;!= 0) then header check fails
*'''Finally i was able to decrypt profile file from 3.41 but by using SPE HV calls only&nbsp;!!! And Linux Manager is still there&nbsp;!!!'''
*The decrypted file is a binary file


Here are the contents of [[Default.spp#3.56_RETAIL.2FCEX]] from 3.55. <br />
offset 0x48 - outlet id (8 bytes)
Here are the contents of [[Default.spp#3.55_RETAIL.2FCEX]] from 3.55. <br />
Here are the contents of [[Default.spp#3.41_RETAIL.2FCEX]] from 3.41. <br />
Here are the contents of [[Default.spp#3.15_RETAIL.2FCEX]] from 3.15. <br />
Here are the contents of [[Default.spp#1.00_DEBUG.2FDEX]] from 1.00 Debug Firmware. <br />


==== SPP Header  ====
offset 0x90 - VIRQ assigned to this Outlet object (4 bytes)


offset 0x2 - header format version (2 bytes)
== Event Receive Port class  ==


offset 0x4 - header size (4 bytes)
*This type of Outlet is created e.g. in '''lv1_construct_event_receive_port''' and in '''syscall 0x1001A'''.
*HV calls '''lv1_connect_irq_plug''' and '''lv1_connect_irq_plug_ext''' assigns a VIRQ to Event Receive Port object.


offset 0x18 - number of segments (4 bytes)
=== vtable  ===


==== Segments  ====
0x00357E88


*Segments follow after the header
== VUART Outlet  ==
*SPP file contains several segments.


Here is the list of profile segments from 3.41:
*HV supports only one VUART Outlet per LPAR
*'''lv1_configure_virtual_uart_irq''' constructs a VUART Outlet object and passes the address of LPAR's VUART IRQ Bitmap to HV


{| class="wikitable FCK__ShowTableBorders"
=== vtable  ===
|-
! Name
! auth id/authority id
|-
|*SCE_CELLOS_PME               
|0x1070000001000001
|-
|*PS3_LPAR                     
|0x1070000002000001
|-
|*PS2_LPAR                     
|0x1020000003000001
|-
|*PS2_GX_LPAR                   
|0x1020000003000001
|-
|*PS2_SW_LPAR                   
|0x1020000003000001
|-
|*LINUX_LPAR                   
|0x1080000004000001
|-
|*SCE_CELLOS_SYSTEM_MGR         
|0x107000001D000001
|-
|*SCE_CELLOS_SYSTEM_MGR_LINUX   
|0x107000001D000001
|-
|*SCE_CELLOS_SYSTEM_MGR_PS2     
|0x107000001D000001
|-
|*SCE_CELLOS_SYSTEM_MGR_PS2_SW 
|0x107000001D000001
|-
|*SCE_CELLOS_SYSTEM_MGR_PS2_GX 
|0x107000001D000001
|-
|*SCE_CELLOS_SS_SECURE_RTC     
|0x1070000033000001
|-
|*SCE_CELLOS_SS_INDI_INFO_EID
|
|-
|*SCE_CELLOS_SS_INIT_LV1_ACL   
|0x1070000017000001
|}


== 0x15003 - Get Contents Size/Get Contents  ==
0x00357DC0


*This service provides the contents of a segment specified by a service requester
=== VUART IRQ Bitmap  ===
*I have got access to this service through DM but couldn't get through access policy yet, the service returns error code 0x00000005 that means '''Access Violation'''
*But i still could test with this service which segment names are valid
*I need valid '''laid''' and '''paid''' to get through it


== 0x17000 - Indi Info Manager  ==
*At address 0x38(LPAR ptr) + 0x158 is the VUART IRQ Bitmap owned by HV for LPAR (4 * 8 bytes = 256 bits)
*At address 0x38(LPAR ptr) + 0x150 is stored the physical address of LPAR's VUART IRQ Bitmap that was passed to '''lv1_configure_virtual_uart_irq'''
*When a VUART interrupt is generated by HV then first the VUART IRQ Bitmap owned by HV is updated and then this bitmap is copied to LPAR's VUART IRQ Bitmap, so VUART IRQ Bitmap is stored twice, once in HV and once in LPAR, just like IRQ State Bitmap.
*VUART IRQ Bitmap is not allowed to cross page boundary of LPAR memory region where it is stored. HV checks it and makes sure that it doesn't happen.
*'''GameOS 3.41''' VUART IRQ bitmap is at address '''0x80000000003556E8''' and of size '''32 bytes (256 bits, each bit corresponds to a VUART port)'''.
*'''GameOS 3.15''' VUART IRQ bitmap is at address '''0x8000000000354768'''.


{| class="wikitable FCK__ShowTableBorders"
= Logical PPE  =
|-
! Packet ID
! Description
|-
| 0x17001
| Read EID Data Size By Index/Read metldr Size
|-
| 0x17002
| Read EID Data By Index/Read metldr
|-
| 0x17003
| Read ID Data
|-
| 0x17004
| Read System Data
|-
| 0x17005
| Write System Data?
|-
| 0x17006
| Write smth?
|-
| 0x17007
| Read System Data From EEPROM
|-
| 0x17009
| unknown
|-
| 0x17010
| unknown
|-
| 0x17011
| unknown
|-
| 0x17012
| unknown
|-
| 0x17013
| Read eEID Size
|-
| 0x17014
| Write eEID/Write metldr
|-
| 0x17015
| Read cISD Size
|-
| 0x17016
| Read cISD
|-
| 0x17017
| Write cISD
|}


*Indi Info Manager is accessed e.g. in '''syscall 868''' on GameOS
*Logical PPE is used for interrupt management of LPAR.
*A Logical PPE object is created in '''syscall 0x10005'''. It' used e.g. in Process 9 during LPAR construction.
*'''syscall 0x10007''' activates a Logical PPE object
*0x67F0(HSPRG0) - pointer to currently active Logical PPE object (in HV dump it points to Linux PPE object naturally because the dump was made on Linux, so Linux LPAR was active at that time)
*E.g. '''lv1_get_logical_ppe_id''', '''lv1_start_ppe_periodic_tracer''' and '''lv1_set_ppe_periodic_tracer_frequency''' grab the currently active Logical PPE object


=== 0x17001 - Read EID Data Size By Index ===
== vtable ==
 
0x00357DF0 (3.15)


*I have got access to this service through DM and tested it
== Member variables  ==
*This service is used e.g. by Update Manager, User Token Manager or Storage Manager
 
*The service expects 2 additional parameters, each parameter is 8 bytes
offset 0x90 - pointer to an object that contains VIRQ-Outlet mapping table for thread 0
*I tested it with values: 0x0, 0x4 and 0x1000 for the 1st parameter. I extracted this values from HV Processes which use this service
 
*The 2nd parameter is not used in a request but in a response. It contains EID size.
offset 0x98 - pointer to an object that contains VIRQ-Outlet mapping table for thread 1
 
== Objects  ==
 
Here is the list of Logical PPE objects i found in HV 3.15.  


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Index
! Address in HV dump
! Size Of Data
! LPAR id
! Description
! PPE id
|-
| 0
| 0x860
| EID0
|-
|-
| 0x0069C7F0
| 1  
| 1  
| 0x2A0
| 1
| EID1
|-
|-
| 0x007A8900
| 2  
| 2  
| 0x730
| 1
| EID2
|-
| 3
| 0x100
| EID3
|-
| 4
| 0x030
| EID4
|-
| 5
| 0xA00
| EID5
|-
| 6
| 0x020
| cISD0
|-
| 7
| 0x200
| cISD1
|-
| 8
| 0x010
| cISD2
|-
| 9
| 0x030
| cCSD0
|-
| 0x1000
| 0xe960
| metldr - size is version dependand
|}
|}


=== 0x17002 - Read EID Data By Index ===
== Virtual IRQ - Outlet Mapping ==


*I have got access to this service through DM and tested it
*HV maintains 2 tables per PPE that map a VIRQ to an Outlet object.  
*This service is used e.g. by Update Manager, User Token Manager or Storage Manager
*The table has 256 entries and is indexed by VIRQ.  
*The service expects 2 additional parameters, each parameter is 8 bytes
*Each entry is a pointer to Outlet object.  
*The 1st parameter is same as the 1st parameter of service '''Read EID Data Size By Index'''
*Each Logical PPE object has 2 tables, one for each thread of Cell CPU.
*The 2nd parameter is '''EID Data Size''' that is returned by the service '''Read EID Data Size By Index'''
*The returned data is some binary data.  
*The data returned by the service with 1st parameter set to 0x0 or 0x4 is from file '''eEID''' stored on FLASH storage device region 0.
*The data returned by the service with 1st parameter set to 0x1000 contains string '''metldr'''.  
*E.g. EID0 data is passed by Update Manager to SPU module '''spu_token_processor.self''' when Update Manager loads and executes it with syscall '''0x10043'''.  
*E.g. EID4 data is passed by Storage Manager to SPU module '''sb_iso_spu_module.self'''.


=== 0x17004 - Read System Data ===
=== LPAR 1 PPE 1 Thread 0 ===


*Reads data from '''cISD''' or '''cCSD''' files stored on '''/dev/rflash1'''.  
0x0069C990 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 0''' (not empty)
*E.g. Gelic MAC address is stored in file '''cISD'''.
 
=== 0x17007 - Read System Data From EEPROM  ===
 
*Reads data from SC EEPROM
*An index is passed to the service. The index is mapped to a specific SC EEPROM offset.
 
Here is the list of possible EEPROM offsets from HV 3.15:


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Index
! VIRQ
! SC EEPROM Offset
! Address of Outlet object in HV dump
! Size Of Data
! Description
|-
|-
| 0
| 58
| 0x48D20
| 0x00090D10
| 6
| -
|-
|-
| 1
| 59
| 0x48D28
| 0x006BAC50
| 6
| -
|-
|-
| 2
| 60
| 0x48D30
| 0x006B3ED0
| 6
| FLASH storage device / Storage device notification for LPAR 1
|-
|-
| 3
| 61
| 0x48D38
| 0x00697E70
| 6
| VUART interrupts
|-
|-
| 4
| 62
| 0x48D00
| 0x001C8F20
| 4
| -
|-
| 5
| 0x48D04
| 4
|-
| 6
| 0x48D08
| 4
|}
|}


=== 0x17014 - Write eEID/Write metldr ===
=== LPAR 1 PPE 1 Thread 1 ===


*'''Holy crap, it writes passed data to the region of FLASH memory where eEID or metldr data is stored&nbsp;!!!'''
0x0069D9B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 1''' (empty)
*'''And GameOS is allowed to use this service&nbsp;!!!'''
*'''Do not experiment with this service if you don't know what it does or else your PS3 will not work anymore&nbsp;!!!'''


=== 0x17015 - Read cISD Size ===
=== LPAR 2 PPE 1 Thread 0 ===


*Returns size of data '''cISD''' that is stored on '''FLASH storage device region 0'''
0x000A06B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 0''' (not empty)  
 
=== 0x17016 - Read cISD  ===
 
*Returns data '''cISD''' that is stored on '''FLASH storage device region 0'''
 
=== 0x17017 - Write cISD  ===
 
*'''Writes passed data to the region of FLASH memory where cISD data is stored&nbsp;!!!'''
 
== 0x18000 - DM (Dispatcher Manager)  ==
 
*Dispatcher Manager runs in Process 3.  
*When SLL (Secure LPAR Loader) creates GamesOS LPAR and loads it, it also creates a VUART with port number '''10''' owned by GameOS using a service provided by Dispatcher Manager (0x18001 - Construct Service Port).
*Dispatcher Manager communicates with GameOS through this VUART. It opens the file '''/proc/partitions/&lt;LPAR id&gt;/vuart/10'''. When the file '''/proc/partitions/&lt;LPAR id&gt;/vuart/10''' is opened by Dispatcher Manager, the Hypervisor creates a peer VUART which is connected to the GameOS's VUART 10.
*After that Dispatcher Manager reads requests from this VUART sent by GameOS and dispatches these requests to services (functions) provided by Hypervisor Processes through sockets. '''Through VUART and Dispatcher Manager, the GameOS LPAR has access to all services provided by Hypervisor Processes.'''
*However, the services provided by Hypervisor Processes are protected by Security Policy Manager (SPM). Before Dispatcher Manager routes the requests from GameOS to these services, it consults SPM (by using 0x11001 service of SPM) and checks if the GameOS has access rights to the requested service. If not then the request is not routed.
*DM overwrites the LAID sent in SS packet header with the LAID of the LPAR that sent the request. So, no matter what LAID you send in SS packet header, it will be always overwritten with the correct one by DM. That is the reason why e.g. USB Dongle Master Key cannot be decrypted by GameOS without patching DM. But with HV access rights, DM can be easily patched and access to SYSCON can be gained.
*Linux LPAR doesn't have a VUART communication link to Dispatcher Manager.
*I tested VUART 10 on GameOS with PSGroove and it's there.
*On GamesOS, '''_ss_multiplexer''' accesses DM (VUART 10)


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Packet ID
! VIRQ
! Address of Outlet object in HV dump
! Description
! Description
|-
|-
| 0x18001
| 20
| Construct Service Port
| 0x003AA210
| -
|-
|-
| 0x18002
| 21
| Destruct Service Port
| 0x003AFEC0
|}
| -
 
=== Dispatcher Manager Messages  ===
 
==== Dispatcher Manager Header  ====
 
*Payload follows after header
*Payload is a SS packet
<pre>struct dispmgr_header
{
    uint32_t request_id;
    uint32_t function_id;
    uint32_t request_size;        /* payload size of request */
    uint32_t response_size;        /* payload size of response */
}
</pre>
=== Packet ID - SS ID Mapping  ===
 
*Before DM routes a received request to a service provider (HV Process) it consults SPM
*DM sends a request to SPM
*Request contains SS ID and Subject ID (laid and paid)
*DM obtains SS ID by mapping Packet ID
 
Here is the mapping table i extracted from HV Process 3 where SPM and DM run:
 
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Packet ID
| 22
! SS ID
| 0x001FC010
| -
|-
|-
| 0x2001
| 23
| 0x34
| 0x003A8E50
| -
|-
|-
| 0x2002
| 24
| 0x35
| 0x001FFED0
| SPE 0 Class 0 Interrupt
|-
|-
| 0x2003
| 25
| 0x36
| 0x003AE160
| SPE 0 Class 1 Interrupt
|-
|-
| 0x2004
| 26
| 0x37
| 0x003AE350
|-
| SPE 0 Class 2 Interrupt
| 0x2005
| 0x38
|-
|-
| 0x2006
| 27
| 0x39
| 0x003AB100
| SPE 1 Class 0 Interrupt
|-
|-
| 0x200A
| 28
| 0x3D
| 0x003AB2F0
| SPE 1 Class 1 Interrupt
|-
|-
| 0x200B
| 29
| 0x3E
| 0x003AB4E0
| SPE 1 Class 2 Interrupt
|-
|-
| 0x200C
| 30
| 0x3F
| 0x003AA6A0
| SPE 2 Class 0 Interrupt
|-
|-
| 0x200D
| 31
| 0x40
| 0x003AA890
| SPE 2 Class 1 Interrupt
|-
|-
| 0x200E
| 32
| 0x41
| 0x003AAA80
| SPE 2 Class 2 Interrupt
|-
|-
| 0x2012
| 33
| 0x7B
| 0x003B44A0
| SPE 3 Class 0 Interrupt
|-
|-
| 0x2013
| 34
| 0x7C
| 0x003B4690
| SPE 3 Class 1 Interrupt
|-
|-
| 0x2014
| 35
| 0x7E
| 0x003B4AD0
| SPE 3 Class 2 Interrupt
|-
|-
| 0x2015
| 36
| 0x7F
| 0x003B5300
| SPE 4 Class 0 Interrupt
|-
|-
| 0x2016
| 37
| 0x7D
| 0x003B54F0
| SPE 4 Class 1 Interrupt
|-
|-
| 0x2017
| 38
| 0x80
| 0x003B56E0
|}
| SPE 4 Class 2 Interrupt
 
== 0x19000 - AIM  ==
 
*Executes isolated SPU module '''aim_spu_module.self'''
*'''EID0 data''' is passed to '''aim_spu_module.self'''
 
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Packet ID
| 39
! Description
| 0x003AE7C0
| SPE 5 Class 0 Interrupt
|-
|-
| 0x19002
| 40
| Get Device Type
| 0x003AE9B0
| SPE 5 Class 1 Interrupt
|-
|-
| 0x19003
| 41
| Get Device ID
| 0x003AEBA0
| SPE 5 Class 2 Interrupt
|-
|-
| 0x19004
| 42
| Get PS Code
| 0x003B2040
| Storage device notification for LPAR 2
|-
|-
| 0x19005
| 43
| Get Open PS ID
| 0x003AEE30
| VUART interrupts
|-
|-
| 0x19006
| 44
| Unknown
| 0x001FEAA0
|}
| -
 
|-
=== 0x19002 - Get Device Type ===
| 45
 
| 0x001FEED0
On my fat PS3 with HV 3.41 it returns:
| HDD storage device
<pre>
|-
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x85
| 46
</pre>
| 0x003B5E20
 
| -
<pre>
|-
struct ss_aim_get_device_type
| 47
{
| 0x003B7040
    u8 field0[16];
| -
};
|-
</pre>
| 48
 
| 0x003B9B40
=== 0x19003 - Get Device ID ===
| -
 
|-
<pre>
| 49
struct ss_aim_get_device_id
| 0x003B3A40
{
| -
    u8 field0[16];
};
</pre>
 
=== 0x19004 - Get PS Code ===
 
<pre>
struct ss_aim_get_ps_code
{
    u8 field0[8];
};
</pre>
 
=== 0x19005 - Get Open PS ID ===
 
<pre>
struct ss_aim_get_open_ps_id
{
    u8 field0[16];
};
</pre>
 
=== 0x19006 - ===
 
* usage found in bdp_BDVD for example...with 1 param (=0), dunno yet what it returns.
 
== 0x24000 - USB Dongle Authenticator  ==
 
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Packet ID
| 50
! Description
| 0x003BACA0
| Gelic device
|-
|-
| 0x24001
| 51
| Generate Challenge
| 0x003BAE10
| UNKNOWN storage device
|-
|-
| 0x24002
| 52
| Verify Response
| 0x003B8350
| -
|}
|}


=== 0x24001 - Generate Challenge ===
=== LPAR 2 PPE 1 Thread 1 ===


*I have got access to this service through DM and tested it
0x007A89E0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 1''' (not empty)
*The service expects no input parameters except those in SS packet header
*It uses 0x5003 service (Generate Random Number) to generate random numbers that are used in challenge body
*The length of a challnge body is always 23 bytes, first 3 bytes are always the same: '''0x2E 0x02 0x01'''


Here are hexdumps of some challenge bodies i let 0x24001 service generate:
{| class="wikitable FCK__ShowTableBorders"
<pre>2E 02 01 72 3A 0A 76 BB 81 CB 29 BC E7 B5 D6 62 7C 0E EE 23 18 A9 1D
</pre><pre>2E 02 01 F0 DA 78 D4 1D CB D7 C9 C7 F0 32 F4 2E 92 39 BD 3F 32 93 AA
</pre><pre>2E 02 01 3B B2 9D FD A8 83 AF 9A C0 E9 13 BB AE D5 6C 8C 45 2E DE 13
</pre>
=== 0x24002 - Verify Response  ===
 
*I have got access to this service and tested it with PSGroove
*The response body is 25 bytes large
*The first 3 bytes have to be '''0x2E 0x02 0x02''' or else the check fails
*The 16 bit at offset 3 is a dongle ID
*The dongle ID is checked if it's revoked or not
*When the verification succeedes then '''product mode''' is set to '''1'''
*The service calculates '''USB Dongle Key''' from '''USB Dongle ID''' and '''USB Dongle Master Key''' by using '''HMAC SHA-1'''
*The service uses '''HMAC SHA-1''' to calculate the correct response body from the challenge body and '''USB Dongle Key'''
*After that the service compares the calculated response body with the given one that was sent to the service
*It seems that '''laid''' and '''paid''' from SS packet header are used in decryption process
 
==== USB Dongle Master Key  ====
 
*USB Dongle Master Key is stored encrypted in Process 6
*The encrypted key is 64 bytes large
*The decrypted key is 20 bytes large
*The USB Dongle Master Key is decrypted first time the service 0x24002 is used
*The USB Dongle Master Key is decrypted by using the service '''0x200E (Decrypt Master)''' of '''Vitual TRM Manager'''
*The decrypted USB Dongle Master Key is stored in Process 6 in clear text (after first usage of this service)
*When decryption of USB Dongle Master Key fails then a dummy key is used
*Unfortunately, in the HV dump 3.15 the USB Dongle Master Key was not decrypted at the moment of dumping
*The first 12 bytes of decrypted USB Dongle Master Key is a magic value: '''_USB_DONGLE_'''. After these 12 bytes follows the real USB Dongle Master Key of size 20 bytes. So, if after decryption of USB Dongle Master Key, you see this magic value then the decryption was successfull.
 
Here is the encrypted USB Dongle Master Key from HV 3.15:
<pre>0x22 0xD5 0xD1 0x8C 0xFF 0xE2 0x4F 0xAC 0xEC 0x72 0xA2 0x42 0xA7 0x18 0x98 0x10
0x25 0x33 0xE0 0x96 0xF2 0xC1 0x91 0x0D 0x15 0x23 0xD3 0x07 0x74 0xE7 0x2B 0x72
0xDF 0xA6 0xDD 0xE9 0x68 0x8B 0x76 0x2A 0x6A 0x87 0x51 0x7F 0x85 0x39 0x0B 0xD4
0x20 0x3F 0x46 0x89 0x04 0x82 0xB7 0x30 0x84 0x89 0x4B 0xCC 0x9D 0xB1 0x24 0x7C
</pre>
This is the '''decrypted''' dongle master key:
<pre>
0x46 0xDC 0xEA 0xD3 0x17 0xFE 0x45 0xD8 0x09 0x23
0xEB 0x97 0xE4 0x95 0x64 0x10 0xD4 0xCD 0xB2 0xC2
</pre>
This is the '''decrypted''' dongle key for dongle ID&nbsp;0xAAAA&nbsp;which works up to 3.55:
<pre>
0x04 0x4E 0x61 0x1B 0xA6 0xA6 0xE3 0x9A 0x98 0xCF
0x35 0x81 0x2C 0x80 0x68 0xC7 0xFC 0x5F 0x7A 0xE8
</pre>
Here is the USB Dongle Master Dummy Key from HV 3.15:
<pre>0xD1 0xFC 0x57 0x55 0xBF 0x20 0xFA 0xB2 0xD4 0xA5 0x4A 0x0A 0x0C 0x5D 0x52 0x8E
0xDF 0x66 0xCD 0x74
</pre>
 
==== USB Dongle ID Revoke List  ====
 
*Process 6 contains a revoke list for USB Dongle IDs
*The revoke list is 0x2000 bytes large. It's a bitmap.
*Each bit represents a USB Dongle ID. If bit is 0 then USB Dongle ID is revoked.
 
The following USB Dongle IDs are revoked in HV 3.15:
<pre>0, 2, 13, 32, 34, 176, 241
</pre>
== 0x25000 - User Token Manager  ==
 
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Packet ID
! VIRQ
! Address of Outlet object in HV dump
! Description
! Description
|-
|-
| 0x25001
| 16
| Encrypt User Token
| 0x003B2480
| -
|-
| 17
| 0x003B2590
| -
|-
| 18
| 0x003B26A0
| -
|-
|-
| 0x25002
| 19
| Decrypt User Token
| 0x003B27B0
| -
|}
|}


=== User Token ===
== IRQ State Bitmap ==


*Before User Token Manager encrypts a received user token it checks it's format.
*There is one IRQ State Bitmap (256 bits = 32 bytes) per thread of Logical PPE
*User Tokens are processed by '''spu_utoken_processor.self'''  
*'''HSPRG0 value is per thread''', so there are 2 HSPRG0 values in HV dump&nbsp;!!!
*Before User Token is processed, User Token Manager reads IDPS by sending SS requests to Indi Info Manager (packet ids 0x17001 and 0x17002). Indi Info Manager runs in HV Process 5.
*The IRQ State Bitmap of a thread is stored at -0x68E0(HSPRG0)
*When an Event or Interrupt happens then the bitmap at 0x68E0(HSPRG0) is updated
*The physical address of '''LPAR's IRQ State Bitmap''' of thread is stored at offset -0x68C0(HSPRG0)
*The address of LPAR's IRQ State Bitmap is passed to Hypervisor through HV call '''lv1_configure_irq_state_bitmap'''
*'''lv1_detect_pending_interrupts''' returns value of current IRQ State Bitmap.
*The IRQ State Bitmap is updated if an Outlet object is assigned to VIRQ and when Outlet generates an event
*After IRQ State Bitmap update, it's copied to LPAR's IRQ State Bitmap and a hardware interrupt is generated so that LPAR can read it's IRQ State Bitmap and handle interrupts.  
*So, IRQ State Bitmap is stored twice, once in HV and once in LPAR, just like VUART IRQ Bitmap.
*'''GameOS''' IRQ state bitmap is stored at address '''SPRG0 + 0x1C0 and of size 64 bytes (256 bits state + 256 bits mask) per thread of Cell CPU'''. So there are 2 IRQ state bitmaps.


==== User Token Format  ====
0x8941FC0 - physical address of LPAR's IRQ State Bitmap for Thread 0 of LINUX LPAR
<pre>stuct user_token_attr
{
    uint32_t type;                                /* 0x00000001, value&nbsp;!= 0x00000001 means attribute list ends here */
    uint32_t size;                                /* 8 + sizeof(data) */
    /* data follows here, size of data may be 0 */
}


struct user_token
0x8948FC0 - physical address of LPAR's IRQ State Bitmap for Thread 1 of LINUX LPAR  
{
    uint32_t magic;                                /* 0x73757400 = "sut\0" */
    uint32_t format_version;                      /* 0x00000001 */
    uint64_t size;
    uint8_t idps[16];
    uint64_t expire_date;
    uint64_t capability;
    union
    {
        stuct user_token_attr attrs[0];
        uint8_t dummy[3072];
    } attrs;
    /* 0xC30 */
    uint8_t digest[20];
}
</pre>
= LPAR Memory Management  =


== Memory Region class ==
= System Controller (SC or SYSCON) =


This class is the base class for different memory region types.  
*Data received from SC is sent to a VUART
*'''lv1_get_rtc''' and '''syscall 0x10036''' communicate with '''SC VUART 4'''.


=== vtable ===
=== VUART Table ===


0x003578B0 (3.15)  
*Address of SC VUART Table - 0x00610410 (3.15).
*There are 5 VUARTs for SC in HV 3.15


=== Member variables  ===
Here is the SC VUART table from HV 3.15:


offset 0x40 - pointer to LPAR object that owns this memory region
{| class="wikitable FCK__ShowTableBorders"
|-
! Index
! Address of VUART object in HV dump
! Description
|-
| 0
| 0x0060FD20
| This VUART is connected with the '''VUART 0 (/dev/sc0)''' of LPAR 1
|-
| 1
| 0x0060FE20
| This VUART is connected with the '''VUART 1 (/dev/sc1)''' of LPAR 1
|-
| 2
| 0x0060FF20
| This VUART is not connected to some peer VUART but i guess that it should be connected to '''VUART 2 (/dev/sc2)''' of LPAR1
|-
| 3
| 0x006124E0
| This VUART is connected with the '''VUART 3 (/dev/sc3)''' of LPAR 1
|-
| 4
| 0x00612DF0
| '''lv1_get_rtc''' and '''syscall 0x10036''' communicate with this VUART.
|}


offset 0x48 - type of memory region (8 bytes)
== Interrupt Handling  ==


offset 0x50 - LPAR start address of memory region
spider_sc_interrupt_handler - 0x0020A68C (3.15)


offset 0x58 - size of memory region (8 bytes)
== Methods  ==


offset 0x60 - flags (8 bytes)  
sc_vuart_4_get_peer_vuart - 0x002ED384 (3.15)  


offset 0xA0 - log2 of page size
sc_send - 0x0020A908 (3.15)


=== Generating New LPAR Memory Region Addresses ===
sc_receive - 0x0020A354 (3.15)


generate_new_lpar_mem_region_address(?, memory region size, log2(page size), ?, ?) - 002C82E8 (3.15)
sc_vuart_rx_trigger_callback - 0x002ED470 (3.15)


generate_new_lpar_mem_region_address - 002C6570 (3.41)
== lv1_get_rtc  ==


*The function returns a new LPAR memory region address.
*'''lv1_get_rtc''' communicates with SC VUART 4.
*This method is used e.g. in all HV calls which create any kind of memory regions, e.g. '''lv1_allocate_memory''', '''lv1_map_htab''', '''lv1_undocumented_function_114''', '''lv1_construct_logical_spe''', '''lv1_map_device_mmio_region''' or '''syscall 0x10040'''.
*20 bytes are written to the peer VUART of SC VUART 4.
*After a request is sent to SC VUART 4, '''lv1_get_rtc''' busy waits until SC VUART 4 receive data buffer is not empty.
*When SC VUART 4 receive data buffer is not empty, '''lv1_get_rtc''' reads 24 bytes from the VUART.


==== Encoding LPAR Memory Region Start Addresses and Sizes ====
== SYSCON Protocol ==


*Size of LPAR memory region is encoded in the LPAR memory region start address.
* I was able to enable SYSCON Manager debug messages in HV Process 5
*That is why e.g. the LPAR Memory Region Start Addresses of LPAR Memory Region of size 4096 byte begin with '''0x300000000000''', '''0x300000000000 >> 42 = 0xC = log2(4096)'''.
* Messages sent to SYSCON are at least '''0x10''' bytes of size. SC VUARTs check it before sending the messages to SYSCON.
*Each LPAR has a counter (8 bytes) which is incremented by 1 every time a new LPAR Memory Region is created.
* The header size of the SYSCON messages is '''0x10''' bytes.
*Before incrementing, the counter is shifted left by '''log2(LPAR Memory Region Size)''' and ored with '''log2(LPAR Memory Region Size) << 42'''.


LPAR Memory Region Start Address >> 42 = log2(LPAR Memory Region Size)
=== Packet Header ===


LPAR Memory Region Start Address = (log2(LPAR Memory Region Size) << 42) |
* Packet header is of size '''0x10''' bytes.
    (counter << log2(LPAR Memory Region Size))
* At offset '''0x6''' of SYSCON packet is the header checksum which is of size '''2''' bytes.
* '''The header checkum is just a sum of first 6 header bytes and 0x8000 constant'''
* The '''2nd byte''' in every SYSCON message has to be '''1''' or else the function '''sc_send''' fails.
* The '''word''' at offset '''0x8''' is the '''SC VUART index'''.
* The '''half-words''' at offset '''0xC''' and '''0xE''' have to be equal or the function '''sc_send''' fails.


===== LPAR Memory Region Address Counter =====
<pre>
struct sc_hdr
{
    uint8_t field0;
    uint8_t field1;          /* always 1 */
    uint8_t field2[4];
    uint16_t cksum;          /* header checksum */
    uint32_t index;          /* syscon index (0 - /dev/sc0, 1 - /dev/sc1, 2 - /dev/sc2, 3 - /dev/sc3) */
    uint16_t size1;          /* body size */
    uint16_t size2;          /* body size */
};
</pre>
 
==== Calculating Packet Header Checksum ====
 
<pre>
/* calculating SC packet header checksum */


*LPAR Memory Region Address Counter is stored at address: '''0x38(LPAR ptr) + 0x9E8'''
/*
*LPAR1's Memory Region Address Counter is at address '''0x00677A48''' in HV dump 3.15
* sc_hdr_cksum
*LPAR2's Memory Region Address Counter is at address '''0x007632D8''' in HV dump 3.15
*/
*LPAR1's Memory Region Address Counter is at address '''0x00677A48''' in HV dump 3.41
uint16_t sc_hdr_cksum(struct sc_hdr *sc_hdr)
*LPAR2's Memory Region Address Counter is at address '''0x00161E68''' in HV dump 3.41
{
    uint8_t *ptr;
    uint32_t sum;


== Physical Memory Region class  ==
    ptr = (uint8_t *) sc_hdr;
    sum = 0;


This type of memory region is created e.g. in '''lv1_allocate_memory''' HV call or in '''syscall 0x10000'''.
    for (i = 0; i < 6; i++)
        sum += *ptr++;


=== vtable  ===
    sum += 0x8000;


0x00357D08 (3.15)
    return sum & 0xffff;
}


=== Member variables  ===
struct sc_hdr sc_hdr;


offset 0xB0 - pointer to object that stores a list of addresses of physical pages owned by this memory region
memset(&sc_hdr, 0, sizeof(sc_hdr));


offset 0xB8 - pointer to LPAR object that owns this memory region
sc_hdr.cksum = sc_hdr_cksum(sc_hdr);


offset 0xC0 - reference counter (8 bytes)
/* fill sc header here */


=== Objects  ===
sc_hdr.cksum = sc_hdr_cksum(sc_hdr);
</pre>


Here is the list of physical memory region objects i found in HV 3.15.
=== Packet Body ===


{| class="wikitable FCK__ShowTableBorders"
* Packet body follows packet header
|-
* Packet body size is stored at offset '''0xC''' and '''0xE''' in packet header and is of size 2 bytes
! Address in HV dump
! LPAR id
! LPAR Start Address
! Size
! Flags
! log2(Page Size)
! Physical Page Addresses
|-
| 0x006B5510
| 1
| 0x300000001000
| 0x1000
| 0x0
| 0xC
| 0x672000
|-
| 0x006B5E50
| 1
| 0x440000040000
| 0x20000
| 0x0
| 0x11
| 0x6C0000
|-
| 0x006B6980
| 1
| 0x440000060000
| 0x20000
| 0x0
| 0x11
| 0x6E0000
|-
| 0x006B7F00
| 1
| 0x400000040000
| 0x10000
| 0x0
| 0x10
| 0x100000
|-
| 0x003A80F0
| 2
| 0x6C0058000000
| 0x7000000
| 0x4
| 0x18
| 0x1000000 - 0x7000000
|-
| 0x003BE800
| 2  
| 0x300000047000
| 0x1000
| 0x0
| 0xC
| 0x1FA000
|-
| 0x006BDAA0
| 2
| 0x0
| 0x8000000
| 0x8
| 0x1B (single huge page)
| 0x8000000
|}


So, Linux kernel should be located at physical address 0x8000000 and Linux syscall handler at 0x8000C00. Too bad that the HV dump is not large enough.
=== Reading SYSCON EPROM (NVS Service) ===


=== GameOS Physical Memory Regions  ===
Here is a command which is sent to SYSCON to read 1 byte of EPROM at offset 0x48C07 (Product Mode):
0x14 <span style="background:#00FF00">0x01</span> 0x00 0x00 0x00 0x00 <span style="background:#FF0000">0x80 0x15</span> <span style="background:#FFFF00">0x00 0x00 0x00 0x00</span> <span style="background:#00FFFF">0x00 0x04</span> <span style="background:#00FFFF">0x00 0x04</span> 0x20 0x02 0x07 0x01


*GameOS allocates nearly all physical memory of PS3 for itself&nbsp;!!! That is why new HV calls '''lv1_allocate_memory''' with large memory region sizes will fail.
And here is the response to the above request:
*So when someone wants a large piece of physical memory, he can borrow it from GameOS's LPAR memory region that starts at '''0x700020000000'''. It can be used for example to send update packages to Update Manager which are very large.
0x14 <span style="background:#00FF00">0x01</span> 0x00 0x00 0x00 0x00 <span style="background:#FF0000">0x80 0x15</span> <span style="background:#FFFF00">0x00 0x00 0x00 0x03</span> <span style="background:#00FFFF">0x00 0x05</span> <span style="background:#00FFFF">0x00 0x05</span> 0x00 0x02 0x07 0x01 0xff
 
=== PCI Bus Power ===


Here is the list of physical memory regions of GameOS i found in HV 3.41:
* '''Used by PS2EMU System Manager in HV process 9 when PS2 EMU is booted'''


{| class="wikitable FCK__ShowTableBorders"
==== PCI Bus Power On ====
|-
! Start Address
! Size
! Access Right
! Max Page Size
! Flags
! Real Addresses
|-
| 0x0
| 0x1000000
| 0x3
| 0x18
| 0x8
| 0x1000000 - 0x1FFF000
|-
| 0x500000300000
| 0xA0000
| 0x3
| 0x10
| 0x8
| 0x380000 - 0x38F000, 0x3B0000 - 0x3BF000, 0x1E0000 - 0x1FF000, 0x3C0000 - 0x3FF000, 0xFF00000 - 0xFF1F000
|-
| 0x700020000000
| 0xE900000 (huge memory region)
| 0x3
| 0x14
| 0x0
| 0x400000 - 0x5FF000, 0x800000 - 0xFFF000, 0x2000000 - 0xFEFF000
|}


== HTAB Memory Region class ==
'''Request to SC1:'''
  0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x01


This memory region is created when a HTAB is mapped into LPAR's address space. It's created in '''lv1_map_htab''' HV call.
==== PCI Bus Power Off ====


=== vtable ===
'''Request to SC1:'''
  0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x00


0x00357C98 (3.15)
=== Ring Buzzer ===


=== Member variables ===
'''Request:'''
  0x16 0x01 0x00 0x00 0x00 0x00 0x80 0x17 0x00 0x00 0x00 0x00 0x00 0x08 0x00 0x08 0x20 0x00 0x00 0x00 0x00 0x00 0x00 0x00


offset 0xB0 - pointer to VAS object that owns the HTAB
=SYSCON=
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#SYSCON gitbrew.org::SYSCON] <br />


=== Objects  ===
SYSCON MMIO registers can be accessed on Linux with a driver using lv1_undocumented_function_114, e.g. '''ps3sbmmio'''.
Use ps3sbmmio device driver carefully, an access at some addresses could shutdown your PS3.


Here is the list of HTAB memory region objects i found in HV 3.15.
==Packet Header==


{| class="wikitable FCK__ShowTableBorders"
* Size is '''0x10'''.
|-
! Address in HV dump
! LPAR id
! VAS id
! LPAR Start Address
! Size  
! Flags
! log2(Page Size)
|-
| 0x001FE0F0
| 2
| 3
| 0x500000C00000
| 0x100000
| 0xC000000000000000
| 0x14
|-
| 0x003BD850
| 2
| 3
| 0x500004300000
| 0x100000
| 0xC000000000000000
| 0x14
|-
| 0x003BDEA0
| 2
| 3
| 0x500004500000
| 0x100000
| 0xC000000000000000
| 0x14
|}


=== GameOS HTAB  ===
<pre>
struct sc_hdr {
    uint8_t service_id;
    uint8_t version;              /* must be 1 !!! */
    uint16_t transaction_id;      /* returned in response */
    uint8_t res[2];
    uint16_t cksum;              /* checksum of first 6 header bytes */
    uint32_t communication_tag;  /* SYSCON tag: 0-4 */
    uint16_t payload_size[2];    /* body size */
};
</pre>


*HTAB of GameOS is already mapped into address space of GameOS so that is why HV call '''lv1_map_htab''' will fail until you unmap it with '''lv1_unmap_htab'''
==Sending Packets==
*Effective address of GameOS HTAB is '''0x800000000F000000'''
*Virtual address of GameOS HTAB is '''0xF000000'''
*Size of GameOS HTAB is '''0x40000'''
*GameOS HTAB supports large pages of size '''64K''' and '''1M'''
*GameOS HTAB can be easily dumped by reading 0x40000 bytes at EA 0x800000000F000000


=== GameOS SLB  ===
* Before sending new packet to SYSCON, the Hypervisor checks 2 words at offsets 0x2400008DFF0 and 0x2400008CFF4.
* The Hypervisor busy waits until (value + 1) at offset 0x2400008CFF4 is NOT equal to value at offset 0x2400008DFF0.
* The packet is sent with 4 byte transfers.
* First, the Hypervisor sends the header of the packet, 4 word transfers.
* The header is written beginning at the address 0x2400008D000.
* After that the Hypervisor sends the body of the packet, with 4 byte transfers too.
* The body is written beginning at the address 0x2400008D010.
* If the packet size is NOT divisible by 4 then the Hypervisor sends the remaining bytes (at most 3) as a word padded with 0s.
* After the packet body was written, the Hypervisor calculates checksum of the whole packet and writes it at the address where the last word of packet body was written + 4.
<pre>
uint32_t cksum = 0;


Here is the dump of SLB entries from GameOS 3.41:
for (i = 0; i < packet_size; i++)
<pre>0x8000000008000000  0x0000000000000500
    cksum -= packet[i];
0x8000000208000000  0x0000000000020500
 
0x8000000300000000  0x0000000000030510
cksum = cksum & 0xffff;
0x0000000000000000  0x0000000000000000
0x0000000080000000  0x0000000000038C00
0x00000000A0000000  0x000000000003AC00
0x00000000C0000000  0x000000000003CC00
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x8000000010057960  0x8000000000313E78
0x8000000010057940  0x0000000000000000
0x800000000001B698  0x0000000000000000
0x8000000010057930  0x8000000000490708
0x80000000002B6C68  0x80000000003DE928
0x8000000010057EC0  0x80000000003DE920
0x0000000000000000  0x8000000000309810
0x80000000004B3000  0x0000000000000000
0x8000000010057CC0  0x0000000000000000
0x80000000004AF000  0x80000000004E1F00
0x80000000100579C8  0x80000000100579C0
0x80000000100579E0  0x2400002200000000
0x80000000004CF5B0  0x8000000200012000
0x80000000100579F8  0x80000000100579F0
0x8000000010057A10  0x80000000004A3A00
0x80000000004CF5B0  0x80000000004C8D00
0x800000000001BF6C  0x80000000004CD400
0x800000000001B698  0x80000000004C8100
0x80000000100579D0  0x80000000004B48C0
0x0000000000001C08  0x0000000000000000
0x8000000010057A78  0x8000000010057A70
0x8000000010057A90  0x0000000000000000
0x80000000004CF90C  0x0000000000000000
0x0000000000000000  0x8000000010057A80
0x8000000010057A90  0x8000000000309810
0x80000000004CF62C  0x0000000000000000
0x8000000010057CC0  0x0000000000000000
0x80000000004AF000  0x80000000004B48C0
0x00004000001C0000  0x0000000000000001
0x00000000D0000000  0x0000A8E3EE7D10DA
0x0000000000000000  0x0000000000000000
0x80000000004D8088  0x80000000004D9000
</pre>
</pre>
== SPE MMIO Memory Region class ==
* After the packet checksum was written, the Hypervisor reads the value at offset 0x2400008DFF0, modifies it and stores back:
<pre>
value = value + 1;
value &= 0xffff;
value = (value << 16) | value;
</pre>
* To notify the SYSCON about the new packet, the Hypervisor writes 0x1 to address 0x2400008E100.
 
==Receiving Packets==
 
* The Hypervisor installs an interrupt handler for the SYSCON.
* First, the Hypervisor reads a word from address 0x2400008E000, ors it with 0xFFFFFFFD and writes the value back.
* Then, the Hypervisor reads a word from address 0x2400008E004 and tests if bit 0x2 is set or not. The bit 0x2 should be not 0 or else the Hypervisor panics.
* After that, the Hypervisor reads a word at address 0x2400008CFF0 and 0x2400008DFF4. If there is a new packet pending from SYSCON, then the (value + 1) at 0x2400008CFF0 should be equal the value at 0x2400008DFF4.
* The Hypervisor reads the header of the packet beginning at the address 0x2400008C000.
* The header is read with 4 word transfers by the Hypervisor.
* The byte at offset 1 in the packet header must be 1 or else the Hypervisor discards the packet as invalid.
* The Hypervisor calculates the checksum of the packet header and checks it with the checksum stored in the header. If they don't match then the Hypervisor discards the packet.
* The Hypervisor reads the body of the packet beginning at the address 0x2400008C010.
* The header and the body of the received packet can be read as many times as you want !!! They remain until next SYSCON packet is received
which gives us the possibility to communicate with SYSCON on Linux easily :)
 
==Test==
 
'''1. Before sending SYSCON packet''':
<pre>
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C
 
00000000 01 18 01 18                                      |....|
00000004
 
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C


This type of memory region represents MMIO memory region of a SPE. It's created e.g. in '''lv1_construct_logical_spe''' or in '''syscall 0x10040'''.  
00000000  01 18 01 18                                      |....|
00000004


=== vtable  ===
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C


0x003583F8 (3.15)
00000000  01 24 01 24                                      |.$.$|
00000004


=== Member variables  ===
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C


=== Objects ===
00000000 01 24 01 24                                      |.$.$|
00000004
</pre>


Here is the list of SPE memory region objects i found in HV 3.15.  
'''2. SYSCON packet was sent by using ps3dm_scm read_eprom.'''


{| class="wikitable FCK__ShowTableBorders"
'''3. After sending SYSCON packet''':
|-
<pre>
! Address in HV dump
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C
! LPAR id
! SPE
! LPAR Start Address
! Size
! Physical Address
! Flags
! log2(Page Size)
|-
| 0x003ABC20
| 2
| 1  
| 0x4C0000880000
| 0x80000
| 0x20000080000
| 0xA000000000000000
| 0xC
|-
| 0x003AAD70
| 2
| 2
| 0x4C0000980000
| 0x80000
| 0x20000100000
| 0xA000000000000000
| 0xC
|-
| 0x003A8880
| 2
| 3
| 0x4C0000780000
| 0x80000
| 0x20000180000
| 0xA000000000000000
| 0xC
|-
| 0x003B4F70
| 2
| 4  
| 0x4C0000A80000
| 0x80000
| 0x20000200000
| 0xA000000000000000
| 0xC
|-
| 0x003AB700
| 2
| 5
| 0x4C0000680000
| 0x80000
| 0x20000280000
| 0xA000000000000000
| 0xC
|-
| 0x003B5BE0
| 2
| 6
| 0x4C0000B80000
| 0x80000
| 0x20000300000
| 0xA000000000000000
| 0xC
|}


== SPE Shadow Registers Memory Region class ==
00000000 01 19 01 19                                      |....|
00000004


This type of memory region represents shadow registers memory region of a SPE. It's created e.g. in '''lv1_construct_logical_spe''' or in '''syscall 0x10040'''.
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C


=== vtable ===
00000000 01 19 01 19                                      |....|
00000004


0x00358448 (3.15)  
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C


=== Objects ===
00000000 01 25 01 25                                      |.%.%|
00000004


Here is the list of SPE Shadow Registers memory region objects i found in HV 3.15.  
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C
 
00000000  01 25 01 25                                      |.%.%|
00000004
</pre>
 
'''4. Received Header'''
 
<pre>
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=16 skip=$((0x8c000)) status=noxfer | hexdump -C
 
00000000  14 01 00 00 00 00 80 15 00 00 00 03 00 05 00 05  |................|
00000010
 
</pre>
 
'''5. Received Body'''
 
<pre>
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=8 skip=$((0x8c010)) status=noxfer | hexdump -C
 
00000000  00 00 c7 01 ff 00 00 00                          |..Ç.ÿ...|
00000008
</pre>
 
==Examples==
 
===Get RTC===
 
* Used by LV1 call '''lv1_get_rtc'''
* Communication with SYSCON 4


{| class="wikitable FCK__ShowTableBorders"
Request:
|-
<pre>
! Address in HV dump
# write packet
! LPAR id
 
! SPE
# echo "0: 13 01 0000 0000 8014 00000004 0001 0001 33 00 00 00 0000ff1f" | xxd -c256 -r | \
! LPAR Start Address
      dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer
! Size
 
! Physical Address
# dump packet counter
! Flags
! log2(Page Size)
|-
| 0x003ABDA0
| 2
| 1  
| 0x300000012000
| 0x1000
| -
| 0xA000000000000000
| 0xC
|-
| 0x003B4290
| 2
| 2
| 0x300000014000
| 0x1000
| -
| 0xA000000000000000
| 0xC
|-
| 0x003A8A00
| 2
| 3
| 0x300000010000
| 0x1000
| -
| 0xA000000000000000
| 0xC
|-
| 0x003B50F0
| 2
| 4
| 0x300000016000
| 0x1000
| -
| 0xA000000000000000
| 0xC
|-
| 0x001FFC90
| 2
| 5
| 0x30000000E000
| 0x1000
| -
| 0xA000000000000000
| 0xC
|-
| 0x003AE5B0
| 2
| 6
| 0x300000018000
| 0x1000
| -
| 0xA000000000000000
| 0xC
|}


== Device MMIO Memory Region class  ==
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C


This type of memory region is created when a device MMIO region is mapped into LPAR address space, e.g. in '''lv1_map_device_mmio_region'''.  
00000000  00 c0 00 c0                                      |.À.À|
00000004


=== vtable  ===
# increment packet counter


0x00352468 (3.15)  
echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer


=== Member variables  ===
# kick packet


offset 0xA8 - physical address where the device MMIO region is mapped to
# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer


=== Objects  ===
</pre>


Here is the list of Device MMIO memory region objects i found in HV 3.15.
Response:


{| class="wikitable FCK__ShowTableBorders"
<pre>
|-
# dump packet counter
! Address in HV dump  
! LPAR id
! LPAR Start Address
! Size
! Flags
! log2(Page Size)
! Physical Address
! Device
|-
| 0x001FDF00
| 2
| 0x4000001D0000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003010000
| USB controller
|-
| 0x003B3850
| 2
| 0x400000200000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003020000
| USB controller
|-
| 0x003B6E50
| 2
| 0x4000001E0000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003810000
| USB controller
|-
| 0x003B9950
| 2
| 0x4000001F0000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003820000
| USB controller
|}


== GPU Device Memory Region class  ==
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C


This type of memory region is created e.g. in '''lv1_gpu_open''', '''lv1_gpu_device_map''' and '''lv1_undocumented_function_114'''.  
00000000  00 c1 00 c1                                      |.Á.Á|
00000004


=== vtable  ===
# dump response packet


0x00357C48 (3.15)  
# dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C


=== Member variables ===
00000000 13 01 00 00 00 00 80 14  00 00 00 04 00 08 00 08  |................|
00000010  00 00 00 00 15 af 47 6b                          |.....¯Gk|
00000018
</pre>


offset 0xA8 - physical address
===Ring Buzzer===


=== Objects  ===
* Used by System Manager
* Communication with SYSCON 1


Here is the list of Device GPU memory region objects i found in HV 3.15.
Request:


{| class="wikitable FCK__ShowTableBorders"
<pre>
|-
# write packet
! Address in HV dump
! LPAR id
! LPAR Start Address
! Size
! Flags
! log2(Page Size)
! Physical Address
|-
| 0x003AF380
| 2
| 0x700190000000
| 0xFE00000
| 0x8000000000000000
| 0x14
| 0x28080000000
|-
| 0x003AF500
| 2
| 0x4000001A0000
| 0xC000
| 0x8000000000000000
| 0xC
| 0x3C0000
|-
| 0x003AF680
| 2
| 0x4800006C0000
| 0x40000
| 0x8000000000000000
| 0xC
| 0x2808FE00000
|-
| 0x003AFC30
| 2
| 0x440000380000
| 0x20000
| 0x8000000000000000
| 0xC
| 0x28000C00000
|-
| 0x003BB420
| 2
| 0x3C0000108000
| 0x8000
| 0x8000000000000000
| 0xC
| 0x28000080100
|}


== Direct Map Memory Region class ==
# echo "0: 16 01 1620 0000 804d 00000001 0008 0008 20 29 0a 00 000001b6 0000fdcb" | xxd -c256 -r | \
      dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer


This type of memory region is created in HV call '''lv1_undocumented_function_114'''.
# dump packet counter
'''lv1_undocumented_function_114''' allows you to map any memory address into LPAR's memory address.


* The HV call '''lv1_undocumented_function_115''' destroys a memory region of this type.
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
* HV allows GameOS to create objects of this type of size 0 only !!! But it can be exploited with a dangling HTAB entry.


=== vtable ===
00000000 00 c0 00 c0                                      |.À.À|
00000004


0x00357C48 (3.15)
# increment packet counter


=== Member variables  ===
echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer


offset 0xA8 - physical address
# kick packet


=== Exploiting HV with memory glitching and HV call lv1_undocumented_function_114 ===
# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer


Here is a short description of the method i used to exploit HV from GameOS 3.15 and 3.41.
# you should hear a beep


* First i used the Geohot's method to create a dangling HTAB entry.
</pre>
* Making memory glitch work on GameOS was the largest of my obstacles but i solved it and i'm able to create a dangling HTAB entry from GameOS within 1-3 minutes.
* Then i created many '''Direct Map Memory Region''' objects of size 0 with HV call '''lv1_undocumented_function_114''' and checked if they are within the page to which the dangling HTAB entry points to.
* When i found one such '''Direct Map Memory Region''' object i patched the size of this object to 0x1000. Then i pointed this memory region object to the code of HV call '''lv1_undocumented_function_114''' and patched 4 bytes in this HV call which allows me to create any '''Direct Map Memory Region''' objects without any restrictions.
* Function '''LPAR_construct_direct_mapping_mem_region''' which is used by HV call '''lv1_undocumented_function_114''' has a parameter (register %r9) and when this parameter is not 0 then HV will allow you to create any '''Direct Map Memory Region''' objects without restrictions, but unfortunately the HV call '''lv1_undocumented_function_114''' passes 0 in this parameter, so i just patched it.
* Then i mapped whole HV memory range with the patched HV call '''lv1_undocumented_function_114''' into the address space of GameOS.
* And now you have read/write access to the whole HV.
* $ONY could fix this exploit by disallowing creating of '''Direct Map Memory Region''' objects of size 0, but i know tons of other HV C++ classes which will allow me to exploit the HV in a similar way, so it wouldn't bring $ONY anything :-) And they have to change member variable offsets in those objects to make sure that i cannot patch them easily :-)


== Methods  ==
Response:


LPAR_get_memory_region_by_start_address - 0x002C7C40 (3.15)
<pre>
# dump packet counter


LPAR_get_memory_region_by_address - 0x002C7DA8 (3.15)  
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C


LPAR_mem_addr_to_phys_addr(LPAR id, LPAR address, phys_addr) - 0x002FB8F0 (3.15)
00000000  00 c1 00 c1                                      |.Á.Á|
00000004


LPAR_construct_direct_mapping_mem_region - 0x002D4D04 (3.15)
# dump response packet


= Network Devices =
# dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C
00000000  16 01 16 20 00 00 80 4d  00 00 00 01 00 01 00 01  |... ...M........|
00000010 00 00 00 00 00 00 fe e3                          |......þã|
00000018


== Ethernet Gelic Device  ==
</pre>


device id = 0
=Isolation=
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#Isolation gitbrew.org::Isolation] <br />


MAC Address: 00:1F:A7:C6:2A:C5
==Running Isolated SPE Modules On OtherOS++ Linux==


device memory base address = 0x24003004000 (size = 0x1000)
* spp_verifier is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux.
* It decrypts default.spp profile
* Tested on 3.41 and 3.55.
* You can modify it easily to run other SPE modules.


== WLAN Gelic Device  ==
<pre>
root@debian-hdd:/home/glevand/spp_verifier# cat spp_verifier_355.self > /proc/spp_verifier/spu
root@debian-hdd:/home/glevand/spp_verifier# cat default_355.spp > /proc/spp_verifier/profile
root@debian-hdd:/home/glevand/spp_verifier# echo 1 > /proc/spp_verifier/run
root@debian-hdd:/home/glevand/spp_verifier# cat /proc/spp_verifier/debug


device id = 0
PPE id (0x0000000000000001) VAS id (0x0000000000000002)
 
lv1_construct_logical_spe (0x00000000)
MAC Address: 02:1F:A7:C6:2A:C5 (locally administered)  
SPE id (0x000000000000002b)
 
lv1_undocumented_function_209 (0x00000000)
=== Net Manager  ===
shadow execution status (0x0000000000000002)
 
lv1_get_spe_interrupt_status(1) (0x00000000)
*Net Manager runs in Process 9
interrupt status 1 (0x0000000000000000)
*It sends commands to '''/dev/sc1''' to reset WLAN Gelic device
sleep
*It opens '''/dev/net0''', sets MAC address and writes device firmware '''eurus_fw.bin''' to WLAN device by using '''ioctl''' syscall
shadow execution status (0x0000000000000002)
lv1_get_spe_interrupt_status(1) (0x00000000)
interrupt status 1 (0x0000000000000001)
ea (0xc000000002920000) esid (0xc000000008000000) vsid (0x0000408f92c94500)
lv1_undocumented_function_62 (0x00000000)
lv1_clear_spe_interrupt_status(1) (0x00000000)
lv1_undocumented_function_168 (0x00000000)
sleep
shadow execution status (0x0000000000000007)
lv1_get_spe_interrupt_status(1) (0x00000000)
interrupt status 1 (0x0000000000000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
interrupt status 2 (0x0000000000000000)
out interrupt mbox (0x0000000000000002)
out interrupt mbox (0x0000000000000002)
lv1_undocumented_function_167 (0x00000000)
lv1_clear_spe_interrupt_status (0x00000000)
lv1_undocumented_function_200 (0x00000000)
sleep
shadow execution status (0x000000000000000b)
lv1_get_spe_interrupt_status(1) (0x00000000)
interrupt status 1 (0x0000000000000000)
shadow execution status (0x000000000000000b)
problem status (0x01000082)
lv1_destruct_logical_spe (0x00000000)


=== /dev/net0 ===
root@debian-hdd:/home/glevand/spp_verifier# hexdump -C /proc/spp_verifier/profile | less
...
...
00000200  00 02 00 05 00 00 20 a0  00 00 00 01 00 03 00 00  |......  ........|
00000210  00 00 00 00 00 00 00 01  00 00 00 0e 00 00 00 00 |................|
00000220  00 00 02 88 00 00 00 01  10 70 00 00 01 00 00 01  |.........p......|
00000230  00 00 00 00 00 00 00 00  53 43 45 5f 43 45 4c 4c  |........SCE_CELL|
00000240  4f 53 5f 50 4d 45 00 00  00 00 00 00 00 00 00 00  |OS_PME..........|
00000250  00 00 00 00 00 00 00 00  00 00 00 06 00 00 02 50  |...............P|
00000260  10 70 00 00 01 00 00 01  2f 66 6c 68 2f 6f 73 2f  |.p....../flh/os/|
00000270  74 68 69 73 5f 69 73 5f  64 75 6d 6d 79 00 00 00  |this_is_dummy...|
00000280  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
...
</pre>


The device supports 3 ioctl commands:
==Using metldr On OtherOS++ Linux==


*0 - 0x002AC10C (3.15)
* spp_verifier_direct is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux by using metldr directly.
*1 - 0x002AC250 (3.15)
* It decrypts default.spp profile.
*2 - EURUS_STAT 0x002AC320 (3.15)
* Tested on 3.41 and 3.55.
* You can modify it easily to run other SPE modules.


=== Methods ===
<pre>
 
root@debian-hdd:/home/glevand/spp_verifier_direct# insmod ./spp_verifier_direct.ko
net_control_cmd_GELIC_LV1_POST_WLAN_CMD - 0x0024A55C (3.15)  
root@debian-hdd:/home/glevand/spp_verifier_direct# cat metldr > /proc/spp_verifier_direct/metldr
 
root@debian-hdd:/home/glevand/spp_verifier_direct# cat isoldr_355 > /proc/spp_verifier_direct/isoldr
net_control_wlan_cmd_GELIC_EURUS_CMD_ASSOC - 0x00246C78 (3.15)  
root@debian-hdd:/home/glevand/spp_verifier_direct# cat RL_FOR_PROGRAM_355.img > /proc/spp_verifier_direct/rvkprg
 
root@debian-hdd:/home/glevand/spp_verifier_direct# cat EID0 > /proc/spp_verifier_direct/eid0
net_control_wlan_cmd_GELIC_EURUS_CMD_START_SCAN - 0x00248A14 (3.15)  
root@debian-hdd:/home/glevand/spp_verifier_direct# cat spp_verifier_355.self > /proc/spp_verifier_direct/spu
root@debian-hdd:/home/glevand/spp_verifier_direct# cat default_355.spp > /proc/spp_verifier_direct/profile
root@debian-hdd:/home/glevand/spp_verifier_direct# echo 1 > /proc/spp_verifier_direct/run
root@debian-hdd:/home/glevand/spp_verifier_direct# cat /proc/spp_verifier_direct/debug
   
PPE id (0x0000000000000001) VAS id (0x0000000000000002)
lv1_construct_logical_spe (0x00000000)
SPE id (0x0000000000000033)
lv1_enable_logical_spe (0x00000000)
lv1_set_spe_interrupt_mask(0) (0x00000000)
lv1_set_spe_interrupt_mask(1) (0x00000000)
lv1_set_spe_interrupt_mask(2) (0x00000000)
lv1_set_spe_privilege_state_area_1_register (0x00000000)
ea (0xc000000002680000) esid (0xc000000008000000) vsid (0x0000408f92c94500)
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
out interrupt mbox (0x0000000000000001)
lv1_clear_spe_interrupt_status(2) (0x00000000)
transferring EID0, ldr args and revoke list to LS
waiting until MFC transfers are finished
MFC transfers done
out mbox (0x00000001)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
out interrupt mbox (0x0000000000000002)
lv1_clear_spe_interrupt_status(2) (0x00000000)
out mbox (0x00000002)
lv1_clear_spe_interrupt_status(2) (0x00000000)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
problem status (0x01000082)
lv1_destruct_logical_spe (0x00000000)


net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WEP_CFG - 0x00249F24 (3.15)
root@debian-hdd:/home/glevand/spp_verifier_direct# hexdump -C /proc/spp_verifier_direct/profile | less
...
...
00000200  00 02 00 05 00 00 20 a0  00 00 00 01 00 03 00 00  |......  ........|
00000210  00 00 00 00 00 00 00 01  00 00 00 0e 00 00 00 00  |................|
00000220  00 00 02 88 00 00 00 01  10 70 00 00 01 00 00 01  |.........p......|
00000230  00 00 00 00 00 00 00 00  53 43 45 5f 43 45 4c 4c  |........SCE_CELL|
00000240  4f 53 5f 50 4d 45 00 00  00 00 00 00 00 00 00 00  |OS_PME..........|
00000250  00 00 00 00 00 00 00 00  00 00 00 06 00 00 02 50  |...............P|
00000260  10 70 00 00 01 00 00 01  2f 66 6c 68 2f 6f 73 2f  |.p....../flh/os/|
00000270  74 68 69 73 5f 69 73 5f  64 75 6d 6d 79 00 00 00  |this_is_dummy...|
00000280  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
...
</pre>


net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WPA_CFG - 0x002497B8 (3.15)
= Gelic Device =
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#Gelic_Device gitbrew.org::Gelic Device] <br />


= Event Notification  =
==sys.hw.config==


*Event Notfication is used e.g. to notify a LPAR about some event, e.g. device interrupt or notify a LPAR about destruction of another LPAR.  
* Value of the loader parameter "sys.hw.config" controls if Gelic WLAN is enabled or not.
*For example Process 9 is notified through Event Notification when LPAR 2 is destructed.  
* Value of the loader parameter "sys.hw.config" is stored in the repository node "sys.hw.config" too.
*During LPAR construction, Process 9 creates an Outlet object with '''syscall 0x1001A''' and then passes the outlet ID to the '''syscall 0x10009''' that constructs the LINUX LPAR. In this way Process 9 is notified when LINUX LPAR is destructed.
* If bit '''0x40000''' is set then LV1 allows using Gelic WLAN interface from LV2.
* Value on my PS3 slim '''0x4e00ffff0a03bc3c''' with Gelic WLAN interface disabled. As you can see, the Gelic WLAN interface is disabled and LV1 doesn't allow using of LV1 calls 196 and 195. It returns LV1_CONDITION_NOT_SATISFIED.
* GameOS checks bit '''0x40000''' of the repository node "sys.hw.config" during network initialization and if it's set then LV2 initializes Gelic WLAN interface.
* Check your "sys.hw.config" repository node and if bit '''0x40000''' is set then you are a lucky owner of a PS3 model with the old WLAN interface.
* '''On newer PS3 models, GameOS uses USB interface to communicate with WLAN.'''
* On PS3 models, where bit '''0x40000''' is NOT set in "sys.hw.config" repository node, the new USB interface is used.
 
''Note:[http://www.ps3devwiki.com/index.php?title=Wifi old vs. new]: Old == CECHA up to CECHK, New == CECHL and later''


== Outlet class  ==
== Control Interface ==


This is the base Outlet class. There are different types of Outlet and they derive from this base class.  
HV calls 195 and 196 are used by GameOS to send commands to Gelic device directly.


=== vtable  ===
=== lv1_undocumented_function_196 ===


0x00357DC0 (3.15)
==== Parameters ====


=== Member variables  ===
r3 - LPAR address of data buffer


offset 0x30 - type (8 bytes)
r4 - size of data buffer


offset 0x38 - pointer to LPAR that owns this Outlet object
r5 - must be 0


offset 0x48 - outlet id (8 bytes)
=== lv1_undocumented_function_195 ===


offset 0x90 - VIRQ assigned to this Outlet object (4 bytes)
==== Parameters ====


== Event Receive Port class  ==
r3 - command (16 bit value)


*This type of Outlet is created e.g. in '''lv1_construct_event_receive_port''' and in '''syscall 0x1001A'''.
r4 - command data size
*HV calls '''lv1_connect_irq_plug''' and '''lv1_connect_irq_plug_ext''' assigns a VIRQ to Event Receive Port object.


=== vtable  ===
r5 - must be 0


0x00357E88
=== Data Buffer ===


== VUART Outlet  ==
* Data Buffer passed to HV call 196 is divided into 2 parts.
* The first 0x800 bytes are for sending and receiving command data
* The remaining 0x800 bytes are for event notification.


*HV supports only one VUART Outlet per LPAR
=== Command Data Buffer ===
*'''lv1_configure_virtual_uart_irq''' constructs a VUART Outlet object and passes the address of LPAR's VUART IRQ Bitmap to HV


=== vtable  ===
* Every command data sent to Gelic device contains header of size '''0xC'''
* After the header follows the command data
* After the Gelic device processed the command, it notifies LV2 kernel about command completion by sending an interrupt


0x00357DC0
==== Header ====


=== VUART IRQ Bitmap  ===
* Size is '''0xc'''.
* Byte order is little-endian.
* Header data in a request command buffer is always all 0s.


*At address 0x38(LPAR ptr) + 0x158 is the VUART IRQ Bitmap owned by HV for LPAR (4 * 8 bytes = 256 bits)
0x0 - command = request command + 1 (2 bytes)
*At address 0x38(LPAR ptr) + 0x150 is stored the physical address of LPAR's VUART IRQ Bitmap that was passed to '''lv1_configure_virtual_uart_irq'''
*When a VUART interrupt is generated by HV then first the VUART IRQ Bitmap owned by HV is updated and then this bitmap is copied to LPAR's VUART IRQ Bitmap, so VUART IRQ Bitmap is stored twice, once in HV and once in LPAR, just like IRQ State Bitmap.
*VUART IRQ Bitmap is not allowed to cross page boundary of LPAR memory region where it is stored. HV checks it and makes sure that it doesn't happen.
*'''GameOS 3.41''' VUART IRQ bitmap is at address '''0x80000000003556E8''' and of size '''32 bytes (256 bits, each bit corresponds to a VUART port)'''.
*'''GameOS 3.15''' VUART IRQ bitmap is at address '''0x8000000000354768'''.


= Logical PPE  =
0x4 - result, 0x1 - success ??? 0x2 - buffer too small ??? (2 bytes)


*Logical PPE is used for interrupt management of LPAR.
0x6 - body size (2 bytes)
*A Logical PPE object is created in '''syscall 0x10005'''. It' used e.g. in Process 9 during LPAR construction.
 
*'''syscall 0x10007''' activates a Logical PPE object
=== Event Data Buffer ===
*0x67F0(HSPRG0) - pointer to currently active Logical PPE object (in HV dump it points to Linux PPE object naturally because the dump was made on Linux, so Linux LPAR was active at that time)
 
*E.g. '''lv1_get_logical_ppe_id''', '''lv1_start_ppe_periodic_tracer''' and '''lv1_set_ppe_periodic_tracer_frequency''' grab the currently active Logical PPE object
* The Gelic device notifies LV2 kernel by sending an interrupt when new events are available
* Event Data Buffer has 8 bytes header
* The remaining bytes are divided into event slots
* Each event slot is of size 64 bytes
* Events are in little-endian format


== vtable  ==
==== Header ====


0x00357DF0 (3.15)  
offset 0x0 - GET index (4 bytes)


== Member variables  ==
offset 0x4 - PUT index (4 bytes)


offset 0x90 - pointer to an object that contains VIRQ-Outlet mapping table for thread 0
* GET index is updated by Gelic driver. The Gelic driver reads events beginning with the event slot at index GET.
* PUT index is the index of event entry where next Gelic event will be stored by the Gelic device.
* If GET index is equal to PUT index then there are no Gelic events.


offset 0x98 - pointer to an object that contains VIRQ-Outlet mapping table for thread 1
=== GameOS ===


== Objects  ==
* LV2 syscall 726 sends Gelic device command and blocks until a response from the Gelic device arrives
* LV2 kernel uses this LV1 interface to send commands to Gelic device internally too, probably for wireless controllers and Wake On WLAN.
* The system call 726 is used heavily by VSH.


Here is the list of Logical PPE objects i found in HV 3.15.
==== Parameters ====


{| class="wikitable FCK__ShowTableBorders"
r3 - command (16 bits)
|-
! Address in HV dump
! LPAR id
! PPE id
|-
| 0x0069C7F0
| 1
| 1
|-
| 0x007A8900
| 2
| 1
|}


== Virtual IRQ - Outlet Mapping  ==
r4 - effective address of command data buffer


*HV maintains 2 tables per PPE that map a VIRQ to an Outlet object.
r5 - size of command data buffer
*The table has 256 entries and is indexed by VIRQ.
*Each entry is a pointer to Outlet object.
*Each Logical PPE object has 2 tables, one for each thread of Cell CPU.


=== LPAR 1 PPE 1 Thread 0  ===
=== Commands ===


0x0069C990 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 0''' (not empty)  
====Unknown (0x1)====


{| class="wikitable FCK__ShowTableBorders"
* Used by VSH.
|-
* Command buffer size is '''0x10'''.
! VIRQ
* Used in AP mode.
! Address of Outlet object in HV dump
* Enables AP mode ???
! Description
 
|-
====Get AP SSID (0x3)====
| 58
 
| 0x00090D10
* Command buffer is of size '''0x30'''.
| -
* Returns SSID in AP mode.
|-
 
| 59
offset 0xC - SSID (32 bytes)
| 0x006BAC50
 
| -
====Set AP SSID (0x5)====
|-
 
| 60
* Used by VSH.
| 0x006B3ED0
* Command buffer is of size '''0x30'''.
| FLASH storage device / Storage device notification for LPAR 1
* Sets SSID in AP mode.
|-
 
| 61
offset 0xC - SSID (32 bytes)
| 0x00697E70
| VUART interrupts
|-
| 62
| 0x001C8F20
| -
|}


=== LPAR 1 PPE 1 Thread 1  ===
====Get Channel (0xf)====


0x0069D9B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 1''' (empty)
* Used by VSH.
* Command buffer is of size '''0x31'''.
* Data is returned from the device.
* Returns list of channels and active channel.


=== LPAR 2 PPE 1 Thread 0  ===
offset 0x2F - active channel (2 bytes)


0x000A06B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 0''' (not empty)  
====Set Channel (0x11)====


{| class="wikitable FCK__ShowTableBorders"
* Used by VSH.
|-
* Command buffer size is '''0xd'''
! VIRQ
* Valid channels: '''0 - 13'''. '''0''' means that the channel is selected '''automatically'''.
! Address of Outlet object in HV dump
 
! Description
offset 0xC - channel (1 byte)
|-
 
| 20
====Unknown (0x27)====
| 0x003AA210
 
| -
* Command buffer size is '''0xF'''.
|-
 
| 21
====Set Antenna (0x29)====
| 0x003AFEC0
 
| -
* Command buffer size is '''0xe'''
|-
 
| 22
offset 0xC - 0,1 or 2 (1 byte)
| 0x001FC010
 
| -
offset 0xD - 2 (1 byte)
|-
 
| 23
====Set AP WEP Configuration (0x5b)====
| 0x003A8E50
 
| -
* Used by VSH.
|-
* Command buffer is of size '''0x56'''.
| 24
* Sets WEP security type and WEP key.
| 0x001FFED0
* Security types: 0 - none, 1 - wep64, 2 - wep128
| SPE 0 Class 0 Interrupt
 
|-
offset 0xE - security mode: 0 - none, 1 - wep64, 2 - wep128 (1 byte)
| 25
 
| 0x003AE160
offset 0x10 - WEP key (64 bytes)
| SPE 0 Class 1 Interrupt
 
|-
====Unknown (0x61)====
| 26
 
| 0x003AE350
* Used by VSH.
| SPE 0 Class 2 Interrupt
* Command buffer size is '''0xd'''
|-
 
| 27
====Unknown (0x65)====
| 0x003AB100
 
| SPE 1 Class 0 Interrupt
* Used by VSH.
|-
* Command  uffer size is '''0xd'''.
| 28
* Used in AP mode.
| 0x003AB2F0
 
| SPE 1 Class 1 Interrupt
====Get Eurus Firmware Version (0x99)====
|-
 
| 29
* Used by VSH.
| 0x003AB4E0
 
| SPE 1 Class 2 Interrupt
Here is the response on my PS3 Slim:
|-
<pre>
| 30
00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2|
| 0x003AA6A0
00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19|
| SPE 2 Class 0 Interrupt
00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).|
|-
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00      |..............  |
| 31
</pre>
| 0x003AA890
 
| SPE 2 Class 1 Interrupt
====Get AP Operating Mode (0xb7)====
|-
 
| 32
* Used by VSH.
| 0x003AAA80
* Command buffer size is '''0x10'''
| SPE 2 Class 2 Interrupt
* Returns AP operating mode (mixed, 11b or 11g).
|-
 
| 33
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)
| 0x003B44A0
 
| SPE 3 Class 0 Interrupt
====Set AP Operating Mode (0xb9)====
|-
 
| 34
* Used by VSH.
| 0x003B4690
* Command buffer size is '''0x10'''
| SPE 3 Class 1 Interrupt
* Sets AP operating mode (mixed, 11b or 11g).
|-
 
| 35
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)
| 0x003B4AD0
 
| SPE 3 Class 2 Interrupt
====Unknown (0xc5)====
|-
 
| 36
* Used by VSH.
| 0x003B5300
* Command buffer size is '''0x10'''.
| SPE 4 Class 0 Interrupt
* Used in AP mode.
|-
| 37
| 0x003B54F0
| SPE 4 Class 1 Interrupt
|-
| 38
| 0x003B56E0
| SPE 4 Class 2 Interrupt
|-
| 39  
| 0x003AE7C0
| SPE 5 Class 0 Interrupt
|-
| 40
| 0x003AE9B0
| SPE 5 Class 1 Interrupt
|-
| 41
| 0x003AEBA0
| SPE 5 Class 2 Interrupt
|-
| 42
| 0x003B2040
| Storage device notification for LPAR 2
|-
| 43
| 0x003AEE30
| VUART interrupts
|-
| 44
| 0x001FEAA0
| -
|-
| 45
| 0x001FEED0
| HDD storage device
|-
| 46
| 0x003B5E20
| -
|-
| 47
| 0x003B7040
| -
|-
| 48
| 0x003B9B40
| -
|-
| 49
| 0x003B3A40
| -
|-
| 50
| 0x003BACA0
| Gelic device
|-
| 51
| 0x003BAE10
| UNKNOWN storage device
|-
| 52
| 0x003B8350
| -
|}


=== LPAR 2 PPE 1 Thread 1  ===
offset 0xC - ??? (4 bytes)


0x007A89E0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 1''' (not empty)  
====Set AP WPA AKM Suite (0xc9)====


{| class="wikitable FCK__ShowTableBorders"
* Used by VSH.
|-
* Command buffer size is '''0x11'''.
! VIRQ
* Sets WPA AKM suite in AP mode.
! Address of Outlet object in HV dump
 
! Description
offset 0xC - AKM suite (4 bytes)
|-
| 16
| 0x003B2480
| -
|-
| 17
| 0x003B2590
| -
|-
| 18
| 0x003B26A0
| -
|-
| 19
| 0x003B27B0
| -
|}


== IRQ State Bitmap  ==
====Set AP WPA Group Cipher Suite (0xcf)====


*There is one IRQ State Bitmap (256 bits = 32 bytes) per thread of Logical PPE
* Used by VSH.
*'''HSPRG0 value is per thread''', so there are 2 HSPRG0 values in HV dump&nbsp;!!!
* Command buffer size is '''0x10'''
*The IRQ State Bitmap of a thread is stored at -0x68E0(HSPRG0)
* Used in AP + WPA mode.
*When an Event or Interrupt happens then the bitmap at 0x68E0(HSPRG0) is updated
*The physical address of '''LPAR's IRQ State Bitmap''' of thread is stored at offset -0x68C0(HSPRG0)
*The address of LPAR's IRQ State Bitmap is passed to Hypervisor through HV call '''lv1_configure_irq_state_bitmap'''  
*'''lv1_detect_pending_interrupts''' returns value of current IRQ State Bitmap.
*The IRQ State Bitmap is updated if an Outlet object is assigned to VIRQ and when Outlet generates an event
*After IRQ State Bitmap update, it's copied to LPAR's IRQ State Bitmap and a hardware interrupt is generated so that LPAR can read it's IRQ State Bitmap and handle interrupts.
*So, IRQ State Bitmap is stored twice, once in HV and once in LPAR, just like VUART IRQ Bitmap.
*'''GameOS''' IRQ state bitmap is stored at address '''SPRG0 + 0x1C0 and of size 64 bytes (256 bits state + 256 bits mask) per thread of Cell CPU'''. So there are 2 IRQ state bitmaps.


0x8941FC0 - physical address of LPAR's IRQ State Bitmap for Thread 0 of LINUX LPAR
offset 0xC - group cipher suite: group (4 bytes)


0x8948FC0 - physical address of LPAR's IRQ State Bitmap for Thread 1 of LINUX LPAR
====Set AP WPA PSK Binary (0xd3)====


= System Controller (SC or SYSCON)  =
* Used by VSH.
* Command buffer size is '''0x4c'''
* Sets WPA PSK binary


*Data received from SC is sent to a VUART
offset 0xC - PSK (64 bytes)
*'''lv1_get_rtc''' and '''syscall 0x10036''' communicate with '''SC VUART 4'''.


=== VUART Table  ===
====Set AP WPA Reauthentication Timeout (0xd5)====


*Address of SC VUART Table - 0x00610410 (3.15).  
* Used by VSH.
*There are 5 VUARTs for SC in HV 3.15
* Command buffer size is '''0x10'''
* Sets WPA Reauth timeout value in AP WPA mode.
* VSH uses 36000 as timeout.


Here is the SC VUART table from HV 3.15:
offset 0xC - timeout value in seconds (2 bytes)


{| class="wikitable FCK__ShowTableBorders"
====Unknown (0x127)====
|-
 
! Index
* Used by VSH.
! Address of VUART object in HV dump
* Command buffer size is '''0x10'''.
! Description
* Used in AP + WPA mode.
|-
| 0
| 0x0060FD20
| This VUART is connected with the '''VUART 0 (/dev/sc0)''' of LPAR 1
|-
| 1
| 0x0060FE20
| This VUART is connected with the '''VUART 1 (/dev/sc1)''' of LPAR 1
|-
| 2
| 0x0060FF20
| This VUART is not connected to some peer VUART but i guess that it should be connected to '''VUART 2 (/dev/sc2)''' of LPAR1
|-
| 3
| 0x006124E0
| This VUART is connected with the '''VUART 3 (/dev/sc3)''' of LPAR 1
|-
| 4
| 0x00612DF0
| '''lv1_get_rtc''' and '''syscall 0x10036''' communicate with this VUART.
|}


== Interrupt Handling  ==
====Unknown (0x12b)====


spider_sc_interrupt_handler - 0x0020A68C (3.15)
* Used by VSH.
* Command buffer size is '''0x10'''.
* Used in AP + WPA mode.


== Methods  ==
====Set AP WPA PSK Passphrase (0x17d)====


sc_vuart_4_get_peer_vuart - 0x002ED384 (3.15)
* Used by VSH.
* Command buffer size is '''0x2D'''


sc_send - 0x0020A908 (3.15)
offset 0xD - passphrase (32 bytes)


sc_receive - 0x0020A354 (3.15)
====Set AP WPA Pairwise Cipher Suite (0x1bf)====


sc_vuart_rx_trigger_callback - 0x002ED470 (3.15)
* Used by VSH.
* Command buffer size is '''0x11'''
* Used in AP + WPA mode.


== lv1_get_rtc  ==
offset 0xC - pairwise cipher suite (4 bytes)


*'''lv1_get_rtc''' communicates with SC VUART 4.
offset 0x10 - ??? (1 byte)
*20 bytes are written to the peer VUART of SC VUART 4.
*After a request is sent to SC VUART 4, '''lv1_get_rtc''' busy waits until SC VUART 4 receive data buffer is not empty.
*When SC VUART 4 receive data buffer is not empty, '''lv1_get_rtc''' reads 24 bytes from the VUART.


== SYSCON Protocol ==
====Unknown (0x1d9)====


* I was able to enable SYSCON Manager debug messages in HV Process 5
* Used by VSH.
* Messages sent to SYSCON are at least '''0x10''' bytes of size. SC VUARTs check it before sending the messages to SYSCON.
* Command buffer size is '''0x10'''
* The header size of the SYSCON messages is '''0x10''' bytes.


=== Packet Header ===
====Unknown (0x1dd)====


* Packet header is of size '''0x10''' bytes.
* Used by VSH.
* At offset '''0x6''' of SYSCON packet is the header checksum which is of size '''2''' bytes.
* Command buffer size is '''0xd'''
* '''The header checkum is just a sum of first 6 header bytes and 0x8000 constant'''
* The '''2nd byte''' in every SYSCON message has to be '''1''' or else the function '''sc_send''' fails.
* The '''word''' at offset '''0x8''' is the '''SC VUART index'''.
* The '''half-words''' at offset '''0xC''' and '''0xE''' have to be equal or the function '''sc_send''' fails.


<pre>
====Unknown (0x1ed)====
struct sc_hdr
{
    uint8_t field0;
    uint8_t field1;          /* always 1 */
    uint8_t field2[4];
    uint16_t cksum;          /* header checksum */
    uint32_t index;          /* syscon index (0 - /dev/sc0, 1 - /dev/sc1, 2 - /dev/sc2, 3 - /dev/sc3) */
    uint16_t size1;          /* body size */
    uint16_t size2;          /* body size */
};
</pre>


==== Calculating Packet Header Checksum ====
* Used by VSH.
* Command buffer is of size '''0x17'''.
* Rate control ???


<pre>
====Get Eurus HW Revision (0x1fb)====
/* calculating SC packet header checksum */


/*
* Command buffer size is '''0x10'''.
* sc_hdr_cksum
*/
uint16_t sc_hdr_cksum(struct sc_hdr *sc_hdr)
{
    uint8_t *ptr;
    uint32_t sum;


    ptr = (uint8_t *) sc_hdr;
====Associate (0x1001)====
    sum = 0;


    for (i = 0; i < 6; i++)
* Used by VSH.
        sum += *ptr++;
* Used by LV1 on FAT models.
* Command buffer size is '''0xd'''
* Data passed to Gelic device is all 0s


    sum += 0x8000;
====Get Common Configuration (0x1003)====


    return sum & 0xffff;
* Used by VSH.
}
* Used by LV1 on FAT models.
* Command buffer size is '''0x18'''
* Data passed to Gelic device is all 0s


struct sc_hdr sc_hdr;
====Set Common Configuration (0x1005)====


memset(&sc_hdr, 0, sizeof(sc_hdr));
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0x18'''
* Hmm, VSH always removes QOS bit from capability, that means Jupiter doesn't support QOS ???


sc_hdr.cksum = sc_hdr_cksum(sc_hdr);
offset 0xC - BSS type: 0 - infrastructure, 1 - ???, 2 - adhoc (1 byte)


/* fill sc header here */
offset 0xD - authentication mode: 0 - open, 1 - shared key


sc_hdr.cksum = sc_hdr_cksum(sc_hdr);
offset 0xE - opmode: 0 - 11bg, 1 - 11b, 2 - 11g (1 byte)  
</pre>


=== Packet Body ===
offset 0xF - ??? (1 byte)


* Packet body follows packet header
offset 0x10 - BSSID (6 bytes)
* Packet body size is stored at offset '''0xC''' and '''0xE''' in packet header and is of size 2 bytes


=== Reading SYSCON EPROM (NVS Service) ===
offset 0x16 - capability (2 bytes)


Here is a command which is sent to SYSCON to read 1 byte of EPROM at offset 0x48C07 (Product Mode):
====Get WEP Configuration (0x1013)====
0x14 <span style="background:#00FF00">0x01</span> 0x00 0x00 0x00 0x00 <span style="background:#FF0000">0x80 0x15</span> <span style="background:#FFFF00">0x00 0x00 0x00 0x00</span> <span style="background:#00FFFF">0x00 0x04</span> <span style="background:#00FFFF">0x00 0x04</span> 0x20 0x02 0x07 0x01


And here is the response to the above request:
* Used by VSH.
0x14 <span style="background:#00FF00">0x01</span> 0x00 0x00 0x00 0x00 <span style="background:#FF0000">0x80 0x15</span> <span style="background:#FFFF00">0x00 0x00 0x00 0x03</span> <span style="background:#00FFFF">0x00 0x05</span> <span style="background:#00FFFF">0x00 0x05</span> 0x00 0x02 0x07 0x01 0xff
* Used by LV1 on FAT models.
* Command buffer size is '''0x50'''
* Data passed to Gelic device is all 0s


=== PCI Bus Power ===
====Set WEP Configuration (0x1015)====


* '''Used by PS2EMU System Manager in HV process 9 when PS2 EMU is booted'''
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0x50'''


==== PCI Bus Power On ====
====Get WPA Configuration (0x1017)====


'''Request to SC1:'''
* Used by VSH.
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x01
* Used by LV1 on FAT models.
* Command buffer size is '''0x5b'''
* Data passed to Gelic device is all 0s


==== PCI Bus Power Off ====
====Set WPA Configuration (0x1019)====


'''Request to SC1:'''
* Used by VSH.
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x00
* Used by LV1 on FAT models.
* Command buffer size is '''0x5b'''


=== Ring Buzzer ===
offset 0xE - security type: 0 - WPA, 1 - RSNA (1 byte)


'''Request:'''
offset 0xF - psk type: 0 - hex, 1 - bin (1 byte)
0x16 0x01 0x00 0x00 0x00 0x00 0x80 0x17 0x00 0x00 0x00 0x00 0x00 0x08 0x00 0x08 0x20 0x00 0x00 0x00 0x00 0x00 0x00 0x00


=SYSCON=
offset 0x10 - psk key: hex or bin (64 bytes)
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#SYSCON gitbrew.org::SYSCON] <br />


SYSCON MMIO registers can be accessed on Linux with a driver using lv1_undocumented_function_114, e.g. '''ps3sbmmio'''.
offset 0x50 - group cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
Use ps3sbmmio device driver carefully, an access at some addresses could shutdown your PS3.


==Packet Header==
offset 0x54 - pairwise cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)


* Size is '''0x10'''.
offset 0x58 - AKM suite: 0x0050f202 - WPA PSK, 0x000fac02 - RSNA PSK (4 bytes)


<pre>
'''See IEEE 802.11 specification for more details about cipher/AKM suites
struct sc_hdr {
'''
    uint8_t service_id;
    uint8_t version;              /* must be 1 !!! */
    uint16_t transaction_id;      /* returned in response */
    uint8_t res[2];
    uint16_t cksum;              /* checksum of first 6 header bytes */
    uint32_t index;              /* SYSCON index: 0-4 */
    uint16_t payload_size[2];    /* body size */
};
</pre>


==Sending Packets==
802.11 spec: [http://standards.ieee.org/getieee802/download/802.11-2007.pdf]


* Before sending new packet to SYSCON, the Hypervisor checks 2 words at offsets 0x2400008DFF0 and 0x2400008CFF4.
====Unknown (0x1025)====
* The Hypervisor busy waits until (value + 1) at offset 0x2400008CFF4 is NOT equal to value at offset 0x2400008DFF0.
* The packet is sent with 4 byte transfers.
* First, the Hypervisor sends the header of the packet, 4 word transfers.
* The header is written beginning at the address 0x2400008D000.
* After that the Hypervisor sends the body of the packet, with 4 byte transfers too.
* The body is written beginning at the address 0x2400008D010.
* If the packet size is NOT divisible by 4 then the Hypervisor sends the remaining bytes (at most 3) as a word padded with 0s.
* After the packet body was written, the Hypervisor calculates checksum of the whole packet and writes it at the address where the last word of packet body was written + 4.
<pre>
uint32_t cksum = 0;


for (i = 0; i < packet_size; i++)
* Used by VSH.
    cksum -= packet[i];
* Command buffer size is '''0x10'''.
* Sets preamble type, something else ???


cksum = cksum & 0xffff;
offset 0xC - preamble mode: 0 - short, 1 - long (1 byte)
</pre>
* After the packet checksum was written, the Hypervisor reads the value at offset 0x2400008DFF0, modifies it and stores back:
<pre>
value = value + 1;
value &= 0xffff;
value = (value << 16) | value;
</pre>
* To notify the SYSCON about the new packet, the Hypervisor writes 0x1 to address 0x2400008E100.


==Receiving Packets==
====Unknown (0x1031)====


* The Hypervisor installs an interrupt handler for the SYSCON.
* Used by VSH.
* First, the Hypervisor reads a word from address 0x2400008E000, ors it with 0xFFFFFFFD and writes the value back.
* Command buffer size is '''0xe'''
* Then, the Hypervisor reads a word from address 0x2400008E004 and tests if bit 0x2 is set or not. The bit 0x2 should be not 0 or else the Hypervisor panics.
* After that, the Hypervisor reads a word at address 0x2400008CFF0 and 0x2400008DFF4. If there is a new packet pending from SYSCON, then the (value + 1) at 0x2400008CFF0 should be equal the value at 0x2400008DFF4.
* The Hypervisor reads the header of the packet beginning at the address 0x2400008C000.
* The header is read with 4 word transfers by the Hypervisor.
* The byte at offset 1 in the packet header must be 1 or else the Hypervisor discards the packet as invalid.
* The Hypervisor calculates the checksum of the packet header and checks it with the checksum stored in the header. If they don't match then the Hypervisor discards the packet.
* The Hypervisor reads the body of the packet beginning at the address 0x2400008C010.
* The header and the body of the received packet can be read as many times as you want !!! They remain until next SYSCON packet is received
which gives us the possibility to communicate with SYSCON on Linux easily :)


==Test==
====Get Scan Results (0x1033)====


'''1. Before sending SYSCON packet''':
* Used by VSH.
<pre>
* Used by LV1 on FAT models.
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C
* Command buffer size is '''0x5b0'''
* Data passed to Gelic device is all 0s


00000000  01 18 01 18                                      |....|
=====Scan Results=====
00000004


root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
offset 0x0 - number of scan entries (1 byte)


00000000  01 18 01 18                                      |....|
offset 0x1 - array of scan entries
00000004


root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C
======Scan Entry======


00000000  01 24 01 24                                      |.$.$|
offset 0x0 - size of this entry in bytes, this field is NOT included (2 bytes)
00000004


root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C
offset 0x2 - BSSID (6 bytes)


00000000  01 24 01 24                                      |.$.$|
offset 0x8 - RSSI (1 byte)
00000004
</pre>


'''2. SYSCON packet was sent by using ps3dm_scm read_eprom.'''
offset 0x9 - timestamp (8 bytes)


'''3. After sending SYSCON packet''':
offset 0x11 - beacon period (2 bytes)
<pre>
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C


00000000  01 19 01 19                                      |....|
offset 0x13 - capability (2 bytes)
00000004


root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
offset 0x15 - information elements (see 802.11 specification)


00000000  01 19 01 19                                      |....|
====Start Scan (0x1035)====
00000004


root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size depends on size of channel list and ESSID string length
* Data passed to Gelic device contains channel list and ESSID string
* First '''0x16''' bytes in command data buffer are all 0s, then follows the channel list and after that ESSID


00000000  01 25 01 25                                      |.%.%|
====Diassociate (0x1037)====
00000004


root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0xd'''
* Data passed to Gelic device is all 0s


00000000  01 25 01 25                                      |.%.%|
====Get RSSI (0x103d)====
00000004
</pre>


'''4. Received Header'''
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0x17'''


<pre>
offset 0x10 - MAC address of node (6 bytes)
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=16 skip=$((0x8c000)) status=noxfer | hexdump -C


00000000  14 01 00 00 00 00 80 15  00 00 00 03 00 05 00 05  |................|
offset 0x16 - RSSI (1 byte)
00000010


</pre>
====Get MAC Address (0x103f)====


'''5. Received Body'''
* Command buffer size is '''0x13'''


<pre>
offset 0xD - MAC address (6 bytes)
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=8 skip=$((0x8c010)) status=noxfer | hexdump -C


00000000  00 00 c7 01 ff 00 00 00                          |..Ç.ÿ...|
====Set MAC Address (0x1041)====
00000008
</pre>


==Examples==
* Used by VSH.
* Used by LV1 too.
* Command buffer size is '''0x12'''


===Get RTC===
====Unknown (0x104d)====


* Used by LV1 call '''lv1_get_rtc'''
* Used by VSH.
* Communication with SYSCON 4
* Command buffer size is '''0xd'''.


Request:
offset 0xC - 0 - ???, 1 - ??? (1 byte)
<pre>
# write packet


# echo "0: 13 01 0000 0000 8014 00000004 0001 0001 33 00 00 00 0000ff1f" | xxd -c256 -r | \
====Unknown (0x104f)====
      dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer


# dump packet counter
* Command buffer size is '''0xd'''.
* Returns 1 byte.


# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
offset 0xC - 0 - ???, 1 - ??? (1 byte)


00000000  00 c0 00 c0                                      |.À.À|
====Unknown (0x1051)====
00000004


# increment packet counter
* Used by VSH.
* Command buffer size is '''0x5b3'''.
* Returns '''0x5a7''' bytes.


echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer
offset 0xC - number of entries


# kick packet
offset 0x10 - entries (each entry is 0xd bytes)


# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer
====Unknown (0x1053)====


</pre>
* Used by VSH.
* Command buffer size is '''0x70'''.


Response:
offset 0xC - ??? (4 bytes)


<pre>
offset 0x10 - MAC address (6 bytes)
# dump packet counter


# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
====Unknown (0x1059)====


00000000  00 c1 00 c1                                      |.Á.Á|
* Used by VSH.
00000004
* Command buffer size is '''0x2a8'''.


# dump response packet
====Unknown (0x105f)====


# dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C
* Used by LV2.


00000000  13 01 00 00 00 00 80 14  00 00 00 04 00 08 00 08  |................|
====Get Zephyr HW Revision (0x1101)====
00000010  00 00 00 00 15 af 47 6b                          |.....¯Gk|
00000018
</pre>


===Ring Buzzer===
* Used by VSH.
* Not a Gelic device command, handled by LV2 kernel.
* LV2 uses LV1 call '''lv1_net_control(0x8000000000000002)'''
* Command buffer size is '''0x18'''.


* Used by System Manager
====Get MAC Address List (0x1117)====
* Communication with SYSCON 1


Request:
* Command buffer size is '''0xce'''.
* Returns several MAC addresses.


<pre>
offset 0xC - number of MAC addresses (2 bytes)
# write packet


# echo "0: 16 01 1620 0000 804d 00000001 0008 0008 20 29 0a 00 000001b6 0000fdcb" | xxd -c256 -r | \
offset 0xE - MAC addresses (6 * number of MAC addresses)
      dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer


# dump packet counter
====Unknown (0x1133)====


# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
* Used by VSH.
* Command buffer size is '''0x1A'''.
 
====Set WOL MAC Address Filter (0x1139)====


00000000  00 c0 00 c0                                      |.À.À|
* Used by LV2 internally.
00000004
* Command buffer is of size '''0x28'''.


# increment packet counter
====Unknown (0x113b)====


echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer
* Used by LV2 internally.
* Command buffer size is '''0x20'''.


# kick packet
====Set WOL Multicast Address Filter (0x113d)====


# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer
* Used by LV2 internally.
* Command buffer is of size '''0x2c'''.


# you should hear a beep
====Clear WOL Multicast Address Filter (0x113f)====


</pre>
* Used by LV2 internally.
* Command buffer is of size '''0x28'''.


Response:
====Unknown (0x1141)====


<pre>
* Used by LV2 internally.
# dump packet counter
* Command buffer is of size 0x12.


# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
====Clear WOL Address Filter (0x1143)====


00000000  00 c1 00 c1                                      |.Á.Á|
* Used by LV2 internally.
00000004
* Command buffer size is '''0x2c'''.


# dump response packet
====Unknown (0x114b)====


# dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C
* Used by LV2 internally.
00000000  16 01 16 20 00 00 80 4d  00 00 00 01 00 01 00 01  |... ...M........|
00000010  00 00 00 00 00 00 fe e3                          |......þã|
00000018


</pre>
====Set WOL Magic Packet Mode (0x1155)====


=Isolation=
* Used by LV2 internally.
Crossreference: [http://portal.gitbrew.org/wikibrew/PS3:HvReverseEngineering#Isolation gitbrew.org::Isolation] <br />
* Command buffer is of size '''0x10'''.
* Enables/Disables WOL magic packet.


==Running Isolated SPE Modules On OtherOS++ Linux==
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)


* spp_verifier is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux.
====Unknown (0x1157)====
* It decrypts default.spp profile
* Tested on 3.41 and 3.55.
* You can modify it easily to run other SPE modules.


<pre>
* Used by LV2 internally.
root@debian-hdd:/home/glevand/spp_verifier# cat spp_verifier_355.self > /proc/spp_verifier/spu
* Command buffer size is '''0x10'''.
root@debian-hdd:/home/glevand/spp_verifier# cat default_355.spp > /proc/spp_verifier/profile
root@debian-hdd:/home/glevand/spp_verifier# echo 1 > /proc/spp_verifier/run
root@debian-hdd:/home/glevand/spp_verifier# cat /proc/spp_verifier/debug


PPE id (0x0000000000000001) VAS id (0x0000000000000002)
====Set WOL Multicast Address Filter Mode (0x1159)====
lv1_construct_logical_spe (0x00000000)
 
SPE id (0x000000000000002b)
* Used by LV2 internally.
lv1_undocumented_function_209 (0x00000000)
* Command buffer size is '''0x10'''.
shadow execution status (0x0000000000000002)
* WOL function
lv1_get_spe_interrupt_status(1) (0x00000000)
interrupt status 1 (0x0000000000000000)
sleep
shadow execution status (0x0000000000000002)
lv1_get_spe_interrupt_status(1) (0x00000000)
interrupt status 1 (0x0000000000000001)
ea (0xc000000002920000) esid (0xc000000008000000) vsid (0x0000408f92c94500)
lv1_undocumented_function_62 (0x00000000)
lv1_clear_spe_interrupt_status(1) (0x00000000)
lv1_undocumented_function_168 (0x00000000)
sleep
shadow execution status (0x0000000000000007)
lv1_get_spe_interrupt_status(1) (0x00000000)
interrupt status 1 (0x0000000000000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
interrupt status 2 (0x0000000000000000)
out interrupt mbox (0x0000000000000002)
out interrupt mbox (0x0000000000000002)
lv1_undocumented_function_167 (0x00000000)
lv1_clear_spe_interrupt_status (0x00000000)
lv1_undocumented_function_200 (0x00000000)
sleep
shadow execution status (0x000000000000000b)
lv1_get_spe_interrupt_status(1) (0x00000000)
interrupt status 1 (0x0000000000000000)
shadow execution status (0x000000000000000b)
problem status (0x01000082)
lv1_destruct_logical_spe (0x00000000)


root@debian-hdd:/home/glevand/spp_verifier# hexdump -C /proc/spp_verifier/profile | less
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
...
...
00000200  00 02 00 05 00 00 20 a0  00 00 00 01 00 03 00 00  |......  ........|
00000210  00 00 00 00 00 00 00 01  00 00 00 0e 00 00 00 00  |................|
00000220  00 00 02 88 00 00 00 01  10 70 00 00 01 00 00 01  |.........p......|
00000230  00 00 00 00 00 00 00 00  53 43 45 5f 43 45 4c 4c  |........SCE_CELL|
00000240  4f 53 5f 50 4d 45 00 00  00 00 00 00 00 00 00 00  |OS_PME..........|
00000250  00 00 00 00 00 00 00 00  00 00 00 06 00 00 02 50  |...............P|
00000260  10 70 00 00 01 00 00 01  2f 66 6c 68 2f 6f 73 2f  |.p....../flh/os/|
00000270  74 68 69 73 5f 69 73 5f  64 75 6d 6d 79 00 00 00  |this_is_dummy...|
00000280  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
...
</pre>


==Using metldr On OtherOS++ Linux==
====Set Unicast Address Filter (0x115b)====


* spp_verifier_direct is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux by using metldr directly.
* Used by LV2 internally.
* It decrypts default.spp profile.
* Command buffer is of size '''0x6a'''.
* Tested on 3.41 and 3.55.
* This command should be used to set proper MAC address or else device won't be able to receive packets destined to its own MAC address
* You can modify it easily to run other SPE modules.


<pre>
offset 0xC - ??? (2 bytes)
root@debian-hdd:/home/glevand/spp_verifier_direct# insmod ./spp_verifier_direct.ko
root@debian-hdd:/home/glevand/spp_verifier_direct# cat metldr > /proc/spp_verifier_direct/metldr
root@debian-hdd:/home/glevand/spp_verifier_direct# cat isoldr_355 > /proc/spp_verifier_direct/isoldr
root@debian-hdd:/home/glevand/spp_verifier_direct# cat RL_FOR_PROGRAM_355.img > /proc/spp_verifier_direct/rvkprg
root@debian-hdd:/home/glevand/spp_verifier_direct# cat EID0 > /proc/spp_verifier_direct/eid0
root@debian-hdd:/home/glevand/spp_verifier_direct# cat spp_verifier_355.self > /proc/spp_verifier_direct/spu
root@debian-hdd:/home/glevand/spp_verifier_direct# cat default_355.spp > /proc/spp_verifier_direct/profile
root@debian-hdd:/home/glevand/spp_verifier_direct# echo 1 > /proc/spp_verifier_direct/run
root@debian-hdd:/home/glevand/spp_verifier_direct# cat /proc/spp_verifier_direct/debug
PPE id (0x0000000000000001) VAS id (0x0000000000000002)
lv1_construct_logical_spe (0x00000000)
SPE id (0x0000000000000033)
lv1_enable_logical_spe (0x00000000)
lv1_set_spe_interrupt_mask(0) (0x00000000)
lv1_set_spe_interrupt_mask(1) (0x00000000)
lv1_set_spe_interrupt_mask(2) (0x00000000)
lv1_set_spe_privilege_state_area_1_register (0x00000000)
ea (0xc000000002680000) esid (0xc000000008000000) vsid (0x0000408f92c94500)
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
out interrupt mbox (0x0000000000000001)
lv1_clear_spe_interrupt_status(2) (0x00000000)
transferring EID0, ldr args and revoke list to LS
waiting until MFC transfers are finished
MFC transfers done
out mbox (0x00000001)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
out interrupt mbox (0x0000000000000002)
lv1_clear_spe_interrupt_status(2) (0x00000000)
out mbox (0x00000002)
lv1_clear_spe_interrupt_status(2) (0x00000000)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
problem status (0x01000082)
lv1_destruct_logical_spe (0x00000000)


root@debian-hdd:/home/glevand/spp_verifier_direct# hexdump -C /proc/spp_verifier_direct/profile | less
offset 0xE - ??? (2 bytes)
...
...
00000200  00 02 00 05 00 00 20 a0  00 00 00 01 00 03 00 00  |......  ........|
00000210  00 00 00 00 00 00 00 01  00 00 00 0e 00 00 00 00  |................|
00000220  00 00 02 88 00 00 00 01  10 70 00 00 01 00 00 01  |.........p......|
00000230  00 00 00 00 00 00 00 00  53 43 45 5f 43 45 4c 4c  |........SCE_CELL|
00000240  4f 53 5f 50 4d 45 00 00  00 00 00 00 00 00 00 00  |OS_PME..........|
00000250  00 00 00 00 00 00 00 00  00 00 00 06 00 00 02 50  |...............P|
00000260  10 70 00 00 01 00 00 01  2f 66 6c 68 2f 6f 73 2f  |.p....../flh/os/|
00000270  74 68 69 73 5f 69 73 5f  64 75 6d 6d 79 00 00 00  |this_is_dummy...|
00000280  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
...
</pre>


= Gelic Device =
offset 0x10 - MAC address (6 bytes)
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#Gelic_Device gitbrew.org::Gelic Device] <br />


==sys.hw.config==
====Clear Unicast Address Filter (0x115d)====


* Value of the loader parameter "sys.hw.config" controls if Gelic WLAN is enabled or not.
* Used by LV2 internally.
* Value of the loader parameter "sys.hw.config" is stored in the repository node "sys.hw.config" too.
* Command buffer size is '''0x6a'''.
* If bit '''0x40000''' is set then LV1 allows using Gelic WLAN interface from LV2.
* Value on my PS3 slim '''0x4e00ffff0a03bc3c''' with Gelic WLAN interface disabled. As you can see, the Gelic WLAN interface is disabled and LV1 doesn't allow using of LV1 calls 196 and 195. It returns LV1_CONDITION_NOT_SATISFIED.
* GameOS checks bit '''0x40000''' of the repository node "sys.hw.config" during network initialization and if it's set then LV2 initializes Gelic WLAN interface.
* Check your "sys.hw.config" repository node and if bit '''0x40000''' is set then you are a lucky owner of a PS3 model with the old WLAN interface.
* '''On newer PS3 models, GameOS uses USB interface to communicate with WLAN.'''
* On PS3 models, where bit '''0x40000''' is NOT set in "sys.hw.config" repository node, the new USB interface is used.


''Note:[http://www.ps3devwiki.com/index.php?title=Wifi old vs. new]: Old == CECHA up to CECHK, New == CECHL and later''
====Get Unicast Address Filter (0x115f)====


== Control Interface ==
* Used by LV2 internally.
* Command buffer is of size '''0x6a'''.


HV calls 195 and 196 are used by GameOS to send commands to Gelic device directly.
====Set Multicast Address Filter (0x1161)====


=== lv1_undocumented_function_196 ===
* Used by LV2 internally.
* Command buffer size is '''0x2c'''.


==== Parameters ====
====Clear Multicast Address Filter (0x1163)====


r3 - LPAR address of data buffer
* Used by LV2 internally.
* Command buffer size is '''0x2c'''
* To clear all multicast addresses send command with all 0s.


r4 - size of data buffer
offset 0xC - multicast address filter (4 * 8 bytes)


r5 - must be 0
====Get Multicast Address Filter (0x1165)====


=== lv1_undocumented_function_195 ===
* Used by LV2 internally.
* Command buffer is of size '''0x2c'''.


==== Parameters ====
====Set WOL Address Filter (0x1167)====


r3 - command (16 bit value)
* Used by LV2 internally.
* Command buffer size is '''0x70'''.


r4 - command data size
====Set WOL Address Filter Mode (0x116d)====


r5 - must be 0
* Used by LV2 internally.
* Command buffer size is '''0x10'''.
* Enables/Disables WOL address matching


=== Data Buffer ===
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)


* Data Buffer passed to HV call 196 is divided into 2 parts.
====Set Unicast Address Filter Mode (0x116f)====
* The first 0x800 bytes are for sending and receiving command data
* The remaining 0x800 bytes are for event notification.


=== Command Data Buffer ===
* Used by LV2 internally.
* Command buffer size is '''0x10'''.


* Every command data sent to Gelic device contains header of size '''0xC'''
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
* After the header follows the command data
* After the Gelic device processed the command, it notifies LV2 kernel about command completion by sending an interrupt


==== Header ====
====Get Device Status (0xfffb)====


* Size is '''0xc'''.
* Used by VSH.
* Byte order is little-endian.
* Not a Gelic device command, handled by LV2 kernel.
* Header data in a request command buffer is always all 0s.
* Returned data size in command buffer is '''0x10'''.


0x0 - command = request command + 1 (2 bytes)
====Unknown (0xfffc)====


0x4 - result, 0x1 - success ??? 0x2 - buffer too small ??? (2 bytes)
* Used by VSH.
* Not a Gelic device command, handled by LV2 kernel.
* LV2 uses LV1 call '''lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x4, 0x0, 0x0)'''


0x6 - body size (2 bytes)
====Get Channel Information (0xfffd)====


=== Event Data Buffer ===
* Used by VSH.
* Not a Gelic device command, handled by LV2 kernel.
* LV2 uses LV1 call '''lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x0, 0x0, 0x0)'''
* Returns supported WLAN channels


* The Gelic device notifies LV2 kernel by sending an interrupt when new events are available
====Set Response Timeout (0xfffe)====
* Event Data Buffer has 8 bytes header
* The remaining bytes are divided into event slots
* Each event slot is of size 64 bytes
* Events are in little-endian format


==== Header ====
* Used by VSH.
* Not a Gelic device command, handled by LV2 kernel.
* Sets timeout value which is used to wait for a response from Gelic device.
* Typical value used by VSH is '''0x989680'''.
* Command buffer size is '''0x14'''.


offset 0x0 - GET index (4 bytes)
====Unknown (0xffff)====


offset 0x4 - PUT index (4 bytes)
* Used by VSH.
* Not a Gelic device command, handled by LV2 kernel.
* Returns 0x10 bytes in command buffer.
* Returns gelic device state ???


* GET index is updated by Gelic driver. The Gelic driver reads events beginning with the event slot at index GET.
=== Events ===
* PUT index is the index of event entry where next Gelic event will be stored by the Gelic device.
* If GET index is equal to PUT index then there are no Gelic events.


=== GameOS ===
<pre>
struct ps3_eurus_event_hdr {
__le32 type;
__le32 id;
__le32 timestamp;
__le32 payload_length;
__le32 unknown;
} __packed;


* LV2 syscall 726 sends Gelic device command and blocks until a response from the Gelic device arrives
struct ps3_eurus_event {
* LV2 kernel uses this LV1 interface to send commands to Gelic device internally too, probably for wireless controllers and Wake On WLAN.
struct ps3_eurus_event_hdr hdr;
* The system call 726 is used heavily by VSH.
u8 payload[44];
} __packed;
</pre>


==== Parameters ====
====Event Type 0x00000040====


r3 - command (16 bits)
{| class="wikitable"
|-
! Id !! Description
|-
| 0x00000001 || Deauthenticated
|}


r4 - effective address of command data buffer
====Event Type 0x00000080====


r5 - size of command data buffer
{| class="wikitable"
|-
! Id !! Description
|-
| 0x00000001 || Beacon Lost
|-
| 0x00000002 || Connected
|-
| 0x00000004 || Scan Completed
|-
| 0x00000020 || WPA Connected
|-
| 0x00000040 || WPA Error (MIC Error)
|}


=== Commands ===
====Event Type 0x80000000====


====Unknown (0x1)====
{| class="wikitable"
|-
! Id !! Description
|-
| 0x00000001 || Device Ready
|}


* Used by VSH.
== Enabling WLAN Gelic On FAT ==
* Command buffer size is '''0x10'''.
* Used in AP mode.
* Enables AP mode ???


====Get AP SSID (0x3)====
Linux kernel doesn't use Gelic Device Control Interface like GameOS does it.
To get WLAN working on Linux booted with GameOS rights, we have to disable
Gelic Device Control Interface first because it's enabled for GameOS by default.


* Command buffer is of size '''0x30'''.
The value of repository node "ios.net.eurus.lpar" controls access to Gelic Device Control Interface.
* Returns SSID in AP mode.
It's a bitmap. The position of a bit corresponds to LPAR id. During GameOS booting, HV process 9 (System Manager) sets bit at postion 2 to 1 which means enable Gelic Device Control Interface for LPAR 2.


offset 0xC - SSID (32 bytes)
To disable Gelic Device Control Interface on Linux, first unload Gelic device driver, then set
value of repository node "ios.net.eurus.lpar" to 0 and load Gelic device driver again. After that WLAN should work again but only on FATs.


====Set AP SSID (0x5)====
For PS3 Slim we need a new Linux Gelic device driver which uses Gelic Device Control Interface directly.


* Used by VSH.
* Command buffer is of size '''0x30'''.
* Sets SSID in AP mode.


offset 0xC - SSID (32 bytes)
==USB WLAN Interface (Codename Jupiter 2)==


====Get Channel (0xf)====
* On new PS3 models, WLAN interface is USB.
* '''Good news is that  the same commands are used as with LV1 calls 196 and 195'''.
* There are 2 wireless devices: Station and AP.
* I got WLAN scan working.


* Used by VSH.
===Endpoints===
* Command buffer is of size '''0x31'''.
* Data is returned from the device.
* Returns list of channels and active channel.


offset 0x2F - active channel (2 bytes)
* LV2 uses 3 USB endpoints of interface 3,4 and 5 to communicate with WLAN.
 
* Endpoints EP5 IN/OUT, EP6 IN/OUT and EP7 IN/OUT.
====Set Channel (0x11)====
* '''WLAN commands''' are sent to endpoint '''EP5 OUT''' with '''interrupt transfers'''.
 
* '''WLAN events''' and '''WLAN command responses''' are received on endpoint '''EP5 IN''' with '''interrupt transfers'''.
* Used by VSH.
* LV2 opens a USB communication pipe to endpoint EP5 IN and EP5 OUT.
* Command buffer size is '''0xd'''
* In my LV2 3.55 dump, pipe to EP5 IN has id '''0x2''' and pipe to EP5 OUT has id '''0x3'''. Array of all opened USB pipes is at address '''0x80000000004bd000''' in my LV2 3.55 dump.
* Valid channels: '''0 - 13'''. '''0''' means that the channel is selected '''automatically'''.
* EP5 is used to send commands to Jupiter and receive events from it.
* EP6 is used to send/receive data packets to/from the 1st WLAN device.
* EP7 is used to send/receive data packets to/from the 2nd WLAN device.
* '''lsusb is buggy on big-endian arch and shows some fields with bytes swapped !!!'''


offset 0xC - channel (1 byte)
====Unknown (0x27)====
* Command buffer size is '''0xF'''.
====Set Antenna (0x29)====
* Command buffer size is '''0xe'''
offset 0xC - ??? (1 byte)
offset 0xD - ??? (1 byte)
====Set AP WEP Configuration (0x5b)====
* Used by VSH.
* Command buffer is of size '''0x56'''.
* Sets WEP security type and WEP key.
* Security types: 0 - none, 1 - wep64, 2 - wep128
offset 0xE - security mode: 0 - none, 1 - wep64, 2 - wep128 (1 byte)
offset 0x10 - WEP key (64 bytes)
====Unknown (0x61)====
* Used by VSH.
* Command buffer size is '''0xd'''
====Unknown (0x65)====
* Used by VSH.
* Command  uffer size is '''0xd'''.
* Used in AP mode.
====Get Eurus Firmware Version (0x99)====
* Used by VSH.
Here is the response on my PS3 Slim:
<pre>
<pre>
00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2|
Bus 002 Device 002: ID 054c:036f Sony Corp.
00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19|
Device Descriptor:
00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).|
  bLength                18
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00       |..............  |
  bDescriptorType        1
</pre>
  bcdUSB              2.00
 
  bDeviceClass          224 Wireless
====Get AP Operating Mode (0xb7)====
  bDeviceSubClass        1 Radio Frequency
 
  bDeviceProtocol        1 Bluetooth
* Used by VSH.
  bMaxPacketSize0        64
* Command buffer size is '''0x10'''
  idVendor          0x054c Sony Corp.
* Returns AP operating mode (mixed, 11b or 11g).
  idProduct          0x036f
 
  bcdDevice          20.12
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)
  iManufacturer          1
 
  iProduct                2
====Set AP Operating Mode (0xb9)====
  iSerial                0
 
  bNumConfigurations      1
* Used by VSH.
    Interface Descriptor:
* Command buffer size is '''0x10'''
       bLength                9
* Sets AP operating mode (mixed, 11b or 11g).
      bDescriptorType        4
 
      bInterfaceNumber        3
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)
      bAlternateSetting      0
 
      bNumEndpoints          2
====Unknown (0xc5)====
      bInterfaceClass      255 Vendor Specific Class
 
      bInterfaceSubClass      2
* Used by VSH.
      bInterfaceProtocol      1
* Command buffer size is '''0x10'''.
      iInterface              0
* Used in AP mode.
      Endpoint Descriptor:
 
        bLength                7
offset 0xC - ??? (4 bytes)
        bDescriptorType        5
 
        bEndpointAddress    0x85  EP 5 IN
====Set AP WPA AKM Suite (0xc9)====
        bmAttributes            3
 
          Transfer Type            Interrupt
* Used by VSH.
          Synch Type              None
* Command buffer size is '''0x11'''.
          Usage Type              Data
* Sets WPA AKM suite in AP mode.
        wMaxPacketSize    0x4000  1x 0 bytes
 
        bInterval              1
offset 0xC - AKM suite (4 bytes)
      Endpoint Descriptor:
 
        bLength                7
====Set AP WPA Group Cipher Suite (0xcf)====
        bDescriptorType        5
 
        bEndpointAddress    0x05  EP 5 OUT
* Used by VSH.
        bmAttributes            3
* Command buffer size is '''0x10'''
          Transfer Type            Interrupt
* Used in AP + WPA mode.
          Synch Type              None
 
          Usage Type              Data
offset 0xC - group cipher suite: group (4 bytes)
        wMaxPacketSize    0x4000  1x 0 bytes
 
        bInterval              1
====Set AP WPA PSK Binary (0xd3)====
    Interface Descriptor:
 
      bLength                9
* Used by VSH.
      bDescriptorType        4
* Command buffer size is '''0x4c'''
      bInterfaceNumber        4
* Sets WPA PSK binary
      bAlternateSetting      0
 
      bNumEndpoints          2
offset 0xC - PSK (64 bytes)
      bInterfaceClass      255 Vendor Specific Class
 
      bInterfaceSubClass      2
====Set AP WPA Reauthentication Timeout (0xd5)====
      bInterfaceProtocol      2
 
      iInterface              0
* Used by VSH.
      Endpoint Descriptor:
* Command buffer size is '''0x10'''
        bLength                7
* Sets WPA Reauth timeout value in AP WPA mode.
        bDescriptorType        5
* VSH uses 36000 as timeout.
        bEndpointAddress    0x86  EP 6 IN
 
        bmAttributes            2
offset 0xC - timeout value in seconds (2 bytes)
          Transfer Type            Bulk
 
          Synch Type              None
====Unknown (0x127)====
          Usage Type              Data
 
        wMaxPacketSize    0x0002  1x 2 bytes
* Used by VSH.
        bInterval              0
* Command buffer size is '''0x10'''.
      Endpoint Descriptor:
* Used in AP + WPA mode.
        bLength                7
 
        bDescriptorType        5
====Unknown (0x12b)====
        bEndpointAddress    0x06  EP 6 OUT
 
        bmAttributes            2
* Used by VSH.
          Transfer Type            Bulk
* Command buffer size is '''0x10'''.
          Synch Type              None
* Used in AP + WPA mode.
          Usage Type              Data
        wMaxPacketSize    0x0002  1x 2 bytes
        bInterval            255
    Interface Descriptor:
      bLength                9
      bDescriptorType        4
      bInterfaceNumber        5
      bAlternateSetting      0
      bNumEndpoints          2
      bInterfaceClass      255 Vendor Specific Class
      bInterfaceSubClass      2
      bInterfaceProtocol      3
      iInterface              0
      Endpoint Descriptor:
        bLength                7
        bDescriptorType        5
        bEndpointAddress    0x87  EP 7 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type              None
          Usage Type              Data
        wMaxPacketSize    0x0002  1x 2 bytes
        bInterval              0
      Endpoint Descriptor:
        bLength                7
        bDescriptorType        5
        bEndpointAddress    0x07  EP 7 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type              None
          Usage Type              Data
        wMaxPacketSize    0x0002  1x 2 bytes
        bInterval            255
</pre>


====Set AP WPA PSK Passphrase (0x17d)====
===Device Initialization===


* Used by VSH.
* LV2 does 2 control transfers to EP0 during WLAN initialization
* Command buffer size is '''0x2D'''
* First control transfer sends magic '''0x20''' data to device as '''CLEAR_FEATURE''' request.
* Second control transfer reads '''0x2''' bytes device status. On my PS3 slim, the status data is always '''0x2031''' if you send the right magic.
* Magic data sent in first control transfer is stored in LV2.
* '''If you send wrong magic, the first control transfer will fail !!!'''
* LV2 uses a state machine to initialize the Jupiter device. The state machine has 17 states.


offset 0xD - passphrase (32 bytes)
==== Magic Data in Control Transfer ====


====Set AP WPA Pairwise Cipher Suite (0x1bf)====
<pre>
unsigned char ps3_usb_wlan_magic_data[] = {
0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8,
0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d,
};
</pre>


* Used by VSH.
==== Initialization State Machine ====
* Command buffer size is '''0x11'''
* Used in AP + WPA mode.


offset 0xC - pairwise cipher suite (4 bytes)
* Implemented in LV2.


offset 0x10 - ??? (1 byte)
=====State 1=====


====Unknown (0x1d9)====
* Command '''0x114f''' is sent to WLAN device.


* Used by VSH.
=====State 2=====
* Command buffer size is '''0x10'''


====Unknown (0x1dd)====
* Command '''0x1171''' is sent to WLAN device.


* Used by VSH.
=====State 3=====
* Command buffer size is '''0xd'''


====Unknown (0x1ed)====
* LV2 waits for an event from WLAN device.


* Used by VSH.
=====State 4=====
* Command buffer is of size '''0x17'''.
* Rate control ???


====Get Eurus HW Revision (0x1fb)====
* Command '''0x116f''' is sent to WLAN device.


* Command buffer size is '''0x10'''.
=====State 5=====


====Associate (0x1001)====
* Command '''0x115b''' is sent to WLAN device.
* Command data sent to WLAN device contains MAC address.


* Used by VSH.
=====State 6=====
* Used by LV1 on FAT models.
* Command buffer size is '''0xd'''
* Data passed to Gelic device is all 0s


====Get Common Configuration (0x1003)====
* Command '''0x1161''' is sent to WLAN device.
* Sets multicast address filter.


* Used by VSH.
=====State 7=====
* Used by LV1 on FAT models.
* Command buffer size is '''0x18'''
* Data passed to Gelic device is all 0s


====Set Common Configuration (0x1005)====
* Command '''0x110d''' is sent to WLAN device.


* Used by VSH.
=====State 8=====
* Used by LV1 on FAT models.
* Command buffer size is '''0x18'''
* Hmm, VSH always removes QOS bit from capability, that means Jupiter doesn't support QOS ???


offset 0xC - BSS type: 0 - infrastructure, 1 - ???, 2 - adhoc (1 byte)
* Command '''0x1031''' is sent to WLAN device.


offset 0xD - authentication mode: 0 - open, 1 - shared key
=====State 9=====


offset 0xE - opmode: 0 - 11bg, 1 - 11b, 2 - 11g (1 byte)
* Command '''0x1041''' is sent to WLAN device.
* Command data sent to WLAN device contains MAC address.


offset 0xF - ??? (1 byte)
=====State 10=====


offset 0x10 - BSSID (6 bytes)
* Command '''0x29''' is sent to WLAN device.
* Sets antenna.


offset 0x16 - capability (2 bytes)
=====State 11=====


====Get WEP Configuration (0x1013)====
* Command '''0x110b''' is sent to WLAN device.


* Used by VSH.
=====State 12=====
* Used by LV1 on FAT models.
* Command buffer size is '''0x50'''
* Data passed to Gelic device is all 0s


====Set WEP Configuration (0x1015)====
* Command '''0x1109''' is sent to WLAN device.


* Used by VSH.
=====State 13=====
* Used by LV1 on FAT models.
* Command buffer size is '''0x50'''


====Get WPA Configuration (0x1017)====
* Command '''0x207''' is sent to WLAN device.


* Used by VSH.
=====State 14=====
* Used by LV1 on FAT models.
* Command buffer size is '''0x5b'''
* Data passed to Gelic device is all 0s


====Set WPA Configuration (0x1019)====
* Command '''0x203''' is sent to WLAN device.


* Used by VSH.
=====State 15=====
* Used by LV1 on FAT models.
* Command buffer size is '''0x5b'''


offset 0xE - security type: 0 - WPA, 1 - RSNA (1 byte)
* Command '''0x105f''' is sent to WLAN device.
* Command data sent to WLAN device contains MAC address, channel info and region code.


offset 0xF - psk type: 0 - hex, 1 - bin (1 byte)
=====State 16=====


offset 0x10 - psk key: hex or bin (64 bytes)
* LV2 waits for an event from WLAN device.


offset 0x50 - group cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
=====State 17=====


offset 0x54 - pairwise cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
* LV2 accepts commands sent by LV2 syscall 726.


offset 0x58 - AKM suite: 0x0050f202 - WPA PSK, 0x000fac02 - RSNA PSK (4 bytes)
===Test Program===


'''See IEEE 802.11 specification for more details about cipher/AKM suites
* Here is a small program which executes a WLAN scan.
'''
* I used libusb.


802.11 spec: [http://standards.ieee.org/getieee802/download/802.11-2007.pdf]
====Source Code====
<pre>


====Unknown (0x1025)====
/*
* PS3 USB WLAN
*
* Copyright (C) 2011 glevand ([email protected])
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/


* Used by VSH.
#include <stdio.h>
* Command buffer size is '''0x10'''.
#include <stdlib.h>
* Sets preamble type, something else ???
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>


offset 0xC - preamble mode: 0 - short, 1 - long (1 byte)
#include <libusb-1.0/libusb.h>


====Unknown (0x1031)====
#define USB_VENDOR_ID 0x054c /* $ONY */
#define USB_PRODUCT_ID 0x036f
#define USB_IFACE_NUMBER 3


* Used by VSH.
#define USB_INTR_TRANSFER_EP5_IN_BUF_SIZE 0x800
* Command buffer size is '''0xe'''
#define USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE 0x800


====Get Scan Results (0x1033)====
struct wlan_cmd_pkt_hdr {
uint8_t unknown1;
uint8_t unknown2;
uint8_t unknown3;
uint8_t unknown4;
uint16_t unknown5;
uint8_t res1[2];
uint16_t tag;
uint8_t res2[14];
} __attribute__ ((packed));


* Used by VSH.
struct wlan_cmd_hdr {
* Used by LV1 on FAT models.
uint16_t command;
* Command buffer size is '''0x5b0'''
uint16_t tag;
* Data passed to Gelic device is all 0s
uint16_t status;
uint16_t payload_size;
uint8_t res[4];
} __attribute__ ((packed));


=====Scan Results=====
struct wlan_event_pkt_hdr {
uint8_t unknown1;
uint8_t unknown2;
uint8_t unknown3;
uint8_t event_count;
} __attribute__ ((packed));


offset 0x0 - number of scan entries (1 byte)
static libusb_context *usb_ctx;
static libusb_device_handle *usb_dev_handle;


offset 0x1 - array of scan entries
static struct libusb_transfer *usb_intr_transfer_ep5_in;
static unsigned char usb_intr_transfer_ep5_in_buf[USB_INTR_TRANSFER_EP5_IN_BUF_SIZE];


======Scan Entry======
static unsigned char usb_intr_transfer_ep5_out_buf[USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE];


offset 0x0 - size of this entry in bytes, this field is NOT included (2 bytes)
static pthread_mutex_t usb_wlan_cmd_mutex;
static pthread_cond_t usb_wlan_cmd_cond;
static int volatile usb_wlan_cmd_busy;
static uint16_t usb_wlan_cmd;
static void *usb_wlan_cmd_data;


offset 0x2 - BSSID (6 bytes)
static int volatile usb_wlan_cmd_thread_done;


offset 0x8 - RSSI (1 byte)
/*
* WLAN won't work without this magic !!!
*/
static unsigned char usb_magic_data[] = {
0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8,
0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d,
};


offset 0x9 - timestamp (8 bytes)
static unsigned char my_mac_addr[] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
};


offset 0x11 - beacon period (2 bytes)
/*
* hexdump
*/
static void hexdump(const unsigned char *data, unsigned int data_size)
{
int i, j;


offset 0x13 - capability (2 bytes)
for (i = 0; i < data_size; i += 16) {
fprintf(stdout, "%08x:", i);


offset 0x15 - information elements (see 802.11 specification)
for (j = 0; j < 16; j++) {
if (i + j < data_size) {
fprintf(stdout, " %02x", data[i + j]);
} else {
fprintf(stdout, "  ");
}
}


====Start Scan (0x1035)====
fprintf(stdout, " |");


* Used by VSH.
for (j = 0; j < 16; j++) {
* Used by LV1 on FAT models.
if (i + j < data_size) {
* Command buffer size depends on size of channel list and ESSID string length
if (isprint(data[i + j]))
* Data passed to Gelic device contains channel list and ESSID string
fprintf(stdout, "%c", data[i + j]);
* First '''0x16''' bytes in command data buffer are all 0s, then follows the channel list and after that ESSID
else
fprintf(stdout, ".");
} else {
fprintf(stdout, " ");
}
}


====Diassociate (0x1037)====
fprintf(stdout, "|\n");
}
}


* Used by VSH.
/*
* Used by LV1 on FAT models.
* usb_handle_wlan_event
* Command buffer size is '''0xd'''
*/
* Data passed to Gelic device is all 0s
static void usb_handle_wlan_event(struct wlan_event_pkt_hdr *wlan_event_pkt_hdr)
{
fprintf(stdout, "%s:%d: === got WLAN event ===\n", __func__, __LINE__);


====Get RSSI (0x103d)====
/*
fprintf(stdout, "%s:%d: event packet header:\n", __func__, __LINE__);
fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__,
wlan_event_pkt_hdr->unknown1);
fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__,
wlan_event_pkt_hdr->unknown2);
fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__,
wlan_event_pkt_hdr->unknown3);
*/
fprintf(stdout, "%s:%d: event_count (0x%02x)\n", __func__, __LINE__,
wlan_event_pkt_hdr->event_count);


* Used by VSH.
hexdump((unsigned char *) (wlan_event_pkt_hdr + 1), wlan_event_pkt_hdr->event_count * 64);
* Used by LV1 on FAT models.
}
* Command buffer size is '''0x17'''


offset 0x10 - MAC address of node (6 bytes)
/*
* usb_handle_wlan_cmd_response
*/
static void usb_handle_wlan_cmd_response(struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr)
{
struct wlan_cmd_hdr *wlan_cmd_hdr;
uint8_t *wlan_cmd_payload;


offset 0x16 - RSSI (1 byte)
fprintf(stdout, "%s:%d: === got WLAN command response ===\n", __func__, __LINE__);


====Get MAC Address (0x103f)====
wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);


* Command buffer size is '''0x13'''
/* convert all header fields to big-endian byte order !!! */


offset 0xD - MAC address (6 bytes)
wlan_cmd_pkt_hdr->unknown5 = le16toh(wlan_cmd_pkt_hdr->unknown5);
wlan_cmd_pkt_hdr->tag = le16toh(wlan_cmd_pkt_hdr->tag); /* returned from request */


====Set MAC Address (0x1041)====
wlan_cmd_hdr->command = le16toh(wlan_cmd_hdr->command); /* request command + 1 */
wlan_cmd_hdr->tag = le16toh(wlan_cmd_hdr->tag); /* returned from request */
wlan_cmd_hdr->status = le16toh(wlan_cmd_hdr->status); /* 1 - success
  2 - invalid parameters ???
  3 - invalid command ??? */
wlan_cmd_hdr->payload_size = le16toh(wlan_cmd_hdr->payload_size); /* length of data that follows the header */


* Used by VSH.
/*
* Used by LV1 too.
fprintf(stdout, "%s:%d: command packet header:\n", __func__, __LINE__);
* Command buffer size is '''0x12'''
fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown1);
fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown2);
fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown3);
fprintf(stdout, "%s:%d: unknown4 (0x%02x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown4);
fprintf(stdout, "%s:%d: unknown5 (0x%04x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown5);
fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->tag);
*/


====Unknown (0x104d)====
fprintf(stdout, "%s:%d: command header:\n", __func__, __LINE__);
fprintf(stdout, "%s:%d: command (0x%04x)\n", __func__, __LINE__,
wlan_cmd_hdr->command);


* Used by VSH.
if ((usb_wlan_cmd + 1) != wlan_cmd_hdr->command)
* Command buffer size is '''0xd'''.
fprintf(stdout, "%s:%d: ==> command does not match, got (0x%04x) expected (0x%04x)\n",
__func__, __LINE__, wlan_cmd_hdr->command, usb_wlan_cmd + 1);


offset 0xC - ??? (1 byte)
fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__,
wlan_cmd_hdr->tag);
fprintf(stdout, "%s:%d: status (0x%04x)\n", __func__, __LINE__,
wlan_cmd_hdr->status);


====Unknown (0x1051)====
if (wlan_cmd_hdr->status != 0x1)
fprintf(stdout, "%s:%d: ==> command status != 0x1\n", __func__, __LINE__);


* Used by VSH.
fprintf(stdout, "%s:%d: payload_size (0x%04x)\n", __func__, __LINE__,
* Command buffer size is '''0x5b3'''.
wlan_cmd_hdr->payload_size);


====Unknown (0x1053)====
fprintf(stdout, "%s:%d: command payload:\n", __func__, __LINE__);


* Used by VSH.
hexdump(wlan_cmd_payload, wlan_cmd_hdr->payload_size);
* Command buffer size is '''0x70'''.


offset 0xC - ??? (4 bytes)
memcpy(usb_wlan_cmd_data, wlan_cmd_payload, wlan_cmd_hdr->payload_size);


offset 0x10 - MAC address (6 bytes)
pthread_mutex_lock(&usb_wlan_cmd_mutex);


====Unknown (0x1059)====
usb_wlan_cmd_busy = 0;


* Used by VSH.
pthread_cond_signal(&usb_wlan_cmd_cond);
* Command buffer size is '''0x2a8'''.


====Unknown (0x105f)====
pthread_mutex_unlock(&usb_wlan_cmd_mutex);
}


* Used by LV2.
/*
* usb_intr_transfer_ep5_in_cb
*/
static void usb_intr_transfer_ep5_in_cb(struct libusb_transfer *transfer)
{
struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr;
int error;


====Get Zephyr HW Revision (0x1101)====
fprintf(stdout, "%s:%d: === got interrupt transfer ===\n", __func__, __LINE__);


* Used by VSH.
fprintf(stdout, "%s:%d: transfer status (%d) length (%d)\n",
* Not a Gelic device command, handled by LV2 kernel.
__func__, __LINE__, transfer->status, transfer->actual_length);
* LV2 uses LV1 call '''lv1_net_control(0x8000000000000002)'''
* Command buffer size is '''0x18'''.


====Get MAC Address List (0x1117)====
wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) transfer->buffer;


* Command buffer size is '''0xce'''.
if (wlan_cmd_pkt_hdr->unknown3 == 0x6)
* Returns several MAC addresses.
usb_handle_wlan_cmd_response(wlan_cmd_pkt_hdr);
else if (wlan_cmd_pkt_hdr->unknown3 == 0x8)
usb_handle_wlan_event((struct wlan_event_pkt_hdr *) transfer->buffer);
else
fprintf(stdout, "%s:%d: got unknown packet (0x%02x)\n",
__func__, __LINE__, wlan_cmd_pkt_hdr->unknown3);


offset 0xC - number of MAC addresses (2 bytes)
memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf));


offset 0xE - MAC addresses (6 * number of MAC addresses)
libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf),
usb_intr_transfer_ep5_in_cb, NULL, 0);


====Unknown (0x1133)====
error = libusb_submit_transfer(usb_intr_transfer_ep5_in);
if (error) {
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
__func__, __LINE__, error);
exit(1);
}
}


* Used by VSH.
/*
* Command buffer size is '''0x1A'''.
* usb_intr_transfer_ep5_out_cb
 
*/
====Set WOL MAC Address Filter (0x1139)====
static void usb_intr_transfer_ep5_out_cb(struct libusb_transfer *transfer)
{
/*
fprintf(stdout, "%s:%d: sent interrupt transfer\n", __func__, __LINE__);


* Used by LV2 internally.
fprintf(stdout, "%s:%d: transfer status (%d)\n", __func__, __LINE__, transfer->status);
* Command buffer is of size '''0x28'''.
*/


====Unknown (0x113b)====
libusb_free_transfer(transfer);
}


* Used by LV2 internally.
/*
* Command buffer size is '''0x20'''.
* usb_wlan_cmd_send
*/
static int usb_wlan_cmd_send(uint16_t command, const uint8_t *data, unsigned int data_size)
{
struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr;
struct wlan_cmd_hdr *wlan_cmd_hdr;
uint8_t *wlan_cmd_payload;
struct libusb_transfer *transfer;
int error;


====Set WOL Multicast Address Filter (0x113d)====
fprintf(stdout, "%s:%d: sending command (0x%04x) data size (0x%04x) command size (0x%04x)\n",
__func__, __LINE__, command, data_size, data_size + sizeof(struct wlan_cmd_hdr));


* Used by LV2 internally.
transfer = libusb_alloc_transfer(0);
* Command buffer is of size '''0x2c'''.
if (!transfer) {
fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__);
error = -1;
goto fail;
}


====Clear WOL Multicast Address Filter (0x113f)====
wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) usb_intr_transfer_ep5_out_buf;
wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);


* Used by LV2 internally.
wlan_cmd_pkt_hdr->unknown1 = 0x1;
* Command buffer is of size '''0x28'''.
wlan_cmd_pkt_hdr->unknown2 = 0x1;
wlan_cmd_pkt_hdr->unknown3 = 0x6;
wlan_cmd_pkt_hdr->unknown4 = 0x0;
wlan_cmd_pkt_hdr->unknown5 = 0x1;
wlan_cmd_pkt_hdr->tag = 0xf00d; /* returned in response */


====Unknown (0x1141)====
wlan_cmd_hdr->command = command;
wlan_cmd_hdr->tag = 0xcafe; /* returned in response */
wlan_cmd_hdr->status = 0xa;
wlan_cmd_hdr->payload_size = data_size;


* Used by LV2 internally.
memcpy(wlan_cmd_payload, data, data_size);
* Command buffer is of size 0x12.


====Clear WOL Address Filter (0x1143)====
usb_wlan_cmd = command;
usb_wlan_cmd_data = (void *) data;


* Used by LV2 internally.
libusb_fill_interrupt_transfer(transfer, usb_dev_handle, LIBUSB_ENDPOINT_OUT | 0x5,
* Command buffer size is '''0x2c'''.
usb_intr_transfer_ep5_out_buf,
sizeof(struct wlan_cmd_pkt_hdr) + sizeof(struct wlan_cmd_hdr) + wlan_cmd_hdr->payload_size,
usb_intr_transfer_ep5_out_cb, NULL, 0);


====Unknown (0x114b)====
/* convert all header fields to little-endian byte order !!! */


* Used by LV2 internally.
wlan_cmd_pkt_hdr->unknown5 = htole16(wlan_cmd_pkt_hdr->unknown5);
wlan_cmd_pkt_hdr->tag = htole16(wlan_cmd_pkt_hdr->tag);


====Set WOL Magic Packet Mode (0x1155)====
wlan_cmd_hdr->command = htole16(wlan_cmd_hdr->command);
wlan_cmd_hdr->tag = htole16(wlan_cmd_hdr->tag);
wlan_cmd_hdr->status = htole16(wlan_cmd_hdr->status);
wlan_cmd_hdr->payload_size = htole16(wlan_cmd_hdr->payload_size);


* Used by LV2 internally.
error = libusb_submit_transfer(transfer);
* Command buffer is of size '''0x10'''.
if (error) {
* Enables/Disables WOL magic packet.
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
__func__, __LINE__, error);
goto fail_free_transfer;
}


offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
pthread_mutex_lock(&usb_wlan_cmd_mutex);


====Unknown (0x1157)====
usb_wlan_cmd_busy = 1;


* Used by LV2 internally.
while (usb_wlan_cmd_busy)
* Command buffer size is '''0x10'''.
pthread_cond_wait(&usb_wlan_cmd_cond, &usb_wlan_cmd_mutex);


====Set WOL Multicast Address Filter Mode (0x1159)====
pthread_mutex_unlock(&usb_wlan_cmd_mutex);


* Used by LV2 internally.
return 0;
* Command buffer size is '''0x10'''.
* WOL function


offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
fail_free_transfer:


====Set Unicast Address Filter (0x115b)====
libusb_free_transfer(transfer);


* Used by LV2 internally.
fail:
* Command buffer is of size '''0x6a'''.
* This command should be used to set proper MAC address or else device won't be able to receive packets destined to its own MAC address


offset 0xC - ??? (2 bytes)
return error;
}


offset 0xE - ??? (2 bytes)
/*
* usb_wlan_cmd_start_scan
*/
static int usb_wlan_cmd_start_scan(void)
{
unsigned char data[256], *ptr;
unsigned int data_size;


offset 0x10 - MAC address (6 bytes)
memset(data, 0, sizeof(data));


====Clear Unicast Address Filter (0x115d)====
ptr = data;
*ptr++ = 0x0;
*ptr++ = 0x1;
*ptr++ = 0x64;
*ptr++ = 0x0;


* Used by LV2 internally.
ptr = data + 0xa;
* Command buffer size is '''0x6a'''.
*ptr++ = 0x3;


====Get Unicast Address Filter (0x115f)====
*ptr++ = 13; /* number of channels */
 
*ptr++ = 1; /* channels */
* Used by LV2 internally.
*ptr++ = 2;
* Command buffer is of size '''0x6a'''.
*ptr++ = 3;
*ptr++ = 4;
*ptr++ = 5;
*ptr++ = 6;
*ptr++ = 7;
*ptr++ = 8;
*ptr++ = 9;
*ptr++ = 10;
*ptr++ = 11;
*ptr++ = 12;
*ptr++ = 13;


====Set Multicast Address Filter (0x1161)====
data_size = ptr - data;


* Used by LV2 internally.
return usb_wlan_cmd_send(0x1035, data, data_size);
* Command buffer size is '''0x2c'''.
}


====Clear Multicast Address Filter (0x1163)====
/*
* usb_wlan_cmd_get_scan_results
*/
static int usb_wlan_cmd_get_scan_results(void)
{
unsigned char data[1456];
unsigned int data_size;


* Used by LV2 internally.
memset(data, 0, sizeof(data));
* Command buffer size is '''0x2c'''
* To clear all multicast addresses send command with all 0s.


offset 0xC - multicast address filter (4 * 8 bytes)
data_size = sizeof(data);


====Get Multicast Address Filter (0x1165)====
return usb_wlan_cmd_send(0x1033, data, data_size);
}


* Used by LV2 internally.
/*
* Command buffer is of size '''0x2c'''.
* usb_wlan_cmd_0x99
*/
static int usb_wlan_cmd_0x99(void)
{
unsigned char data[0x3e];
unsigned int data_size;


====Set WOL Address Filter (0x1167)====
memset(data, 0, sizeof(data));


* Used by LV2 internally.
data_size = sizeof(data);
* Command buffer size is '''0x70'''.


====Set WOL Address Filter Mode (0x116d)====
return usb_wlan_cmd_send(0x99, data, data_size);
}


* Used by LV2 internally.
/*
* Command buffer size is '''0x10'''.
* usb_wlan_init
* Enables/Disables WOL address matching
*/
static int usb_wlan_init(void)
{
unsigned char data[1456], *ptr;
unsigned int data_size;
int error;


offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
/* state 0x1 */


====Set Unicast Address Filter Mode (0x116f)====
memset(data, 0, sizeof(data));


* Used by LV2 internally.
data_size = 0x518;
* Command buffer size is '''0x10'''.


offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
error = usb_wlan_cmd_send(0x114f, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x114f (%d)\n",
__func__, __LINE__, error);
return error;
}


====Get Device Status (0xfffb)====
sleep(2);


* Used by VSH.
/* state 0x2 */
* Not a Gelic device command, handled by LV2 kernel.
* Returned data size in command buffer is '''0x10'''.


====Unknown (0xfffc)====
memset(data, 0, sizeof(data));


* Used by VSH.
data_size = 0;
* Not a Gelic device command, handled by LV2 kernel.
* LV2 uses LV1 call '''lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x4, 0x0, 0x0)'''


====Get Channel Information (0xfffd)====
error = usb_wlan_cmd_send(0x1171, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1171 (%d)\n",
__func__, __LINE__, error);
return error;
}


* Used by VSH.
sleep(2);
* Not a Gelic device command, handled by LV2 kernel.
* LV2 uses LV1 call '''lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x0, 0x0, 0x0)'''
* Returns supported WLAN channels


====Set Response Timeout (0xfffe)====
/* wait for a WLAN event */


* Used by VSH.
/* state 0x4 */
* Not a Gelic device command, handled by LV2 kernel.
* Sets timeout value which is used to wait for a response from Gelic device.
* Typical value used by VSH is '''0x989680'''.
* Command buffer size is '''0x14'''.


====Unknown (0xffff)====
memset(data, 0, sizeof(data));


* Used by VSH.
ptr = data;
* Not a Gelic device command, handled by LV2 kernel.
* Returns 0x10 bytes in command buffer.
* Returns gelic device state ???


=== Events ===
*ptr++ = 0x1;


<pre>
data_size = 0x4;
struct ps3_eurus_event_hdr {
__le32 type;
__le32 id;
__le32 timestamp;
__le32 payload_length;
__le32 unknown;
} __packed;


struct ps3_eurus_event {
error = usb_wlan_cmd_send(0x116f, data, data_size);
struct ps3_eurus_event_hdr hdr;
if (error) {
u8 payload[44];
fprintf(stderr, "%s:%d: could not send command 0x116f (%d)\n",
} __packed;
__func__, __LINE__, error);
</pre>
return error;
}


====Event Type 0x00000040====
sleep(2);


{| class="wikitable"
/* state 0x5 */
|-
! Id !! Description
|-
| 0x00000001 || Deauthenticated
|}


====Event Type 0x00000080====
memset(data, 0, sizeof(data));


{| class="wikitable"
ptr = data;
|-
! Id !! Description
|-
| 0x00000001 || Beacon Lost
|-
| 0x00000002 || Connected
|-
| 0x00000004 || Scan Completed
|-
| 0x00000020 || WPA Connected
|-
| 0x00000040 || WPA Error (MIC Error)
|}


====Event Type 0x80000000====
*ptr++ = 0x1;


{| class="wikitable"
ptr = data + 0x4;
|-
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));
! Id !! Description
|-
| 0x00000001 || Device Ready
|}


== Enabling WLAN Gelic On FAT ==
data_size = 0x5e;


Linux kernel doesn't use Gelic Device Control Interface like GameOS does it.
error = usb_wlan_cmd_send(0x115b, data, data_size);
To get WLAN working on Linux booted with GameOS rights, we have to disable
if (error) {
Gelic Device Control Interface first because it's enabled for GameOS by default.
fprintf(stderr, "%s:%d: could not send command 0x115b (%d)\n",
__func__, __LINE__, error);
return error;
}


The value of repository node "ios.net.eurus.lpar" controls access to Gelic Device Control Interface.
sleep(2);
It's a bitmap. The position of a bit corresponds to LPAR id. During GameOS booting, HV process 9 (System Manager) sets bit at postion 2 to 1 which means enable Gelic Device Control Interface for LPAR 2.


To disable Gelic Device Control Interface on Linux, first unload Gelic device driver, then set
/* state 0x6 */
value of repository node "ios.net.eurus.lpar" to 0 and load Gelic device driver again. After that WLAN should work again but only on FATs.


For PS3 Slim we need a new Linux Gelic device driver which uses Gelic Device Control Interface directly.
memset(data, 0, sizeof(data));


ptr = data + 0x1c;


==USB WLAN Interface (Codename Jupiter 2)==
*ptr++ = 0x20;
 
data_size = 0x20;
 
error = usb_wlan_cmd_send(0x1161, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1161 (%d)\n",
__func__, __LINE__, error);
return error;
}
 
sleep(2);
 
memset(data, 0, sizeof(data));
 
ptr = data + 0xc;
memset(ptr, 0xff, 7 * 4);
 
data_size = 0x80;
 
error = usb_wlan_cmd_send(0x110d, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x110d (%d)\n",
__func__, __LINE__, error);
return error;
}


* On new PS3 models, WLAN interface is USB.
sleep(2);
* '''Good news is that  the same commands are used as with LV1 calls 196 and 195'''.
* There are 2 wireless devices: Station and AP.
* I got WLAN scan working.


===Endpoints===
memset(data, 0, sizeof(data));


* LV2 uses 3 USB endpoints of interface 3,4 and 5 to communicate with WLAN.
data_size = 0x2;
* Endpoints EP5 IN/OUT, EP6 IN/OUT and EP7 IN/OUT.
* '''WLAN commands''' are sent to endpoint '''EP5 OUT''' with '''interrupt transfers'''.
* '''WLAN events''' and '''WLAN command responses''' are received on endpoint '''EP5 IN''' with '''interrupt transfers'''.
* LV2 opens a USB communication pipe to endpoint EP5 IN and EP5 OUT.
* In my LV2 3.55 dump, pipe to EP5 IN has id '''0x2''' and pipe to EP5 OUT has id '''0x3'''. Array of all opened USB pipes is at address '''0x80000000004bd000''' in my LV2 3.55 dump.
* EP5 is used to send commands to Jupiter and receive events from it.
* EP6 is used to send/receive data packets to/from the 1st WLAN device.
* EP7 is used to send/receive data packets to/from the 2nd WLAN device.
* '''lsusb is buggy on big-endian arch and shows some fields with bytes swapped !!!'''


<pre>
error = usb_wlan_cmd_send(0x1031, data, data_size);
Bus 002 Device 002: ID 054c:036f Sony Corp.
if (error) {
Device Descriptor:
fprintf(stderr, "%s:%d: could not send command 0x1031 (%d)\n",
  bLength                18
__func__, __LINE__, error);
  bDescriptorType        1
return error;
  bcdUSB              2.00
}
  bDeviceClass          224 Wireless
 
  bDeviceSubClass        1 Radio Frequency
sleep(2);
  bDeviceProtocol        1 Bluetooth
 
  bMaxPacketSize0        64
memset(data, 0, sizeof(data));
  idVendor          0x054c Sony Corp.
 
  idProduct          0x036f
ptr = data;
  bcdDevice          20.12
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));
  iManufacturer          1
 
  iProduct                2
data_size = 0x6;
  iSerial                0
 
  bNumConfigurations      1
error = usb_wlan_cmd_send(0x1041, data, data_size);
    Interface Descriptor:
if (error) {
      bLength                9
fprintf(stderr, "%s:%d: could not send command 0x1041 (%d)\n",
      bDescriptorType        4
__func__, __LINE__, error);
      bInterfaceNumber        3
return error;
      bAlternateSetting      0
}
      bNumEndpoints          2
 
      bInterfaceClass      255 Vendor Specific Class
sleep(2);
      bInterfaceSubClass      2  
 
      bInterfaceProtocol      1
/* state 0xa */
      iInterface              0  
 
      Endpoint Descriptor:
memset(data, 0, sizeof(data));
        bLength                7
 
        bDescriptorType        5
ptr = data;
        bEndpointAddress    0x85  EP 5 IN
 
        bmAttributes            3
*ptr++ = 0x2;
          Transfer Type            Interrupt
*ptr++ = 0x2;
          Synch Type              None
          Usage Type              Data
        wMaxPacketSize    0x4000  1x 0 bytes
        bInterval              1
      Endpoint Descriptor:
        bLength                7
        bDescriptorType        5
        bEndpointAddress    0x05  EP 5 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type              None
          Usage Type              Data
        wMaxPacketSize    0x4000  1x 0 bytes
        bInterval              1
    Interface Descriptor:
      bLength                9
      bDescriptorType        4
      bInterfaceNumber        4
      bAlternateSetting      0
      bNumEndpoints          2
      bInterfaceClass      255 Vendor Specific Class
      bInterfaceSubClass      2
      bInterfaceProtocol      2
      iInterface              0
      Endpoint Descriptor:
        bLength                7
        bDescriptorType        5
        bEndpointAddress    0x86  EP 6 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type              None
          Usage Type              Data
        wMaxPacketSize    0x0002  1x 2 bytes
        bInterval              0
      Endpoint Descriptor:
        bLength                7
        bDescriptorType        5
        bEndpointAddress    0x06  EP 6 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type              None
          Usage Type              Data
        wMaxPacketSize    0x0002  1x 2 bytes
        bInterval            255
    Interface Descriptor:
      bLength                9
      bDescriptorType        4
      bInterfaceNumber        5
      bAlternateSetting      0
      bNumEndpoints          2
      bInterfaceClass      255 Vendor Specific Class
      bInterfaceSubClass      2
      bInterfaceProtocol      3
      iInterface              0
      Endpoint Descriptor:
        bLength                7
        bDescriptorType        5
        bEndpointAddress    0x87  EP 7 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type              None
          Usage Type              Data
        wMaxPacketSize    0x0002  1x 2 bytes
        bInterval              0
      Endpoint Descriptor:
        bLength                7
        bDescriptorType        5
        bEndpointAddress    0x07  EP 7 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type              None
          Usage Type              Data
        wMaxPacketSize    0x0002  1x 2 bytes
        bInterval            255
</pre>


===Device Initialization===
data_size = 0x2;


* LV2 does 2 control transfers to EP0 during WLAN initialization
error = usb_wlan_cmd_send(0x29, data, data_size);
* First control transfer sends magic '''0x20''' data to device as '''CLEAR_FEATURE''' request.
if (error) {
* Second control transfer reads '''0x2''' bytes device status. On my PS3 slim, the status data is always '''0x2031''' if you send the right magic.
fprintf(stderr, "%s:%d: could not send command 0x29 (%d)\n",
* Magic data sent in first control transfer is stored in LV2.
__func__, __LINE__, error);
* '''If you send wrong magic, the first control transfer will fail !!!'''
return error;
* LV2 uses a state machine to initialize the Jupiter device. The state machine has 17 states.
}


==== Magic Data in Control Transfer ====
sleep(2);


<pre>
memset(data, 0, sizeof(data));
unsigned char ps3_usb_wlan_magic_data[] = {
0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8,
0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d,
};
</pre>


==== Initialization State Machine ====
ptr = data;


* Implemented in LV2.
*ptr++ = 0x1;


=====State 1=====
ptr = data + 8;


* Command '''0x114f''' is sent to WLAN device.
*ptr++ = 0x20;


=====State 2=====
data_size = 0xc;


* Command '''0x1171''' is sent to WLAN device.
error = usb_wlan_cmd_send(0x110b, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x110b (%d)\n",
__func__, __LINE__, error);
return error;
}


=====State 3=====
sleep(2);


* LV2 waits for an event from WLAN device.
memset(data, 0, sizeof(data));


=====State 4=====
ptr = data;


* Command '''0x116f''' is sent to WLAN device.
*ptr++ = 0x1;


=====State 5=====
ptr = data + 0x4;


* Command '''0x115b''' is sent to WLAN device.
*ptr++ = 0x15;
* Command data sent to WLAN device contains MAC address.
*ptr++ = 0x27;


=====State 6=====
*ptr++ = 0x12;
*ptr++ = 0x0;


* Command '''0x1161''' is sent to WLAN device.
*ptr++ = 0x6;
* Sets multicast address filter.
*ptr++ = 0x0;


=====State 7=====
ptr = data + 0xc;


* Command '''0x110d''' is sent to WLAN device.
*ptr++ = 0x9;
*ptr++ = 0x0;
*ptr++ = 0x1;


=====State 8=====
ptr = data + 0x10;


* Command '''0x1031''' is sent to WLAN device.
*ptr++ = 0xff;
 
*ptr++ = 0xff;
=====State 9=====
*ptr++ = 0xff;
*ptr++ = 0xff;
*ptr++ = 0xff;
*ptr++ = 0xff;


* Command '''0x1041''' is sent to WLAN device.
data_size = 0x16;
* Command data sent to WLAN device contains MAC address.


=====State 10=====
error = usb_wlan_cmd_send(0x1109, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1109 (%d)\n",
__func__, __LINE__, error);
return error;
}


* Command '''0x29''' is sent to WLAN device.
sleep(2);
* Sets antenna.


=====State 11=====
memset(data, 0, sizeof(data));


* Command '''0x110b''' is sent to WLAN device.
ptr = data;


=====State 12=====
*ptr++ = 0x1;


* Command '''0x1109''' is sent to WLAN device.
data_size = 0x4;


=====State 13=====
error = usb_wlan_cmd_send(0x207, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x207 (%d)\n",
__func__, __LINE__, error);
return error;
}


* Command '''0x207''' is sent to WLAN device.
sleep(2);


=====State 14=====
memset(data, 0, sizeof(data));


* Command '''0x203''' is sent to WLAN device.
ptr = data;


=====State 15=====
*ptr++ = 0x4;


* Command '''0x105f''' is sent to WLAN device.
data_size = 0x4;
* Command data sent to WLAN device contains MAC address, channel info and region code.


=====State 16=====
error = usb_wlan_cmd_send(0x203, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x203 (%d)\n",
__func__, __LINE__, error);
return error;
}


* LV2 waits for an event from WLAN device.
sleep(2);


=====State 17=====
/* state 0xf */


* LV2 accepts commands sent by LV2 syscall 726.
memset(data, 0, sizeof(data));


===Test Program===
ptr = data;


* Here is a small program which executes a WLAN scan.
*ptr++ = 0xff;
* I used libusb.
*ptr++ = 0x1f;


====Source Code====
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));
<pre>


/*
ptr = data + 0x8;
* PS3 USB WLAN
*
* Copyright (C) 2011 glevand ([email protected])
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/


#include <stdio.h>
*ptr++ = 0x2;
#include <stdlib.h>
*ptr++ = 0x2;
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>


#include <libusb-1.0/libusb.h>
data_size = 0xa;


#define USB_VENDOR_ID 0x054c /* $ONY */
error = usb_wlan_cmd_send(0x105f, data, data_size);
#define USB_PRODUCT_ID 0x036f
if (error) {
#define USB_IFACE_NUMBER 3
fprintf(stderr, "%s:%d: could not send command 0x105f (%d)\n",
__func__, __LINE__, error);
return error;
}


#define USB_INTR_TRANSFER_EP5_IN_BUF_SIZE 0x800
return 0;
#define USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE 0x800
}


struct wlan_cmd_pkt_hdr {
/*
uint8_t unknown1;
* usb_wlan_cmd_thread
uint8_t unknown2;
*/
uint8_t unknown3;
static void *usb_wlan_cmd_thread(void *arg)
uint8_t unknown4;
{
uint16_t unknown5;
int error;
uint8_t res1[2];
uint16_t tag;
uint8_t res2[14];
} __attribute__ ((packed));


struct wlan_cmd_hdr {
error = usb_wlan_init();
uint16_t command;
if (error) {
uint16_t tag;
fprintf(stderr, "%s:%d: could not initialize device (%d)\n",
uint16_t status;
__func__, __LINE__, error);
uint16_t payload_size;
goto done;
uint8_t res[4];
}
} __attribute__ ((packed));
 
sleep(5);


struct wlan_event_pkt_hdr {
error = usb_wlan_cmd_0x99();
uint8_t unknown1;
if (error) {
uint8_t unknown2;
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
uint8_t unknown3;
__func__, __LINE__, error);
uint8_t event_count;
goto done;
} __attribute__ ((packed));
}


static libusb_context *usb_ctx;
error = usb_wlan_cmd_start_scan();
static libusb_device_handle *usb_dev_handle;
if (error) {
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
__func__, __LINE__, error);
goto done;
}


static struct libusb_transfer *usb_intr_transfer_ep5_in;
sleep(10);
static unsigned char usb_intr_transfer_ep5_in_buf[USB_INTR_TRANSFER_EP5_IN_BUF_SIZE];


static unsigned char usb_intr_transfer_ep5_out_buf[USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE];
error = usb_wlan_cmd_get_scan_results();
if (error) {
fprintf(stderr, "%s:%d: could not get scan results (%d)\n",
__func__, __LINE__, error);
goto done;
}


static pthread_mutex_t usb_wlan_cmd_mutex;
sleep(10);
static pthread_cond_t usb_wlan_cmd_cond;
static int volatile usb_wlan_cmd_busy;
static uint16_t usb_wlan_cmd;
static void *usb_wlan_cmd_data;


static int volatile usb_wlan_cmd_thread_done;
done:


/*
usb_wlan_cmd_thread_done = 1;
* WLAN won't work without this magic !!!
*/
static unsigned char usb_magic_data[] = {
0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8,
0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d,
};


static unsigned char my_mac_addr[] = {
return NULL;
0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
}
};


/*
/*
  * hexdump
  * main
  */
  */
static void hexdump(const unsigned char *data, unsigned int data_size)
int main(int argc, char **argv)
{
{
int i, j;
unsigned char buf[256];
pthread_t tid;
struct timeval tv;
int error;
 
pthread_mutex_init(&usb_wlan_cmd_mutex, NULL);
pthread_cond_init(&usb_wlan_cmd_cond, NULL);
 
error = libusb_init(&usb_ctx);
if (error) {
fprintf(stderr, "%s:%d: libusb_init failed (%d)\n", __func__, __LINE__, error);
exit(1);
}


for (i = 0; i < data_size; i += 16) {
libusb_set_debug(usb_ctx, 5);
fprintf(stdout, "%08x:", i);


for (j = 0; j < 16; j++) {
usb_dev_handle = libusb_open_device_with_vid_pid(usb_ctx, USB_VENDOR_ID, USB_PRODUCT_ID);
if (i + j < data_size) {
if (!usb_dev_handle) {
fprintf(stdout, " %02x", data[i + j]);
fprintf(stderr, "%s:%d: could not open device\n", __func__, __LINE__);
} else {
exit(1);
fprintf(stdout, "  ");
}
}
}


fprintf(stdout, " |");
if(libusb_kernel_driver_active(usb_dev_handle, USB_IFACE_NUMBER)) {
fprintf(stdout, "%s:%d: kernel driver is attached\n", __func__, __LINE__);


for (j = 0; j < 16; j++) {
error = libusb_detach_kernel_driver(usb_dev_handle, USB_IFACE_NUMBER);
if (i + j < data_size) {
if (error) {
if (isprint(data[i + j]))
fprintf(stderr, "%s:%d: could not detach kernel driver (%d)\n",
fprintf(stdout, "%c", data[i + j]);
__func__, __LINE__, error);
else
exit(1);
fprintf(stdout, ".");
} else {
fprintf(stdout, " ");
}
}
}


fprintf(stdout, "|\n");
fprintf(stdout, "%s:%d: kernel driver dettached\n", __func__, __LINE__);
}
}
}


/*
error = libusb_claim_interface(usb_dev_handle, USB_IFACE_NUMBER);
* usb_handle_wlan_event
if (error) {
*/
fprintf(stderr, "%s:%d: could not claim interface (%d)\n",
static void usb_handle_wlan_event(struct wlan_event_pkt_hdr *wlan_event_pkt_hdr)
__func__, __LINE__, error);
{
exit(1);
fprintf(stdout, "%s:%d: === got WLAN event ===\n", __func__, __LINE__);
}


/*
error = libusb_control_transfer(usb_dev_handle, 0x40, 0x1, 0x9, 0x0,
fprintf(stdout, "%s:%d: event packet header:\n", __func__, __LINE__);
usb_magic_data, sizeof(usb_magic_data), 0);
fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__,
if (error < 0) {
wlan_event_pkt_hdr->unknown1);
fprintf(stderr, "%s:%d: could not do control transfer (%d)\n",
fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__,
__func__, __LINE__, error);
wlan_event_pkt_hdr->unknown2);
exit(1);
fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__,
}
wlan_event_pkt_hdr->unknown3);
*/
fprintf(stdout, "%s:%d: event_count (0x%02x)\n", __func__, __LINE__,
wlan_event_pkt_hdr->event_count);


hexdump((unsigned char *) (wlan_event_pkt_hdr + 1), wlan_event_pkt_hdr->event_count * 64);
fprintf(stdout, "%s:%d: number of bytes transferred (%d)\n", __func__, __LINE__, error);
}


/*
error = libusb_control_transfer(usb_dev_handle, 0xc0, 0x0, 0x2, 0x0, buf, 2, 0);
* usb_handle_wlan_cmd_response
if (error < 0) {
*/
fprintf(stderr, "%s:%d: could not do control transfer (%d)\n",
static void usb_handle_wlan_cmd_response(struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr)
__func__, __LINE__, error);
{
exit(1);
struct wlan_cmd_hdr *wlan_cmd_hdr;
}
uint8_t *wlan_cmd_payload;


fprintf(stdout, "%s:%d: === got WLAN command response ===\n", __func__, __LINE__);
fprintf(stdout, "%s:%d: number of bytes received (%d)\n", __func__, __LINE__, error);


wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
fprintf(stdout, "%s:%d: 0x%02x 0x%02x\n", __func__, __LINE__, buf[0], buf[1]);
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);


/* convert all header fields to big-endian byte order !!! */
usb_intr_transfer_ep5_in = libusb_alloc_transfer(0);
if (!usb_intr_transfer_ep5_in) {
fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__);
exit(1);
}


wlan_cmd_pkt_hdr->unknown5 = le16toh(wlan_cmd_pkt_hdr->unknown5);
memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf));
wlan_cmd_pkt_hdr->tag = le16toh(wlan_cmd_pkt_hdr->tag); /* returned from request */


wlan_cmd_hdr->command = le16toh(wlan_cmd_hdr->command); /* request command + 1 */
libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
wlan_cmd_hdr->tag = le16toh(wlan_cmd_hdr->tag); /* returned from request */
usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf),
wlan_cmd_hdr->status = le16toh(wlan_cmd_hdr->status); /* 1 - success
usb_intr_transfer_ep5_in_cb, NULL, 0);
  2 - invalid parameters ???
  3 - invalid command ??? */
wlan_cmd_hdr->payload_size = le16toh(wlan_cmd_hdr->payload_size); /* length of data that follows the header */


/*
error = libusb_submit_transfer(usb_intr_transfer_ep5_in);
fprintf(stdout, "%s:%d: command packet header:\n", __func__, __LINE__);
if (error) {
fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__,
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
wlan_cmd_pkt_hdr->unknown1);
__func__, __LINE__, error);
fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__,
exit(1);
wlan_cmd_pkt_hdr->unknown2);
}
fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown3);
fprintf(stdout, "%s:%d: unknown4 (0x%02x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown4);
fprintf(stdout, "%s:%d: unknown5 (0x%04x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown5);
fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->tag);
*/


fprintf(stdout, "%s:%d: command header:\n", __func__, __LINE__);
error = pthread_create(&tid, NULL, usb_wlan_cmd_thread, NULL);
fprintf(stdout, "%s:%d: command (0x%04x)\n", __func__, __LINE__,
if (error) {
wlan_cmd_hdr->command);
fprintf(stderr, "%s:%d: could not create WLAN command thread (%d)\n",
__func__, __LINE__, error);
exit(1);
}


if ((usb_wlan_cmd + 1) != wlan_cmd_hdr->command)
while (!usb_wlan_cmd_thread_done) {
fprintf(stdout, "%s:%d: ==> command does not match, got (0x%04x) expected (0x%04x)\n",
tv.tv_sec = 1;
__func__, __LINE__, wlan_cmd_hdr->command, usb_wlan_cmd + 1);
tv.tv_usec = 0;


fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__,
error = libusb_handle_events_timeout(usb_ctx, &tv);
wlan_cmd_hdr->tag);
if (error) {
fprintf(stdout, "%s:%d: status (0x%04x)\n", __func__, __LINE__,
fprintf(stderr, "%s:%d: could not handle events (%d)\n",
wlan_cmd_hdr->status);
__func__, __LINE__, error);
exit(1);
}
}


if (wlan_cmd_hdr->status != 0x1)
libusb_free_transfer(usb_intr_transfer_ep5_in);
fprintf(stdout, "%s:%d: ==> command status != 0x1\n", __func__, __LINE__);


fprintf(stdout, "%s:%d: payload_size (0x%04x)\n", __func__, __LINE__,
error = libusb_release_interface(usb_dev_handle, USB_IFACE_NUMBER);
wlan_cmd_hdr->payload_size);
if (error)
fprintf(stderr, "%s:%d: could not release interface (%d)\n",
__func__, __LINE__, error);


fprintf(stdout, "%s:%d: command payload:\n", __func__, __LINE__);
libusb_close(usb_dev_handle);


hexdump(wlan_cmd_payload, wlan_cmd_hdr->payload_size);
libusb_exit(usb_ctx);


memcpy(usb_wlan_cmd_data, wlan_cmd_payload, wlan_cmd_hdr->payload_size);
exit(0);
}
</pre>


pthread_mutex_lock(&usb_wlan_cmd_mutex);
====Output====


usb_wlan_cmd_busy = 0;
<pre>
 
glevand@debian-hdd:~/ps3_usb_wlan$ sudo ./ps3_usb_wlan
pthread_cond_signal(&usb_wlan_cmd_cond);
sudo: unable to resolve host debian-hdd
 
main:824: number of bytes transferred (32)
pthread_mutex_unlock(&usb_wlan_cmd_mutex);
main:833: number of bytes received (2)
}
main:835: 0x20 0x31
 
usb_wlan_cmd_send:288: sending command (0x114f) data size (0x0518) command size (0x0524)
/*
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* usb_intr_transfer_ep5_in_cb
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
*/
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
static void usb_intr_transfer_ep5_in_cb(struct libusb_transfer *transfer)
usb_handle_wlan_cmd_response:191: command header:
{
usb_handle_wlan_cmd_response:192: command (0x1150)
struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr;
usb_handle_wlan_cmd_response:199: tag (0xcafe)
int error;
usb_handle_wlan_cmd_response:201: status (0x0006)
 
usb_handle_wlan_cmd_response:205: ==> command status != 0x1
fprintf(stdout, "%s:%d: === got interrupt transfer ===\n", __func__, __LINE__);
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
 
usb_handle_wlan_cmd_response:210: command payload:
fprintf(stdout, "%s:%d: transfer status (%d) length (%d)\n",
usb_wlan_cmd_send:288: sending command (0x1171) data size (0x0000) command size (0x000c)
__func__, __LINE__, transfer->status, transfer->actual_length);
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) transfer->buffer;
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
if (wlan_cmd_pkt_hdr->unknown3 == 0x6)
usb_handle_wlan_cmd_response:192: command (0x1172)
usb_handle_wlan_cmd_response(wlan_cmd_pkt_hdr);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
else if (wlan_cmd_pkt_hdr->unknown3 == 0x8)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_event((struct wlan_event_pkt_hdr *) transfer->buffer);
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
else
usb_handle_wlan_cmd_response:210: command payload:
fprintf(stdout, "%s:%d: got unknown packet (0x%02x)\n",
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
__func__, __LINE__, wlan_cmd_pkt_hdr->unknown3);
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
 
usb_handle_wlan_event:133: === got WLAN event ===
memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf));
usb_handle_wlan_event:144: event_count (0x01)
 
00000000: 00 04 00 00 10 00 00 00 3c 22 02 00 00 00 00 00 |........<"......|
libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................|
usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf),
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............|
usb_intr_transfer_ep5_in_cb, NULL, 0);
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
 
usb_wlan_cmd_send:288: sending command (0x116f) data size (0x0004) command size (0x0010)
error = libusb_submit_transfer(usb_intr_transfer_ep5_in);
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
if (error) {
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
__func__, __LINE__, error);
usb_handle_wlan_cmd_response:191: command header:
exit(1);
usb_handle_wlan_cmd_response:192: command (0x1170)
}
usb_handle_wlan_cmd_response:199: tag (0xcafe)
}
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
/*
usb_handle_wlan_cmd_response:210: command payload:
* usb_intr_transfer_ep5_out_cb
usb_wlan_cmd_send:288: sending command (0x115b) data size (0x005e) command size (0x006a)
*/
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
static void usb_intr_transfer_ep5_out_cb(struct libusb_transfer *transfer)
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
{
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
/*
usb_handle_wlan_cmd_response:191: command header:
fprintf(stdout, "%s:%d: sent interrupt transfer\n", __func__, __LINE__);
usb_handle_wlan_cmd_response:192: command (0x115c)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
fprintf(stdout, "%s:%d: transfer status (%d)\n", __func__, __LINE__, transfer->status);
usb_handle_wlan_cmd_response:201: status (0x0001)
*/
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
 
usb_handle_wlan_cmd_response:210: command payload:
libusb_free_transfer(transfer);
usb_wlan_cmd_send:288: sending command (0x1161) data size (0x0020) command size (0x002c)
}
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
/*
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
* usb_wlan_cmd_send
usb_handle_wlan_cmd_response:191: command header:
*/
usb_handle_wlan_cmd_response:192: command (0x1162)
static int usb_wlan_cmd_send(uint16_t command, const uint8_t *data, unsigned int data_size)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
{
usb_handle_wlan_cmd_response:201: status (0x0001)
struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr;
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
struct wlan_cmd_hdr *wlan_cmd_hdr;
usb_handle_wlan_cmd_response:210: command payload:
uint8_t *wlan_cmd_payload;
usb_wlan_cmd_send:288: sending command (0x110d) data size (0x0080) command size (0x008c)
struct libusb_transfer *transfer;
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
int error;
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
fprintf(stdout, "%s:%d: sending command (0x%04x) data size (0x%04x) command size (0x%04x)\n",
usb_handle_wlan_cmd_response:191: command header:
__func__, __LINE__, command, data_size, data_size + sizeof(struct wlan_cmd_hdr));
usb_handle_wlan_cmd_response:192: command (0x110e)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
transfer = libusb_alloc_transfer(0);
usb_handle_wlan_cmd_response:201: status (0x0001)
if (!transfer) {
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__);
usb_handle_wlan_cmd_response:210: command payload:
error = -1;
usb_wlan_cmd_send:288: sending command (0x1031) data size (0x0002) command size (0x000e)
goto fail;
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
}
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) usb_intr_transfer_ep5_out_buf;
usb_handle_wlan_cmd_response:191: command header:
wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
usb_handle_wlan_cmd_response:192: command (0x1032)
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
wlan_cmd_pkt_hdr->unknown1 = 0x1;
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
wlan_cmd_pkt_hdr->unknown2 = 0x1;
usb_handle_wlan_cmd_response:210: command payload:
wlan_cmd_pkt_hdr->unknown3 = 0x6;
00000000: 00 00                                          |..              |
wlan_cmd_pkt_hdr->unknown4 = 0x0;
usb_wlan_cmd_send:288: sending command (0x1041) data size (0x0006) command size (0x0012)
wlan_cmd_pkt_hdr->unknown5 = 0x1;
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
wlan_cmd_pkt_hdr->tag = 0xf00d; /* returned in response */
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (42)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
wlan_cmd_hdr->command = command;
usb_handle_wlan_cmd_response:191: command header:
wlan_cmd_hdr->tag = 0xcafe; /* returned in response */
usb_handle_wlan_cmd_response:192: command (0x1042)
wlan_cmd_hdr->status = 0xa;
usb_handle_wlan_cmd_response:199: tag (0xcafe)
wlan_cmd_hdr->payload_size = data_size;
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0006)
memcpy(wlan_cmd_payload, data, data_size);
usb_handle_wlan_cmd_response:210: command payload:
 
00000000: 00 11 22 33 44 55                              |.."3DU          |
usb_wlan_cmd = command;
usb_wlan_cmd_send:288: sending command (0x0029) data size (0x0002) command size (0x000e)
usb_wlan_cmd_data = (void *) data;
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38)
libusb_fill_interrupt_transfer(transfer, usb_dev_handle, LIBUSB_ENDPOINT_OUT | 0x5,
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_intr_transfer_ep5_out_buf,
usb_handle_wlan_cmd_response:191: command header:
sizeof(struct wlan_cmd_pkt_hdr) + sizeof(struct wlan_cmd_hdr) + wlan_cmd_hdr->payload_size,
usb_handle_wlan_cmd_response:192: command (0x002a)
usb_intr_transfer_ep5_out_cb, NULL, 0);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
/* convert all header fields to little-endian byte order !!! */
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
 
usb_handle_wlan_cmd_response:210: command payload:
wlan_cmd_pkt_hdr->unknown5 = htole16(wlan_cmd_pkt_hdr->unknown5);
00000000: 02 02                                          |..              |
wlan_cmd_pkt_hdr->tag = htole16(wlan_cmd_pkt_hdr->tag);
usb_wlan_cmd_send:288: sending command (0x110b) data size (0x000c) command size (0x0018)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
wlan_cmd_hdr->command = htole16(wlan_cmd_hdr->command);
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (48)
wlan_cmd_hdr->tag = htole16(wlan_cmd_hdr->tag);
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
wlan_cmd_hdr->status = htole16(wlan_cmd_hdr->status);
usb_handle_wlan_cmd_response:191: command header:
wlan_cmd_hdr->payload_size = htole16(wlan_cmd_hdr->payload_size);
usb_handle_wlan_cmd_response:192: command (0x110c)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
error = libusb_submit_transfer(transfer);
usb_handle_wlan_cmd_response:201: status (0x0001)
if (error) {
usb_handle_wlan_cmd_response:207: payload_size (0x000c)
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
usb_handle_wlan_cmd_response:210: command payload:
__func__, __LINE__, error);
00000000: 01 00 00 00 00 00 00 00 20 00 00 00            |........ ...    |
goto fail_free_transfer;
usb_wlan_cmd_send:288: sending command (0x1109) data size (0x0016) command size (0x0022)
}
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (58)
pthread_mutex_lock(&usb_wlan_cmd_mutex);
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
usb_wlan_cmd_busy = 1;
usb_handle_wlan_cmd_response:192: command (0x110a)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
while (usb_wlan_cmd_busy)
usb_handle_wlan_cmd_response:201: status (0x0001)
pthread_cond_wait(&usb_wlan_cmd_cond, &usb_wlan_cmd_mutex);
usb_handle_wlan_cmd_response:207: payload_size (0x0016)
 
usb_handle_wlan_cmd_response:210: command payload:
pthread_mutex_unlock(&usb_wlan_cmd_mutex);
00000000: 01 00 00 00 15 27 12 00 06 00 00 00 09 00 01 00 |.....'..........|
 
00000010: ff ff ff ff ff ff                              |......          |
return 0;
usb_wlan_cmd_send:288: sending command (0x0207) data size (0x0004) command size (0x0010)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
fail_free_transfer:
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
libusb_free_transfer(transfer);
usb_handle_wlan_cmd_response:191: command header:
 
usb_handle_wlan_cmd_response:192: command (0x0208)
fail:
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
return error;
usb_handle_wlan_cmd_response:207: payload_size (0x0004)
}
usb_handle_wlan_cmd_response:210: command payload:
 
00000000: 01 00 00 00                                    |....            |
/*
usb_wlan_cmd_send:288: sending command (0x0203) data size (0x0004) command size (0x0010)
* usb_wlan_cmd_start_scan
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
*/
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40)
static int usb_wlan_cmd_start_scan(void)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
{
usb_handle_wlan_cmd_response:191: command header:
unsigned char data[256], *ptr;
usb_handle_wlan_cmd_response:192: command (0x0204)
unsigned int data_size;
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
memset(data, 0, sizeof(data));
usb_handle_wlan_cmd_response:207: payload_size (0x0004)
 
usb_handle_wlan_cmd_response:210: command payload:
ptr = data;
00000000: 04 00 00 00                                    |....            |
*ptr++ = 0x0;
usb_wlan_cmd_send:288: sending command (0x105f) data size (0x000a) command size (0x0016)
*ptr++ = 0x1;
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
*ptr++ = 0x64;
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
*ptr++ = 0x0;
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
ptr = data + 0xa;
usb_handle_wlan_cmd_response:192: command (0x1060)
*ptr++ = 0x3;
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
*ptr++ = 13; /* number of channels */
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
*ptr++ = 1; /* channels */
usb_handle_wlan_cmd_response:210: command payload:
*ptr++ = 2;
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
*ptr++ = 3;
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
*ptr++ = 4;
usb_handle_wlan_event:133: === got WLAN event ===
*ptr++ = 5;
usb_handle_wlan_event:144: event_count (0x01)
*ptr++ = 6;
00000000: 80 00 00 00 00 10 00 00 9e 2b 02 00 04 00 00 00 |.........+......|
*ptr++ = 7;
00000010: fc 90 02 c0 01 00 00 00 00 00 00 00 00 00 00 00 |................|
*ptr++ = 8;
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............|
*ptr++ = 9;
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*ptr++ = 10;
usb_wlan_cmd_send:288: sending command (0x0099) data size (0x003e) command size (0x004a)
*ptr++ = 11;
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
*ptr++ = 12;
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (98)
*ptr++ = 13;
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x009a)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x003e)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2|
00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19|
00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).|
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00      |..............  |
usb_wlan_cmd_send:288: sending command (0x1035) data size (0x0019) command size (0x0025)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (61)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1036)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0019)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 00 01 64 00 00 00 00 00 00 00 03 0d 01 02 03 04 |..d.............|
00000010: 05 06 07 08 09 0a 0b 0c 0d                      |.........      |
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
usb_handle_wlan_event:133: === got WLAN event ===
usb_handle_wlan_event:144: event_count (0x01)
00000000: 80 00 00 00 04 00 00 00 96 2e 02 00 01 00 00 00 |................|
00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............|
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
usb_wlan_cmd_send:288: sending command (0x1033) data size (0x05b0) command size (0x05bc)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (1403)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1034)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0557)
usb_handle_wlan_cmd_response:210: command payload:
...
Here is scan output (removed by me)
...
</pre>


data_size = ptr - data;
===Associate with AP===


return usb_wlan_cmd_send(0x1035, data, data_size);
* I got association with AP working.
}
* If  WLAN device is connected to an AP then the green LED is on, when data is received then the LED blinks.
* '''Data reception works finally !!!'''


/*
====How to Associate with WPA AP====
* usb_wlan_cmd_get_scan_results
* Set common configuration (command 0x1005)
*/
* Set WPA configuration (command 0x1019)
static int usb_wlan_cmd_get_scan_results(void)
* Set rate configuration (command 0x1ed)
{
* Associate (command 0x1001)
unsigned char data[1456];
unsigned int data_size;


memset(data, 0, sizeof(data));
===Packet Reception===


data_size = sizeof(data);
* EP6 IN and EP7 IN endpoints are used for packet reception
* LV2 sends bulk transfers to both endpoints
* '''4''' bulk transfers are sent simultaneously for each enpoint
* Every bulk transfer is of size '''0x620'''
* '''Make sure you set multicast address filter properly or else you won't receive broadcast packets !!!'''
* Bulk transfers returned by the host controller which do not contain any data have size of '''0x10''' bytes else transfers contain valid Ethernet frame. All 802.11 related data is stripped by the WLAN Gelic device.
* '''Make sure you set right MAC address with command 0x115b else device won't be able to receive packets destined to its own MAC address !!!'''


return usb_wlan_cmd_send(0x1033, data, data_size);
====Test with libusb====
}


/*
<pre>
* usb_wlan_cmd_0x99
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
*/
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (98)
static int usb_wlan_cmd_0x99(void)
00000000: ff ff ff ff ff ff ?? ?? ?? ?? ?? ?? 08 00 45 00 |..............E.|
{
00000010: 00 54 00 00 40 00 40 01 b5 fe c0 a8 01 5b c0 a8 |.T..@.@......[..|
unsigned char data[0x3e];
00000020: 01 ff 08 00 9c 69 0d 45 00 e2 4e 5d 34 26 00 07 |.....i.E..N]4&..|
unsigned int data_size;
00000030: df e1 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 |................|
00000040: 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 |.......... !"#$%|
00000050: 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 |&'()*+,-./012345|
00000060: 36 37                                          |67              |
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16)
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 |................|
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16)
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 |................|
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16)
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 |................|
</pre>


memset(data, 0, sizeof(data));
====Multicast Address Filter====


data_size = sizeof(data);
* WLAN Gelic device supports hardware multicast address filtering
* Multicast address filtering is implemented with MAC address hashing and filter bitmap
* Filter bitmap is of size '''4 * 8''' bytes
* Multicast address filter is set with command '''0x1161'''


return usb_wlan_cmd_send(0x99, data, data_size);
=====MAC Address Hash Function=====
}


/*
* Used by LV2
* usb_wlan_init
 
*/
<pre>
static int usb_wlan_init(void)
unsigned char hash(unsigned char *data, unsigned int size)
{
{
unsigned char data[1456], *ptr;
        unsigned int hash;
unsigned int data_size;
        int i, j;
int error;


/* state 0x1 */
        /*XXX: reverse data bits */


memset(data, 0, sizeof(data));
        hash = 0xffffffff;


data_size = 0x518;
        for (i = 0; i < size; i++) {
                hash = (((unsigned int) data[i]) << 24) ^ hash;


error = usb_wlan_cmd_send(0x114f, data, data_size);
                for (j = 0; j < 8; j++) {
if (error) {
                        if (((int) hash) >= 0) {
fprintf(stderr, "%s:%d: could not send command 0x114f (%d)\n",
                                hash = hash << 1;
__func__, __LINE__, error);
                        } else {
return error;
                                hash = (hash << 1) ^ 0x04c10000;
}
                                hash = hash ^ 0x00001db7;
                        }
                }
        }


sleep(2);
        hash = ((hash >> 24) & 0xf8) | (hash & 0x7);


/* state 0x2 */
        return hash & 0xff;
}


memset(data, 0, sizeof(data));
h = hash(mac_addr, 6);
v = 1 << (h & 0x1f);   /* word value in filter */
p = h >> 5;            /* word position in filter */


data_size = 0;


error = usb_wlan_cmd_send(0x1171, data, data_size);
For broadcast address:
if (error) {
------------------------
fprintf(stderr, "%s:%d: could not send command 0x1171 (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
v = 0x20000000
p = 7


/* wait for a WLAN event */
That's why 0x20 is used with command 0x1161 !!! Without it the device won't deliver broadcast traffic.
Learned it the hard way, after 2 days of trying to get packet reception working :)
</pre>


/* state 0x4 */
===Packet Transmission===


memset(data, 0, sizeof(data));
* Tx packets are sent to EP6 OUT
* Tx packets are normal Ethernet frames, they don't contain any WLAN data or other headers


ptr = data;
===AP Mode===


*ptr++ = 0x1;
* I got AP mode working with security disabled for now


data_size = 0x4;
====AP Mode with Security Disabled====


error = usb_wlan_cmd_send(0x116f, data, data_size);
* Set AP SSID (command 0x5)
if (error) {
* Set channel (command 0x11)
fprintf(stderr, "%s:%d: could not send command 0x116f (%d)\n",
* Set AP opmode (command 0xb9)
__func__, __LINE__, error);
* Configure rate control (command 0x1ed)
return error;
* Set AP WEP Configuration (command 0x5b, all 0s)
}
* Command 0x61 (param 0x0)
* Command 0xc5 (param 0x0)
* Command 0x1 (param 0x1)
* Command 0x1dd (param 0x2)
* Now green LED should be on


sleep(2);
===ps3-jupiter Linux Drivers===


/* state 0x5 */
* ps3_jupiter.ko is the common part of STA and AP mode. It implements a command interface to WLAN Gelic device and disptaches events to STA and AP drivers.
* ps3_jupiter_sta.ko is a STA mode implementation.
* ps3_jupiter_ap.ko is a AP mode implementation.
* Simple scanning works already in STA mode (try it out with '''iwlist scan''')
* Packet reception works
* Packet transmission works
* '''WPA/WPA2''' fully working and usable with '''wpa_supplicant'''


memset(data, 0, sizeof(data));


ptr = data;
'''Finally, after several weeks of hard programming and reversing, the WLAN driver ps3_jupiter_sta achieved the milestone where i can use it with WPA2 :) I actually use it currently with WPA2 on my PS3 slim. It works damn !!! Try it out and report bugs and problems to me.'''


*ptr++ = 0x1;
====TODO====


ptr = data + 0x4;
* Implement association in STA mode (finished)
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));
* Implement packet reception and transmission in STA mode (finished)
* Implement WEP support
* Implement AP mode
* Find out if Jupiter supports Monitor mode and if yes how to enable it
* Implement EURUS driver for PHATs (has many advantages over the old OtherOS approach, e.g. AP mode)
* Port to FreeBSD


data_size = 0x5e;
==LV2 Network Stack==


error = usb_wlan_cmd_send(0x115b, data, data_size);
* LV2 uses BSD network stack, e.g. '''struct mbuf'''
if (error) {
* It's almost identical to FreeBSD network stack.
fprintf(stderr, "%s:%d: could not send command 0x115b (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
===Network Device===


/* state 0x6 */
====IOCTLs====


memset(data, 0, sizeof(data));
=====Set Multicast Address Filter (0x81012000)=====


ptr = data + 0x1c;
* Sets multicast address filter
* Uses LV1 calls '''lv1_net_remove_multicast_address''' and '''lv1_net_add_multicast_address''' for Ethernet Gelic device
* Uses Eurus commands '''0x1161''', '''0x1163''' and '''0x1165''' for WLAN Gelic device


*ptr++ = 0x20;
=====Unknown (0x8101200E)=====


data_size = 0x20;
* Uses LV1 call '''lv1_net_control(0x8000000000000001)'''


error = usb_wlan_cmd_send(0x1161, data, data_size);
=====Unknown (0x81040000)=====
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1161 (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
* Uses LV1 call '''lv1_net_control(0x8, [0x0, 0x1 or 0x2])''' for Ethernet Gelic device
* Uses Eurus commands '''0x116F''', '''0x115D''' and '''0x115B''' for WLAN Gelic device


memset(data, 0, sizeof(data));
=====Enable/Disable WOL Magic Packet (0x81080000)=====


ptr = data + 0xc;
* Enables/Disables WOL Magic Packet
memset(ptr, 0xff, 7 * 4);
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x1 /* GELIC_LV1_WOL_MAGIC_PACKET */)''' for Ethernet Gelic device
* Uses Eurus commands '''0x1139''' and '''0x1155''' for WLAN Gelic device


data_size = 0x80;
=====Unknown (0x81080001)=====


error = usb_wlan_cmd_send(0x110d, data, data_size);
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x2)''' for Ethernet Gelic device
if (error) {
* Uses Eurus commands '''0x113B''' and '''0x1157''' for WLAN Gelic device
fprintf(stderr, "%s:%d: could not send command 0x110d (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
=====Unknown (0x81080002)=====


memset(data, 0, sizeof(data));
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x3)''' for Ethernet Gelic device
* Uses Eurus commands '''0x113D''' and '''0x1159''' for WLAN Gelic device


data_size = 0x2;
=====Unknown (0x81080003)=====


error = usb_wlan_cmd_send(0x1031, data, data_size);
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x4)''' for Ethernet Gelic device
if (error) {
* Uses Eurus command '''0x1161''' for WLAN Gelic device
fprintf(stderr, "%s:%d: could not send command 0x1031 (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
=====Unknown (0x81080005)=====


memset(data, 0, sizeof(data));
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x6 /* GELIC_LV1_WOL_ADD_MATCH_ADDR */)''' for Ethernet Gelic device
* Uses Eurus commands '''0x116D''' and '''0x1167''' for WLAN Gelic device


ptr = data;
===Network Packet===
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));


data_size = 0x6;
* LV2 network packet is represented by '''struct mbuf'''


error = usb_wlan_cmd_send(0x1041, data, data_size);
=RSX=
if (error) {
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#RSX gitbrew.org::RSX] <br />
fprintf(stderr, "%s:%d: could not send command 0x1041 (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
==HV Calls==


/* state 0xa */
===lv1_gpu_memory_allocate===


memset(data, 0, sizeof(data));
* LV1 supports 16 memory handles simultaneously.
* LV1 uses a bitmap to manage GPU VRAM.
* The bitmap is located in LV1 memory, 4 double words.
* Each bit corresponds to 1MB VRAM, 256bit = 256MB VRAM.
* 2MB at the top of VRAM are preallocated as you can see below.


ptr = data;
<pre>
<memory handle> = 0x5a5a5a5a xor <memory handle index>
</pre>


*ptr++ = 0x2;
====Memory Context Object====
*ptr++ = 0x2;


data_size = 0x2;
offset 0x8 - memory handle (4 bytes)


error = usb_wlan_cmd_send(0x29, data, data_size);
offset 0x10 - VRAM LPAR start address (8 bytes)
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x29 (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
offset 0x18 - VRAM LPAR end address (8 bytes)


memset(data, 0, sizeof(data));
====Test====


ptr = data;
* The offset of bitmap could be different on your system because it's allocated dynamically.
* '''First 9MB of VRAM were allocated by ps3fb Linux driver.'''


*ptr++ = 0x1;
Before allocating VRAM:
<pre>
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C
00000000  00 00 00 00 00 00 01 ff  00 00 00 00 00 00 00 00  |.......ÿ........|
00000010  00 00 00 00 00 00 00 00  c0 00 00 00 00 00 00 00  |........À.......|
</pre>


ptr = data + 8;
After allocating 32 MB VRAM:
<pre>
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C
00000000  00 00 01 ff ff ff ff ff  00 00 00 00 00 00 00 00  |...ÿÿÿÿÿ........|
00000010  00 00 00 00 00 00 00 00  c0 00 00 00 00 00 00 00  |........À.......|
</pre>


*ptr++ = 0x20;
===lv1_gpu_context_allocate===


data_size = 0xc;
* Register %r4 is flags.
* '''Found the place in LV1 where LV1 sets IO page size for GART memory mapping. We could patch it and set to 4KB. That would make a lot of things easier for RSX developers on Linux.'''
* 1MB pages make RSX driver for Linux hard to implement because allocating 1Mb contiguous memory chunk on Linux is very very hard especially on a system with only 256MB and which was running for some time.


error = usb_wlan_cmd_send(0x110b, data, data_size);
* LV1 supports 16 contexts simultaneously.
if (error) {
* LV1 has an array of context pointers.
fprintf(stderr, "%s:%d: could not send command 0x110b (%d)\n",
* Each context has an index and a handle. The handle is derived from the index of the context.
__func__, __LINE__, error);
return error;
}


sleep(2);
<pre>
<context handle> = 0x55555555 xor <context index>
</pre>


memset(data, 0, sizeof(data));
* Thats why first created context will have handle 0x55555555.
 
====Context Object====


ptr = data;
offset 0x8 - handle (4 bytes)


*ptr++ = 0x1;
offset 0x48 - IO page size, valid range is 4kB, 64KB and 1MB (8 bytes)


ptr = data + 0x4;
====Flags====


*ptr++ = 0x15;
'''0x2 - tells LV1 to use 64KB pages for GART memory mapping else LV1 uses 1MB pages'''
*ptr++ = 0x27;


*ptr++ = 0x12;
===lv1_gpu_context_iomap===
*ptr++ = 0x0;


*ptr++ = 0x6;
* Internally uses lv1_put_iopte function
*ptr++ = 0x0;
* IO page size is the one set during lv1_gpu_context_allocate
* IO address space id is 0x0. IO id is 0x1.


ptr = data + 0xc;
===lv1_gpu_context_attribute===


*ptr++ = 0x9;
====Attribute 0x1====
*ptr++ = 0x0;
*ptr++ = 0x1;


ptr = data + 0x10;
=====FIFO Command Buffer Setup=====


*ptr++ = 0xff;
<pre>
*ptr++ = 0xff;
lv1_gpu_context_attribute(context handle, 0x1, PUT offset, GET offset, 0x0, 0x0)
*ptr++ = 0xff;
</pre>
*ptr++ = 0xff;
*ptr++ = 0xff;
*ptr++ = 0xff;


data_size = 0x16;
====Attribute 0x101====


error = usb_wlan_cmd_send(0x1109, data, data_size);
=====Set Flip Mode=====
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1109 (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
<pre>
lv1_gpu_attribute(0x2, 0x1 /* head */, 0x0, 0x0)
lv1_gpu_context_attribute(context handle, 0x101, 0x1 /* head */, sync mode, 0x0, 0x0)
</pre>


memset(data, 0, sizeof(data));
====Attribute 0x104====


ptr = data;
=====Set Display Buffer=====


*ptr++ = 0x1;
<pre>
lv1_gpu_context_attribute(context handle, 0x104, id, width << 32 | height, pitch << 32 | offset, 0x0)
</pre>


data_size = 0x4;
====Attribute 0x10a====


error = usb_wlan_cmd_send(0x207, data, data_size);
=====Get Flip Status=====
if (error) {
 
fprintf(stderr, "%s:%d: could not send command 0x207 (%d)\n",
* Reads a value at offset '''0x10C0 + 0x1 * 0x40''' in lpar_reports memory.
__func__, __LINE__, error);
return error;
}


sleep(2);
=====Reset Flip Status=====


memset(data, 0, sizeof(data));
<pre>
lv1_gpu_context_attribute(context handle, 0x10a, 0x1 /* id */, 0x7fffffff /* mask */, 0x0 /* value */, 0x0)
</pre>


ptr = data;
* The LV1 call '''lv1_gpu_context_attribute(0x10a)''' accesses LPAR memory returned in '''lpar_reports''' by LV1 call '''lv1_gpu_context_allocate'''.
* Offset into lpar_reports is '''0x10C0 + id * 0x40 = 0x10C0 + 0x1 * 0x40'''.
* Why not access lpar_reports memory directly and use LV1 call instead ???


*ptr++ = 0x4;
====Attribute 0x10b====


data_size = 0x4;
* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''


error = usb_wlan_cmd_send(0x203, data, data_size);
=====Set Cursor Position=====
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x203 (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
<pre>
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x3, x, y)
</pre>


/* state 0xf */
=====Set Cursor Image Offset=====


memset(data, 0, sizeof(data));
<pre>
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x2, offset, 0x0)
</pre>


ptr = data;
====Attribute 0x10c====


*ptr++ = 0xff;
* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''
*ptr++ = 0x1f;


memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));
=====Cursor Function 1=====


ptr = data + 0x8;
<pre>
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x1, 0x0, 0x0)
</pre>


*ptr++ = 0x2;
=====Cursor Function 2=====
*ptr++ = 0x2;


data_size = 0xa;
<pre>
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x2, 0x0, 0x0)
</pre>


error = usb_wlan_cmd_send(0x105f, data, data_size);
====Attribute 0x10d====
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x105f (%d)\n",
__func__, __LINE__, error);
return error;
}


return 0;
* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''
}


/*
=====Cursor Function 1=====
* usb_wlan_cmd_thread
*/
static void *usb_wlan_cmd_thread(void *arg)
{
int error;


error = usb_wlan_init();
<pre>
if (error) {
lv1_gpu_context_attribute(context handle, 0x10d, 0x1, 0x1, 0x0, 0x0)
fprintf(stderr, "%s:%d: could not initialize device (%d)\n",
</pre>
__func__, __LINE__, error);
goto done;
}


sleep(5);
====Attribute 0x300====


error = usb_wlan_cmd_0x99();
=====Set Tile=====
if (error) {
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
__func__, __LINE__, error);
goto done;
}


error = usb_wlan_cmd_start_scan();
=====Set Invalidate Tile=====
if (error) {
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
__func__, __LINE__, error);
goto done;
}


sleep(10);
=====Bind Tile=====


error = usb_wlan_cmd_get_scan_results();
=====Unbind Tile=====
if (error) {
fprintf(stderr, "%s:%d: could not get scan results (%d)\n",
__func__, __LINE__, error);
goto done;
}


sleep(10);
====Attribute 0x301====


done:
=====Set Zcull=====


usb_wlan_cmd_thread_done = 1;
=====Bind Zcull=====


return NULL;
=====Unbind Zcull=====
}


/*
====Attribute 0x601====
* main
*/
int main(int argc, char **argv)
{
unsigned char buf[256];
pthread_t tid;
struct timeval tv;
int error;


pthread_mutex_init(&usb_wlan_cmd_mutex, NULL);
* Copies data from GART memory to VRAM.
pthread_cond_init(&usb_wlan_cmd_cond, NULL);
* LV1 uses internally the FIFO command buffer passed by ps3fb driver with lv1_gpu_context_iomap.


error = libusb_init(&usb_ctx);
FIFO commands:
if (error) {
<pre>
fprintf(stderr, "%s:%d: libusb_init failed (%d)\n", __func__, __LINE__, error);
0x0004C184
exit(1);
0xFEED0001
}
 
0x0004C198
0x313371C3


libusb_set_debug(usb_ctx, 5);
0x00046300
0x0000000A


usb_dev_handle = libusb_open_device_with_vid_pid(usb_ctx, USB_VENDOR_ID, USB_PRODUCT_ID);
for ()
if (!usb_dev_handle) {
{
fprintf(stderr, "%s:%d: could not open device\n", __func__, __LINE__);
    for ()
exit(1);
    {
}
        0x0004630C
        <param>


if(libusb_kernel_driver_active(usb_dev_handle, USB_IFACE_NUMBER)) {
        0x00046304
fprintf(stdout, "%s:%d: kernel driver is attached\n", __func__, __LINE__);
        <param>


error = libusb_detach_kernel_driver(usb_dev_handle, USB_IFACE_NUMBER);
        0x0024C2FC
if (error) {
        0x00000001
fprintf(stderr, "%s:%d: could not detach kernel driver (%d)\n",
        0x00000003
__func__, __LINE__, error);
        0x00000003
exit(1);
        <param1>
}
        <param2>
        <param3>
        <param4>
        0x00010000
        0x00010000


fprintf(stdout, "%s:%d: kernel driver dettached\n", __func__, __LINE__);
        0x0001C400
}
        <param1>
        <param2>
        <param3>
        0x00000000
    }
}


error = libusb_claim_interface(usb_dev_handle, USB_IFACE_NUMBER);
0x00040110
if (error) {
0x00000000
fprintf(stderr, "%s:%d: could not claim interface (%d)\n",
</pre>
__func__, __LINE__, error);
exit(1);
}


error = libusb_control_transfer(usb_dev_handle, 0x40, 0x1, 0x9, 0x0,
==FIFO Command Buffer==
usb_magic_data, sizeof(usb_magic_data), 0);
 
if (error < 0) {
===FIFO Control Registers===
fprintf(stderr, "%s:%d: could not do control transfer (%d)\n",
__func__, __LINE__, error);
exit(1);
}


fprintf(stdout, "%s:%d: number of bytes transferred (%d)\n", __func__, __LINE__, error);
* LV1 call '''lv1_gpu_context_allocate''' returns LPAR address of FIFO control registers.
* You have to map it into Linux address space before you can access FIFO control registers.
* Value of PUT and GET registers are NOT expressed in Linux address space but in RSX address space. You have to convert it to RSX address space.
* GET register is read-only and is modified by RSX while it's processing FIFO commands.


error = libusb_control_transfer(usb_dev_handle, 0xc0, 0x0, 0x2, 0x0, buf, 2, 0);
===Kicking FIFO Command Buffer===
if (error < 0) {
fprintf(stderr, "%s:%d: could not do control transfer (%d)\n",
__func__, __LINE__, error);
exit(1);
}


fprintf(stdout, "%s:%d: number of bytes received (%d)\n", __func__, __LINE__, error);
* As long as values of GET and PUT FIFO control registers are equal, RSX doesn't process commands from the FIFO command buffer.
* When the value of PUT register is not equal to the value of GET register, RSX starts processing commands in the FIFO command buffer.
* To execute FIFO commands, place them in the FIFO command buffer and change the value of PUT register.


fprintf(stdout, "%s:%d: 0x%02x 0x%02x\n", __func__, __LINE__, buf[0], buf[1]);
===FIFO Setup Programs of emer_init.self===


usb_intr_transfer_ep5_in = libusb_alloc_transfer(0);
* [[PS3:HvReverseEngineering:emer_init.self:Program 1]]
if (!usb_intr_transfer_ep5_in) {
* [[PS3:HvReverseEngineering:emer_init.self:Program 2]]
fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__);
* [[PS3:HvReverseEngineering:emer_init.self:Program 3]]
exit(1);
}


memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf));
===FIFO Commands===


libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
[[PS3:HvReverseEngineering:RSXFIFOCommands]]
usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf),
usb_intr_transfer_ep5_in_cb, NULL, 0);


error = libusb_submit_transfer(usb_intr_transfer_ep5_in);
===Example How to Use FIFO Command Buffer===
if (error) {
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
__func__, __LINE__, error);
exit(1);
}


error = pthread_create(&tid, NULL, usb_wlan_cmd_thread, NULL);
Here is a small Linux kernel module which shows you how to use FIFO command buffer on Linux.
if (error) {
fprintf(stderr, "%s:%d: could not create WLAN command thread (%d)\n",
__func__, __LINE__, error);
exit(1);
}


while (!usb_wlan_cmd_thread_done) {
* RSX allows to create multiple contexts.
tv.tv_sec = 1;
* This kernel module should run without problems with '''ps3fb''' driver already running.
tv.tv_usec = 0;
* Make sure you unload '''ps3vram''' driver before running this module because '''ps3vram''' allocates all available RSX memory for itself and because of this, '''lv1_gpu_memory_allocate''' will always fail.
* This kernel module lets the RSX execute a simple program which contains only NOP (No Operation) commands.


error = libusb_handle_events_timeout(usb_ctx, &tv);
Download source code: [http://lol.notsoldierx.com/~glevand/ps3/linux/ps3rsx.tar.gz]
if (error) {
 
fprintf(stderr, "%s:%d: could not handle events (%d)\n",
====Source Code====
__func__, __LINE__, error);
 
exit(1);
<pre>
}
/*
}
* PS3 RSX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
 
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/delay.h>


libusb_free_transfer(usb_intr_transfer_ep5_in);
#include <asm/abs_addr.h>
#include <asm/cell-regs.h>
#include <asm/lv1call.h>
#include <asm/ps3.h>


error = libusb_release_interface(usb_dev_handle, USB_IFACE_NUMBER);
#define RSX_FIFO_CMD_BUF_SIZE (1 * 1024 * 1024)
if (error)
fprintf(stderr, "%s:%d: could not release interface (%d)\n",
__func__, __LINE__, error);


libusb_close(usb_dev_handle);
#define RSX_MEM_SIZE (32 * 1024 * 1024)


libusb_exit(usb_ctx);
#define RSX_GPU_IOIF (0x0e000000ul)


exit(0);
#define RSX_FIFO_CTRL_SIZE (4 * 1024)
}
</pre>


====Output====
struct rsx_fifo_ctrl {
u8 res[0x40];
u32 put;
u32 get;
};


<pre>
static u32 *rsx_fifo_cmd_buf;
glevand@debian-hdd:~/ps3_usb_wlan$ sudo ./ps3_usb_wlan
static u64 rsx_fifo_cmd_buf_lpar;
sudo: unable to resolve host debian-hdd
 
main:824: number of bytes transferred (32)
static u64 rsx_mem_handle, rsx_mem_lpar;
main:833: number of bytes received (2)
static u64 rsx_ctx_handle;
main:835: 0x20 0x31
static u64 rsx_fifo_ctrl_lpar;
usb_wlan_cmd_send:288: sending command (0x114f) data size (0x0518) command size (0x0524)
static u64 rsx_drv_info_lpar;
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
static u64 rsx_reports_lpar, rsx_reports_size;
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
static struct rsx_fifo_ctrl *rsx_fifo_ctrl;
usb_handle_wlan_cmd_response:191: command header:
 
usb_handle_wlan_cmd_response:192: command (0x1150)
/*
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* FIFO program
usb_handle_wlan_cmd_response:201: status (0x0006)
*/
usb_handle_wlan_cmd_response:205: ==> command status != 0x1
static u32 rsx_fifo_prg[] = {
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
0x00000000, /* nop */
usb_handle_wlan_cmd_response:210: command payload:
0x00000000, /* nop */
usb_wlan_cmd_send:288: sending command (0x1171) data size (0x0000) command size (0x000c)
0x00000000, /* nop */
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
};
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
/*
usb_handle_wlan_cmd_response:191: command header:
* ps3rsx_init
usb_handle_wlan_cmd_response:192: command (0x1172)
*/
usb_handle_wlan_cmd_response:199: tag (0xcafe)
static int __init ps3rsx_init(void)
usb_handle_wlan_cmd_response:201: status (0x0001)
{
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
unsigned long timeout;
usb_handle_wlan_cmd_response:210: command payload:
int res;
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
/* FIFO command buffer must be allocated in XDR memory */
usb_handle_wlan_event:133: === got WLAN event ===
 
usb_handle_wlan_event:144: event_count (0x01)
rsx_fifo_cmd_buf = kmalloc(RSX_FIFO_CMD_BUF_SIZE, GFP_KERNEL);
00000000: 00 04 00 00 10 00 00 00 3c 22 02 00 00 00 00 00 |........<"......|
if (!rsx_fifo_cmd_buf) {
00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................|
printk(KERN_INFO"could not allocate FIFO command buffer\n");
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............|
res = -ENOMEM;
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
goto fail;
usb_wlan_cmd_send:288: sending command (0x116f) data size (0x0004) command size (0x0010)
}
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
res = lv1_gpu_memory_allocate(RSX_MEM_SIZE, 0, 0, 0, 0,
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
&rsx_mem_handle, &rsx_mem_lpar);
usb_handle_wlan_cmd_response:191: command header:
if (res) {
usb_handle_wlan_cmd_response:192: command (0x1170)
printk(KERN_INFO"lv1_gpu_memory_allocate failed (%d)\n", res);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
res = -ENXIO;
usb_handle_wlan_cmd_response:201: status (0x0001)
goto fail_free_fifo_cmd_buf_mem;
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
}
usb_handle_wlan_cmd_response:210: command payload:
 
usb_wlan_cmd_send:288: sending command (0x115b) data size (0x005e) command size (0x006a)
res = lv1_gpu_context_allocate(rsx_mem_handle, 0,
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
&rsx_ctx_handle, &rsx_fifo_ctrl_lpar, &rsx_drv_info_lpar,
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
&rsx_reports_lpar, &rsx_reports_size);
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
if (res) {
usb_handle_wlan_cmd_response:191: command header:
printk(KERN_INFO"lv1_gpu_context_allocate failed (%d)\n", res);
usb_handle_wlan_cmd_response:192: command (0x115c)
res = -ENXIO;
usb_handle_wlan_cmd_response:199: tag (0xcafe)
goto fail_free_gpu_mem;
usb_handle_wlan_cmd_response:201: status (0x0001)
}
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
usb_handle_wlan_cmd_response:210: command payload:
/* map FIFO command buffer into RSX address space */
usb_wlan_cmd_send:288: sending command (0x1161) data size (0x0020) command size (0x002c)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
rsx_fifo_cmd_buf_lpar = ps3_mm_phys_to_lpar(__pa(rsx_fifo_cmd_buf));
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
res = lv1_gpu_context_iomap(rsx_ctx_handle,
usb_handle_wlan_cmd_response:191: command header:
RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar, RSX_FIFO_CMD_BUF_SIZE,
usb_handle_wlan_cmd_response:192: command (0x1162)
CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
if (res) {
usb_handle_wlan_cmd_response:201: status (0x0001)
printk(KERN_INFO"lv1_gpu_context_iomap failed (%d)\n", res);
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
res = -ENXIO;
usb_handle_wlan_cmd_response:210: command payload:
goto fail_free_gpu_mem;
usb_wlan_cmd_send:288: sending command (0x110d) data size (0x0080) command size (0x008c)
}
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
/* map RSX FIFO control registers */
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
rsx_fifo_ctrl = (struct rsx_fifo_ctrl *) ioremap(rsx_fifo_ctrl_lpar, RSX_FIFO_CTRL_SIZE);
usb_handle_wlan_cmd_response:192: command (0x110e)
if (!rsx_fifo_ctrl) {
usb_handle_wlan_cmd_response:199: tag (0xcafe)
printk(KERN_INFO"could not map FIFO control\n");
usb_handle_wlan_cmd_response:201: status (0x0001)
res = -ENXIO;
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
goto fail_free_gpu_mem;
usb_handle_wlan_cmd_response:210: command payload:
}
usb_wlan_cmd_send:288: sending command (0x1031) data size (0x0002) command size (0x000e)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
/* PUT and GET offsets are in RSX address space */
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
res = lv1_gpu_context_attribute(rsx_ctx_handle, 0x1,
usb_handle_wlan_cmd_response:191: command header:
RSX_GPU_IOIF + 0x0 /* PUT offset */, RSX_GPU_IOIF + 0x0 /* GET offset */,
usb_handle_wlan_cmd_response:192: command (0x1032)
0x0, 0x0);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
if (res) {
usb_handle_wlan_cmd_response:201: status (0x0001)
printk(KERN_INFO"lv1_gpu_context_attribute(0x1) failed (%d)\n", res);
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
res = -ENXIO;
usb_handle_wlan_cmd_response:210: command payload:
goto fail_unmap_fifo_ctrl;
00000000: 00 00                                          |..              |
}
usb_wlan_cmd_send:288: sending command (0x1041) data size (0x0006) command size (0x0012)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
/* copy FIFO commands to FIFO command buffer */
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (42)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
memcpy(rsx_fifo_cmd_buf, rsx_fifo_prg, sizeof(rsx_fifo_prg));
usb_handle_wlan_cmd_response:191: command header:
 
usb_handle_wlan_cmd_response:192: command (0x1042)
printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
/* kick FIFO */
usb_handle_wlan_cmd_response:207: payload_size (0x0006)
 
usb_handle_wlan_cmd_response:210: command payload:
rsx_fifo_ctrl->put = RSX_GPU_IOIF + sizeof(rsx_fifo_prg);
00000000: 00 11 22 33 44 55                              |.."3DU          |
 
usb_wlan_cmd_send:288: sending command (0x0029) data size (0x0002) command size (0x000e)
/* poll until RSX is done processing FIFO commands */
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38)
timeout = 100;
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
while (timeout--) {
usb_handle_wlan_cmd_response:192: command (0x002a)
if (rsx_fifo_ctrl->get == rsx_fifo_ctrl->put)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
break;
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
msleep(1);
usb_handle_wlan_cmd_response:210: command payload:
}
00000000: 02 02                                          |..              |
 
usb_wlan_cmd_send:288: sending command (0x110b) data size (0x000c) command size (0x0018)
printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (48)
if (rsx_fifo_ctrl->get != rsx_fifo_ctrl->put) {
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
printk(KERN_INFO"FIFO command buffer timeout\n");
usb_handle_wlan_cmd_response:191: command header:
res = -ENXIO;
usb_handle_wlan_cmd_response:192: command (0x110c)
goto fail_unmap_fifo_ctrl;
usb_handle_wlan_cmd_response:199: tag (0xcafe)
}
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x000c)
return 0;
usb_handle_wlan_cmd_response:210: command payload:
 
00000000: 01 00 00 00 00 00 00 00 20 00 00 00            |........ ...    |
fail_unmap_fifo_ctrl:
usb_wlan_cmd_send:288: sending command (0x1109) data size (0x0016) command size (0x0022)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
iounmap(rsx_fifo_ctrl);
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (58)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
fail_free_gpu_mem:
usb_handle_wlan_cmd_response:192: command (0x110a)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
lv1_gpu_memory_free(rsx_mem_handle);
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0016)
fail_free_fifo_cmd_buf_mem:
usb_handle_wlan_cmd_response:210: command payload:
 
00000000: 01 00 00 00 15 27 12 00 06 00 00 00 09 00 01 00 |.....'..........|
kfree(rsx_fifo_cmd_buf);
00000010: ff ff ff ff ff ff                              |......          |
 
usb_wlan_cmd_send:288: sending command (0x0207) data size (0x0004) command size (0x0010)
fail:
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40)
return res;
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
}
usb_handle_wlan_cmd_response:191: command header:
 
usb_handle_wlan_cmd_response:192: command (0x0208)
/*
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* ps3rsx_exit
usb_handle_wlan_cmd_response:201: status (0x0001)
*/
usb_handle_wlan_cmd_response:207: payload_size (0x0004)
static void __exit ps3rsx_exit(void)
usb_handle_wlan_cmd_response:210: command payload:
{
00000000: 01 00 00 00                                    |....            |
iounmap(rsx_fifo_ctrl);
usb_wlan_cmd_send:288: sending command (0x0203) data size (0x0004) command size (0x0010)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
lv1_gpu_context_iomap(rsx_ctx_handle, RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar,
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40)
RSX_FIFO_CMD_BUF_SIZE, CBE_IOPTE_M);
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
lv1_gpu_context_free(rsx_ctx_handle);
usb_handle_wlan_cmd_response:192: command (0x0204)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
lv1_gpu_memory_free(rsx_mem_handle);
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0004)
kfree(rsx_fifo_cmd_buf);
usb_handle_wlan_cmd_response:210: command payload:
}
00000000: 04 00 00 00                                    |....            |
 
usb_wlan_cmd_send:288: sending command (0x105f) data size (0x000a) command size (0x0016)
module_init(ps3rsx_init);
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
module_exit(ps3rsx_exit);
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
MODULE_LICENSE("GPL");
usb_handle_wlan_cmd_response:191: command header:
MODULE_DESCRIPTION("PS3 RSX");
usb_handle_wlan_cmd_response:192: command (0x1060)
MODULE_AUTHOR("glevand");
usb_handle_wlan_cmd_response:199: tag (0xcafe)
</pre>
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
====Test====
usb_handle_wlan_cmd_response:210: command payload:
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
<pre>
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
# insmod ./ps3rsx.ko
usb_handle_wlan_event:133: === got WLAN event ===
# dmesg
usb_handle_wlan_event:144: event_count (0x01)
 
00000000: 80 00 00 00 00 10 00 00 9e 2b 02 00 04 00 00 00 |.........+......|
GET offset (0x0e000000) PUT offset (0x0e000000) # GET and PUT offsets before kicking FIFO
00000010: fc 90 02 c0 01 00 00 00 00 00 00 00 00 00 00 00 |................|
GET offset (0x0e00000c) PUT offset (0x0e00000c) # GET and PUT offsets after kicking FIFO
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............|
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
usb_wlan_cmd_send:288: sending command (0x0099) data size (0x003e) command size (0x004a)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (98)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x009a)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x003e)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2|
00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19|
00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).|
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00      |..............  |
usb_wlan_cmd_send:288: sending command (0x1035) data size (0x0019) command size (0x0025)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (61)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1036)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0019)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 00 01 64 00 00 00 00 00 00 00 03 0d 01 02 03 04 |..d.............|
00000010: 05 06 07 08 09 0a 0b 0c 0d                      |.........      |
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
usb_handle_wlan_event:133: === got WLAN event ===
usb_handle_wlan_event:144: event_count (0x01)
00000000: 80 00 00 00 04 00 00 00 96 2e 02 00 01 00 00 00 |................|
00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............|
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
usb_wlan_cmd_send:288: sending command (0x1033) data size (0x05b0) command size (0x05bc)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (1403)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1034)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0557)
usb_handle_wlan_cmd_response:210: command payload:
...
Here is scan output (removed by me)
...
</pre>
</pre>


===Associate with AP===
As you see, RSX processed our FIFO commands :)


* I got association with AP working.
==Linux Driver==
* If  WLAN device is connected to an AP then the green LED is on, when data is received then the LED blinks.
* '''Data reception works finally !!!'''


====How to Associate with WPA AP====
* '''DRI/DRM is the ONLY way to go !!! No hacks like kernel modules with tons of IOCTLs !!!'''
* Set common configuration (command 0x1005)
* First implement 2D acceleration and then add 3D support
* Set WPA configuration (command 0x1019)
* The driver consists of 2 parts: '''DDX driver''' for X11 (user space) and '''DRM driver''' for Linux Kernel (kernel space)
* Set rate configuration (command 0x1ed)
* First implement DRM driver and test it from user space without DDX and libdrm by talking to it directly
* Associate (command 0x1001)


===Packet Reception===
===DDX Driver===


* EP6 IN and EP7 IN endpoints are used for packet reception
* Use '''libdrm'''
* LV2 sends bulk transfers to both endpoints
* Use '''EXA API''' for 2D acceleration on X11 (or maybe use '''XAA API''')
* '''4''' bulk transfers are sent simultaneously for each enpoint
* Use '''Kernel Mode Setting'''
* Every bulk transfer is of size '''0x620'''
* '''Make sure you set multicast address filter properly or else you won't receive broadcast packets !!!'''
* Bulk transfers returned by the host controller which do not contain any data have size of '''0x10''' bytes else transfers contain valid Ethernet frame. All 802.11 related data is stripped by the WLAN Gelic device.
* '''Make sure you set right MAC address with command 0x115b else device won't be able to receive packets destined to its own MAC address !!!'''


====Test with libusb====
===DRM Driver===


<pre>
* Extend '''nouveau''' driver or create a new one ???
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
* '''Decision: create new DRM driver in order to learn how DRM framework in Linux kernel works and because we have to use LV1 calls to access RSX (and because it's a lot more fun to do it on my own). But use nouveau as an example for DRM driver. Maybe i should better use radeon DRM driver as an example beacuse it seems to be better designed and implemnted !!!'''
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (98)
* The driver is very low level and allows direct access to almost all RSX funtions, e.g. FIFO buffer, to achieve maximum performance.
00000000: ff ff ff ff ff ff ?? ?? ?? ?? ?? ?? 08 00 45 00 |..............E.|
* All data buffers, e.g. vertices and textures, are managed by DRM framework (Linux kernel). To avoid copying from user to kernel space, the buffers will be mmaped into user space.
00000010: 00 54 00 00 40 00 40 01 b5 fe c0 a8 01 5b c0 a8 |.T..@.@......[..|
* Provides an interface to manage graphic objects in VRAM.
00000020: 01 ff 08 00 9c 69 0d 45 00 e2 4e 5d 34 26 00 07 |.....i.E..N]4&..|
* Use '''TTM''' or '''GEM''' ??? TTM is used by radeon and nouvea drivers, so i guess we could use it too. GEM is for Intel chips.
00000030: df e1 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 |................|
* Extend '''libdrm''' library to support new DRM driver.
00000040: 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 |.......... !"#$%|
* Fences can be implemented with '''RSX REF Control Register'''
00000050: 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 |&'()*+,-./012345|
00000060: 36 37                                          |67              |
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16)
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 |................|
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16)
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 |................|
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16)
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 |................|
</pre>


====Multicast Address Filter====
====Memory Management====


* WLAN Gelic device supports hardware multicast address filtering
* Size of all memory objects must be multiple of the page size (4096 bytes) even if a smaller size is requested by user
* Multicast address filtering is implemented with MAC address hashing and filter bitmap
* Nouveau driver uses IOCTL '''DRM_NOUVEAU_GEM_NEW''' to allocate memory objects in VRAM or GART. The IOCTL returns the handle of the newly allocated memory object.
* Filter bitmap is of size '''4 * 8''' bytes
* An example from Mesa how memory objects are used: [http://fxr.watson.org/fxr/source/external/bsd/drm/dist/libdrm/nouveau/nouveau_bo.c?v=NETBSD;im=10] [http://www.opensource.apple.com/source/X11libs/X11libs-60/mesa/Mesa-7.8.2/src/mesa/drivers/dri/nouveau/nouveau_bufferobj.c]
* Multicast address filter is set with command '''0x1161'''


=====MAC Address Hash Function=====
====Video RAM====


* Used by LV2
* VRAM is allocated once during context creating and cannot be changed during the whole life of the context.
 
* '''lv1_gpu_memory_allocate''' returns LPAR address of allocated VRAM which can be mapped into kernel address space.
<pre>
* '''VRAM starts at offset 0x0 in GPU address space.'''
unsigned char hash(unsigned char *data, unsigned int size)
* VRAM heap management is necessary, use e.g. TTM (ttm_bo_init_mm).
{
* This memory type is used e.g. for vertices or textures.
        unsigned int hash;
* It should be mappable from user space in order to allow user to put data there.
        int i, j;
* GameOS calls it '''Local Memory'''.
* VRAM can be mapped into kernel-space with '''ioremap'''.
* To map VRAM into user-space map it first into kernel-space with '''ioremap''' and then use '''remap_pfn_range''' to map into user-space.
* Use '''VM_IO''' flag for this kind of memory when mapping it into user-space.
* Mapping examples: [http://www.scs.ch/~frey/linux/memorymap.html] [http://www.cs.fsu.edu/~baker/devices/projects/antgeo/avnet_june19/pci_avnet.c]


        /*XXX: reverse data bits */
====GART Memory====


        hash = 0xffffffff;
* GART memory region is a memory region in System Memory but accessible by RSX through GART [http://dri.freedesktop.org/wiki/GART].
* GameOS calls it '''Main Memory'''.
* '''Problem: lv1_gpu_context_iomap supports ONLY 1MB and 64kB pages'''
* Size of system memory objects mapped into GPU address space should be either multiple of 1MB which means wasting lots of RAM and we don't have enough of it anyways. This solution is NOT suitable.
* Or place several GART memory objects into 1 MB page and map it. That would mean we have to use memory manager for each 1MB page.
* That means, we have to allocate 1MB page even if user requested a smaller memory region. Then initialize a heap manager for this 1MB page and return ONLY requested size. The following requests for GART memory regions can be satisfied from the previously allocated 1MB pages which still have enough free memory.
* FIFO command buffer is an example of a GART memory object which has to be mapped into GPU address space with lv1_gpu_context_iomap before it can be used by RSX.
* User allocates FIFO command buffer in GART address space, maps it into user space, write commands into it and then pushes it to DRM driver which maps it into RSX address space and CALLs it.
* '''TTM: TTM_PL_FLAG_TT for GART memory'''
* '''GameOS applications using GCM library map GART memory beginning at offset 0x10000000 or 0x20000000, just after where the whole VRAM is mapped.'''
* '''Don't use kmalloc for this type of memory. Use __get_free_pages and mark pages with flag VM_RESERVED before exporting it to user-space else they can be swapped out.'''
* TTM uses '''struct ttm_backend_func''' to call driver specific GART mapping functions. '''nouveau_sgdma.c''' handles GART memory mapping.


        for (i = 0; i < size; i++) {
====CPU Memory====
                hash = (((unsigned int) data[i]) << 24) ^ hash;


                for (j = 0; j < 8; j++) {
* This type of memory cannot be accessed by RSX at all.
                        if (((int) hash) >= 0) {
* Because this type of memory is not mapped into RSX address space through GART we don't need to allocate it in 1MB multiples.
                                hash = hash << 1;
* What do we need it for ???
                        } else {
                                hash = (hash << 1) ^ 0x04c10000;
                                hash = hash ^ 0x00001db7;
                        }
                }
        }


        hash = ((hash >> 24) & 0xf8) | (hash & 0x7);
====Mapping Memory Objects into Kernel-Space====


        return hash & 0xff;
* Nouveau driver uses '''ttm_bo_kmap''' to map memory objects into kernel-space (see '''ttm_bo_util.c''').
}
* Nouveau driver uses '''ttm_bo_ioremap''' to map IO memory into kernel-space, e.g. VRAM or GPU registers (see '''ttm_bo_util.c''') which uses '''ioremp_wc''' or '''ioremp_nocache'''.
* TTM uses page-wise allocation for buffers. The buffers are contiguous ONLY in a single page. That has a huge advantage over allocating 1MB contiguous memory blocks in kernel space. It's far easier to allocate a single page in Linux kernel than 1MB memory chunk, especially on PS3 arch which has only 256MB.
* '''Problem: lv1_gpu_context_iomap allows ONLY 1MB pages. Use lv1_put_iopte ???'''. See [http://lwn.net/Articles/304188/], [http://lxr.free-electrons.com/source/arch/powerpc/platforms/ps3/mm.c?a=sh#L562],  [http://wiki.ps2dev.org/ps3:hypervisor:lv1_put_iopte ] and [http://wiki.ps2dev.org/ps3:hypervisor:lv1_gpu_context_iomap].
* Yes, we can use '''lv1_put_iopte''' instead of '''lv1_gpu_context_iomap'''. That would solve the problem with 1MB pages on Linux. Both LV1 calls use the same internal LV1 function to map memory pages.
* '''lv1_gpu_context_iomap uses IOAS_ID 0 and IOID 1.'''
* TTM allows to map a buffer multiple times. Mapping information is stored in '''struct ttm_bo_kmap_obj'''.
* '''To make single allocated pages look contiguous to kernel-space, TTM uses vmap'''.
* '''It is possible to use 64KB pages for GART mapping without patching LV1. To enable 4KB pages support we have to patch LV1.'''
* Tested with 64kB IO page size. It works fine.


h = hash(mac_addr, 6);
====Mapping Memory Objects into User-Space====
v = 1 << (h & 0x1f);    /* word value in filter */
p = h >> 5;            /* word position in filter */


* User-space programs should be able to allocate memory objects in VRAM or GART and map it with '''mmap syscall'''.
* See '''nouveau_ttm.c:nouveau_ttm_mmap'''.
* Mapping memory objects into user-space avoids copying of data between user/kernel spaces.
* Problem: how to identify memory objects ???
* '''libdrm''' uses handles which are returned by DRM kernel driver when a new memory object is created. The handle is passed to mmap syscall as parameter '''offset'''. DRM driver looks up the handle and identifies the appropriate memory object which is mapped into user-space then.
* Nouveau driver uses TTM framework to map memory objects into user-space. TTM doesn't map all pages owned by the memory object at once but installs '''VM operation fault''' which maps single pages on demand. It makes sense because user application rarely accesses all pages of the mapped memory object at once.
* To map memory objects located in VRAM we have to map it into kernel space first with '''ioremap'''.


For broadcast address:
====FIFO Command Buffer====
------------------------


v = 0x20000000
* Every context has its own one main FIFO command buffer which is NOT accessible directly by user space.
p = 7
* User-space applications can allocate additional FIFO command buffers in GART memory space, map it into user space, store commands there and submit to DRM driver.
* Nouveau driver uses IOCTL '''NOUVEAU_GEM_PUSHBUF''' to execute FIFO command buffers. See '''nouveau_gem.c:nouveau_gem_ioctl_pushbuf'''.
* By user applications submitted FIFO command buffers are mapped by DRM driver into RSX address space first and then executed with CALL command.
* '''Problem: All references to graphics objects contained in FIFO command buffers must be expressed in RSX address space. How does user space know the right offsets of the referenced objects ???'''
* To solve the above problem, Nouveau driver uses relocations which are submitted to DRM driver together with FIFO command buffers. The DRM driver applies the specified relocations before executing the FIFO command buffer. See '''nouveau_gem.c:nouveau_gem_pushbuf_reloc_apply'''.
* Relocations contain memory object handles which they apply to. The DRM driver looks up the memory object by its handle and the memory objects contain GPU address space offsets.


That's why 0x20 is used with command 0x1161 !!! Without it the device won't deliver broadcast traffic.
=====Example=====
Learned it the hard way, after 2 days of trying to get packet reception working :)
<pre>
      ---------------------------------------------------------------
      |                                                              |
      |                                                              |
    \|/    Main FIFO command buffer (one per allocated context)    |
------------------------------        ------------------------------------
|          |        |                    |          |          |          |
|    ...    |  CALL  |        ...        |  CALL  |  ...    |  JMP    |
|          |        |                    |          |          |          |
------------------------------        ------------------------------------
                |      /|\                    |        /|\
    -------------|        |                    |          |
    |              ------|            --------|          |
  \|/              |                  |              ---|
-----------------------                |              |
|      |      |      |              |              |
|  ...  |  ...  |  RET  |              |              |
|      |      |      |              |              |
-----------------------                |              |
  FIFO command buffer 1                |              |
  (allocated by user space)            \|/              |
                                    -----------------------
                                    |      |      |      |
                                    |  ...  |  ...  |  RET  |
                                    |      |      |      |
                                    -----------------------
                                      FIFO command buffer 2
                                    (allocated by user space)
</pre>
</pre>


===Packet Transmission===
====Fences====
 
* Nouveau driver implements DRM fences with REF control register. See '''nouveau_fence.c:nouveau_fence_new'''.
* Newer Nvidia chips support semaphores. Nouveau driver uses semaphores for fences if they are supported.
* libgcm functions '''SetWriteCommandLabel''' and '''SetWaitLabel''' use semaphores.
* '''SetWriteCommandLabel''' releases semaphore and '''SetWaitLabel''' acquires semaphore.
* Semaphores are placed in VRAM. Nouveau driver creates a small VRAM heap for semaphores. See '''nouveau_fence.c:nouveau_fence_channel_init'''.
 
====IOCTLs====


* Tx packets are sent to EP6 OUT
=====Context Create=====
* Tx packets are normal Ethernet frames, they don't contain any WLAN data or other headers


===AP Mode===
* Creates new RSX context
* Allocates VRAM and memory for FIFO buffer
* Needed VRAM size and FIFO buffer size must be known during context creation


* I got AP mode working with security disabled for now
=====Context Destroy=====


====AP Mode with Security Disabled====
* Destroys previously allocated context


* Set AP SSID (command 0x5)
=====Context Attribute=====
* Set channel (command 0x11)
* Set AP opmode (command 0xb9)
* Configure rate control (command 0x1ed)
* Set AP WEP Configuration (command 0x5b, all 0s)
* Command 0x61 (param 0x0)
* Command 0xc5 (param 0x0)
* Command 0x1 (param 0x1)
* Command 0x1dd (param 0x2)
* Now green LED should be on


===ps3-jupiter Linux Drivers===
* Changes context attributes


* ps3_jupiter.ko is the common part of STA and AP mode. It implements a command interface to WLAN Gelic device and disptaches events to STA and AP drivers.
=====Graphic Object Creatre=====
* ps3_jupiter_sta.ko is a STA mode implementation.
* ps3_jupiter_ap.ko is a AP mode implementation.
* Simple scanning works already in STA mode (try it out with '''iwlist scan''')
* Packet reception works
* Packet transmission works
* '''WPA/WPA2''' fully working and usable with '''wpa_supplicant'''


* Create a graphic object either in VRAM or in XDR
* Used to create FIFO command buffers too (only in XDR of course because RSX supoorts FIFO command buffer in XDR only)


'''Finally, after several weeks of hard programming and reversing, the WLAN driver ps3_jupiter_sta achieved the milestone where i can use it with WPA2 :) I actually use it currently with WPA2 on my PS3 slim. It works damn !!! Try it out and report bugs and problems to me.'''
=====Graphic Object Destroy=====


====TODO====
* Frees previously created graphic object


* Implement association in STA mode (finished)
=====FIFO Execute=====
* Implement packet reception and transmission in STA mode (finished)
* Implement WEP support
* Implement AP mode
* Find out if Jupiter supports Monitor mode and if yes how to enable it
* Implement EURUS driver for PHATs (has many advantages over the old OtherOS approach, e.g. AP mode)
* Port to FreeBSD


==LV2 Network Stack==
* Allows user space applications to execute FIFO commands.
* To avoid copying of buffers allocated by user space to main FIFO command buffer use CALL and RET RSX FIFO commands to execute FIFO commands in buffers allocated by user space.
* Several FIFO command buffers can be submitted at once.


* LV2 uses BSD network stack, e.g. '''struct mbuf'''
=====Framebuffer=====
* It's almost identical to FreeBSD network stack.


===Network Device===
* Kernel DRM driver has to implement a frame buffer driver too
* Nouvea driver allocates frame buffer in video RAM and maps it into kernel address space (see '''nouveau_fbcon.c:nouveau_fbcon_create'''). Current ps3fb Linux driver doesn't allocate frame buffer in vide RAM but in system RAM.
* Direct access to video RAM from kernel is very very slow but some of frame buffer functions in Nouvea driver are hardware accelerated. We could do it the same way on Linux and get a hardware accelerated frame buffer this way. Not sure why ps3fb authors didn't add hardware acceleration to frame buffer. The reason why it was not implemnted in ps3fb is because LV1 doesn't create 2D graphic objects needed for 2D hardware acceleration.
* '''lv1_gpu_allocate_memory''' returns LPAR address of video RAM allocated for the RSX context.
* Unfortunately '''lv1_gpu_context_allocate''' doesn't initialize 2D ROP objects but we could use 3D operations to implement 2D ROPs.


====IOCTLs====
===libdrm===


=====Set Multicast Address Filter (0x81012000)=====
* Add support for RSX DRM to '''libdrm'''


* Sets multicast address filter
===Test Kernel Module and Program===
* Uses LV1 calls '''lv1_net_remove_multicast_address''' and '''lv1_net_add_multicast_address''' for Ethernet Gelic device
* Uses Eurus commands '''0x1161''', '''0x1163''' and '''0x1165''' for WLAN Gelic device


=====Unknown (0x8101200E)=====
* I uploaded here a test kernel module and a test user application: [http://www.gitbrew.org/~glevand/ps3/linux/ps3rsx_kernel.tar.gz] and [http://www.gitbrew.org/~glevand/ps3/linux/ps3rsx_user.tar.gz]
* I used a similar technique for mapping GPU resources into user-space like Linux kernel DRM drivers do it, e.g. Nouveau. But of course everything is very simplified in comparison with Nouveau driver. All GPU resources are mapped to user-space with mmap and there is no data copying between user and kernel space, for performance reasons. Mapping GPU resources into user-space like this is more flexible than IOCTLs.
* '''The purpose of the kernel module and the user application is to test how RSX works, to test FIFO commands and other stuff i reversed from Lv2. It's NOT for end users.'''
* Before loading the kernel module make sure ps3vram kernel module is NOT loaded.
* I used 64kB IO pages for GPU context. 4kB IO page size would be definitely a lot better for that we have to patch LV1. I will add this patch to my ps3mfw tasks for LV1.
* Just load the kernel module and then run the user application.
* The user application maps all context resources and executes some simple FIFO commands, like JMP or SET REF.
* I will add more examples later.
* By default, the kernel module allocates 8MB VRAM, 64kB FIFO and 1MB GART memory. You can change it by using kernel module parameters.
* Take a look at how i made non-contiguous allocated GART memory look contiguous to GPU, kernel-space and user-space.
* The kernel module needs some IOCTLs, e.g. for setting display buffers or flip status, because it can be done ONLY with LV1 calls. I will add it later.


* Uses LV1 call '''lv1_net_control(0x8000000000000001)'''
===Links===


=====Unknown (0x81040000)=====
* http://yangman.ca/blog/2009/10/linux-graphics-driver-stack-explained
* http://www.bitwiz.org.uk/s/how-dri-and-drm-work.html
* http://dri.sourceforge.net/doc/drm_low_level.html
* http://www.botchco.com/agd5f/?p=50
* http://webcvs.freedesktop.org/xorg/xc/programs/Xserver/hw/xfree86/doc/DESIGN?view=co
* http://www.x.org/wiki/ModularDevelopersGuide
* http://www.xfree86.org/current/DESIGN20.html
* http://nouveau.freedesktop.org/wiki/GraphicStackOverview
* http://cgit.freedesktop.org/nouveau/xf86-video-nouveau/tree/
* http://cgit.freedesktop.org/xorg/xserver/tree/hw/xfree86/doc/exa-driver.txt
* http://cgit.freedesktop.org/xorg/xserver/tree/hw/xfree86/xaa/XAA.HOWTO
* http://cgit.freedesktop.org/nouveau/linux-2.6/tree/drivers/gpu/drm
* http://kernel.org/doc/htmldocs/drm/drmInternals.html
* http://paginas.fe.up.pt/~mei04010/dri-architecture.pdf
* http://www.ecsl.cs.sunysb.edu/tr/TR222.pdf
* http://www.freesoftwaremagazine.com/columns/the_new_xorg_features
* http://www.freesoftwaremagazine.com/columns/xorgs_x_window_innovation_its_not_all_about_graphics#
* http://www.virtuousgeek.org/exa-driver.txt
* http://www.x.org/wiki/ttm
* http://nouveau.freedesktop.org/wiki/NvObjectTypes
* TTM: [http://lwn.net/Articles/257417/] [http://nouveau.freedesktop.org/wiki/TTMMemoryManager?action=AttachFile&do=get&target=mm.pdf]
* GEM: [http://lwn.net/Articles/283798/]
* TTM vs GEM: [http://lwn.net/Articles/283793/]
* OMAP DRM Driver: https://github.com/robclark/kernel-omap4/tree/omap_gpu-android/drivers/gpu/drm/omap


* Uses LV1 call '''lv1_net_control(0x8, [0x0, 0x1 or 0x2])''' for Ethernet Gelic device
=BD Drive=
* Uses Eurus commands '''0x116F''', '''0x115D''' and '''0x115B''' for WLAN Gelic device
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#BD_Drive gitbrew.org::HV#BD Drive] <br />


=====Enable/Disable WOL Magic Packet (0x81080000)=====


* Enables/Disables WOL Magic Packet
==Profile==
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x1 /* GELIC_LV1_WOL_MAGIC_PACKET */)''' for Ethernet Gelic device
* Uses Eurus commands '''0x1139''' and '''0x1155''' for WLAN Gelic device


=====Unknown (0x81080001)=====
* BD profile can be read with '''GET PROFILE''' device command or SCSI command '''GET CONFIGURATION'''


* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x2)''' for Ethernet Gelic device
===Profile Table===
* Uses Eurus commands '''0x113B''' and '''0x1157''' for WLAN Gelic device


=====Unknown (0x81080002)=====
{| class="wikitable"
 
|-
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x3)''' for Ethernet Gelic device
! Profile !! Description
* Uses Eurus commands '''0x113D''' and '''0x1159''' for WLAN Gelic device
|-
 
| 0x0 || No Current Profile
=====Unknown (0x81080003)=====
|-
 
| 0x2 || Removable Disk
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x4)''' for Ethernet Gelic device
|-
* Uses Eurus command '''0x1161''' for WLAN Gelic device
| 0x8 || CD-ROM
 
|-
=====Unknown (0x81080005)=====
| 0x9 || CD-R
 
|-
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x6 /* GELIC_LV1_WOL_ADD_MATCH_ADDR */)''' for Ethernet Gelic device
| 0xa || CD-RW
* Uses Eurus commands '''0x116D''' and '''0x1167''' for WLAN Gelic device
|-
| 0x10 || DVD-ROM
|-
| 0x11 || DVD-R Sequential recording
|-
| 0x12 || DVD-RAM
|-
| 0x13 || DVD-RW Restricted Overwrite
|-
| 0x14 || DVD-RW Sequential recording
|-
| 0x1a || DVD+RW
|-
| 0x1b || DVD+R
|-
| 0x40 || BD-ROM
|-
| 0x41 || BD-R Sequential Recording(TBD)
|-
| 0x42 || BD-R Random Recording(TBD)
|-
| 0x43 || BD-RE
|-
| 0x50 || PS1 CD-ROM
|-
| 0x60 || PS2 CD-ROM
|-
| 0x61 || PS2 DVD-ROM
|-
| 0x70 || PS3 DVD-ROM
|-
| 0x71 || PS3 BD-ROM
|-
| 0x10000 || CD-DA
|-
| 0x20000 || SACD
|-
| 0x100000 || Dual Layer (Parallel)
|-
| 0x200000 || Dual Layer (else Parallel)
|}


===Network Packet===
==Buffer==


* LV2 network packet is represented by '''struct mbuf'''
* BD drive has several buffers associated with internal flash
* Buffer can be read and written with SCSI commands '''READ/WRITE BUFFER'''
* Writing buffer is enabled with SCSI command '''MODE SELECT 10''' first


=RSX=
===Buffer Table===
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#RSX gitbrew.org::RSX] <br />


==HV Calls==
{| class="wikitable"
 
|-
===lv1_gpu_memory_allocate===
! ID !! Size !! Description
 
|-
* LV1 supports 16 memory handles simultaneously.
| 0x0 || 0x8000 || Used to transfer firmware to BD drive
* LV1 uses a bitmap to manage GPU VRAM.
|-
* The bitmap is located in LV1 memory, 4 double words.
| 0x1 || 0x800 || Serial Flash
* Each bit corresponds to 1MB VRAM, 256bit = 256MB VRAM.
|-
* 2MB at the top of VRAM are preallocated as you can see below.
| 0x2 || 0x60 || P-Block
|-
| 0x3 || 0x670 || S-Block
|-
| 0x4 || 0x8000 || Host Revocation List (HRL) Empty
|-
| 0x5 || 0x8000 || Host Revocation List (HRL) Current
|-
| 0x6 || 0x670 || S-Block
|-
| 0x7 || 0x8000 || Host Revocation List (HRL)
|}
 
===HRL Buffer===
 
* Size is 32KB just like AACS specifications prescribes (See AACS Common Specification 3.2.5.2 Host Revocation List Record)
* '''We could replace HRL with an older one in BD drive flash and restore revoked Host Certificates !!!'''


<pre>
==Device Commands==
<memory handle> = 0x5a5a5a5a xor <memory handle index>
</pre>


====Memory Context Object====
===Get Profile (0x11)===


offset 0x8 - memory handle (4 bytes)
* BD profile can be read with LV1 call '''lv1_send_storage_device_command''' and command '''0x11'''
* LV1 sends SCSI command '''GET CONFIGURATION''' to BD drive with '''requested type 0x0''', '''starting feature number 0x0''' and '''allocation length 0x8'''
* See SCSI command '''GET CONFIGURATION'''


offset 0x10 - VRAM LPAR start address (8 bytes)
===Auto Request Sense Mode On/Off (0x30)===


offset 0x18 - VRAM LPAR end address (8 bytes)
* LV1 expects a 4 byte value: 0x0 - On, 0x1 - Off
* can be get/set via GameOS sc0x25C/604: sys_storage_send_device_command(fd of bdvd,0x30,value,4,0,0 )


====Test====
==SCSI Commands==


* The offset of bitmap could be different on your system because it's allocated dynamically.
===Get Configuration===
* '''First 9MB of VRAM were allocated by ps3fb Linux driver.'''


Before allocating VRAM:
Getting the profile of a BD movie disc:
<pre>
<pre>
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00
00000000  00 00 00 00 00 00 01 ff  00 00 00 00 00 00 00 00 |.......ÿ........|
SCSI Status: Good
00000010  00 00 00 00 00 00 00 00  c0 00 00 00 00 00 00 00  |........À.......|
</pre>


After allocating 32 MB VRAM:
Sense Information:
<pre>
sense buffer empty
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C
00000000  00 00 01 ff ff ff ff ff  00 00 00 00 00 00 00 00  |...ÿÿÿÿÿ........|
00000010  00 00 00 00 00 00 00 00  c0 00 00 00 00 00 00 00  |........À.......|
</pre>


===lv1_gpu_context_allocate===
Received 8 bytes of data:
00    00 00 00 38 00 00 00 40                            ...8...@ 


* Register %r4 is flags.
# 0x40 means BD-ROM
* '''Found the place in LV1 where LV1 sets IO page size for GART memory mapping. We could patch it and set to 4KB. That would make a lot of things easier for RSX developers on Linux.'''
</pre>
* 1MB pages make RSX driver for Linux hard to implement because allocating 1Mb contiguous memory chunk on Linux is very very hard especially on a system with only 256MB and which was running for some time.
 
* LV1 supports 16 contexts simultaneously.
* LV1 has an array of context pointers.
* Each context has an index and a handle. The handle is derived from the index of the context.


Getting the profile of a PS3 game disc:
<pre>
<pre>
<context handle> = 0x55555555 xor <context index>
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00
</pre>
SCSI Status: Good


* Thats why first created context will have handle 0x55555555.
Sense Information:
sense buffer empty


====Context Object====
Received 8 bytes of data:
00    00 00 00 38 00 00 ff 71                            ...8...q
# 0x71 means PS3 BD-ROM
</pre>


offset 0x8 - handle (4 bytes)
===Get SS Key===


offset 0x48 - IO page size, valid range is 4kB, 64KB and 1MB (8 bytes)
* By SCSI standard undocumented parameters are used
* '''SCSI Report Key''' command with '''key format 0x3''' and '''key class 0xe0'''
* 8 bytes are returned by BD drive
* Used by VSH


====Flags====
Test with PS3 game disc:
<pre>
# sg_raw -r 8 /dev/sr0 a4 00 00 00 00 00 00 e0 00 08 03 00
SCSI Status: Good


'''0x2 - tells LV1 to use 64KB pages for GART memory mapping else LV1 uses 1MB pages'''
Sense Information:
sense buffer empty


===lv1_gpu_context_iomap===
Received 8 bytes of data:
00    00 06 00 00 00 00 00 04                            ........       
</pre>


* Internally uses lv1_put_iopte function
===Eject Media===
* IO page size is the one set during lv1_gpu_context_allocate
* IO address space id is 0x0. IO id is 0x1.


===lv1_gpu_context_attribute===
<pre>
sg_raw /dev/sr0 0x1b 00 00 00 02 00
</pre>


====Attribute 0x1====
===Load Media===
 
=====FIFO Command Buffer Setup=====


<pre>
<pre>
lv1_gpu_context_attribute(context handle, 0x1, PUT offset, GET offset, 0x0, 0x0)
sg_raw /dev/sr0 0x1b 00 00 00 03 00
</pre>
</pre>


====Attribute 0x101====
===Mode Select 10===
 
====Enable Buffer Write====


=====Set Flip Mode=====
* Uses '''PF 0x1''', '''SP 0x0''' and '''parameter list length 0x10'''
* Uses the following parameter list: '''0x00 0x0e 0x00 0x00 0x00 0x00 0x00 0x00 0x2d 0x6 <buffer id> 0x00 0x00 0x00 0x00 0x00'''
* '''Enables writing to BD drive flash, e.g. to HRL buffer !!!'''


Test with sg3-utils which enables write to HRL buffer:
<pre>
<pre>
lv1_gpu_attribute(0x2, 0x1 /* head */, 0x0, 0x0)
sg_raw /dev/sr0 55 10 00 00 00 00 00 00 10 00 00 0e 00 00 00 00 00 00 2d 06 04 00 00 00 00 00
lv1_gpu_context_attribute(context handle, 0x101, 0x1 /* head */, sync mode, 0x0, 0x0)
</pre>
</pre>


====Attribute 0x104====
===Write Buffer===


=====Set Display Buffer=====
* Used e.g. by Update Manager to send BD firmware to BD drive
* '''Mode 0x5 (Download microcode and save)''' is used e.g. to write HRL to BD drive flash
* '''Mode 0x7 (Download microcode with offsets and save)''' is used e.g. to write BD firmware to BD drive flash


<pre>
==AACS==
lv1_gpu_context_attribute(context handle, 0x104, id, width << 32 | height, pitch << 32 | offset, 0x0)
</pre>


====Attribute 0x10a====
===AACS SPU Module===


=====Get Flip Status=====
* BD player on GameOS uses '''AacsModule.spu.isoself''' (/dev_flash/bdplayer) to perform AACS authentication
* Tested on OtherOS++ 3.55
* Host certificate, host private key and AACS LA public key are stored encrypted with AES-256-CTR in the SPU module and are decrypted when the SPU module is loaded or when it's accessed first. The AES-256-CTR key and IV are in the SPU module too.
* 4.76 uses new Host certificate


* Reads a value at offset '''0x10C0 + 0x1 * 0x40''' in lpar_reports memory.
====Communication====


=====Reset Flip Status=====
* BD player reads '''EID3''' with '''Indi Info Manager 0x17001/0x17002''' services and passes it to SPU module
* '''EID3 is NEVER used in the SPU module although BD player passes it to the SPU module'''
* Data is exchanged with the SPU module through '''SPU In Mbox''', '''SPU Out Intr Mbox''' and a data buffer in XDR memory of size '''0x2000''' bytes.


<pre>
====Commands====
lv1_gpu_context_attribute(context handle, 0x10a, 0x1 /* id */, 0x7fffffff /* mask */, 0x0 /* value */, 0x0)
</pre>


* The LV1 call '''lv1_gpu_context_attribute(0x10a)''' accesses LPAR memory returned in '''lpar_reports''' by LV1 call '''lv1_gpu_context_allocate'''.
* The SPU module supports max '''0x78''' (til 4.75, 0x57 since 4.76) commands but not all are implemented
* Offset into lpar_reports is '''0x10C0 + id * 0x40 = 0x10C0 + 0x1 * 0x40'''.
* After a command is finished by the SPU module, it sends the status of the command to PPU through '''SPU Out Intr Mbox'''. Value 0 means success.
* Why not access lpar_reports memory directly and use LV1 call instead ???


====Attribute 0x10b====


* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''
{| class="wikitable sortable"
 
|+ style="caption-side:bottom; color:#e76700;"|''No full list!''
=====Set Cursor Position=====
! colspan="2" style="background-color:#FFEBAD;"| Command in FW !! rowspan="2" style="background-color:#FFEBAD;"| Name !! rowspan="2" style="background-color:#FFEBAD;"| Parameters !! rowspan="2" style="background-color:#FFEBAD;"| Info
 
|-
<pre>
! style="background-color:#FFEBAD;"| -4.75 !! style="background-color:#FFEBAD;"| 4.76+
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x3, x, y)
|-
</pre>
| 0x02|| 0x34 || Read 4 Bytes from XDR Buffer || ||
 
* It just reads 4 bytes of data from the XDR buffer passed to the SPU module.
=====Set Cursor Image Offset=====
|-
 
| 0x1C|| 0x48 || Set KCD || ||
<pre>
* Sends KCD (Key Conversion Data) to the SPU module.
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x2, offset, 0x0)
* KCD is encrypted with the Bus Key which was established previously by AACS authentication.
</pre>
|-
 
| 0x34|| 0x23 || Init AES_H || ||
====Attribute 0x10c====
* Initializes AES_H hashing function.
 
|-
* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''
| 0x35|| 0x22 || Calculate AES_H 1 || ||
 
* Calculates AES_H hash of the data stored in XDR buffer.
=====Cursor Function 1=====
|-
| || 0x21 ||  || 2x 4 Bytes ||
Signed CSS CheckCRL
|-
| || 0x56||  || ||
Get Random Seed
|-
| || 0x32||  || ||
Unknown
|-
| 0x36|| 0x24 || Calculate AES_H 2 || ||
* Calculates AES_H hash of the data stored in XDR buffer.
|-
| 0x3C|| 0x12 || Generate Host Nonce || ||
* Generates a nonce which is returned in command '''0x3D''' / '''0x0C'''
|-
| 0x3D|| 0x0C || Get Host Nonce and Certificate || ||
* The data returned by this command is of size '''0x14 (Nonce) + 0x5c (Host Certificate)'''
* The data returned by this command is sent by BD player with SCSI command '''SEND KEY''' to BD drive during AACS authentication
* '''Host Certificate is easy to get from the SPU module, e.g. with aacs_module on OtherOS++'''
* The data contains a nonce, host public key and host certificate signature.
|-
| 0x3E|| 0x0D|| Set Drive Nonce and Certificate || ||
* Stores BD drive nonce and certificate in local memory of SPU
|-
| 0x3F|| 0x0E|| Verify Drive Certificate || ||
|-
| 0x40|| 0x0A|| Set Drive Key || ||
|-
| 0x44|| 0x10 || Sign Host Key || ||
|-
| 0x45|| 0x0B || Get Host Key || ||
|-
| 0x46|| 0x14 || Calculate Bus Key || ||
|-
| 0x47|| 0x1C || Set Volume ID || ||
* Sends volume id and its MAC to the SPU module
|-
| 0x48|| 0x1D || Calculate Volume ID MAC || ||
* Calculates MAC of the passed volume id
|-
| 0x49|| 0x15 || Verify Volume ID MAC || ||
* Verifies MAC of the passed volume id
|-
| 0x4A|| 0x1A || Set PMSN || ||
* Sends PMSN and its MAC to the SPU module
|-
| 0x4B|| 0x1B || Calculate PMSN MAC || ||
* Calculates MAC of the passed PMSN
|-
| 0x4C|| 0x16 || Verify PMSN || ||
* Sends media id and its MAC to the SPU module
|-
| 0x4D|| 0x18 || Set Media ID || ||
* Sends media id and its MAC to the SPU module
|-
| 0x4E|| 0x19 || Calculate Media ID MAC || ||
* Calculates MAC of the passed media id
|-
| 0x4F|| 0x17 || Verify Media ID MAC || ||
* Verifies MAC of the passed media id
|-
| 0x55|| 0x1F || Verify Host/Drive Revocation || ||
* BD player stores HRL/DRL list entries in XDR buffer and passes it to the SPU module for verification
|-
| 0x72|| 0x25 ||  || || OCRL related, Content Revocation List
|-
| 0x74|| 0x26 ||  || || OCRT related
|-
| 0x75|| 0x27 ||  || || OSIG related
|-
| 0xFEFEFEFF|| 0xFEFEFEFF|| Terminate Session || ||
* AACS SPU module runs and processes commands as long as you need
* After a command is complete, the SPU module waits for the next command
* This command terminates the current session and stops SPU module
|-
|}


<pre>
===Drive Revocation List (DRL)===
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x1, 0x0, 0x0)
</pre>


=====Cursor Function 2=====
* SHA1 hash is encrypted/decrypted by '''SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)'''
* SHA1 hash is read with '''VTRM service 0x2005 (Retrieve)'''
* SHA1 hash is written with '''VTRM service 0x2003 (Store With Update)'''


<pre>
===Content Revocation List (CRL)===
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x2, 0x0, 0x0)
</pre>


====Attribute 0x10d====
* SHA1 hash is encrypted/decrypted by '''SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)'''
* SHA1 hash is read with '''VTRM service 0x2005 (Retrieve)'''
* SHA1 hash is written with '''VTRM service 0x2003 (Store With Update)'''


* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''
===Host Revocation List (HRL)===


=====Cursor Function 1=====
* Stored in BD drive flash
* It can be read/written with SCSI commands '''READ/WRITE BUFFER'''. Yeah, it can be written too :D


<pre>
====Read HRL from BD Drive Flash====
lv1_gpu_context_attribute(context handle, 0x10d, 0x1, 0x1, 0x0, 0x0)
</pre>


====Attribute 0x300====
* It seems that BD drive has several HRL in its flash
* Empty HRL stored on BD drive flash can be read with SCSI command '''READ BUFFER''' by using as '''mode 0x2''', '''buffer id 0x4''' and '''allocation length 0x40'''
* Current HRL stored on BD drive flash can be read with SCSI command '''READ BUFFER''' by using as '''mode 0x2''', '''buffer id 0x5'''
 
====Empty HRL====
 
<pre>
# sg_read_buffer -m 2 -i 4 -o 0 -l $((0x40)) /dev/sr0
00    10 00 00 0c 00 03 10 03  00 00 00 01 21 00 00 34                   
10    00 00 00 00 00 00 00 00  1b 0b f2 6d 47 9e 77 62                   
20    3d 91 fc 78 b1 59 c9 52  ca a4 c7 41 85 24 96 64                   
30    8d 1d 95 8e 9b 84 c6 fa  4a dd 43 9b 42 98 fe ff 


=====Set Tile=====
# byte 0x21 at offset 0xc means Record Type HRL


=====Set Invalidate Tile=====
# as you see this HRL is empty


=====Bind Tile=====
</pre>


=====Unbind Tile=====
====Current HRL====


====Attribute 0x301====
<pre>
# sg_read_buffer -m 2 -i 5 -o 0 -l $((0x7c)) /dev/sr0
00    10 00 00 0c 00 04 10 03  00 00 00 09 21 00 00 6c                   
10    00 00 00 07 00 00 00 07  00 09 ff ff 00 00 00 0b                   
20    00 00 ff ff 00 00 00 16  00 08 ff ff 00 00 00 21                   
30    00 03 ff ff 00 00 00 35  00 04 ff ff 00 00 00 4e                   
40    00 03 ff ff 00 00 00 54  00 03 ff ff 00 00 00 5e                   
50    80 93 3a 62 f5 5a 9c 8c  62 ce 7d b8 69 5d d7 b1                   
60    c3 0f 36 ff 96 a2 3b 32  cb cd 58 d4 12 c9 fd bf                   
70    f5 16 a6 4a 32 ba 60 f0  5d 71 74 10


=====Set Zcull=====
# the current HRL is NOT empty and is from MKBv9 because the only BD movie i played on my PS3 has MKBv9
</pre>


=====Bind Zcull=====
===PS3 BD Player Host Certificate===


=====Unbind Zcull=====
<pre>
$ hexdump -C aacs_auth/ps3_host_cert.bin
00000000  02 01 00 5c ff ff 80 00  00 39 00 00 65 ea c9 87  |...\ÿÿ...9..eêÉ.|
00000010  8b 85 ef f4 d7 7a 62 b1  d6 00 02 4a ce 68 dd 33  |..ïô×zb±Ö..JÎhÝ3|
00000020  66 88 0e 4f 84 4f 34 b7  7a 05 01 35 a2 0e 73 b6  |f..O.O4·z..5¢.s¶|
00000030  26 da ea 51 57 b3 2e b8  4b c6 e8 7b 0d ee 4d 83  |&ÚêQW³.žKÆè{.îM.|
00000040  3c ea da 86 12 01 51 00  2c 3c 66 d5 25 6f 71 cf  |<êÚ...Q.,<fÕ%oqÏ|
00000050  a6 8b 7e 55 ba 1b 35 1f  34 03 43 4e              |Š.~Uº.5.4.CN|
0000005c


====Attribute 0x601====
# Host ID is 0xffff80000039
</pre>


* Copies data from GART memory to VRAM.
===PS3 BD Player Host Private Key===
* LV1 uses internally the FIFO command buffer passed by ps3fb driver with lv1_gpu_context_iomap.


FIFO commands:
<pre>
<pre>
0x0004C184
$ hexdump -C aacs_auth/ps3_host_priv_key.bin
0xFEED0001
00000000  00 66 8c 9a 75 ee fc 8d  a4 26 19 38 e2 71 28 50  |.f..u....&.8.q(P|
00000010  61 bb 09 f0 dd                                    |a....|
</pre>


0x0004C198
===AACS Processing Keys===
0x313371C3


0x00046300
====MKB v1====
0x0000000A


for ()
<pre>
{
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v1.inf
    for ()
    {
        0x0004630C
        <param>


        0x00046304
=MKB=
        <param>
type:
0x00031003
version:
0x00000001


        0x0024C2FC
MKB U masks and UVs: 514
        0x00000001
        0x00000003
        0x00000003
        <param1>
        <param2>
        <param3>
        <param4>
        0x00010000
        0x00010000


        0x0001C400
=applying subset-difference=
        <param1>
index: 0
        <param2>
UV: 0x00000001
        <param3>
U mask: 0xff800000
        0x00000000
V mask: 0xfffffffe
    }
}


0x00040110
=applying device key=
0x00000000
index: 244
</pre>
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|


==FIFO Command Buffer==
processing key:
00000000: 09 f9 11 02 9d 74 e3 5b d8 41 56 c5 63 56 88 c0 |.....t.[.AV.cV..|


===FIFO Control Registers===
C value:
00000000: cb 06 90 db e6 54 55 7b 12 62 aa d7 89 f4 9d 92 |.....TU{.b......|


* LV1 call '''lv1_gpu_context_allocate''' returns LPAR address of FIFO control registers.
media key:
* You have to map it into Linux address space before you can access FIFO control registers.
00000000: b4 6c 48 5e f7 51 ae 29 ef 87 bc 58 28 f3 2a 8d |.lH^.Q.)...X(.*.|
* Value of PUT and GET registers are NOT expressed in Linux address space but in RSX address space. You have to convert it to RSX address space.
* GET register is read-only and is modified by RSX while it's processing FIFO commands.


===Kicking FIFO Command Buffer===
=MKB verify media key data=
 
encrypted:
* As long as values of GET and PUT FIFO control registers are equal, RSX doesn't process commands from the FIFO command buffer.
00000000: 46 32 5b 42 48 b4 86 5a fc ef 75 25 47 b1 b5 12 |F2[BH..Z..u%G...|
* When the value of PUT register is not equal to the value of GET register, RSX starts processing commands in the FIFO command buffer.
decrypted:
* To execute FIFO commands, place them in the FIFO command buffer and change the value of PUT register.
00000000: 01 23 45 67 89 ab cd ef 0d ac 14 b9 ee f4 bd cc |.#Eg............|
</pre>
 
====MKB v3====
 
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v3.inf


===FIFO Setup Programs of emer_init.self===
=MKB=
type:
0x00031003
version:
0x00000003


* [[PS3:HvReverseEngineering:emer_init.self:Program 1]]
MKB U masks and UVs: 528
* [[PS3:HvReverseEngineering:emer_init.self:Program 2]]
* [[PS3:HvReverseEngineering:emer_init.self:Program 3]]


===FIFO Commands===
=applying subset-difference=
index: 14
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


[[PS3:HvReverseEngineering:RSXFIFOCommands]]
=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|


===Example How to Use FIFO Command Buffer===
processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|


Here is a small Linux kernel module which shows you how to use FIFO command buffer on Linux.
C value:
00000000: 0a b7 33 82 85 62 91 d1 91 4a 95 9e 36 18 c7 a1 |..3..b...J..6...|


* RSX allows to create multiple contexts.
media key:
* This kernel module should run without problems with '''ps3fb''' driver already running.
00000000: 6e da eb d4 88 aa 38 58 74 26 35 fd fd 36 66 d5 |n.....8Xt&5..6f.|
* Make sure you unload '''ps3vram''' driver before running this module because '''ps3vram''' allocates all available RSX memory for itself and because of this, '''lv1_gpu_memory_allocate''' will always fail.
* This kernel module lets the RSX execute a simple program which contains only NOP (No Operation) commands.


Download source code: [http://lol.notsoldierx.com/~glevand/ps3/linux/ps3rsx.tar.gz]
=MKB verify media key data=
encrypted:
00000000: 99 76 96 b0 6f 49 37 9b c4 b9 2b be 73 ce 96 1a |.v..oI7...+.s...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef fb 01 cc 85 eb e5 bf 0a |.#Eg............|
</pre>


====Source Code====
====MKB v4====


<pre>
<pre>
/*
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v4.inf
* PS3 RSX
 
*
=MKB=
* This program is free software; you can redistribute it and/or modify it
type:
* under the terms of the GNU General Public License as published
0x00031003
* by the Free Software Foundation; version 2 of the License.
version:
*
0x00000004
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/


#include <linux/module.h>
MKB U masks and UVs: 526
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/delay.h>


#include <asm/abs_addr.h>
=applying subset-difference=
#include <asm/cell-regs.h>
index: 12
#include <asm/lv1call.h>
UV: 0x00000080
#include <asm/ps3.h>
U mask: 0xff800000
V mask: 0xffffff00


#define RSX_FIFO_CMD_BUF_SIZE (1 * 1024 * 1024)
=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|


#define RSX_MEM_SIZE (32 * 1024 * 1024)
processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|


#define RSX_GPU_IOIF (0x0e000000ul)
C value:
00000000: bf 71 0c 8b 46 a0 24 d8 f0 3a a1 26 37 9d fb fc |.q..F.$..:.&7...|


#define RSX_FIFO_CTRL_SIZE (4 * 1024)
media key:
00000000: ef 18 c0 dd bf 02 32 a1 2f 57 f7 65 79 2c 1c 58 |......2./W.ey,.X|


struct rsx_fifo_ctrl {
=MKB verify media key data=
u8 res[0x40];
encrypted:
u32 put;
00000000: 54 85 08 a9 6a 70 2a c9 32 e3 74 a6 55 78 6c 01 |T...jp*.2.t.Uxl.|
u32 get;
decrypted:
};
00000000: 01 23 45 67 89 ab cd ef da 90 cf 2a e5 b2 6c 45 |.#Eg.......*..lE|
</pre>


static u32 *rsx_fifo_cmd_buf;
====MKB v7====
static u64 rsx_fifo_cmd_buf_lpar;


static u64 rsx_mem_handle, rsx_mem_lpar;
<pre>
static u64 rsx_ctx_handle;
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v7.inf
static u64 rsx_fifo_ctrl_lpar;
 
static u64 rsx_drv_info_lpar;
=MKB=
static u64 rsx_reports_lpar, rsx_reports_size;
type:
0x00031003
version:
0x00000007


static struct rsx_fifo_ctrl *rsx_fifo_ctrl;
MKB U masks and UVs: 526


/*
=applying subset-difference=
* FIFO program
index: 7
*/
UV: 0x00000080
static u32 rsx_fifo_prg[] = {
U mask: 0xff800000
0x00000000, /* nop */
V mask: 0xffffff00
0x00000000, /* nop */
0x00000000, /* nop */
};


/*
=applying device key=
* ps3rsx_init
index: 244
*/
UV: 0x00000100
static int __init ps3rsx_init(void)
U mask: 0xff800000
{
V mask: 0xfffffe00
unsigned long timeout;
device key:
int res;
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|


/* FIFO command buffer must be allocated in XDR memory */
processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|


rsx_fifo_cmd_buf = kmalloc(RSX_FIFO_CMD_BUF_SIZE, GFP_KERNEL);
C value:
if (!rsx_fifo_cmd_buf) {
00000000: 21 fd c9 4b 3e 1a f3 fe 9e b4 7a e6 ef 01 75 1b |!..K>.....z...u.|
printk(KERN_INFO"could not allocate FIFO command buffer\n");
res = -ENOMEM;
goto fail;
}


res = lv1_gpu_memory_allocate(RSX_MEM_SIZE, 0, 0, 0, 0,
media key:
&rsx_mem_handle, &rsx_mem_lpar);
00000000: af cd e2 c8 67 12 a4 b6 a8 58 0c 15 ef 07 6e f8 |....g....X....n.|
if (res) {
printk(KERN_INFO"lv1_gpu_memory_allocate failed (%d)\n", res);
res = -ENXIO;
goto fail_free_fifo_cmd_buf_mem;
}


res = lv1_gpu_context_allocate(rsx_mem_handle, 0,
=MKB verify media key data=
&rsx_ctx_handle, &rsx_fifo_ctrl_lpar, &rsx_drv_info_lpar,
encrypted:
&rsx_reports_lpar, &rsx_reports_size);
00000000: 4b 21 29 a5 0f db 96 bc bc 01 04 71 42 79 00 e5 |K!)........qBy..|
if (res) {
decrypted:
printk(KERN_INFO"lv1_gpu_context_allocate failed (%d)\n", res);
00000000: 01 23 45 67 89 ab cd ef 4e f9 d2 05 6e 19 c1 79 |.#Eg....N...n..y|
res = -ENXIO;
</pre>
goto fail_free_gpu_mem;
}
/* map FIFO command buffer into RSX address space */


rsx_fifo_cmd_buf_lpar = ps3_mm_phys_to_lpar(__pa(rsx_fifo_cmd_buf));
====MKB v8====


res = lv1_gpu_context_iomap(rsx_ctx_handle,
<pre>
RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar, RSX_FIFO_CMD_BUF_SIZE,
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v8.inf
CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M);
if (res) {
printk(KERN_INFO"lv1_gpu_context_iomap failed (%d)\n", res);
res = -ENXIO;
goto fail_free_gpu_mem;
}


/* map RSX FIFO control registers */
=MKB=
type:
0x00031003
version:
0x00000008


rsx_fifo_ctrl = (struct rsx_fifo_ctrl *) ioremap(rsx_fifo_ctrl_lpar, RSX_FIFO_CTRL_SIZE);
MKB U masks and UVs: 523
if (!rsx_fifo_ctrl) {
printk(KERN_INFO"could not map FIFO control\n");
res = -ENXIO;
goto fail_free_gpu_mem;
}


/* PUT and GET offsets are in RSX address space */
=applying subset-difference=
index: 4
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


res = lv1_gpu_context_attribute(rsx_ctx_handle, 0x1,
=applying device key=
RSX_GPU_IOIF + 0x0 /* PUT offset */, RSX_GPU_IOIF + 0x0 /* GET offset */,
index: 244
0x0, 0x0);
UV: 0x00000100
if (res) {
U mask: 0xff800000
printk(KERN_INFO"lv1_gpu_context_attribute(0x1) failed (%d)\n", res);
V mask: 0xfffffe00
res = -ENXIO;
device key:
goto fail_unmap_fifo_ctrl;
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|
}


/* copy FIFO commands to FIFO command buffer */
processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|


memcpy(rsx_fifo_cmd_buf, rsx_fifo_prg, sizeof(rsx_fifo_prg));
C value:
00000000: 73 2d 10 bd f8 b4 87 e2 86 a6 d5 3a 6d db 69 15 |s-.........:m.i.|


printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);
media key:
00000000: dd 46 d4 0d 26 54 5a ce 6c 59 0c 65 b7 2b 3a 9f |.F..&TZ.lY.e.+:.|


/* kick FIFO */
=MKB verify media key data=
encrypted:
00000000: c6 f6 f9 54 ce 90 e0 5e 2b 3b e4 1e 24 92 90 b2 |...T...^+;..$...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 97 e6 61 8b d1 69 3e a0 |.#Eg......a..i>.|
</pre>


rsx_fifo_ctrl->put = RSX_GPU_IOIF + sizeof(rsx_fifo_prg);
====MKB v9====


/* poll until RSX is done processing FIFO commands */
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v9.inf


timeout = 100;
=MKB=
type:
0x00031003
version:
0x00000009


while (timeout--) {
MKB U masks and UVs: 520
if (rsx_fifo_ctrl->get == rsx_fifo_ctrl->put)
break;


msleep(1);
=applying subset-difference=
}
index: 2
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);
=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|


if (rsx_fifo_ctrl->get != rsx_fifo_ctrl->put) {
processing key:
printk(KERN_INFO"FIFO command buffer timeout\n");
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
res = -ENXIO;
goto fail_unmap_fifo_ctrl;
}


return 0;
C value:
00000000: a4 5a c6 87 43 49 70 bb bf 0c 22 52 83 9e 2a f6 |.Z..CIp..."R..*.|


fail_unmap_fifo_ctrl:
media key:
00000000: 37 02 bd fc 96 dc a2 18 2e 55 b0 79 6d ad 36 6b |7........U.ym.6k|


iounmap(rsx_fifo_ctrl);
=MKB verify media key data=
encrypted:
00000000: 4d 5b 7b 9c 5d ee 55 a6 94 de e1 db 8d 08 c7 a2 |M[{.].U.........|
decrypted:
00000000: 01 23 45 67 89 ab cd ef cd 1d a8 8a 42 5a 10 43 |.#Eg........BZ.C|
</pre>


====MKB v10====


fail_free_gpu_mem:
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v10.inf


lv1_gpu_memory_free(rsx_mem_handle);
=MKB=
 
type:
fail_free_fifo_cmd_buf_mem:
0x00031003
version:
0x0000000a


kfree(rsx_fifo_cmd_buf);
MKB U masks and UVs: 522


fail:
=applying subset-difference=
index: 3
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


return res;
=applying device key=
}
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|


/*
processing key:
* ps3rsx_exit
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
*/
static void __exit ps3rsx_exit(void)
{
iounmap(rsx_fifo_ctrl);


lv1_gpu_context_iomap(rsx_ctx_handle, RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar,
C value:
RSX_FIFO_CMD_BUF_SIZE, CBE_IOPTE_M);
00000000: d4 77 dd 1a 8a 5c 6d d1 dd 31 2d af f7 d3 14 fa |.w...\m..1-.....|


lv1_gpu_context_free(rsx_ctx_handle);
media key:
00000000: 38 32 2b 3c 61 b0 35 b4 52 89 84 59 f4 7a 76 e6 |82+<a.5.R..Y.zv.|


lv1_gpu_memory_free(rsx_mem_handle);
=MKB verify media key data=
 
encrypted:
kfree(rsx_fifo_cmd_buf);
00000000: 3f d3 d5 fb 42 37 d9 05 b8 db 6b 03 a0 fe 2e 48 |?...B7....k....H|
}
decrypted:
 
00000000: 01 23 45 67 89 ab cd ef 65 b1 87 8c eb 0d 60 0f |.#Eg....e.....`.|
module_init(ps3rsx_init);
module_exit(ps3rsx_exit);
 
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PS3 RSX");
MODULE_AUTHOR("glevand");
</pre>
</pre>


====Test====
====MKB v12====


<pre>
<pre>
# insmod ./ps3rsx.ko
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v12.inf
# dmesg


GET offset (0x0e000000) PUT offset (0x0e000000)  # GET and PUT offsets before kicking FIFO
=MKB=
GET offset (0x0e00000c) PUT offset (0x0e00000c)  # GET and PUT offsets after kicking FIFO
type:
</pre>
0x00031003
version:
0x0000000c


As you see, RSX processed our FIFO commands :)
MKB U masks and UVs: 522
 
=applying subset-difference=
index: 3
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


==Linux Driver==
=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|


* '''DRI/DRM is the ONLY way to go !!! No hacks like kernel modules with tons of IOCTLs !!!'''
processing key:
* First implement 2D acceleration and then add 3D support
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
* The driver consists of 2 parts: '''DDX driver''' for X11 (user space) and '''DRM driver''' for Linux Kernel (kernel space)
* First implement DRM driver and test it from user space without DDX and libdrm by talking to it directly


===DDX Driver===
C value:
00000000: 89 75 89 e6 6f 4a de 95 11 32 57 6a cb 99 dd 69 |.u..oJ...2Wj...i|


* Use '''libdrm'''
media key:
* Use '''EXA API''' for 2D acceleration on X11 (or maybe use '''XAA API''')
00000000: 4b dd 69 9d 32 98 d7 b0 ad 32 71 6b 3d 9c e3 c2 |K.i.2....2qk=...|
* Use '''Kernel Mode Setting'''


===DRM Driver===
=MKB verify media key data=
encrypted:
00000000: 8d 43 fd f2 15 fa 58 78 64 db 25 46 62 ab 02 30 |.C....Xxd.%Fb..0|
decrypted:
00000000: 01 23 45 67 89 ab cd ef e6 1c bf 98 45 82 64 d9 |.#Eg........E.d.|
</pre>


* Extend '''nouveau''' driver or create a new one ???
====MKB v14====
* '''Decision: create new DRM driver in order to learn how DRM framework in Linux kernel works and because we have to use LV1 calls to access RSX (and because it's a lot more fun to do it on my own). But use nouveau as an example for DRM driver. Maybe i should better use radeon DRM driver as an example beacuse it seems to be better designed and implemnted !!!'''
* The driver is very low level and allows direct access to almost all RSX funtions, e.g. FIFO buffer, to achieve maximum performance.
* All data buffers, e.g. vertices and textures, are managed by DRM framework (Linux kernel). To avoid copying from user to kernel space, the buffers will be mmaped into user space.
* Provides an interface to manage graphic objects in VRAM.
* Use '''TTM''' or '''GEM''' ??? TTM is used by radeon and nouvea drivers, so i guess we could use it too. GEM is for Intel chips.
* Extend '''libdrm''' library to support new DRM driver.
* Fences can be implemented with '''RSX REF Control Register'''


====Memory Management====
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v14.inf


* Size of all memory objects must be multiple of the page size (4096 bytes) even if a smaller size is requested by user
=MKB=
* Nouveau driver uses IOCTL '''DRM_NOUVEAU_GEM_NEW''' to allocate memory objects in VRAM or GART. The IOCTL returns the handle of the newly allocated memory object.
type:
* An example from Mesa how memory objects are used: [http://fxr.watson.org/fxr/source/external/bsd/drm/dist/libdrm/nouveau/nouveau_bo.c?v=NETBSD;im=10] [http://www.opensource.apple.com/source/X11libs/X11libs-60/mesa/Mesa-7.8.2/src/mesa/drivers/dri/nouveau/nouveau_bufferobj.c]
0x00031003
version:
0x0000000e


====Video RAM====
MKB U masks and UVs: 526


* VRAM is allocated once during context creating and cannot be changed during the whole life of the context.
=applying subset-difference=
* '''lv1_gpu_memory_allocate''' returns LPAR address of allocated VRAM which can be mapped into kernel address space.
index: 6
* '''VRAM starts at offset 0x0 in GPU address space.'''
UV: 0x00000248
* VRAM heap management is necessary, use e.g. TTM (ttm_bo_init_mm).
U mask: 0xfffffe00
* This memory type is used e.g. for vertices or textures.
V mask: 0xfffffff0
* It should be mappable from user space in order to allow user to put data there.
* GameOS calls it '''Local Memory'''.
* VRAM can be mapped into kernel-space with '''ioremap'''.
* To map VRAM into user-space map it first into kernel-space with '''ioremap''' and then use '''remap_pfn_range''' to map into user-space.
* Use '''VM_IO''' flag for this kind of memory when mapping it into user-space.
* Mapping examples: [http://www.scs.ch/~frey/linux/memorymap.html] [http://www.cs.fsu.edu/~baker/devices/projects/antgeo/avnet_june19/pci_avnet.c]


====GART Memory====
=applying device key=
index: 28
UV: 0x00000280
U mask: 0xfffffe00
V mask: 0xffffff00
device key:
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...|


* GART memory region is a memory region in System Memory but accessible by RSX through GART [http://dri.freedesktop.org/wiki/GART].
processing key:
* GameOS calls it '''Main Memory'''.
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|
* '''Problem: lv1_gpu_context_iomap supports ONLY 1MB and 64kB pages'''
* Size of system memory objects mapped into GPU address space should be either multiple of 1MB which means wasting lots of RAM and we don't have enough of it anyways. This solution is NOT suitable.
* Or place several GART memory objects into 1 MB page and map it. That would mean we have to use memory manager for each 1MB page.
* That means, we have to allocate 1MB page even if user requested a smaller memory region. Then initialize a heap manager for this 1MB page and return ONLY requested size. The following requests for GART memory regions can be satisfied from the previously allocated 1MB pages which still have enough free memory.
* FIFO command buffer is an example of a GART memory object which has to be mapped into GPU address space with lv1_gpu_context_iomap before it can be used by RSX.
* User allocates FIFO command buffer in GART address space, maps it into user space, write commands into it and then pushes it to DRM driver which maps it into RSX address space and CALLs it.
* '''TTM: TTM_PL_FLAG_TT for GART memory'''
* '''GameOS applications using GCM library map GART memory beginning at offset 0x10000000 or 0x20000000, just after where the whole VRAM is mapped.'''
* '''Don't use kmalloc for this type of memory. Use __get_free_pages and mark pages with flag VM_RESERVED before exporting it to user-space else they can be swapped out.'''
* TTM uses '''struct ttm_backend_func''' to call driver specific GART mapping functions. '''nouveau_sgdma.c''' handles GART memory mapping.


====CPU Memory====
C value:
00000000: 8c 7e 31 e8 15 17 7e c3 2c 67 b7 cc 87 e9 39 c3 |.~1...~.,g....9.|


* This type of memory cannot be accessed by RSX at all.
media key:
* Because this type of memory is not mapped into RSX address space through GART we don't need to allocate it in 1MB multiples.
00000000: 4b b1 31 d1 6e 0e 86 45 89 07 a2 68 91 c4 e5 38 |K.1.n..E...h...8|
* What do we need it for ???


====Mapping Memory Objects into Kernel-Space====
=MKB verify media key data=
encrypted:
00000000: 20 03 8c 70 7d ab d0 6f ba 86 39 f0 31 26 86 5f | ..p}..o..9.1&._|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 27 9f e5 35 0b df 3d a5 |.#Eg....'..5..=.|
</pre>


* Nouveau driver uses '''ttm_bo_kmap''' to map memory objects into kernel-space (see '''ttm_bo_util.c''').
====MKB v15====
* Nouveau driver uses '''ttm_bo_ioremap''' to map IO memory into kernel-space, e.g. VRAM or GPU registers (see '''ttm_bo_util.c''') which uses '''ioremp_wc''' or '''ioremp_nocache'''.
* TTM uses page-wise allocation for buffers. The buffers are contiguous ONLY in a single page. That has a huge advantage over allocating 1MB contiguous memory blocks in kernel space. It's far easier to allocate a single page in Linux kernel than 1MB memory chunk, especially on PS3 arch which has only 256MB.
* '''Problem: lv1_gpu_context_iomap allows ONLY 1MB pages. Use lv1_put_iopte ???'''. See [http://lwn.net/Articles/304188/], [http://lxr.free-electrons.com/source/arch/powerpc/platforms/ps3/mm.c?a=sh#L562],  [http://wiki.ps2dev.org/ps3:hypervisor:lv1_put_iopte ] and [http://wiki.ps2dev.org/ps3:hypervisor:lv1_gpu_context_iomap].
* Yes, we can use '''lv1_put_iopte''' instead of '''lv1_gpu_context_iomap'''. That would solve the problem with 1MB pages on Linux. Both LV1 calls use the same internal LV1 function to map memory pages.
* '''lv1_gpu_context_iomap uses IOAS_ID 0 and IOID 1.'''
* TTM allows to map a buffer multiple times. Mapping information is stored in '''struct ttm_bo_kmap_obj'''.
* '''To make single allocated pages look contiguous to kernel-space, TTM uses vmap'''.
* '''It is possible to use 64KB pages for GART mapping without patching LV1. To enable 4KB pages support we have to patch LV1.'''
* Tested with 64kB IO page size. It works fine.


====Mapping Memory Objects into User-Space====
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v15.inf


* User-space programs should be able to allocate memory objects in VRAM or GART and map it with '''mmap syscall'''.
=MKB=
* See '''nouveau_ttm.c:nouveau_ttm_mmap'''.
type:
* Mapping memory objects into user-space avoids copying of data between user/kernel spaces.
0x00031003
* Problem: how to identify memory objects ???
version:
* '''libdrm''' uses handles which are returned by DRM kernel driver when a new memory object is created. The handle is passed to mmap syscall as parameter '''offset'''. DRM driver looks up the handle and identifies the appropriate memory object which is mapped into user-space then.
0x0000000f
* Nouveau driver uses TTM framework to map memory objects into user-space. TTM doesn't map all pages owned by the memory object at once but installs '''VM operation fault''' which maps single pages on demand. It makes sense because user application rarely accesses all pages of the mapped memory object at once.
* To map memory objects located in VRAM we have to map it into kernel space first with '''ioremap'''.


====FIFO Command Buffer====
MKB U masks and UVs: 527


* Every context has its own one main FIFO command buffer which is NOT accessible directly by user space.
=applying subset-difference=
* User-space applications can allocate additional FIFO command buffers in GART memory space, map it into user space, store commands there and submit to DRM driver.
index: 6
* Nouveau driver uses IOCTL '''NOUVEAU_GEM_PUSHBUF''' to execute FIFO command buffers. See '''nouveau_gem.c:nouveau_gem_ioctl_pushbuf'''.
UV: 0x00000248
* By user applications submitted FIFO command buffers are mapped by DRM driver into RSX address space first and then executed with CALL command.
U mask: 0xfffffe00
* '''Problem: All references to graphics objects contained in FIFO command buffers must be expressed in RSX address space. How does user space know the right offsets of the referenced objects ???'''
V mask: 0xfffffff0
* To solve the above problem, Nouveau driver uses relocations which are submitted to DRM driver together with FIFO command buffers. The DRM driver applies the specified relocations before executing the FIFO command buffer. See '''nouveau_gem.c:nouveau_gem_pushbuf_reloc_apply'''.
* Relocations contain memory object handles which they apply to. The DRM driver looks up the memory object by its handle and the memory objects contain GPU address space offsets.


=====Example=====
=applying device key=
<pre>
index: 28
      ---------------------------------------------------------------
UV: 0x00000280
      |                                                              |
U mask: 0xfffffe00
      |                                                              |
V mask: 0xffffff00
    \|/    Main FIFO command buffer (one per allocated context)    |
device key:
------------------------------        ------------------------------------
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...|
|          |        |                    |          |          |          |
|    ...    |  CALL  |        ...        |  CALL  |  ...    |  JMP    |
|          |        |                    |          |          |          |
------------------------------        ------------------------------------
                |      /|\                    |        /|\
    -------------|        |                    |          |
    |              ------|            --------|          |
  \|/              |                  |              ---|
-----------------------                |              |
|      |      |      |              |              |
| ... ... |  RET  |              |              |
|      |      |      |              |              |
-----------------------                |              |
  FIFO command buffer 1                |              |
  (allocated by user space)            \|/              |
                                    -----------------------
                                    |      |      |      |
                                    |  ... ... |  RET  |
                                    |       |      |      |
                                    -----------------------
                                      FIFO command buffer 2
                                    (allocated by user space)
</pre>


====Fences====
processing key:
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|


* Nouveau driver implements DRM fences with REF control register. See '''nouveau_fence.c:nouveau_fence_new'''.
C value:
* Newer Nvidia chips support semaphores. Nouveau driver uses semaphores for fences if they are supported.
00000000: 75 da 59 cf 0d c2 c0 95 86 fc 6b 8e 2e e9 cc 85 |u.Y.......k.....|
* libgcm functions '''SetWriteCommandLabel''' and '''SetWaitLabel''' use semaphores.
* '''SetWriteCommandLabel''' releases semaphore and '''SetWaitLabel''' acquires semaphore.
* Semaphores are placed in VRAM. Nouveau driver creates a small VRAM heap for semaphores. See '''nouveau_fence.c:nouveau_fence_channel_init'''.


====IOCTLs====
media key:
00000000: 28 46 25 38 3d cc 4f 1f 90 be 7d f7 8a ba 7b fd |(F%8=.O...}...{.|


=====Context Create=====
=MKB verify media key data=
encrypted:
00000000: 8d d2 69 e0 b7 6a 44 53 03 ad ef 58 44 fc a7 d7 |..i..jDS...XD...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef ff 6a 7d c3 17 bb 19 11 |.#Eg.....j}.....|
</pre>
====MKB v16====


* Creates new RSX context
<pre>
* Allocates VRAM and memory for FIFO buffer
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v16.inf
* Needed VRAM size and FIFO buffer size must be known during context creation


=====Context Destroy=====
=MKB=
type:
0x00031003
version:
0x00000010


* Destroys previously allocated context
MKB U masks and UVs: 531


=====Context Attribute=====
=applying subset-difference=
index: 5
UV: 0x00000248
U mask: 0xfffffe00
V mask: 0xfffffff0


* Changes context attributes
=applying device key=
index: 28
UV: 0x00000280
U mask: 0xfffffe00
V mask: 0xffffff00
device key:
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...|


=====Graphic Object Creatre=====
processing key:
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|


* Create a graphic object either in VRAM or in XDR
C value:
* Used to create FIFO command buffers too (only in XDR of course because RSX supoorts FIFO command buffer in XDR only)
00000000: f8 49 9b d1 32 f9 6e 8d 33 98 35 a8 54 80 d9 fe |.I..2.n.3.5.T...|


=====Graphic Object Destroy=====
media key:
00000000: 3a bf bf d7 7e b8 01 43 a9 3c 15 3f ba 47 8c e1 |:...~..C.<.?.G..|


* Frees previously created graphic object
=MKB verify media key data=
encrypted:
00000000: 8a 67 86 b6 9d 0d 22 dd 5d c2 88 1f 08 f3 ab b4 |.g....".].......|
decrypted:
00000000: 01 23 45 67 89 ab cd ef d6 32 1f 17 c4 2f e2 4a |.#Eg.....2.../.J|
</pre>


=====FIFO Execute=====
====MKB v17====


* Allows user space applications to execute FIFO commands.
<pre>
* To avoid copying of buffers allocated by user space to main FIFO command buffer use CALL and RET RSX FIFO commands to execute FIFO commands in buffers allocated by user space.
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v17.inf
* Several FIFO command buffers can be submitted at once.
 
=MKB=
type:
0x00031003
version:
0x00000011
 
MKB U masks and UVs: 540


=====Framebuffer=====
=applying subset-difference=
index: 14
UV: 0x00000308
U mask: 0xffffff00
V mask: 0xfffffff0


* Kernel DRM driver has to implement a frame buffer driver too
=applying device key=
* Nouvea driver allocates frame buffer in video RAM and maps it into kernel address space (see '''nouveau_fbcon.c:nouveau_fbcon_create'''). Current ps3fb Linux driver doesn't allocate frame buffer in vide RAM but in system RAM.
index: 21
* Direct access to video RAM from kernel is very very slow but some of frame buffer functions in Nouvea driver are hardware accelerated. We could do it the same way on Linux and get a hardware accelerated frame buffer this way. Not sure why ps3fb authors didn't add hardware acceleration to frame buffer. The reason why it was not implemnted in ps3fb is because LV1 doesn't create 2D graphic objects needed for 2D hardware acceleration.
UV: 0x00000340
* '''lv1_gpu_allocate_memory''' returns LPAR address of video RAM allocated for the RSX context.
U mask: 0xffffff00
* Unfortunately '''lv1_gpu_context_allocate''' doesn't initialize 2D ROP objects but we could use 3D operations to implement 2D ROPs.
V mask: 0xffffff80
device key:
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss|


===libdrm===
processing key:
00000000: 46 5f a8 be 82 85 09 01 4d 05 d2 fc ce ff 35 d2 |F_......M.....5.|


* Add support for RSX DRM to '''libdrm'''
C value:
00000000: 01 f7 54 0b 34 e8 c1 ce 63 8d ea fa bc ce 6e 7b |..T.4...c.....n{|


===Test Kernel Module and Program===
media key:
00000000: ef 63 4e a8 ca 06 d1 6a c7 21 65 1b 18 b3 04 c6 |.cN....j.!e.....|


* I uploaded here a test kernel module and a test user application: [http://www.gitbrew.org/~glevand/ps3/linux/ps3rsx_kernel.tar.gz] and [http://www.gitbrew.org/~glevand/ps3/linux/ps3rsx_user.tar.gz]
=MKB verify media key data=
* I used a similar technique for mapping GPU resources into user-space like Linux kernel DRM drivers do it, e.g. Nouveau. But of course everything is very simplified in comparison with Nouveau driver. All GPU resources are mapped to user-space with mmap and there is no data copying between user and kernel space, for performance reasons. Mapping GPU resources into user-space like this is more flexible than IOCTLs.
encrypted:
* '''The purpose of the kernel module and the user application is to test how RSX works, to test FIFO commands and other stuff i reversed from Lv2. It's NOT for end users.'''
00000000: d3 b9 d4 9c b6 94 47 d5 3d cc 42 fe 3e 47 40 04 |......G.=.B.>G@.|
* Before loading the kernel module make sure ps3vram kernel module is NOT loaded.
decrypted:
* I used 64kB IO pages for GPU context. 4kB IO page size would be definitely a lot better for that we have to patch LV1. I will add this patch to my ps3mfw tasks for LV1.
00000000: 01 23 45 67 89 ab cd ef f6 b4 c8 6a b7 b8 39 fc |.#Eg.......j..9.|
* Just load the kernel module and then run the user application.
</pre>
* The user application maps all context resources and executes some simple FIFO commands, like JMP or SET REF.
* I will add more examples later.
* By default, the kernel module allocates 8MB VRAM, 64kB FIFO and 1MB GART memory. You can change it by using kernel module parameters.
* Take a look at how i made non-contiguous allocated GART memory look contiguous to GPU, kernel-space and user-space.
* The kernel module needs some IOCTLs, e.g. for setting display buffers or flip status, because it can be done ONLY with LV1 calls. I will add it later.


===Links===
====MKB v18====


* http://yangman.ca/blog/2009/10/linux-graphics-driver-stack-explained
<pre>
* http://www.bitwiz.org.uk/s/how-dri-and-drm-work.html
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v18.inf
* http://dri.sourceforge.net/doc/drm_low_level.html
 
* http://www.botchco.com/agd5f/?p=50
=MKB=
* http://webcvs.freedesktop.org/xorg/xc/programs/Xserver/hw/xfree86/doc/DESIGN?view=co
type:
* http://www.x.org/wiki/ModularDevelopersGuide
0x00031003
* http://www.xfree86.org/current/DESIGN20.html
version:
* http://nouveau.freedesktop.org/wiki/GraphicStackOverview
0x00000012
* http://cgit.freedesktop.org/nouveau/xf86-video-nouveau/tree/
 
* http://cgit.freedesktop.org/xorg/xserver/tree/hw/xfree86/doc/exa-driver.txt
MKB U masks and UVs: 543
* http://cgit.freedesktop.org/xorg/xserver/tree/hw/xfree86/xaa/XAA.HOWTO
 
* http://cgit.freedesktop.org/nouveau/linux-2.6/tree/drivers/gpu/drm
=applying subset-difference=
* http://kernel.org/doc/htmldocs/drm/drmInternals.html
index: 17
* http://paginas.fe.up.pt/~mei04010/dri-architecture.pdf
UV: 0x00000320
* http://www.ecsl.cs.sunysb.edu/tr/TR222.pdf
U mask: 0xffffff00
* http://www.freesoftwaremagazine.com/columns/the_new_xorg_features
V mask: 0xffffffc0
* http://www.freesoftwaremagazine.com/columns/xorgs_x_window_innovation_its_not_all_about_graphics#
* http://www.virtuousgeek.org/exa-driver.txt
* http://www.x.org/wiki/ttm
* http://nouveau.freedesktop.org/wiki/NvObjectTypes
* TTM: [http://lwn.net/Articles/257417/] [http://nouveau.freedesktop.org/wiki/TTMMemoryManager?action=AttachFile&do=get&target=mm.pdf]
* GEM: [http://lwn.net/Articles/283798/]
* TTM vs GEM: [http://lwn.net/Articles/283793/]
* OMAP DRM Driver: https://github.com/robclark/kernel-omap4/tree/omap_gpu-android/drivers/gpu/drm/omap


=BD Drive=
=applying device key=
Crossreference: [http://portal.gitbrew.org/wikibrew/PS3:HvReverseEngineering#BD_Drive gitbrew.org::HV#BD Drive] <br />
index: 21
UV: 0x00000340
U mask: 0xffffff00
V mask: 0xffffff80
device key:
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss|


processing key:
00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..|


==Profile==
C value:
00000000: 7a 8f 03 41 27 c4 86 58 05 37 3a 90 de f8 de 26 |z..A'..X.7:....&|


* BD profile can be read with '''GET PROFILE''' device command or SCSI command '''GET CONFIGURATION'''
media key:
00000000: e3 ed cd b4 59 b4 12 d4 ae f9 4d 8e 78 7a cd 7d |....Y.....M.xz.}|


===Profile Table===
=MKB verify media key data=
encrypted:
00000000: ea 45 fa 35 65 70 56 6f 6a 86 65 ad 52 e7 71 a4 |.E.5epVoj.e.R.q.|
decrypted:
00000000: 01 23 45 67 89 ab cd ef bd 36 f9 ce 60 54 80 3c |.#Eg.....6..`T.<|
</pre>


{| class="wikitable"
====MKB v19====
|-
! Profile !! Description
|-
| 0x0 || No Current Profile
|-
| 0x2 || Removable Disk
|-
| 0x8 || CD-ROM
|-
| 0x9 || CD-R
|-
| 0xa || CD-RW
|-
| 0x10 || DVD-ROM
|-
| 0x11 || DVD-R Sequential recording
|-
| 0x12 || DVD-RAM
|-
| 0x13 || DVD-RW Restricted Overwrite
|-
| 0x14 || DVD-RW Sequential recording
|-
| 0x1a || DVD+RW
|-
| 0x1b || DVD+R
|-
| 0x40 || BD-ROM
|-
| 0x41 || BD-R Sequential Recording(TBD)
|-
| 0x42 || BD-R Random Recording(TBD)
|-
| 0x43 || BD-RE
|-
| 0x50 || PS1 CD-ROM
|-
| 0x60 || PS2 CD-ROM
|-
| 0x61 || PS2 DVD-ROM
|-
| 0x70 || PS3 DVD-ROM
|-
| 0x71 || PS3 BD-ROM
|-
| 0x10000 || CD-DA
|-
| 0x20000 || SACD
|-
| 0x100000 || Dual Layer (Parallel)
|-
| 0x200000 || Dual Layer (else Parallel)
|}


==Buffer==
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v19.inf


* BD drive has several buffers associated with internal flash
=MKB=
* Buffer can be read and written with SCSI commands '''READ/WRITE BUFFER'''
type:
* Writing buffer is enabled with SCSI command '''MODE SELECT 10''' first
0x00031003
version:
0x00000013


===Buffer Table===
MKB U masks and UVs: 544


{| class="wikitable"
=applying subset-difference=
|-
index: 17
! ID !! Size !! Description
UV: 0x00000320
|-
U mask: 0xffffff00
| 0x0 || 0x8000 || Used to transfer firmware to BD drive
V mask: 0xffffffc0
|-
| 0x1 || 0x800 || Serial Flash
|-
| 0x2 || 0x60 || P-Block
|-
| 0x3 || 0x670 || S-Block
|-
| 0x4 || 0x8000 || Host Revocation List (HRL) Empty
|-
| 0x5 || 0x8000 || Host Revocation List (HRL) Current
|-
| 0x6 || 0x670 || S-Block
|-
| 0x7 || 0x8000 || Host Revocation List (HRL)
|}


===HRL Buffer===
=applying device key=
index: 21
UV: 0x00000340
U mask: 0xffffff00
V mask: 0xffffff80
device key:
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss|


* Size is 32KB just like AACS specifications prescribes (See AACS Common Specification 3.2.5.2 Host Revocation List Record)
processing key:
* '''We could replace HRL with an older one in BD drive flash and restore revoked Host Certificates !!!'''
00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..|


==Device Commands==
C value:
00000000: b9 0b 55 d1 18 3c cc 80 20 1c 9f 26 c3 58 27 18 |..U..<.. ..&.X'.|


===Get Profile (0x11)===
media key:
00000000: 75 a9 79 9c 67 50 13 89 98 62 34 5b eb 54 34 dd |u.y.gP...b4[.T4.|


* BD profile can be read with LV1 call '''lv1_send_storage_device_command''' and command '''0x11'''
=MKB verify media key data=
* LV1 sends SCSI command '''GET CONFIGURATION''' to BD drive with '''requested type 0x0''', '''starting feature number 0x0''' and '''allocation length 0x8'''
encrypted:
* See SCSI command '''GET CONFIGURATION'''
00000000: c4 f0 ce 75 1b 12 b9 f0 22 2f 31 70 66 a9 6a b8 |...u...."/1pf.j.|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 66 5c 65 d3 c4 4c c7 b0 |.#Eg....f\e..L..|
</pre>


===Auto Request Sense Mode On/Off (0x30)===
====MKB v20====


* LV1 expects a 4 byte value: 0x0 - On, 0x1 - Off
<pre>
* can be get/set via GameOS sc0x25C/604: sys_storage_send_device_command(fd of bdvd,0x30,value,4,0,0 )
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v20.inf


==SCSI Commands==
=MKB=
type:
0x00031003
version:
0x00000014


===Get Configuration===
MKB U masks and UVs: 544


Getting the profile of a BD movie disc:
=applying subset-difference=
<pre>
index: 19
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00
UV: 0x00000384
SCSI Status: Good
U mask: 0xffffff80
 
V mask: 0xfffffff8
Sense Information:
sense buffer empty


Received 8 bytes of data:
=applying device key=
00    00 00 00 38 00 00 00 40                            ...8...
index: 18
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8
device key:
00000000: fb 4a c3 90 09 e8 21 13 d4 5e cf 4b 7e ae a4 67 |.J....!..^.K~..g|


# 0x40 means BD-ROM
processing key:
</pre>
00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k|


Getting the profile of a PS3 game disc:
C value:
<pre>
00000000: 10 9f f1 69 36 07 7d 7e ad 8f d2 1a 28 c5 09 ed |...i6.}~....(...|
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00
SCSI Status: Good


Sense Information:
media key:
sense buffer empty
00000000: dc 9f 08 f7 cb 1b f8 c4 cf 96 4e 96 df 23 56 58 |..........N..#VX|


Received 8 bytes of data:
=MKB verify media key data=
00    00 00 00 38 00 00 ff 71                            ...8...q
encrypted:
00000000: 18 ca f5 51 8f 36 ef 2f 7a 49 78 ff 54 40 a5 f1 |...Q.6./zIx.T@..|
# 0x71 means PS3 BD-ROM
decrypted:
00000000: 01 23 45 67 89 ab cd ef c5 5d 11 08 c3 26 db 48 |.#Eg.....]...&.H|
</pre>
</pre>


===Get SS Key===
====MKB v21====
 
* By SCSI standard undocumented parameters are used
* '''SCSI Report Key''' command with '''key format 0x3''' and '''key class 0xe0'''
* 8 bytes are returned by BD drive
* Used by VSH


Test with PS3 game disc:
<pre>
<pre>
# sg_raw -r 8 /dev/sr0 a4 00 00 00 00 00 00 e0 00 08 03 00
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v21.inf
SCSI Status: Good


Sense Information:
=MKB=
sense buffer empty
type:
0x00031003
version:
0x00000015


Received 8 bytes of data:
MKB U masks and UVs: 552
00    00 06 00 00 00 00 00 04                            ........       
</pre>


===Eject Media===
=applying subset-difference=
index: 19
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8


<pre>
=applying device key=
sg_raw /dev/sr0 0x1b 00 00 00 02 00
index: 18
</pre>
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8
device key:
00000000: fb 4a c3 90 09 e8 21 13 d4 5e cf 4b 7e ae a4 67 |.J....!..^.K~..g|


===Load Media===
processing key:
00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k|


<pre>
C value:
sg_raw /dev/sr0 0x1b 00 00 00 03 00
00000000: c0 0c fa bf f0 fe f2 32 77 19 db c4 d8 f8 60 c9 |.......2w.....`.|
</pre>


===Mode Select 10===
media key:
00000000: 55 83 aa 69 ff 52 16 83 c2 93 b3 48 03 2a 57 38 |U..i.R.....H.*W8|


====Enable Buffer Write====
=MKB verify media key data=
encrypted:
00000000: 12 5b f2 75 c8 f8 05 6b 4f 31 a5 ea 4a 12 9f a9 |.[.u...kO1..J...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef dc 46 45 b4 79 8d 4f 68 |.#Eg.....FE.y.Oh|
</pre>


* Uses '''PF 0x1''', '''SP 0x0''' and '''parameter list length 0x10'''
====MKB v23====
* Uses the following parameter list: '''0x00 0x0e 0x00 0x00 0x00 0x00 0x00 0x00 0x2d 0x6 <buffer id> 0x00 0x00 0x00 0x00 0x00'''
* '''Enables writing to BD drive flash, e.g. to HRL buffer !!!'''


Test with sg3-utils which enables write to HRL buffer:
<pre>
<pre>
sg_raw /dev/sr0 55 10 00 00 00 00 00 00 10 00 00 0e 00 00 00 00 00 00 2d 06 04 00 00 00 00 00
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v23.inf
</pre>


===Write Buffer===
=MKB=
type:
0x00031003
version:
0x00000017


* Used e.g. by Update Manager to send BD firmware to BD drive
MKB U masks and UVs: 556
* '''Mode 0x5 (Download microcode and save)''' is used e.g. to write HRL to BD drive flash
* '''Mode 0x7 (Download microcode with offsets and save)''' is used e.g. to write BD firmware to BD drive flash


==AACS==
=applying subset-difference=
index: 17
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8


===AACS SPU Module===
=applying device key=
index: 7
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8
device key:
00000000: 8b f4 fb d9 1a 7f b7 db 85 76 d1 e5 a1 5a 85 44 |.........v...Z.D|


* BD player on GameOS uses '''AacsModule.spu.isoself''' (/dev_flash/bdplayer) to perform AACS authentication
processing key:
* Tested on OtherOS++ 3.55
00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr|
* Host certificate, host private key and AACS LA public key are stored encrypted with AES-256-CTR in the SPU module and are decrypted when the SPU module is loaded or when it's accessed first. The AES-256-CTR key and IV are in the SPU module too.


====Communication====
C value:
00000000: f0 81 d4 93 aa b5 01 1a a7 ff 8e 18 8a 48 8a 2d |.............H.-|
 
media key:
00000000: 02 04 59 d0 7c b5 54 94 bf 46 9b 98 91 1e 43 1f |..Y.|.T..F....C.|


* BD player reads '''EID3''' with '''Indi Info Manager 0x17001/0x17002''' services and passes it to SPU module
=MKB verify media key data=
* '''EID3 is NEVER used in the SPU module although BD player passes it to the SPU module'''
encrypted:
* Data is exchanged with the SPU module through '''SPU In Mbox''', '''SPU Out Intr Mbox''' and a data buffer in XDR memory of size '''0x2000''' bytes.
00000000: 24 a1 27 f9 30 70 25 67 07 2f 2a d4 13 89 0d aa |$.'.0p%g./*.....|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 21 00 20 84 c4 5f 36 78 |.#Eg....!. .._6x|
</pre>


====Commands====
====MKB v25====


* The SPU module supports max '''0x78''' commands but not all are implemented
<pre>
* After a command is finished by the SPU module, it sends the status of the command to PPU through '''SPU Out Intr Mbox'''. Value 0 means success.
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v25.inf


=====Read 4 Bytes from XDR Buffer (0x2)=====
=MKB=
type:
0x00031003
version:
0x00000019


* It just reads 4 bytes of data from the XDR buffer passed to the SPU module.
MKB U masks and UVs: 564


=====Set KCD (0x1e)=====
=applying subset-difference=
index: 13
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8


* Sends KCD (Key Conversion Data) to the SPU module.
=applying device key=
* KCD is encrypted with the Bus Key which was established previously by AACS authentication.
index: 7
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8
device key:
00000000: 8b f4 fb d9 1a 7f b7 db 85 76 d1 e5 a1 5a 85 44 |.........v...Z.D|


=====Init AES_H (0x34)=====
processing key:
00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr|


* Initializes AES_H hashing function.
C value:
00000000: 19 62 23 7d 81 01 c2 55 2f 36 20 1b 3e 69 40 10 |.b#}...U/6 .>i@.|


=====Calculate AES_H 1 (0x35)=====
media key:
00000000: 70 b5 9f 35 86 5d 18 73 bb 80 c3 2b f7 41 f6 14 |p..5.].s...+.A..|


* Calculates AES_H hash of the data stored in XDR buffer.
=MKB verify media key data=
encrypted:
00000000: 89 be 1e 1e b1 93 4c f2 2d ac c3 ce ed 10 07 f0 |......L.-.......|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 3f 5d 87 7a 88 09 db c4 |.#Eg....?].z....|
</pre>


=====Calculate AES_H 2 (0x36)=====
===Documentation===


* Calculates AES_H hash of the data stored in XDR buffer.
* SCSI Specification: [http://www.13thmonkey.org/documentation/SCSI/]
* AACS Specification Common: [http://www.aacsla.com/specifications/specs091/AACS_Spec_Common_0.91.pdf]
* AACS Specification Pre-recorded Video Book [http://www.aacsla.com/specifications/AACS_Spec_Prerecorded_Final_0.951.pdf]
* AACS Tutorial: [http://forum.doom9.org/showthread.php?t=122363]
* AACS Overview: [http://www.cacr.math.uwaterloo.ca/techreports/2007/cacr2007-25.pdf]


=====Generate Host Nonce (0x3c)=====
==CSS==


* Generates a nonce which is returned in command '''0x3d'''
===CSS SPU Module===


=====Get Host Nonce and Certificate (0x3d)=====
* DVD player on GameOS uses '''CssModule.spu.isoself''' (/dev_flash/bdplayer) to perform CSS authentication


* The data returned by this command is of size '''0x14 (Nonce) + 0x5c (Host Certificate)'''
====Commands====
* The data returned by this command is sent by BD player with SCSI command '''SEND KEY''' to BD drive during AACS authentication
* '''Host Certificate is easy to get from the SPU module, e.g. with aacs_module on OtherOS++'''
* The data contains a nonce, host public key and host certificate signature.


=====Set Drive Nonce and Certificate (0x3e)=====
* The SPU module supports max '''0x25''' commands but not all are implemented
* After a command is finished by the SPU module, it sends the status of the command to PPU through '''SPU Out Intr Mbox'''. Value 0 means success.


* Stores BD drive nonce and certificate in local memory of SPU
=====Generate Host Challenge Key (0x3)=====


=====Verify Drive Certificate (0x3f)=====
* Generates '''0x10''' bytes of '''host challenge key'''


=====Set Drive Key (0x40)=====
=====Set Drive Key1 (0x4)=====


=====Sign Host Key (0x44)=====
* Sends '''key1''' of size '''0x5''' returned by DVD drive to the SPU module


=====Get Host Key (0x45)=====
=====Set Drive Challenge Key (0x5)=====


=====Calculate Bus Key (0x46)=====
* Sends '''0x10''' bytes of '''drive challenge key''' to the SPU module


=====Set Volume ID (0x47)=====
=====Calculate Host Key2 (0x6)=====


* Sends volume id and its MAC to the SPU module
* Calculates '''key2''' of size '''0x5'''


=====Calculate Volume ID MAC (0x48)=====
=====Get Host Key2 (0x7)=====


* Calculates MAC of the passed volume id
* Returns '''key2''' of size '''0x5'''


=====Verify Volume ID MAC (0x49)=====
=====Set Disc Key (0x8)=====


* Verifies MAC of the passed volume id
* Sends Disc Key block of size '''0x800''' to the SPU module


=====Set PMSN (0x4a)=====
=====Decrypt Sector (0xc)=====


* Sends PMSN and its MAC to the SPU module
* Decrypts the passed sector


=====Calculate PMSN MAC (0x4b)=====
===CSS Salt===


* Calculates MAC of the passed PMSN
* It's NOT in clear text in the SPU module, it's obfuscated by xoring '''0xDEAF''' (SONY employees have a sense of humor).
* There are 2 bytes for every salt byte


=====Verify PMSN (0x4c)=====
Obfuscated:
<pre>
71 ED 3F A3 DA FE E4 94  40 8C
</pre>


* Verifies MAC of the passed PMSN
Clear text:
<pre>
F4  10  45  A3  E2
</pre>


=====Set Media ID (0x4d)=====
===PS3 DVD Player Key Index===


* Sends media id and its MAC to the SPU module
<pre>
0x69
</pre>


=====Calculate Media ID MAC (0x4e)=====
===Documentation===


* Calculates MAC of the passed media id
* The Content Scrambling System (CSS): [http://www.tinyted.net/eddie/css_auth.html]


=====Verify Media ID MAC (0x4f)=====
* Cryptanalysis of Contents Scrambling System: [http://www.cs.cmu.edu/~dst/DeCSS/FrankStevenson/analysis.html]


* Verifies MAC of the passed media id
* Cryptography in Home Entertainment: [http://www.math.ucsd.edu/~crypto/Projects/MarkBarry/index.htm]


=====Unknown (0x54)=====
* Patching DVD Firmware: [http://xvi.rpc1.org/Patching%20DVD%20firmware.pdf]


=====Verify Host/Drive Revocation (0x55)=====
==CPRM==


* BD player stores HRL/DRL list entries in XDR buffer and passes it to the SPU module for verification
===Commands===


=====Terminate Session (0xfefefeff)=====
* The SPU module supports '''0x13''' commands.


* AACS SPU module runs and processes commands as long as you need
===4C Secret Constant (S-Box)===
* After a command is complete, the SPU module waits for the next command
* This command terminates the current session and stops SPU module


===Drive Revocation List (DRL)===
===Documentation===


* SHA1 hash is encrypted/decrypted by '''SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)'''
* Cryptomeria C2 Specification: [http://edipermadi.files.wordpress.com/2008/08/cryptomeria-c2-spec.pdf]
* SHA1 hash is read with '''VTRM service 0x2005 (Retrieve)'''
* SHA1 hash is written with '''VTRM service 0x2003 (Store With Update)'''


===Content Revocation List (CRL)===
* Cryptoanalysis of C2: [http://caislab.kaist.ac.kr/lecture/2010/spring/cs548/advanced/A05.pdf]


* SHA1 hash is encrypted/decrypted by '''SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)'''
==Remarrying BD Drive on OtherOS++==
* SHA1 hash is read with '''VTRM service 0x2005 (Retrieve)'''
* SHA1 hash is written with '''VTRM service 0x2003 (Store With Update)'''


===Host Revocation List (HRL)===
===fdm_spu_module.self===


* Stored in BD drive flash
* This SPU module can create either P- or S-Block which are sent to BD drive
* It can be read/written with SCSI commands '''READ/WRITE BUFFER'''. Yeah, it can be written too :D
* '''EID2''' is passed to the SPU module
* A XDR memory buffer of size '''0x1000''' is passed to the SPU module
* 4 bytes at offset 0x10 of the XDR memory buffer contain the type of block which should be produced by the SPU module
* When the SPU module is finished, the XDR memory buffer contains the needed block
* After the S- and P-Blocks are produced by the SPU module, they are decrypted again but with '''DES''' (CBC mode, key length is 64 bits, initialization vector length is 64 bits) before they are sent to BD drive. S$ny cuts the header and the footer of 16 bytes each from final P- and S-blocks before sending them to drive.


====Read HRL from BD Drive Flash====
====Block types====


* It seems that BD drive has several HRL in its flash
{| class="wikitable"
* Empty HRL stored on BD drive flash can be read with SCSI command '''READ BUFFER''' by using as '''mode 0x2''', '''buffer id 0x4''' and '''allocation length 0x40'''
|-
* Current HRL stored on BD drive flash can be read with SCSI command '''READ BUFFER''' by using as '''mode 0x2''', '''buffer id 0x5'''
! Type !! Description !! BD Drive Buffer ID
|-
| 0x1 || P-Block || 0x2
|-
| 0x2 || S-Block || 0x3
|}


====Empty HRL====
===Remarrying===


<pre>
====Preparations====
# sg_read_buffer -m 2 -i 4 -o 0 -l $((0x40)) /dev/sr0
00    10 00 00 0c 00 03 10 03  00 00 00 01 21 00 00 34                   
10    00 00 00 00 00 00 00 00  1b 0b f2 6d 47 9e 77 62                   
20    3d 91 fc 78 b1 59 c9 52  ca a4 c7 41 85 24 96 64                   
30    8d 1d 95 8e 9b 84 c6 fa  4a dd 43 9b 42 98 fe ff 


# byte 0x21 at offset 0xc means Record Type HRL
* You will need '''ps3dm-utils''' and '''sg3-utils'''
* Dump your '''EID2''' from '''flash''' or with '''ps3dm-utils'''
* First create P- and S-Blocks from your EID2 with kernel module '''fdm_spu_module'''


# as you see this HRL is empty
====P-Block====


</pre>
Decrypted P-Block (and EID4) contains region settings (see below)


====Current HRL====
In decrypted P-Block(bytes 0x30 and 0x32) and in EID4(first byte) these bytes match [[Product Code]]:
{| class="wikitable sortable" style="font-size:small; border:2px ridge #999999;"
|-
! Hex !! bitflag !! [[Product Code]] !! Console Type !! Remarks
|-
| 0xFF || '''11111111''' || {{TID80}} || No BD playback on that [[Product Code]]
|-
| 0xFF || '''11111111''' || {{TID81}} || No BD playback on that [[Product Code]]
|-
| 0xFF || '''11111111''' || {{TID82}} || No BD playback on that [[Product Code]]
|-
| 0x01 || 0000000'''1''' || {{TID83}} || bit 0 (Region 0: Japan?)
|-
| 0x02 || 000000'''1'''0 || {{TID84}} || bit 1 (Region 1: USA & Canada, Bermuda, and US Territories)
|-
| 0x04 || 00000'''1'''00 || {{TID85}} || bit 2 (Region 2: Europe (with the exceptions of Russia, Ukraine, Belarus), South Africa, Swaziland, Middle East, Egypt, Lesotho, and Greenland)
|-
| 0x10 || 000'''1'''0000 || {{TID86}} || bit 4 (Region 3: Southeastern Asia)
|-
| 0x04 || 00000'''1'''00 || {{TID87}} || bit 2 (Region 2: Europe (with the exceptions of Russia, Ukraine, Belarus), South Africa, Swaziland, Middle East, Egypt, Lesotho, and Greenland)
|-
| 0x08 || 0000'''1'''000 || {{TID88}} || bit 3 (Region 4: Latin America and Australia)
|-
| 0x08 || 0000'''1'''000 || {{TID89}} || bit 3 (Region 4: Latin America and Australia)
|-
| 0x20 || 00'''1'''00000 || {{TID8A}} || bit 5 (Region 5: Russia, Asia (non-southeast), and Africa)
|-
| 0x10 || 000'''1'''0000 || {{TID8B}} || bit 4 (Region 3: Southeastern Asia)
|-
| 0x20 || 00'''1'''00000 || {{TID8C}} || bit 5 (Region 5: Russia, Asia (non-southeast), and Africa)
|-
| 0x40 || 0'''1'''000000 || {{TID8D}} || bit 6? (Region 6: China)
|-
| 0x10 || 000'''1'''0000 || {{TID8E}} || bit 4  (Region 3: Southeastern Asia)
|-
| 0x08 || 0000'''1'''000 || {{TID8F}} || bit 3 (Region 4: Latin America and Australia)
|-
| 0xFF || '''11111111''' || {{TIDA0}} || No BD playback on that [[Product Code]]
|-
|}


<pre>
=====Creating=====
# sg_read_buffer -m 2 -i 5 -o 0 -l $((0x7c)) /dev/sr0
 
00    10 00 00 0c 00 04 10 03  00 00 00 09 21 00 00 6c                   
=====Sending to BD Drive=====
10    00 00 00 07 00 00 00 07  00 09 ff ff 00 00 00 0b                   
20    00 00 ff ff 00 00 00 16  00 08 ff ff 00 00 00 21                   
30    00 03 ff ff 00 00 00 35  00 04 ff ff 00 00 00 4e                   
40    00 03 ff ff 00 00 00 54  00 03 ff ff 00 00 00 5e                   
50    80 93 3a 62 f5 5a 9c 8c  62 ce 7d b8 69 5d d7 b1                   
60    c3 0f 36 ff 96 a2 3b 32  cb cd 58 d4 12 c9 fd bf                   
70    f5 16 a6 4a 32 ba 60 f0  5d 71 74 10


# the current HRL is NOT empty and is from MKBv9 because the only BD movie i played on my PS3 has MKBv9
====S-Block====
</pre>


===PS3 BD Player Host Certificate===
=====Creating=====


<pre>
=====Sending to BD Drive=====
$ hexdump -C aacs_auth/ps3_host_cert.bin
00000000  02 01 00 5c ff ff 80 00  00 39 00 00 65 ea c9 87  |...\ÿÿ...9..eêÉ.|
00000010  8b 85 ef f4 d7 7a 62 b1  d6 00 02 4a ce 68 dd 33  |..ïô×zb±Ö..JÎhÝ3|
00000020  66 88 0e 4f 84 4f 34 b7  7a 05 01 35 a2 0e 73 b6  |f..O.O4·z..5¢.s¶|
00000030  26 da ea 51 57 b3 2e b8  4b c6 e8 7b 0d ee 4d 83  |&ÚêQW³.žKÆè{.îM.|
00000040  3c ea da 86 12 01 51 00  2c 3c 66 d5 25 6f 71 cf  |<êÚ...Q.,<fÕ%oqÏ|
00000050  a6 8b 7e 55 ba 1b 35 1f  34 03 43 4e              |Š.~Uº.5.4.CN|
0000005c


# Host ID is 0xffff80000039
====HRL====
</pre>


===PS3 BD Player Host Private Key===
=====Empty HRL=====


<pre>
=====Sending to BD Drive=====
$ hexdump -C aacs_auth/ps3_host_priv_key.bin
00000000  00 66 8c 9a 75 ee fc 8d  a4 26 19 38 e2 71 28 50  |.f..u....&.8.q(P|
00000010  61 bb 09 f0 dd                                    |a....|
</pre>


===AACS Processing Keys===
=VTRM=
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#VTRM gitbrew.org::HV#VTRM] <br />


====MKB v1====
==VTRM Services==


<pre>
===Store With Update (0x2003)===
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v1.inf


=MKB=
* Used by GameOS BD player to update DRL/CRL hashes
type:
0x00031003
version:
0x00000001


MKB U masks and UVs: 514
===Retrieve (0x2005)===


=applying subset-difference=
* '''Product mode''' is NOT required
index: 0
* '''0x40''' bytes of data are read from NOR flash, decrypted by SYSCON and returned to the caller
UV: 0x00000001
* Used e.g. by GameOS BD player to read SHA1 hashes of DRL and CRL
U mask: 0xff800000
V mask: 0xfffffffe


=applying device key=
====DRL and CRL Hashes====
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|


processing key:
* Written by GameOS BD player during DRL/CRL update
00000000: 09 f9 11 02 9d 74 e3 5b d8 41 56 c5 63 56 88 c0 |.....t.[.AV.cV..|
* Read by GameOS BD player
* Hashes are stored encrypted on NOR flash
* Encryption/decryption is done by SYSCON (SYSCON Manager)


C value:
Test with ps3dm-utils:
00000000: cb 06 90 db e6 54 55 7b 12 62 aa d7 89 f4 9d 92 |.....TU{.b......|
<pre>
# mount dev_flash3


media key:
glevand@debian-hdd:~/ps3dm-utils$ sudo mount /dev/ps3vflashe /mnt
00000000: b4 6c 48 5e f7 51 ae 29 ef 87 bc 58 28 f3 2a 8d |.lH^.Q.)...X(.*.|


=MKB verify media key data=
# DRL SHA1 hash
encrypted:
00000000: 46 32 5b 42 48 b4 86 5a fc ef 75 25 47 b1 b5 12 |F2[BH..Z..u%G...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 0d ac 14 b9 ee f4 bd cc |.#Eg............|
</pre>


====MKB v3====
glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/drl/DRL1
8f0652bc6162a240362f90f1b2e5405bb82ee502  /mnt/data-revoke/drl/DRL1


<pre>
# CRL SHA1 hash
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v3.inf


=MKB=
glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/crl/CRL1
type:
96791f41f9a76f4d895dd5820db108ec03d19250  /mnt/data-revoke/crl/CRL1
0x00031003
version:
0x00000003


MKB U masks and UVs: 528
# Retrieve DRL and CRL SHA1 hashes from VTRM
# DRL hash is first and then follows CRL hash


=applying subset-difference=
glevand@debian-hdd:~/ps3dm-utils$ sudo ./ps3dm_vtrm -l 0x0 -p 0x1070000034000001 /dev/ps3dmproxy retrieve 0
index: 14
0x8f 0x06 0x52 0xbc 0x61 0x62 0xa2 0x40 0x36 0x2f 0x90 0xf1 0xb2 0xe5 0x40 0x5b 0xb8 0x2e 0xe5 0x02
UV: 0x00000080
0x96 0x79 0x1f 0x41 0xf9 0xa7 0x6f 0x4d 0x89 0x5d 0xd5 0x82 0x0d 0xb1 0x08 0xec 0x03 0xd1 0x92 0x50
U mask: 0xff800000
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
V mask: 0xffffff00
</pre>


=applying device key=
===Backup Flash (0x2012)===
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|


processing key:
* Requires enabled '''product mode''' or else service returns always error '''0x5'''
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
* Reads and returns data from NOR flash beginning at NOR flash offset '''0xec0000'''


C value:
Test with ps3dm-utils:
00000000: 0a b7 33 82 85 62 91 d1 91 4a 95 9e 36 18 c7 a1 |..3..b...J..6...|
<pre>
# enable product mode
 
# ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0
/dev/ps3dmproxy: SS retval 0
 
# ps3dm_vtrm /dev/ps3dmproxy  backup_flash 0 0x200 | hexdump -C
00000000 53 43 45 49 ff ff ff ff  ff ff ff ff ff ff ff ff  |SCEIÿÿÿÿÿÿÿÿÿÿÿÿ|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|
*
00000200


media key:
# dd if=/dev/ps3nflasha bs=1 count=$((0x100)) skip=$((0xec0000)) | hexdump -C
00000000: 6e da eb d4 88 aa 38 58 74 26 35 fd fd 36 66 d5 |n.....8Xt&5..6f.|
00000000 53 43 45 49 ff ff ff ff  ff ff ff ff ff ff ff ff  |SCEIÿÿÿÿÿÿÿÿÿÿÿÿ|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|
*


=MKB verify media key data=
encrypted:
00000000: 99 76 96 b0 6f 49 37 9b c4 b9 2b be 73 ce 96 1a |.v..oI7...+.s...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef fb 01 cc 85 eb e5 bf 0a |.#Eg............|
</pre>
</pre>


====MKB v4====
===Flash Address Size (0x2016)===
 
* Requires enabled '''product mode''' or else service returns always error '''0x5'''
* Returns 2 64bit values: offset and size of NOR flash region


Test with ps3dm-utils:
<pre>
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v4.inf
# ps3dm_um /dev/ps3dmproxy read_eprom 0x48c07
0xff


=MKB=
# ps3dm_vtrm /dev/ps3dmproxy  flash_addr_size
type:
/dev/ps3dmproxy: SS retval 5
0x00031003
version:
0x00000004


MKB U masks and UVs: 526
# enable product mode


=applying subset-difference=
# ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0
index: 12
/dev/ps3dmproxy: SS retval 0
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


=applying device key=
# ps3dm_um /dev/ps3dmproxy read_eprom 0x48c07
index: 244
0x00
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|


processing key:
# ps3dm_vtrm /dev/ps3dmproxy  flash_addr_size
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
0x0000000000000000 0x0000000000040000
</pre>


C value:
=Revoke List=
00000000: bf 71 0c 8b 46 a0 24 d8 f0 3a a1 26 37 9d fb fc |.q..F.$..:.&7...|
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#Revoke_List gitbrew::Revoke List]<br />


media key:
==LPAR 1 System Call 0x1004A==
00000000: ef 18 c0 dd bf 02 32 a1 2f 57 f7 65 79 2c 1c 58 |......2./W.ey,.X|


=MKB verify media key data=
* Installs new revoke list in LV1
encrypted:
* LPAR 1 processes can use this syscall to install new revoke lists at runtime
00000000: 54 85 08 a9 6a 70 2a c9 32 e3 74 a6 55 78 6c 01 |T...jp*.2.t.Uxl.|
* '''lv2ldr''' is loaded by LV1 and used to verify the passed revoke list
decrypted:
* After lv2ldr is done verifying the passed revoke list, it checks for stop code and if it's '''0xB''' then LV1 replaces the old revoke list with the new one
00000000: 01 23 45 67 89 ab cd ef da 90 cf 2a e5 b2 6c 45 |.#Eg.......*..lE|
* If the verification of the revoke list was successfull then LV1 installs new revoke list and replaces the old one in the ISO loader table at address '''0x10100'''
</pre>


====MKB v7====
==rvk_list_verifier==


<pre>
* Stop code 0xB means that the passed revoke list is valid.
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v7.inf
 
=MKB=
type:
0x00031003
version:
0x00000007
 
MKB U masks and UVs: 526
 
=applying subset-difference=
index: 7
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00
 
=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|
 
processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
 
C value:
00000000: 21 fd c9 4b 3e 1a f3 fe 9e b4 7a e6 ef 01 75 1b |!..K>.....z...u.|
 
media key:
00000000: af cd e2 c8 67 12 a4 b6 a8 58 0c 15 ef 07 6e f8 |....g....X....n.|
 
=MKB verify media key data=
encrypted:
00000000: 4b 21 29 a5 0f db 96 bc bc 01 04 71 42 79 00 e5 |K!)........qBy..|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 4e f9 d2 05 6e 19 c1 79 |.#Eg....N...n..y|
</pre>
 
====MKB v8====


<pre>
<pre>
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v8.inf
root@debian-hdd:/home/glevand/rvk_list_verifier# cat /proc/rvk_list_verifier/debug
 
PPE id (0x0000000000000001) VAS id (0x0000000000000002)
=MKB=
lv1_construct_logical_spe (0x00000000)
type:
SPE id (0x0000000000000033)
0x00031003
lv1_enable_logical_spe (0x00000000)
version:
lv1_set_spe_interrupt_mask(0) (0x00000000)
0x00000008
lv1_set_spe_interrupt_mask(1) (0x00000000)
 
lv1_set_spe_interrupt_mask(2) (0x00000000)
MKB U masks and UVs: 523
lv1_set_spe_privilege_state_area_1_register (0x00000000)
 
ea (0xc000000003f40000) esid (0xc000000008000000) vsid (0x0000408f92c94500)
=applying subset-difference=
lv1_get_spe_interrupt_status(0) (0x00000000)
index: 4
lv1_get_spe_interrupt_status(1) (0x00000000)
UV: 0x00000080
lv1_get_spe_interrupt_status(2) (0x00000000)
U mask: 0xff800000
sleep
V mask: 0xffffff00
lv1_get_spe_interrupt_status(0) (0x00000000)
 
lv1_get_spe_interrupt_status(1) (0x00000000)
=applying device key=
lv1_get_spe_interrupt_status(2) (0x00000000)
index: 244
out interrupt mbox (0x0000000000000001)
UV: 0x00000100
lv1_clear_spe_interrupt_status(2) (0x00000000)
U mask: 0xff800000
transferring ldr args to LS
V mask: 0xfffffe00
waiting until MFC transfers are finished
device key:
MFC transfers done
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|
out mbox (0x00000001)
 
sleep
processing key:
lv1_get_spe_interrupt_status(0) (0x00000000)
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
lv1_get_spe_interrupt_status(1) (0x00000000)
 
lv1_get_spe_interrupt_status(2) (0x00000000)
C value:
problem status (0x000b0082)
00000000: 73 2d 10 bd f8 b4 87 e2 86 a6 d5 3a 6d db 69 15 |s-.........:m.i.|
lv1_destruct_logical_spe (0x00000000)
 
media key:
00000000: dd 46 d4 0d 26 54 5a ce 6c 59 0c 65 b7 2b 3a 9f |.F..&TZ.lY.e.+:.|
 
=MKB verify media key data=
encrypted:
00000000: c6 f6 f9 54 ce 90 e0 5e 2b 3b e4 1e 24 92 90 b2 |...T...^+;..$...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 97 e6 61 8b d1 69 3e a0 |.#Eg......a..i>.|
</pre>
 
====MKB v9====
 
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v9.inf
 
=MKB=
type:
0x00031003
version:
0x00000009
 
MKB U masks and UVs: 520
 
=applying subset-difference=
index: 2
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00
 
=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|
 
processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
 
C value:
00000000: a4 5a c6 87 43 49 70 bb bf 0c 22 52 83 9e 2a f6 |.Z..CIp..."R..*.|
 
media key:
00000000: 37 02 bd fc 96 dc a2 18 2e 55 b0 79 6d ad 36 6b |7........U.ym.6k|
 
=MKB verify media key data=
encrypted:
00000000: 4d 5b 7b 9c 5d ee 55 a6 94 de e1 db 8d 08 c7 a2 |M[{.].U.........|
decrypted:
00000000: 01 23 45 67 89 ab cd ef cd 1d a8 8a 42 5a 10 43 |.#Eg........BZ.C|
</pre>
 
====MKB v10====
 
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v10.inf
 
=MKB=
type:
0x00031003
version:
0x0000000a
 
MKB U masks and UVs: 522
 
=applying subset-difference=
index: 3
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00
 
=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|
 
processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
 
C value:
00000000: d4 77 dd 1a 8a 5c 6d d1 dd 31 2d af f7 d3 14 fa |.w...\m..1-.....|
 
media key:
00000000: 38 32 2b 3c 61 b0 35 b4 52 89 84 59 f4 7a 76 e6 |82+<a.5.R..Y.zv.|
 
=MKB verify media key data=
encrypted:
00000000: 3f d3 d5 fb 42 37 d9 05 b8 db 6b 03 a0 fe 2e 48 |?...B7....k....H|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 65 b1 87 8c eb 0d 60 0f |.#Eg....e.....`.|
</pre>
 
====MKB v12====
 
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v12.inf
 
=MKB=
type:
0x00031003
version:
0x0000000c
 
MKB U masks and UVs: 522
 
=applying subset-difference=
index: 3
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00
 
=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|
 
processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
 
C value:
00000000: 89 75 89 e6 6f 4a de 95 11 32 57 6a cb 99 dd 69 |.u..oJ...2Wj...i|
 
media key:
00000000: 4b dd 69 9d 32 98 d7 b0 ad 32 71 6b 3d 9c e3 c2 |K.i.2....2qk=...|
 
=MKB verify media key data=
encrypted:
00000000: 8d 43 fd f2 15 fa 58 78 64 db 25 46 62 ab 02 30 |.C....Xxd.%Fb..0|
decrypted:
00000000: 01 23 45 67 89 ab cd ef e6 1c bf 98 45 82 64 d9 |.#Eg........E.d.|
</pre>
 
====MKB v14====
 
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v14.inf
 
=MKB=
type:
0x00031003
version:
0x0000000e
 
MKB U masks and UVs: 526
 
=applying subset-difference=
index: 6
UV: 0x00000248
U mask: 0xfffffe00
V mask: 0xfffffff0
 
=applying device key=
index: 28
UV: 0x00000280
U mask: 0xfffffe00
V mask: 0xffffff00
device key:
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...|
 
processing key:
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|
 
C value:
00000000: 8c 7e 31 e8 15 17 7e c3 2c 67 b7 cc 87 e9 39 c3 |.~1...~.,g....9.|
 
media key:
00000000: 4b b1 31 d1 6e 0e 86 45 89 07 a2 68 91 c4 e5 38 |K.1.n..E...h...8|
 
=MKB verify media key data=
encrypted:
00000000: 20 03 8c 70 7d ab d0 6f ba 86 39 f0 31 26 86 5f | ..p}..o..9.1&._|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 27 9f e5 35 0b df 3d a5 |.#Eg....'..5..=.|
</pre>
 
====MKB v15====
 
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v15.inf
 
=MKB=
type:
0x00031003
version:
0x0000000f
 
MKB U masks and UVs: 527
 
=applying subset-difference=
index: 6
UV: 0x00000248
U mask: 0xfffffe00
V mask: 0xfffffff0
 
=applying device key=
index: 28
UV: 0x00000280
U mask: 0xfffffe00
V mask: 0xffffff00
device key:
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...|
 
processing key:
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|
 
C value:
00000000: 75 da 59 cf 0d c2 c0 95 86 fc 6b 8e 2e e9 cc 85 |u.Y.......k.....|
 
media key:
00000000: 28 46 25 38 3d cc 4f 1f 90 be 7d f7 8a ba 7b fd |(F%8=.O...}...{.|
 
=MKB verify media key data=
encrypted:
00000000: 8d d2 69 e0 b7 6a 44 53 03 ad ef 58 44 fc a7 d7 |..i..jDS...XD...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef ff 6a 7d c3 17 bb 19 11 |.#Eg.....j}.....|
</pre>
====MKB v16====
 
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v16.inf
 
=MKB=
type:
0x00031003
version:
0x00000010
 
MKB U masks and UVs: 531
 
=applying subset-difference=
index: 5
UV: 0x00000248
U mask: 0xfffffe00
V mask: 0xfffffff0
 
=applying device key=
index: 28
UV: 0x00000280
U mask: 0xfffffe00
V mask: 0xffffff00
device key:
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...|
 
processing key:
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|
 
C value:
00000000: f8 49 9b d1 32 f9 6e 8d 33 98 35 a8 54 80 d9 fe |.I..2.n.3.5.T...|
 
media key:
00000000: 3a bf bf d7 7e b8 01 43 a9 3c 15 3f ba 47 8c e1 |:...~..C.<.?.G..|
 
=MKB verify media key data=
encrypted:
00000000: 8a 67 86 b6 9d 0d 22 dd 5d c2 88 1f 08 f3 ab b4 |.g....".].......|
decrypted:
00000000: 01 23 45 67 89 ab cd ef d6 32 1f 17 c4 2f e2 4a |.#Eg.....2.../.J|
</pre>
 
====MKB v17====
 
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v17.inf
 
=MKB=
type:
0x00031003
version:
0x00000011
 
MKB U masks and UVs: 540
 
=applying subset-difference=
index: 14
UV: 0x00000308
U mask: 0xffffff00
V mask: 0xfffffff0
 
=applying device key=
index: 21
UV: 0x00000340
U mask: 0xffffff00
V mask: 0xffffff80
device key:
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss|
 
processing key:
00000000: 46 5f a8 be 82 85 09 01 4d 05 d2 fc ce ff 35 d2 |F_......M.....5.|
 
C value:
00000000: 01 f7 54 0b 34 e8 c1 ce 63 8d ea fa bc ce 6e 7b |..T.4...c.....n{|
 
media key:
00000000: ef 63 4e a8 ca 06 d1 6a c7 21 65 1b 18 b3 04 c6 |.cN....j.!e.....|
 
=MKB verify media key data=
encrypted:
00000000: d3 b9 d4 9c b6 94 47 d5 3d cc 42 fe 3e 47 40 04 |......G.=.B.>G@.|
decrypted:
00000000: 01 23 45 67 89 ab cd ef f6 b4 c8 6a b7 b8 39 fc |.#Eg.......j..9.|
</pre>
 
====MKB v18====
 
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v18.inf
 
=MKB=
type:
0x00031003
version:
0x00000012
 
MKB U masks and UVs: 543
 
=applying subset-difference=
index: 17
UV: 0x00000320
U mask: 0xffffff00
V mask: 0xffffffc0
 
=applying device key=
index: 21
UV: 0x00000340
U mask: 0xffffff00
V mask: 0xffffff80
device key:
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss|
 
processing key:
00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..|
 
C value:
00000000: 7a 8f 03 41 27 c4 86 58 05 37 3a 90 de f8 de 26 |z..A'..X.7:....&|
 
media key:
00000000: e3 ed cd b4 59 b4 12 d4 ae f9 4d 8e 78 7a cd 7d |....Y.....M.xz.}|
 
=MKB verify media key data=
encrypted:
00000000: ea 45 fa 35 65 70 56 6f 6a 86 65 ad 52 e7 71 a4 |.E.5epVoj.e.R.q.|
decrypted:
00000000: 01 23 45 67 89 ab cd ef bd 36 f9 ce 60 54 80 3c |.#Eg.....6..`T.<|
</pre>
 
====MKB v19====
 
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v19.inf
 
=MKB=
type:
0x00031003
version:
0x00000013
 
MKB U masks and UVs: 544
 
=applying subset-difference=
index: 17
UV: 0x00000320
U mask: 0xffffff00
V mask: 0xffffffc0
 
=applying device key=
index: 21
UV: 0x00000340
U mask: 0xffffff00
V mask: 0xffffff80
device key:
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss|
 
processing key:
00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..|
 
C value:
00000000: b9 0b 55 d1 18 3c cc 80 20 1c 9f 26 c3 58 27 18 |..U..<.. ..&.X'.|
 
media key:
00000000: 75 a9 79 9c 67 50 13 89 98 62 34 5b eb 54 34 dd |u.y.gP...b4[.T4.|
 
=MKB verify media key data=
encrypted:
00000000: c4 f0 ce 75 1b 12 b9 f0 22 2f 31 70 66 a9 6a b8 |...u...."/1pf.j.|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 66 5c 65 d3 c4 4c c7 b0 |.#Eg....f\e..L..|
</pre>
 
====MKB v20====
 
<pre>
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v20.inf
 
=MKB=
type:
0x00031003
version:
0x00000014
 
MKB U masks and UVs: 544
 
=applying subset-difference=
index: 19
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8
 
=applying device key=
index: 18
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8
device key:
00000000: fb 4a c3 90 09 e8 21 13 d4 5e cf 4b 7e ae a4 67 |.J....!..^.K~..g|
 
processing key:
00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k|
 
C value:
00000000: 10 9f f1 69 36 07 7d 7e ad 8f d2 1a 28 c5 09 ed |...i6.}~....(...|
 
media key:
00000000: dc 9f 08 f7 cb 1b f8 c4 cf 96 4e 96 df 23 56 58 |..........N..#VX|
 
=MKB verify media key data=
encrypted:
00000000: 18 ca f5 51 8f 36 ef 2f 7a 49 78 ff 54 40 a5 f1 |...Q.6./zIx.T@..|
decrypted:
00000000: 01 23 45 67 89 ab cd ef c5 5d 11 08 c3 26 db 48 |.#Eg.....]...&.H|
</pre>
 
====MKB v21====
 
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v21.inf
 
=MKB=
type:
0x00031003
version:
0x00000015
 
MKB U masks and UVs: 552
 
=applying subset-difference=
index: 19
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8
 
=applying device key=
index: 18
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8
device key:
00000000: fb 4a c3 90 09 e8 21 13 d4 5e cf 4b 7e ae a4 67 |.J....!..^.K~..g|
 
processing key:
00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k|
 
C value:
00000000: c0 0c fa bf f0 fe f2 32 77 19 db c4 d8 f8 60 c9 |.......2w.....`.|
 
media key:
00000000: 55 83 aa 69 ff 52 16 83 c2 93 b3 48 03 2a 57 38 |U..i.R.....H.*W8|
 
=MKB verify media key data=
encrypted:
00000000: 12 5b f2 75 c8 f8 05 6b 4f 31 a5 ea 4a 12 9f a9 |.[.u...kO1..J...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef dc 46 45 b4 79 8d 4f 68 |.#Eg.....FE.y.Oh|
</pre>
 
====MKB v23====
 
<pre>
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v23.inf
 
=MKB=
type:
0x00031003
version:
0x00000017
 
MKB U masks and UVs: 556
 
=applying subset-difference=
index: 17
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8
 
=applying device key=
index: 7
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8
device key:
00000000: 8b f4 fb d9 1a 7f b7 db 85 76 d1 e5 a1 5a 85 44 |.........v...Z.D|
 
processing key:
00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr|
 
C value:
00000000: f0 81 d4 93 aa b5 01 1a a7 ff 8e 18 8a 48 8a 2d |.............H.-|
 
media key:
00000000: 02 04 59 d0 7c b5 54 94 bf 46 9b 98 91 1e 43 1f |..Y.|.T..F....C.|
 
=MKB verify media key data=
encrypted:
00000000: 24 a1 27 f9 30 70 25 67 07 2f 2a d4 13 89 0d aa |$.'.0p%g./*.....|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 21 00 20 84 c4 5f 36 78 |.#Eg....!. .._6x|
</pre>
 
====MKB v25====
 
<pre>
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v25.inf
 
=MKB=
type:
0x00031003
version:
0x00000019
 
MKB U masks and UVs: 564
 
=applying subset-difference=
index: 13
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8
 
=applying device key=
index: 7
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8
device key:
00000000: 8b f4 fb d9 1a 7f b7 db 85 76 d1 e5 a1 5a 85 44 |.........v...Z.D|
 
processing key:
00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr|
 
C value:
00000000: 19 62 23 7d 81 01 c2 55 2f 36 20 1b 3e 69 40 10 |.b#}...U/6 .>i@.|
 
media key:
00000000: 70 b5 9f 35 86 5d 18 73 bb 80 c3 2b f7 41 f6 14 |p..5.].s...+.A..|
 
=MKB verify media key data=
encrypted:
00000000: 89 be 1e 1e b1 93 4c f2 2d ac c3 ce ed 10 07 f0 |......L.-.......|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 3f 5d 87 7a 88 09 db c4 |.#Eg....?].z....|
</pre>
 
===Documentation===
 
* SCSI Specification: [http://www.13thmonkey.org/documentation/SCSI/]
* AACS Specification Common: [http://www.aacsla.com/specifications/specs091/AACS_Spec_Common_0.91.pdf]
* AACS Specification Pre-recorded Video Book [http://www.aacsla.com/specifications/AACS_Spec_Prerecorded_Final_0.951.pdf]
* AACS Tutorial: [http://forum.doom9.org/showthread.php?t=122363]
* AACS Overview: [http://www.cacr.math.uwaterloo.ca/techreports/2007/cacr2007-25.pdf]
 
==CSS==
 
===CSS SPU Module===
 
* DVD player on GameOS uses '''CssModule.spu.isoself''' (/dev_flash/bdplayer) to perform CSS authentication
 
====Commands====
 
* The SPU module supports max '''0x25''' commands but not all are implemented
* After a command is finished by the SPU module, it sends the status of the command to PPU through '''SPU Out Intr Mbox'''. Value 0 means success.
 
=====Generate Host Challenge Key (0x3)=====
 
* Generates '''0x10''' bytes of '''host challenge key'''
 
=====Set Drive Key1 (0x4)=====
 
* Sends '''key1''' of size '''0x5''' returned by DVD drive to the SPU module
 
=====Set Drive Challenge Key (0x5)=====
 
* Sends '''0x10''' bytes of '''drive challenge key''' to the SPU module
 
=====Calculate Host Key2 (0x6)=====
 
* Calculates '''key2''' of size '''0x5'''
 
=====Get Host Key2 (0x7)=====
 
* Returns '''key2''' of size '''0x5'''
 
=====Set Disc Key (0x8)=====
 
* Sends Disc Key block of size '''0x800''' to the SPU module
 
=====Decrypt Sector (0xc)=====
 
* Decrypts the passed sector
 
===CSS Salt===
 
* It's NOT in clear text in the SPU module, it's obfuscated by xoring '''0xDEAF''' (SONY employees have a sense of humor).
* There are 2 bytes for every salt byte
 
Obfuscated:
<pre>
71 ED 3F A3 DA FE E4 94  40 8C
</pre>
 
Clear text:
<pre>
F4  10  45  A3  E2
</pre>
 
===PS3 DVD Player Key Index===
 
<pre>
0x69
</pre>
 
===Documentation===
 
* The Content Scrambling System (CSS): [http://www.tinyted.net/eddie/css_auth.html]
 
* Cryptanalysis of Contents Scrambling System: [http://www.cs.cmu.edu/~dst/DeCSS/FrankStevenson/analysis.html]
 
* Cryptography in Home Entertainment: [http://www.math.ucsd.edu/~crypto/Projects/MarkBarry/index.htm]
 
* Patching DVD Firmware: [http://xvi.rpc1.org/Patching%20DVD%20firmware.pdf]
 
==CPRM==
 
===Commands===
 
* The SPU module supports '''0x13''' commands.
 
===4C Secret Constant (S-Box)===
 
===Documentation===
 
* Cryptomeria C2 Specification: [http://edipermadi.files.wordpress.com/2008/08/cryptomeria-c2-spec.pdf]
 
* Cryptoanalysis of C2: [http://caislab.kaist.ac.kr/lecture/2010/spring/cs548/advanced/A05.pdf]
 
==Remarrying BD Drive on OtherOS++==
 
===fdm_spu_module.self===
 
* This SPU module can create either P- or S-Block which are sent to BD drive
* '''EID2''' is passed to the SPU module
* A XDR memory buffer of size '''0x1000''' is passed to the SPU module
* 4 bytes at offset 0x10 of the XDR memory buffer contain the type of block which should be produced by the SPU module
* When the SPU module is finished, the XDR memory buffer contains the needed block
* After the S- and P-Blocks are produced by the SPU module, they are encrypted with '''DES''' before they are sent to BD drive
 
====Block types====
 
{| class="wikitable"
|-
! Type !! Description !! BD Drive Buffer ID
|-
| 0x1 || P-Block || 0x2
|-
| 0x2 || S-Block || 0x3
|}
 
===Remarrying===
 
====Preparations====
 
* You will need '''ps3dm-utils''' and '''sg3-utils'''
* Dump your '''EID2''' from '''flash''' or with '''ps3dm-utils'''
* First create P- and S-Blocks from your EID2 with kernel module '''fdm_spu_module'''
 
====P-Block====
 
=====Creating=====
 
=====Sending to BD Drive=====
 
====S-Block====
 
=====Creating=====
 
=====Sending to BD Drive=====
 
====HRL====
 
=====Empty HRL=====
 
=====Sending to BD Drive=====
 
=VTRM=
Crossreference: [http://portal.gitbrew.org/wikibrew/PS3:HvReverseEngineering#VTRM gitbrew.org::HV#VTRM] <br />
 
==VTRM Services==
 
===Store With Update (0x2003)===
 
* Used by GameOS BD player to update DRL/CRL hashes
 
===Retrieve (0x2005)===
 
* '''Product mode''' is NOT required
* '''0x40''' bytes of data are read from NOR flash, decrypted by SYSCON and returned to the caller
* Used e.g. by GameOS BD player to read SHA1 hashes of DRL and CRL
 
====DRL and CRL Hashes====
 
* Written by GameOS BD player during DRL/CRL update
* Read by GameOS BD player
* Hashes are stored encrypted on NOR flash
* Encryption/decryption is done by SYSCON (SYSCON Manager)
 
Test with ps3dm-utils:
<pre>
# mount dev_flash3
 
glevand@debian-hdd:~/ps3dm-utils$ sudo mount /dev/ps3vflashe /mnt
 
# DRL SHA1 hash
 
glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/drl/DRL1
8f0652bc6162a240362f90f1b2e5405bb82ee502  /mnt/data-revoke/drl/DRL1
 
# CRL SHA1 hash
 
glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/crl/CRL1
96791f41f9a76f4d895dd5820db108ec03d19250  /mnt/data-revoke/crl/CRL1
 
# Retrieve DRL and CRL SHA1 hashes from VTRM
# DRL hash is first and then follows CRL hash
 
glevand@debian-hdd:~/ps3dm-utils$ sudo ./ps3dm_vtrm -l 0x0 -p 0x1070000034000001 /dev/ps3dmproxy retrieve 0
0x8f 0x06 0x52 0xbc 0x61 0x62 0xa2 0x40 0x36 0x2f 0x90 0xf1 0xb2 0xe5 0x40 0x5b 0xb8 0x2e 0xe5 0x02
0x96 0x79 0x1f 0x41 0xf9 0xa7 0x6f 0x4d 0x89 0x5d 0xd5 0x82 0x0d 0xb1 0x08 0xec 0x03 0xd1 0x92 0x50
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
</pre>
 
===Backup Flash (0x2012)===
 
* Requires enabled '''product mode''' or else service returns always error '''0x5'''
* Reads and returns data from NOR flash beginning at NOR flash offset '''0xec0000'''
 
Test with ps3dm-utils:
<pre>
# enable product mode
 
# ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0
/dev/ps3dmproxy: SS retval 0
 
# ps3dm_vtrm /dev/ps3dmproxy  backup_flash 0 0x200 | hexdump -C
00000000  53 43 45 49 ff ff ff ff  ff ff ff ff ff ff ff ff  |SCEIÿÿÿÿÿÿÿÿÿÿÿÿ|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|
*
00000200
 
# dd if=/dev/ps3nflasha bs=1 count=$((0x100)) skip=$((0xec0000)) | hexdump -C
00000000  53 43 45 49 ff ff ff ff  ff ff ff ff ff ff ff ff  |SCEIÿÿÿÿÿÿÿÿÿÿÿÿ|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|
*
 
</pre>
 
===Flash Address Size (0x2016)===
 
* Requires enabled '''product mode''' or else service returns always error '''0x5'''
* Returns 2 64bit values: offset and size of NOR flash region
 
Test with ps3dm-utils:
<pre>
# ps3dm_um /dev/ps3dmproxy read_eprom 0x48c07
0xff
 
# ps3dm_vtrm /dev/ps3dmproxy  flash_addr_size
/dev/ps3dmproxy: SS retval 5
 
# enable product mode
 
# ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0
/dev/ps3dmproxy: SS retval 0
 
# ps3dm_um /dev/ps3dmproxy read_eprom 0x48c07
0x00
 
# ps3dm_vtrm /dev/ps3dmproxy  flash_addr_size
0x0000000000000000 0x0000000000040000
</pre>
</pre>


=Revoke List=
Crossreference: [http://portal.gitbrew.org/wikibrew/PS3:HvReverseEngineering#Revoke_List gitbrew::Revoke List]<br />
==LPAR 1 System Call 0x1004A==
* Installs new revoke list in LV1
* LPAR 1 processes can use this syscall to install new revoke lists at runtime
* '''lv2ldr''' is loaded by LV1 and used to verify the passed revoke list
* After lv2ldr is done verifying the passed revoke list, it checks for stop code and if it's '''0xB''' then LV1 replaces the old revoke list with the new one
* If the verification of the revoke list was successfull then LV1 installs new revoke list and replaces the old one in the ISO loader table at address '''0x10100'''
==rvk_list_verifier==


* Stop code 0xB means that the passed revoke list is valid.
{{Reverse engineering}}<noinclude>[[Category:Main]]</noinclude>
 
<pre>
root@debian-hdd:/home/glevand/rvk_list_verifier# cat /proc/rvk_list_verifier/debug
PPE id (0x0000000000000001) VAS id (0x0000000000000002)
lv1_construct_logical_spe (0x00000000)
SPE id (0x0000000000000033)
lv1_enable_logical_spe (0x00000000)
lv1_set_spe_interrupt_mask(0) (0x00000000)
lv1_set_spe_interrupt_mask(1) (0x00000000)
lv1_set_spe_interrupt_mask(2) (0x00000000)
lv1_set_spe_privilege_state_area_1_register (0x00000000)
ea (0xc000000003f40000) esid (0xc000000008000000) vsid (0x0000408f92c94500)
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
out interrupt mbox (0x0000000000000001)
lv1_clear_spe_interrupt_status(2) (0x00000000)
transferring ldr args to LS
waiting until MFC transfers are finished
MFC transfers done
out mbox (0x00000001)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
problem status (0x000b0082)
lv1_destruct_logical_spe (0x00000000)
</pre>

Latest revision as of 09:39, 4 October 2024

Warning, this page is way too long and is voted to be split into seperate sections


Difference between Debug Firmware HV and Retail HV[edit | edit source]

There is no difference between debug firmware lv1.self and retail firmware lv1.self The differences reside on the repository nodes loaded because of policies/flags.

DECR/Tool + CEX/Retail LV1.self 1.60 1.92 3.41 3.55

HSPRG[edit | edit source]

  • The hypervisor stores a pointer to some structures per LPAR in HSPRG0 register.
  • There are actually 2 HSPRG0 values: one for each thread of Cell CPU !!!
  • There is a HSPRG0 array at 0x8(-0x69A0(HSPRG0)) + 0x20.

LPAR[edit | edit source]

LPAR = Logical Partition

lpar1 starts at 0x<unknown>, and it's believed to be the memory space where lv1 stores its variables, flags and other data.

lpar2 starts at 0x80000000000 and it's believed to be the memory space where lv2 stores its variables, flags and other data.


The pointer to active LPAR is stored at -0x67E8(HSPRG0).

vtable[edit | edit source]

0x0033CA40 (3.15)

Member variables[edit | edit source]

offset 0x38 - some pointer

offset 0x50 - LPAR id (8 bytes)

offset 0x70 - pointer to VAS id bitmap

offset 0x78 - power of 2 of word size from VAS id bitmap (4 bytes), equal to 6

offset 0x7C - number of 64-bit words in VAS id bitmap(4 bytes)

Interrupt handling[edit | edit source]

The pointer to the interrupt handler that is called e.g. when an external interrupt occurs is at -0x69F0(HSPRG0).

0x00001930 (3.15 and 2.60)

Interrupt vector tables[edit | edit source]

There are 2 interrupt vector tables. One for each thread. The pointer to these tables is at -0x6950(HSPRG0).

offset 0x8 - IIC memory base address (8 bytes)

offset 0x10 - thread register offset (8 bytes)

offset 0x18 - start of interrupt vector table (19 entries, each entry 32 bytes)

Interrupt vector table entry[edit | edit source]

offset 0x0 - pointer to interrupt handler

offset 0x8 - TOC

offset 0x10 - 0

offset 0x18 - parameter to interrupt handler

Interrupt handlers[edit | edit source]

Spurious interrupt handler[edit | edit source]

0x002BC174 (3.15)

RSX[edit | edit source]

0x00219A44 (3.15)

0x002176FC (2.60)

SB bus[edit | edit source]

0x002B9CC4 (3.15)

I/O address translation[edit | edit source]

0x002CD7D8 (3.15)

0x002C9214 (2.60)

Performance monitor[edit | edit source]

0x002F0584 (3.15)

0x002EB1B0 (2.60)

Token manager[edit | edit source]

0x002BBA9C (3.15)

0x002B754C (2.60)

HV call[edit | edit source]

  • The address of HV table is stored at -0x6FC8(HSPRG0).
  • The address of HV table size is stored at -0x6FD0(HSPRG0).

HV call[edit | edit source]

editorial note: The table listed here was moved/merged to a seperate page : HV Syscalls

Initializing HV Call Table[edit | edit source]

set_lv1_hvcall_table[edit | edit source]

0x002C02B4 (3.41)

0x002C1F04 (3.15)

0x002C2B4C (3.55)

This function sets pointer to HV Call Table and the size of HV Call Table in HSPRG context of a LPAR.

set_lv1_hvcall_table_entry_invalid[edit | edit source]

0x002C1F28 (3.15)

0x002C02D8 (3.41)

0x002C2B70 (3.55)

This function initializes an entry in HV Call Table to the Invalid HV Call function.

Memory HV call[edit | edit source]

  • All memory HV calls branch to lv1_mm_call
  • lv1_mm_call has it's own function table
  • Memory HV call number = HV call number

Memory HV call table[edit | edit source]

  • Each entry is a pointer to a function TOC entry.
  • table size = 256

0x00364208 (3.15)

0x00362308 (3.41)

0x00366088 (3.55)

Memory HV calls[edit | edit source]

lv1_map_htab - 0x002D595C (3.15)

lv1_unmap_htab - 0x002D56B8 (3.15)

lv1_allocate_memory - 0x002D72F0 (3.15)

lv1_release_memory - 0x002D66A4 (3.15)

lv1_query_logical_partition_address_region_info - 0x002C9B24 (3.15)

lv1_create_repository_node - 0x002DD014 (3.15)

lv1_get_repository_node_value - 0x002DD260 (3.15)

lv1_undocumented_function_231 - 0x0030B560 (3.15)

System call[edit | edit source]

HV Processes do not use HV calls. They use syscalls only.

System call handler[edit | edit source]

0x002974D8 (3.15)

0x00292F6C (2.60)

There are 2 system call tables in HV. The first one stores system calls 0 - 36. The second one stores system calls 0x10000 - 0x100FF.

UX System call table 0 - 36[edit | edit source]

0x0035FAE8 (3.15)

0x00358ED0 (2.60)

System call numbers[edit | edit source]

0x0 - void eosh(void) //end_of_signal_handling(void)

0x1 - pid_t getpid(void)

0x2 - pid_t getppid(void)

0x3 - pid_t fork(void)

0x4 - void exit(int status)

0x5 - void execv(const char *path, char *const argv[])

0x6 - void wait(int *status)

0x7 - int open(const char *path, int flags)

0x8 - void close(int fd)

0x9 - ssize_t read(int fd, void *buf, unsigned int nbyte)

0xA - ssize_t write(int fd, const void *buf, unsigned int nbyte)

0xB - void lseek(int fd, long offset, int whence)

0xC - unlink(const char *path)

0xD - void signal(int sig, void *func(int sig))

0xE - int kill(int pid, int signal_type)

0xF - int brk(void *addr)

0x10 - int socket(int af, int type, int protocol) (supports only address family 0x1F, type 0x0 and protocol 0x0)

0x11 - int bind(int sockfd , const sockaddr *addr, unsigned int addrlen)

0x12 - int listen(int sockfd, int backlog)

0x13 - int accept(int sockfd, sockaddr *addr, unsigned int *addrlen)

0x14 - int connect(int sockfd, const sockaddr *serv_addr, unsigned int addrlen)

0x15 - void putchar(int c)

0x16 - int pause(void)

0x17 - int sleep(unsigned int seconds)

0x18 - int mmap(void *addr, unsigned long size, int prot, int flags, int fd, long offset, void *mapped_addr)

0x19 - int munmap (void *addr, unsigned long size)

0x1A - int chdir(const char *path)

0x1B - void getchar(char *c)

0x1C - map_pages(...) (used for alloc)

0x1D - unmap_pages(...) (used for free)

0x1E - int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)

0x1F - getcwd(...)

0x20 - Not used

0x21 - unsigned int alarm(unsigned int seconds)

0x22 - int ioctl(int fd, unsigned __int64 request, ...)

0x23 - pme_memalign(...)

0x24 - ?

PMI System call table 0x10000 - 0x100FF[edit | edit source]

0x0035DE78 (3.15)

0x00357260 (2.60)

System call numbers[edit | edit source]

0x10000 - allocate_memory(LPAR id, size, log2 of page size, ?, ?) / construct_memory_segment

0x10001 - query_logical_partition_address_region_info

0x10002 - translate_logical_partition_to_physical_address(LPAR id, LPAR address, physical addr)

0x10003 - map_physical_address_region

0x10004 - unmap_physical_address_region

0x10005 - construct_logical_pu

0x10006 - destruct_logical_pu

0x10007 - activate_logical_pu(LPAR id, PPE id)

0x10009 - construct_logical_partition(0, LPAR id, outlet)

0x1000A - get_logical_console_info

0x1000B - get_remote_file_size

0x1000C - read_remote_file

0x1000D - write_remote_file

0x1000E - release_memory_region(LPAR id, memory region address)

0x1001A - construct_event_receive_port

0x1001B - destruct_event_receive_port

0x1001C - request_to_connect_event_ports

0x1001D - connect_event_ports

0x1001E - destruct_event_send_port

0x1001F - send_event_externally

0x10020 - get_status_of_event_send_port

0x10021 - get_event_port_connection_request

0x10022 - end_of_control_signal_processing

0x10024 - shutdown_logical_partition(LPAR id, shutdown command)

0x10025 - destruct_logical_partition(LPAR id)

0x10026 - get_logical_partition_info

0x10027 - read_privilege_set

0x10028 - modify_privilege_set

0x10029 - get_remote_file_size_long_name

0x1002A - read_remote_file_long_name

0x1002B - write_remote_file_long_name

0x1002C - construct_scheduling_table

0x1002D - set_scheduling_slot

0x1002E - load_scheduling_table

0x10032 - poweroff

0x10033 - get_remote_file_name

0x10034 - allocate_cp_channel

0x10035 - release_cp_channel

0x10036 - power_down

0x10037 - ?

0x10038 - ?

0x10039 - ?

0x10040 - construct_spe_type_1(SPE id, shaddow_addr) / construct_logical_spu

0x10041 - destruct_spe(SPE id) / destruct_logical_spu

0x10042 - decrypt_lv2_self(spe id, LPAR auth id, SELF file image ptr, LPAR memory address)

0x10043 - load_spe_module(spe id, SCE module ptr, arg1, arg2, arg3, arg4)

0x10044 - disable_spe_execution

0x10045 - read_spu_puint_mb(unsigned long spu_id, unsigned long msg)

0x10046 - read_spe_problem_state_register(spe id, register offset, value) / read_spu_problem_state_area_register

0x10047 - write_spe_problem_state_register(spe id, register offset, value) / write_spu_problem_state_area_register

0x1004A - install_revoke_list

0x1004B - disable_spe_loading

0x1004C - install_access_control_table?

0x1004D - get_storage_status?

0x1004E - get_region_table_bits?

0x1004F - commit_region_update?

0x10050 - abort_region_update?

0x10051 - set_storage_tampered?

0x10053 - pmi_set_guest_os_mode

0x1007F - pause

0x10080 - get_total_execution_time

0x10081 - reset

0x10083 - construct_logical_rsx

0x10084 - construct_virtual_uart(LPAR id, VUART id, VUART data buffer size)

0x10085 - destruct_virtual_uart(LPAR id, VUART id)

0x10086 - establish_virtual_uart_channel

0x10088 - RSX_syscall_10088(LPAR id)

0x10089 - RSX_syscall_10089

0x1008A - RSX_syscall_1008A

0x100BE - lv1_ioctl

0x100C0 - create_repository_node(LPAR id)

0x100C1 - get_repository_node_value(LPAR id)

0x100C2 - modify_repository_node_value(LPAR id)

0x100C3 - remove_repository_node(LPAR id)

Process[edit | edit source]

Process table[edit | edit source]

HV supports only 32 processes simultaneously. The number of processes currently running in HV is stored at address 0x0035EA54 (3.15) and 0x00357E3C (2.60).

The process table is an array of 32 process table entries.

0x0036C930 (4.30)

0x0036C8B0 (4.21)

0x00365458 (4.11)

0x0035F8D0 (3.55)

0x0035C550 (3.41)

0x0035E850 (3.15)

0x00357C38 (2.60)

Process table entry[edit | edit source]

offset 0x0 - process status ? (8 bytes)

offset 0x8 - pointer to Process object

create_new_proc[edit | edit source]

This function creates a new Process object.

0x00298E2C (3.15)

0x002948BC (2.60)

Parameters[edit | edit source]

r3 - pointer to parent Process object

r4 - ?

copy_user_data[edit | edit source]

This function copies data to/from user space.

0x00299688 (3.15)

0x00295118 (2.60)

Parameters[edit | edit source]

r3 - pointer to Process object

r4 - some address in address space of Process

r5 - pointer to buffer in HV space

r6 - size to copy

r7 - ?

r8 - direction of copy (0 - copy from user space, != 0 - copy to user space)

r9 - ?

vtable[edit | edit source]

Processes have no vtables. That means they have no virtual functions.

Member variables[edit | edit source]

offset 0x0 - PID (4 bytes)

offset 0x8 - pointer to parent Process object

offset 0x10 - pointer to AddressSpace object

offset 0x30 - pointer to first PThread object of process

offset 0x38 - array of signal handlers (192 * 8 bytes)

offset 0x638 - pointer to pointer to ELF image

offset 0x640 - start of file table (20 * 24 bytes)

offset 0x820 - exit status (4 bytes)

offset 0x898 - pointer to Inode object of current directory

offset 0x8A8 - some pointer

Signals[edit | edit source]

A process can have upto 192 signal handlers. For example, signal 9 is SIGKILL. A signal handler for SIGKILL cannot be installed and it cannot be ignored.

A process does not have a signal mask. Every thread of a process has it's own signal mask.

Signal constants[edit | edit source]

0x9 - SIGKILL

0xE - SIGALRM

0x20 - SIGSPUMB

0x21 - SIGSPUMB_SL

0x22 - SIGSPUSTOP

0x23 - SIGSPUSTOP_SL

0x24 - SIGSPUDMA

0x26 - SIGSPUTIMEOUT

0x27 - SIGSPUERR

0x41 - SIGSHUTDOWN

File table[edit | edit source]

The file table has 20 entries. So, a process can have at most 20 files opened simultaneously. Each entry is 24 bytes large.

offset 0x0 - entry valid or invalid (1 byte), 0 - invalid, 1 - valid

offset 0x8 - pointer to object with File interface

offset 0x10 - current file position (8 bytes)

Process_EA_to_RA[edit | edit source]

This function translates an effective process address to real address.

0x00297E08 (3.15)

Objects[edit | edit source]

Here are the addresses of Process objects i could identify in HV dump 3.15:

  • 0x006BB0D0 (PID 0)
  • 0x0012C010 (PID 3) - ss_server3.fself
  • 0x000915D0 (PID 5) - ss_server2.fself
  • 0x000E4D70 (PID 6) - ss_server1.fself
  • 0x0012C8D0 (PID 9) - sysmgr_ss.fself

Here are the addresses of Process objects i could identify in HV dump 2.60:

  • 0x006B7580 (PID 0)
  • 0x00135F90 (PID 3)
  • 0x000862D0 (PID 5)
  • 0x000A9870 (PID 6)
  • 0x00084B80 (PID 9)

In JIG 2.43:

  • (PID3) <- ss_server3
  • (PID4) <- ss_sc_init_pu
  • (PID5) <- ss_server2
  • (PID6) <- ss_server1
  • (PID7) <- factory_data_mngr_server
  • (PID8) <- updater_frontend

(see this)

PThread[edit | edit source]

All PThread objects of the same Process object are linked together in a list.

vtable[edit | edit source]

0x003556D8 (3.15)

0x0034ECC0 (2.60)

offset 0x60 - pointer to TOC entry of system call handler

Member variables[edit | edit source]

offset 0x10 - pointer to next PThread object of Process

offset 0x18 - Thread object

offset 0x2B8 - ? (4 bytes)

offset 0x2C0 - pointer to TOC of some function

offset 0x2C8 - pointer to TOC of some function

offset 0x348 - some conter (4 bytes)

offset 0x3C0 - pointer to Process object that owns PThread object

offset 0x3F8 - signal pending mask (3 * 8 bytes = 192 signals)

offset 0x440 - ConditionVariable object

Signals[edit | edit source]

A PThread has it's own signal mask, independant of all other PThreads in the same process.

Methods[edit | edit source]

wait_for_my_turn(Pthread ptr, ?, sleep interruptible flag) = wakeup status - 0x00296FB0 (3.15)

Thread[edit | edit source]

get_current_thread[edit | edit source]

This function returns the pointer to current running thread.

0x0028B994 (3.15)

0x0028744C (2.60)

vtable[edit | edit source]

0x00355750 (3.15)

Member variables[edit | edit source]

offset 0x288 - some pointer

offset 0x290 - some pointer

AddressSpace[edit | edit source]

vtable[edit | edit source]

0x003549A0 (3.15)

0x0034DF88 (2.60)

Member variables[edit | edit source]

offset 0x8 - Mutex object

offset 0x40 - AddressProtectionDomain object

offset 0x50 - some pointer

offset 0xC0 - some counter (4 bytes)

AddressSpace_EA_to_RA[edit | edit source]

0x002874D0 (3.15)

AddressProtectionDomain[edit | edit source]

vtable[edit | edit source]

0x00354980 (3.15)

Member variables[edit | edit source]

offset 0x0 - pointer to previous AddressProtectionDomain object

offset 0x8 - pointer to next AddressProtectionDomain object

offset 0x10 - poiinter to pointer to SLB entries

offset 0x18 - pointer to AddressSpace object that owns this object

offset 0x2A - pointer to previous ProtectionPage

offset 0x34 - pointer to next ProtectionPage

offset 0x40 - Mutex object

ProtectionPage[edit | edit source]

vtable[edit | edit source]

none

Member variables[edit | edit source]

offset 0x0 - RA (8 bytes)

offset 0x8 - EA (4 bytes)

offset 0x10 - pointer to previous ProtectionPage (4 bytes)

offset 0x14 - pointer to next ProtectionPage (4 bytes)

Mutex[edit | edit source]

vtable[edit | edit source]

0x00354D08 (3.15)

0x0034E2F0 (2.60)

Member variables[edit | edit source]

offset 0x18 - ? (4 bytes)

offset 0x1C - ? (4 bytes)

ConditionVariable[edit | edit source]

vtable[edit | edit source]

0x003549C0 (3.15)

offset 0x20 - wait

Member variables[edit | edit source]

offset 0x20 - pointer to Mutex object

File interface[edit | edit source]

vtable[edit | edit source]

offset 0x8 - ?

offset 0x28 - open

offset 0x30 - close

offset 0x38 - read

offset 0x40 - write

offset 0x50 - mmap

offset 0x58 - ioctl

StorageRegionFile[edit | edit source]

Flash device file class.

vtable[edit | edit source]

0x003569F8 (3.15)

VUARTFile[edit | edit source]

VUART device file class.

vtable[edit | edit source]

0x00356458 (3.15)

STDLCFile[edit | edit source]

Console device file class.

vtable[edit | edit source]

0x003561F8 (3.15)

Member variables[edit | edit source]

offset 0x20 - reference counter (8 bytes)

offset 0x28 - free buffer space ? (8 bytes)

SocketFile[edit | edit source]

vtable[edit | edit source]

0x00355DB0 (3.15)

offset 0xB0 - bind

RegionManager[edit | edit source]

vtable[edit | edit source]

0x00355F80 (3.15)

Inode[edit | edit source]

DirectoryInode[edit | edit source]

vtable[edit | edit source]

0x00355788 (3.15)

offset 0x20 - link

offset 0x28 - unlink

get_root_inode[edit | edit source]

This function returns the pointer to the Inode object of the root directory.

0x0029C124 (3.15)

0x00297BB4 (2.60)

vtable[edit | edit source]

0x00334E50 (3.15)

offset 0x30 - lookup

File system[edit | edit source]

Console device file objects[edit | edit source]

Here is the list of console device file objects i found in HV dump 3.15:

  • console

vtable[edit | edit source]

0x003561F8 (3.15)

Flash device file objects[edit | edit source]

Here is the list of flash device file objects i found in HV dump 3.15:

  • /dev/eflash0
  • /dev/eflash1
  • /dev/rflash0
  • /dev/rflash1
  • /dev/rflash_1x
  • /dev/rflash_1xp

vtable[edit | edit source]

0x003569F8 (3.15)

IOIF device file objects[edit | edit source]

Here is the list of IOIF device file objects i found in HV dump 3.15:

  • /dev/ioif0

vtable[edit | edit source]

0x00356688 (3.15)

Member variables[edit | edit source]

0x360 = MMIO base address

SD detector device file objects[edit | edit source]

Here is the list of SD detector device file objects i found in HV dump 3.15:

  • /dev/sd_detector

vtable[edit | edit source]

0x00356B48 (3.15)

NET device file objects[edit | edit source]

Here is the list of NET device file objects i found in HV dump 3.15:

  • /dev/net0

vtable[edit | edit source]

0x00356DE8 (3.15)

INODES[edit | edit source]

INODE OBJECT

+0x04: previos inode

+0x08: next inodes

+ 0x38:  path

+ 0x358: childer_inode


MFS_ROOT_INODE

(2.60) 0x3580B0

+ 0x60 = ROOT_INODE


SOME ADDRESSES IN 2.60

0x60C010: "/dev" inode

0x6AA580: "/proc" inode

using linked list you can follow all inodes

Repository[edit | edit source]

  • Each LPAR has it's own node repository
  • Repository nodes are stored in a hash table which can have several sub-hash tables.

RepositoryNode[edit | edit source]

vtable[edit | edit source]

0x00357F58 (3.15)

Member variables[edit | edit source]

offset 0x30 - pointer to next RepositoryNode obj

offset 0x38 - 2nd hash value of name (4 bytes)

offset 0x40 - 1st field name (8 bytes)

offset 0x48 - 2nd field name (8 bytes)

offset 0x50 - 3rd field name (8 bytes)

offset 0x58 - 4th field name (8 bytes)

offset 0x60 - ? (4 bytes)

offset 0x68 - 1st field value (8 bytes)

offset 0x70 - 2nd field value (8 bytes)

Hash Function[edit | edit source]

  • The name of a repository node is hashed and 2 hash values (2 32bit values) are produced.
  • The 1st hash value is used to select a sub-hash table.
  • The 2nd hash value is used to find a sub-hash table bucket.
  • Repository nodes in a hash bucket are ordered by the 2nd hash value.
void hash(unsigned long long n1,
          unsigned long long n2,
          unsigned long long n3,
          unsigned long long n4,
          unsigned long *h1,
          unsigned long *h2)
{
    unsigned long long h;
    unsigned long hl;

    h = ((((n1 ^ n4) >> 32) ^ (n2 ^ n3)) ^ (((n2 ^ n3) >> 32) ^ (n1 ^ n4))) & ~0xC0000000ULL;

    *h1 = h & 0xFFFFFFFFULL;

    h = ((h & 0x55555555ULL) << 1) | ((h & 0xAAAAAAAAULL) >> 1);

    h = ((h & 0x33333333ULL) << 2) | ((h & 0xCCCCCCCCULL) >> 2);

    h = ((h & 0xF0F0F0FULL) << 4) | ((h & 0xF0F0F0F0ULL) >> 4);

    hl = (h << 8) | ((h & 0xFF000000ULL) >> 24);

    hl = (hl & ~0xFF000000UL) | ((h & 0xFFULL) << 24);

    hl = (hl & ~0x0000FF00UL) | (((h << 24) | (h >> 8)) & 0x0000FF00ULL);

    hl |= 0x1; 

    *h2 = hl;
}

Repository nodes from HV 3.15[edit | edit source]

Dump of all repository nodes from HV 3.15

Repository nodes from HV 3.41 dump made from GameOS[edit | edit source]

Dump of all repository nodes from HV 3.41 dump made from GameOS

Buses[edit | edit source]

SB bus[edit | edit source]

type - 4

index - 1

num_devices - 4 (repository node says this but there are more devices !!!)

Storage bus[edit | edit source]

type - 5

index - 4

num_devices - 4

SB bus subsystem[edit | edit source]

vtable[edit | edit source]

0x00352600 (3.15)

Member variables[edit | edit source]

offset 0x10 - MMIO memory base address

offset 0x20 - array of 16 pointers to SB devices (0 - Gelic device, 1 - USB device)

Objects[edit | edit source]

0x00349528 - pointer to pointer to SB bus subsystem object

Memory base address[edit | edit source]

0x24000000000

All SB bus device MMIO addresses are relative to this memory address.

SB device MMIO/DMA memory region[edit | edit source]

vtable[edit | edit source]

0x352308 (3.15)

Member variables[edit | edit source]

offset 0x18 - pointer to previous bus memory region object

offset 0x20 - pointer to next bus memory region object

offset 0x30 - relative bus memory start address

offset 0x38 - size of bus memory region

SB bus device[edit | edit source]

vtable[edit | edit source]

0x00352620 (3.15)

Member variables[edit | edit source]

offset 0x18 - array of pointers to MMIO memory region objects owned by device (8 * 8 bytes)

offset 0x60 - pointer to first DMA region object

offset 0x6C - device opened flag (1 byte, 0 - not opened, 1 - already opened)

offset 0x70 - id of LPAR that opened this device

offset 0x90 - pointer to an object that contains the address of interrupt handler for this device and SB bus interrupt index

Gelic device (Network Interface)[edit | edit source]

device id = 0

interrupt index = 8

The Gelic device is similar to the spider_net device from Toshiba. There are some differences with mmio initialization values within LV1 in comparison to the spider_net.c linux driver.

Gelic defines:

DEFINE Value
GELIC_CKRCTRL_REGISTER 0xFF0
GELIC_CKRCTRL_STOP_VALUE 0x00000105
GELIC_CKRCTRL_RUN_VALUE 0x1D7F0105
GELIC_MACADDR_HIGH_REG 0x500
GELIC_MACADDR_LOW_REG 0x504

MMIO regions[edit | edit source]

Index Relative Bus Start Address Absolute Bus Start Address Size
0 0x2800 0x24000002800 0x200
1 0x3004000 0x24003004000 0x1000
2 - - -
3 - - -
4 - - -
5 - - -
6 - - -
7 - - -

DMA regions[edit | edit source]

Relative Bus Start Address Absolute Bus Start Address Size
0xA0000000 - 0x8000
0xC0000000 - 0x10000000

SATA Controller 1 device[edit | edit source]

device id = 1

interrupt index = 49

MMIO regions[edit | edit source]

Index Relative Bus Start Address Absolute Bus Start Address Size
0 0x2000 0x24000002000 0x200
1 0x3000000 0x24003000000 0x1000
2 0x3800000 0x24003800000 0x1000
3 0x3802000 0x24003802000 0x1000
4 - - -
5 - - -
6 - - -
7 - - -

DMA regions[edit | edit source]

Relative Bus Start Address Absolute Bus Start Address Size
0xA0000000 - 0x1000
0xA0001000 - 0x1000
0xA0002000 - 0x1000

SATA Controller 2 device[edit | edit source]

device id = 2

interrupt index = 13

MMIO regions[edit | edit source]

Index Relative Bus Start Address Absolute Bus Start Address Size
0 0x2200 0x24000002200 0x200
1 0x3001000 0x24003001000 0x1000
2 0x3801000 0x24003801000 0x1000
3 0x3803000 0x24003803000 0x1000
4 - - -
5 - - -
6 - - -
7 - - -

DMA regions[edit | edit source]

Relative Bus Start Address Absolute Bus Start Address Size
0xA0000000 - 0x1000
0xA0001000 - 0x1000
0xA0002000 - 0x1000

USB Controller 1 device[edit | edit source]

device id = 3

MMIO regions[edit | edit source]

Index Relative Bus Start Address Absolute Bus Start Address Size
0 0x2400 0x24000002400 0x200
1 0x3010000 0x24003010000 0x10000
2 0x3810000 0x24003810000 0x10000
3 - - -
4 - - -
5 - - -
6 - - -
7 - - -

DMA regions[edit | edit source]

Relative Bus Start Address Absolute Bus Start Address Size
0xC0000000 - 0x10000000
0xD0000000 - 0x10000000

USB Controller 2 device[edit | edit source]

device id = 4

MMIO regions[edit | edit source]

Index Relative Bus Start Address Absolute Bus Start Address Size
0 0x2600 0x24000002600 0x200
1 0x3020000 0x24003020000 0x10000
2 0x3820000 0x24003820000 0x10000
3 - - -
4 - - -
5 - - -
6 - - -
7 - - -

DMA regions[edit | edit source]

Relative Bus Start Address Absolute Bus Start Address Size
0xC0000000 - 0x10000000
0xD0000000 - 0x10000000

ENCDEC device[edit | edit source]

device id = 7

interrupt index = 5

MMIO regions[edit | edit source]

Index Relative Bus Start Address Absolute Bus Start Address Size
0 0x2C00 0x24000002C00 0x200
1 0x3005000 0x24003005000 0x1000
2 0x3006000 0x24003006000 0x1000
3 - - -
4 - - -
5 - - -
6 - - -
7 - - -

DMA regions[edit | edit source]

Relative Bus Start Address Absolute Bus Start Address Size
0x80010000 - 0x10000
0x80004000 - 0x4000
0x80001000 - 0x1000
0x80003000 - 0x1000
0x80008000 - 0x1000
0x80009000 - 0x1000
0x80040000 - 0x10000
0x8000A000 - 0x1000
0x90020000 - 0x20000
0xC0000000 - 0x10000
0xC0040000 - 0x40000

FLASH Controller device (StarShip - SS)[edit | edit source]

device id = 9

interrupt index = 41

MMIO regions[edit | edit source]

FLASH controller doesn't have MMIO regions.

DMA regions[edit | edit source]

Relative Bus Start Address Absolute Bus Start Address Size
0x80000000 - 0x1000
0x80020000 - 0x20000
0x80002000 - 0x1000
0x90000000 - 0x20000

SB Bus Interrupt Handling[edit | edit source]

  • There is a table of interrupt handlers for SB devices
  • The size of table is 64
  • The main SB bus interrupt handler is at 0x002B9CC4 (3.15)
  • The main interrupt handler reads interrupt index and dispatches interrupts

Interrupt Index[edit | edit source]

  • The main SB bus interrupt handler reads 2 32-bit values from addresses 0x24000008100 and 0x0x24000008104
  • The interrupt index is calculated from these values

Interrupt Handler Table[edit | edit source]

Interrupt Description Address in HV
5 ENCDEC device 0x00275C60 (3.15)
6 EH EPCIC internal 0x0023B6B0 (3.15)
8 Gelic device 0x00245330 (3.15)
12 ATA interrupt handler 0x0026B984 (3.15)
13 ATA interrupt handler 0x0026B984 (3.15)
14 Spider SC 0x0020A68C (3.15)
29 SBERR 0x0023AA50 (3.15)
30 SBERR 0x0023AA50 (3.15)
41 EBUS (Flash StarShip) 0x002814EC (3.15)
49 ATA media interrupt handler 0x00268A8C (3.15)
50 Flash ? 0x00280B24 (3.15)
55 EH EPCIC SERR 0x0023B67C (3.15)

Storage bus subsystem[edit | edit source]

vtable[edit | edit source]

0x00353AC8 (3.15)

Member variables[edit | edit source]

offset 0xEE8 - table of pointers to storage device objects (7 * 8 bytes, max 7 devices)

Storage device class[edit | edit source]

Member variables[edit | edit source]

offset 0x8 - device id (8 bytes)

offset 0xD50 - device id (8 bytes)

offset 0xD60 - pointer to ENCDEC SB bus device object

Region Manager[edit | edit source]

  • Each storage device has a Region Manager (i call it like that)
  • Region Manager stores information about each Region of the storage device
  • All Regions of a Region Manager are linked together
  • Free Regions of a Region Manager are linked together also
  • A Region Manager can have at most 8 Regions

Region[edit | edit source]

  • Each storage device can have at most 8 regions (0-7)
  • Each region has ACL table
  • HV checks region ACLs before allowing access to the region
  • Each region has a start sector that is an offset from the physical first sector of the storage device and a number of sectors
  • The start sector passed to lv1 storage hvcalls is relative to the start sector of the region passed to the lv1 storage hvcall
Region ACL[edit | edit source]

offset 0x0 - LPAR AUTH ID (8 bytes)

offset 0x8 - access rigths (8 bytes)

offset 0x10 - entry valid flag: 0 - invalid, 1 - valid (1 byte)

Region Access Protection[edit | edit source]
  • Before a storage region is accessed, HV checks access rights of the caller.
  • Repository node ss.laid (LPAR Authority ID) is evaluated for this purpose.
  • If LPAR has a repository node ios.ata.region0.access (value doesn't matter) then the access rights check never fails. After System Manager sets ATA keys it removes this repository node from LPAR 1. If we add this repository node again or patch System Manager so it's not removed then we will be able to access all storage regions of all storage devices.
  • ALL storage accesses from LPAR 1 are allowed
  • If (flags & 0x100000002) != 0 then access rights check is skipped !!!.
 I tested on HV 3.41 with flags 0x2 and got access to regions which were denied by policy (LV1_DENIED_BY_POLICY result).

Storage Device Partition Table[edit | edit source]

  • Each storage device has a Partition Table
  • Partition Table contains information about each region on the storage device

Methods[edit | edit source]

lv1_storage_create_region (lv1_undocumented_function_250) - 0x00301328 (3.15)

lv1_storage_delete_region (lv1_undocumented_function_251) - 0x003011E8 (3.15)

lv1_storage_set_region_acl (lv1_undocumented_function_252) - 0x00300F3C (3.15)

lv1_storage_get_region_acl (lv1_undocumented_function_253) - 0x00301090 (3.15)

storage_device_create_region - 0x00253988 (3.15)

storage_device_delete_region - 0x00253BE8 (3.15)

storage_device_region_set_acl - 0x00252C80 (3.15)

storage_device_region_get_acl - 0x00252710 (3.15)

storage_region_mgr_create_region - 0x0025A530 (3.15)

storage_region_mgr_delete_region - 0x0025BA64 (3.15)

storage_region_mgr_set_acl - 0x0025A140 (3.15)

storage_region_mgr_get_acl - 0x0025A298 (3.15)

storage_region_mgr_update_partition_table - 0x00259924 (3.15)

storage_region_acl_entry_reset - 0x0025C1A8 (3.15)

storage_region_acl_entry_check_laid - 0x0025C1FC (3.15)

storage_region_overlap - 0x0025C094 (3.15)

storage_region_check_access - 0x00259EC8 (3.15)

Storage subsystem device[edit | edit source]

device id = -1

  • The storage subsystem is a storage device itself.
  • It's a pseudo device used to notify a LPAR when storage devices become e.g. ready.
  • Linux implements a loop and reads from this device and process notifications (adds new devices dynamically).

Notification Events[edit | edit source]

List of supported notification events:

  • Notify Device Ready (0x1)
  • Notify Region Probe (0x2)
  • Notify Region Update (0x4)

RBD device[edit | edit source]

  • On Linux, ENCDEC and RBD devices are mapped to the storage device with device id 0.
  • On GameOS, ENCDEC device has device id 0 and RBD device has device id 2.

device id = 0

block size = 2048

/dev/rbd0

  • The RBD storage device uses ENCDEC device.

vtable[edit | edit source]

0x00354288 (3.15)

Member variables[edit | edit source]

offset 0x1808 - request table (0x58 * 32 bytes)

Regions[edit | edit source]

Index Start sector Number of sectors
0 0x0 0x7FFFFFFF
1 - -
2 - -
3 - -
4 - -
5 - -
6 - -
7 - -

Supported Device Commands[edit | edit source]

Here is the list of commands supported by RBD storage device.

  • The commands can be used with HV call lv1_storage_send_device_command.
  • However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node ss.laid or also called LPAR Authority ID. If this test fails then the command is NOT executed.
Command Description
0x1 LV1_STORAGE_SEND_ATAPI_COMMAND
0x10 ATAPI Read Capacity
0x11 ATAPI Get Configuration
0x13 ATAPI Read TOC
0x1A ATAPI Get Event

/dev/rbd0[edit | edit source]

  • This LPAR 1 device accesses RBD storage device.
  • A write to this device sends a device command to RBD storage device.

ENCDEC Device[edit | edit source]

bus id = 4

device id = 0

  • ENCDEC device has a request table of size 32.
  • ENCDEC device supports upto 16 keys simultaneously.

Member variables[edit | edit source]

offset 0xDC0 - request table (0x58 * 32 bytes)

Methods[edit | edit source]

encdec_device_initialize - 0x00273524 (3.15)

InitializeENCDEC - 0x00277310 (3.15)

ENCDEC_ConnectBusDriver - 0x00275A98 (3.15)

encdec_interrupt_handler - 0x00275C60 (3.15)

encdec_process_interrupt - 0x0027526C (3.15)

encdec_device_enqueue_decsec_request - 0x00273738 (3.15)

encdec_device_do_request - 0x00273EA8 (3.15)

encdec_device_do_SS_request - 0x00274940 (3.15)

Encdec_KickDMA - 0x00277118 (3.15)

encdec_device_is_in_testmode - 0x002756E0 (3.15)

is_encdec_in_testmode - 0x002732D0 (3.15)

ENCDEC Device Commands[edit | edit source]

  • EdecKgen1 command is used e.g. by Storage Manager Service 0x5003 to generate random numbers. Storage Manager performs this command through LPAR 1 device /dev/encdec0.
Command Description
0x81 EdecKgen1
0x82 EdecKgen2
0x83 EdecKset/EdecKset NG
0x84 EdecKgenFlash
0x85 Encrypts/decrypts sectors (This command cannot be executed through ioctl interface !!!)
0x86 Encdec decsec (This command cannot be executed through ioctl interface !!!)
0x87 EdecSBClear

EdecKgen1 Command (0x81)[edit | edit source]

  • First, ENCDEC device key generator is flashed by executing the operation which is also performed during EdecKgenFlash command.
  • 0x30 bytes of data are written to MMIO registers of ENCDEC device.
  • 0x40 bytes of data are read from MMIO registers of ENCDEC device.
  • The base address of MMIO registers used in this command is 0x24003006000.
  • I tested this command by directly communicating with ENCDEC device from GameOS by using HV call lv1_storage_send_device_command and it returns random data.

Here is the data i sent to ENCDEC device:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   00 01 00 30 72 A7 88 EC  FC A4 06 71 4C B1 50 C9   ...0r§ˆìü¤.qL±PÉ
00000010   FB E0 06 C2 74 B5 84 C4  E6 BD 1E 55 4E 36 E9 C9   ûà.Âtµ„Äæ½.UN6éÉ
00000020   D6 09 BC B4 79 A6 BC DE  60 A5 B2 41 C7 15 68 68   Ö.¼´y¦¼Þ`¥²AÇ.hh
00000030   82 1D 8F D6 00 00 00 00  00 00 00 00 00 00 00 00   ‚.Ö............

Here is the data i received back from ENCDEC device:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   00 02 00 00 57 CF 06 AF  53 85 1B B8 49 37 06 28   ....WÏ.¯S….¸I7.(
00000010   51 8D 4E F9 EF 76 E2 C7  17 EF 41 14 FA 6C 96 A8   QNùïvâÇ.ïA.úl–¨
00000020   7E 41 43 96 15 9A 0D 71  A9 B6 A6 B0 F1 96 15 C5   ~AC–.š.q©¶¦°ñ–.Å
00000030   30 25 C3 8E 6F AC FB 7F  E7 2A FB E2 36 E1 85 92   0%ÃŽo¬û?ç*ûâ6á…’
00000040   99 66 DB EC 00 00 00 00  00 00 00 00 00 00 00 00   ™fÛì............

Here is another data i received back from ENCDEC device by using the same command and data:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   00 02 00 00 57 CF 06 AF  53 85 1B B8 49 37 06 28   ....WÏ.¯S….¸I7.(
00000010   51 8D 4E F9 EF 76 E2 C7  17 EF 41 14 FA 6C 96 A8   QNùïvâÇ.ïA.úl–¨
00000020   7E 41 43 96 17 08 75 F6  66 2F 32 5A 9E 3E E7 FD   ~AC–..uöf/2Zž>çý
00000030   16 3E 18 CA B2 5E 90 84  29 7F 98 BC 73 36 0E 7B   .>.ʲ^„)?˜¼s6.{
00000040   7D EC B6 37 00 00 00 00  00 00 00 00 00 00 00 00   }ì¶7............

EdecKgen2 Command (0x82)[edit | edit source]

  • The base address of MMIO registers used in this command is 0x24003006000.

EdecKset Command (0x83)[edit | edit source]

EdecKgenFlash Command (0x84)[edit | edit source]

  • The base address of MMIO registers used in this command is 0x24003006000.
  • The command reads 4 bytes from address 0x240030060A0, sets bit 1 to 1 (old value | 0x2) and writes the new value to the same address.

Encdec decsec Command (0x86)[edit | edit source]

  • This command is used to decrypt/encrypt sectors.
  • FLASH, HDD and RBD storage devices use this command to decrypt/encrypt sectors.
  • This command cannot be executed through lv1_storage_send_device_command HV call, it's used by HV only internally.

EdecSBClear Command (0x87)[edit | edit source]

  • The command expects arg2 to be 4 or else it returns with an error.
  • This command is used e.g. by Storage Manager service 0x5002 when ATA keys are deleted.

Test Mode[edit | edit source]

  • ENCDEC device has Test Mode
  • Some HV functions test it by reading a 4 byte value from address 0x24003005200. If this value is 0 then ENCDEC device is NOT in Test Mode.

ENCDEC Request[edit | edit source]

offset 0x34 - start sector (4 bytes)

offset 0x38 - sector count (4 bytes)

offset 0x3C - sector size (4 bytes)

offset 0x40 - key (4 bytes)

offset 0x44 - 0 = decrypt, 1 = encrypt (4 bytes)

Encrypting and Decrypting Sectors[edit | edit source]

  • HV passes to ENCDEC device addresses of 2 buffers: ENCDEC User Buffer and ENCDEC Descriptor Buffer.
  • ENCDEC User Buffer contains the following information: Start Sector, Sector Count, Sector Size and Key

ENCDEC User Buffer[edit | edit source]

offset 0x0 - start sector (4 bytes)

offset 0x4 - sector count (4 bytes)

offset 0x8 - sector size (4 bytes)

offset 0xC - key (4 bytes)

FLASH device[edit | edit source]

device id = 1

  • The FLASH device uses ENCDEC device.

vtable[edit | edit source]

0x00354450 (3.15)

Member variables[edit | edit source]

offset 0x18F0 - request table (0x58 * 16 bytes)

Regions[edit | edit source]

Index Start sector Number of sectors
0 0x0 0x8000
1 0x8 0x77F8
2 0x7900 0x100
3 0x7A00 0x400
4 - -
5 - -
6 - -
7 - -

Supported Device Commands[edit | edit source]

Here is the list of commands supported by FLASH StarShip 2 storage device.

  • The commands can be used with HV call lv1_storage_send_device_command.
  • However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node ss.laid or also called LPAR Authority ID. If this test fails then the command is NOT executed.
Command Description
0x31 Dummy (This command does nothing, returns success immediately)
0xA2 -
0xA3 -
0xA4 -
0xA6 SS2 HW Reset
0xAC -
0xAD TEST

/dev/eflash1 and /dev/rflash1[edit | edit source]

  • These LPAR 1 devices access region 0 of FLASH storage device.
  • /dev/rflash1 is 16MB large
  • There is no file system on /dev/rflash1
  • There is some sort of TOC (Table Of Contents) stored in it. It contains file names, offsets and sizes.
  • On /dev/rflash1 you will find lv0, lv1ldr, lv2_lernel.self and all the other important SELFs.
  • The files are encryted of course.

Content of /dev/rflash1 (FLASH storage device region 0, size 16 MB)[edit | edit source]

  • There is a main TOC which describes different regions on /dev/rflash1
  • It seems that TOC 0xC0000 and TOC 0x7C0000 contain the same files but from different SDK versions.
  • TOC 0xC0000 is SDK version 3.41 and TOC 0x7C0000 is SDK version 3.30 (look at the content of files sdk_version).
  • I guess it's because when i bought my PS 3 Slim it had Firmware 3.30 and i updated it to 3.41 for PSGroove.
  • TOC on /dev/rflash1 is used by HV Processes to locate files and load them into memory, e.g. SPU modules. E.g. Process 6 loads spu_utoken_processor.self to decrypt and verify user tokens or SPL which runs in Process 5 loads spp_verifier.self from there in order to decrypt and verify profile files. And Update Manager stores e.g. there files.
TOC Entry[edit | edit source]

A TOC entry is 0x30 bytes large.

offset 0x0 - relative offset from this TOC to entry data

offset 0x8 - entry data size

offset 0x10 - entry name (max 32 characters)

Main TOC[edit | edit source]

Here is a list of regions/files stored on /dev/rflash1 i found in HV 3.41 and dumped with PSGroove:

Entry Name TOC Offset Entry TOC Index Entry Relative Offset Entry Absolute Offset Entry Size
asecure_loader 0x400 0 0x400 0x810 0x2E800
eEID 0x400 1 0x2EC00 0x2F010 0x10000
cISD 0x400 2 0x3EC00 0x3F010 0x800
cCSD 0x400 3 0x3F400 0x3F810 0x800
trvk_prg0 0x400 4 0x3FC00 0x40010 0x20000
trvk_prg1 0x400 5 0x5FC00 0x60010 0x20000
trvk_pkg0 0x400 6 0x7FC00 0x80010 0x20000
trvk_pkg1 0x400 7 0x9FC00 0xA0010 0x20000
ros0 0x400 8 0xBFC00 0xC0010 0x700000
ros1 0x400 9 0x7BFC00 0x7C0010 0x700000
cvtrm 0x400 10 0xEBFC00 0xEC0010 0x40000
asecure_loader Region TOC[edit | edit source]

Here is a list of files stored on /dev/rflash1 i found in HV 3.41 and dumped with PSGroove:

Entry Name TOC Offset Entry TOC Index Entry Relative Offset Entry Absolute Offset Entry Size
metldr 0x800 0 0x40 0x840 0xE920
ros1 Region TOC[edit | edit source]

Here is a list of files stored on /dev/rflash1 i found in HV 3.41 and dumped with PSGroove:

Entry Name TOC Offset Entry TOC Index Entry Relative Offset Entry Absolute Offset Entry Size
creserved_0 0xC0000 0 0x460 0xC0470 0x40000
sdk_version 0xC0000 1 0x40460 0x100470 0x8
lv1ldr 0xC0000 2 0x40480 0x100490 0x1E948
lv2ldr 0xC0000 3 0x5EE00 0x11EE10 0x16FF0
isoldr 0xC0000 4 0x75E00 0x135E10 0x13074
appldr 0xC0000 5 0x88E80 0x148E90 0x1E254
spu_pkg_rvk_verifier.self 0xC0000 6 0xA70D4 0x1670E4 0xFACC
spu_token_processor.self 0xC0000 7 0xB6BA0 0x176BB0 0x5C94
spu_utoken_processor.self 0xC0000 8 0xBC834 0x17C844 0x65D0
sc_iso.self 0xC0000 9 0xC2E04 0x182E14 0x1532C
aim_spu_module.self 0xC0000 10 0xD8130 0x198140 0x4498
spp_verifier.self 0xC0000 11 0xDC5C8 0x19C5D8 0xD7F0
mc_iso_spu_module.self 0xC0000 12 0xE9DB8 0x1A9DC8 0x808C
me_iso_spu_module.self 0xC0000 13 0xF1E44 0x1B1E54 0x88B8
sv_iso_spu_module.self 0xC0000 14 0xFA6FC 0x1BA70C 0xC078
sb_iso_spu_module.self 0xC0000 15 0x106774 0x1C6784 0x5DB0
default.spp 0xC0000 16 0x10C524 0x1CC534 0x22A0
lv1.self 0xC0000 17 0x10E800 0x1CE810 0x127DF0
lv0 0xC0000 18 0x236600 0x2F6610 0x3E678
lv2_kernel.self 0xC0000 19 0x274C78 0x334C88 0x171B88
eurus_fw.bin 0xC0000 20 0x3E6800 0x4A6810 0x70F94
emer_init.self 0xC0000 21 0x457794 0x5177A4 0x7CDB8
hdd_copy.self 0xC0000 22 0x4D454C 0x59455C 0x60D68
ros2 Region TOC[edit | edit source]

Here is a list of files stored on /dev/rflash1 i found in HV 3.41 and dumped with PSGroove:

Entry Name TOC Offset Entry TOC Index Entry Relative Offset Entry Absolute Offset Entry Size
creserved_0 0x7C0000 0 0x460 0x7C0470 0x40000
sdk_version 0x7C0000 1 0x40460 0x800470 0x8
lv1ldr 0x7C0000 2 0x40480 0x800490 0x1E64C
lv2ldr 0x7C0000 3 0x5EB00 0x81EB10 0x16E30
isoldr 0x7C0000 4 0x75980 0x835990 0x12EC4
appldr 0x7C0000 5 0x88880 0x848890 0x1DB64
spu_pkg_rvk_verifier.self 0x7C0000 6 0xA63E4 0x8663F4 0xFACC
spu_token_processor.self 0x7C0000 7 0xB5EB0 0x875EC0 0x5C94
spu_utoken_processor.self 0x7C0000 8 0xBBB44 0x87BB54 0x65D0
sc_iso.self 0x7C0000 9 0xC2114 0x882124 0x1532C
aim_spu_module.self 0x7C0000 10 0xD7440 0x897450 0x4498
spp_verifier.self 0x7C0000 11 0xDB8D8 0x89B8E8 0xD7F0
mc_iso_spu_module.self 0x7C0000 12 0xE90C8 0x8A90D8 0x808C
me_iso_spu_module.self 0x7C0000 13 0xF1154 0x8B1164 0x88B8
sv_iso_spu_module.self 0x7C0000 14 0xF9A0C 0x8B9A1C 0xC078
sb_iso_spu_module.self 0x7C0000 15 0x105A84 0x8C5A94 0x5DB0
default.spp 0x7C0000 16 0x10B834 0x8CB844 0x22A0
lv1.self 0x7C0000 17 0x10DB00 0x8CDB10 0x129040
lv0 0x7C0000 18 0x236B80 0x9F6B90 0x3E570
lv2_kernel.self 0x7C0000 19 0x2750F0 0xA35100 0x1712D0
eurus_fw.bin 0x7C0000 20 0x3E63C0 0xBA63D0 0x70F94
emer_init.self 0x7C0000 21 0x457354 0xC17364 0x7FBB8
hdd_copy.self 0x7C0000 22 0x4D6F0C 0xC96F1C 0x61518

Methods[edit | edit source]

initialize_starship - 0x0028298C (3.15)

SSOperation - 0x0027BFB0 (3.15)

SSTransfer - 0x0027BE68 (3.15)

FLASH_Memory_SS2_on_complete - 0x00278E48 (3.15)

_FLASH_read_data - 0x0022D89C (3.15)

_FLASH_write_data - 0x0022D8C8 (3.15)

FLASH_SS2_HW_Reset - 0x0027BD1C (3.15)

HDD device[edit | edit source]

device id = 2

block size = 512

  • The HDD device uses ENCDEC device.

vtable[edit | edit source]

0x00353F48 (3.15)

Member variables[edit | edit source]

offset 0x1590 - LBA48 capability flag (4 bytes)

offset 0x17E8 - request table (0x58 * 16 bytes)

offset 0x1DB8 - request timer active flag (1 byte)

Regions[edit | edit source]

Index Start sector Number of sectors
0 0x0 0x950F8B0
1 0x8 0x80000
2 0x80018 0x7C8F898
3 0x7D0F8B8 0x3FFFF8
4 0x810F8B8 0x13FFFF8
5 - -
6 - -
7 - -

Supported Device Commands[edit | edit source]

Here is the list of commands supported by HDD storage device.

  • The commands can be used with HV call lv1_storage_send_device_command.
  • However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node ss.laid or also called LPAR Authority ID. If this test fails then the command is NOT executed.
Command Description
0x2 LV1_STORAGE_SEND_ATA_COMMAND
0x10 -
0x1B ATA Set UltraDMA Mode
0x1C ATA Set Features PIO Flow Control Transfer Mode
0x21 -
0x22 ATA Identify Device
0x23 LV1_STORAGE_ATA_HDDOUT (ATA Flush Cache Ext)
0x26 ATA Read Alternative Status
0x27 ATA Read Error
0x28 -
0x31 ATA Flush Cache/ATA Flush Cache Ext
0x32 ATA Standby Immediate
0x33 -

Virtual FLASH device (VFLASH)[edit | edit source]

device id = 3 (on Linux)/ 4 (on GameOS)

block size = 512

  • It's a pseudo device.
  • This storage device redirects all requests to the region 1 of HDD storage device !!!

vtable[edit | edit source]

0x00353D88 (3.15)

Member variables[edit | edit source]

offset 0xD60 - pointer to a storage device that all requests are redirected to

offset 0xD68 - region ID of the storage device that all requests are redirected to

Regions[edit | edit source]

Index Start sector Number of sectors
0 0x0 0x80000
1 0x8 0x75F8
2 0x7800 0x63E00
3 0x6B600 0x8000
4 0x73600 0x400
5 0x73A00 0x2000
6 0x77C00 0x200
7 - -

Supported Device Commands[edit | edit source]

Here is the list of commands supported by VFLASH storage device.

Index Start sector Number of sectors
0x31 ATA Flush Cache/ATA Flush Cache Ext -

/dev/rflash1_1x and /dev/rflash_1xp[edit | edit source]

  • These LPAR 1 devices access region 5 of UNKNOWN storage device.
  • In region 5 of UNKNOWN storage device is e.g. LINUX image stored.

GameOS's dev_flash[edit | edit source]

  • dev_flash has FAT16 file system.
  • Accesses to GameOS's dev_flash are routed to UNKNOWN storage device region 2
  • To decrypt sectors read from this region use as flags 0x4 !!! Without using flags 0x4 the sectors will be encrypted.
  • The sectors are decrypted not by GameOS but by ENCDEC device.

Here is a snippet from raw dev_flash dump made with HV call lv1_storage_read (flags 0x4) from GameOS:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   E9 00 00 20 20 20 20 20  20 20 20 00 02 10 10 00   é..        .....
00000010   02 00 02 00 00 F8 70 00  00 00 00 00 00 00 00 00   .....øp.........
00000020   00 3E 06 00 00 00 29 00  00 00 00 4E 4F 20 4E 41   .>....)....NO NA
00000030   4D 45 20 20 20 20 46 41  54 31 36 20 20 20 00 00   ME    FAT16   ..
00000040   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000050   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000060   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000070   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000080   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000090   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000000A0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000000B0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000000C0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000000D0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000000E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000000F0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000100   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000110   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000120   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000130   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000140   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000150   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000160   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000170   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000180   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00000190   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000001A0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000001B0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000001C0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000001D0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000001E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000001F0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 AA   ..............Uª


Methods[edit | edit source]

initialize_virtual_flash - 0x00282954 (3.15)

Enqueueing and Scheduling of Storage Requests[edit | edit source]

  • HV uses a simple FIFO scheduling algorithm for Storage Requests and a request timeout.
  • Each storage device has a table of size 16 to store incomming and pending Storage Requests
  • ENCDEC storage device has a table of size 32 to store incomming and pending Storage Requests
  • When a new Storage Request is submitted e.g. by HV call lv1_storage_read or lv1_storage_write, the table is scanned for a free slot, if there are no pending Storage Requests then the Storage Request is executed immediately
  • When a Storage Request is completed, the finished Storage Reuqest is passed to function storage_device_async_request_complete and the table of Storage Requests is scanned again for the next pending Storage Request which will be executed
  • There are 2 types of Storage Requests: Read/Write (1) and Device Command (2).
  • Read and Write Storage Requests use the same HV function of a Storage Device to enqueue the request. Before Write Storage Request is inserted into the Request Table of a Storage Device, the flags parameter passed e.g. in lv1_storage_read or lv1_storage_write is ored with 0x8. That is how HV differentiates between Read and Write Storage Requests.

Storage Device Request Table[edit | edit source]

  • Each request slot is of size 0x58

Request Slot[edit | edit source]

offset 0x0 - state: 1 - free, 2 - ? (4 bytes)

offset 0x4 - type: 1 - Read/Write, 2 - Command, 0x86 - ENCDEC command (4 bytes)

offset 0x10 - request tag (8 bytes)

offset 0x20 - start sector (8 bytes)

offset 0x28 - sector count (4 bytes)

ENCDEC Storage Device[edit | edit source]

  • Request Table begins at offset 0xDC0 of ENCDEC storage device.

RBD Storage Device[edit | edit source]

  • Request Table begins at offset 0x1808 of RBD storage device.

FLASH Storage Device[edit | edit source]

  • Request Table begins at offset 0x18F0 of FLASH storage device.

HDD Storage Device[edit | edit source]

  • Request Table begins at offset 0x17E8 of HDD storage device.

Methods[edit | edit source]

storage_device_HDD_enqueue_request - 0x0026E21C (3.15)

storage_device_HDD_do_device_command - 0x0026CED0 (3.15)

storage_device_HDD_do_request - 0x0026DED8 (3.15)

storage_device_HDD_request_complete - 0x0026E57C (3.15)

storage_device_FLASH_enqueue_request - 0x0027A518 (3.15)

storage_device_FLASH_do_request - 0x00278D1C (3.15)

storage_device_FLASH_do_device_command - 0x00279250 (3.15)

FLASH_Memory_SS2_on_complete - 0x00278E48 (3.15)

storage_device_async_request_complete - 0x00255184 (3.15)

storage_device_TransLparAddrToPhysAddr - 0x002533B4 (3.15)

storage_device_add_async_request_locked - 0x002527B8 (3.15)

storage_device_RBD_enqueue_request - 0x002723F0 (3.15)

storage_device_RBD_do_request - 0x0025EF70 (3.15)

storage_device_RBD_do_next_request - 0x00270994 (3.15)

storage_device_RBD_request_complete - 0x00271FD4 (3.15)

storage_device_rbd_do_request - 0x0025EE94 (3.41)

storage_device_rbd_do_device_command - 0x0027061C (3.41)

Encryption and Decryption of Storage Devices[edit | edit source]

HDD[edit | edit source]

  • ENCDEC peripheral device is used for HDD encryption/decryption
  • Write request is first passed to ENCDEC device for encryption. When ENCDEC device is done, it calls a callback and passes the encrypted data to the callback. The callback writes the encrypted data with ATA WriteDMAExt command to HDD.
  • When a storage device request is processed by HV, Storage Subsystem checks if cryptography is enabled for the storage device.
  • HV checks 1 byte of data owned by the storage device and when the value of this flag is not 0 then it uses encryption/decryption.
  • By setting this flag to 0 at runtime, encryption/decryption of storage devices can be disabled at runtime.
  • We could patch lv1.self so that encryption/decryption of storage devices is disabled permanently.
  • HDD sectors can be both decrypted and encrypted with HV calls

UFS2[edit | edit source]

  • Superblock starts at sector 0x80.
  • At the end of the superblock structure you will find UFS2 signature 0x19540119.

Here is the decrypted superblock of UFS2 filesystem:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00010000   00 00 00 00 00 00 00 00  00 00 00 28 00 00 00 30   ...........(...0
00010010   00 00 00 38 00 00 0B B8  00 00 00 00 00 00 00 00   ...8...¸........
00010020   00 00 00 00 00 00 00 00  00 00 78 10 00 00 01 5C   ..........x....\
00010030   00 00 40 00 00 00 08 00  00 00 00 08 00 00 00 08   ..@.............
00010040   00 00 00 00 00 00 00 00  FF FF C0 00 FF FF F8 00   ........ÿÿÀ.ÿÿø.
00010050   00 00 00 0E 00 00 00 0B  00 00 00 08 00 00 08 00   ................
00010060   00 00 00 03 00 00 00 02  00 00 08 00 00 00 00 00   ................
00010070   00 00 00 00 00 00 08 00  00 00 00 40 00 00 00 00   ...........@....
00010080   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010090   00 00 00 00 F5 35 BD 07  00 00 00 00 00 00 18 00   ....õ5½.........
000100A0   00 00 40 00 00 00 00 00  00 00 00 00 00 00 00 00   ..@.............
000100B0   00 00 00 00 00 00 00 00  00 00 5C 00 00 01 6F 70   ..........\...op
000100C0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000100D0   00 00 00 80 2F 63 65 6C  6C 5F 6D 77 5F 63 66 73   ...€/cell_mw_cfs
000100E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000100F0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010100   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010110   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010120   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010130   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010140   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010150   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010160   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010170   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010180   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010190   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000101A0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000101B0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000101C0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000101D0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000101E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000101F0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010200   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010210   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010220   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010230   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010240   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010250   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010260   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010270   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010280   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010290   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000102A0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000102B0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000102C0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000102D0   00 00 00 00 00 00 00 7C  00 00 00 00 00 00 00 00   .......|........
000102E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000102F0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010300   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010310   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010320   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010330   00 00 00 00 00 00 00 00  80 00 00 00 00 55 FD 70   ........€....Uýp
00010340   80 00 00 00 00 55 E0 00  80 00 00 00 00 55 F8 00   €....Uà.€....Uø.
00010350   00 00 00 00 00 00 00 00  00 00 00 00 00 00 40 00   ..............@.
00010360   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010370   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010380   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010390   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000103A0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000103B0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000103C0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000103D0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000103E0   00 00 00 00 00 00 00 00  00 00 00 00 00 01 00 00   ................
000103F0   00 00 00 00 00 00 00 3C  00 00 00 00 00 3B D3 23   .......<.....;Ó#
00010400   00 00 00 00 00 7D 0F 82  00 00 00 00 00 00 00 9F   .....}.‚.......Ÿ
00010410   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010420   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010430   00 00 00 00 49 B0 5E 3B  00 00 00 00 01 F2 3E 26   ....I°^;.....ò>&
00010440   00 00 00 00 01 E2 86 3B  00 00 00 00 00 00 0B B8   .....â†;.......¸
00010450   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010460   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010470   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010480   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010490   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000104A0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 40 00   ..............@.
000104B0   00 00 00 40 00 00 00 00  00 00 00 00 00 00 00 00   ...@............
000104C0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000104D0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000104E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
000104F0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010500   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010510   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
00010520   00 00 00 03 00 00 00 08  00 00 00 78 00 00 00 00   ...........x....
00010530   00 00 80 10 02 02 FF FF  00 00 00 00 00 00 3F FF   ..€...ÿÿ......?ÿ
00010540   00 00 00 00 00 00 07 FF  00 00 00 00 00 00 00 00   .......ÿ........
00010550   00 00 00 00 00 00 00 00  00 00 00 00 19 54 01 19   .............T..

Other examples:

http://pastebin.com/3L241qu3
http://pastebin.com/WJv40nUQ
http://pastie.org/1529241
http://pastie.org/1588747

FLASH[edit | edit source]

RBD[edit | edit source]

SATA/ATA/ATAPI[edit | edit source]

ATA Interrupt Handler[edit | edit source]

0x0026B984 (3.15)

ATA_SetDMA[edit | edit source]

0x00268ADC (3.15)

ATA_make_PRD_table[edit | edit source]

0x00267DB4 (3.15)

This function initializes a PRD (Physical Region Descriptor) table.

ClearPATACInterrupt[edit | edit source]

0x00267CAC (3.15)

EnablePATACInterrupt[edit | edit source]

0x00267D44 (3.15)

DisablePATACInterrupt[edit | edit source]

0x00267AF0 (3.15)

ATA_read_AltStatus_reg[edit | edit source]

0x00267C40 (3.15)

This function reads the ATA Alternate Status Register and returns it's value.

ATA_write_DATA_reg[edit | edit source]

0x00268A10 (3.15)

This function writes a 16-bit value to the ATA Data Register.

ATA_read_DATA_reg[edit | edit source]

0x0026887C (3.15)

ATA_write_DATA[edit | edit source]

0x0026635C (3.15)

This function writes several 16-bit values to the ATA Data register.

ATA_write_CMD_reg[edit | edit source]

0x002688A0 (3.15)

ATA_read_Error_reg[edit | edit source]

0x00267BD4 (3.15)

ATA_write_Features_reg[edit | edit source]

0x002689F0 (3.15)

ATA_write_DevCtrl_reg[edit | edit source]

0x00267BB4 (3.15)

ATA_write_TaskFile_regs[edit | edit source]

0x00266BC8 (3.15) 0x002665A0 (3.15)

ATA_send_ATAPI_cmd[edit | edit source]

0x002655F4 (3.15)

ATA_send_cmd[edit | edit source]

0x0026580C (3.15)

ATA_send_ReadSectors_cmd[edit | edit source]

This function uses LBA28.

0x0025D2B4 (3.15)

ATA_send_WriteSectors_cmd[edit | edit source]

This function uses LBA28.

0x0025CEF4 (3.15)

ATA_send_ReadDMA_cmd[edit | edit source]

This function uses LBA28.

0x0025D380 (3.15)

ATA_send_WriteDMA_cmd[edit | edit source]

This function uses LBA28.

0x0025CFB8 (3.15)

ATA_send_ReadDMAExt_cmd[edit | edit source]

This function uses LBA48.

0x0025D74C (3.15)

ATA_send_WriteDMAExt_cmd[edit | edit source]

This function uses LBA48.

0x0025D664 (3.15)

ATA_send_IdentifyDevice_cmd[edit | edit source]

0x0025D4D8 (3.15)

ATA_send_IdentifyPacketDevice_cmd[edit | edit source]

0x0025D448 (3.15)

ATA_send_FlushCache_cmd[edit | edit source]

0x0025D5E8 (3.15)

ATA_send_FlushCacheExt_cmd[edit | edit source]

0x0025D568 (3.15)

ATA_send_StandbyImmediate_cmd[edit | edit source]

0x0025D07C (3.15)

ATA_send_SetFeatures_cmd[edit | edit source]

0x0025D208 (3.15)

ATA_send_SMARTEnable_cmd[edit | edit source]

0x0025D0F8 (3.15)

ATA_send_SMARTSaveAttributeValue_cmd[edit | edit source]

0x0025D180 (3.15)

ATA_SetUDMAMode[edit | edit source]

0x00260EE8 (3.15)

Parameters[edit | edit source]

r5 - UltraDMA mode (0-5)

Booting a Bootloader from VFLASH[edit | edit source]

Coming soon !!!

High precision timers[edit | edit source]

These timers are used e.g. in SATA/ATA/ATAPI driver.

timer_add[edit | edit source]

0x002C3F2C (3.15)

timer_del[edit | edit source]

0x002C41AC (3.15)

timer_run_expired[edit | edit source]

This function is called from HDEC interrupt handler.

0x002C4020 (3.15)

timer_set_HDEC[edit | edit source]

0x002BCF80 (3.15)

SPE[edit | edit source]

There are 3 SPE classes.

The HV call lv1_construct_logical_spe can create LogicalSPE, SPEType1 and SPEType2 objects.

The syscall 0x10040 creates only SPEType1 objects.

The SPEType1 and SPEType2 objects cannot be created when isolation mode is disabled. The right most bit of repository node sys.lv1.iso_enbl is checked and when it's not 1 then the SPEType1 and SPEType2 objects cannot be created. In LPAR 1, this check succeedes always. Only in LPARs different from 1, the repository node sys.lv1.iso_enbl is checked.

LogicalSPE[edit | edit source]

SPE type = 0

Objects of this class are used e.g. on Linux.

vtable[edit | edit source]

0x00358360 (3.15)

offset 0x20 - pointer to TOC entry of interrupt handler for SPE

Member variables[edit | edit source]

offset 0x38 - pointer to LPAR obj that owns this SPE obj

offset 0x78 - table of pointers to Outlet objects (3 * 8 bytes, one for each Class 0-2)

offset 0xB0 - pointer to VAS object

offset 0xC8 - pointer to Logical PPE object

offset 0xE0 - SPE id

offset 0x1A0 - pointer to MMIO Memory Region object

offset 0x1A8 - pointer to Shadow Registers Memory Region object

Objects[edit | edit source]

Here is the list of logical SPE objects i found in HV 3.15:

  • 0x003A82E0 - SPE id 0
  • 0x003A8660 - SPE id 1
  • 0x003ABA00 - SPE id 2
  • 0x003B4010 - SPE id 3
  • 0x003B4D60 - SPE id 4
  • 0x003B5970 - SPE id 5

SPEType1[edit | edit source]

SPE type = 1

vtable[edit | edit source]

0x00359750

Member Variables[edit | edit source]

offset 0x198 - pointer to MMIO Memory Region object

offset 0x1A0 - pointer to Shadow Registers Memory Region object

SPEType2[edit | edit source]

SPE type = 2

vtable[edit | edit source]

0x00359790

SPE Register Shadow Area[edit | edit source]

  • HV createas a SPE Register Shadow Area for each contstructed SPE.
  • The area is 1 4Kb page of physical memory.
  • When SPE state changes then HV updates data in this area.
  • The value of shadow_addr that is returned by lv1_construct_logical_spe is a LPAR start address of this area and it cannot be accessed until it's mapped in the HTAB.
  • The SPE Register Shadow Area may be mapped only with read-only page protection or else HV call lv1_insert_htab_entry fails. I tested it with PSGroove and could map the whole memory range and read it after i constructed SPE of type 1 with lv1_construct_logical_spe.
  • The shadow_addr is also returned by syscall_10040 (that creates SPE of type 1) but it returns already mapped Process address so HV Processes do not have to map it in HTAB.
  • When an isoated SPU is done, HV Processes checks the value at offset 0x30 to determine if the SPU execution was successfull or not.
  • GameOS checks also the value at offset 0x30 in the SPE Shadow Area.
  • When GameOS creates SPE of type 1 then it maps only SPE Register Shadow Area into it's address space.

SPE Register Shadow Area Offsets[edit | edit source]

0x30 - SPU_Status register value (4 bytes)

0xF10 - ?

0xF18 - ?

Stop Code[edit | edit source]

  • The high-order 16 bit of SPU_Status register value is a Stop Code.

Here is the list of Stop Codes i extracted from HV Processes which read the value at offset 0x30 when SPU is done:

Value Description
0xA Success
0xC Access Violation (LPAR auth id error)
0xE  ?
0xF Revoked
0x12 Invalid Parameter
0x13  ?
0x17 Invalid Parameter
0x25  ?

SPU_send_MFC_cmd[edit | edit source]

0x002B09B0 (3.15)

This function programs a MFC.

SPU_write_MFC_cmd_status_reg[edit | edit source]

0x002AEE70 (3.15)

SPU_write_Sig_Notify1_reg[edit | edit source]

0x002AEF4C (3.15)

SPU_write_Sig_Notify2_reg[edit | edit source]

0x002AEF30 (3.15)

SPU_write_Sig_Notify1_and_Notify2[edit | edit source]

0x002B0A78 (3.15)

SPU_enable_iso_load_request[edit | edit source]

0x002AEDE0 (3.15)

SPU_iso_load_request[edit | edit source]

0x002AEED0 (3.15)

SPU_enable_runcntl[edit | edit source]

0x002AEB24 (3.15)

SPU_stop_request[edit | edit source]

0x002AEEF0 (3.15)

SPU_run_request[edit | edit source]

0x002AEF10 (3.15)

SPU_read_status_reg[edit | edit source]

0x002AE978 (3.15)

SPU_read_Mbox_Stat_reg[edit | edit source]

0x002AE998 (3.15)

lv1_undocumented_function_62[edit | edit source]

Updates SLB entry.

Parameters[edit | edit source]

%r3 - SPE id

%r4 - ? (valid values: 0 - 3)

%r5 - SLB entry index (valid values: 0 - 7)

%r6 - ESID

%r7 - VSID

spe_type1_interrupt_handler[edit | edit source]

0x0030E238 (3.15)

spe_type2_interrupt_handler[edit | edit source]

0x003103F8 (3.15)

spe_type3_interrupt_handler[edit | edit source]

0x002F36F4 (3.15)

Isolation[edit | edit source]

Loaders Table[edit | edit source]

  • All the binary files needed for isolation and decryption are already stored in HV memory !!!
  • They are probably loaded during HV initialization from FLASH.
  • The table has 9 entries.
  • Each entry is 16 bytes large.

0x00010100 (3.15)

Loaders Table Entry[edit | edit source]

offset 0x0 - pointer to data in memory

offset 0x8 - size of data

Here are the contents of the Loaders Table from HV 3.15:

Index Name Address of Data in HV Dump Size of Data Entry Id
0 lv1ldr 0x0C150000 0x1E5CC 0x01
1 metldr 0x00011000 0xE8D0 0x00
2 lv2ldr 0x00020000 0x16DA0 0x02
3 isoldr 0x00055000 0x12E44 0x04
4 appldr 0x00037000 0x1DAE4 0x03
5 EID0 0x00068000 0x860 0x0C
6 QA Flag 0x00069010 0x8 0x0F
7 QA Flag Token 0x00069020 0x50 0x10
8 Trace Level 0x00069070 0x8 0x11

Methods[edit | edit source]

get_iso_loaders_tab - 0x002B0B70 (3.15)

iso_loaders_tab_get_entry - 0x002B0CB8 (3.15)

metldr[edit | edit source]

Loading metldr[edit | edit source]

  • Physical/Virtual memory address of an isolation module that should be loaded by metldr is written into SPU register SPU_In_Mbox. The SPU register SPU_In_Mbox is 32bit, so 64bit memory address is written in 2 steps.
  • MFC relocation is turned off by clearing R-bit in SPU register MFC_SR1. By doing this, HV enables real address mode for MFC of SPU.
  • On GameOS, it also works with relocation on. You just have to initialize SLB of SPU and insert valid SLB entries.
  • Physical/Virtual memory address of metldr is written to SPU registers Sig_Notify1 and Sig_Notify2
  • Isolation load request is enabled by writing SPU register SPU_PrivCntl
  • Isolation load request is made by writing value 0x3 into SPU register SPU_RunCntl

Methods[edit | edit source]

SPE_load_request_metldr - 0x002B00A4 (3.15)

lv2ldr[edit | edit source]

  • lv2ldr is used to decrypt lv2_kernel.self
  • syscalls 0x10042 and 0x1004A use lv2ldr
  • syscall 0x10042 is used by HV Process 3 during LV2 LPAR construction
  • syscall 0x1004A uses different parameters as syscall 0x10042

Methods[edit | edit source]

SPE_load_request_lv2ldr_1 - 0x002AE82C (3.15)

SPE_load_request_lv2ldr_2 - 0x002AE8D8 (3.15)

Loading lv2ldr[edit | edit source]

  • 64 bit memory address of lv2ldr is written into 32 bit SPU register SPU_In_Mbox
  • metldr is loaded

isoldr[edit | edit source]

  • isoldr is used for executing isolated SPUs
  • syscall 0x10043 and HV call lv1_undocumented_function_209 use isoldr to execute isolated SPUs
  • EID0 data is transferred to Local Storage Address 0x3E400 by MFC
  • Revoke List For Program is transferred to Local Storage Address 0x3F000 by MFC

Revoke List For Programs[edit | edit source]

0x00361980 (3.15)

Methods[edit | edit source]

SPE_load_request_isoldr - 0x002B0394

Loading isoldr[edit | edit source]

  • 64 bit memory address of isoldr is written into 32 bit SPU register SPU_In_Mbox
  • metldr is loaded

appldr[edit | edit source]

  • appldr is used for decryption of SELFs or EDATs
  • HV call lv1_authenticate_program_segment loads appldr

Methods[edit | edit source]

SPE_load_request_appldr - 0x002AE900

Loading appldr[edit | edit source]

  • 64 bit memory address of appldr is written into 32 bit SPU register SPU_In_Mbox
  • metldr is loaded

Decrypting SELFs with appldr and lv1_authenticate_program_segment[edit | edit source]

  • lv1_authenticate_program_segment loads and prepares appldr for SELF decryption.
  • When appldr is ready to decrypt data, it sends a message via mailbox.
  • The address and the size of the encrypted data is passed to appldr via a shared memory.

Socket[edit | edit source]

The socket supports only one address family 0x1F, one socket type 0 and one protocol 0.

Socket address[edit | edit source]

Socket address is called port ID. Valid port IDs are 0-63. Port ID 0 is reserved.

Socket state[edit | edit source]

2 - LISTEN

Socket table[edit | edit source]

The socket table contains 64 entries, one for each port ID. Each entry is 16 bytes large.

The socket table is at 0x0035F6E8 (3.15).

Here is the list of opened sockets i found in HV 3.15:

  • 0x00091FE0 (port ID 0x23, accepts connections)
  • 0x00127850 (port ID 0x24, accepts connections)
  • 0x0012F810 (port ID 0x25, accepts connections)

Socket table entry[edit | edit source]

offset 0x0 - pointer to Socket obj

offset 0x8 - socket accepts connections or not (0 - does not accept, 1 - accepts, 1 byte)

vtable[edit | edit source]

0x00355DB0 (3.15)

offset 0xB0 - bind

offset 0xB8 - listen

offset 0xC8 - connect

Member variables[edit | edit source]

offset 0x360 - socket state (4 bytes)

offset 0x368 - port ID (8 bytes)

offset 0x370 - max backlog queue size (8 bytes)

Virtual Address Space[edit | edit source]

VAS[edit | edit source]

vtable[edit | edit source]

0x00357958 (3.15)

Member variables[edit | edit source]

offset 0x18 - pointer to LPAR that owns this VAS object

offset 0x48 - VAS id (8 bytes)

offset 0x70 - number of page sizes (4 bytes)

offset 0x74 - log2 of HTAB size

offset 0x78 - pointer to HTAB object

Objects[edit | edit source]

Here is the list of the VAS objects i found in HV dump 3.15:

  • 0x001C8050 (VAS id 2, LPAR 1)
  • 0x003B4910 (VAS id 3, LPAR 2)
  • 0x003BDB50 (VAS id 48, LPAR 2)

HTAB[edit | edit source]

0x38(-0x69A8(HSPRG0)) - pointer to the currently active HTAB in LPAR

vtable[edit | edit source]

0x003575B0 (3.15)

Member variables[edit | edit source]

offset 0x48 - pointer to first PTE

offset 0x60 - LPID (4 bytes)

offset 0x64 - log2 of HTAB size (4 bytes)

Objects[edit | edit source]

Here is the list of the HTAB objects i found in HV dump 3.15:

  • 0x001C8270 (VAS id 2, LPAR 1)
 * 0x00180000 - HTAB PTEs (HTAB size 256 kB)
  • 0x003A8050 (VAS id 3, LPAR 2)
 * 0x00500000 - HTAB PTEs (HTAB size 1 MB)
  • 0x003BC510 (VAS id 48, LPAR 2)
 * 0x00800000 - HTAB PTEs (HTAB size 1 MB)

LPAR_change_HTAB[edit | edit source]

This function changes currently active HTAB. It writes to SDR1 register where HTAB address and size is stored.

0x002BE5D4 (3.15)

Process SLB[edit | edit source]

Each HV process has 16 SLB entries.

Each SLB entry is 16 bytes large and is in format expected by opcode slbmte.

Most of the entries are zero (invalid).

Each process has 4 valid SLB entries: code, data, heap and stack.

Process 3[edit | edit source]

SLB entries[edit | edit source]

0x0012D1F0 (3.15)

Name ESID VSID
code 0x8 0x38
data 0xC 0x3C
heap 0xA 0x3A
stack 0xF 0x3F

Process 5[edit | edit source]

SLB entries[edit | edit source]

0x00093120 (3.15)

Name ESID VSID
code 0x8 0x48
data 0xC 0x4C
heap 0xA 0x4A
stack 0xF 0x4F

Process 6[edit | edit source]

SLB entries[edit | edit source]

0x000E6960 (3.15)

Name ESID VSID
code 0x8 0x58
data 0xC 0x5C
heap 0xA 0x5A
stack 0xF 0x5F

Process 9[edit | edit source]

SLB entries[edit | edit source]

0x00763E20 (3.15)

Name ESID VSID
code 0x8 0x8
data 0xC 0xC
heap 0xA 0xA
stack 0xF 0xF

VUART[edit | edit source]

VUART is a bi-directional communication link. A VUART object has a peer VUART object.

Data written to a VUART object is stored NOT in the data buffer of the VUART object but in the data buffer of the peer VUART object.

VUART table[edit | edit source]

Every LPAR has a VUART table. A VUART table has 256 entries. Each entry is a pointer to a VUART object that implements VUART interface.

0x00677218 (3.15) - address of VUART table of LPAR 1

Here is the list of all VUART objects in LPAR 1 i found in HV 3.15:

  • 0x006ABD90 - VUART 0
  • 0x006ABEB0 - VUART 1
  • 0x006A3CB0 - VUART 2
  • 0x006A3DD0 - VUART 3
  • 0x000A3410 - VUART 5
  • 0x000A3250 - VUART 6

VUART [0-3] are used by /dev/sc[0-3] respectively.

VUART [0-3] are linked to VUART objects of different type i could not yet identify. These unknown VUART objects use eieio opcode a lot. So i think, they communicate with hardware peripheral.

A write/read to/from /dev/sc[0-3] is a write/read to/from VUART.


0x00762AA8 (3.15) - address of VUART table of LPAR 2

Here is the list of all VUART objects in LPAR 2 i found in HV 3.15:

  • 0x00126660 - VUART 0
  • 0x000A3010 - VUART 2

VUART 0 and VUART 2 of LPAR 2 are created by Process 9 during LPAR construction.

VUART class[edit | edit source]

Member variables[edit | edit source]

offset 0x48 - pointer to peer VUART object

offset 0x58 - write pointer into data ring buffer

offset 0x60 - read pointer into data ring buffer

offset 0x68 - pointer to data ring buffer

offset 0x70 - size of data ring buffer (8 bytes)

offset 0x78 - size of data stored in data ring buffer currently (8 bytes)

offset 0x88 - tx trigger (8 bytes)

offset 0x90 - rx trigger (8 bytes)

offset 0x98 - interrupt mask (8 bytes)

offset 0xA8 - port number (4 bytes)

Methods[edit | edit source]

pmpi_read_virtual_uart(port, buf, size, nread) - 0x002EB30C (3.15)

pmpi_write_virtual_uart(port, buf, size, nwritten) - 0x002EB0EC (3.15)

VUART_read(pointer to VUART object, buf, size, nread) - 0x002E8654 (3.15)

VUART_write(pointer to VUART object, buf, size, nwritten) - 0x002E8428 (3.15)

Guest OS VUART 0 (AV Manager)[edit | edit source]

All data sent to VUART 0 in LPAR 2 is written into the data buffer of VUART 5 of LPAR 1.

VUART 5 of LPAR 1 is accessed by Process 9 in LPAR 1 through the file /proc/partitions/2/vuart/0.

  • Process 9 of LPAR 1 uses RSX syscalls to access RSX driver and memory mapped device access (/dev/ioif0).

Guest OS VUART 2 (System Manager)[edit | edit source]

All data sent to VUART 2 in LPAR 2 is written into the data buffer of VUART 6 of LPAR 1.

VUART 6 of LPAR 1 is accessed by Process 9 in LPAR 1 through the file /proc/partitions/2/vuart/2.

  • System manager supports 62 (0-61) service ids.
  • Process 9 has a SID table. SID table has 62 entries.
  • Each entry is a pointer to a function responsible for processing SID packets.

AV Manager[edit | edit source]

Crossreference: gitbrew.org::AV Manager

  • AV Manager is running in Process 9 of HV.
  • It communicates with Guest OS through /proc/partitions/0/vuart/0 file.
  • GameOS accesses AV Manager through syscalls 367 - 370.
  • PS2 Soft EMU accesses AV Manager also.
  • Communicates with SYSCON 0 (/dev/sc0)
  • Communicates with IOIF0 (/dev/ioif0 or RSX)

Commands[edit | edit source]

Get HDCP KSV (0xC)[edit | edit source]

  • Returns HDCP KSV
  • HDMI KSV is read from SYSCON
  • KSV is stored in memory dump of HV process 9 (where AV Manager runs)

SYSCON request packet:

30 01 0200 0000 8033 00000000 0004 0004 11 00 0000 0000ff01

Set HDMI Mode (0x40001)[edit | edit source]

  • Sets HDMI mode
  • Mode is set by SYSCON
  • Disabling HDCP

System Manager (SM)[edit | edit source]

  • System Manager (SM) is running in Process 9 of HV.
  • It communicates with Guest OS through /proc/partitions/2/vuart/2 file.
  • GameOS accesses SM through syscalls 372 - 415

System Manager class[edit | edit source]

Member variables[edit | edit source]

offset 0x10 - LPAR state (8 bytes)

offset 0x68 - LPAR auth id

offset 0x70 - LPAR name

offset 0x90 - LPAR image path

offset 0x1C0 - LPAR ability (8 bytes)

Types of System Manager[edit | edit source]

  • There are 6 different SM types
  • When Process 9 starts it reads profile file, by default DEFAULT.SPP, by sending requests to SPL (Secure Profile Loader) and constructs System Managers listed in this profile file.
  • So, the profile file controls which System Manager types are available later.
Name LPAR name
SCE_CELLOS_PME -
SCE_CELLOS_SYSTEM_MGR PS3_LPAR
SCE_CELLOS_SYSTEM_MGR_PS2 PS2_LPAR
SCE_CELLOS_SYSTEM_MGR_PS2_SW PS2_SW_LPAR
SCE_CELLOS_SYSTEM_MGR_PS2_GX PS2_GX_LPAR
SCE_CELLOS_SYSTEM_MGR_LINUX LINUX_LPAR

Ability Bitmask[edit | edit source]

Index Name Ability Bitmask (Hex) Ability Bitmask (Binary)
0 SCE_CELLOS_PME 0x1 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
1 SCE_CELLOS_SYSTEM_MGR 0x3BF7EF 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0011 1011 1111 0111 1110 1111
2 SCE_CELLOS_SYSTEM_MGR_PS2_SW 0x1226D 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0010 0010 0110 1101
3 SCE_CELLOS_SYSTEM_MGR_LINUX 0x40012 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100 0000 0000 0001 0010
Bit Position (from right) SID Description
1 5 (SET_NEXT_OP) Shutdown or Reboot LPAR
2 5 (SET_NEXT_OP) Boot PS3 LPAR
3 5 (SET_NEXT_OP) Boot PS2_SW LPAR
4 5 (SET_NEXT_OP) Boot LINUX LPAR
5 12 (CONTROL_LED) Control LED
6 21 (RING_BUZZER) Ring Buzzer
7 19 (SET_CONFIG) Set Config
9 25 / 50 (FAN_POLICY) Fan Policy
10 26 (REQUEST_ERROR_LOG) Request Error Log
10 28 (REQUEST_BE_COUNT) Request BE Count
10 32 (REQUEST_SYSTEM_EVENT_LOG) Request System Event Log
12 30 (REQUEST_SC_VERSION) Request SC Version
14 39 (SET_SHOP_DEMO_MODE) Set Shop Demo Mode

Service ID (SID)[edit | edit source]

SM supports 62 (0-61) SIDs.

The value of SM member variable ability controls which SIDs may be used by LPAR.

SID Name Description
0 - -
1 REQUEST -
2 RESPONSE -
3 COMMAND -
4 EXTERN_EVENT -
5 SET_NEXT_OP -
6 - -
7 - -
8 SET_ATTR -
9 GET_INTER_LPAR_PARAM -
10 SET_INTER_LPAR_PARAM -
11 - -
12 CONTROL_LED -
13 TEMPERATURE -
14 - -
15 Shares data with 25 -
16 - -
17 - -
18 - -
19 SET_CONFIG -
20 - -
21 RING_BUZZER -
22 - -
23 - -
24 - -
25 FAN_POLICY -
26 REQUEST_ERROR_LOG -
27 - -
28 REQUEST_BE_COUNT -
29 - -
30 REQUEST_SC_VERSION -
31 - -
32 REQUEST_SYSTEM_EVENT_LOG -
33 - -
34 RTC_ALARM -
35 - -
36 RTC_ALARM -
37 - -
38 RTC_ALARM -
39 SET_SHOP_DEMO_MODE -
40 BOOT_PARAMETER -
41 - -
42 BOOT_PARAMETER -
43 - -
44 FACTORY_PROCESS_COMP -
45 - -
46 FACTORY_PROCESS_COMP -
47 - -
48 FACTORY_PROCESS_COMP -
49 - -
50 FAN_POLICY -
51 - -
52 - -
53 - -
54 - -
55 - -
56 - -
57 - -
58 - -
59 - -
60 - -
61 - -

12 - CONTROL_LED[edit | edit source]

  • I have tested this service with PSGroove and GameOS is allowed to use it.
  • GameOS syscall 386 uses this service.

Packet Body[edit | edit source]

struct sysmgr_ctrl_led
{
    u8 field0;
    u8 field1;
    u8 field2;
    u8 res1;
    u8 field4;
    u8 field5;
    u8 res2[10];
};

Parameters[edit | edit source]

I have tested the following parameters with this service:

field0 field1 field2 field4 field5 Description
0x1 0x0 0xFF 0xFF 0xFF Turns off the power button LED
0x1 0x1 0xFF 0xFF 0xFF Turns on the power button LED

21 - RING_BUZZER[edit | edit source]

  • I have tested this service with PSGroove and GameOS is allowed to use it

Packet Body[edit | edit source]

struct sysmgr_ring_buzzer
{
    u8 res1;
    u8 field1;
    u8 field2;
    u8 res2;
    u32 field4;
};

Parameters[edit | edit source]

I have tested the following parameters with this service:

field1 field2 field4 Description
0x29 0x4 0x6 Makes a short single beep
0x29 0xA 0x1B6 Makes a triple beep
0x29 0x7 0x36 -
0x29 0xA 0xFFF Makes a continuous beep

field 1 seems relative to beep tone, as 0x25 sounds different

Active System Managers in HV dump 3.15[edit | edit source]

There are 4 active SMs in HV dump.

Index Name LPAR auth id LPAR image pathname Ability Bitmask (Hex)
0 SCE_CELLOS_PME 0x1070000001000001 /flh/os/this_is_dummy 0x1
1 SCE_CELLOS_SYSTEM_MGR 0x1070000002000001 /flh/os/lv2_kernel.self 0x3BF7EF
2 SCE_CELLOS_SYSTEM_MGR_PS2_SW 0x1020000003000001 /local_sys0/ps2emu/ps2_softemu.self 0x1226D
3 SCE_CELLOS_SYSTEM_MGR_LINUX 0x1080000004000001 /flh/lx/linux 0x40012
  • GameOS file image lv2_kernel.self is stored on /dev/rflash1
  • Linux file image is stored on /dev/rflash_1x or /dev/rflash_1xp

Booting Linux LPAR through System Manager[edit | edit source]

To boot Linux LPAR from GameOS when Linux support was not removed (Ability Mask of PS3 System Manager needs patching !!!):

  • Send SID packet SET_NEXT_OP with operation OP_LPAR_REBOOT and the index of Linux system manager to System Manager (VUART 2)
  • Send SID packet REQUEST with type SHUTDOWN to System Manager (VUART 2)
  • Execute lv1_panic HV call in GameOS

It should also work when Linux support was removed but Linux system manager was not removed from Process 9 and also assumed that a Linux kernel image is stored at the right place in /dev/rflash_1x.

It's just a theory, nothing else, that i gathered during HV reversing. It needs a practical proof. Unfortunately, i don't have access to Hypervisor.

Booting modified and reencrypted lv2_kernel.self[edit | edit source]

  • The System Manager of GameOS sends the path to lv2_kernel.self to SLL (Secure LPAR Loader) and SLL loads it from FLASH device file /dev/rflash1
  • I stored a new lv2_kernel.self on FLASH directly by writing FLASH from GameOS. It't risky but if you know what you are doing then it's safe. I warned you guys. You could brick your PS3.
  • Then i added a new TOC entry to FLASH device which points to the new lv2_kernel.self
  • I patched the path to lv2_kernel.self in the System Manager of GameOS so it points to my new GameOS kernel (You need HV rights to do it)
  • Then i rebooted GameOS without rebooting HV, so the patched file path should not change
  • This method has the advantage that when the new lv2_kernel.self won't work you can just reboot HV and it will load the original lv2_kernel.self again
  • lv2_kernel.self can be also loaded from GameOS dev_flash. For that, you have to change the path to lv2_kernel.self in default.spp from /flh/os/lv2_kernel.self to /local_sys0/lv2_kernel.self and store lv2_kernel.self on dev_flash.

AV Manager[edit | edit source]

All data sent to VUART 0 in LPAR 2 is written into the data buffer of VUART 5 of LPAR 1.

VUART 5 of LPAR 1 is accessed by Process 9 in LPAR 1 through the file /proc/partitions/2/vuart/0.

  • During initialization, AV Manager opens /dev/ioif0 device and maps different address ranges of the device into address space of Process 9
  • /dev/ioif0 is NOT opened and mapped if the value of repository node lv1.rsx.enable is less than 1
  • /dev/ioif0 is mapped with READ/WRITE protection
  • File descriptor of /dev/ioif0 in Process 9 is 4
  • AV Manager supports a lot more commands than used on Linux
  • Every command is implemented by a class

Mapped Address Ranges From /dev/ioif0[edit | edit source]

The base address of /dev/ioif0 is 0x28000000000. The device supports only mmap system call, it cannot be read or written. It also doesn't support ioctl.

Index Absolute Address Range Size Mapped Address in Process 9 Address Space
0 0x28000000000 - 0x28000002000 0x2000 0xA0019000
1 0x28001800000 - 0x28001801000 0x1000 0xA0004000
2 0x28000600000 - 0x28000604000 0x4000 0xA001A000
3 0x28000680000 - 0x28000684000 0x4000 0xA0006000
4 0x28000080000 - 0x28000088000 0x8000 0xA000A000
5 0x28000088000 - 0x28000089000 0x1000 0xA000E000
6 0x2800000C000 - 0x2800000D000 0x1000 0xA0016000
7 0x2800008A000 - 0x2800008B000 0x1000 0xA0017000
8 0x2800008C000 - 0x2800008D000 0x1000 0xA0018000

Process socket services[edit | edit source]

Function ID and Packet ID[edit | edit source]

  • Processes 3, 5 and 6 provide services (functions) to other Processes through sockets (something like RPC).
  • A service is identified by a function ID.
  • Each process has a hash table which maps a function ID to socket port ID.
  • Services (functions) can be further differentiated by a packet ID.
  • To request a service, a Process sends a packet with specified function and packet ID to the Process that provides the service.
  • A process that provides a service (function) has a table of objects which handle different packet IDs.
  • Services are synchronous, a client sends a request and waits for a response.
  • If a Process requests a service that is located in the same Process then the service is called directly and sockets are not used !!! (e.g. SLL requests from DM creating VUART port during GameOS loading, SLL and DM are in the same Process, so SLL calls DM directly)

Port ID - Process ID mapping[edit | edit source]

Port ID Process ID
0x23 6
0x24 5
0x25 3

Function ID - Port ID mapping[edit | edit source]

Function ID Port ID Supported Packet IDs Function Description
0x2000 0x23 0x2001 - 0x2017 Virtual TRM Manager
0x3000 0x24 0x3001 - 0x3003 Secure RTC
0x5000 0x23 0x5001 - 0x500A Storage Manager
0x6000 0x23 0x6001 - 0x6011 Update Manager
0x8000 8 0x8001 - 0x8005 Updater Frontend
0x9000 0x24 0x9001 - 0x9016 SC Manager
0x10000 0x23 0x10001-0x10007 SBM (South Bridge Manager)
0x11000 0x25 0x11001 - 0x11002 SPM (Security Policy Manager)
0x14000 0x25 0x14004 - 0x14005 SLL (Secure LPAR Loader)
0x15000 0x24 0x15001, 0x15003, 0x15009 SPL (Secure Profile Loader)
0x17000 0x24 0x17001 - 0x17017 Indi Info Manager
0x18000 0x25 0x18001, 0x18002, 0x18004 Dispatcher Manager
0x19000 0x24 0x19002 - 0x19005 AIM
0x22000 0x16 0x22001 - 0x22004 Factory Data Manager
0x24000 0x23 0x24001 - 0x24002 USB Dongle Authenticator
0x25000 0x23 0x25001 - 0x25002 User Token Manager

SS Packet[edit | edit source]

  • SS means Secure Service ?
  • Processes send SS Packets to request a service or to reply to a service request.

Member variables[edit | edit source]

offset 0x8 - packet ID (8 bytes)

offset 0x10 - function ID (8 bytes)

offset 0x18 - return value (4 bytes)

offset 0x20 - subject ID (2 * 8 bytes)

Header[edit | edit source]

  • All services use a common header.
  • The header of a SS Packet is 0x28 bytes large.
struct ss_header
{
    uint64_t packet_id;
    uint64_t function_id;
    uint32_t retval;
    uint8_t res[4];
    uint64_t laid;             /* LPAR Authority ID */
    uint64_t paid;             /* Program Authority ID */
}

SS Service Return Values[edit | edit source]

Error Code Description
0x00000000 Success
0x00000005 Access Violation
0x00000006 No Entry ?
0x00000009 Invalid Parameter
0x0000000F Call Limit Exceeded ?

Body[edit | edit source]

  • The body of a SS Packet follows after the header.
  • The size of the body depends on a used service.

LPAR Memory Management[edit | edit source]

Memory Region class[edit | edit source]

This class is the base class for different memory region types.

vtable[edit | edit source]

0x003578B0 (3.15)

Member variables[edit | edit source]

offset 0x40 - pointer to LPAR object that owns this memory region

offset 0x48 - type of memory region (8 bytes)

offset 0x50 - LPAR start address of memory region

offset 0x58 - size of memory region (8 bytes)

offset 0x60 - flags (8 bytes)

offset 0xA0 - log2 of page size

Generating New LPAR Memory Region Addresses[edit | edit source]

generate_new_lpar_mem_region_address(?, memory region size, log2(page size), ?, ?) - 002C82E8 (3.15)

generate_new_lpar_mem_region_address - 002C6570 (3.41)

  • The function returns a new LPAR memory region address.
  • This method is used e.g. in all HV calls which create any kind of memory regions, e.g. lv1_allocate_memory, lv1_map_htab, lv1_undocumented_function_114, lv1_construct_logical_spe, lv1_map_device_mmio_region or syscall 0x10040.

Encoding LPAR Memory Region Start Addresses and Sizes[edit | edit source]

  • Size of LPAR memory region is encoded in the LPAR memory region start address.
  • That is why e.g. the LPAR Memory Region Start Addresses of LPAR Memory Region of size 4096 byte begin with 0x300000000000, 0x300000000000 >> 42 = 0xC = log2(4096).
  • Each LPAR has a counter (8 bytes) which is incremented by 1 every time a new LPAR Memory Region is created.
  • Before incrementing, the counter is shifted left by log2(LPAR Memory Region Size) and ored with log2(LPAR Memory Region Size) << 42.
LPAR Memory Region Start Address >> 42 = log2(LPAR Memory Region Size)
LPAR Memory Region Start Address = (log2(LPAR Memory Region Size) << 42) |
   (counter << log2(LPAR Memory Region Size))
LPAR Memory Region Address Counter[edit | edit source]
  • LPAR Memory Region Address Counter is stored at address: 0x38(LPAR ptr) + 0x9E8
  • LPAR1's Memory Region Address Counter is at address 0x00677A48 in HV dump 3.15
  • LPAR2's Memory Region Address Counter is at address 0x007632D8 in HV dump 3.15
  • LPAR1's Memory Region Address Counter is at address 0x00677A48 in HV dump 3.41
  • LPAR2's Memory Region Address Counter is at address 0x00161E68 in HV dump 3.41

Physical Memory Region class[edit | edit source]

This type of memory region is created e.g. in lv1_allocate_memory HV call or in syscall 0x10000.

vtable[edit | edit source]

0x00357D08 (3.15)

Member variables[edit | edit source]

offset 0xB0 - pointer to object that stores a list of addresses of physical pages owned by this memory region

offset 0xB8 - pointer to LPAR object that owns this memory region

offset 0xC0 - reference counter (8 bytes)

Objects[edit | edit source]

Here is the list of physical memory region objects i found in HV 3.15.

Address in HV dump LPAR id LPAR Start Address Size Flags log2(Page Size) Physical Page Addresses
0x006B5510 1 0x300000001000 0x1000 0x0 0xC 0x672000
0x006B5E50 1 0x440000040000 0x20000 0x0 0x11 0x6C0000
0x006B6980 1 0x440000060000 0x20000 0x0 0x11 0x6E0000
0x006B7F00 1 0x400000040000 0x10000 0x0 0x10 0x100000
0x003A80F0 2 0x6C0058000000 0x7000000 0x4 0x18 0x1000000 - 0x7000000
0x003BE800 2 0x300000047000 0x1000 0x0 0xC 0x1FA000
0x006BDAA0 2 0x0 0x8000000 0x8 0x1B (single huge page) 0x8000000

So, Linux kernel should be located at physical address 0x8000000 and Linux syscall handler at 0x8000C00. Too bad that the HV dump is not large enough.

GameOS Physical Memory Regions[edit | edit source]

  • GameOS allocates nearly all physical memory of PS3 for itself !!! That is why new HV calls lv1_allocate_memory with large memory region sizes will fail.
  • So when someone wants a large piece of physical memory, he can borrow it from GameOS's LPAR memory region that starts at 0x700020000000. It can be used for example to send update packages to Update Manager which are very large.

Here is the list of physical memory regions of GameOS i found in HV 3.41:

Start Address Size Access Right Max Page Size Flags Real Addresses
0x0 0x1000000 0x3 0x18 0x8 0x1000000 - 0x1FFF000
0x500000300000 0xA0000 0x3 0x10 0x8 0x380000 - 0x38F000, 0x3B0000 - 0x3BF000, 0x1E0000 - 0x1FF000, 0x3C0000 - 0x3FF000, 0xFF00000 - 0xFF1F000
0x700020000000 0xE900000 (huge memory region) 0x3 0x14 0x0 0x400000 - 0x5FF000, 0x800000 - 0xFFF000, 0x2000000 - 0xFEFF000

HTAB Memory Region class[edit | edit source]

This memory region is created when a HTAB is mapped into LPAR's address space. It's created in lv1_map_htab HV call.

vtable[edit | edit source]

0x00357C98 (3.15)

Member variables[edit | edit source]

offset 0xB0 - pointer to VAS object that owns the HTAB

Objects[edit | edit source]

Here is the list of HTAB memory region objects i found in HV 3.15.

Address in HV dump LPAR id VAS id LPAR Start Address Size Flags log2(Page Size)
0x001FE0F0 2 3 0x500000C00000 0x100000 0xC000000000000000 0x14
0x003BD850 2 3 0x500004300000 0x100000 0xC000000000000000 0x14
0x003BDEA0 2 3 0x500004500000 0x100000 0xC000000000000000 0x14

GameOS HTAB[edit | edit source]

  • HTAB of GameOS is already mapped into address space of GameOS so that is why HV call lv1_map_htab will fail until you unmap it with lv1_unmap_htab
  • Effective address of GameOS HTAB is 0x800000000F000000
  • Virtual address of GameOS HTAB is 0xF000000
  • Size of GameOS HTAB is 0x40000
  • GameOS HTAB supports large pages of size 64K and 1M
  • GameOS HTAB can be easily dumped by reading 0x40000 bytes at EA 0x800000000F000000

GameOS SLB[edit | edit source]

Here is the dump of SLB entries from GameOS 3.41:

0x8000000008000000  0x0000000000000500
0x8000000208000000  0x0000000000020500
0x8000000300000000  0x0000000000030510
0x0000000000000000  0x0000000000000000
0x0000000080000000  0x0000000000038C00
0x00000000A0000000  0x000000000003AC00
0x00000000C0000000  0x000000000003CC00
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x8000000010057960  0x8000000000313E78
0x8000000010057940  0x0000000000000000
0x800000000001B698  0x0000000000000000
0x8000000010057930  0x8000000000490708
0x80000000002B6C68  0x80000000003DE928
0x8000000010057EC0  0x80000000003DE920
0x0000000000000000  0x8000000000309810
0x80000000004B3000  0x0000000000000000
0x8000000010057CC0  0x0000000000000000
0x80000000004AF000  0x80000000004E1F00
0x80000000100579C8  0x80000000100579C0
0x80000000100579E0  0x2400002200000000
0x80000000004CF5B0  0x8000000200012000
0x80000000100579F8  0x80000000100579F0
0x8000000010057A10  0x80000000004A3A00
0x80000000004CF5B0  0x80000000004C8D00
0x800000000001BF6C  0x80000000004CD400
0x800000000001B698  0x80000000004C8100
0x80000000100579D0  0x80000000004B48C0
0x0000000000001C08  0x0000000000000000
0x8000000010057A78  0x8000000010057A70
0x8000000010057A90  0x0000000000000000
0x80000000004CF90C  0x0000000000000000
0x0000000000000000  0x8000000010057A80
0x8000000010057A90  0x8000000000309810
0x80000000004CF62C  0x0000000000000000
0x8000000010057CC0  0x0000000000000000
0x80000000004AF000  0x80000000004B48C0
0x00004000001C0000  0x0000000000000001
0x00000000D0000000  0x0000A8E3EE7D10DA
0x0000000000000000  0x0000000000000000
0x80000000004D8088  0x80000000004D9000

SPE MMIO Memory Region class[edit | edit source]

This type of memory region represents MMIO memory region of a SPE. It's created e.g. in lv1_construct_logical_spe or in syscall 0x10040.

vtable[edit | edit source]

0x003583F8 (3.15)

Member variables[edit | edit source]

Objects[edit | edit source]

Here is the list of SPE memory region objects i found in HV 3.15.

Address in HV dump LPAR id SPE LPAR Start Address Size Physical Address Flags log2(Page Size)
0x003ABC20 2 1 0x4C0000880000 0x80000 0x20000080000 0xA000000000000000 0xC
0x003AAD70 2 2 0x4C0000980000 0x80000 0x20000100000 0xA000000000000000 0xC
0x003A8880 2 3 0x4C0000780000 0x80000 0x20000180000 0xA000000000000000 0xC
0x003B4F70 2 4 0x4C0000A80000 0x80000 0x20000200000 0xA000000000000000 0xC
0x003AB700 2 5 0x4C0000680000 0x80000 0x20000280000 0xA000000000000000 0xC
0x003B5BE0 2 6 0x4C0000B80000 0x80000 0x20000300000 0xA000000000000000 0xC

SPE Shadow Registers Memory Region class[edit | edit source]

This type of memory region represents shadow registers memory region of a SPE. It's created e.g. in lv1_construct_logical_spe or in syscall 0x10040.

vtable[edit | edit source]

0x00358448 (3.15)

Objects[edit | edit source]

Here is the list of SPE Shadow Registers memory region objects i found in HV 3.15.

Address in HV dump LPAR id SPE LPAR Start Address Size Physical Address Flags log2(Page Size)
0x003ABDA0 2 1 0x300000012000 0x1000 - 0xA000000000000000 0xC
0x003B4290 2 2 0x300000014000 0x1000 - 0xA000000000000000 0xC
0x003A8A00 2 3 0x300000010000 0x1000 - 0xA000000000000000 0xC
0x003B50F0 2 4 0x300000016000 0x1000 - 0xA000000000000000 0xC
0x001FFC90 2 5 0x30000000E000 0x1000 - 0xA000000000000000 0xC
0x003AE5B0 2 6 0x300000018000 0x1000 - 0xA000000000000000 0xC

Device MMIO Memory Region class[edit | edit source]

This type of memory region is created when a device MMIO region is mapped into LPAR address space, e.g. in lv1_map_device_mmio_region.

vtable[edit | edit source]

0x00352468 (3.15)

Member variables[edit | edit source]

offset 0xA8 - physical address where the device MMIO region is mapped to

Objects[edit | edit source]

Here is the list of Device MMIO memory region objects i found in HV 3.15.

Address in HV dump LPAR id LPAR Start Address Size Flags log2(Page Size) Physical Address Device
0x001FDF00 2 0x4000001D0000 0x10000 0x8000000000000000 0xC 0x24003010000 USB controller
0x003B3850 2 0x400000200000 0x10000 0x8000000000000000 0xC 0x24003020000 USB controller
0x003B6E50 2 0x4000001E0000 0x10000 0x8000000000000000 0xC 0x24003810000 USB controller
0x003B9950 2 0x4000001F0000 0x10000 0x8000000000000000 0xC 0x24003820000 USB controller

GPU Device Memory Region class[edit | edit source]

This type of memory region is created e.g. in lv1_gpu_open, lv1_gpu_device_map and lv1_undocumented_function_114.

vtable[edit | edit source]

0x00357C48 (3.15)

Member variables[edit | edit source]

offset 0xA8 - physical address

Objects[edit | edit source]

Here is the list of Device GPU memory region objects i found in HV 3.15.

Address in HV dump LPAR id LPAR Start Address Size Flags log2(Page Size) Physical Address
0x003AF380 2 0x700190000000 0xFE00000 0x8000000000000000 0x14 0x28080000000
0x003AF500 2 0x4000001A0000 0xC000 0x8000000000000000 0xC 0x3C0000
0x003AF680 2 0x4800006C0000 0x40000 0x8000000000000000 0xC 0x2808FE00000
0x003AFC30 2 0x440000380000 0x20000 0x8000000000000000 0xC 0x28000C00000
0x003BB420 2 0x3C0000108000 0x8000 0x8000000000000000 0xC 0x28000080100

Direct Map Memory Region class[edit | edit source]

This type of memory region is created in HV call lv1_undocumented_function_114. lv1_undocumented_function_114 allows you to map any memory address into LPAR's memory address.

  • The HV call lv1_undocumented_function_115 destroys a memory region of this type.
  • HV allows GameOS to create objects of this type of size 0 only !!! But it can be exploited with a dangling HTAB entry.

vtable[edit | edit source]

0x00357C48 (3.15)

Member variables[edit | edit source]

offset 0xA8 - physical address

Exploiting HV with memory glitching and HV call lv1_undocumented_function_114[edit | edit source]

Here is a short description of the method i used to exploit HV from GameOS 3.15 and 3.41.

  • First i used the Geohot's method to create a dangling HTAB entry.
  • Making memory glitch work on GameOS was the largest of my obstacles but i solved it and i'm able to create a dangling HTAB entry from GameOS within 1-3 minutes.
  • Then i created many Direct Map Memory Region objects of size 0 with HV call lv1_undocumented_function_114 and checked if they are within the page to which the dangling HTAB entry points to.
  • When i found one such Direct Map Memory Region object i patched the size of this object to 0x1000. Then i pointed this memory region object to the code of HV call lv1_undocumented_function_114 and patched 4 bytes in this HV call which allows me to create any Direct Map Memory Region objects without any restrictions.
  • Function LPAR_construct_direct_mapping_mem_region which is used by HV call lv1_undocumented_function_114 has a parameter (register %r9) and when this parameter is not 0 then HV will allow you to create any Direct Map Memory Region objects without restrictions, but unfortunately the HV call lv1_undocumented_function_114 passes 0 in this parameter, so i just patched it.
  • Then i mapped whole HV memory range with the patched HV call lv1_undocumented_function_114 into the address space of GameOS.
  • And now you have read/write access to the whole HV.
  • $ONY could fix this exploit by disallowing creating of Direct Map Memory Region objects of size 0, but i know tons of other HV C++ classes which will allow me to exploit the HV in a similar way, so it wouldn't bring $ONY anything :-) And they have to change member variable offsets in those objects to make sure that i cannot patch them easily :-)

Methods[edit | edit source]

LPAR_get_memory_region_by_start_address - 0x002C7C40 (3.15)

LPAR_get_memory_region_by_address - 0x002C7DA8 (3.15)

LPAR_mem_addr_to_phys_addr(LPAR id, LPAR address, phys_addr) - 0x002FB8F0 (3.15)

LPAR_construct_direct_mapping_mem_region - 0x002D4D04 (3.15)

Network Devices[edit | edit source]

Ethernet Gelic Device[edit | edit source]

device id = 0

MAC Address: 00:1F:A7:C6:2A:C5

device memory base address = 0x24003004000 (size = 0x1000)

WLAN Gelic Device[edit | edit source]

device id = 0

MAC Address: 02:1F:A7:C6:2A:C5 (locally administered)

Net Manager[edit | edit source]

  • Net Manager runs in Process 9
  • It sends commands to /dev/sc1 to reset WLAN Gelic device
  • It opens /dev/net0, sets MAC address and writes device firmware eurus_fw.bin to WLAN device by using ioctl syscall

/dev/net0[edit | edit source]

The device supports 3 ioctl commands:

  • 0 - 0x002AC10C (3.15)
  • 1 - 0x002AC250 (3.15)
  • 2 - EURUS_STAT 0x002AC320 (3.15)

Methods[edit | edit source]

net_control_cmd_GELIC_LV1_POST_WLAN_CMD - 0x0024A55C (3.15)

net_control_wlan_cmd_GELIC_EURUS_CMD_ASSOC - 0x00246C78 (3.15)

net_control_wlan_cmd_GELIC_EURUS_CMD_START_SCAN - 0x00248A14 (3.15)

net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WEP_CFG - 0x00249F24 (3.15)

net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WPA_CFG - 0x002497B8 (3.15)

Event Notification[edit | edit source]

  • Event Notfication is used e.g. to notify a LPAR about some event, e.g. device interrupt or notify a LPAR about destruction of another LPAR.
  • For example Process 9 is notified through Event Notification when LPAR 2 is destructed.
  • During LPAR construction, Process 9 creates an Outlet object with syscall 0x1001A and then passes the outlet ID to the syscall 0x10009 that constructs the LINUX LPAR. In this way Process 9 is notified when LINUX LPAR is destructed.

Outlet class[edit | edit source]

This is the base Outlet class. There are different types of Outlet and they derive from this base class.

vtable[edit | edit source]

0x00357DC0 (3.15)

Member variables[edit | edit source]

offset 0x30 - type (8 bytes)

offset 0x38 - pointer to LPAR that owns this Outlet object

offset 0x48 - outlet id (8 bytes)

offset 0x90 - VIRQ assigned to this Outlet object (4 bytes)

Event Receive Port class[edit | edit source]

  • This type of Outlet is created e.g. in lv1_construct_event_receive_port and in syscall 0x1001A.
  • HV calls lv1_connect_irq_plug and lv1_connect_irq_plug_ext assigns a VIRQ to Event Receive Port object.

vtable[edit | edit source]

0x00357E88

VUART Outlet[edit | edit source]

  • HV supports only one VUART Outlet per LPAR
  • lv1_configure_virtual_uart_irq constructs a VUART Outlet object and passes the address of LPAR's VUART IRQ Bitmap to HV

vtable[edit | edit source]

0x00357DC0

VUART IRQ Bitmap[edit | edit source]

  • At address 0x38(LPAR ptr) + 0x158 is the VUART IRQ Bitmap owned by HV for LPAR (4 * 8 bytes = 256 bits)
  • At address 0x38(LPAR ptr) + 0x150 is stored the physical address of LPAR's VUART IRQ Bitmap that was passed to lv1_configure_virtual_uart_irq
  • When a VUART interrupt is generated by HV then first the VUART IRQ Bitmap owned by HV is updated and then this bitmap is copied to LPAR's VUART IRQ Bitmap, so VUART IRQ Bitmap is stored twice, once in HV and once in LPAR, just like IRQ State Bitmap.
  • VUART IRQ Bitmap is not allowed to cross page boundary of LPAR memory region where it is stored. HV checks it and makes sure that it doesn't happen.
  • GameOS 3.41 VUART IRQ bitmap is at address 0x80000000003556E8 and of size 32 bytes (256 bits, each bit corresponds to a VUART port).
  • GameOS 3.15 VUART IRQ bitmap is at address 0x8000000000354768.

Logical PPE[edit | edit source]

  • Logical PPE is used for interrupt management of LPAR.
  • A Logical PPE object is created in syscall 0x10005. It' used e.g. in Process 9 during LPAR construction.
  • syscall 0x10007 activates a Logical PPE object
  • 0x67F0(HSPRG0) - pointer to currently active Logical PPE object (in HV dump it points to Linux PPE object naturally because the dump was made on Linux, so Linux LPAR was active at that time)
  • E.g. lv1_get_logical_ppe_id, lv1_start_ppe_periodic_tracer and lv1_set_ppe_periodic_tracer_frequency grab the currently active Logical PPE object

vtable[edit | edit source]

0x00357DF0 (3.15)

Member variables[edit | edit source]

offset 0x90 - pointer to an object that contains VIRQ-Outlet mapping table for thread 0

offset 0x98 - pointer to an object that contains VIRQ-Outlet mapping table for thread 1

Objects[edit | edit source]

Here is the list of Logical PPE objects i found in HV 3.15.

Address in HV dump LPAR id PPE id
0x0069C7F0 1 1
0x007A8900 2 1

Virtual IRQ - Outlet Mapping[edit | edit source]

  • HV maintains 2 tables per PPE that map a VIRQ to an Outlet object.
  • The table has 256 entries and is indexed by VIRQ.
  • Each entry is a pointer to Outlet object.
  • Each Logical PPE object has 2 tables, one for each thread of Cell CPU.

LPAR 1 PPE 1 Thread 0[edit | edit source]

0x0069C990 (3.15) - address of VIRQ-Outlet table for LPAR 1 PPE 1 Thread 0 (not empty)

VIRQ Address of Outlet object in HV dump Description
58 0x00090D10 -
59 0x006BAC50 -
60 0x006B3ED0 FLASH storage device / Storage device notification for LPAR 1
61 0x00697E70 VUART interrupts
62 0x001C8F20 -

LPAR 1 PPE 1 Thread 1[edit | edit source]

0x0069D9B0 (3.15) - address of VIRQ-Outlet table for LPAR 1 PPE 1 Thread 1 (empty)

LPAR 2 PPE 1 Thread 0[edit | edit source]

0x000A06B0 (3.15) - address of VIRQ-Outlet table for LPAR 2 PPE 1 Thread 0 (not empty)

VIRQ Address of Outlet object in HV dump Description
20 0x003AA210 -
21 0x003AFEC0 -
22 0x001FC010 -
23 0x003A8E50 -
24 0x001FFED0 SPE 0 Class 0 Interrupt
25 0x003AE160 SPE 0 Class 1 Interrupt
26 0x003AE350 SPE 0 Class 2 Interrupt
27 0x003AB100 SPE 1 Class 0 Interrupt
28 0x003AB2F0 SPE 1 Class 1 Interrupt
29 0x003AB4E0 SPE 1 Class 2 Interrupt
30 0x003AA6A0 SPE 2 Class 0 Interrupt
31 0x003AA890 SPE 2 Class 1 Interrupt
32 0x003AAA80 SPE 2 Class 2 Interrupt
33 0x003B44A0 SPE 3 Class 0 Interrupt
34 0x003B4690 SPE 3 Class 1 Interrupt
35 0x003B4AD0 SPE 3 Class 2 Interrupt
36 0x003B5300 SPE 4 Class 0 Interrupt
37 0x003B54F0 SPE 4 Class 1 Interrupt
38 0x003B56E0 SPE 4 Class 2 Interrupt
39 0x003AE7C0 SPE 5 Class 0 Interrupt
40 0x003AE9B0 SPE 5 Class 1 Interrupt
41 0x003AEBA0 SPE 5 Class 2 Interrupt
42 0x003B2040 Storage device notification for LPAR 2
43 0x003AEE30 VUART interrupts
44 0x001FEAA0 -
45 0x001FEED0 HDD storage device
46 0x003B5E20 -
47 0x003B7040 -
48 0x003B9B40 -
49 0x003B3A40 -
50 0x003BACA0 Gelic device
51 0x003BAE10 UNKNOWN storage device
52 0x003B8350 -

LPAR 2 PPE 1 Thread 1[edit | edit source]

0x007A89E0 (3.15) - address of VIRQ-Outlet table for LPAR 2 PPE 1 Thread 1 (not empty)

VIRQ Address of Outlet object in HV dump Description
16 0x003B2480 -
17 0x003B2590 -
18 0x003B26A0 -
19 0x003B27B0 -

IRQ State Bitmap[edit | edit source]

  • There is one IRQ State Bitmap (256 bits = 32 bytes) per thread of Logical PPE
  • HSPRG0 value is per thread, so there are 2 HSPRG0 values in HV dump !!!
  • The IRQ State Bitmap of a thread is stored at -0x68E0(HSPRG0)
  • When an Event or Interrupt happens then the bitmap at 0x68E0(HSPRG0) is updated
  • The physical address of LPAR's IRQ State Bitmap of thread is stored at offset -0x68C0(HSPRG0)
  • The address of LPAR's IRQ State Bitmap is passed to Hypervisor through HV call lv1_configure_irq_state_bitmap
  • lv1_detect_pending_interrupts returns value of current IRQ State Bitmap.
  • The IRQ State Bitmap is updated if an Outlet object is assigned to VIRQ and when Outlet generates an event
  • After IRQ State Bitmap update, it's copied to LPAR's IRQ State Bitmap and a hardware interrupt is generated so that LPAR can read it's IRQ State Bitmap and handle interrupts.
  • So, IRQ State Bitmap is stored twice, once in HV and once in LPAR, just like VUART IRQ Bitmap.
  • GameOS IRQ state bitmap is stored at address SPRG0 + 0x1C0 and of size 64 bytes (256 bits state + 256 bits mask) per thread of Cell CPU. So there are 2 IRQ state bitmaps.

0x8941FC0 - physical address of LPAR's IRQ State Bitmap for Thread 0 of LINUX LPAR

0x8948FC0 - physical address of LPAR's IRQ State Bitmap for Thread 1 of LINUX LPAR

System Controller (SC or SYSCON)[edit | edit source]

  • Data received from SC is sent to a VUART
  • lv1_get_rtc and syscall 0x10036 communicate with SC VUART 4.

VUART Table[edit | edit source]

  • Address of SC VUART Table - 0x00610410 (3.15).
  • There are 5 VUARTs for SC in HV 3.15

Here is the SC VUART table from HV 3.15:

Index Address of VUART object in HV dump Description
0 0x0060FD20 This VUART is connected with the VUART 0 (/dev/sc0) of LPAR 1
1 0x0060FE20 This VUART is connected with the VUART 1 (/dev/sc1) of LPAR 1
2 0x0060FF20 This VUART is not connected to some peer VUART but i guess that it should be connected to VUART 2 (/dev/sc2) of LPAR1
3 0x006124E0 This VUART is connected with the VUART 3 (/dev/sc3) of LPAR 1
4 0x00612DF0 lv1_get_rtc and syscall 0x10036 communicate with this VUART.

Interrupt Handling[edit | edit source]

spider_sc_interrupt_handler - 0x0020A68C (3.15)

Methods[edit | edit source]

sc_vuart_4_get_peer_vuart - 0x002ED384 (3.15)

sc_send - 0x0020A908 (3.15)

sc_receive - 0x0020A354 (3.15)

sc_vuart_rx_trigger_callback - 0x002ED470 (3.15)

lv1_get_rtc[edit | edit source]

  • lv1_get_rtc communicates with SC VUART 4.
  • 20 bytes are written to the peer VUART of SC VUART 4.
  • After a request is sent to SC VUART 4, lv1_get_rtc busy waits until SC VUART 4 receive data buffer is not empty.
  • When SC VUART 4 receive data buffer is not empty, lv1_get_rtc reads 24 bytes from the VUART.

SYSCON Protocol[edit | edit source]

  • I was able to enable SYSCON Manager debug messages in HV Process 5
  • Messages sent to SYSCON are at least 0x10 bytes of size. SC VUARTs check it before sending the messages to SYSCON.
  • The header size of the SYSCON messages is 0x10 bytes.

Packet Header[edit | edit source]

  • Packet header is of size 0x10 bytes.
  • At offset 0x6 of SYSCON packet is the header checksum which is of size 2 bytes.
  • The header checkum is just a sum of first 6 header bytes and 0x8000 constant
  • The 2nd byte in every SYSCON message has to be 1 or else the function sc_send fails.
  • The word at offset 0x8 is the SC VUART index.
  • The half-words at offset 0xC and 0xE have to be equal or the function sc_send fails.
struct sc_hdr
{
    uint8_t field0;
    uint8_t field1;          /* always 1 */
    uint8_t field2[4];
    uint16_t cksum;          /* header checksum */
    uint32_t index;          /* syscon index (0 - /dev/sc0, 1 - /dev/sc1, 2 - /dev/sc2, 3 - /dev/sc3) */
    uint16_t size1;          /* body size */
    uint16_t size2;          /* body size */
};

Calculating Packet Header Checksum[edit | edit source]

/* calculating SC packet header checksum */

/*
 * sc_hdr_cksum
 */
uint16_t sc_hdr_cksum(struct sc_hdr *sc_hdr)
{
    uint8_t *ptr;
    uint32_t sum;

    ptr = (uint8_t *) sc_hdr;
    sum = 0;

    for (i = 0; i < 6; i++)
        sum += *ptr++;

    sum += 0x8000;

    return sum & 0xffff;
}

struct sc_hdr sc_hdr;

memset(&sc_hdr, 0, sizeof(sc_hdr));

sc_hdr.cksum = sc_hdr_cksum(sc_hdr);

/* fill sc header here */

sc_hdr.cksum = sc_hdr_cksum(sc_hdr);

Packet Body[edit | edit source]

  • Packet body follows packet header
  • Packet body size is stored at offset 0xC and 0xE in packet header and is of size 2 bytes

Reading SYSCON EPROM (NVS Service)[edit | edit source]

Here is a command which is sent to SYSCON to read 1 byte of EPROM at offset 0x48C07 (Product Mode):

0x14 0x01 0x00 0x00 0x00 0x00 0x80 0x15 0x00 0x00 0x00 0x00 0x00 0x04 0x00 0x04 0x20 0x02 0x07 0x01

And here is the response to the above request:

0x14 0x01 0x00 0x00 0x00 0x00 0x80 0x15 0x00 0x00 0x00 0x03 0x00 0x05 0x00 0x05 0x00 0x02 0x07 0x01 0xff

PCI Bus Power[edit | edit source]

  • Used by PS2EMU System Manager in HV process 9 when PS2 EMU is booted

PCI Bus Power On[edit | edit source]

Request to SC1:

0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x01

PCI Bus Power Off[edit | edit source]

Request to SC1:

0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x00

Ring Buzzer[edit | edit source]

Request:

0x16 0x01 0x00 0x00 0x00 0x00 0x80 0x17 0x00 0x00 0x00 0x00 0x00 0x08 0x00 0x08 0x20 0x00 0x00 0x00 0x00 0x00 0x00 0x00

SYSCON[edit | edit source]

Crossreference: gitbrew.org::SYSCON

SYSCON MMIO registers can be accessed on Linux with a driver using lv1_undocumented_function_114, e.g. ps3sbmmio. Use ps3sbmmio device driver carefully, an access at some addresses could shutdown your PS3.

Packet Header[edit | edit source]

  • Size is 0x10.
struct sc_hdr {
    uint8_t service_id;
    uint8_t version;              /* must be 1 !!! */
    uint16_t transaction_id;      /* returned in response */
    uint8_t res[2];
    uint16_t cksum;               /* checksum of first 6 header bytes */
    uint32_t communication_tag;   /* SYSCON tag: 0-4 */
    uint16_t payload_size[2];     /* body size */
};

Sending Packets[edit | edit source]

  • Before sending new packet to SYSCON, the Hypervisor checks 2 words at offsets 0x2400008DFF0 and 0x2400008CFF4.
  • The Hypervisor busy waits until (value + 1) at offset 0x2400008CFF4 is NOT equal to value at offset 0x2400008DFF0.
  • The packet is sent with 4 byte transfers.
  • First, the Hypervisor sends the header of the packet, 4 word transfers.
  • The header is written beginning at the address 0x2400008D000.
  • After that the Hypervisor sends the body of the packet, with 4 byte transfers too.
  • The body is written beginning at the address 0x2400008D010.
  • If the packet size is NOT divisible by 4 then the Hypervisor sends the remaining bytes (at most 3) as a word padded with 0s.
  • After the packet body was written, the Hypervisor calculates checksum of the whole packet and writes it at the address where the last word of packet body was written + 4.
uint32_t cksum = 0;

for (i = 0; i < packet_size; i++)
    cksum -= packet[i];

cksum = cksum & 0xffff;
  • After the packet checksum was written, the Hypervisor reads the value at offset 0x2400008DFF0, modifies it and stores back:
 
value = value + 1;
value &= 0xffff;
value = (value << 16) | value;
  • To notify the SYSCON about the new packet, the Hypervisor writes 0x1 to address 0x2400008E100.

Receiving Packets[edit | edit source]

  • The Hypervisor installs an interrupt handler for the SYSCON.
  • First, the Hypervisor reads a word from address 0x2400008E000, ors it with 0xFFFFFFFD and writes the value back.
  • Then, the Hypervisor reads a word from address 0x2400008E004 and tests if bit 0x2 is set or not. The bit 0x2 should be not 0 or else the Hypervisor panics.
  • After that, the Hypervisor reads a word at address 0x2400008CFF0 and 0x2400008DFF4. If there is a new packet pending from SYSCON, then the (value + 1) at 0x2400008CFF0 should be equal the value at 0x2400008DFF4.
  • The Hypervisor reads the header of the packet beginning at the address 0x2400008C000.
  • The header is read with 4 word transfers by the Hypervisor.
  • The byte at offset 1 in the packet header must be 1 or else the Hypervisor discards the packet as invalid.
  • The Hypervisor calculates the checksum of the packet header and checks it with the checksum stored in the header. If they don't match then the Hypervisor discards the packet.
  • The Hypervisor reads the body of the packet beginning at the address 0x2400008C010.
  • The header and the body of the received packet can be read as many times as you want !!! They remain until next SYSCON packet is received

which gives us the possibility to communicate with SYSCON on Linux easily :)

Test[edit | edit source]

1. Before sending SYSCON packet:

root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C

00000000  01 18 01 18                                       |....|
00000004

root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C

00000000  01 18 01 18                                       |....|
00000004

root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C

00000000  01 24 01 24                                       |.$.$|
00000004

root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C

00000000  01 24 01 24                                       |.$.$|
00000004

2. SYSCON packet was sent by using ps3dm_scm read_eprom.

3. After sending SYSCON packet:

root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C

00000000  01 19 01 19                                       |....|
00000004

root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C

00000000  01 19 01 19                                       |....|
00000004

root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C

00000000  01 25 01 25                                       |.%.%|
00000004

root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C

00000000  01 25 01 25                                       |.%.%|
00000004

4. Received Header

root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=16 skip=$((0x8c000)) status=noxfer | hexdump -C

00000000  14 01 00 00 00 00 80 15  00 00 00 03 00 05 00 05  |................|
00000010

5. Received Body

root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=8 skip=$((0x8c010)) status=noxfer | hexdump -C

00000000  00 00 c7 01 ff 00 00 00                           |..Ç.ÿ...|
00000008

Examples[edit | edit source]

Get RTC[edit | edit source]

  • Used by LV1 call lv1_get_rtc
  • Communication with SYSCON 4

Request:

# write packet

# echo "0: 13 01 0000 0000 8014 00000004 0001 0001 33 00 00 00 0000ff1f" | xxd -c256 -r | \
       dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer

# dump packet counter

# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C

00000000  00 c0 00 c0                                       |.À.À|
00000004

# increment packet counter

echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer

# kick packet

# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer

Response:

# dump packet counter

# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C

00000000  00 c1 00 c1                                       |.Á.Á|
00000004

# dump response packet

# dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C

00000000  13 01 00 00 00 00 80 14  00 00 00 04 00 08 00 08  |................|
00000010  00 00 00 00 15 af 47 6b                           |.....¯Gk|
00000018

Ring Buzzer[edit | edit source]

  • Used by System Manager
  • Communication with SYSCON 1

Request:

# write packet

# echo "0: 16 01 1620 0000 804d 00000001 0008 0008 20 29 0a 00 000001b6 0000fdcb" | xxd -c256 -r | \
       dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer

# dump packet counter

# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C

00000000  00 c0 00 c0                                       |.À.À|
00000004

# increment packet counter

echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer

# kick packet

# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer

# you should hear a beep

Response:

# dump packet counter

# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C

00000000  00 c1 00 c1                                       |.Á.Á|
00000004

# dump response packet

# dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C
00000000  16 01 16 20 00 00 80 4d  00 00 00 01 00 01 00 01  |... ...M........|
00000010  00 00 00 00 00 00 fe e3                           |......þã|
00000018

Isolation[edit | edit source]

Crossreference: gitbrew.org::Isolation

Running Isolated SPE Modules On OtherOS++ Linux[edit | edit source]

  • spp_verifier is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux.
  • It decrypts default.spp profile
  • Tested on 3.41 and 3.55.
  • You can modify it easily to run other SPE modules.
root@debian-hdd:/home/glevand/spp_verifier# cat spp_verifier_355.self > /proc/spp_verifier/spu
root@debian-hdd:/home/glevand/spp_verifier# cat default_355.spp > /proc/spp_verifier/profile
root@debian-hdd:/home/glevand/spp_verifier# echo 1 > /proc/spp_verifier/run
root@debian-hdd:/home/glevand/spp_verifier# cat /proc/spp_verifier/debug 

PPE id (0x0000000000000001) VAS id (0x0000000000000002)
lv1_construct_logical_spe (0x00000000)
SPE id (0x000000000000002b)
lv1_undocumented_function_209 (0x00000000)
shadow execution status (0x0000000000000002)
lv1_get_spe_interrupt_status(1) (0x00000000)
interrupt status 1 (0x0000000000000000)
sleep
shadow execution status (0x0000000000000002)
lv1_get_spe_interrupt_status(1) (0x00000000)
interrupt status 1 (0x0000000000000001)
ea (0xc000000002920000) esid (0xc000000008000000) vsid (0x0000408f92c94500)
lv1_undocumented_function_62 (0x00000000)
lv1_clear_spe_interrupt_status(1) (0x00000000)
lv1_undocumented_function_168 (0x00000000)
sleep
shadow execution status (0x0000000000000007)
lv1_get_spe_interrupt_status(1) (0x00000000)
interrupt status 1 (0x0000000000000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
interrupt status 2 (0x0000000000000000)
out interrupt mbox (0x0000000000000002)
out interrupt mbox (0x0000000000000002)
lv1_undocumented_function_167 (0x00000000)
lv1_clear_spe_interrupt_status (0x00000000)
lv1_undocumented_function_200 (0x00000000)
sleep
shadow execution status (0x000000000000000b)
lv1_get_spe_interrupt_status(1) (0x00000000)
interrupt status 1 (0x0000000000000000)
shadow execution status (0x000000000000000b)
problem status (0x01000082)
lv1_destruct_logical_spe (0x00000000)

root@debian-hdd:/home/glevand/spp_verifier# hexdump -C /proc/spp_verifier/profile | less
...
...
00000200  00 02 00 05 00 00 20 a0  00 00 00 01 00 03 00 00  |......  ........|
00000210  00 00 00 00 00 00 00 01  00 00 00 0e 00 00 00 00  |................|
00000220  00 00 02 88 00 00 00 01  10 70 00 00 01 00 00 01  |.........p......|
00000230  00 00 00 00 00 00 00 00  53 43 45 5f 43 45 4c 4c  |........SCE_CELL|
00000240  4f 53 5f 50 4d 45 00 00  00 00 00 00 00 00 00 00  |OS_PME..........|
00000250  00 00 00 00 00 00 00 00  00 00 00 06 00 00 02 50  |...............P|
00000260  10 70 00 00 01 00 00 01  2f 66 6c 68 2f 6f 73 2f  |.p....../flh/os/|
00000270  74 68 69 73 5f 69 73 5f  64 75 6d 6d 79 00 00 00  |this_is_dummy...|
00000280  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
...

Using metldr On OtherOS++ Linux[edit | edit source]

  • spp_verifier_direct is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux by using metldr directly.
  • It decrypts default.spp profile.
  • Tested on 3.41 and 3.55.
  • You can modify it easily to run other SPE modules.
root@debian-hdd:/home/glevand/spp_verifier_direct# insmod ./spp_verifier_direct.ko
root@debian-hdd:/home/glevand/spp_verifier_direct# cat metldr > /proc/spp_verifier_direct/metldr
root@debian-hdd:/home/glevand/spp_verifier_direct# cat isoldr_355 > /proc/spp_verifier_direct/isoldr
root@debian-hdd:/home/glevand/spp_verifier_direct# cat RL_FOR_PROGRAM_355.img > /proc/spp_verifier_direct/rvkprg
root@debian-hdd:/home/glevand/spp_verifier_direct# cat EID0 > /proc/spp_verifier_direct/eid0
root@debian-hdd:/home/glevand/spp_verifier_direct# cat spp_verifier_355.self > /proc/spp_verifier_direct/spu
root@debian-hdd:/home/glevand/spp_verifier_direct# cat default_355.spp > /proc/spp_verifier_direct/profile
root@debian-hdd:/home/glevand/spp_verifier_direct# echo 1 > /proc/spp_verifier_direct/run
root@debian-hdd:/home/glevand/spp_verifier_direct# cat /proc/spp_verifier_direct/debug
 
PPE id (0x0000000000000001) VAS id (0x0000000000000002)
lv1_construct_logical_spe (0x00000000)
SPE id (0x0000000000000033)
lv1_enable_logical_spe (0x00000000)
lv1_set_spe_interrupt_mask(0) (0x00000000)
lv1_set_spe_interrupt_mask(1) (0x00000000)
lv1_set_spe_interrupt_mask(2) (0x00000000)
lv1_set_spe_privilege_state_area_1_register (0x00000000)
ea (0xc000000002680000) esid (0xc000000008000000) vsid (0x0000408f92c94500)
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
out interrupt mbox (0x0000000000000001)
lv1_clear_spe_interrupt_status(2) (0x00000000)
transferring EID0, ldr args and revoke list to LS
waiting until MFC transfers are finished
MFC transfers done
out mbox (0x00000001)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
out interrupt mbox (0x0000000000000002)
lv1_clear_spe_interrupt_status(2) (0x00000000)
out mbox (0x00000002)
lv1_clear_spe_interrupt_status(2) (0x00000000)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
problem status (0x01000082)
lv1_destruct_logical_spe (0x00000000)

root@debian-hdd:/home/glevand/spp_verifier_direct# hexdump -C /proc/spp_verifier_direct/profile | less
...
...
00000200  00 02 00 05 00 00 20 a0  00 00 00 01 00 03 00 00  |......  ........|
00000210  00 00 00 00 00 00 00 01  00 00 00 0e 00 00 00 00  |................|
00000220  00 00 02 88 00 00 00 01  10 70 00 00 01 00 00 01  |.........p......|
00000230  00 00 00 00 00 00 00 00  53 43 45 5f 43 45 4c 4c  |........SCE_CELL|
00000240  4f 53 5f 50 4d 45 00 00  00 00 00 00 00 00 00 00  |OS_PME..........|
00000250  00 00 00 00 00 00 00 00  00 00 00 06 00 00 02 50  |...............P|
00000260  10 70 00 00 01 00 00 01  2f 66 6c 68 2f 6f 73 2f  |.p....../flh/os/|
00000270  74 68 69 73 5f 69 73 5f  64 75 6d 6d 79 00 00 00  |this_is_dummy...|
00000280  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
...

Gelic Device[edit | edit source]

Crossreference: gitbrew.org::Gelic Device

sys.hw.config[edit | edit source]

  • Value of the loader parameter "sys.hw.config" controls if Gelic WLAN is enabled or not.
  • Value of the loader parameter "sys.hw.config" is stored in the repository node "sys.hw.config" too.
  • If bit 0x40000 is set then LV1 allows using Gelic WLAN interface from LV2.
  • Value on my PS3 slim 0x4e00ffff0a03bc3c with Gelic WLAN interface disabled. As you can see, the Gelic WLAN interface is disabled and LV1 doesn't allow using of LV1 calls 196 and 195. It returns LV1_CONDITION_NOT_SATISFIED.
  • GameOS checks bit 0x40000 of the repository node "sys.hw.config" during network initialization and if it's set then LV2 initializes Gelic WLAN interface.
  • Check your "sys.hw.config" repository node and if bit 0x40000 is set then you are a lucky owner of a PS3 model with the old WLAN interface.
  • On newer PS3 models, GameOS uses USB interface to communicate with WLAN.
  • On PS3 models, where bit 0x40000 is NOT set in "sys.hw.config" repository node, the new USB interface is used.

Note:old vs. new: Old == CECHA up to CECHK, New == CECHL and later

Control Interface[edit | edit source]

HV calls 195 and 196 are used by GameOS to send commands to Gelic device directly.

lv1_undocumented_function_196[edit | edit source]

Parameters[edit | edit source]

r3 - LPAR address of data buffer

r4 - size of data buffer

r5 - must be 0

lv1_undocumented_function_195[edit | edit source]

Parameters[edit | edit source]

r3 - command (16 bit value)

r4 - command data size

r5 - must be 0

Data Buffer[edit | edit source]

  • Data Buffer passed to HV call 196 is divided into 2 parts.
  • The first 0x800 bytes are for sending and receiving command data
  • The remaining 0x800 bytes are for event notification.

Command Data Buffer[edit | edit source]

  • Every command data sent to Gelic device contains header of size 0xC
  • After the header follows the command data
  • After the Gelic device processed the command, it notifies LV2 kernel about command completion by sending an interrupt

Header[edit | edit source]

  • Size is 0xc.
  • Byte order is little-endian.
  • Header data in a request command buffer is always all 0s.

0x0 - command = request command + 1 (2 bytes)

0x4 - result, 0x1 - success ??? 0x2 - buffer too small ??? (2 bytes)

0x6 - body size (2 bytes)

Event Data Buffer[edit | edit source]

  • The Gelic device notifies LV2 kernel by sending an interrupt when new events are available
  • Event Data Buffer has 8 bytes header
  • The remaining bytes are divided into event slots
  • Each event slot is of size 64 bytes
  • Events are in little-endian format

Header[edit | edit source]

offset 0x0 - GET index (4 bytes)

offset 0x4 - PUT index (4 bytes)

  • GET index is updated by Gelic driver. The Gelic driver reads events beginning with the event slot at index GET.
  • PUT index is the index of event entry where next Gelic event will be stored by the Gelic device.
  • If GET index is equal to PUT index then there are no Gelic events.

GameOS[edit | edit source]

  • LV2 syscall 726 sends Gelic device command and blocks until a response from the Gelic device arrives
  • LV2 kernel uses this LV1 interface to send commands to Gelic device internally too, probably for wireless controllers and Wake On WLAN.
  • The system call 726 is used heavily by VSH.

Parameters[edit | edit source]

r3 - command (16 bits)

r4 - effective address of command data buffer

r5 - size of command data buffer

Commands[edit | edit source]

Unknown (0x1)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x10.
  • Used in AP mode.
  • Enables AP mode ???

Get AP SSID (0x3)[edit | edit source]

  • Command buffer is of size 0x30.
  • Returns SSID in AP mode.

offset 0xC - SSID (32 bytes)

Set AP SSID (0x5)[edit | edit source]

  • Used by VSH.
  • Command buffer is of size 0x30.
  • Sets SSID in AP mode.

offset 0xC - SSID (32 bytes)

Get Channel (0xf)[edit | edit source]

  • Used by VSH.
  • Command buffer is of size 0x31.
  • Data is returned from the device.
  • Returns list of channels and active channel.

offset 0x2F - active channel (2 bytes)

Set Channel (0x11)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0xd
  • Valid channels: 0 - 13. 0 means that the channel is selected automatically.

offset 0xC - channel (1 byte)

Unknown (0x27)[edit | edit source]

  • Command buffer size is 0xF.

Set Antenna (0x29)[edit | edit source]

  • Command buffer size is 0xe

offset 0xC - 0,1 or 2 (1 byte)

offset 0xD - 2 (1 byte)

Set AP WEP Configuration (0x5b)[edit | edit source]

  • Used by VSH.
  • Command buffer is of size 0x56.
  • Sets WEP security type and WEP key.
  • Security types: 0 - none, 1 - wep64, 2 - wep128

offset 0xE - security mode: 0 - none, 1 - wep64, 2 - wep128 (1 byte)

offset 0x10 - WEP key (64 bytes)

Unknown (0x61)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0xd

Unknown (0x65)[edit | edit source]

  • Used by VSH.
  • Command uffer size is 0xd.
  • Used in AP mode.

Get Eurus Firmware Version (0x99)[edit | edit source]

  • Used by VSH.

Here is the response on my PS3 Slim:

00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2|
00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19|
00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).|
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00       |..............  |

Get AP Operating Mode (0xb7)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x10
  • Returns AP operating mode (mixed, 11b or 11g).

offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)

Set AP Operating Mode (0xb9)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x10
  • Sets AP operating mode (mixed, 11b or 11g).

offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)

Unknown (0xc5)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x10.
  • Used in AP mode.

offset 0xC - ??? (4 bytes)

Set AP WPA AKM Suite (0xc9)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x11.
  • Sets WPA AKM suite in AP mode.

offset 0xC - AKM suite (4 bytes)

Set AP WPA Group Cipher Suite (0xcf)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x10
  • Used in AP + WPA mode.

offset 0xC - group cipher suite: group (4 bytes)

Set AP WPA PSK Binary (0xd3)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x4c
  • Sets WPA PSK binary

offset 0xC - PSK (64 bytes)

Set AP WPA Reauthentication Timeout (0xd5)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x10
  • Sets WPA Reauth timeout value in AP WPA mode.
  • VSH uses 36000 as timeout.

offset 0xC - timeout value in seconds (2 bytes)

Unknown (0x127)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x10.
  • Used in AP + WPA mode.

Unknown (0x12b)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x10.
  • Used in AP + WPA mode.

Set AP WPA PSK Passphrase (0x17d)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x2D

offset 0xD - passphrase (32 bytes)

Set AP WPA Pairwise Cipher Suite (0x1bf)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x11
  • Used in AP + WPA mode.

offset 0xC - pairwise cipher suite (4 bytes)

offset 0x10 - ??? (1 byte)

Unknown (0x1d9)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x10

Unknown (0x1dd)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0xd

Unknown (0x1ed)[edit | edit source]

  • Used by VSH.
  • Command buffer is of size 0x17.
  • Rate control ???

Get Eurus HW Revision (0x1fb)[edit | edit source]

  • Command buffer size is 0x10.

Associate (0x1001)[edit | edit source]

  • Used by VSH.
  • Used by LV1 on FAT models.
  • Command buffer size is 0xd
  • Data passed to Gelic device is all 0s

Get Common Configuration (0x1003)[edit | edit source]

  • Used by VSH.
  • Used by LV1 on FAT models.
  • Command buffer size is 0x18
  • Data passed to Gelic device is all 0s

Set Common Configuration (0x1005)[edit | edit source]

  • Used by VSH.
  • Used by LV1 on FAT models.
  • Command buffer size is 0x18
  • Hmm, VSH always removes QOS bit from capability, that means Jupiter doesn't support QOS ???

offset 0xC - BSS type: 0 - infrastructure, 1 - ???, 2 - adhoc (1 byte)

offset 0xD - authentication mode: 0 - open, 1 - shared key

offset 0xE - opmode: 0 - 11bg, 1 - 11b, 2 - 11g (1 byte)

offset 0xF - ??? (1 byte)

offset 0x10 - BSSID (6 bytes)

offset 0x16 - capability (2 bytes)

Get WEP Configuration (0x1013)[edit | edit source]

  • Used by VSH.
  • Used by LV1 on FAT models.
  • Command buffer size is 0x50
  • Data passed to Gelic device is all 0s

Set WEP Configuration (0x1015)[edit | edit source]

  • Used by VSH.
  • Used by LV1 on FAT models.
  • Command buffer size is 0x50

Get WPA Configuration (0x1017)[edit | edit source]

  • Used by VSH.
  • Used by LV1 on FAT models.
  • Command buffer size is 0x5b
  • Data passed to Gelic device is all 0s

Set WPA Configuration (0x1019)[edit | edit source]

  • Used by VSH.
  • Used by LV1 on FAT models.
  • Command buffer size is 0x5b

offset 0xE - security type: 0 - WPA, 1 - RSNA (1 byte)

offset 0xF - psk type: 0 - hex, 1 - bin (1 byte)

offset 0x10 - psk key: hex or bin (64 bytes)

offset 0x50 - group cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)

offset 0x54 - pairwise cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)

offset 0x58 - AKM suite: 0x0050f202 - WPA PSK, 0x000fac02 - RSNA PSK (4 bytes)

See IEEE 802.11 specification for more details about cipher/AKM suites

802.11 spec: [1]

Unknown (0x1025)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x10.
  • Sets preamble type, something else ???

offset 0xC - preamble mode: 0 - short, 1 - long (1 byte)

Unknown (0x1031)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0xe

Get Scan Results (0x1033)[edit | edit source]

  • Used by VSH.
  • Used by LV1 on FAT models.
  • Command buffer size is 0x5b0
  • Data passed to Gelic device is all 0s
Scan Results[edit | edit source]

offset 0x0 - number of scan entries (1 byte)

offset 0x1 - array of scan entries

Scan Entry[edit | edit source]

offset 0x0 - size of this entry in bytes, this field is NOT included (2 bytes)

offset 0x2 - BSSID (6 bytes)

offset 0x8 - RSSI (1 byte)

offset 0x9 - timestamp (8 bytes)

offset 0x11 - beacon period (2 bytes)

offset 0x13 - capability (2 bytes)

offset 0x15 - information elements (see 802.11 specification)

Start Scan (0x1035)[edit | edit source]

  • Used by VSH.
  • Used by LV1 on FAT models.
  • Command buffer size depends on size of channel list and ESSID string length
  • Data passed to Gelic device contains channel list and ESSID string
  • First 0x16 bytes in command data buffer are all 0s, then follows the channel list and after that ESSID

Diassociate (0x1037)[edit | edit source]

  • Used by VSH.
  • Used by LV1 on FAT models.
  • Command buffer size is 0xd
  • Data passed to Gelic device is all 0s

Get RSSI (0x103d)[edit | edit source]

  • Used by VSH.
  • Used by LV1 on FAT models.
  • Command buffer size is 0x17

offset 0x10 - MAC address of node (6 bytes)

offset 0x16 - RSSI (1 byte)

Get MAC Address (0x103f)[edit | edit source]

  • Command buffer size is 0x13

offset 0xD - MAC address (6 bytes)

Set MAC Address (0x1041)[edit | edit source]

  • Used by VSH.
  • Used by LV1 too.
  • Command buffer size is 0x12

Unknown (0x104d)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0xd.

offset 0xC - 0 - ???, 1 - ??? (1 byte)

Unknown (0x104f)[edit | edit source]

  • Command buffer size is 0xd.
  • Returns 1 byte.

offset 0xC - 0 - ???, 1 - ??? (1 byte)

Unknown (0x1051)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x5b3.
  • Returns 0x5a7 bytes.

offset 0xC - number of entries

offset 0x10 - entries (each entry is 0xd bytes)

Unknown (0x1053)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x70.

offset 0xC - ??? (4 bytes)

offset 0x10 - MAC address (6 bytes)

Unknown (0x1059)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x2a8.

Unknown (0x105f)[edit | edit source]

  • Used by LV2.

Get Zephyr HW Revision (0x1101)[edit | edit source]

  • Used by VSH.
  • Not a Gelic device command, handled by LV2 kernel.
  • LV2 uses LV1 call lv1_net_control(0x8000000000000002)
  • Command buffer size is 0x18.

Get MAC Address List (0x1117)[edit | edit source]

  • Command buffer size is 0xce.
  • Returns several MAC addresses.

offset 0xC - number of MAC addresses (2 bytes)

offset 0xE - MAC addresses (6 * number of MAC addresses)

Unknown (0x1133)[edit | edit source]

  • Used by VSH.
  • Command buffer size is 0x1A.

Set WOL MAC Address Filter (0x1139)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer is of size 0x28.

Unknown (0x113b)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer size is 0x20.

Set WOL Multicast Address Filter (0x113d)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer is of size 0x2c.

Clear WOL Multicast Address Filter (0x113f)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer is of size 0x28.

Unknown (0x1141)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer is of size 0x12.

Clear WOL Address Filter (0x1143)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer size is 0x2c.

Unknown (0x114b)[edit | edit source]

  • Used by LV2 internally.

Set WOL Magic Packet Mode (0x1155)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer is of size 0x10.
  • Enables/Disables WOL magic packet.

offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)

Unknown (0x1157)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer size is 0x10.

Set WOL Multicast Address Filter Mode (0x1159)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer size is 0x10.
  • WOL function

offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)

Set Unicast Address Filter (0x115b)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer is of size 0x6a.
  • This command should be used to set proper MAC address or else device won't be able to receive packets destined to its own MAC address

offset 0xC - ??? (2 bytes)

offset 0xE - ??? (2 bytes)

offset 0x10 - MAC address (6 bytes)

Clear Unicast Address Filter (0x115d)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer size is 0x6a.

Get Unicast Address Filter (0x115f)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer is of size 0x6a.

Set Multicast Address Filter (0x1161)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer size is 0x2c.

Clear Multicast Address Filter (0x1163)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer size is 0x2c
  • To clear all multicast addresses send command with all 0s.

offset 0xC - multicast address filter (4 * 8 bytes)

Get Multicast Address Filter (0x1165)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer is of size 0x2c.

Set WOL Address Filter (0x1167)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer size is 0x70.

Set WOL Address Filter Mode (0x116d)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer size is 0x10.
  • Enables/Disables WOL address matching

offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)

Set Unicast Address Filter Mode (0x116f)[edit | edit source]

  • Used by LV2 internally.
  • Command buffer size is 0x10.

offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)

Get Device Status (0xfffb)[edit | edit source]

  • Used by VSH.
  • Not a Gelic device command, handled by LV2 kernel.
  • Returned data size in command buffer is 0x10.

Unknown (0xfffc)[edit | edit source]

  • Used by VSH.
  • Not a Gelic device command, handled by LV2 kernel.
  • LV2 uses LV1 call lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x4, 0x0, 0x0)

Get Channel Information (0xfffd)[edit | edit source]

  • Used by VSH.
  • Not a Gelic device command, handled by LV2 kernel.
  • LV2 uses LV1 call lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x0, 0x0, 0x0)
  • Returns supported WLAN channels

Set Response Timeout (0xfffe)[edit | edit source]

  • Used by VSH.
  • Not a Gelic device command, handled by LV2 kernel.
  • Sets timeout value which is used to wait for a response from Gelic device.
  • Typical value used by VSH is 0x989680.
  • Command buffer size is 0x14.

Unknown (0xffff)[edit | edit source]

  • Used by VSH.
  • Not a Gelic device command, handled by LV2 kernel.
  • Returns 0x10 bytes in command buffer.
  • Returns gelic device state ???

Events[edit | edit source]

struct ps3_eurus_event_hdr {
	__le32 type;
	__le32 id;
	__le32 timestamp;
	__le32 payload_length;
	__le32 unknown;
} __packed;

struct ps3_eurus_event {
	struct ps3_eurus_event_hdr hdr;
	u8 payload[44];
} __packed;

Event Type 0x00000040[edit | edit source]

Id Description
0x00000001 Deauthenticated

Event Type 0x00000080[edit | edit source]

Id Description
0x00000001 Beacon Lost
0x00000002 Connected
0x00000004 Scan Completed
0x00000020 WPA Connected
0x00000040 WPA Error (MIC Error)

Event Type 0x80000000[edit | edit source]

Id Description
0x00000001 Device Ready

Enabling WLAN Gelic On FAT[edit | edit source]

Linux kernel doesn't use Gelic Device Control Interface like GameOS does it. To get WLAN working on Linux booted with GameOS rights, we have to disable Gelic Device Control Interface first because it's enabled for GameOS by default.

The value of repository node "ios.net.eurus.lpar" controls access to Gelic Device Control Interface. It's a bitmap. The position of a bit corresponds to LPAR id. During GameOS booting, HV process 9 (System Manager) sets bit at postion 2 to 1 which means enable Gelic Device Control Interface for LPAR 2.

To disable Gelic Device Control Interface on Linux, first unload Gelic device driver, then set value of repository node "ios.net.eurus.lpar" to 0 and load Gelic device driver again. After that WLAN should work again but only on FATs.

For PS3 Slim we need a new Linux Gelic device driver which uses Gelic Device Control Interface directly.


USB WLAN Interface (Codename Jupiter 2)[edit | edit source]

  • On new PS3 models, WLAN interface is USB.
  • Good news is that the same commands are used as with LV1 calls 196 and 195.
  • There are 2 wireless devices: Station and AP.
  • I got WLAN scan working.

Endpoints[edit | edit source]

  • LV2 uses 3 USB endpoints of interface 3,4 and 5 to communicate with WLAN.
  • Endpoints EP5 IN/OUT, EP6 IN/OUT and EP7 IN/OUT.
  • WLAN commands are sent to endpoint EP5 OUT with interrupt transfers.
  • WLAN events and WLAN command responses are received on endpoint EP5 IN with interrupt transfers.
  • LV2 opens a USB communication pipe to endpoint EP5 IN and EP5 OUT.
  • In my LV2 3.55 dump, pipe to EP5 IN has id 0x2 and pipe to EP5 OUT has id 0x3. Array of all opened USB pipes is at address 0x80000000004bd000 in my LV2 3.55 dump.
  • EP5 is used to send commands to Jupiter and receive events from it.
  • EP6 is used to send/receive data packets to/from the 1st WLAN device.
  • EP7 is used to send/receive data packets to/from the 2nd WLAN device.
  • lsusb is buggy on big-endian arch and shows some fields with bytes swapped !!!
Bus 002 Device 002: ID 054c:036f Sony Corp. 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass          224 Wireless
  bDeviceSubClass         1 Radio Frequency
  bDeviceProtocol         1 Bluetooth
  bMaxPacketSize0        64
  idVendor           0x054c Sony Corp.
  idProduct          0x036f 
  bcdDevice           20.12
  iManufacturer           1 
  iProduct                2 
  iSerial                 0 
  bNumConfigurations      1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        3
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      2 
      bInterfaceProtocol      1 
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x85  EP 5 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x4000  1x 0 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x05  EP 5 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x4000  1x 0 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        4
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      2 
      bInterfaceProtocol      2 
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x86  EP 6 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0002  1x 2 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x06  EP 6 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0002  1x 2 bytes
        bInterval             255
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        5
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      2 
      bInterfaceProtocol      3 
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x87  EP 7 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0002  1x 2 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x07  EP 7 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0002  1x 2 bytes
        bInterval             255

Device Initialization[edit | edit source]

  • LV2 does 2 control transfers to EP0 during WLAN initialization
  • First control transfer sends magic 0x20 data to device as CLEAR_FEATURE request.
  • Second control transfer reads 0x2 bytes device status. On my PS3 slim, the status data is always 0x2031 if you send the right magic.
  • Magic data sent in first control transfer is stored in LV2.
  • If you send wrong magic, the first control transfer will fail !!!
  • LV2 uses a state machine to initialize the Jupiter device. The state machine has 17 states.

Magic Data in Control Transfer[edit | edit source]

unsigned char ps3_usb_wlan_magic_data[] = {
	0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8,
	0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d,
};

Initialization State Machine[edit | edit source]

  • Implemented in LV2.
State 1[edit | edit source]
  • Command 0x114f is sent to WLAN device.
State 2[edit | edit source]
  • Command 0x1171 is sent to WLAN device.
State 3[edit | edit source]
  • LV2 waits for an event from WLAN device.
State 4[edit | edit source]
  • Command 0x116f is sent to WLAN device.
State 5[edit | edit source]
  • Command 0x115b is sent to WLAN device.
  • Command data sent to WLAN device contains MAC address.
State 6[edit | edit source]
  • Command 0x1161 is sent to WLAN device.
  • Sets multicast address filter.
State 7[edit | edit source]
  • Command 0x110d is sent to WLAN device.
State 8[edit | edit source]
  • Command 0x1031 is sent to WLAN device.
State 9[edit | edit source]
  • Command 0x1041 is sent to WLAN device.
  • Command data sent to WLAN device contains MAC address.
State 10[edit | edit source]
  • Command 0x29 is sent to WLAN device.
  • Sets antenna.
State 11[edit | edit source]
  • Command 0x110b is sent to WLAN device.
State 12[edit | edit source]
  • Command 0x1109 is sent to WLAN device.
State 13[edit | edit source]
  • Command 0x207 is sent to WLAN device.
State 14[edit | edit source]
  • Command 0x203 is sent to WLAN device.
State 15[edit | edit source]
  • Command 0x105f is sent to WLAN device.
  • Command data sent to WLAN device contains MAC address, channel info and region code.
State 16[edit | edit source]
  • LV2 waits for an event from WLAN device.
State 17[edit | edit source]
  • LV2 accepts commands sent by LV2 syscall 726.

Test Program[edit | edit source]

  • Here is a small program which executes a WLAN scan.
  • I used libusb.

Source Code[edit | edit source]


/*
 * PS3 USB WLAN
 *
 * Copyright (C) 2011 glevand ([email protected])
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published
 * by the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>

#include <libusb-1.0/libusb.h>

#define USB_VENDOR_ID				0x054c /* $ONY */
#define USB_PRODUCT_ID				0x036f
#define USB_IFACE_NUMBER			3

#define USB_INTR_TRANSFER_EP5_IN_BUF_SIZE	0x800
#define USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE	0x800

struct wlan_cmd_pkt_hdr {
	uint8_t unknown1;
	uint8_t unknown2;
	uint8_t unknown3;
	uint8_t unknown4;
	uint16_t unknown5;
	uint8_t res1[2];
	uint16_t tag;
	uint8_t res2[14];
} __attribute__ ((packed));

struct wlan_cmd_hdr {
	uint16_t command;
	uint16_t tag;
	uint16_t status;
	uint16_t payload_size;
	uint8_t res[4];
} __attribute__ ((packed));

struct wlan_event_pkt_hdr {
	uint8_t unknown1;
	uint8_t unknown2;
	uint8_t unknown3;
	uint8_t event_count;
} __attribute__ ((packed));

static libusb_context *usb_ctx;
static libusb_device_handle *usb_dev_handle;

static struct libusb_transfer *usb_intr_transfer_ep5_in;
static unsigned char usb_intr_transfer_ep5_in_buf[USB_INTR_TRANSFER_EP5_IN_BUF_SIZE];

static unsigned char usb_intr_transfer_ep5_out_buf[USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE];

static pthread_mutex_t usb_wlan_cmd_mutex;
static pthread_cond_t usb_wlan_cmd_cond;
static int volatile usb_wlan_cmd_busy;
static uint16_t usb_wlan_cmd;
static void *usb_wlan_cmd_data;

static int volatile usb_wlan_cmd_thread_done;

/*
 * WLAN won't work without this magic !!!
 */
static unsigned char usb_magic_data[] = {
	0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8,
	0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d,
};

static unsigned char my_mac_addr[] = {
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
};

/*
 * hexdump
 */
static void hexdump(const unsigned char *data, unsigned int data_size)
{
	int i, j;

	for (i = 0; i < data_size; i += 16) {
		fprintf(stdout, "%08x:", i);

		for (j = 0; j < 16; j++) {
			if (i + j < data_size) {
				fprintf(stdout, " %02x", data[i + j]);
			} else {
				fprintf(stdout, "   ");
			}
		}

		fprintf(stdout, " |");

		for (j = 0; j < 16; j++) {
			if (i + j < data_size) {
				if (isprint(data[i + j]))
					fprintf(stdout, "%c", data[i + j]);
				else
					fprintf(stdout, ".");
			} else {
				fprintf(stdout, " ");
			}
		}

		fprintf(stdout, "|\n");
	}
}

/*
 * usb_handle_wlan_event
 */
static void usb_handle_wlan_event(struct wlan_event_pkt_hdr *wlan_event_pkt_hdr)
{
	fprintf(stdout, "%s:%d: === got WLAN event ===\n", __func__, __LINE__);

	/*
	fprintf(stdout, "%s:%d: event packet header:\n", __func__, __LINE__);
	fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__,
		wlan_event_pkt_hdr->unknown1);
	fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__,
		wlan_event_pkt_hdr->unknown2);
	fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__,
		wlan_event_pkt_hdr->unknown3);
	*/
	fprintf(stdout, "%s:%d: event_count (0x%02x)\n", __func__, __LINE__,
		wlan_event_pkt_hdr->event_count);

	hexdump((unsigned char *) (wlan_event_pkt_hdr + 1), wlan_event_pkt_hdr->event_count * 64);
}

/*
 * usb_handle_wlan_cmd_response
 */
static void usb_handle_wlan_cmd_response(struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr)
{
	struct wlan_cmd_hdr *wlan_cmd_hdr;
	uint8_t *wlan_cmd_payload;

	fprintf(stdout, "%s:%d: === got WLAN command response ===\n", __func__, __LINE__);

	wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
	wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);

	/* convert all header fields to big-endian byte order !!! */

	wlan_cmd_pkt_hdr->unknown5 = le16toh(wlan_cmd_pkt_hdr->unknown5);
	wlan_cmd_pkt_hdr->tag = le16toh(wlan_cmd_pkt_hdr->tag);			/* returned from request */

	wlan_cmd_hdr->command = le16toh(wlan_cmd_hdr->command);			/* request command + 1 */
	wlan_cmd_hdr->tag = le16toh(wlan_cmd_hdr->tag);				/* returned from request */
	wlan_cmd_hdr->status = le16toh(wlan_cmd_hdr->status);			/* 1 - success
										   2 - invalid parameters ???
										   3 - invalid command ??? */
	wlan_cmd_hdr->payload_size = le16toh(wlan_cmd_hdr->payload_size);	/* length of data that follows the header */

	/*
	fprintf(stdout, "%s:%d: command packet header:\n", __func__, __LINE__);
	fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__,
		wlan_cmd_pkt_hdr->unknown1);
	fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__,
		wlan_cmd_pkt_hdr->unknown2);
	fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__,
		wlan_cmd_pkt_hdr->unknown3);
	fprintf(stdout, "%s:%d: unknown4 (0x%02x)\n", __func__, __LINE__,
		wlan_cmd_pkt_hdr->unknown4);
	fprintf(stdout, "%s:%d: unknown5 (0x%04x)\n", __func__, __LINE__,
		wlan_cmd_pkt_hdr->unknown5);
	fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__,
		wlan_cmd_pkt_hdr->tag);
	*/

	fprintf(stdout, "%s:%d: command header:\n", __func__, __LINE__);
	fprintf(stdout, "%s:%d: command (0x%04x)\n", __func__, __LINE__,
		wlan_cmd_hdr->command);

	if ((usb_wlan_cmd + 1) != wlan_cmd_hdr->command)
		fprintf(stdout, "%s:%d: ==> command does not match, got (0x%04x) expected (0x%04x)\n",
			__func__, __LINE__, wlan_cmd_hdr->command, usb_wlan_cmd + 1);

	fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__,
		wlan_cmd_hdr->tag);
	fprintf(stdout, "%s:%d: status (0x%04x)\n", __func__, __LINE__,
		wlan_cmd_hdr->status);

	if (wlan_cmd_hdr->status != 0x1)
		fprintf(stdout, "%s:%d: ==> command status != 0x1\n", __func__, __LINE__);

	fprintf(stdout, "%s:%d: payload_size (0x%04x)\n", __func__, __LINE__,
		wlan_cmd_hdr->payload_size);

	fprintf(stdout, "%s:%d: command payload:\n", __func__, __LINE__);

	hexdump(wlan_cmd_payload, wlan_cmd_hdr->payload_size);

	memcpy(usb_wlan_cmd_data, wlan_cmd_payload, wlan_cmd_hdr->payload_size);

	pthread_mutex_lock(&usb_wlan_cmd_mutex);

	usb_wlan_cmd_busy = 0;

	pthread_cond_signal(&usb_wlan_cmd_cond);

	pthread_mutex_unlock(&usb_wlan_cmd_mutex);
}

/*
 * usb_intr_transfer_ep5_in_cb
 */
static void usb_intr_transfer_ep5_in_cb(struct libusb_transfer *transfer)
{
	struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr;
	int error;

	fprintf(stdout, "%s:%d: === got interrupt transfer ===\n", __func__, __LINE__);

	fprintf(stdout, "%s:%d: transfer status (%d) length (%d)\n",
		__func__, __LINE__, transfer->status, transfer->actual_length);

	wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) transfer->buffer;

	if (wlan_cmd_pkt_hdr->unknown3 == 0x6)
		usb_handle_wlan_cmd_response(wlan_cmd_pkt_hdr);
	else if (wlan_cmd_pkt_hdr->unknown3 == 0x8)
		usb_handle_wlan_event((struct wlan_event_pkt_hdr *) transfer->buffer);
	else
		fprintf(stdout, "%s:%d: got unknown packet (0x%02x)\n",
			__func__, __LINE__, wlan_cmd_pkt_hdr->unknown3);

	memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf));

	libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
		usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf),
		usb_intr_transfer_ep5_in_cb, NULL, 0);

	error = libusb_submit_transfer(usb_intr_transfer_ep5_in);
	if (error) {
		fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
			__func__, __LINE__, error);
		exit(1);
	}
}

/*
 * usb_intr_transfer_ep5_out_cb
 */
static void usb_intr_transfer_ep5_out_cb(struct libusb_transfer *transfer)
{
	/*
	fprintf(stdout, "%s:%d: sent interrupt transfer\n", __func__, __LINE__);

	fprintf(stdout, "%s:%d: transfer status (%d)\n", __func__, __LINE__, transfer->status);
	*/

	libusb_free_transfer(transfer);
}

/*
 * usb_wlan_cmd_send
 */
static int usb_wlan_cmd_send(uint16_t command, const uint8_t *data, unsigned int data_size)
{
	struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr;
	struct wlan_cmd_hdr *wlan_cmd_hdr;
	uint8_t *wlan_cmd_payload;
	struct libusb_transfer *transfer;
	int error;

	fprintf(stdout, "%s:%d: sending command (0x%04x) data size (0x%04x) command size (0x%04x)\n",
		__func__, __LINE__, command, data_size, data_size + sizeof(struct wlan_cmd_hdr));

	transfer = libusb_alloc_transfer(0);
	if (!transfer) {
		fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__);
		error = -1;
		goto fail;
	}

	wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) usb_intr_transfer_ep5_out_buf;
	wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
	wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);

	wlan_cmd_pkt_hdr->unknown1 = 0x1;
	wlan_cmd_pkt_hdr->unknown2 = 0x1;
	wlan_cmd_pkt_hdr->unknown3 = 0x6;
	wlan_cmd_pkt_hdr->unknown4 = 0x0;
	wlan_cmd_pkt_hdr->unknown5 = 0x1;
	wlan_cmd_pkt_hdr->tag = 0xf00d;		/* returned in response */

	wlan_cmd_hdr->command = command;
	wlan_cmd_hdr->tag = 0xcafe;		/* returned in response */
	wlan_cmd_hdr->status = 0xa;
	wlan_cmd_hdr->payload_size = data_size;

	memcpy(wlan_cmd_payload, data, data_size);

	usb_wlan_cmd = command;
	usb_wlan_cmd_data = (void *) data;

	libusb_fill_interrupt_transfer(transfer, usb_dev_handle, LIBUSB_ENDPOINT_OUT | 0x5,
		usb_intr_transfer_ep5_out_buf,
		sizeof(struct wlan_cmd_pkt_hdr) + sizeof(struct wlan_cmd_hdr) + wlan_cmd_hdr->payload_size,
		usb_intr_transfer_ep5_out_cb, NULL, 0);

	/* convert all header fields to little-endian byte order !!! */

	wlan_cmd_pkt_hdr->unknown5 = htole16(wlan_cmd_pkt_hdr->unknown5);
	wlan_cmd_pkt_hdr->tag = htole16(wlan_cmd_pkt_hdr->tag);

	wlan_cmd_hdr->command = htole16(wlan_cmd_hdr->command);
	wlan_cmd_hdr->tag = htole16(wlan_cmd_hdr->tag);
	wlan_cmd_hdr->status = htole16(wlan_cmd_hdr->status);
	wlan_cmd_hdr->payload_size = htole16(wlan_cmd_hdr->payload_size);

	error = libusb_submit_transfer(transfer);
	if (error) {
		fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
			__func__, __LINE__, error);
		goto fail_free_transfer;
	}

	pthread_mutex_lock(&usb_wlan_cmd_mutex);

	usb_wlan_cmd_busy = 1;

	while (usb_wlan_cmd_busy)
		pthread_cond_wait(&usb_wlan_cmd_cond, &usb_wlan_cmd_mutex);

	pthread_mutex_unlock(&usb_wlan_cmd_mutex);

	return 0;

fail_free_transfer:

	libusb_free_transfer(transfer);

fail:

	return error;
}

/*
 * usb_wlan_cmd_start_scan
 */
static int usb_wlan_cmd_start_scan(void)
{
	unsigned char data[256], *ptr;
	unsigned int data_size;

	memset(data, 0, sizeof(data));

	ptr = data;
	*ptr++ = 0x0;
	*ptr++ = 0x1;
	*ptr++ = 0x64;
	*ptr++ = 0x0;

	ptr = data + 0xa;
	*ptr++ = 0x3;

	*ptr++ = 13;	/* number of channels */
	*ptr++ = 1;	/* channels */
	*ptr++ = 2;
	*ptr++ = 3;
	*ptr++ = 4;
	*ptr++ = 5;
	*ptr++ = 6;
	*ptr++ = 7;
	*ptr++ = 8;
	*ptr++ = 9;
	*ptr++ = 10;
	*ptr++ = 11;
	*ptr++ = 12;
	*ptr++ = 13;

	data_size = ptr - data;

	return usb_wlan_cmd_send(0x1035, data, data_size);
}

/*
 * usb_wlan_cmd_get_scan_results
 */
static int usb_wlan_cmd_get_scan_results(void)
{
	unsigned char data[1456];
	unsigned int data_size;

	memset(data, 0, sizeof(data));

	data_size = sizeof(data);

	return usb_wlan_cmd_send(0x1033, data, data_size);
}

/*
 * usb_wlan_cmd_0x99
 */
static int usb_wlan_cmd_0x99(void)
{
	unsigned char data[0x3e];
	unsigned int data_size;

	memset(data, 0, sizeof(data));

	data_size = sizeof(data);

	return usb_wlan_cmd_send(0x99, data, data_size);
}

/*
 * usb_wlan_init
 */
static int usb_wlan_init(void)
{
	unsigned char data[1456], *ptr;
	unsigned int data_size;
	int error;

	/* state 0x1 */

	memset(data, 0, sizeof(data));

	data_size = 0x518;

	error = usb_wlan_cmd_send(0x114f, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x114f (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	/* state 0x2 */

	memset(data, 0, sizeof(data));

	data_size = 0;

	error = usb_wlan_cmd_send(0x1171, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x1171 (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	/* wait for a WLAN event */

	/* state 0x4 */

	memset(data, 0, sizeof(data));

	ptr = data;

	*ptr++ = 0x1;

	data_size = 0x4;

	error = usb_wlan_cmd_send(0x116f, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x116f (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	/* state 0x5 */

	memset(data, 0, sizeof(data));

	ptr = data;

	*ptr++ = 0x1;

	ptr = data + 0x4;
	memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));

	data_size = 0x5e;

	error = usb_wlan_cmd_send(0x115b, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x115b (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	/* state 0x6 */

	memset(data, 0, sizeof(data));

	ptr = data + 0x1c;

	*ptr++ = 0x20;

	data_size = 0x20;

	error = usb_wlan_cmd_send(0x1161, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x1161 (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	memset(data, 0, sizeof(data));

	ptr = data + 0xc;
	memset(ptr, 0xff, 7 * 4);

	data_size = 0x80;

	error = usb_wlan_cmd_send(0x110d, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x110d (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	memset(data, 0, sizeof(data));

	data_size = 0x2;

	error = usb_wlan_cmd_send(0x1031, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x1031 (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	memset(data, 0, sizeof(data));

	ptr = data;
	memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));

	data_size = 0x6;

	error = usb_wlan_cmd_send(0x1041, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x1041 (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	/* state 0xa */

	memset(data, 0, sizeof(data));

	ptr = data;

	*ptr++ = 0x2;
	*ptr++ = 0x2;

	data_size = 0x2;

	error = usb_wlan_cmd_send(0x29, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x29 (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	memset(data, 0, sizeof(data));

	ptr = data;

	*ptr++ = 0x1;

	ptr = data + 8;

	*ptr++ = 0x20;

	data_size = 0xc;

	error = usb_wlan_cmd_send(0x110b, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x110b (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	memset(data, 0, sizeof(data));

	ptr = data;

	*ptr++ = 0x1;

	ptr = data + 0x4;

	*ptr++ = 0x15;
	*ptr++ = 0x27;

	*ptr++ = 0x12;
	*ptr++ = 0x0;

	*ptr++ = 0x6;
	*ptr++ = 0x0;

	ptr = data + 0xc;

	*ptr++ = 0x9;
	*ptr++ = 0x0;
	*ptr++ = 0x1;

	ptr = data + 0x10;

	*ptr++ = 0xff;
	*ptr++ = 0xff;
	*ptr++ = 0xff;
	*ptr++ = 0xff;
	*ptr++ = 0xff;
	*ptr++ = 0xff;

	data_size = 0x16;

	error = usb_wlan_cmd_send(0x1109, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x1109 (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	memset(data, 0, sizeof(data));

	ptr = data;

	*ptr++ = 0x1;

	data_size = 0x4;

	error = usb_wlan_cmd_send(0x207, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x207 (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	memset(data, 0, sizeof(data));

	ptr = data;

	*ptr++ = 0x4;

	data_size = 0x4;

	error = usb_wlan_cmd_send(0x203, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x203 (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	sleep(2);

	/* state 0xf */

	memset(data, 0, sizeof(data));

	ptr = data;

	*ptr++ = 0xff;
	*ptr++ = 0x1f;

	memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));

	ptr = data + 0x8;

	*ptr++ = 0x2;
	*ptr++ = 0x2;

	data_size = 0xa;

	error = usb_wlan_cmd_send(0x105f, data, data_size);
	if (error) {
		fprintf(stderr, "%s:%d: could not send command 0x105f (%d)\n",
			__func__, __LINE__, error);
		return error;
	}

	return 0;
}

/*
 * usb_wlan_cmd_thread
 */
static void *usb_wlan_cmd_thread(void *arg)
{
	int error;

	error = usb_wlan_init();
	if (error) {
		fprintf(stderr, "%s:%d: could not initialize device (%d)\n",
			__func__, __LINE__, error);
		goto done;
	}

	sleep(5);

	error = usb_wlan_cmd_0x99();
	if (error) {
		fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
			__func__, __LINE__, error);
		goto done;
	}

	error = usb_wlan_cmd_start_scan();
	if (error) {
		fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
			__func__, __LINE__, error);
		goto done;
	}

	sleep(10);

	error = usb_wlan_cmd_get_scan_results();
	if (error) {
		fprintf(stderr, "%s:%d: could not get scan results (%d)\n",
			__func__, __LINE__, error);
		goto done;
	}

	sleep(10);

done:

	usb_wlan_cmd_thread_done = 1;

	return NULL;
}

/*
 * main
 */
int main(int argc, char **argv)
{
	unsigned char buf[256];
	pthread_t tid;
	struct timeval tv;
	int error;

	pthread_mutex_init(&usb_wlan_cmd_mutex, NULL);
	pthread_cond_init(&usb_wlan_cmd_cond, NULL);

	error = libusb_init(&usb_ctx);
	if (error) {
		fprintf(stderr, "%s:%d: libusb_init failed (%d)\n", __func__, __LINE__, error);
		exit(1);
	}

	libusb_set_debug(usb_ctx, 5);

	usb_dev_handle = libusb_open_device_with_vid_pid(usb_ctx, USB_VENDOR_ID, USB_PRODUCT_ID);
	if (!usb_dev_handle) {
		fprintf(stderr, "%s:%d: could not open device\n", __func__, __LINE__);
		exit(1);
	}

	if(libusb_kernel_driver_active(usb_dev_handle, USB_IFACE_NUMBER)) {
		fprintf(stdout, "%s:%d: kernel driver is attached\n", __func__, __LINE__);

		error = libusb_detach_kernel_driver(usb_dev_handle, USB_IFACE_NUMBER);
		if (error) {
			fprintf(stderr, "%s:%d: could not detach kernel driver (%d)\n",
				__func__, __LINE__, error);
			exit(1);
		}

		fprintf(stdout, "%s:%d: kernel driver dettached\n", __func__, __LINE__);
	}

	error = libusb_claim_interface(usb_dev_handle, USB_IFACE_NUMBER);
	if (error) {
		fprintf(stderr, "%s:%d: could not claim interface (%d)\n",
			__func__, __LINE__, error);
		exit(1);
	}

	error = libusb_control_transfer(usb_dev_handle, 0x40, 0x1, 0x9, 0x0,
		usb_magic_data, sizeof(usb_magic_data), 0);
	if (error < 0) {
		fprintf(stderr, "%s:%d: could not do control transfer (%d)\n",
			__func__, __LINE__, error);
		exit(1);
	}

	fprintf(stdout, "%s:%d: number of bytes transferred (%d)\n", __func__, __LINE__, error);

	error = libusb_control_transfer(usb_dev_handle, 0xc0, 0x0, 0x2, 0x0, buf, 2, 0);
	if (error < 0) {
		fprintf(stderr, "%s:%d: could not do control transfer (%d)\n",
			__func__, __LINE__, error);
		exit(1);
	}

	fprintf(stdout, "%s:%d: number of bytes received (%d)\n", __func__, __LINE__, error);

	fprintf(stdout, "%s:%d: 0x%02x 0x%02x\n", __func__, __LINE__, buf[0], buf[1]);

	usb_intr_transfer_ep5_in = libusb_alloc_transfer(0);
	if (!usb_intr_transfer_ep5_in) {
		fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__);
		exit(1);
	}

	memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf));

	libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
		usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf),
		usb_intr_transfer_ep5_in_cb, NULL, 0);

	error = libusb_submit_transfer(usb_intr_transfer_ep5_in);
	if (error) {
		fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
			__func__, __LINE__, error);
		exit(1);
	}

	error = pthread_create(&tid, NULL, usb_wlan_cmd_thread, NULL);
	if (error) {
		fprintf(stderr, "%s:%d: could not create WLAN command thread (%d)\n",
			__func__, __LINE__, error);
		exit(1);
	}

	while (!usb_wlan_cmd_thread_done) {
		tv.tv_sec = 1;
		tv.tv_usec = 0;

		error = libusb_handle_events_timeout(usb_ctx, &tv);
		if (error) {
			fprintf(stderr, "%s:%d: could not handle events (%d)\n",
				__func__, __LINE__, error);
			exit(1);
		}
	}

	libusb_free_transfer(usb_intr_transfer_ep5_in);

	error = libusb_release_interface(usb_dev_handle, USB_IFACE_NUMBER);
	if (error)
		fprintf(stderr, "%s:%d: could not release interface (%d)\n",
			__func__, __LINE__, error);

	libusb_close(usb_dev_handle);

	libusb_exit(usb_ctx);

	exit(0);
}

Output[edit | edit source]

glevand@debian-hdd:~/ps3_usb_wlan$ sudo ./ps3_usb_wlan 
sudo: unable to resolve host debian-hdd
main:824: number of bytes transferred (32)
main:833: number of bytes received (2)
main:835: 0x20 0x31
usb_wlan_cmd_send:288: sending command (0x114f) data size (0x0518) command size (0x0524)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1150)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0006)
usb_handle_wlan_cmd_response:205: ==> command status != 0x1
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
usb_handle_wlan_cmd_response:210: command payload:
usb_wlan_cmd_send:288: sending command (0x1171) data size (0x0000) command size (0x000c)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1172)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
usb_handle_wlan_cmd_response:210: command payload:
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
usb_handle_wlan_event:133: === got WLAN event ===
usb_handle_wlan_event:144: event_count (0x01)
00000000: 00 04 00 00 10 00 00 00 3c 22 02 00 00 00 00 00 |........<"......|
00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............|
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
usb_wlan_cmd_send:288: sending command (0x116f) data size (0x0004) command size (0x0010)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1170)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
usb_handle_wlan_cmd_response:210: command payload:
usb_wlan_cmd_send:288: sending command (0x115b) data size (0x005e) command size (0x006a)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x115c)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
usb_handle_wlan_cmd_response:210: command payload:
usb_wlan_cmd_send:288: sending command (0x1161) data size (0x0020) command size (0x002c)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1162)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
usb_handle_wlan_cmd_response:210: command payload:
usb_wlan_cmd_send:288: sending command (0x110d) data size (0x0080) command size (0x008c)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x110e)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
usb_handle_wlan_cmd_response:210: command payload:
usb_wlan_cmd_send:288: sending command (0x1031) data size (0x0002) command size (0x000e)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1032)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 00 00                                           |..              |
usb_wlan_cmd_send:288: sending command (0x1041) data size (0x0006) command size (0x0012)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (42)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1042)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0006)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 00 11 22 33 44 55                               |.."3DU          |
usb_wlan_cmd_send:288: sending command (0x0029) data size (0x0002) command size (0x000e)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x002a)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 02 02                                           |..              |
usb_wlan_cmd_send:288: sending command (0x110b) data size (0x000c) command size (0x0018)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (48)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x110c)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x000c)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 01 00 00 00 00 00 00 00 20 00 00 00             |........ ...    |
usb_wlan_cmd_send:288: sending command (0x1109) data size (0x0016) command size (0x0022)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (58)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x110a)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0016)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 01 00 00 00 15 27 12 00 06 00 00 00 09 00 01 00 |.....'..........|
00000010: ff ff ff ff ff ff                               |......          |
usb_wlan_cmd_send:288: sending command (0x0207) data size (0x0004) command size (0x0010)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x0208)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0004)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 01 00 00 00                                     |....            |
usb_wlan_cmd_send:288: sending command (0x0203) data size (0x0004) command size (0x0010)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x0204)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0004)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 04 00 00 00                                     |....            |
usb_wlan_cmd_send:288: sending command (0x105f) data size (0x000a) command size (0x0016)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1060)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
usb_handle_wlan_cmd_response:210: command payload:
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
usb_handle_wlan_event:133: === got WLAN event ===
usb_handle_wlan_event:144: event_count (0x01)
00000000: 80 00 00 00 00 10 00 00 9e 2b 02 00 04 00 00 00 |.........+......|
00000010: fc 90 02 c0 01 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............|
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
usb_wlan_cmd_send:288: sending command (0x0099) data size (0x003e) command size (0x004a)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (98)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x009a)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x003e)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2|
00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19|
00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).|
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00       |..............  |
usb_wlan_cmd_send:288: sending command (0x1035) data size (0x0019) command size (0x0025)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (61)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1036)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0019)
usb_handle_wlan_cmd_response:210: command payload:
00000000: 00 01 64 00 00 00 00 00 00 00 03 0d 01 02 03 04 |..d.............|
00000010: 05 06 07 08 09 0a 0b 0c 0d                      |.........       |
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
usb_handle_wlan_event:133: === got WLAN event ===
usb_handle_wlan_event:144: event_count (0x01)
00000000: 80 00 00 00 04 00 00 00 96 2e 02 00 01 00 00 00 |................|
00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............|
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
usb_wlan_cmd_send:288: sending command (0x1033) data size (0x05b0) command size (0x05bc)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (1403)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
usb_handle_wlan_cmd_response:191: command header:
usb_handle_wlan_cmd_response:192: command (0x1034)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_handle_wlan_cmd_response:201: status (0x0001)
usb_handle_wlan_cmd_response:207: payload_size (0x0557)
usb_handle_wlan_cmd_response:210: command payload:
...
Here is scan output (removed by me)
...

Associate with AP[edit | edit source]

  • I got association with AP working.
  • If WLAN device is connected to an AP then the green LED is on, when data is received then the LED blinks.
  • Data reception works finally !!!

How to Associate with WPA AP[edit | edit source]

  • Set common configuration (command 0x1005)
  • Set WPA configuration (command 0x1019)
  • Set rate configuration (command 0x1ed)
  • Associate (command 0x1001)

Packet Reception[edit | edit source]

  • EP6 IN and EP7 IN endpoints are used for packet reception
  • LV2 sends bulk transfers to both endpoints
  • 4 bulk transfers are sent simultaneously for each enpoint
  • Every bulk transfer is of size 0x620
  • Make sure you set multicast address filter properly or else you won't receive broadcast packets !!!
  • Bulk transfers returned by the host controller which do not contain any data have size of 0x10 bytes else transfers contain valid Ethernet frame. All 802.11 related data is stripped by the WLAN Gelic device.
  • Make sure you set right MAC address with command 0x115b else device won't be able to receive packets destined to its own MAC address !!!

Test with libusb[edit | edit source]

usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (98)
00000000: ff ff ff ff ff ff ?? ?? ?? ?? ?? ?? 08 00 45 00 |..............E.|
00000010: 00 54 00 00 40 00 40 01 b5 fe c0 a8 01 5b c0 a8 |.T..@.@......[..|
00000020: 01 ff 08 00 9c 69 0d 45 00 e2 4e 5d 34 26 00 07 |.....i.E..N]4&..|
00000030: df e1 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 |................|
00000040: 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 |.......... !"#$%|
00000050: 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 |&'()*+,-./012345|
00000060: 36 37                                           |67              |
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16)
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 |................|
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16)
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 |................|
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16)
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 |................|

Multicast Address Filter[edit | edit source]

  • WLAN Gelic device supports hardware multicast address filtering
  • Multicast address filtering is implemented with MAC address hashing and filter bitmap
  • Filter bitmap is of size 4 * 8 bytes
  • Multicast address filter is set with command 0x1161
MAC Address Hash Function[edit | edit source]
  • Used by LV2
unsigned char hash(unsigned char *data, unsigned int size)
{
        unsigned int hash;
        int i, j;

        /*XXX: reverse data bits */

        hash = 0xffffffff;

        for (i = 0; i < size; i++) {
                hash = (((unsigned int) data[i]) << 24) ^ hash;

                for (j = 0; j < 8; j++) {
                        if (((int) hash) >= 0) {
                                hash = hash << 1;
                        } else {
                                hash = (hash << 1) ^ 0x04c10000;
                                hash = hash ^ 0x00001db7;
                        }
                }
        }

        hash = ((hash >> 24) & 0xf8) | (hash & 0x7);

        return hash & 0xff;
}

h = hash(mac_addr, 6);
v = 1 << (h & 0x1f);    /* word value in filter */
p = h >> 5;             /* word position in filter */


For broadcast address:
------------------------

v = 0x20000000
p = 7

That's why 0x20 is used with command 0x1161 !!! Without it the device won't deliver broadcast traffic.
Learned it the hard way, after 2 days of trying to get packet reception working :)

Packet Transmission[edit | edit source]

  • Tx packets are sent to EP6 OUT
  • Tx packets are normal Ethernet frames, they don't contain any WLAN data or other headers

AP Mode[edit | edit source]

  • I got AP mode working with security disabled for now

AP Mode with Security Disabled[edit | edit source]

  • Set AP SSID (command 0x5)
  • Set channel (command 0x11)
  • Set AP opmode (command 0xb9)
  • Configure rate control (command 0x1ed)
  • Set AP WEP Configuration (command 0x5b, all 0s)
  • Command 0x61 (param 0x0)
  • Command 0xc5 (param 0x0)
  • Command 0x1 (param 0x1)
  • Command 0x1dd (param 0x2)
  • Now green LED should be on

ps3-jupiter Linux Drivers[edit | edit source]

  • ps3_jupiter.ko is the common part of STA and AP mode. It implements a command interface to WLAN Gelic device and disptaches events to STA and AP drivers.
  • ps3_jupiter_sta.ko is a STA mode implementation.
  • ps3_jupiter_ap.ko is a AP mode implementation.
  • Simple scanning works already in STA mode (try it out with iwlist scan)
  • Packet reception works
  • Packet transmission works
  • WPA/WPA2 fully working and usable with wpa_supplicant


Finally, after several weeks of hard programming and reversing, the WLAN driver ps3_jupiter_sta achieved the milestone where i can use it with WPA2 :) I actually use it currently with WPA2 on my PS3 slim. It works damn !!! Try it out and report bugs and problems to me.

TODO[edit | edit source]

  • Implement association in STA mode (finished)
  • Implement packet reception and transmission in STA mode (finished)
  • Implement WEP support
  • Implement AP mode
  • Find out if Jupiter supports Monitor mode and if yes how to enable it
  • Implement EURUS driver for PHATs (has many advantages over the old OtherOS approach, e.g. AP mode)
  • Port to FreeBSD

LV2 Network Stack[edit | edit source]

  • LV2 uses BSD network stack, e.g. struct mbuf
  • It's almost identical to FreeBSD network stack.

Network Device[edit | edit source]

IOCTLs[edit | edit source]

Set Multicast Address Filter (0x81012000)[edit | edit source]
  • Sets multicast address filter
  • Uses LV1 calls lv1_net_remove_multicast_address and lv1_net_add_multicast_address for Ethernet Gelic device
  • Uses Eurus commands 0x1161, 0x1163 and 0x1165 for WLAN Gelic device
Unknown (0x8101200E)[edit | edit source]
  • Uses LV1 call lv1_net_control(0x8000000000000001)
Unknown (0x81040000)[edit | edit source]
  • Uses LV1 call lv1_net_control(0x8, [0x0, 0x1 or 0x2]) for Ethernet Gelic device
  • Uses Eurus commands 0x116F, 0x115D and 0x115B for WLAN Gelic device
Enable/Disable WOL Magic Packet (0x81080000)[edit | edit source]
  • Enables/Disables WOL Magic Packet
  • Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x1 /* GELIC_LV1_WOL_MAGIC_PACKET */) for Ethernet Gelic device
  • Uses Eurus commands 0x1139 and 0x1155 for WLAN Gelic device
Unknown (0x81080001)[edit | edit source]
  • Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x2) for Ethernet Gelic device
  • Uses Eurus commands 0x113B and 0x1157 for WLAN Gelic device
Unknown (0x81080002)[edit | edit source]
  • Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x3) for Ethernet Gelic device
  • Uses Eurus commands 0x113D and 0x1159 for WLAN Gelic device
Unknown (0x81080003)[edit | edit source]
  • Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x4) for Ethernet Gelic device
  • Uses Eurus command 0x1161 for WLAN Gelic device
Unknown (0x81080005)[edit | edit source]
  • Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x6 /* GELIC_LV1_WOL_ADD_MATCH_ADDR */) for Ethernet Gelic device
  • Uses Eurus commands 0x116D and 0x1167 for WLAN Gelic device

Network Packet[edit | edit source]

  • LV2 network packet is represented by struct mbuf

RSX[edit | edit source]

Crossreference: gitbrew.org::RSX

HV Calls[edit | edit source]

lv1_gpu_memory_allocate[edit | edit source]

  • LV1 supports 16 memory handles simultaneously.
  • LV1 uses a bitmap to manage GPU VRAM.
  • The bitmap is located in LV1 memory, 4 double words.
  • Each bit corresponds to 1MB VRAM, 256bit = 256MB VRAM.
  • 2MB at the top of VRAM are preallocated as you can see below.
<memory handle> = 0x5a5a5a5a xor <memory handle index>

Memory Context Object[edit | edit source]

offset 0x8 - memory handle (4 bytes)

offset 0x10 - VRAM LPAR start address (8 bytes)

offset 0x18 - VRAM LPAR end address (8 bytes)

Test[edit | edit source]

  • The offset of bitmap could be different on your system because it's allocated dynamically.
  • First 9MB of VRAM were allocated by ps3fb Linux driver.

Before allocating VRAM:

glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C 
00000000  00 00 00 00 00 00 01 ff  00 00 00 00 00 00 00 00  |.......ÿ........|
00000010  00 00 00 00 00 00 00 00  c0 00 00 00 00 00 00 00  |........À.......|

After allocating 32 MB VRAM:

glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C 
00000000  00 00 01 ff ff ff ff ff  00 00 00 00 00 00 00 00  |...ÿÿÿÿÿ........|
00000010  00 00 00 00 00 00 00 00  c0 00 00 00 00 00 00 00  |........À.......|

lv1_gpu_context_allocate[edit | edit source]

  • Register %r4 is flags.
  • Found the place in LV1 where LV1 sets IO page size for GART memory mapping. We could patch it and set to 4KB. That would make a lot of things easier for RSX developers on Linux.
  • 1MB pages make RSX driver for Linux hard to implement because allocating 1Mb contiguous memory chunk on Linux is very very hard especially on a system with only 256MB and which was running for some time.
  • LV1 supports 16 contexts simultaneously.
  • LV1 has an array of context pointers.
  • Each context has an index and a handle. The handle is derived from the index of the context.
<context handle> = 0x55555555 xor <context index>
  • Thats why first created context will have handle 0x55555555.

Context Object[edit | edit source]

offset 0x8 - handle (4 bytes)

offset 0x48 - IO page size, valid range is 4kB, 64KB and 1MB (8 bytes)

Flags[edit | edit source]

0x2 - tells LV1 to use 64KB pages for GART memory mapping else LV1 uses 1MB pages

lv1_gpu_context_iomap[edit | edit source]

  • Internally uses lv1_put_iopte function
  • IO page size is the one set during lv1_gpu_context_allocate
  • IO address space id is 0x0. IO id is 0x1.

lv1_gpu_context_attribute[edit | edit source]

Attribute 0x1[edit | edit source]

FIFO Command Buffer Setup[edit | edit source]
lv1_gpu_context_attribute(context handle, 0x1, PUT offset, GET offset, 0x0, 0x0)

Attribute 0x101[edit | edit source]

Set Flip Mode[edit | edit source]
lv1_gpu_attribute(0x2, 0x1 /* head */, 0x0, 0x0)
lv1_gpu_context_attribute(context handle, 0x101, 0x1 /* head */, sync mode, 0x0, 0x0)

Attribute 0x104[edit | edit source]

Set Display Buffer[edit | edit source]
lv1_gpu_context_attribute(context handle, 0x104, id, width << 32 | height, pitch << 32 | offset, 0x0)

Attribute 0x10a[edit | edit source]

Get Flip Status[edit | edit source]
  • Reads a value at offset 0x10C0 + 0x1 * 0x40 in lpar_reports memory.
Reset Flip Status[edit | edit source]
lv1_gpu_context_attribute(context handle, 0x10a, 0x1 /* id */, 0x7fffffff /* mask */, 0x0 /* value */, 0x0)
  • The LV1 call lv1_gpu_context_attribute(0x10a) accesses LPAR memory returned in lpar_reports by LV1 call lv1_gpu_context_allocate.
  • Offset into lpar_reports is 0x10C0 + id * 0x40 = 0x10C0 + 0x1 * 0x40.
  • Why not access lpar_reports memory directly and use LV1 call instead ???

Attribute 0x10b[edit | edit source]

  • This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.
Set Cursor Position[edit | edit source]
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x3, x, y)
Set Cursor Image Offset[edit | edit source]
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x2, offset, 0x0)

Attribute 0x10c[edit | edit source]

  • This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.
Cursor Function 1[edit | edit source]
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x1, 0x0, 0x0)
Cursor Function 2[edit | edit source]
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x2, 0x0, 0x0)

Attribute 0x10d[edit | edit source]

  • This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.
Cursor Function 1[edit | edit source]
lv1_gpu_context_attribute(context handle, 0x10d, 0x1, 0x1, 0x0, 0x0)

Attribute 0x300[edit | edit source]

Set Tile[edit | edit source]
Set Invalidate Tile[edit | edit source]
Bind Tile[edit | edit source]
Unbind Tile[edit | edit source]

Attribute 0x301[edit | edit source]

Set Zcull[edit | edit source]
Bind Zcull[edit | edit source]
Unbind Zcull[edit | edit source]

Attribute 0x601[edit | edit source]

  • Copies data from GART memory to VRAM.
  • LV1 uses internally the FIFO command buffer passed by ps3fb driver with lv1_gpu_context_iomap.

FIFO commands:

0x0004C184
0xFEED0001

0x0004C198
0x313371C3

0x00046300
0x0000000A

for ()
{
    for ()
    {
        0x0004630C
        <param>

        0x00046304
        <param>

        0x0024C2FC
        0x00000001
        0x00000003
        0x00000003
        <param1>
        <param2>
        <param3>
        <param4>
        0x00010000
        0x00010000

        0x0001C400
        <param1>
        <param2>
        <param3>
        0x00000000
    }
}

0x00040110
0x00000000

FIFO Command Buffer[edit | edit source]

FIFO Control Registers[edit | edit source]

  • LV1 call lv1_gpu_context_allocate returns LPAR address of FIFO control registers.
  • You have to map it into Linux address space before you can access FIFO control registers.
  • Value of PUT and GET registers are NOT expressed in Linux address space but in RSX address space. You have to convert it to RSX address space.
  • GET register is read-only and is modified by RSX while it's processing FIFO commands.

Kicking FIFO Command Buffer[edit | edit source]

  • As long as values of GET and PUT FIFO control registers are equal, RSX doesn't process commands from the FIFO command buffer.
  • When the value of PUT register is not equal to the value of GET register, RSX starts processing commands in the FIFO command buffer.
  • To execute FIFO commands, place them in the FIFO command buffer and change the value of PUT register.

FIFO Setup Programs of emer_init.self[edit | edit source]

FIFO Commands[edit | edit source]

PS3:HvReverseEngineering:RSXFIFOCommands

Example How to Use FIFO Command Buffer[edit | edit source]

Here is a small Linux kernel module which shows you how to use FIFO command buffer on Linux.

  • RSX allows to create multiple contexts.
  • This kernel module should run without problems with ps3fb driver already running.
  • Make sure you unload ps3vram driver before running this module because ps3vram allocates all available RSX memory for itself and because of this, lv1_gpu_memory_allocate will always fail.
  • This kernel module lets the RSX execute a simple program which contains only NOP (No Operation) commands.

Download source code: [2]

Source Code[edit | edit source]

/*
 * PS3 RSX
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published
 * by the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/delay.h>

#include <asm/abs_addr.h>
#include <asm/cell-regs.h>
#include <asm/lv1call.h>
#include <asm/ps3.h>

#define RSX_FIFO_CMD_BUF_SIZE	(1 * 1024 * 1024)

#define RSX_MEM_SIZE		(32 * 1024 * 1024)

#define RSX_GPU_IOIF		(0x0e000000ul)

#define RSX_FIFO_CTRL_SIZE	(4 * 1024)

struct rsx_fifo_ctrl {
	u8 res[0x40];
	u32 put;
	u32 get;
};

static u32 *rsx_fifo_cmd_buf;
static u64 rsx_fifo_cmd_buf_lpar;

static u64 rsx_mem_handle, rsx_mem_lpar;
static u64 rsx_ctx_handle;
static u64 rsx_fifo_ctrl_lpar;
static u64 rsx_drv_info_lpar;
static u64 rsx_reports_lpar, rsx_reports_size;

static struct rsx_fifo_ctrl *rsx_fifo_ctrl;

/*
 * FIFO program
 */
static u32 rsx_fifo_prg[] = {
	0x00000000, /* nop */
	0x00000000, /* nop */
	0x00000000, /* nop */
};

/*
 * ps3rsx_init
 */
static int __init ps3rsx_init(void)
{
	unsigned long timeout;
	int res;

	/* FIFO command buffer must be allocated in XDR memory */

	rsx_fifo_cmd_buf = kmalloc(RSX_FIFO_CMD_BUF_SIZE, GFP_KERNEL);
	if (!rsx_fifo_cmd_buf) {
		printk(KERN_INFO"could not allocate FIFO command buffer\n");
		res = -ENOMEM;
		goto fail;
	}

	res = lv1_gpu_memory_allocate(RSX_MEM_SIZE, 0, 0, 0, 0,
		&rsx_mem_handle, &rsx_mem_lpar);
	if (res) {
		printk(KERN_INFO"lv1_gpu_memory_allocate failed (%d)\n", res);
		res = -ENXIO;
		goto fail_free_fifo_cmd_buf_mem;
	}

	res = lv1_gpu_context_allocate(rsx_mem_handle, 0,
		&rsx_ctx_handle, &rsx_fifo_ctrl_lpar, &rsx_drv_info_lpar,
		&rsx_reports_lpar, &rsx_reports_size);
	if (res) {
		printk(KERN_INFO"lv1_gpu_context_allocate failed (%d)\n", res);
		res = -ENXIO;
		goto fail_free_gpu_mem;
	}
	
	/* map FIFO command buffer into RSX address space */

	rsx_fifo_cmd_buf_lpar = ps3_mm_phys_to_lpar(__pa(rsx_fifo_cmd_buf));

	res = lv1_gpu_context_iomap(rsx_ctx_handle,
		RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar, RSX_FIFO_CMD_BUF_SIZE,
		CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M);
	if (res) {
		printk(KERN_INFO"lv1_gpu_context_iomap failed (%d)\n", res);
		res = -ENXIO;
		goto fail_free_gpu_mem;
	}

	/* map RSX FIFO control registers */

	rsx_fifo_ctrl = (struct rsx_fifo_ctrl *) ioremap(rsx_fifo_ctrl_lpar, RSX_FIFO_CTRL_SIZE);
	if (!rsx_fifo_ctrl) {
		printk(KERN_INFO"could not map FIFO control\n");
		res = -ENXIO;
		goto fail_free_gpu_mem;
	}

	/* PUT and GET offsets are in RSX address space */

	res = lv1_gpu_context_attribute(rsx_ctx_handle, 0x1,
		RSX_GPU_IOIF + 0x0 /* PUT offset */, RSX_GPU_IOIF + 0x0 /* GET offset */,
		0x0, 0x0);
	if (res) {
		printk(KERN_INFO"lv1_gpu_context_attribute(0x1) failed (%d)\n", res);
		res = -ENXIO;
		goto fail_unmap_fifo_ctrl;
	}

	/* copy FIFO commands to FIFO command buffer */

	memcpy(rsx_fifo_cmd_buf, rsx_fifo_prg, sizeof(rsx_fifo_prg));

	printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);

	/* kick FIFO */

	rsx_fifo_ctrl->put = RSX_GPU_IOIF + sizeof(rsx_fifo_prg);

	/* poll until RSX is done processing FIFO commands */

	timeout = 100;

	while (timeout--) {
		if (rsx_fifo_ctrl->get == rsx_fifo_ctrl->put)
			break;

		msleep(1);
	}

	printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);

	if (rsx_fifo_ctrl->get != rsx_fifo_ctrl->put) {
		printk(KERN_INFO"FIFO command buffer timeout\n");
		res = -ENXIO;
		goto fail_unmap_fifo_ctrl;
	}

	return 0;

fail_unmap_fifo_ctrl:

	iounmap(rsx_fifo_ctrl);


fail_free_gpu_mem:

	lv1_gpu_memory_free(rsx_mem_handle);

fail_free_fifo_cmd_buf_mem:

	kfree(rsx_fifo_cmd_buf);

fail:

	return res;
}

/*
 * ps3rsx_exit
 */
static void __exit ps3rsx_exit(void)
{
	iounmap(rsx_fifo_ctrl);

	lv1_gpu_context_iomap(rsx_ctx_handle, RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar,
		RSX_FIFO_CMD_BUF_SIZE, CBE_IOPTE_M);

	lv1_gpu_context_free(rsx_ctx_handle);

	lv1_gpu_memory_free(rsx_mem_handle);

	kfree(rsx_fifo_cmd_buf);
}

module_init(ps3rsx_init);
module_exit(ps3rsx_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PS3 RSX");
MODULE_AUTHOR("glevand");

Test[edit | edit source]

# insmod ./ps3rsx.ko
# dmesg

GET offset (0x0e000000) PUT offset (0x0e000000)  # GET and PUT offsets before kicking FIFO
GET offset (0x0e00000c) PUT offset (0x0e00000c)  # GET and PUT offsets after kicking FIFO

As you see, RSX processed our FIFO commands :)

Linux Driver[edit | edit source]

  • DRI/DRM is the ONLY way to go !!! No hacks like kernel modules with tons of IOCTLs !!!
  • First implement 2D acceleration and then add 3D support
  • The driver consists of 2 parts: DDX driver for X11 (user space) and DRM driver for Linux Kernel (kernel space)
  • First implement DRM driver and test it from user space without DDX and libdrm by talking to it directly

DDX Driver[edit | edit source]

  • Use libdrm
  • Use EXA API for 2D acceleration on X11 (or maybe use XAA API)
  • Use Kernel Mode Setting

DRM Driver[edit | edit source]

  • Extend nouveau driver or create a new one ???
  • Decision: create new DRM driver in order to learn how DRM framework in Linux kernel works and because we have to use LV1 calls to access RSX (and because it's a lot more fun to do it on my own). But use nouveau as an example for DRM driver. Maybe i should better use radeon DRM driver as an example beacuse it seems to be better designed and implemnted !!!
  • The driver is very low level and allows direct access to almost all RSX funtions, e.g. FIFO buffer, to achieve maximum performance.
  • All data buffers, e.g. vertices and textures, are managed by DRM framework (Linux kernel). To avoid copying from user to kernel space, the buffers will be mmaped into user space.
  • Provides an interface to manage graphic objects in VRAM.
  • Use TTM or GEM ??? TTM is used by radeon and nouvea drivers, so i guess we could use it too. GEM is for Intel chips.
  • Extend libdrm library to support new DRM driver.
  • Fences can be implemented with RSX REF Control Register

Memory Management[edit | edit source]

  • Size of all memory objects must be multiple of the page size (4096 bytes) even if a smaller size is requested by user
  • Nouveau driver uses IOCTL DRM_NOUVEAU_GEM_NEW to allocate memory objects in VRAM or GART. The IOCTL returns the handle of the newly allocated memory object.
  • An example from Mesa how memory objects are used: [3] [4]

Video RAM[edit | edit source]

  • VRAM is allocated once during context creating and cannot be changed during the whole life of the context.
  • lv1_gpu_memory_allocate returns LPAR address of allocated VRAM which can be mapped into kernel address space.
  • VRAM starts at offset 0x0 in GPU address space.
  • VRAM heap management is necessary, use e.g. TTM (ttm_bo_init_mm).
  • This memory type is used e.g. for vertices or textures.
  • It should be mappable from user space in order to allow user to put data there.
  • GameOS calls it Local Memory.
  • VRAM can be mapped into kernel-space with ioremap.
  • To map VRAM into user-space map it first into kernel-space with ioremap and then use remap_pfn_range to map into user-space.
  • Use VM_IO flag for this kind of memory when mapping it into user-space.
  • Mapping examples: [5] [6]

GART Memory[edit | edit source]

  • GART memory region is a memory region in System Memory but accessible by RSX through GART [7].
  • GameOS calls it Main Memory.
  • Problem: lv1_gpu_context_iomap supports ONLY 1MB and 64kB pages
  • Size of system memory objects mapped into GPU address space should be either multiple of 1MB which means wasting lots of RAM and we don't have enough of it anyways. This solution is NOT suitable.
  • Or place several GART memory objects into 1 MB page and map it. That would mean we have to use memory manager for each 1MB page.
  • That means, we have to allocate 1MB page even if user requested a smaller memory region. Then initialize a heap manager for this 1MB page and return ONLY requested size. The following requests for GART memory regions can be satisfied from the previously allocated 1MB pages which still have enough free memory.
  • FIFO command buffer is an example of a GART memory object which has to be mapped into GPU address space with lv1_gpu_context_iomap before it can be used by RSX.
  • User allocates FIFO command buffer in GART address space, maps it into user space, write commands into it and then pushes it to DRM driver which maps it into RSX address space and CALLs it.
  • TTM: TTM_PL_FLAG_TT for GART memory
  • GameOS applications using GCM library map GART memory beginning at offset 0x10000000 or 0x20000000, just after where the whole VRAM is mapped.
  • Don't use kmalloc for this type of memory. Use __get_free_pages and mark pages with flag VM_RESERVED before exporting it to user-space else they can be swapped out.
  • TTM uses struct ttm_backend_func to call driver specific GART mapping functions. nouveau_sgdma.c handles GART memory mapping.

CPU Memory[edit | edit source]

  • This type of memory cannot be accessed by RSX at all.
  • Because this type of memory is not mapped into RSX address space through GART we don't need to allocate it in 1MB multiples.
  • What do we need it for ???

Mapping Memory Objects into Kernel-Space[edit | edit source]

  • Nouveau driver uses ttm_bo_kmap to map memory objects into kernel-space (see ttm_bo_util.c).
  • Nouveau driver uses ttm_bo_ioremap to map IO memory into kernel-space, e.g. VRAM or GPU registers (see ttm_bo_util.c) which uses ioremp_wc or ioremp_nocache.
  • TTM uses page-wise allocation for buffers. The buffers are contiguous ONLY in a single page. That has a huge advantage over allocating 1MB contiguous memory blocks in kernel space. It's far easier to allocate a single page in Linux kernel than 1MB memory chunk, especially on PS3 arch which has only 256MB.
  • Problem: lv1_gpu_context_iomap allows ONLY 1MB pages. Use lv1_put_iopte ???. See [8], [9], [10] and [11].
  • Yes, we can use lv1_put_iopte instead of lv1_gpu_context_iomap. That would solve the problem with 1MB pages on Linux. Both LV1 calls use the same internal LV1 function to map memory pages.
  • lv1_gpu_context_iomap uses IOAS_ID 0 and IOID 1.
  • TTM allows to map a buffer multiple times. Mapping information is stored in struct ttm_bo_kmap_obj.
  • To make single allocated pages look contiguous to kernel-space, TTM uses vmap.
  • It is possible to use 64KB pages for GART mapping without patching LV1. To enable 4KB pages support we have to patch LV1.
  • Tested with 64kB IO page size. It works fine.

Mapping Memory Objects into User-Space[edit | edit source]

  • User-space programs should be able to allocate memory objects in VRAM or GART and map it with mmap syscall.
  • See nouveau_ttm.c:nouveau_ttm_mmap.
  • Mapping memory objects into user-space avoids copying of data between user/kernel spaces.
  • Problem: how to identify memory objects ???
  • libdrm uses handles which are returned by DRM kernel driver when a new memory object is created. The handle is passed to mmap syscall as parameter offset. DRM driver looks up the handle and identifies the appropriate memory object which is mapped into user-space then.
  • Nouveau driver uses TTM framework to map memory objects into user-space. TTM doesn't map all pages owned by the memory object at once but installs VM operation fault which maps single pages on demand. It makes sense because user application rarely accesses all pages of the mapped memory object at once.
  • To map memory objects located in VRAM we have to map it into kernel space first with ioremap.

FIFO Command Buffer[edit | edit source]

  • Every context has its own one main FIFO command buffer which is NOT accessible directly by user space.
  • User-space applications can allocate additional FIFO command buffers in GART memory space, map it into user space, store commands there and submit to DRM driver.
  • Nouveau driver uses IOCTL NOUVEAU_GEM_PUSHBUF to execute FIFO command buffers. See nouveau_gem.c:nouveau_gem_ioctl_pushbuf.
  • By user applications submitted FIFO command buffers are mapped by DRM driver into RSX address space first and then executed with CALL command.
  • Problem: All references to graphics objects contained in FIFO command buffers must be expressed in RSX address space. How does user space know the right offsets of the referenced objects ???
  • To solve the above problem, Nouveau driver uses relocations which are submitted to DRM driver together with FIFO command buffers. The DRM driver applies the specified relocations before executing the FIFO command buffer. See nouveau_gem.c:nouveau_gem_pushbuf_reloc_apply.
  • Relocations contain memory object handles which they apply to. The DRM driver looks up the memory object by its handle and the memory objects contain GPU address space offsets.
Example[edit | edit source]
       ---------------------------------------------------------------
      |                                                               |
      |                                                               |
     \|/     Main FIFO command buffer (one per allocated context)     |
 ------------------------------         ------------------------------------
|           |         |                    |          |          |          |
|    ...    |   CALL  |         ...        |   CALL   |   ...    |   JMP    |
|           |         |                    |          |          |          |
 ------------------------------         ------------------------------------
                 |       /|\                    |         /|\
    -------------|        |                     |          |
    |               ------|             --------|          |
   \|/              |                   |               ---|
 -----------------------                |               |
|       |       |       |               |               |
|  ...  |  ...  |  RET  |               |               |
|       |       |       |               |               |
 -----------------------                |               |
   FIFO command buffer 1                |               |
  (allocated by user space)            \|/              |
                                     -----------------------
                                    |       |       |       |
                                    |  ...  |  ...  |  RET  |
                                    |       |       |       |
                                     -----------------------
                                       FIFO command buffer 2
                                     (allocated by user space)

Fences[edit | edit source]

  • Nouveau driver implements DRM fences with REF control register. See nouveau_fence.c:nouveau_fence_new.
  • Newer Nvidia chips support semaphores. Nouveau driver uses semaphores for fences if they are supported.
  • libgcm functions SetWriteCommandLabel and SetWaitLabel use semaphores.
  • SetWriteCommandLabel releases semaphore and SetWaitLabel acquires semaphore.
  • Semaphores are placed in VRAM. Nouveau driver creates a small VRAM heap for semaphores. See nouveau_fence.c:nouveau_fence_channel_init.

IOCTLs[edit | edit source]

Context Create[edit | edit source]
  • Creates new RSX context
  • Allocates VRAM and memory for FIFO buffer
  • Needed VRAM size and FIFO buffer size must be known during context creation
Context Destroy[edit | edit source]
  • Destroys previously allocated context
Context Attribute[edit | edit source]
  • Changes context attributes
Graphic Object Creatre[edit | edit source]
  • Create a graphic object either in VRAM or in XDR
  • Used to create FIFO command buffers too (only in XDR of course because RSX supoorts FIFO command buffer in XDR only)
Graphic Object Destroy[edit | edit source]
  • Frees previously created graphic object
FIFO Execute[edit | edit source]
  • Allows user space applications to execute FIFO commands.
  • To avoid copying of buffers allocated by user space to main FIFO command buffer use CALL and RET RSX FIFO commands to execute FIFO commands in buffers allocated by user space.
  • Several FIFO command buffers can be submitted at once.
Framebuffer[edit | edit source]
  • Kernel DRM driver has to implement a frame buffer driver too
  • Nouvea driver allocates frame buffer in video RAM and maps it into kernel address space (see nouveau_fbcon.c:nouveau_fbcon_create). Current ps3fb Linux driver doesn't allocate frame buffer in vide RAM but in system RAM.
  • Direct access to video RAM from kernel is very very slow but some of frame buffer functions in Nouvea driver are hardware accelerated. We could do it the same way on Linux and get a hardware accelerated frame buffer this way. Not sure why ps3fb authors didn't add hardware acceleration to frame buffer. The reason why it was not implemnted in ps3fb is because LV1 doesn't create 2D graphic objects needed for 2D hardware acceleration.
  • lv1_gpu_allocate_memory returns LPAR address of video RAM allocated for the RSX context.
  • Unfortunately lv1_gpu_context_allocate doesn't initialize 2D ROP objects but we could use 3D operations to implement 2D ROPs.

libdrm[edit | edit source]

  • Add support for RSX DRM to libdrm

Test Kernel Module and Program[edit | edit source]

  • I uploaded here a test kernel module and a test user application: [12] and [13]
  • I used a similar technique for mapping GPU resources into user-space like Linux kernel DRM drivers do it, e.g. Nouveau. But of course everything is very simplified in comparison with Nouveau driver. All GPU resources are mapped to user-space with mmap and there is no data copying between user and kernel space, for performance reasons. Mapping GPU resources into user-space like this is more flexible than IOCTLs.
  • The purpose of the kernel module and the user application is to test how RSX works, to test FIFO commands and other stuff i reversed from Lv2. It's NOT for end users.
  • Before loading the kernel module make sure ps3vram kernel module is NOT loaded.
  • I used 64kB IO pages for GPU context. 4kB IO page size would be definitely a lot better for that we have to patch LV1. I will add this patch to my ps3mfw tasks for LV1.
  • Just load the kernel module and then run the user application.
  • The user application maps all context resources and executes some simple FIFO commands, like JMP or SET REF.
  • I will add more examples later.
  • By default, the kernel module allocates 8MB VRAM, 64kB FIFO and 1MB GART memory. You can change it by using kernel module parameters.
  • Take a look at how i made non-contiguous allocated GART memory look contiguous to GPU, kernel-space and user-space.
  • The kernel module needs some IOCTLs, e.g. for setting display buffers or flip status, because it can be done ONLY with LV1 calls. I will add it later.

Links[edit | edit source]

BD Drive[edit | edit source]

Crossreference: gitbrew.org::HV#BD Drive


Profile[edit | edit source]

  • BD profile can be read with GET PROFILE device command or SCSI command GET CONFIGURATION

Profile Table[edit | edit source]

Profile Description
0x0 No Current Profile
0x2 Removable Disk
0x8 CD-ROM
0x9 CD-R
0xa CD-RW
0x10 DVD-ROM
0x11 DVD-R Sequential recording
0x12 DVD-RAM
0x13 DVD-RW Restricted Overwrite
0x14 DVD-RW Sequential recording
0x1a DVD+RW
0x1b DVD+R
0x40 BD-ROM
0x41 BD-R Sequential Recording(TBD)
0x42 BD-R Random Recording(TBD)
0x43 BD-RE
0x50 PS1 CD-ROM
0x60 PS2 CD-ROM
0x61 PS2 DVD-ROM
0x70 PS3 DVD-ROM
0x71 PS3 BD-ROM
0x10000 CD-DA
0x20000 SACD
0x100000 Dual Layer (Parallel)
0x200000 Dual Layer (else Parallel)

Buffer[edit | edit source]

  • BD drive has several buffers associated with internal flash
  • Buffer can be read and written with SCSI commands READ/WRITE BUFFER
  • Writing buffer is enabled with SCSI command MODE SELECT 10 first

Buffer Table[edit | edit source]

ID Size Description
0x0 0x8000 Used to transfer firmware to BD drive
0x1 0x800 Serial Flash
0x2 0x60 P-Block
0x3 0x670 S-Block
0x4 0x8000 Host Revocation List (HRL) Empty
0x5 0x8000 Host Revocation List (HRL) Current
0x6 0x670 S-Block
0x7 0x8000 Host Revocation List (HRL)

HRL Buffer[edit | edit source]

  • Size is 32KB just like AACS specifications prescribes (See AACS Common Specification 3.2.5.2 Host Revocation List Record)
  • We could replace HRL with an older one in BD drive flash and restore revoked Host Certificates !!!

Device Commands[edit | edit source]

Get Profile (0x11)[edit | edit source]

  • BD profile can be read with LV1 call lv1_send_storage_device_command and command 0x11
  • LV1 sends SCSI command GET CONFIGURATION to BD drive with requested type 0x0, starting feature number 0x0 and allocation length 0x8
  • See SCSI command GET CONFIGURATION

Auto Request Sense Mode On/Off (0x30)[edit | edit source]

  • LV1 expects a 4 byte value: 0x0 - On, 0x1 - Off
  • can be get/set via GameOS sc0x25C/604: sys_storage_send_device_command(fd of bdvd,0x30,value,4,0,0 )

SCSI Commands[edit | edit source]

Get Configuration[edit | edit source]

Getting the profile of a BD movie disc:

# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00
SCSI Status: Good 

Sense Information:
sense buffer empty

Received 8 bytes of data:
 00     00 00 00 38 00 00 00 40                             ...8...@   

# 0x40 means BD-ROM

Getting the profile of a PS3 game disc:

# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00
SCSI Status: Good 

Sense Information:
sense buffer empty

Received 8 bytes of data:
 00     00 00 00 38 00 00 ff 71                             ...8...q 
 
# 0x71 means PS3 BD-ROM

Get SS Key[edit | edit source]

  • By SCSI standard undocumented parameters are used
  • SCSI Report Key command with key format 0x3 and key class 0xe0
  • 8 bytes are returned by BD drive
  • Used by VSH

Test with PS3 game disc:

# sg_raw -r 8 /dev/sr0 a4 00 00 00 00 00 00 e0 00 08 03 00
SCSI Status: Good 

Sense Information:
sense buffer empty

Received 8 bytes of data:
 00     00 06 00 00 00 00 00 04                             ........        

Eject Media[edit | edit source]

sg_raw /dev/sr0 0x1b 00 00 00 02 00

Load Media[edit | edit source]

sg_raw /dev/sr0 0x1b 00 00 00 03 00

Mode Select 10[edit | edit source]

Enable Buffer Write[edit | edit source]

  • Uses PF 0x1, SP 0x0 and parameter list length 0x10
  • Uses the following parameter list: 0x00 0x0e 0x00 0x00 0x00 0x00 0x00 0x00 0x2d 0x6 <buffer id> 0x00 0x00 0x00 0x00 0x00
  • Enables writing to BD drive flash, e.g. to HRL buffer !!!

Test with sg3-utils which enables write to HRL buffer:

sg_raw /dev/sr0 55 10 00 00 00 00 00 00 10 00 00 0e 00 00 00 00 00 00 2d 06 04 00 00 00 00 00

Write Buffer[edit | edit source]

  • Used e.g. by Update Manager to send BD firmware to BD drive
  • Mode 0x5 (Download microcode and save) is used e.g. to write HRL to BD drive flash
  • Mode 0x7 (Download microcode with offsets and save) is used e.g. to write BD firmware to BD drive flash

AACS[edit | edit source]

AACS SPU Module[edit | edit source]

  • BD player on GameOS uses AacsModule.spu.isoself (/dev_flash/bdplayer) to perform AACS authentication
  • Tested on OtherOS++ 3.55
  • Host certificate, host private key and AACS LA public key are stored encrypted with AES-256-CTR in the SPU module and are decrypted when the SPU module is loaded or when it's accessed first. The AES-256-CTR key and IV are in the SPU module too.
  • 4.76 uses new Host certificate

Communication[edit | edit source]

  • BD player reads EID3 with Indi Info Manager 0x17001/0x17002 services and passes it to SPU module
  • EID3 is NEVER used in the SPU module although BD player passes it to the SPU module
  • Data is exchanged with the SPU module through SPU In Mbox, SPU Out Intr Mbox and a data buffer in XDR memory of size 0x2000 bytes.

Commands[edit | edit source]

  • The SPU module supports max 0x78 (til 4.75, 0x57 since 4.76) commands but not all are implemented
  • After a command is finished by the SPU module, it sends the status of the command to PPU through SPU Out Intr Mbox. Value 0 means success.


No full list!
Command in FW Name Parameters Info
-4.75 4.76+
0x02 0x34 Read 4 Bytes from XDR Buffer
  • It just reads 4 bytes of data from the XDR buffer passed to the SPU module.
0x1C 0x48 Set KCD
  • Sends KCD (Key Conversion Data) to the SPU module.
  • KCD is encrypted with the Bus Key which was established previously by AACS authentication.
0x34 0x23 Init AES_H
  • Initializes AES_H hashing function.
0x35 0x22 Calculate AES_H 1
  • Calculates AES_H hash of the data stored in XDR buffer.
0x21 2x 4 Bytes

Signed CSS CheckCRL

0x56

Get Random Seed

0x32

Unknown

0x36 0x24 Calculate AES_H 2
  • Calculates AES_H hash of the data stored in XDR buffer.
0x3C 0x12 Generate Host Nonce
  • Generates a nonce which is returned in command 0x3D / 0x0C
0x3D 0x0C Get Host Nonce and Certificate
  • The data returned by this command is of size 0x14 (Nonce) + 0x5c (Host Certificate)
  • The data returned by this command is sent by BD player with SCSI command SEND KEY to BD drive during AACS authentication
  • Host Certificate is easy to get from the SPU module, e.g. with aacs_module on OtherOS++
  • The data contains a nonce, host public key and host certificate signature.
0x3E 0x0D Set Drive Nonce and Certificate
  • Stores BD drive nonce and certificate in local memory of SPU
0x3F 0x0E Verify Drive Certificate
0x40 0x0A Set Drive Key
0x44 0x10 Sign Host Key
0x45 0x0B Get Host Key
0x46 0x14 Calculate Bus Key
0x47 0x1C Set Volume ID
  • Sends volume id and its MAC to the SPU module
0x48 0x1D Calculate Volume ID MAC
  • Calculates MAC of the passed volume id
0x49 0x15 Verify Volume ID MAC
  • Verifies MAC of the passed volume id
0x4A 0x1A Set PMSN
  • Sends PMSN and its MAC to the SPU module
0x4B 0x1B Calculate PMSN MAC
  • Calculates MAC of the passed PMSN
0x4C 0x16 Verify PMSN
  • Sends media id and its MAC to the SPU module
0x4D 0x18 Set Media ID
  • Sends media id and its MAC to the SPU module
0x4E 0x19 Calculate Media ID MAC
  • Calculates MAC of the passed media id
0x4F 0x17 Verify Media ID MAC
  • Verifies MAC of the passed media id
0x55 0x1F Verify Host/Drive Revocation
  • BD player stores HRL/DRL list entries in XDR buffer and passes it to the SPU module for verification
0x72 0x25 OCRL related, Content Revocation List
0x74 0x26 OCRT related
0x75 0x27 OSIG related
0xFEFEFEFF 0xFEFEFEFF Terminate Session
  • AACS SPU module runs and processes commands as long as you need
  • After a command is complete, the SPU module waits for the next command
  • This command terminates the current session and stops SPU module

Drive Revocation List (DRL)[edit | edit source]

  • SHA1 hash is encrypted/decrypted by SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)
  • SHA1 hash is read with VTRM service 0x2005 (Retrieve)
  • SHA1 hash is written with VTRM service 0x2003 (Store With Update)

Content Revocation List (CRL)[edit | edit source]

  • SHA1 hash is encrypted/decrypted by SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)
  • SHA1 hash is read with VTRM service 0x2005 (Retrieve)
  • SHA1 hash is written with VTRM service 0x2003 (Store With Update)

Host Revocation List (HRL)[edit | edit source]

  • Stored in BD drive flash
  • It can be read/written with SCSI commands READ/WRITE BUFFER. Yeah, it can be written too :D

Read HRL from BD Drive Flash[edit | edit source]

  • It seems that BD drive has several HRL in its flash
  • Empty HRL stored on BD drive flash can be read with SCSI command READ BUFFER by using as mode 0x2, buffer id 0x4 and allocation length 0x40
  • Current HRL stored on BD drive flash can be read with SCSI command READ BUFFER by using as mode 0x2, buffer id 0x5

Empty HRL[edit | edit source]

# sg_read_buffer -m 2 -i 4 -o 0 -l $((0x40)) /dev/sr0 
 00     10 00 00 0c 00 03 10 03  00 00 00 01 21 00 00 34                    
 10     00 00 00 00 00 00 00 00  1b 0b f2 6d 47 9e 77 62                    
 20     3d 91 fc 78 b1 59 c9 52  ca a4 c7 41 85 24 96 64                    
 30     8d 1d 95 8e 9b 84 c6 fa  4a dd 43 9b 42 98 fe ff  

# byte 0x21 at offset 0xc means Record Type HRL

# as you see this HRL is empty

Current HRL[edit | edit source]

# sg_read_buffer -m 2 -i 5 -o 0 -l $((0x7c)) /dev/sr0 
 00     10 00 00 0c 00 04 10 03  00 00 00 09 21 00 00 6c                    
 10     00 00 00 07 00 00 00 07  00 09 ff ff 00 00 00 0b                    
 20     00 00 ff ff 00 00 00 16  00 08 ff ff 00 00 00 21                    
 30     00 03 ff ff 00 00 00 35  00 04 ff ff 00 00 00 4e                    
 40     00 03 ff ff 00 00 00 54  00 03 ff ff 00 00 00 5e                    
 50     80 93 3a 62 f5 5a 9c 8c  62 ce 7d b8 69 5d d7 b1                    
 60     c3 0f 36 ff 96 a2 3b 32  cb cd 58 d4 12 c9 fd bf                    
 70     f5 16 a6 4a 32 ba 60 f0  5d 71 74 10 

# the current HRL is NOT empty and is from MKBv9 because the only BD movie i played on my PS3 has MKBv9

PS3 BD Player Host Certificate[edit | edit source]

$ hexdump -C aacs_auth/ps3_host_cert.bin 
00000000  02 01 00 5c ff ff 80 00  00 39 00 00 65 ea c9 87  |...\ÿÿ...9..eêÉ.|
00000010  8b 85 ef f4 d7 7a 62 b1  d6 00 02 4a ce 68 dd 33  |..ïô×zb±Ö..JÎhÝ3|
00000020  66 88 0e 4f 84 4f 34 b7  7a 05 01 35 a2 0e 73 b6  |f..O.O4·z..5¢.s¶|
00000030  26 da ea 51 57 b3 2e b8  4b c6 e8 7b 0d ee 4d 83  |&ÚêQW³.žKÆè{.îM.|
00000040  3c ea da 86 12 01 51 00  2c 3c 66 d5 25 6f 71 cf  |<êÚ...Q.,<fÕ%oqÏ|
00000050  a6 8b 7e 55 ba 1b 35 1f  34 03 43 4e              |Š.~Uº.5.4.CN|
0000005c

# Host ID is 0xffff80000039

PS3 BD Player Host Private Key[edit | edit source]

$ hexdump -C aacs_auth/ps3_host_priv_key.bin 
00000000  00 66 8c 9a 75 ee fc 8d  a4 26 19 38 e2 71 28 50  |.f..u....&.8.q(P|
00000010  61 bb 09 f0 dd                                    |a....|

AACS Processing Keys[edit | edit source]

MKB v1[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v1.inf

=MKB=
type:
0x00031003
version:
0x00000001

MKB U masks and UVs: 514

=applying subset-difference=
index: 0
UV: 0x00000001
U mask: 0xff800000
V mask: 0xfffffffe

=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|

processing key:
00000000: 09 f9 11 02 9d 74 e3 5b d8 41 56 c5 63 56 88 c0 |.....t.[.AV.cV..|

C value:
00000000: cb 06 90 db e6 54 55 7b 12 62 aa d7 89 f4 9d 92 |.....TU{.b......|

media key:
00000000: b4 6c 48 5e f7 51 ae 29 ef 87 bc 58 28 f3 2a 8d |.lH^.Q.)...X(.*.|

=MKB verify media key data=
encrypted:
00000000: 46 32 5b 42 48 b4 86 5a fc ef 75 25 47 b1 b5 12 |F2[BH..Z..u%G...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 0d ac 14 b9 ee f4 bd cc |.#Eg............|

MKB v3[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v3.inf

=MKB=
type:
0x00031003
version:
0x00000003

MKB U masks and UVs: 528

=applying subset-difference=
index: 14
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00

=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|

processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|

C value:
00000000: 0a b7 33 82 85 62 91 d1 91 4a 95 9e 36 18 c7 a1 |..3..b...J..6...|

media key:
00000000: 6e da eb d4 88 aa 38 58 74 26 35 fd fd 36 66 d5 |n.....8Xt&5..6f.|

=MKB verify media key data=
encrypted:
00000000: 99 76 96 b0 6f 49 37 9b c4 b9 2b be 73 ce 96 1a |.v..oI7...+.s...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef fb 01 cc 85 eb e5 bf 0a |.#Eg............|

MKB v4[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v4.inf

=MKB=
type:
0x00031003
version:
0x00000004

MKB U masks and UVs: 526

=applying subset-difference=
index: 12
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00

=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|

processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|

C value:
00000000: bf 71 0c 8b 46 a0 24 d8 f0 3a a1 26 37 9d fb fc |.q..F.$..:.&7...|

media key:
00000000: ef 18 c0 dd bf 02 32 a1 2f 57 f7 65 79 2c 1c 58 |......2./W.ey,.X|

=MKB verify media key data=
encrypted:
00000000: 54 85 08 a9 6a 70 2a c9 32 e3 74 a6 55 78 6c 01 |T...jp*.2.t.Uxl.|
decrypted:
00000000: 01 23 45 67 89 ab cd ef da 90 cf 2a e5 b2 6c 45 |.#Eg.......*..lE|

MKB v7[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v7.inf

=MKB=
type:
0x00031003
version:
0x00000007

MKB U masks and UVs: 526

=applying subset-difference=
index: 7
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00

=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|

processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|

C value:
00000000: 21 fd c9 4b 3e 1a f3 fe 9e b4 7a e6 ef 01 75 1b |!..K>.....z...u.|

media key:
00000000: af cd e2 c8 67 12 a4 b6 a8 58 0c 15 ef 07 6e f8 |....g....X....n.|

=MKB verify media key data=
encrypted:
00000000: 4b 21 29 a5 0f db 96 bc bc 01 04 71 42 79 00 e5 |K!)........qBy..|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 4e f9 d2 05 6e 19 c1 79 |.#Eg....N...n..y|

MKB v8[edit | edit source]

glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v8.inf

=MKB=
type:
0x00031003
version:
0x00000008

MKB U masks and UVs: 523

=applying subset-difference=
index: 4
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00

=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|

processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|

C value:
00000000: 73 2d 10 bd f8 b4 87 e2 86 a6 d5 3a 6d db 69 15 |s-.........:m.i.|

media key:
00000000: dd 46 d4 0d 26 54 5a ce 6c 59 0c 65 b7 2b 3a 9f |.F..&TZ.lY.e.+:.|

=MKB verify media key data=
encrypted:
00000000: c6 f6 f9 54 ce 90 e0 5e 2b 3b e4 1e 24 92 90 b2 |...T...^+;..$...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 97 e6 61 8b d1 69 3e a0 |.#Eg......a..i>.|

MKB v9[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v9.inf

=MKB=
type:
0x00031003
version:
0x00000009

MKB U masks and UVs: 520

=applying subset-difference=
index: 2
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00

=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|

processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|

C value:
00000000: a4 5a c6 87 43 49 70 bb bf 0c 22 52 83 9e 2a f6 |.Z..CIp..."R..*.|

media key:
00000000: 37 02 bd fc 96 dc a2 18 2e 55 b0 79 6d ad 36 6b |7........U.ym.6k|

=MKB verify media key data=
encrypted:
00000000: 4d 5b 7b 9c 5d ee 55 a6 94 de e1 db 8d 08 c7 a2 |M[{.].U.........|
decrypted:
00000000: 01 23 45 67 89 ab cd ef cd 1d a8 8a 42 5a 10 43 |.#Eg........BZ.C|

MKB v10[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v10.inf

=MKB=
type:
0x00031003
version:
0x0000000a

MKB U masks and UVs: 522

=applying subset-difference=
index: 3
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00

=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|

processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|

C value:
00000000: d4 77 dd 1a 8a 5c 6d d1 dd 31 2d af f7 d3 14 fa |.w...\m..1-.....|

media key:
00000000: 38 32 2b 3c 61 b0 35 b4 52 89 84 59 f4 7a 76 e6 |82+<a.5.R..Y.zv.|

=MKB verify media key data=
encrypted:
00000000: 3f d3 d5 fb 42 37 d9 05 b8 db 6b 03 a0 fe 2e 48 |?...B7....k....H|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 65 b1 87 8c eb 0d 60 0f |.#Eg....e.....`.|

MKB v12[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v12.inf

=MKB=
type:
0x00031003
version:
0x0000000c

MKB U masks and UVs: 522

=applying subset-difference=
index: 3
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00

=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|

processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|

C value:
00000000: 89 75 89 e6 6f 4a de 95 11 32 57 6a cb 99 dd 69 |.u..oJ...2Wj...i|

media key:
00000000: 4b dd 69 9d 32 98 d7 b0 ad 32 71 6b 3d 9c e3 c2 |K.i.2....2qk=...|

=MKB verify media key data=
encrypted:
00000000: 8d 43 fd f2 15 fa 58 78 64 db 25 46 62 ab 02 30 |.C....Xxd.%Fb..0|
decrypted:
00000000: 01 23 45 67 89 ab cd ef e6 1c bf 98 45 82 64 d9 |.#Eg........E.d.|

MKB v14[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v14.inf

=MKB=
type:
0x00031003
version:
0x0000000e

MKB U masks and UVs: 526

=applying subset-difference=
index: 6
UV: 0x00000248
U mask: 0xfffffe00
V mask: 0xfffffff0

=applying device key=
index: 28
UV: 0x00000280
U mask: 0xfffffe00
V mask: 0xffffff00
device key:
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...|

processing key:
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|

C value:
00000000: 8c 7e 31 e8 15 17 7e c3 2c 67 b7 cc 87 e9 39 c3 |.~1...~.,g....9.|

media key:
00000000: 4b b1 31 d1 6e 0e 86 45 89 07 a2 68 91 c4 e5 38 |K.1.n..E...h...8|

=MKB verify media key data=
encrypted:
00000000: 20 03 8c 70 7d ab d0 6f ba 86 39 f0 31 26 86 5f | ..p}..o..9.1&._|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 27 9f e5 35 0b df 3d a5 |.#Eg....'..5..=.|

MKB v15[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v15.inf

=MKB=
type:
0x00031003
version:
0x0000000f

MKB U masks and UVs: 527

=applying subset-difference=
index: 6
UV: 0x00000248
U mask: 0xfffffe00
V mask: 0xfffffff0

=applying device key=
index: 28
UV: 0x00000280
U mask: 0xfffffe00
V mask: 0xffffff00
device key:
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...|

processing key:
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|

C value:
00000000: 75 da 59 cf 0d c2 c0 95 86 fc 6b 8e 2e e9 cc 85 |u.Y.......k.....|

media key:
00000000: 28 46 25 38 3d cc 4f 1f 90 be 7d f7 8a ba 7b fd |(F%8=.O...}...{.|

=MKB verify media key data=
encrypted:
00000000: 8d d2 69 e0 b7 6a 44 53 03 ad ef 58 44 fc a7 d7 |..i..jDS...XD...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef ff 6a 7d c3 17 bb 19 11 |.#Eg.....j}.....|

MKB v16[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v16.inf

=MKB=
type:
0x00031003
version:
0x00000010

MKB U masks and UVs: 531

=applying subset-difference=
index: 5
UV: 0x00000248
U mask: 0xfffffe00
V mask: 0xfffffff0

=applying device key=
index: 28
UV: 0x00000280
U mask: 0xfffffe00
V mask: 0xffffff00
device key:
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...|

processing key:
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|

C value:
00000000: f8 49 9b d1 32 f9 6e 8d 33 98 35 a8 54 80 d9 fe |.I..2.n.3.5.T...|

media key:
00000000: 3a bf bf d7 7e b8 01 43 a9 3c 15 3f ba 47 8c e1 |:...~..C.<.?.G..|

=MKB verify media key data=
encrypted:
00000000: 8a 67 86 b6 9d 0d 22 dd 5d c2 88 1f 08 f3 ab b4 |.g....".].......|
decrypted:
00000000: 01 23 45 67 89 ab cd ef d6 32 1f 17 c4 2f e2 4a |.#Eg.....2.../.J|

MKB v17[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v17.inf

=MKB=
type:
0x00031003
version:
0x00000011

MKB U masks and UVs: 540

=applying subset-difference=
index: 14
UV: 0x00000308
U mask: 0xffffff00
V mask: 0xfffffff0

=applying device key=
index: 21
UV: 0x00000340
U mask: 0xffffff00
V mask: 0xffffff80
device key:
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss|

processing key:
00000000: 46 5f a8 be 82 85 09 01 4d 05 d2 fc ce ff 35 d2 |F_......M.....5.|

C value:
00000000: 01 f7 54 0b 34 e8 c1 ce 63 8d ea fa bc ce 6e 7b |..T.4...c.....n{|

media key:
00000000: ef 63 4e a8 ca 06 d1 6a c7 21 65 1b 18 b3 04 c6 |.cN....j.!e.....|

=MKB verify media key data=
encrypted:
00000000: d3 b9 d4 9c b6 94 47 d5 3d cc 42 fe 3e 47 40 04 |......G.=.B.>G@.|
decrypted:
00000000: 01 23 45 67 89 ab cd ef f6 b4 c8 6a b7 b8 39 fc |.#Eg.......j..9.|

MKB v18[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v18.inf

=MKB=
type:
0x00031003
version:
0x00000012

MKB U masks and UVs: 543

=applying subset-difference=
index: 17
UV: 0x00000320
U mask: 0xffffff00
V mask: 0xffffffc0

=applying device key=
index: 21
UV: 0x00000340
U mask: 0xffffff00
V mask: 0xffffff80
device key:
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss|

processing key:
00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..|

C value:
00000000: 7a 8f 03 41 27 c4 86 58 05 37 3a 90 de f8 de 26 |z..A'..X.7:....&|

media key:
00000000: e3 ed cd b4 59 b4 12 d4 ae f9 4d 8e 78 7a cd 7d |....Y.....M.xz.}|

=MKB verify media key data=
encrypted:
00000000: ea 45 fa 35 65 70 56 6f 6a 86 65 ad 52 e7 71 a4 |.E.5epVoj.e.R.q.|
decrypted:
00000000: 01 23 45 67 89 ab cd ef bd 36 f9 ce 60 54 80 3c |.#Eg.....6..`T.<|

MKB v19[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v19.inf

=MKB=
type:
0x00031003
version:
0x00000013

MKB U masks and UVs: 544

=applying subset-difference=
index: 17
UV: 0x00000320
U mask: 0xffffff00
V mask: 0xffffffc0

=applying device key=
index: 21
UV: 0x00000340
U mask: 0xffffff00
V mask: 0xffffff80
device key:
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss|

processing key:
00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..|

C value:
00000000: b9 0b 55 d1 18 3c cc 80 20 1c 9f 26 c3 58 27 18 |..U..<.. ..&.X'.|

media key:
00000000: 75 a9 79 9c 67 50 13 89 98 62 34 5b eb 54 34 dd |u.y.gP...b4[.T4.|

=MKB verify media key data=
encrypted:
00000000: c4 f0 ce 75 1b 12 b9 f0 22 2f 31 70 66 a9 6a b8 |...u...."/1pf.j.|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 66 5c 65 d3 c4 4c c7 b0 |.#Eg....f\e..L..|

MKB v20[edit | edit source]

glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v20.inf

=MKB=
type:
0x00031003
version:
0x00000014

MKB U masks and UVs: 544

=applying subset-difference=
index: 19
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8

=applying device key=
index: 18
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8
device key:
00000000: fb 4a c3 90 09 e8 21 13 d4 5e cf 4b 7e ae a4 67 |.J....!..^.K~..g|

processing key:
00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k|

C value:
00000000: 10 9f f1 69 36 07 7d 7e ad 8f d2 1a 28 c5 09 ed |...i6.}~....(...|

media key:
00000000: dc 9f 08 f7 cb 1b f8 c4 cf 96 4e 96 df 23 56 58 |..........N..#VX|

=MKB verify media key data=
encrypted:
00000000: 18 ca f5 51 8f 36 ef 2f 7a 49 78 ff 54 40 a5 f1 |...Q.6./zIx.T@..|
decrypted:
00000000: 01 23 45 67 89 ab cd ef c5 5d 11 08 c3 26 db 48 |.#Eg.....]...&.H|

MKB v21[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v21.inf

=MKB=
type:
0x00031003
version:
0x00000015

MKB U masks and UVs: 552

=applying subset-difference=
index: 19
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8

=applying device key=
index: 18
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8
device key:
00000000: fb 4a c3 90 09 e8 21 13 d4 5e cf 4b 7e ae a4 67 |.J....!..^.K~..g|

processing key:
00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k|

C value:
00000000: c0 0c fa bf f0 fe f2 32 77 19 db c4 d8 f8 60 c9 |.......2w.....`.|

media key:
00000000: 55 83 aa 69 ff 52 16 83 c2 93 b3 48 03 2a 57 38 |U..i.R.....H.*W8|

=MKB verify media key data=
encrypted:
00000000: 12 5b f2 75 c8 f8 05 6b 4f 31 a5 ea 4a 12 9f a9 |.[.u...kO1..J...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef dc 46 45 b4 79 8d 4f 68 |.#Eg.....FE.y.Oh|

MKB v23[edit | edit source]

glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v23.inf 

=MKB=
type:
0x00031003
version:
0x00000017

MKB U masks and UVs: 556

=applying subset-difference=
index: 17
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8

=applying device key=
index: 7
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8
device key:
00000000: 8b f4 fb d9 1a 7f b7 db 85 76 d1 e5 a1 5a 85 44 |.........v...Z.D|

processing key:
00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr|

C value:
00000000: f0 81 d4 93 aa b5 01 1a a7 ff 8e 18 8a 48 8a 2d |.............H.-|

media key:
00000000: 02 04 59 d0 7c b5 54 94 bf 46 9b 98 91 1e 43 1f |..Y.|.T..F....C.|

=MKB verify media key data=
encrypted:
00000000: 24 a1 27 f9 30 70 25 67 07 2f 2a d4 13 89 0d aa |$.'.0p%g./*.....|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 21 00 20 84 c4 5f 36 78 |.#Eg....!. .._6x|

MKB v25[edit | edit source]

glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v25.inf

=MKB=
type:
0x00031003
version:
0x00000019

MKB U masks and UVs: 564

=applying subset-difference=
index: 13
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8

=applying device key=
index: 7
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8
device key:
00000000: 8b f4 fb d9 1a 7f b7 db 85 76 d1 e5 a1 5a 85 44 |.........v...Z.D|

processing key:
00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr|

C value:
00000000: 19 62 23 7d 81 01 c2 55 2f 36 20 1b 3e 69 40 10 |.b#}...U/6 .>i@.|

media key:
00000000: 70 b5 9f 35 86 5d 18 73 bb 80 c3 2b f7 41 f6 14 |p..5.].s...+.A..|

=MKB verify media key data=
encrypted:
00000000: 89 be 1e 1e b1 93 4c f2 2d ac c3 ce ed 10 07 f0 |......L.-.......|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 3f 5d 87 7a 88 09 db c4 |.#Eg....?].z....|

Documentation[edit | edit source]

  • SCSI Specification: [18]
  • AACS Specification Common: [19]
  • AACS Specification Pre-recorded Video Book [20]
  • AACS Tutorial: [21]
  • AACS Overview: [22]

CSS[edit | edit source]

CSS SPU Module[edit | edit source]

  • DVD player on GameOS uses CssModule.spu.isoself (/dev_flash/bdplayer) to perform CSS authentication

Commands[edit | edit source]

  • The SPU module supports max 0x25 commands but not all are implemented
  • After a command is finished by the SPU module, it sends the status of the command to PPU through SPU Out Intr Mbox. Value 0 means success.
Generate Host Challenge Key (0x3)[edit | edit source]
  • Generates 0x10 bytes of host challenge key
Set Drive Key1 (0x4)[edit | edit source]
  • Sends key1 of size 0x5 returned by DVD drive to the SPU module
Set Drive Challenge Key (0x5)[edit | edit source]
  • Sends 0x10 bytes of drive challenge key to the SPU module
Calculate Host Key2 (0x6)[edit | edit source]
  • Calculates key2 of size 0x5
Get Host Key2 (0x7)[edit | edit source]
  • Returns key2 of size 0x5
Set Disc Key (0x8)[edit | edit source]
  • Sends Disc Key block of size 0x800 to the SPU module
Decrypt Sector (0xc)[edit | edit source]
  • Decrypts the passed sector

CSS Salt[edit | edit source]

  • It's NOT in clear text in the SPU module, it's obfuscated by xoring 0xDEAF (SONY employees have a sense of humor).
  • There are 2 bytes for every salt byte

Obfuscated:

71 ED 3F A3 DA FE E4 94  40 8C

Clear text:

F4  10  45  A3  E2

PS3 DVD Player Key Index[edit | edit source]

0x69

Documentation[edit | edit source]

  • The Content Scrambling System (CSS): [23]
  • Cryptanalysis of Contents Scrambling System: [24]
  • Cryptography in Home Entertainment: [25]
  • Patching DVD Firmware: [26]

CPRM[edit | edit source]

Commands[edit | edit source]

  • The SPU module supports 0x13 commands.

4C Secret Constant (S-Box)[edit | edit source]

Documentation[edit | edit source]

  • Cryptomeria C2 Specification: [27]
  • Cryptoanalysis of C2: [28]

Remarrying BD Drive on OtherOS++[edit | edit source]

fdm_spu_module.self[edit | edit source]

  • This SPU module can create either P- or S-Block which are sent to BD drive
  • EID2 is passed to the SPU module
  • A XDR memory buffer of size 0x1000 is passed to the SPU module
  • 4 bytes at offset 0x10 of the XDR memory buffer contain the type of block which should be produced by the SPU module
  • When the SPU module is finished, the XDR memory buffer contains the needed block
  • After the S- and P-Blocks are produced by the SPU module, they are decrypted again but with DES (CBC mode, key length is 64 bits, initialization vector length is 64 bits) before they are sent to BD drive. S$ny cuts the header and the footer of 16 bytes each from final P- and S-blocks before sending them to drive.

Block types[edit | edit source]

Type Description BD Drive Buffer ID
0x1 P-Block 0x2
0x2 S-Block 0x3

Remarrying[edit | edit source]

Preparations[edit | edit source]

  • You will need ps3dm-utils and sg3-utils
  • Dump your EID2 from flash or with ps3dm-utils
  • First create P- and S-Blocks from your EID2 with kernel module fdm_spu_module

P-Block[edit | edit source]

Decrypted P-Block (and EID4) contains region settings (see below)

In decrypted P-Block(bytes 0x30 and 0x32) and in EID4(first byte) these bytes match Product Code:

Hex bitflag Product Code Console Type Remarks
0xFF 11111111 0x80  NOT IN USE  No BD playback on that Product Code
0xFF 11111111 0x81  TOOL  Reference Tool or  SD  System Debugger / DECR No BD playback on that Product Code
0xFF 11111111 0x82  DEX   AV TEST   DTCP-IP  Debug / AV Tool / DTCP-IP Debugger / DECH / DECHS No BD playback on that Product Code
0x01 00000001 0x83  CEX  Retail or  SHOP  Kiosk - Japan / CECH bit 0 (Region 0: Japan?)
0x02 00000010 0x84  CEX  Retail or  SHOP  Kiosk - USA / CECH bit 1 (Region 1: USA & Canada, Bermuda, and US Territories)
0x04 00000100 0x85  CEX  Retail or  SHOP  Kiosk - Europe / CECH bit 2 (Region 2: Europe (with the exceptions of Russia, Ukraine, Belarus), South Africa, Swaziland, Middle East, Egypt, Lesotho, and Greenland)
0x10 00010000 0x86  CEX  Retail or  SHOP  Kiosk - Korea / CECH bit 4 (Region 3: Southeastern Asia)
0x04 00000100 0x87  CEX  Retail or  SHOP  Kiosk - United Kingdom / CECH bit 2 (Region 2: Europe (with the exceptions of Russia, Ukraine, Belarus), South Africa, Swaziland, Middle East, Egypt, Lesotho, and Greenland)
0x08 00001000 0x88  CEX  Retail or  SHOP  Kiosk - Mexico / CECH bit 3 (Region 4: Latin America and Australia)
0x08 00001000 0x89  CEX  Retail or  SHOP  Kiosk - Australia & New Zealand / CECH bit 3 (Region 4: Latin America and Australia)
0x20 00100000 0x8A  CEX  Retail or  SHOP  Kiosk - South Asia / CECH bit 5 (Region 5: Russia, Asia (non-southeast), and Africa)
0x10 00010000 0x8B  CEX  Retail or  SHOP  Kiosk - Taiwan / CECH bit 4 (Region 3: Southeastern Asia)
0x20 00100000 0x8C  CEX  Retail or  SHOP  Kiosk - Russia / CECH bit 5 (Region 5: Russia, Asia (non-southeast), and Africa)
0x40 01000000 0x8D  CEX  Retail or  SHOP  Kiosk - China / CECH bit 6? (Region 6: China)
0x10 00010000 0x8E  CEX  Retail or  SHOP  Kiosk - Hong Kong / CECH bit 4 (Region 3: Southeastern Asia)
0x08 00001000 0x8F  CEX  Retail or  SHOP  Kiosk - Brazil / CECH bit 3 (Region 4: Latin America and Australia)
0xFF 11111111 0xA0  ARC  Arcade / GECR No BD playback on that Product Code
Creating[edit | edit source]
Sending to BD Drive[edit | edit source]

S-Block[edit | edit source]

Creating[edit | edit source]
Sending to BD Drive[edit | edit source]

HRL[edit | edit source]

Empty HRL[edit | edit source]
Sending to BD Drive[edit | edit source]

VTRM[edit | edit source]

Crossreference: gitbrew.org::HV#VTRM

VTRM Services[edit | edit source]

Store With Update (0x2003)[edit | edit source]

  • Used by GameOS BD player to update DRL/CRL hashes

Retrieve (0x2005)[edit | edit source]

  • Product mode is NOT required
  • 0x40 bytes of data are read from NOR flash, decrypted by SYSCON and returned to the caller
  • Used e.g. by GameOS BD player to read SHA1 hashes of DRL and CRL

DRL and CRL Hashes[edit | edit source]

  • Written by GameOS BD player during DRL/CRL update
  • Read by GameOS BD player
  • Hashes are stored encrypted on NOR flash
  • Encryption/decryption is done by SYSCON (SYSCON Manager)

Test with ps3dm-utils:

# mount dev_flash3

glevand@debian-hdd:~/ps3dm-utils$ sudo mount /dev/ps3vflashe /mnt

# DRL SHA1 hash

glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/drl/DRL1
8f0652bc6162a240362f90f1b2e5405bb82ee502  /mnt/data-revoke/drl/DRL1

# CRL SHA1 hash

glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/crl/CRL1 
96791f41f9a76f4d895dd5820db108ec03d19250  /mnt/data-revoke/crl/CRL1

# Retrieve DRL and CRL SHA1 hashes from VTRM
# DRL hash is first and then follows CRL hash

glevand@debian-hdd:~/ps3dm-utils$ sudo ./ps3dm_vtrm -l 0x0 -p 0x1070000034000001 /dev/ps3dmproxy retrieve 0
0x8f 0x06 0x52 0xbc 0x61 0x62 0xa2 0x40 0x36 0x2f 0x90 0xf1 0xb2 0xe5 0x40 0x5b 0xb8 0x2e 0xe5 0x02
0x96 0x79 0x1f 0x41 0xf9 0xa7 0x6f 0x4d 0x89 0x5d 0xd5 0x82 0x0d 0xb1 0x08 0xec 0x03 0xd1 0x92 0x50
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

Backup Flash (0x2012)[edit | edit source]

  • Requires enabled product mode or else service returns always error 0x5
  • Reads and returns data from NOR flash beginning at NOR flash offset 0xec0000

Test with ps3dm-utils:

# enable product mode

# ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0
/dev/ps3dmproxy: SS retval 0

# ps3dm_vtrm /dev/ps3dmproxy  backup_flash 0 0x200 | hexdump -C
00000000  53 43 45 49 ff ff ff ff  ff ff ff ff ff ff ff ff  |SCEIÿÿÿÿÿÿÿÿÿÿÿÿ|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|
*
00000200

# dd if=/dev/ps3nflasha bs=1 count=$((0x100)) skip=$((0xec0000)) | hexdump -C
00000000  53 43 45 49 ff ff ff ff  ff ff ff ff ff ff ff ff  |SCEIÿÿÿÿÿÿÿÿÿÿÿÿ|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|
*

Flash Address Size (0x2016)[edit | edit source]

  • Requires enabled product mode or else service returns always error 0x5
  • Returns 2 64bit values: offset and size of NOR flash region

Test with ps3dm-utils:

# ps3dm_um /dev/ps3dmproxy read_eprom 0x48c07
0xff

# ps3dm_vtrm /dev/ps3dmproxy  flash_addr_size
/dev/ps3dmproxy: SS retval 5

# enable product mode

# ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0
/dev/ps3dmproxy: SS retval 0

# ps3dm_um /dev/ps3dmproxy read_eprom 0x48c07
0x00

# ps3dm_vtrm /dev/ps3dmproxy  flash_addr_size
0x0000000000000000 0x0000000000040000

Revoke List[edit | edit source]

Crossreference: gitbrew::Revoke List

LPAR 1 System Call 0x1004A[edit | edit source]

  • Installs new revoke list in LV1
  • LPAR 1 processes can use this syscall to install new revoke lists at runtime
  • lv2ldr is loaded by LV1 and used to verify the passed revoke list
  • After lv2ldr is done verifying the passed revoke list, it checks for stop code and if it's 0xB then LV1 replaces the old revoke list with the new one
  • If the verification of the revoke list was successfull then LV1 installs new revoke list and replaces the old one in the ISO loader table at address 0x10100

rvk_list_verifier[edit | edit source]

  • Stop code 0xB means that the passed revoke list is valid.
root@debian-hdd:/home/glevand/rvk_list_verifier# cat /proc/rvk_list_verifier/debug 
PPE id (0x0000000000000001) VAS id (0x0000000000000002)
lv1_construct_logical_spe (0x00000000)
SPE id (0x0000000000000033)
lv1_enable_logical_spe (0x00000000)
lv1_set_spe_interrupt_mask(0) (0x00000000)
lv1_set_spe_interrupt_mask(1) (0x00000000)
lv1_set_spe_interrupt_mask(2) (0x00000000)
lv1_set_spe_privilege_state_area_1_register (0x00000000)
ea (0xc000000003f40000) esid (0xc000000008000000) vsid (0x0000408f92c94500)
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
out interrupt mbox (0x0000000000000001)
lv1_clear_spe_interrupt_status(2) (0x00000000)
transferring ldr args to LS
waiting until MFC transfers are finished
MFC transfers done
out mbox (0x00000001)
sleep
lv1_get_spe_interrupt_status(0) (0x00000000)
lv1_get_spe_interrupt_status(1) (0x00000000)
lv1_get_spe_interrupt_status(2) (0x00000000)
problem status (0x000b0082)
lv1_destruct_logical_spe (0x00000000)