Editing Hypervisor Reverse Engineering

Jump to navigation Jump to search
Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.

Latest revision Your text
Line 1: Line 1:
<span style="background:red; color:#ffffff;">Warning, this page is way too long and is voted to be split into seperate sections</span>
[[Category:Software]]
 
----
----
= 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 19: Line 16:
LPAR = Logical Partition  
LPAR = Logical Partition  


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.  
lpar1 starts at 0x&lt;unknown&gt;, and its belived 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.  
lpar2 starts at 0x80000000000 and it's belived to be the memory space where lv2 stores its variables, flags and other data.  


<br>
<br>
Line 109: Line 106:


== HV call  ==
== HV call  ==
''editorial note: The table listed here was moved/merged to a seperate page : [[HV Syscalls]]''
 
{| class="wikitable FCK__ShowTableBorders"
|-
! Id
! Name
! Description
|-
| 8
| lv1_undocumented_function_8
| Retruns current uptime of HV
|-
| 62
| lv1_undocumented_function_62
| SPE (isolation, it updates a SLB entry, writes to SLB_Index, SLB_VSID, SLB_ESID and SLB_Invalidate_Entry registers)
|-
| 89
| lv1_undocumented_function_89
| SPE (writes to MFC_TLB_Invalidate_Entry register)
|-
| 99
| lv1_authenticate_program_segment
| SPE (isolation, syscall 0x10043, syscall 0x10042, syscall 0x1004A)
|-
| 102
| lv1_undocumented_function_102
| Returns current TB ticks
|-
| 137
| lv1_undocumented_function_137
| SPE
|-
| 138
| lv1_undocumented_function_138
| SPE
|-
| 167
| lv1_undocumented_function_167
| SPE (isolation, reads from SPU_Out_Intr_Mbox and MFC_CNTL registers)
|-
| 168
| lv1_undocumented_function_168
| SPE (isolation, writes to MFC_CNTL register)
|-
| 195
| lv1_undocumented_function_195
| WLAN Gelic device
|-
| 196
| lv1_undocumented_function_196
| WLAN Gelic device
|-
| 200
| lv1_undocumented_function_200
| SPE (isolation)
|-
| 201
| lv1_undocumented_function_201
| SPE (isolation)
|-
| 209
| lv1_undocumented_function_209
| SPE (isolation)
|-
| 250
| lv1_undocumented_function_250
| Storage device
|-
| 251
| lv1_undocumented_function_251
| Storage device
|-
| 252
| lv1_undocumented_function_252
| Storage device
|-
| 253
| lv1_undocumented_function_253
| Storage device
|}


=== Initializing HV Call Table ===
=== Initializing HV Call Table ===
Line 166: Line 241:
lv1_get_repository_node_value - 0x002DD260 (3.15)  
lv1_get_repository_node_value - 0x002DD260 (3.15)  


lv1_undocumented_function_231 - 0x0030B560 (3.15)
lv1_undocumented_function_231 - 0x0030B560 (3.15)  


= System call  =
= System call  =
Line 180: Line 255:
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.  


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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 - int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
0x1E - select  


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


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


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


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


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


0x24 - ?
0x24 - _unmap_pages


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


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


0x10000 - allocate_memory(LPAR id, size, log2 of page size,&nbsp;?,&nbsp;?) / construct_memory_segment
0x10000 - allocate_memory_region(LPAR id, size, log2 of page size,&nbsp;?,&nbsp;?)  
 
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
0x10001 - lpar_query_address_region_info


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


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 320: Line 364:


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 335: Line 369:
0x1002D - set_scheduling_slot  
0x1002D - set_scheduling_slot  


0x1002E - load_scheduling_table
0x10032 - accesses system console
 
0x10032 - poweroff
 
0x10033 - get_remote_file_name
 
0x10034 - allocate_cp_channel
 
0x10035 - release_cp_channel
 
0x10036 - power_down
 
0x10037 - ?
 
0x10038 - ?


0x10039 - ?
0x10036 - accesses system console


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


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


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 363: Line 383:
0x10044 - disable_spe_execution  
0x10044 - disable_spe_execution  


0x10045 - read_spu_puint_mb(unsigned long spu_id, unsigned long msg)
0x10045 - set_spe_interrupt_mask
 
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
0x10046 - read_spe_problem_state_register(spe id, register offset, value)  


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


0x1004B - disable_spe_loading  
0x1004B - disable_spe_loading  


0x1004C - install_access_control_table?
0x10053 - pmi_set_guest_os_mode


0x1004D - get_storage_status?
0x10081 - accesses system console


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


0x1004F - commit_region_update?
0x10085 - destruct_virtual_uart(LPAR id, VUART id)


0x10050 - abort_region_update?
0x10088 - RSX_syscall_10088(LPAR id)


0x10051 - set_storage_tampered?
0x10089 - RSX_syscall_10089


0x10053 - pmi_set_guest_os_mode
0x1008A - RSX_syscall_1008A


0x1007F - pause
0x100BE - lv1_ioctl


0x10080 - get_total_execution_time
0x100C0 - create_repository_node(LPAR id)  
 
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)  
0x100C1 - get_repository_node_value(LPAR id)  
Line 415: Line 413:
0x100C2 - modify_repository_node_value(LPAR id)  
0x100C2 - modify_repository_node_value(LPAR id)  


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


= Process  =
= Process  =
Line 423: Line 421:
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 570: Line 562:
*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 671: Line 653:
== Member variables  ==
== Member variables  ==


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


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


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


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


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


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


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


= ProtectionPage  =
= ProtectionPage  =
Line 840: Line 822:
=== vtable  ===
=== vtable  ===


0x003569F8 (3.15)
0x003569F8 (3.15)  


== IOIF device file objects  ==
== IOIF device file objects  ==
Line 976: Line 958:
== Repository nodes from HV 3.15  ==
== Repository nodes from HV 3.15  ==


[http://www.ps3devwiki.com/index.php?title=Repository_Nodes#3.15_Linux Dump of all repository nodes from HV 3.15]
[[Dump of all repository nodes from HV 3.15]]


== Repository nodes from HV 3.41 dump made from GameOS  ==
== Repository nodes from HV 3.41 dump made from GameOS  ==


[http://www.ps3devwiki.com/index.php?title=Repository_Nodes#3.41_GameOS Dump of all repository nodes from HV 3.41 dump made from GameOS]
[[Dump of all repository nodes from HV 3.41 dump made from GameOS]]


= Buses  =
= Buses  =
Line 1,026: Line 1,008:
=== vtable  ===
=== vtable  ===


0x352308 (3.15)
0x000x352308 (3.15)  


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


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,641: Line 1,604:
|-
|-
| 41  
| 41  
| EBUS (Flash StarShip)  
| EBUS (Flash StartShip)  
| 0x002814EC (3.15)
| 0x002814EC (3.15)
|-
|-
Line 1,705: Line 1,668:


*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''' ([[Authority ID|LPAR Authority ID]]) is evaluated for this purpose.  
*Repository node '''ss.laid''' (LPAR authentication 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,758: Line 1,721:


*The storage subsystem is a storage device itself.  
*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.  
*It's a psuedo 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,836: Line 1,799:


*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 '''[[Authority ID|LPAR Authority 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 '''LPAR authentication ID'''. If this test fails then the command is NOT executed.


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


*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 '''[[Authority ID|LPAR Authority 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 '''LPAR authentication ID'''. If this test fails then the command is NOT executed.


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


*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 '''[[Authority ID|LPAR Authority 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 '''LPAR authentication ID'''. If this test fails then the command is NOT executed.


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
Line 2,730: Line 2,693:
|-
|-
| 0x32  
| 0x32  
| ATA Standby Immediate
| ATA Stanby Immediate
|-
|-
| 0x33  
| 0x33  
Line 2,742: Line 2,705:
block size = 512  
block size = 512  


*It's a pseudo device.  
*It's a psuedo 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,475: Line 3,438:
! 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,487: Line 3,448:
| 0x00011000  
| 0x00011000  
| 0xE8D0
| 0xE8D0
| 0x00
|-
|-
| 2  
| 2  
Line 3,493: Line 3,453:
| 0x00020000  
| 0x00020000  
| 0x16DA0
| 0x16DA0
| 0x02
|-
|-
| 3  
| 3  
Line 3,499: Line 3,458:
| 0x00055000  
| 0x00055000  
| 0x12E44
| 0x12E44
| 0x04
|-
|-
| 4  
| 4  
Line 3,505: Line 3,463:
| 0x00037000  
| 0x00037000  
| 0x1DAE4
| 0x1DAE4
| 0x03
|-
|-
| 5  
| 5  
Line 3,511: Line 3,468:
| 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,593: Line 3,546:
=== appldr  ===
=== appldr  ===


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


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


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


Line 3,941: Line 3,894:
*Each entry is a pointer to a function responsible for processing SID packets.
*Each entry is a pointer to a function responsible for processing SID packets.


= AV Manager  =
= A/V Manager  =
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#AV_Manager gitbrew.org::AV Manager] <br />
 
* 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==
 
===Get HDCP KSV (0xC)===
 
* 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:
<pre>
30 01 0200 0000 8033 00000000 0004 0004 11 00 0000 0000ff01
</pre>
 
===Set HDMI Mode (0x40001)===


* Sets HDMI mode
*A/V Manager is running in Process 9 of HV.
* Mode is set by SYSCON
*It communicates with Guest OS through '''/proc/partitions/0/vuart/0 file'''.
* Disabling HDCP
*GameOS accesses A/V Manager through '''syscalls 367 - 370'''.
*PS2 Soft EMU accesses A/V Manager also.


= System Manager (SM)  =
= System Manager (SM)  =
Line 3,989: Line 3,919:
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,449: Line 4,379:
| 0xA  
| 0xA  
| 0x1B6  
| 0x1B6  
| Makes a triple beep
| Makes a double beep
|-
|-
| 0x29  
| 0x29  
Line 4,461: Line 4,391:
| 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,637: Line 4,566:
| 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_Manager|Secure RTC]]
| 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  
| [[Security_Policy_Manager|SPM (Security Policy Manager)]]
| SPM (Security Policy Manager)
|-
|-
| 0x14000  
| 0x14000  
| 0x25  
| 0x25  
| 0x14004 - 0x14005  
| 0x14004 - 0x14005  
| [[Secure_LPAR_Loader|SLL (Secure LPAR Loader)]]
| SLL (Secure LPAR Loader)
|-
|-
| 0x15000  
| 0x15000  
| 0x24  
| 0x24  
| 0x15001, 0x15003, 0x15009  
| 0x15001, 0x15003, 0x15009  
| [[Secure_Profile_Loader|SPL (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_Manager|AIM]]
| 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,740: Line 4,659:
     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,772: Line 4,691:
*The size of the body depends on a used service.
*The size of the body depends on a used service.


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


== Memory Region class ==
{| class="wikitable FCK__ShowTableBorders"
|-
! 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
|-
| 0x2012
| Backup Flash
|-
| 0x2013
| Restore Flash
|-
| 0x2014
| Backup SRK SRH
|-
| 0x2015
| Restore SRK SRH
|-
| 0x2016
| Flash Address Size
|-
| 0x2017
| Force Restart
|}


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


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


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


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


offset 0x40 - pointer to LPAR object that owns this memory region
*Secure RTC reads LAIDs and PAIDs that are allowed to access Secure RTC service from '''DEFAULT.SPP''' segment '''SCE_CELLOS_SS_SECURE_RTC'''.


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


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


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


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


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


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


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


generate_new_lpar_mem_region_address - 002C6570 (3.41)
{| class="wikitable FCK__ShowTableBorders"
|-
! 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
|}


*The function returns a new LPAR memory region address.
*Storage Manager service is used e.g. by '''syscall 864''' and '''syscall SYS_SS_MEDIA_ID'''
*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'''.
*GameOS's VSH uses '''syscall 864'''
*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.


==== Encoding LPAR Memory Region Start Addresses and Sizes ====
==== SB Isolation DMA Buffer Header  ====
<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.


*Size of LPAR memory region is encoded in the LPAR memory region start address.
=== 0x5001 - Set Encdec Key  ===
*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)
* This service allows you to set ENCDEC keys with index '''0xC - 0xF'''
* '''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.


  LPAR Memory Region Start Address = (log2(LPAR Memory Region Size) << 42) |
=== 0x5002 - Set/Delete ATA (Encdec) Key ===
    (counter << log2(LPAR Memory Region Size))
 
*Sets/Deletes ATA (Encdec) Key
*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 ====
 
{| class="wikitable FCK__ShowTableBorders"
|-
! Service Parameter
! Description
|-
| 0xC - 0xF
| Delete Encdec Key
|-
| 0x10*
| Set ATA Key (index 1)
|-
| 0x11*
| Delete ATA Key (index 1)
|}


===== LPAR Memory Region Address Counter =====
=== 0x5003 - Get Random Number  ===


*LPAR Memory Region Address Counter is stored at address: '''0x38(LPAR ptr) + 0x9E8'''
*I have got access to Get Random Number service through DM and tested it with PSGroove
*LPAR1's Memory Region Address Counter is at address '''0x00677A48''' in HV dump 3.15
*The service returns 192-bit random numbers
*LPAR2's Memory Region Address Counter is at address '''0x007632D8''' in HV dump 3.15
*It has no input parameters except those in SS packet header
*LPAR1's Memory Region Address Counter is at address '''0x00677A48''' in HV dump 3.41
*Storage Manager communicates with device '''/dev/encdec0'''.
*LPAR2's Memory Region Address Counter is at address '''0x00161E68''' in HV dump 3.41
*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.


== Physical Memory Region class ==
=== 0x5004 - Authenticate BD Drive ===


This type of memory region is created e.g. in '''lv1_allocate_memory''' HV call or in '''syscall 0x10000'''.  
*Used by LPAR Manager in HV Process 9 during LPAR 2 loading and unloading.
*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.


=== vtable  ===
==== Service Parameter Table ====


0x00357D08 (3.15)
{| class="wikitable FCK__ShowTableBorders"
|-
! 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
| -
|}


=== Member variables ===
=== 0x5005 - PS2 Disc Authenticate ===


offset 0xB0 - pointer to object that stores a list of addresses of physical pages owned by this memory region
=== 0x5006 - Get Version  ===


offset 0xB8 - pointer to LPAR object that owns this memory region
* By default not accessible from GameOS. But it can be enabled by patching Dispatcher Manager.


offset 0xC0 - reference counter (8 bytes)
=== 0x5007 - Control BD Drive  ===


=== Objects  ===
*Used by GameOS to authenticate discs and for BD emulation.


Here is the list of physical memory region objects i found in HV 3.15.
==== Service Parameter Table ====


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Address in HV dump
! Service Parameter
! LPAR id
! Description
! LPAR Start Address
! Size
! Flags
! log2(Page Size)
! Physical Page Addresses
|-
|-
| 0x006B5510
| 0x3F
| 1
| -
| 0x300000001000
| 0x1000
| 0x0
| 0xC
| 0x672000
|-
|-
| 0x006B5E50
| 0x41
| 1
| -
| 0x440000040000
| 0x20000
| 0x0
| 0x11
| 0x6C0000
|-
|-
| 0x006B6980
| 0x43
| 1
| -
| 0x440000060000
|-
| 0x20000
| 0x46
| 0x0
| -
| 0x11
|-
| 0x6E0000
| 0x51
| -
|-
| 0x52
| -
|-
| 0x53
| -
|-
|-
| 0x006B7F00
| 0xA3
| 1
| BD emu
| 0x400000040000
| 0x10000
| 0x0
| 0x10
| 0x100000
|-
|-
| 0x003A80F0
| 0xA5
| 2
| BD emu
| 0x6C0058000000
| 0x7000000
| 0x4
| 0x18
| 0x1000000 - 0x7000000
|-
|-
| 0x003BE800
| 0xA7
| 2
| BD emu
| 0x300000047000
| 0x1000
| 0x0
| 0xC
| 0x1FA000
|-
|-
| 0x006BDAA0
| 0xAA
| 2
| BD emu
| 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.
== 0x6000 - Update Manager ==
 
=== 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.
 
Here is the list of physical memory regions of GameOS i found in HV 3.41:


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Start Address
! Packet ID
! Size
! Description
! Access Right
|-
! Max Page Size
| 0x6001
! Flags
| Update Package Tophalf
! Real Addresses
|-
| 0x6002
| Inspect Package Tophalf
|-
| 0x6003
| Get Package Info
|-
|-
| 0x0
| 0x6004
| 0x1000000
| Get Fix Instruction
| 0x3
|-
| 0x18
| 0x6005
| 0x8
| Extract Package Tophalf
| 0x1000000 - 0x1FFF000
|-
| 0x6006
| Get Extract Package
|-
| 0x6009
| Get Token Seed
|-
| 0x600A
| Set Token
|-
| 0x600B
| Read EPROM
|-
| 0x600C
| Write EPROM
|-
|-
| 0x500000300000
| 0x6010
| 0xA0000
| Check Integrity
| 0x3
| 0x10
| 0x8
| 0x380000 - 0x38F000, 0x3B0000 - 0x3BF000, 0x1E0000 - 0x1FF000, 0x3C0000 - 0x3FF000, 0xFF00000 - 0xFF1F000
|-
|-
| 0x700020000000
| 0x6011
| 0xE900000 (huge memory region)
| Get Applicable Version
| 0x3
| 0x14
| 0x0
| 0x400000 - 0x5FF000, 0x800000 - 0xFFF000, 0x2000000 - 0xFEFF000
|}
|}


== HTAB Memory Region class ==
*Update Manager service is accessed by GameOS '''syscall 863'''
 
=== 0x6001 - Update Package Tophalf ===
 
*The result of the request can be checked by reading the value of repository node '''ss.update.request.&lt;Request ID&gt;''' periodically


This memory region is created when a HTAB is mapped into LPAR's address space. It's created in '''lv1_map_htab''' HV call.
=== 0x6002 - Inspect Package Tophalf  ===


=== vtable  ===
*I have got access to this service through DM and tested it with PSGroove
*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.


0x00357C98 (3.15)
==== Inspect Package Tophalf Return Values  ====


=== Member variables  ===
{| class="wikitable FCK__ShowTableBorders"
|-
! Error Code
! Description
|-
| 0x00000000
| Success
|-
| 0x00000013
| Same Version/Older Version
|-
| 0x00000014
| -
|}


offset 0xB0 - pointer to VAS object that owns the HTAB
=== 0x6003 - Get Package Info  ===


=== Objects  ===
*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 is the list of HTAB memory region objects i found in HV 3.15.
Here are the versions of packages installed on my PS3:


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Address in HV dump
! Package Type
! LPAR id
! Returned Version
! VAS id
! Description
! LPAR Start Address
! Package Name in PUP File
! Size
|-
! Flags
| 1
! log2(Page Size)
| 0x0003004100000000
| Core OS Package
| CORE_OS_PACKAGE.pkg
|-
|-
| 0x001FE0F0
| 2  
| 2  
| 0x0003004100000000
| Revoke List Package for Program
| RL_FOR_PROGRAM.img
|-
| 3  
| 3  
| 0x500000C00000
| 0x0002003000000000
| 0x100000
| Revoke List Package for Package
| 0xC000000000000000
| RL_FOR_PACKAGE.img
| 0x14
|-
|-
| 0x003BD850
| 4
| 2
| 0xDEADBEAFFACEBABE
| 3
| -
| 0x500004300000
| -
| 0x100000
|-
| 0xC000000000000000
| 5
| 0x14
| 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
| -
| -
|-
|-
| 0x003BDEA0
| 9
| 2
| Invalid Parameter
| 3
| SC Firmware Package
| 0x500004500000
| SYS_CON_FIRMWARE_*.pkg
| 0x100000
| 0xC000000000000000
| 0x14
|}
|}


=== GameOS HTAB ===
==== Decrypting and Extracting Packages with spu_pkg_rvk_verifier.self ====
 
*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'''
*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.
*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.
*I decompressed the decrypted Core OS Packages with zlib.
*I am able now to decrypt and decompress all Core OS Packages
*'''The decrypted and decompressed package CORE_OS_PACKAGE.pkg looks exactly like it's stored on flash.'''
*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  =====
<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....
00000210  00 00 00 06 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000220  00 00 00 03 00 00 00 01  00 03 00 41 00 00 00 00  ...........A....
00000230  00 00 00 00 00 00 00 02  FF FF FF FF FF FF FF FF  ........ÿÿÿÿÿÿÿÿ
00000240  00 00 00 04 00 00 00 01  00 03 00 41 00 00 00 00  ...........A....
00000250  10 70 00 05 FF 00 00 01  FF FF FF FF FF FF FF FF  .p..ÿ...ÿÿÿÿÿÿÿÿ
00000260  00 00 00 04 00 00 00 01  00 03 00 41 00 00 00 00  ...........A....
00000270  10 70 00 05 FE 00 00 01  FF FF FF FF FF FF FF FF  .p..þ...ÿÿÿÿÿÿÿÿ
00000280  00 00 00 04 00 00 00 01  00 03 00 41 00 00 00 00  ...........A....
00000290  10 70 00 05 FD 00 00 01  FF FF FF FF FF FF FF FF  .p..ý...ÿÿÿÿÿÿÿÿ
000002A0  00 00 00 04 00 00 00 01  00 03 00 41 00 00 00 00  ...........A....
000002B0  10 70 00 05 FC 00 00 01  FF FF FF FF FF FF FF FF  .p..ü...ÿÿÿÿÿÿÿÿ
000002C0  00 00 00 04 00 00 00 03  00 01 00 00 00 00 00 00  ................
000002D0  10 70 00 04 00 00 00 01  FF FF FF FF FF FF FF FF  .p......ÿÿÿÿÿÿÿÿ
</pre>
===== RL_FOR_PROGRAM.img 3.50  =====
<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 50 00 00 00 00  ...........P....
00000210  00 00 00 06 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000220  00 00 00 03 00 00 00 01  00 03 00 50 00 00 00 00  ...........P....
00000230  00 00 00 00 00 00 00 02  FF FF FF FF FF FF FF FF  ........ÿÿÿÿÿÿÿÿ
00000240  00 00 00 04 00 00 00 01  00 03 00 50 00 00 00 00  ...........P....
00000250  10 70 00 05 FF 00 00 01  FF FF FF FF FF FF FF FF  .p..ÿ...ÿÿÿÿÿÿÿÿ
00000260  00 00 00 04 00 00 00 01  00 03 00 50 00 00 00 00  ...........P....
00000270  10 70 00 05 FE 00 00 01  FF FF FF FF FF FF FF FF  .p..þ...ÿÿÿÿÿÿÿÿ
00000280  00 00 00 04 00 00 00 01  00 03 00 50 00 00 00 00  ...........P....
00000290  10 70 00 05 FD 00 00 01  FF FF FF FF FF FF FF FF  .p..ý...ÿÿÿÿÿÿÿÿ
000002A0  00 00 00 04 00 00 00 01  00 03 00 50 00 00 00 00  ...........P....
000002B0  10 70 00 05 FC 00 00 01  FF FF FF FF FF FF FF FF  .p..ü...ÿÿÿÿÿÿÿÿ
000002C0  00 00 00 04 00 00 00 03  00 01 00 00 00 00 00 00  ................
000002D0  10 70 00 04 00 00 00 01  FF FF FF FF FF FF FF FF  .p......ÿÿÿÿÿÿÿÿ
</pre>
===== RL_FOR_PACKAGE.img 3.41  =====
<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  ................
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>
===== RL_FOR_PACKAGE.img 3.50  =====
<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  ................
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  =====


*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'''
Here is a piece of data from decrypted and decompressed package.
*Effective address of GameOS HTAB is '''0x800000000F000000'''
<pre>Offset      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
*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 ===
00000000  00 00 00 01 00 00 00 17 00 00 00 00 00 6F FF E0  .............oÿà
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  ................


Here is the dump of SLB entries from GameOS 3.41:
00040460  33 31 35 2E 30 30 30 0A  00 00 00 00 00 00 00 00  315.000.........
<pre>0x8000000008000000 0x0000000000000500
00040470  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
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
</pre>
</pre>
== SPE MMIO Memory Region class ==
===== BDIT_FIRMWARE_PACKAGE.pkg 3.50 =====


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'''.  
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


=== vtable ===
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 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 =====


0x003583F8 (3.15)
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


=== Member variables ===
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


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


== SPE Shadow Registers Memory Region class ==
=== 0x600C - Write EEPROM ===
 
*Writting to EEPROM of Update Manager is also possible through DM
*Tested this service successfully with QA flag
 
=== 0x6010 - Check Integrity  ===
 
*This service checks integrity of important files stored on '''/dev/rflash1''', e.g. '''lv0''' or '''lv1'''
*The service is used e.g. by System Manager
*When '''product mode''' is NOT '''0xFF''' then check is skipped&nbsp;!!!
 
=== 0x6011 - Get Applicable Version  ===
 
*I have got access to this service through DM and PSGroove and tested it
*The service expects one additional unknown parameter of size 4 bytes, it has to be 0x00000001 or else the service fails
 
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  ===


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'''.  
*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.


=== vtable ===
==== Detecting BD Drive Type, Generation and Revision ====


0x00358448 (3.15)
*To detect BD drive type, Update Manager uses '''ATAPI Inquiry''' command.
*To detect BD drive generation, Update Manager uses '''ATAPI Mode Sense 10''' command.


=== Objects ===
===== BD Drive Type Table =====


Here is the list of SPE Shadow Registers memory region objects i found in HV 3.15.
Here is the BD Drive Type Table extracted from HV Process 6 (3.15):


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Address in HV dump
! Index
! LPAR id
! Vendor Identification String
! SPE
! Drive Type
! LPAR Start Address
|-
! Size
| 0
! Physical Address
| <pre>"SONY    EmerFlashROM"</pre>
! Flags
| 0x2100000000000001
! log2(Page Size)
|-
|-
| 0x003ABDA0
| 2
| 1  
| 1  
| 0x300000012000
| <pre>"SONY    PS-EMBOOT  300R"</pre>
| 0x1000
| 0x2100000000000001
| -  
| 0xA000000000000000
| 0xC
|-
|-
| 0x003B4290
| 2
| 2  
| 2  
| 0x300000014000
| <pre>"SONY    BDRW AQUAM(BDIT)"</pre>
| 0x1000
| 0x1100000000000001
| -
| 0xA000000000000000
| 0xC
|-
|-
| 0x003A8A00
| 2
| 3  
| 3  
| 0x300000010000
| <pre>"SONY    PS-SYSTEM  300R"</pre>
| 0x1000
| 0x1100000000000001
| -  
|-
| 0xA000000000000000
| 0xC
|-
| 0x003B50F0
| 2
| 4  
| 4  
| 0x300000016000
| <pre>"SONY    PS-SYSTEM  V300"</pre>
| 0x1000
| 0x1100000000000001
| -  
| 0xA000000000000000
| 0xC
|-
|-
| 0x001FFC90
| 2
| 5  
| 5  
| 0x30000000E000
| <pre>"SCEI    EMER-FLASH-8"</pre>
| 0x1000
| 0x2200000000000002
| -  
| 0xA000000000000000
| 0xC
|-
|-
| 0x003AE5B0
| 2
| 6  
| 6  
| 0x300000018000
| <pre>"SONY    PS-EMBOOT  301R"</pre>
| 0x1000
| 0x2200000000000002
| -  
|-
| 0xA000000000000000
| 7
| 0xC
| <pre>"SONY    PS-SYSTEM  301R"</pre>
| 0x1200000000000002
|-
| 8
| <pre>"SONY    PS-EMBOOT  302R"</pre>
| 0x2200000000000003
|-
| 9
| <pre>"SONY    PS-SYSTEM  302R"</pre>
| 0x1200000000000003
|-
| 10
| <pre>"SONY    PS-EMBOOT  303R"</pre>
| 0x2200000000000004
|-
| 11
| <pre>"SONY    PS-SYSTEM  303R"</pre>
| 0x1200000000000004
|-
| 12
| <pre>"SONY    PS-EMBOOT  304R"</pre>
| 0x2200000000000005
|-
| 13
| <pre>"SONY    PS-SYSTEM  304R"</pre>
| 0x1200000000000005
|-
| 14
| <pre>"SONY    PS-EMBOOT  306R"</pre>
| 0x2200000000000007
|-
| 15
| <pre>"SONY    PS-SYSTEM  306R"</pre>
| 0x1200000000000007
|}
|}


== Device MMIO Memory Region class ==
==== Methods (HV Process 6) ====
 
update_manager_update_bd_firmware - 0x800064BC (3.15)


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'''.  
bd_updater_prepare_drive - 0x80011A88 (3.15)


=== vtable  ===
bd_updater_send_firmware - 0x80011544 (3.15)


0x00352468 (3.15)  
bd_updater_disable_reqsense - 0x80010410 (3.15)  


=== Member variables  ===
bd_updater_enable_reqsense - 0x800104D8 (3.15)


offset 0xA8 - physical address where the device MMIO region is mapped to
send_atp_command - 0x80023B10 (3.15)


=== Objects ===
== 0x9000 - SC Manager ==


Here is the list of Device MMIO 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"
|-
|-
! Address in HV dump
! Packet ID
! LPAR id
! Description
! LPAR Start Address
|-
! Size
| 0x9001
! Flags
| Get SRH
! log2(Page Size)
! Physical Address
! Device
|-
|-
| 0x001FDF00
| 0x9002
| 2
| Set SRH
| 0x4000001D0000
|-
| 0x10000
| 0x9003
| 0x8000000000000000
| Encrypt
| 0xC
|-
| 0x24003010000
| 0x9004
| USB controller
| Decrypt
|-
| 0x9005
| Init For VTRM
|-
| 0x9006
| Get Region Data
|-
| 0x9007
| 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
|-
|-
| 0x003B3850
| 0x9014
| 2
| Set SC Status
| 0x400000200000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003020000
| USB controller
|-
|-
| 0x003B6E50
| 0x9015
| 2
| Backup Root Info
| 0x4000001E0000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003810000
| USB controller
|-
|-
| 0x003B9950
| 0x9016
| 2
| Restore Root Info
| 0x4000001F0000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003820000
| USB controller
|}
|}


== GPU Device Memory Region class ==
=== 0x9001 - SC Get SRH ===
 
<pre>
struct ss_sc_mgr_get_srh
{
    u8 field0[20];
    u8 res1[4];
    u8 field18[20];
    u8 res2[4];
};
</pre>


This type of memory region is created e.g. in '''lv1_gpu_open''', '''lv1_gpu_device_map''' and '''lv1_undocumented_function_114'''.
=== 0x9003 - SC Encrypt  ===


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


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


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


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


=== Objects ===
=== 0x9006 - SC Get Region Data ===


Here is the list of Device GPU memory region objects i found in HV 3.15.  
*This service expects an ID. The valid range of ID is 0 - 15.
*E.g. Update Manager uses this service to retrieve hash and version of some SELFs and firmwares, e.g. '''lv0''' and '''lv1'''.
 
<pre>
struct ss_sc_mgr_get_region_data
{
    u64 id;
    u64 data_size;    /* max 0x30 bytes */
    u8 data[0];
};
</pre>
 
==== Update Package Type - ID Mapping Table ====


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Address in HV dump
! Update Package Type
! LPAR id
! ID
! LPAR Start Address
|-
! Size
| 1
! Flags
| 0
! log2(Page Size)
! Physical Address
|-
|-
| 0x003AF380
| 2
| 2  
| 2
| 0x700190000000
| 0xFE00000
| 0x8000000000000000
| 0x14
| 0x28080000000
|-
|-
| 0x003AF500
| 3
| 2
| 4
| 0x4000001A0000
| 0xC000
| 0x8000000000000000
| 0xC
| 0x3C0000
|-
|-
| 0x003AF680
| 4
| 2
| 6
| 0x4800006C0000
| 0x40000
| 0x8000000000000000
| 0xC
| 0x2808FE00000
|-
|-
| 0x003AFC30
| 5
| 2
| 7
| 0x440000380000
| 0x20000
| 0x8000000000000000
| 0xC
| 0x28000C00000
|-
|-
| 0x003BB420
| 6
| 2
| 8
| 0x3C0000108000
| 0x8000
| 0x8000000000000000
| 0xC
| 0x28000080100
|}
|}


== Direct Map Memory Region class ==
=== 0x9007 - SC Set Region Data  ===


This type of memory region is created in HV call '''lv1_undocumented_function_114'''.
*This service expects an ID. The valid range of ID is 0 - 15.
'''lv1_undocumented_function_114''' allows you to map any memory address into LPAR's memory address.
*E.g. Update Manager uses this service to store hash and version of some SELFs and firmwares, e.g. '''lv0''' and '''lv1'''.


* The HV call '''lv1_undocumented_function_115''' destroys a memory region of this type.
<pre>
* HV allows GameOS to create objects of this type of size 0 only !!! But it can be exploited with a dangling HTAB entry.
struct ss_sc_mgr_set_region_data
{
    u64 id;
    u64 data_size;    /* max 0x30 bytes */
    u8 data[0];
};
</pre>


=== vtable ===
=== 0x900B - SC Read EPROM ===


0x00357C48 (3.15)
* There are 2 ways to access SC EPROM: '''NVS Service''' and '''Device Access Service'''.
* '''NVS Service''' uses '''Block ID''' and '''Block Offset'''.
* Not all EPROM offsets can be accessed through SC Manager.


=== Member variables  ===
<pre>
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>


offset 0xA8 - physical address
==== EPROM Offset - Block ID and Block Offset Mapping Table (NVS Service) ====


=== Exploiting HV with memory glitching and HV call lv1_undocumented_function_114 ===
{| class="wikitable FCK__ShowTableBorders"
|-
! EPROM Offset
! Block ID
! Block Offset
|-
| 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
|-
| 0x3000 - 0x30FF
| 0x20
| 0x3000 - 0x30FF
|-
| All other offsets
| Invalid
| Invalid
|}


Here is a short description of the method i used to exploit HV from GameOS 3.15 and 3.41.
=== 0x900C - SC Write EPROM  ===


* 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.
struct ss_sc_mgr_write_eprom
* 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.
    u32 offset;
* 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 res1[4];
* Then i mapped whole HV memory range with the patched HV call '''lv1_undocumented_function_114''' into the address space of GameOS.
    u32 nwrite;
* And now you have read/write access to the whole HV.
    u8 res2[4];
* $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 :-)
    u64 buf_size;
    u8 buf[0];
    /* here follows buf */
};
</pre>


== Methods  ==
=== 0x900E - SC Get Status ===


LPAR_get_memory_region_by_start_address - 0x002C7C40 (3.15)
Here is what the service returned on my fat PS3:
<pre>
0x00 0x00 0x00 0x03 0x00 0x00 0x00 0x00 0xC0 0x00 0x00 0xFF 0x00 0x00 0x00 0x00
</pre>


LPAR_get_memory_region_by_address - 0x002C7DA8 (3.15)
So, '''version''' is '''0x00000003''' and '''mode''' is '''0xC00000FF'''.


LPAR_mem_addr_to_phys_addr(LPAR id, LPAR address, phys_addr) - 0x002FB8F0 (3.15)
<pre>
struct ss_sc_mgr_get_sc_status
{
    u32 version;
    u8 res1[4];
    u32 mode;
    u8 res2[4];
};
</pre>


LPAR_construct_direct_mapping_mem_region - 0x002D4D04 (3.15)
=== 0x9011 - SC Binary Patch  ===


= Network Devices  =
*This service is used by Update Manager to send a new SC firmware version to SYSCON.


== Ethernet Gelic Device ==
==== SC Isolation DMA Buffer Header ====
<pre>struct sc_iso_header
{
    u32 seqno;
    u32 mbmsg;
    u32 cmd;
    u32 cmd_size;
    u8 cmd_data[0];
};
</pre>


device id = 0
== 0x11000 - SPM (Security Policy Manager)  ==


MAC Address: 00:1F:A7:C6:2A:C5
*Packet ID is mapped to '''SS id'''
*SS id value range is 0x0 - 0x84


device memory base address = 0x24003004000 (size = 0x1000)
{| class="wikitable FCK__ShowTableBorders"
|-
! Packet ID
! Description
|-
| 0x11001
| Request
|-
| 0x11002
| Load Additional Policy
|}


== WLAN Gelic Device ==
== 0x14000 - SLL (Secure LPAR Loader) ==


device id = 0
*SLL opens '''lv2_kernel.self''', parses ELF header and determines the size of initial memory region for GameOS LPAR
*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'''


MAC Address: 02:1F:A7:C6:2A:C5 (locally administered)
{| class="wikitable FCK__ShowTableBorders"
|-
! Packet ID
! Description
|-
| 0x14004
| Load GOS
|-
| 0x14005
| Unload GOS
|}


=== Net Manager ===
== 0x15000 - SPL (Secure Profile Loader) ==


*Net Manager runs in Process 9
*DEFAULT.SPP file is stored on '''/dev/rflash1'''
*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  ===
{| class="wikitable FCK__ShowTableBorders"
 
|-
The device supports 3 ioctl commands:
! Packet ID
 
! Description
*0 - 0x002AC10C (3.15)
|-
*1 - 0x002AC250 (3.15)
| 0x15001
*2 - EURUS_STAT 0x002AC320 (3.15)
| Get LPAR Parameter Size/Get LPAR Parameter
|-
| 0x15003
| Get Contents Size/Get Contents
|-
| 0x15009
| Get Component
|}


=== Methods ===
=== SPP File ===


net_control_cmd_GELIC_LV1_POST_WLAN_CMD - 0x0024A55C (3.15)
*The file is encrypted but can be read by using 0x15003 service of SPL
*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


net_control_wlan_cmd_GELIC_EURUS_CMD_ASSOC - 0x00246C78 (3.15)
Here are the contents of [[:DEFAULT.SPP]] from 3.41.


net_control_wlan_cmd_GELIC_EURUS_CMD_START_SCAN - 0x00248A14 (3.15)
Here are the contents of [[:DEFAULT.SPP 1.18 Debug]] from 1.18 Debug Firmware.  


net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WEP_CFG - 0x00249F24 (3.15)
==== SPP Header  ====


net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WPA_CFG - 0x002497B8 (3.15)  
offset 0x2 - header format version (2 bytes)  


= Event Notification  =
offset 0x4 - header size (4 bytes)


*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.
offset 0x18 - number of segments (4 bytes)
*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 ==
==== Segments ====


This is the base Outlet class. There are different types of Outlet and they derive from this base class.  
*Segments follow after the header
*SPP file contains several segments.


=== vtable  ===
Here is the list of profile segments from 3.41:


0x00357DC0 (3.15)
{| class="wikitable FCK__ShowTableBorders"
|-
! 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
|}


=== Member variables ===
== 0x15003 - Get Contents Size/Get Contents ==


offset 0x30 - type (8 bytes)
*This service provides the contents of a segment specified by a service requester
*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


offset 0x38 - pointer to LPAR that owns this Outlet object
== 0x17000 - Indi Info Manager  ==


offset 0x48 - outlet id (8 bytes)
{| class="wikitable FCK__ShowTableBorders"
|-
! Packet ID
! Description
|-
| 0x17001
| Read EID Data Size By Index/Read metldr Size
|-
| 0x17002
| Read EID Data By Index/Read metldr
|-
| 0x17004
| Read System Data
|-
| 0x17007
| Read System Data From EEPROM
|-
| 0x17013
| Read eEID Size
|-
| 0x17014
| Write eEID/Write metldr
|-
| 0x17015
| Read cISD Size
|-
| 0x17016
| Read cISD
|-
| 0x17017
| Write cISD
|}


offset 0x90 - VIRQ assigned to this Outlet object (4 bytes)
*Indi Info Manager is accessed e.g. in '''syscall 868''' on GameOS


== Event Receive Port class ==
=== 0x17001 - Read EID Data Size By Index ===


*This type of Outlet is created e.g. in '''lv1_construct_event_receive_port''' and in '''syscall 0x1001A'''.  
*I have got access to this service through DM and tested it
*HV calls '''lv1_connect_irq_plug''' and '''lv1_connect_irq_plug_ext''' assigns a VIRQ to Event Receive Port object.
*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
*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.


=== vtable  ===
{| class="wikitable FCK__ShowTableBorders"
 
|-
0x00357E88
! Index
 
! Size Of Data
== VUART Outlet  ==
! Description
 
|-
*HV supports only one VUART Outlet per LPAR
| 0
*'''lv1_configure_virtual_uart_irq''' constructs a VUART Outlet object and passes the address of LPAR's VUART IRQ Bitmap to HV
| 0x860
 
| Used e.g. by Update Manager to decrypt update packages
=== vtable  ===
|-
 
| 4
0x00357DC0
| 0x30
 
| Used e.g. by Storage Manager
=== VUART IRQ Bitmap  ===
|-
 
| 0x1000
*At address 0x38(LPAR ptr) + 0x158 is the VUART IRQ Bitmap owned by HV for LPAR (4 * 8 bytes = 256 bits)
| 0xe960
*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'''
| metldr
*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  =
 
*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 ==
=== 0x17002 - Read EID Data By Index ===


0x00357DF0 (3.15)
*I have got access to this service through DM and tested it
*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
*The 1st parameter is same as the 1st parameter of service '''Read EID Data Size By Index'''
*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'''.


== Member variables ==
=== 0x17004 - Read System Data ===


offset 0x90 - pointer to an object that contains VIRQ-Outlet mapping table for thread 0
*Reads data from '''cISD''' or '''cCSD''' files stored on '''/dev/rflash1'''.
*E.g. Gelic MAC address is stored in file '''cISD'''.


offset 0x98 - pointer to an object that contains VIRQ-Outlet mapping table for thread 1
=== 0x17007 - Read System Data From EEPROM  ===


== Objects  ==
*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 Logical PPE objects i found in HV 3.15.
Here is the list of possible EEPROM offsets from HV 3.15:


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Address in HV dump
! Index
! LPAR id
! SC EEPROM Offset
! PPE id
! Size Of Data
|-
| 0
| 0x48D20
| 6
|-
|-
| 0x0069C7F0
| 1  
| 1  
| 1
| 0x48D28
| 6
|-
|-
| 0x007A8900
| 2  
| 2  
| 1
| 0x48D30
| 6
|-
| 3
| 0x48D38
| 6
|-
| 4
| 0x48D00
| 4
|-
| 5
| 0x48D04
| 4
|-
| 6
| 0x48D08
| 4
|}
|}


== Virtual IRQ - Outlet Mapping ==
=== 0x17014 - Write eEID/Write metldr ===
 
*'''Holy crap, it writes passed data to the region of FLASH memory where eEID or metldr data is stored&nbsp;!!!'''
*'''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  ===
 
*Returns size of data '''cISD''' that is stored on '''FLASH storage device region 0'''
 
=== 0x17016 - Read cISD  ===
 
*Returns data '''cISD''' that is stored on '''FLASH storage device region 0'''
 
=== 0x17017 - Write cISD  ===


*HV maintains 2 tables per PPE that map a VIRQ to an Outlet object.
*'''Writes passed data to the region of FLASH memory where cISD data is stored&nbsp;!!!'''
*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 ===
== 0x18000 - DM (Dispatcher Manager) ==


0x0069C990 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 0''' (not empty)  
*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"
|-
|-
! VIRQ
! Packet ID
! Address of Outlet object in HV dump
! Description
! Description
|-
|-
| 58
| 0x18001
| 0x00090D10
| Construct Service Port
| -
|-
| 59
| 0x006BAC50
| -
|-
| 60
| 0x006B3ED0
| FLASH storage device / Storage device notification for LPAR 1
|-
| 61
| 0x00697E70
| VUART interrupts
|-
|-
| 62
| 0x18002
| 0x001C8F20
| Destruct Service Port
| -
|}
|}


=== LPAR 1 PPE 1 Thread 1 ===
=== Dispatcher Manager Messages ===
 
==== Dispatcher Manager Header  ====


0x0069D9B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 1''' (empty)
*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  ===


=== LPAR 2 PPE 1 Thread 0  ===
*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


0x000A06B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 0''' (not empty)
Here is the mapping table i extracted from HV Process 3 where SPM and DM run:


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! VIRQ
! Packet ID
! Address of Outlet object in HV dump
! SS ID
! Description
|-
| 0x2001
| 0x34
|-
|-
| 20
| 0x2002
| 0x003AA210
| 0x35
| -
|-
|-
| 21
| 0x2003
| 0x003AFEC0
| 0x36
| -
|-
|-
| 22
| 0x2004
| 0x001FC010
| 0x37
| -
|-
|-
| 23
| 0x2005
| 0x003A8E50
| 0x38
| -
|-
|-
| 24
| 0x2006
| 0x001FFED0
| 0x39
| SPE 0 Class 0 Interrupt
|-
|-
| 25
| 0x200A
| 0x003AE160
| 0x3D
| SPE 0 Class 1 Interrupt
|-
|-
| 26
| 0x200B
| 0x003AE350
| 0x3E
| SPE 0 Class 2 Interrupt
|-
|-
| 27
| 0x200C
| 0x003AB100
| 0x3F
| SPE 1 Class 0 Interrupt
|-
|-
| 28
| 0x200D
| 0x003AB2F0
| 0x40
| SPE 1 Class 1 Interrupt
|-
|-
| 29
| 0x200E
| 0x003AB4E0
| 0x41
| SPE 1 Class 2 Interrupt
|-
|-
| 30
| 0x2012
| 0x003AA6A0
| 0x7B
| SPE 2 Class 0 Interrupt
|-
|-
| 31
| 0x2013
| 0x003AA890
| 0x7C
| SPE 2 Class 1 Interrupt
|-
|-
| 32
| 0x2014
| 0x003AAA80
| 0x7E
| SPE 2 Class 2 Interrupt
|-
|-
| 33
| 0x2015
| 0x003B44A0
| 0x7F
| SPE 3 Class 0 Interrupt
|-
|-
| 34
| 0x2016
| 0x003B4690
| 0x7D
| SPE 3 Class 1 Interrupt
|-
|-
| 35
| 0x2017
| 0x003B4AD0
| 0x80
| SPE 3 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"
|-
|-
| 36
! Packet ID
| 0x003B5300
! Description
| SPE 4 Class 0 Interrupt
|-
|-
| 37
| 0x19002
| 0x003B54F0
| Get Device Type
| SPE 4 Class 1 Interrupt
|-
|-
| 38
| 0x19003
| 0x003B56E0
| Get Device ID
| SPE 4 Class 2 Interrupt
|-
|-
| 39
| 0x19004
| 0x003AE7C0
| Get PS Code
| 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
| 0x19005
| 0x003B8350
| Get Open PS ID
| -
|}
|}


=== LPAR 2 PPE 1 Thread 1  ===
=== 0x19002 - Get Device Type ===


0x007A89E0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 1''' (not empty)
On my fat PS3 with HV 3.41 it returns:
<pre>
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x85
</pre>


{| class="wikitable FCK__ShowTableBorders"
<pre>
|-
struct ss_aim_get_device_type
! VIRQ
{
! Address of Outlet object in HV dump
    u8 field0[16];
! Description
};
|-
</pre>
| 16  
| 0x003B2480
| -
|-
| 17
| 0x003B2590
| -
|-
| 18
| 0x003B26A0
| -
|-
| 19
| 0x003B27B0
| -
|}


== IRQ State Bitmap  ==
=== 0x19003 - Get Device ID ===


*There is one IRQ State Bitmap (256 bits = 32 bytes) per thread of Logical PPE
<pre>
*'''HSPRG0 value is per thread''', so there are 2 HSPRG0 values in HV dump&nbsp;!!!
struct ss_aim_get_device_id
*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
    u8 field0[16];
*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'''
</pre>
*'''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
=== 0x19004 - Get PS Code ===


0x8948FC0 - physical address of LPAR's IRQ State Bitmap for Thread 1 of LINUX LPAR
<pre>
struct ss_aim_get_ps_code
{
    u8 field0[8];
};
</pre>


= System Controller (SC or SYSCON)  =
=== 0x19005 - Get Open PS ID ===


*Data received from SC is sent to a VUART
<pre>
*'''lv1_get_rtc''' and '''syscall 0x10036''' communicate with '''SC VUART 4'''.
struct ss_aim_get_open_ps_id
 
{
=== VUART Table  ===
    u8 field0[16];
 
};
*Address of SC VUART Table - 0x00610410 (3.15).
</pre>
*There are 5 VUARTs for SC in HV 3.15


Here is the SC VUART table from HV 3.15:
== 0x24000 - USB Dongle Authenticator  ==


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Index
! Packet ID
! Address of VUART object in HV dump
! Description
! Description
|-
|-
| 0
| 0x24001
| 0x0060FD20
| Generate Challenge
| This VUART is connected with the '''VUART 0 (/dev/sc0)''' of LPAR 1
|-
|-
| 1
| 0x24002
| 0x0060FE20
| Verify Response
| This VUART is connected with the '''VUART 1 (/dev/sc1)''' of LPAR 1
|}
 
=== 0x24001 - Generate Challenge  ===
 
*I have got access to this service through DM and tested it
*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:
<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"
|-
|-
| 2
! Packet ID
| 0x0060FF20
! Description
| 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
| 0x25001
| 0x006124E0
| Encrypt User Token
| This VUART is connected with the '''VUART 3 (/dev/sc3)''' of LPAR 1
|-
|-
| 4
| 0x25002
| 0x00612DF0
| Decrypt User Token
| '''lv1_get_rtc''' and '''syscall 0x10036''' communicate with this VUART.
|}
|}


== Interrupt Handling ==
=== User Token ===


spider_sc_interrupt_handler - 0x0020A68C (3.15)
*Before User Token Manager encrypts a received user token it checks it's format.
*User Tokens are processed by '''spu_utoken_processor.self'''
*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.


== Methods ==
==== User Token Format ====
<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 */
}


sc_vuart_4_get_peer_vuart - 0x002ED384 (3.15)
struct user_token
{
    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  =


sc_send - 0x0020A908 (3.15)
== Memory Region class  ==


sc_receive - 0x0020A354 (3.15)
This class is the base class for different memory region types.  


sc_vuart_rx_trigger_callback - 0x002ED470 (3.15)
=== vtable  ===


== lv1_get_rtc  ==
0x003578B0 (3.15)


*'''lv1_get_rtc''' communicates with SC VUART 4.
=== Member variables  ===
*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 ==
offset 0x40 - pointer to LPAR object that owns this memory region


* I was able to enable SYSCON Manager debug messages in HV Process 5
offset 0x48 - type of memory region (8 bytes)
* 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 ===
offset 0x50 - LPAR start address of memory region


* Packet header is of size '''0x10''' bytes.
offset 0x58 - size of memory region (8 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.


<pre>
offset 0x60 - flags (8 bytes)  
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 ====
offset 0xA0 - log2 of page size


<pre>
=== Generating New LPAR Memory Region Addresses ===
/* calculating SC packet header checksum */


/*
generate_new_lpar_mem_region_address(?, memory region size, log2(page size), ?, ?) - 002C82E8 (3.15)
* sc_hdr_cksum
*/
uint16_t sc_hdr_cksum(struct sc_hdr *sc_hdr)
{
    uint8_t *ptr;
    uint32_t sum;


    ptr = (uint8_t *) sc_hdr;
generate_new_lpar_mem_region_address - 002C6570 (3.41)
    sum = 0;


    for (i = 0; i < 6; i++)
*The function returns a new LPAR memory region address.
        sum += *ptr++;
*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'''.


    sum += 0x8000;
==== Encoding LPAR Memory Region Start Addresses and Sizes ====


    return sum & 0xffff;
*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'''.


struct sc_hdr sc_hdr;
LPAR Memory Region Start Address >> 42 = log2(LPAR Memory Region Size)


memset(&sc_hdr, 0, sizeof(sc_hdr));
LPAR Memory Region Start Address = (log2(LPAR Memory Region Size) << 42) |
    (counter << log2(LPAR Memory Region Size))


sc_hdr.cksum = sc_hdr_cksum(sc_hdr);
===== LPAR Memory Region Address Counter =====


/* fill sc header here */
*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


sc_hdr.cksum = sc_hdr_cksum(sc_hdr);
== Physical Memory Region class  ==
</pre>


=== Packet Body ===
This type of memory region is created e.g. in '''lv1_allocate_memory''' HV call or in '''syscall 0x10000'''.


* Packet body follows packet header
=== vtable  ===
* Packet body size is stored at offset '''0xC''' and '''0xE''' in packet header and is of size 2 bytes


=== Reading SYSCON EPROM (NVS Service) ===
0x00357D08 (3.15)  


Here is a command which is sent to SYSCON to read 1 byte of EPROM at offset 0x48C07 (Product Mode):
=== Member variables  ===
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:
offset 0xB0 - pointer to object that stores a list of addresses of physical pages owned by this memory region
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 ===
offset 0xB8 - pointer to LPAR object that owns this memory region


* '''Used by PS2EMU System Manager in HV process 9 when PS2 EMU is booted'''
offset 0xC0 - reference counter (8 bytes)


==== PCI Bus Power On ====
=== Objects  ===


'''Request to SC1:'''
Here is the list of physical memory region objects i found in HV 3.15.
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x01


==== PCI Bus Power Off ====
{| class="wikitable FCK__ShowTableBorders"
 
|-
'''Request to SC1:'''
! Address in HV dump
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x00
! LPAR id
 
! LPAR Start Address
=== Ring Buzzer ===
! Size
 
! Flags
'''Request:'''
! log2(Page Size)
0x16 0x01 0x00 0x00 0x00 0x00 0x80 0x17 0x00 0x00 0x00 0x00 0x00 0x08 0x00 0x08 0x20 0x00 0x00 0x00 0x00 0x00 0x00 0x00
! Physical Page Addresses
 
|-
=SYSCON=
| 0x006B5510
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#SYSCON gitbrew.org::SYSCON] <br />
| 1
 
| 0x300000001000
SYSCON MMIO registers can be accessed on Linux with a driver using lv1_undocumented_function_114, e.g. '''ps3sbmmio'''.
| 0x1000
Use ps3sbmmio device driver carefully, an access at some addresses could shutdown your PS3.
| 0x0
 
| 0xC
==Packet Header==
| 0x672000
 
|-
* Size is '''0x10'''.
| 0x006B5E50
 
| 1
<pre>
| 0x440000040000
struct sc_hdr {
| 0x20000
    uint8_t service_id;
| 0x0
    uint8_t version;              /* must be 1 !!! */
| 0x11  
    uint16_t transaction_id;      /* returned in response */
| 0x6C0000
    uint8_t res[2];
|-
    uint16_t cksum;              /* checksum of first 6 header bytes */
| 0x006B6980
    uint32_t communication_tag;  /* SYSCON tag: 0-4 */
| 1
    uint16_t payload_size[2];    /* body size */
| 0x440000060000
};
| 0x20000
</pre>
| 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  ===


==Sending Packets==
*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.


* Before sending new packet to SYSCON, the Hypervisor checks 2 words at offsets 0x2400008DFF0 and 0x2400008CFF4.
Here is the list of physical memory regions of GameOS i found in HV 3.41:
* 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++)
{| class="wikitable FCK__ShowTableBorders"
    cksum -= packet[i];
|-
! 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
|}


cksum = cksum & 0xffff;
== HTAB Memory Region class  ==
</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==
This memory region is created when a HTAB is mapped into LPAR's address space. It's created in '''lv1_map_htab''' HV call.


* The Hypervisor installs an interrupt handler for the SYSCON.
=== vtable  ===
* 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==
0x00357C98 (3.15)


'''1. Before sending SYSCON packet''':
=== Member variables  ===
<pre>
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C


00000000  01 18 01 18                                      |....|
offset 0xB0 - pointer to VAS object that owns the HTAB
00000004


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


00000000  01 18 01 18                                      |....|
Here is the list of HTAB memory region objects i found in HV 3.15.  
00000004


root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C
{| class="wikitable FCK__ShowTableBorders"
|-
! 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
|}


00000000 01 24 01 24                                      |.$.$|
=== GameOS HTAB ===
00000004


root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C
*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


00000000  01 24 01 24                                      |.$.$|
=== GameOS SLB ===
00000004
</pre>
 
'''2. SYSCON packet was sent by using ps3dm_scm read_eprom.'''
 
'''3. After sending SYSCON packet''':
<pre>
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
</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


Here is the dump of SLB entries from GameOS 3.41:
<pre>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
</pre>
</pre>
== SPE MMIO Memory Region class  ==


'''5. Received Body'''
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'''.


<pre>
=== vtable  ===
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                          |..Ç.ÿ...|
0x003583F8 (3.15)
00000008
</pre>


==Examples==
=== Member variables  ===


===Get RTC===
=== Objects  ===


* Used by LV1 call '''lv1_get_rtc'''
Here is the list of SPE memory region objects i found in HV 3.15.
* Communication with SYSCON 4


Request:
{| class="wikitable FCK__ShowTableBorders"
<pre>
|-
# write packet
! Address in HV dump
 
! LPAR id
# echo "0: 13 01 0000 0000 8014 00000004 0001 0001 33 00 00 00 0000ff1f" | xxd -c256 -r | \
! SPE
      dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer
! LPAR Start Address
 
! Size
# dump packet counter
! 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
|}


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


00000000  00 c0 00 c0                                      |.À.À|
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'''.  
00000004


# increment packet counter
=== vtable  ===


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


# kick packet
=== Objects  ===


# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer
Here is the list of SPE Shadow Registers memory region objects i found in HV 3.15.


</pre>
{| class="wikitable FCK__ShowTableBorders"
 
|-
Response:
! Address in HV dump
 
! LPAR id
<pre>
! SPE
# dump packet counter
! LPAR Start Address
 
! Size
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
! Physical Address
 
! Flags
00000000  00 c1 00 c1                                      |.Á.Á|
! log2(Page Size)
00000004
|-
 
| 0x003ABDA0
# dump response packet
| 2
 
| 1
# dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C
| 0x300000012000
 
| 0x1000
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|
| 0xA000000000000000
00000018
| 0xC
</pre>
|-
 
| 0x003B4290
===Ring Buzzer===
| 2
 
| 2
* Used by System Manager
| 0x300000014000
* Communication with SYSCON 1
| 0x1000
 
| -
Request:
| 0xA000000000000000
 
| 0xC
<pre>
|-
# write packet
| 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
|}


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


# dump packet counter
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'''.


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


00000000  00 c0 00 c0                                      |.À.À|
0x00352468 (3.15)
00000004


# 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 where the device MMIO region is mapped to


# kick packet
=== Objects  ===


# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer
Here is the list of Device MMIO memory region objects i found in HV 3.15.


# you should hear a beep
{| class="wikitable FCK__ShowTableBorders"
 
|-
</pre>
! Address in HV dump
 
! LPAR id
Response:
! 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
|}


<pre>
== GPU Device Memory Region class  ==
# dump packet counter


# 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                                      |.Á.Á|
=== vtable ===
00000004


# dump response packet
0x00357C48 (3.15)


# dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C
=== Member variables  ===
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>
offset 0xA8 - physical address


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


==Running Isolated SPE Modules On OtherOS++ Linux==
Here is the list of Device GPU memory region objects i found in HV 3.15.


* spp_verifier is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux.
{| class="wikitable FCK__ShowTableBorders"
* It decrypts default.spp profile
|-
* Tested on 3.41 and 3.55.
! Address in HV dump
* You can modify it easily to run other SPE modules.
! 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
|}


<pre>
== Direct Map Memory Region class ==
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)
This type of memory region is created in HV call '''lv1_undocumented_function_114'''.
lv1_construct_logical_spe (0x00000000)
'''lv1_undocumented_function_114''' allows you to map any memory address into LPAR's memory address.
SPE id (0x000000000000002b)
 
lv1_undocumented_function_209 (0x00000000)
* The HV call '''lv1_undocumented_function_115''' destroys a memory region of this type.
shadow execution status (0x0000000000000002)
* HV allows GameOS to create objects of this type of size 0 only !!! But it can be exploited with a dangling HTAB entry.
lv1_get_spe_interrupt_status(1) (0x00000000)
 
interrupt status 1 (0x0000000000000000)
=== vtable  ===
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
0x00357C48 (3.15)
...
...
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==
=== Member variables  ===


* spp_verifier_direct is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux by using metldr directly.
offset 0xA8 - physical address
* It decrypts default.spp profile.
* Tested on 3.41 and 3.55.
* You can modify it easily to run other SPE modules.


<pre>
=== Exploiting HV with memory glitching and HV call lv1_undocumented_function_114 ===
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
Here is a short description of the method i used to exploit HV from GameOS 3.15 and 3.41.
...
...
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 =
* First i used the Geohot's method to create a dangling HTAB entry.
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#Gelic_Device gitbrew.org::Gelic Device] <br />
* 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  ==


==sys.hw.config==
LPAR_get_memory_region_by_start_address - 0x002C7C40 (3.15)


* Value of the loader parameter "sys.hw.config" controls if Gelic WLAN is enabled or not.
LPAR_get_memory_region_by_address - 0x002C7DA8 (3.15)
* 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:[http://www.ps3devwiki.com/index.php?title=Wifi old vs. new]: Old == CECHA up to CECHK, New == CECHL and later''
LPAR_mem_addr_to_phys_addr(LPAR id, LPAR address, phys_addr) - 0x002FB8F0 (3.15)


== Control Interface ==
LPAR_construct_direct_mapping_mem_region - 0x002D4D04 (3.15)


HV calls 195 and 196 are used by GameOS to send commands to Gelic device directly.
= Network Devices  =


=== lv1_undocumented_function_196 ===
== Ethernet Gelic Device  ==


==== Parameters ====
device id = 0


r3 - LPAR address of data buffer
MAC Address: 00:1F:A7:C6:2A:C5


r4 - size of data buffer
device memory base address = 0x24003004000 (size = 0x1000)


r5 - must be 0
== WLAN Gelic Device  ==


=== lv1_undocumented_function_195 ===
device id = 0


==== Parameters ====
MAC Address: 02:1F:A7:C6:2A:C5 (locally administered)


r3 - command (16 bit value)
=== Net Manager  ===


r4 - command data size
*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


r5 - must be 0
=== /dev/net0  ===


=== Data Buffer ===
The device supports 3 ioctl commands:


* Data Buffer passed to HV call 196 is divided into 2 parts.
*0 - 0x002AC10C (3.15)
* The first 0x800 bytes are for sending and receiving command data
*1 - 0x002AC250 (3.15)
* The remaining 0x800 bytes are for event notification.
*2 - EURUS_STAT 0x002AC320 (3.15)


=== Command Data Buffer ===
=== Methods  ===


* Every command data sent to Gelic device contains header of size '''0xC'''
net_control_cmd_GELIC_LV1_POST_WLAN_CMD - 0x0024A55C (3.15)
* 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 ====
net_control_wlan_cmd_GELIC_EURUS_CMD_ASSOC - 0x00246C78 (3.15)


* Size is '''0xc'''.
net_control_wlan_cmd_GELIC_EURUS_CMD_START_SCAN - 0x00248A14 (3.15)
* Byte order is little-endian.
* Header data in a request command buffer is always all 0s.


0x0 - command = request command + 1 (2 bytes)
net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WEP_CFG - 0x00249F24 (3.15)  


0x4 - result, 0x1 - success ??? 0x2 - buffer too small ??? (2 bytes)
net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WPA_CFG - 0x002497B8 (3.15)  


0x6 - body size (2 bytes)
= Event Notification  =


=== Event Data Buffer ===
*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.


* The Gelic device notifies LV2 kernel by sending an interrupt when new events are available
== Outlet class  ==
* 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 ====
This is the base Outlet class. There are different types of Outlet and they derive from this base class.


offset 0x0 - GET index (4 bytes)
=== vtable  ===


offset 0x4 - PUT index (4 bytes)
0x00357DC0 (3.15)  


* GET index is updated by Gelic driver. The Gelic driver reads events beginning with the event slot at index GET.
=== Member variables  ===
* 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 ===
offset 0x30 - type (8 bytes)


* LV2 syscall 726 sends Gelic device command and blocks until a response from the Gelic device arrives
offset 0x38 - pointer to LPAR that owns this Outlet object
* 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 ====
offset 0x48 - outlet id (8 bytes)


r3 - command (16 bits)
offset 0x90 - VIRQ assigned to this Outlet object (4 bytes)  


r4 - effective address of command data buffer
== Event Receive Port class  ==


r5 - size of command data buffer
*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.


=== Commands ===
=== vtable  ===


====Unknown (0x1)====
0x00357E88


* Used by VSH.
== VUART Outlet  ==
* Command buffer size is '''0x10'''.
* Used in AP mode.
* Enables AP mode ???


====Get AP SSID (0x3)====
*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


* Command buffer is of size '''0x30'''.
=== vtable  ===
* Returns SSID in AP mode.


offset 0xC - SSID (32 bytes)
0x00357DC0


====Set AP SSID (0x5)====
=== VUART IRQ Bitmap  ===


* Used by VSH.
*At address 0x38(LPAR ptr) + 0x158 is the VUART IRQ Bitmap owned by HV for LPAR (4 * 8 bytes = 256 bits)
* Command buffer is of size '''0x30'''.
*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'''
* Sets SSID in AP mode.
*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'''.


offset 0xC - SSID (32 bytes)
= Logical PPE  =


====Get Channel (0xf)====
*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


* Used by VSH.
== vtable  ==
* 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)
0x00357DF0 (3.15)  


====Set Channel (0x11)====
== Member variables  ==


* Used by VSH.
offset 0x90 - pointer to an object that contains VIRQ-Outlet mapping table for thread 0  
* Command buffer size is '''0xd'''
* Valid channels: '''0 - 13'''. '''0''' means that the channel is selected '''automatically'''.


offset 0xC - channel (1 byte)
offset 0x98 - pointer to an object that contains VIRQ-Outlet mapping table for thread 1  


====Unknown (0x27)====
== Objects  ==


* Command buffer size is '''0xF'''.
Here is the list of Logical PPE objects i found in HV 3.15.  


====Set Antenna (0x29)====
{| class="wikitable FCK__ShowTableBorders"
|-
! Address in HV dump
! LPAR id
! PPE id
|-
| 0x0069C7F0
| 1
| 1
|-
| 0x007A8900
| 2
| 1
|}


* Command buffer size is '''0xe'''
== Virtual IRQ - Outlet Mapping  ==


offset 0xC - 0,1 or 2 (1 byte)
*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.


offset 0xD - 2 (1 byte)
=== LPAR 1 PPE 1 Thread 0  ===


====Set AP WEP Configuration (0x5b)====
0x0069C990 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 0''' (not empty)  


* Used by VSH.
{| class="wikitable FCK__ShowTableBorders"
* Command buffer is of size '''0x56'''.
|-
* Sets WEP security type and WEP key.
! VIRQ
* Security types: 0 - none, 1 - wep64, 2 - wep128
! 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
| -
|}


offset 0xE - security mode: 0 - none, 1 - wep64, 2 - wep128 (1 byte)
=== LPAR 1 PPE 1 Thread 1  ===


offset 0x10 - WEP key (64 bytes)
0x0069D9B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 1''' (empty)  


====Unknown (0x61)====
=== LPAR 2 PPE 1 Thread 0  ===


* Used by VSH.
0x000A06B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 0''' (not empty)
* Command buffer size is '''0xd'''


====Unknown (0x65)====
{| class="wikitable FCK__ShowTableBorders"
 
|-
* Used by VSH.
! VIRQ
* Command  uffer size is '''0xd'''.
! Address of Outlet object in HV dump
* Used in AP mode.
! Description
 
|-
====Get Eurus Firmware Version (0x99)====
| 20
 
| 0x003AA210
* Used by VSH.
| -
 
|-
Here is the response on my PS3 Slim:
| 21
<pre>
| 0x003AFEC0
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).|
| 22
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00      |..............  |
| 0x001FC010
</pre>
| -
 
|-
====Get AP Operating Mode (0xb7)====
| 23
 
| 0x003A8E50
* Used by VSH.
| -
* Command buffer size is '''0x10'''
|-
* Returns AP operating mode (mixed, 11b or 11g).
| 24
 
| 0x001FFED0
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)
| SPE 0 Class 0 Interrupt
 
|-
====Set AP Operating Mode (0xb9)====
| 25
 
| 0x003AE160
* Used by VSH.
| SPE 0 Class 1 Interrupt
* Command buffer size is '''0x10'''
|-
* Sets AP operating mode (mixed, 11b or 11g).
| 26
 
| 0x003AE350
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)
| SPE 0 Class 2 Interrupt
 
|-
====Unknown (0xc5)====
| 27
 
| 0x003AB100
* Used by VSH.
| SPE 1 Class 0 Interrupt
* Command buffer size is '''0x10'''.
|-
* Used in AP mode.
| 28
 
| 0x003AB2F0
offset 0xC - ??? (4 bytes)
| SPE 1 Class 1 Interrupt
 
|-
====Set AP WPA AKM Suite (0xc9)====
| 29
 
| 0x003AB4E0
* Used by VSH.
| SPE 1 Class 2 Interrupt
* Command buffer size is '''0x11'''.
|-
* Sets WPA AKM suite in AP mode.
| 30
 
| 0x003AA6A0
offset 0xC - AKM suite (4 bytes)
| SPE 2 Class 0 Interrupt
 
|-
====Set AP WPA Group Cipher Suite (0xcf)====
| 31
 
| 0x003AA890
* Used by VSH.
| SPE 2 Class 1 Interrupt
* Command buffer size is '''0x10'''
|-
* Used in AP + WPA mode.
| 32
 
| 0x003AAA80
offset 0xC - group cipher suite: group (4 bytes)
| SPE 2 Class 2 Interrupt
 
|-
====Set AP WPA PSK Binary (0xd3)====
| 33
 
| 0x003B44A0
* Used by VSH.
| SPE 3 Class 0 Interrupt
* Command buffer size is '''0x4c'''
|-
* Sets WPA PSK binary
| 34
 
| 0x003B4690
offset 0xC - PSK (64 bytes)
| SPE 3 Class 1 Interrupt
 
|-
====Set AP WPA Reauthentication Timeout (0xd5)====
| 35
 
| 0x003B4AD0
* Used by VSH.
| SPE 3 Class 2 Interrupt
* Command buffer size is '''0x10'''
|-
* Sets WPA Reauth timeout value in AP WPA mode.
| 36
* VSH uses 36000 as timeout.
| 0x003B5300
 
| SPE 4 Class 0 Interrupt
offset 0xC - timeout value in seconds (2 bytes)
|-
 
| 37
====Unknown (0x127)====
| 0x003B54F0
 
| SPE 4 Class 1 Interrupt
* Used by VSH.
|-
* Command buffer size is '''0x10'''.
| 38
* Used in AP + WPA mode.
| 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
| -
|}


====Unknown (0x12b)====
=== LPAR 2 PPE 1 Thread 1  ===


* Used by VSH.
0x007A89E0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 1''' (not empty)
* Command buffer size is '''0x10'''.
* Used in AP + WPA mode.


====Set AP WPA PSK Passphrase (0x17d)====
{| class="wikitable FCK__ShowTableBorders"
|-
! VIRQ
! Address of Outlet object in HV dump
! Description
|-
| 16
| 0x003B2480
| -
|-
| 17
| 0x003B2590
| -
|-
| 18
| 0x003B26A0
| -
|-
| 19
| 0x003B27B0
| -
|}


* Used by VSH.
== IRQ State Bitmap  ==
* Command buffer size is '''0x2D'''


offset 0xD - passphrase (32 bytes)
*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&nbsp;!!!
*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.


====Set AP WPA Pairwise Cipher Suite (0x1bf)====
0x8941FC0 - physical address of LPAR's IRQ State Bitmap for Thread 0 of LINUX LPAR


* Used by VSH.
0x8948FC0 - physical address of LPAR's IRQ State Bitmap for Thread 1 of LINUX LPAR
* Command buffer size is '''0x11'''
* Used in AP + WPA mode.


offset 0xC - pairwise cipher suite (4 bytes)
= System Controller (SC or SYSCON) =


offset 0x10 - ??? (1 byte)
*Data received from SC is sent to a VUART
*'''lv1_get_rtc''' and '''syscall 0x10036''' communicate with '''SC VUART 4'''.


====Unknown (0x1d9)====
=== VUART Table  ===


* Used by VSH.
*Address of SC VUART Table - 0x00610410 (3.15).  
* Command buffer size is '''0x10'''
*There are 5 VUARTs for SC in HV 3.15


====Unknown (0x1dd)====
Here is the SC VUART table from HV 3.15:


* Used by VSH.
{| class="wikitable FCK__ShowTableBorders"
* Command buffer size is '''0xd'''
|-
! 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.
|}


====Unknown (0x1ed)====
== Interrupt Handling  ==


* Used by VSH.
spider_sc_interrupt_handler - 0x0020A68C (3.15)
* Command buffer is of size '''0x17'''.
* Rate control ???


====Get Eurus HW Revision (0x1fb)====
== Methods  ==


* Command buffer size is '''0x10'''.
sc_vuart_4_get_peer_vuart - 0x002ED384 (3.15)


====Associate (0x1001)====
sc_send - 0x0020A908 (3.15)


* Used by VSH.
sc_receive - 0x0020A354 (3.15)
* Used by LV1 on FAT models.
* Command buffer size is '''0xd'''
* Data passed to Gelic device is all 0s


====Get Common Configuration (0x1003)====
sc_vuart_rx_trigger_callback - 0x002ED470 (3.15)


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


====Set Common Configuration (0x1005)====
*'''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.


* Used by VSH.
== SYSCON Protocol ==
* 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)
* 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.


offset 0xD - authentication mode: 0 - open, 1 - shared key
=== Packet Header ===


offset 0xE - opmode: 0 - 11bg, 1 - 11b, 2 - 11g (1 byte)
* 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.


offset 0xF - ??? (1 byte)
<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>


offset 0x10 - BSSID (6 bytes)
==== Calculating Packet Header Checksum ====


offset 0x16 - capability (2 bytes)
<pre>
/* calculating SC packet header checksum */


====Get WEP Configuration (0x1013)====
/*
* sc_hdr_cksum
*/
uint16_t sc_hdr_cksum(struct sc_hdr *sc_hdr)
{
    uint8_t *ptr;
    uint32_t sum;


* Used by VSH.
    ptr = (uint8_t *) sc_hdr;
* Used by LV1 on FAT models.
    sum = 0;
* Command buffer size is '''0x50'''
* Data passed to Gelic device is all 0s


====Set WEP Configuration (0x1015)====
    for (i = 0; i < 6; i++)
        sum += *ptr++;


* Used by VSH.
    sum += 0x8000;
* Used by LV1 on FAT models.
* Command buffer size is '''0x50'''


====Get WPA Configuration (0x1017)====
    return sum & 0xffff;
}


* Used by VSH.
struct sc_hdr sc_hdr;
* Used by LV1 on FAT models.
* Command buffer size is '''0x5b'''
* Data passed to Gelic device is all 0s


====Set WPA Configuration (0x1019)====
memset(&sc_hdr, 0, sizeof(sc_hdr));


* Used by VSH.
sc_hdr.cksum = sc_hdr_cksum(sc_hdr);
* Used by LV1 on FAT models.
* Command buffer size is '''0x5b'''


offset 0xE - security type: 0 - WPA, 1 - RSNA (1 byte)
/* fill sc header here */


offset 0xF - psk type: 0 - hex, 1 - bin (1 byte)
sc_hdr.cksum = sc_hdr_cksum(sc_hdr);
</pre>


offset 0x10 - psk key: hex or bin (64 bytes)
=== Packet Body ===


offset 0x50 - group cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
* Packet body follows packet header
* Packet body size is stored at offset '''0xC''' and '''0xE''' in packet header and is of size 2 bytes


offset 0x54 - pairwise cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
=== Reading SYSCON EPROM (NVS Service) ===


offset 0x58 - AKM suite: 0x0050f202 - WPA PSK, 0x000fac02 - RSNA PSK (4 bytes)
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


'''See IEEE 802.11 specification for more details about cipher/AKM suites
And here is the response to the above request:
'''
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


802.11 spec: [http://standards.ieee.org/getieee802/download/802.11-2007.pdf]
=== PCI Bus Power ===


====Unknown (0x1025)====
* '''Used by PS2EMU System Manager in HV process 9 when PS2 EMU is booted'''


* Used by VSH.
==== PCI Bus Power On ====
* Command buffer size is '''0x10'''.
* Sets preamble type, something else ???


offset 0xC - preamble mode: 0 - short, 1 - long (1 byte)
'''Request to SC1:'''
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x01


====Unknown (0x1031)====
==== PCI Bus Power Off ====


* Used by VSH.
'''Request to SC1:'''
* Command buffer size is '''0xe'''
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x00


====Get Scan Results (0x1033)====
=== Ring Buzzer ===


* Used by VSH.
'''Request:'''
* Used by LV1 on FAT models.
0x16 0x01 0x00 0x00 0x00 0x00 0x80 0x17 0x00 0x00 0x00 0x00 0x00 0x08 0x00 0x08 0x20 0x00 0x00 0x00 0x00 0x00 0x00 0x00
* Command buffer size is '''0x5b0'''
* Data passed to Gelic device is all 0s


=====Scan Results=====
=SYSCON=
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#SYSCON gitbrew.org::SYSCON] <br />


offset 0x0 - number of scan entries (1 byte)
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.


offset 0x1 - array of scan entries
==Packet Header==


======Scan Entry======
* Size is '''0x10'''.


offset 0x0 - size of this entry in bytes, this field is NOT included (2 bytes)
<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 index;              /* SYSCON index: 0-4 */
    uint16_t payload_size[2];    /* body size */
};
</pre>


offset 0x2 - BSSID (6 bytes)
==Sending Packets==


offset 0x8 - RSSI (1 byte)
* 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;


offset 0x9 - timestamp (8 bytes)
for (i = 0; i < packet_size; i++)
    cksum -= packet[i];


offset 0x11 - beacon period (2 bytes)
cksum = cksum & 0xffff;
</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.


offset 0x13 - capability (2 bytes)
==Receiving Packets==


offset 0x15 - information elements (see 802.11 specification)
* 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 :)


====Start Scan (0x1035)====
==Test==


* Used by VSH.
'''1. Before sending SYSCON packet''':
* Used by LV1 on FAT models.
<pre>
* Command buffer size depends on size of channel list and ESSID string length
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C
* 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)====
00000000  01 18 01 18                                      |....|
00000004


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


====Get RSSI (0x103d)====
00000000  01 18 01 18                                      |....|
00000004


* Used by VSH.
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C
* Used by LV1 on FAT models.
* Command buffer size is '''0x17'''


offset 0x10 - MAC address of node (6 bytes)
00000000  01 24 01 24                                      |.$.$|
00000004


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


====Get MAC Address (0x103f)====
00000000  01 24 01 24                                      |.$.$|
00000004
</pre>


* Command buffer size is '''0x13'''
'''2. SYSCON packet was sent by using ps3dm_scm read_eprom.'''


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


====Set MAC Address (0x1041)====
00000000  01 19 01 19                                      |....|
00000004


* Used by VSH.
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
* Used by LV1 too.
* Command buffer size is '''0x12'''


====Unknown (0x104d)====
00000000  01 19 01 19                                      |....|
00000004


* Used by VSH.
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C
* Command buffer size is '''0xd'''.


offset 0xC - 0 - ???, 1 - ??? (1 byte)
00000000  01 25 01 25                                      |.%.%|
00000004


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


* Command buffer size is '''0xd'''.
00000000  01 25 01 25                                      |.%.%|
* Returns 1 byte.
00000004
</pre>


offset 0xC - 0 - ???, 1 - ??? (1 byte)
'''4. Received Header'''


====Unknown (0x1051)====
<pre>
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=16 skip=$((0x8c000)) status=noxfer | hexdump -C


* Used by VSH.
00000000  14 01 00 00 00 00 80 15  00 00 00 03 00 05 00 05  |................|
* Command buffer size is '''0x5b3'''.
00000010
* Returns '''0x5a7''' bytes.


offset 0xC - number of entries
</pre>


offset 0x10 - entries (each entry is 0xd bytes)
'''5. Received Body'''


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


* Used by VSH.
00000000  00 00 c7 01 ff 00 00 00                          |..Ç.ÿ...|
* Command buffer size is '''0x70'''.
00000008
</pre>


offset 0xC - ??? (4 bytes)
==Examples==


offset 0x10 - MAC address (6 bytes)
===Get RTC===


====Unknown (0x1059)====
* Used by LV1 call '''lv1_get_rtc'''
* Communication with SYSCON 4


* Used by VSH.
Request:
* Command buffer size is '''0x2a8'''.
<pre>
# write packet


====Unknown (0x105f)====
# 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


* Used by LV2.
# dump packet counter


====Get Zephyr HW Revision (0x1101)====
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C


* Used by VSH.
00000000  00 c0 00 c0                                      |.À.À|
* Not a Gelic device command, handled by LV2 kernel.
00000004
* LV2 uses LV1 call '''lv1_net_control(0x8000000000000002)'''
* Command buffer size is '''0x18'''.


====Get MAC Address List (0x1117)====
# increment packet counter


* Command buffer size is '''0xce'''.
echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer
* Returns several MAC addresses.


offset 0xC - number of MAC addresses (2 bytes)
# kick packet


offset 0xE - MAC addresses (6 * number of MAC addresses)
# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer


====Unknown (0x1133)====
</pre>


* Used by VSH.
Response:
* Command buffer size is '''0x1A'''.


====Set WOL MAC Address Filter (0x1139)====
<pre>
# dump packet counter


* Used by LV2 internally.
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
* Command buffer is of size '''0x28'''.


====Unknown (0x113b)====
00000000  00 c1 00 c1                                      |.Á.Á|
00000004


* Used by LV2 internally.
# dump response packet
* Command buffer size is '''0x20'''.


====Set WOL Multicast Address Filter (0x113d)====
# dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C


* Used by LV2 internally.
00000000  13 01 00 00 00 00 80 14  00 00 00 04 00 08 00 08  |................|
* Command buffer is of size '''0x2c'''.
00000010  00 00 00 00 15 af 47 6b                          |.....¯Gk|
00000018
</pre>


====Clear WOL Multicast Address Filter (0x113f)====
===Ring Buzzer===


* Used by LV2 internally.
* Used by System Manager
* Command buffer is of size '''0x28'''.
* Communication with SYSCON 1


====Unknown (0x1141)====
Request:


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


====Clear WOL Address Filter (0x1143)====
# 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


* Used by LV2 internally.
# dump packet counter
* Command buffer size is '''0x2c'''.


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


* Used by LV2 internally.
00000000  00 c0 00 c0                                      |.À.À|
00000004


====Set WOL Magic Packet Mode (0x1155)====
# increment packet counter


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


offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
# kick packet


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


* Used by LV2 internally.
# you should hear a beep
* Command buffer size is '''0x10'''.


====Set WOL Multicast Address Filter Mode (0x1159)====
</pre>


* Used by LV2 internally.
Response:
* Command buffer size is '''0x10'''.
* WOL function


offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
<pre>
# dump packet counter


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


* Used by LV2 internally.
00000000  00 c1 00 c1                                      |.Á.Á|
* Command buffer is of size '''0x6a'''.
00000004
* 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)
# dump response packet


offset 0xE - ??? (2 bytes)
# 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


offset 0x10 - MAC address (6 bytes)
</pre>


====Clear Unicast Address Filter (0x115d)====
=Isolation=
Crossreference: [http://portal.gitbrew.org/wikibrew/PS3:HvReverseEngineering#Isolation gitbrew.org::Isolation] <br />


* Used by LV2 internally.
==Running Isolated SPE Modules On OtherOS++ Linux==
* Command buffer size is '''0x6a'''.


====Get Unicast Address Filter (0x115f)====
* 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.


* Used by LV2 internally.
<pre>
* Command buffer is of size '''0x6a'''.
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


====Set Multicast Address Filter (0x1161)====
PPE id (0x0000000000000001) VAS id (0x0000000000000002)
 
lv1_construct_logical_spe (0x00000000)
* Used by LV2 internally.
SPE id (0x000000000000002b)
* Command buffer size is '''0x2c'''.
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)


====Clear Multicast Address Filter (0x1163)====
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>


* Used by LV2 internally.
==Using metldr On OtherOS++ Linux==
* Command buffer size is '''0x2c'''
* To clear all multicast addresses send command with all 0s.


offset 0xC - multicast address filter (4 * 8 bytes)
* 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.


====Get Multicast Address Filter (0x1165)====
<pre>
 
root@debian-hdd:/home/glevand/spp_verifier_direct# insmod ./spp_verifier_direct.ko
* Used by LV2 internally.
root@debian-hdd:/home/glevand/spp_verifier_direct# cat metldr > /proc/spp_verifier_direct/metldr
* Command buffer is of size '''0x2c'''.
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)


====Set WOL Address Filter (0x1167)====
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>


* Used by LV2 internally.
= Gelic Device =
* Command buffer size is '''0x70'''.
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#Gelic_Device gitbrew.org::Gelic Device] <br />


====Set WOL Address Filter Mode (0x116d)====
==sys.hw.config==
 
* 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.


* Used by LV2 internally.
''Note:[http://www.ps3devwiki.com/index.php?title=Wifi old vs. new]: Old == CECHA up to CECHK, New == CECHL and later''
* Command buffer size is '''0x10'''.
* Enables/Disables WOL address matching


offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
== Control Interface ==


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


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


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


====Get Device Status (0xfffb)====
r3 - LPAR address of data buffer


* Used by VSH.
r4 - size of data buffer
* Not a Gelic device command, handled by LV2 kernel.
* Returned data size in command buffer is '''0x10'''.


====Unknown (0xfffc)====
r5 - must be 0


* Used by VSH.
=== lv1_undocumented_function_195 ===
* 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)====
==== Parameters ====


* Used by VSH.
r3 - command (16 bit value)
* 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)====
r4 - command data size


* Used by VSH.
r5 - must be 0
* 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)====
=== Data Buffer ===


* Used by VSH.
* Data Buffer passed to HV call 196 is divided into 2 parts.
* Not a Gelic device command, handled by LV2 kernel.
* The first 0x800 bytes are for sending and receiving command data
* Returns 0x10 bytes in command buffer.
* The remaining 0x800 bytes are for event notification.
* Returns gelic device state ???


=== Events ===
=== Command Data Buffer ===


<pre>
* Every command data sent to Gelic device contains header of size '''0xC'''
struct ps3_eurus_event_hdr {
* After the header follows the command data
__le32 type;
* After the Gelic device processed the command, it notifies LV2 kernel about command completion by sending an interrupt
__le32 id;
__le32 timestamp;
__le32 payload_length;
__le32 unknown;
} __packed;


struct ps3_eurus_event {
==== Header ====
struct ps3_eurus_event_hdr hdr;
u8 payload[44];
} __packed;
</pre>


====Event Type 0x00000040====
* Size is '''0xc'''.
* Byte order is little-endian.
* Header data in a request command buffer is always all 0s.


{| class="wikitable"
0x0 - command = request command + 1 (2 bytes)
|-
! Id !! Description
|-
| 0x00000001 || Deauthenticated
|}


====Event Type 0x00000080====
0x4 - result, 0x1 - success ??? 0x2 - buffer too small ??? (2 bytes)


{| class="wikitable"
0x6 - body size (2 bytes)
|-
! Id !! Description
|-
| 0x00000001 || Beacon Lost
|-
| 0x00000002 || Connected
|-
| 0x00000004 || Scan Completed
|-
| 0x00000020 || WPA Connected
|-
| 0x00000040 || WPA Error (MIC Error)
|}


====Event Type 0x80000000====
=== Event Data Buffer ===


{| class="wikitable"
* The Gelic device notifies LV2 kernel by sending an interrupt when new events are available
|-
* Event Data Buffer has 8 bytes header
! Id !! Description
* The remaining bytes are divided into event slots
|-
* Each event slot is of size 64 bytes
| 0x00000001 || Device Ready
* Events are in little-endian format
|}


== Enabling WLAN Gelic On FAT ==
==== Header ====


Linux kernel doesn't use Gelic Device Control Interface like GameOS does it.
offset 0x0 - GET index (4 bytes)
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.
offset 0x4 - PUT index (4 bytes)
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
* GET index is updated by Gelic driver. The Gelic driver reads events beginning with the event slot at index GET.
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.
* 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 ===
 
* 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 ====
 
r3 - command (16 bits)
 
r4 - effective address of command data buffer
 
r5 - size of command data buffer


For PS3 Slim we need a new Linux Gelic device driver which uses Gelic Device Control Interface directly.
=== Commands ===


====Unknown (0x1)====


==USB WLAN Interface (Codename Jupiter 2)==
* Used by VSH.
* Command buffer size is '''0x10'''.
* Used in AP mode.
* Enables AP mode ???


* On new PS3 models, WLAN interface is USB.
====Get AP SSID (0x3)====
* '''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===
* Command buffer is of size '''0x30'''.
* Returns SSID in AP mode.


* LV2 uses 3 USB endpoints of interface 3,4 and 5 to communicate with WLAN.
offset 0xC - SSID (32 bytes)
* 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>
====Set AP SSID (0x5)====
Bus 002 Device 002: ID 054c:036f Sony Corp.
 
Device Descriptor:
* Used by VSH.
  bLength                18
* Command buffer is of size '''0x30'''.
  bDescriptorType        1
* Sets SSID in AP mode.
  bcdUSB              2.00
 
  bDeviceClass          224 Wireless
offset 0xC - SSID (32 bytes)
  bDeviceSubClass        1 Radio Frequency
 
  bDeviceProtocol        1 Bluetooth
====Get Channel (0xf)====
  bMaxPacketSize0        64
 
  idVendor          0x054c Sony Corp.
* Used by VSH.
  idProduct          0x036f
* Command buffer is of size '''0x31'''.
  bcdDevice          20.12
* Data is returned from the device.
  iManufacturer          1
* Returns list of channels and active channel.
  iProduct                2
 
  iSerial                0
offset 0x2F - active channel (2 bytes)
  bNumConfigurations      1
 
    Interface Descriptor:
====Set Channel (0x11)====
      bLength                9
 
      bDescriptorType        4
* Used by VSH.
      bInterfaceNumber        3
* Command buffer size is '''0xd'''
      bAlternateSetting      0
* Valid channels: '''0 - 13'''. '''0''' means that the channel is selected '''automatically'''.
      bNumEndpoints          2
 
      bInterfaceClass      255 Vendor Specific Class
offset 0xC - channel (1 byte)
      bInterfaceSubClass      2
 
      bInterfaceProtocol      1
====Unknown (0x27)====
      iInterface              0
 
      Endpoint Descriptor:
* Command buffer size is '''0xF'''.
        bLength                7
 
        bDescriptorType        5
====Set Antenna (0x29)====
        bEndpointAddress    0x85  EP 5 IN
 
        bmAttributes            3
* Command buffer size is '''0xe'''
          Transfer Type            Interrupt
 
          Synch Type              None
offset 0xC - ??? (1 byte)
          Usage Type              Data
 
        wMaxPacketSize    0x4000  1x 0 bytes
offset 0xD - ??? (1 byte)
        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===
====Set AP WEP Configuration (0x5b)====


* LV2 does 2 control transfers to EP0 during WLAN initialization
* Used by VSH.
* First control transfer sends magic '''0x20''' data to device as '''CLEAR_FEATURE''' request.
* Command buffer is of size '''0x56'''.
* Second control transfer reads '''0x2''' bytes device status. On my PS3 slim, the status data is always '''0x2031''' if you send the right magic.
* Sets WEP security type and WEP key.
* Magic data sent in first control transfer is stored in LV2.
* Security types: 0 - none, 1 - wep64, 2 - wep128
* '''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 ====
offset 0xE - security mode: 0 - none, 1 - wep64, 2 - wep128 (1 byte)


<pre>
offset 0x10 - WEP key (64 bytes)
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 ====
====Unknown (0x61)====


* Implemented in LV2.
* Used by VSH.
* Command buffer size is '''0xd'''


=====State 1=====
====Unknown (0x65)====


* Command '''0x114f''' is sent to WLAN device.
* Used by VSH.
* Command uffer size is '''0xd'''.
* Used in AP mode.


=====State 2=====
====Get Eurus Firmware Version (0x99)====


* Command '''0x1171''' is sent to WLAN device.
* Used by VSH.


=====State 3=====
Here is the response on my PS3 Slim:
<pre>
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      |..............  |
</pre>


* LV2 waits for an event from WLAN device.
====Get AP Operating Mode (0xb7)====


=====State 4=====
* Used by VSH.
* Command buffer size is '''0x10'''
* Returns AP operating mode (mixed, 11b or 11g).


* Command '''0x116f''' is sent to WLAN device.
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)


=====State 5=====
====Set AP Operating Mode (0xb9)====


* Command '''0x115b''' is sent to WLAN device.
* Used by VSH.
* Command data sent to WLAN device contains MAC address.
* Command buffer size is '''0x10'''
* Sets AP operating mode (mixed, 11b or 11g).


=====State 6=====
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)


* Command '''0x1161''' is sent to WLAN device.
====Unknown (0xc5)====
* Sets multicast address filter.


=====State 7=====
* Used by VSH.
* Command buffer size is '''0x10'''.
* Used in AP mode.


* Command '''0x110d''' is sent to WLAN device.
offset 0xC - ??? (4 bytes)


=====State 8=====
====Set AP WPA AKM Suite (0xc9)====


* Command '''0x1031''' is sent to WLAN device.
* Used by VSH.
* Command buffer size is '''0x11'''.
* Sets WPA AKM suite in AP mode.


=====State 9=====
offset 0xC - AKM suite (4 bytes)


* Command '''0x1041''' is sent to WLAN device.
====Set AP WPA Group Cipher Suite (0xcf)====
* Command data sent to WLAN device contains MAC address.


=====State 10=====
* Used by VSH.
* Command buffer size is '''0x10'''
* Used in AP + WPA mode.


* Command '''0x29''' is sent to WLAN device.
offset 0xC - group cipher suite: group (4 bytes)
* Sets antenna.


=====State 11=====
====Set AP WPA PSK Binary (0xd3)====


* Command '''0x110b''' is sent to WLAN device.
* Used by VSH.
* Command buffer size is '''0x4c'''
* Sets WPA PSK binary


=====State 12=====
offset 0xC - PSK (64 bytes)


* Command '''0x1109''' is sent to WLAN device.
====Set AP WPA Reauthentication Timeout (0xd5)====


=====State 13=====
* Used by VSH.
* Command buffer size is '''0x10'''
* Sets WPA Reauth timeout value in AP WPA mode.
* VSH uses 36000 as timeout.


* Command '''0x207''' is sent to WLAN device.
offset 0xC - timeout value in seconds (2 bytes)


=====State 14=====
====Unknown (0x127)====


* Command '''0x203''' is sent to WLAN device.
* Used by VSH.
* Command buffer size is '''0x10'''.
* Used in AP + WPA mode.


=====State 15=====
====Unknown (0x12b)====


* Command '''0x105f''' is sent to WLAN device.
* Used by VSH.
* Command data sent to WLAN device contains MAC address, channel info and region code.
* Command buffer size is '''0x10'''.
* Used in AP + WPA mode.


=====State 16=====
====Set AP WPA PSK Passphrase (0x17d)====


* LV2 waits for an event from WLAN device.
* Used by VSH.
* Command buffer size is '''0x2D'''


=====State 17=====
offset 0xD - passphrase (32 bytes)


* LV2 accepts commands sent by LV2 syscall 726.
====Set AP WPA Pairwise Cipher Suite (0x1bf)====


===Test Program===
* Used by VSH.
* Command buffer size is '''0x11'''
* Used in AP + WPA mode.


* Here is a small program which executes a WLAN scan.
offset 0xC - pairwise cipher suite (4 bytes)
* I used libusb.
 
offset 0x10 - ??? (1 byte)
 
====Unknown (0x1d9)====


====Source Code====
* Used by VSH.
<pre>
* Command buffer size is '''0x10'''


/*
====Unknown (0x1dd)====
* 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>
* Used by VSH.
#include <stdlib.h>
* Command buffer size is '''0xd'''
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>


#include <libusb-1.0/libusb.h>
====Unknown (0x1ed)====


#define USB_VENDOR_ID 0x054c /* $ONY */
* Used by VSH.
#define USB_PRODUCT_ID 0x036f
* Command buffer is of size '''0x17'''.
#define USB_IFACE_NUMBER 3
* Rate control ???


#define USB_INTR_TRANSFER_EP5_IN_BUF_SIZE 0x800
====Get Eurus HW Revision (0x1fb)====
#define USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE 0x800


struct wlan_cmd_pkt_hdr {
* Command buffer size is '''0x10'''.
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 {
====Associate (0x1001)====
uint16_t command;
 
uint16_t tag;
* Used by VSH.
uint16_t status;
* Used by LV1 on FAT models.
uint16_t payload_size;
* Command buffer size is '''0xd'''
uint8_t res[4];
* Data passed to Gelic device is all 0s
} __attribute__ ((packed));


struct wlan_event_pkt_hdr {
====Get Common Configuration (0x1003)====
uint8_t unknown1;
uint8_t unknown2;
uint8_t unknown3;
uint8_t event_count;
} __attribute__ ((packed));


static libusb_context *usb_ctx;
* Used by VSH.
static libusb_device_handle *usb_dev_handle;
* Used by LV1 on FAT models.
* Command buffer size is '''0x18'''
* Data passed to Gelic device is all 0s


static struct libusb_transfer *usb_intr_transfer_ep5_in;
====Set Common Configuration (0x1005)====
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];
* 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 ???


static pthread_mutex_t usb_wlan_cmd_mutex;
offset 0xC - BSS type: 0 - infrastructure, 1 - adhoc (1 byte)
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;
offset 0xD - authentication mode: 0 - open, 1 - shared key


/*
offset 0xE - opmode: 0 - 11bg, 1 - 11b, 2 - 11g (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,
};


static unsigned char my_mac_addr[] = {
offset 0xF - ??? (1 byte)
0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
};


/*
offset 0x10 - BSSID (6 bytes)
* hexdump
*/
static void hexdump(const unsigned char *data, unsigned int data_size)
{
int i, j;


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


for (j = 0; j < 16; j++) {
====Get WEP Configuration (0x1013)====
if (i + j < data_size) {
fprintf(stdout, " %02x", data[i + j]);
} else {
fprintf(stdout, "  ");
}
}


fprintf(stdout, " |");
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0x50'''
* Data passed to Gelic device is all 0s


for (j = 0; j < 16; j++) {
====Set WEP Configuration (0x1015)====
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");
* Used by VSH.
}
* Used by LV1 on FAT models.
}
* Command buffer size is '''0x50'''


/*
====Get WPA Configuration (0x1017)====
* 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__);


/*
* Used by VSH.
fprintf(stdout, "%s:%d: event packet header:\n", __func__, __LINE__);
* Used by LV1 on FAT models.
fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__,
* Command buffer size is '''0x5b'''
wlan_event_pkt_hdr->unknown1);
* Data passed to Gelic device is all 0s
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);
====Set WPA Configuration (0x1019)====
}


/*
* Used by VSH.
* usb_handle_wlan_cmd_response
* Used by LV1 on FAT models.
*/
* Command buffer size is '''0x5b'''
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__);
offset 0xE - security type: 0 - WPA, 1 - RSNA (1 byte)


wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
offset 0xF - psk type: 0 - hex, 1 - bin (1 byte)
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);


/* convert all header fields to big-endian byte order !!! */
offset 0x10 - psk key: hex or bin (64 bytes)


wlan_cmd_pkt_hdr->unknown5 = le16toh(wlan_cmd_pkt_hdr->unknown5);
offset 0x50 - group cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
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 */
offset 0x54 - pairwise cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
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 */


/*
offset 0x58 - AKM suite: 0x0050f202 - WPA PSK, 0x000fac02 - RSNA PSK (4 bytes)
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__);
'''See IEEE 802.11 specification for more details about cipher/AKM suites
fprintf(stdout, "%s:%d: command (0x%04x)\n", __func__, __LINE__,
'''
wlan_cmd_hdr->command);


if ((usb_wlan_cmd + 1) != wlan_cmd_hdr->command)
802.11 spec: [http://standards.ieee.org/getieee802/download/802.11-2007.pdf]
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__,
====Unknown (0x1025)====
wlan_cmd_hdr->tag);
fprintf(stdout, "%s:%d: status (0x%04x)\n", __func__, __LINE__,
wlan_cmd_hdr->status);


if (wlan_cmd_hdr->status != 0x1)
* Used by VSH.
fprintf(stdout, "%s:%d: ==> command status != 0x1\n", __func__, __LINE__);
* Command buffer size is '''0x10'''.
* Sets preamble type, something else ???


fprintf(stdout, "%s:%d: payload_size (0x%04x)\n", __func__, __LINE__,
offset 0xC - preamble mode: 0 - short, 1 - long (1 byte)
wlan_cmd_hdr->payload_size);


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


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


memcpy(usb_wlan_cmd_data, wlan_cmd_payload, wlan_cmd_hdr->payload_size);
====Get Scan Results (0x1033)====


pthread_mutex_lock(&usb_wlan_cmd_mutex);
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0x5b0'''
* Data passed to Gelic device is all 0s


usb_wlan_cmd_busy = 0;
=====Scan Results=====


pthread_cond_signal(&usb_wlan_cmd_cond);
offset 0x0 - number of scan entries (1 byte)


pthread_mutex_unlock(&usb_wlan_cmd_mutex);
offset 0x1 - array of scan entries
}


/*
======Scan Entry======
* 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__);
offset 0x0 - size of this entry in bytes, this field is NOT included (2 bytes)


fprintf(stdout, "%s:%d: transfer status (%d) length (%d)\n",
offset 0x2 - BSSID (6 bytes)
__func__, __LINE__, transfer->status, transfer->actual_length);


wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) transfer->buffer;
offset 0x8 - RSSI (1 byte)


if (wlan_cmd_pkt_hdr->unknown3 == 0x6)
offset 0x9 - timestamp (8 bytes)
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));
offset 0x11 - beacon period (2 bytes)


libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
offset 0x13 - capability (2 bytes)
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);
offset 0x15 - information elements (see 802.11 specification)
if (error) {
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
__func__, __LINE__, error);
exit(1);
}
}


/*
====Start Scan (0x1035)====
* 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);
* 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


libusb_free_transfer(transfer);
====Diassociate (0x1037)====
}


/*
* Used by VSH.
* usb_wlan_cmd_send
* Used by LV1 on FAT models.
*/
* Command buffer size is '''0xd'''
static int usb_wlan_cmd_send(uint16_t command, const uint8_t *data, unsigned int data_size)
* Data passed to Gelic device is all 0s
{
 
struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr;
====Get RSSI (0x103d)====
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",
* Used by VSH.
__func__, __LINE__, command, data_size, data_size + sizeof(struct wlan_cmd_hdr));
* Used by LV1 on FAT models.
* Command buffer size is '''0x17'''


transfer = libusb_alloc_transfer(0);
offset 0x10 - MAC address of node (6 bytes)
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;
offset 0x16 - RSSI (1 byte)
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;
====Get MAC Address (0x103f)====
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;
* Command buffer size is '''0x13'''
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);
offset 0xD - MAC address (6 bytes)


usb_wlan_cmd = command;
====Set MAC Address (0x1041)====
usb_wlan_cmd_data = (void *) data;


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


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


wlan_cmd_pkt_hdr->unknown5 = htole16(wlan_cmd_pkt_hdr->unknown5);
* Used by VSH.
wlan_cmd_pkt_hdr->tag = htole16(wlan_cmd_pkt_hdr->tag);
* Command buffer size is '''0xd'''.


wlan_cmd_hdr->command = htole16(wlan_cmd_hdr->command);
offset 0xC - ??? (1 byte)
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);
====Unknown (0x1051)====
if (error) {
 
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
* Used by VSH.
__func__, __LINE__, error);
* Command buffer size is '''0x5b3'''.
goto fail_free_transfer;
 
}
====Unknown (0x1053)====
 
* Used by VSH.
* Command buffer size is '''0x70'''.


pthread_mutex_lock(&usb_wlan_cmd_mutex);
offset 0xC - ??? (4 bytes)


usb_wlan_cmd_busy = 1;
offset 0x10 - MAC address (6 bytes)


while (usb_wlan_cmd_busy)
====Unknown (0x1059)====
pthread_cond_wait(&usb_wlan_cmd_cond, &usb_wlan_cmd_mutex);


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


return 0;
====Unknown (0x105f)====


fail_free_transfer:
* Used by LV2.


libusb_free_transfer(transfer);
====Get Zephyr HW Revision (0x1101)====


fail:
* 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'''.


return error;
====Get MAC Address List (0x1117)====
}


/*
* Command buffer size is '''0xce'''.
* usb_wlan_cmd_start_scan
* Returns several MAC addresses.
*/
static int usb_wlan_cmd_start_scan(void)
{
unsigned char data[256], *ptr;
unsigned int data_size;


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


ptr = data;
offset 0xE - MAC addresses (6 * number of MAC addresses)
*ptr++ = 0x0;
*ptr++ = 0x1;
*ptr++ = 0x64;
*ptr++ = 0x0;


ptr = data + 0xa;
====Unknown (0x1133)====
*ptr++ = 0x3;


*ptr++ = 13; /* number of channels */
* Used by VSH.
*ptr++ = 1; /* channels */
* Command buffer size is '''0x1A'''.
*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;
====Set WOL MAC Address Filter (0x1139)====


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


/*
====Unknown (0x113b)====
* 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));
* Used by LV2 internally.
* Command buffer size is '''0x20'''.


data_size = sizeof(data);
====Set WOL Multicast Address Filter (0x113d)====


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


/*
====Clear WOL Multicast Address Filter (0x113f)====
* usb_wlan_cmd_0x99
*/
static int usb_wlan_cmd_0x99(void)
{
unsigned char data[0x3e];
unsigned int data_size;


memset(data, 0, sizeof(data));
* Used by LV2 internally.
* Command buffer is of size '''0x28'''.


data_size = sizeof(data);
====Unknown (0x1141)====


return usb_wlan_cmd_send(0x99, data, data_size);
* Used by LV2 internally.
}


/*
====Clear WOL Address Filter (0x1143)====
* usb_wlan_init
*/
static int usb_wlan_init(void)
{
unsigned char data[1456], *ptr;
unsigned int data_size;
int error;


/* state 0x1 */
* Used by LV2 internally.
* Command buffer size is '''0x2c'''.


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


data_size = 0x518;
* Used by LV2 internally.


error = usb_wlan_cmd_send(0x114f, data, data_size);
====Set WOL Magic Packet Mode (0x1155)====
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x114f (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
* Used by LV2 internally.
* Command buffer is of size '''0x10'''.
* Enables/Disables WOL magic packet.


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


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


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


error = usb_wlan_cmd_send(0x1171, data, data_size);
====Set WOL Multicast Address Filter Mode (0x1159)====
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1171 (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
* Used by LV2 internally.
* Command buffer size is '''0x10'''.
* WOL function


/* wait for a WLAN event */
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)


/* state 0x4 */
====Set Unicast Address Filter (0x115b)====


memset(data, 0, sizeof(data));
* 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


ptr = data;
offset 0xC - ??? (2 bytes)


*ptr++ = 0x1;
offset 0xE - ??? (2 bytes)


data_size = 0x4;
offset 0x10 - MAC address (6 bytes)


error = usb_wlan_cmd_send(0x116f, data, data_size);
====Clear Unicast Address Filter (0x115d)====
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x116f (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
* Used by LV2 internally.
* Command buffer size is '''0x6a'''.


/* state 0x5 */
====Get Unicast Address Filter (0x115f)====


memset(data, 0, sizeof(data));
* Used by LV2 internally.
* Command buffer is of size '''0x6a'''.


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


*ptr++ = 0x1;
* Used by LV2 internally.
* Command buffer size is '''0x2c'''.


ptr = data + 0x4;
====Clear Multicast Address Filter (0x1163)====
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));


data_size = 0x5e;
* Used by LV2 internally.
* Command buffer size is '''0x2c'''
* To clear all multicast addresses send command with all 0s.


error = usb_wlan_cmd_send(0x115b, data, data_size);
offset 0xC - multicast address filter (4 * 8 bytes)
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x115b (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
====Get Multicast Address Filter (0x1165)====


/* state 0x6 */
* Used by LV2 internally.
* Command buffer is of size '''0x2c'''.


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


ptr = data + 0x1c;
* Used by LV2 internally.
* Command buffer size is '''0x70'''.


*ptr++ = 0x20;
====Set WOL Address Filter Mode (0x116d)====


data_size = 0x20;
* Used by LV2 internally.
* Command buffer size is '''0x10'''.
* Enables/Disables WOL address matching


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


sleep(2);
====Set Unicast Address Filter Mode (0x116f)====


memset(data, 0, sizeof(data));
* Used by LV2 internally.
* Command buffer size is '''0x10'''.


ptr = data + 0xc;
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
memset(ptr, 0xff, 7 * 4);


data_size = 0x80;
====Get Device Status (0xfffb)====


error = usb_wlan_cmd_send(0x110d, data, data_size);
* Used by VSH.
if (error) {
* Not a Gelic device command, handled by LV2 kernel.
fprintf(stderr, "%s:%d: could not send command 0x110d (%d)\n",
* Returned data size in command buffer is '''0x10'''.
__func__, __LINE__, error);
return error;
}


sleep(2);
====Unknown (0xfffc)====


memset(data, 0, sizeof(data));
* 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)'''


data_size = 0x2;
====Get Channel Information (0xfffd)====


error = usb_wlan_cmd_send(0x1031, data, data_size);
* Used by VSH.
if (error) {
* Not a Gelic device command, handled by LV2 kernel.
fprintf(stderr, "%s:%d: could not send command 0x1031 (%d)\n",
* LV2 uses LV1 call '''lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x0, 0x0, 0x0)'''
__func__, __LINE__, error);
* Returns supported WLAN channels
return error;
}


sleep(2);
====Set Response Timeout (0xfffe)====


memset(data, 0, sizeof(data));
* 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'''.


ptr = data;
====Unknown (0xffff)====
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));


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


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


sleep(2);
<pre>
struct ps3_eurus_event_hdr {
__le32 type;
__le32 id;
__le32 timestamp;
__le32 payload_length;
__le32 unknown;
} __packed;


/* state 0xa */
struct ps3_eurus_event {
struct ps3_eurus_event_hdr hdr;
u8 payload[44];
} __packed;
</pre>


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


ptr = data;
{| class="wikitable"
|-
! Id !! Description
|-
| 0x00000001 || Deauthenticated
|}


*ptr++ = 0x2;
====Event Type 0x00000080====
*ptr++ = 0x2;


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


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


sleep(2);
{| class="wikitable"
|-
! Id !! Description
|-
| 0x00000001 || Device Ready
|}


memset(data, 0, sizeof(data));
== Enabling WLAN Gelic On FAT ==


ptr = data;
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.


*ptr++ = 0x1;
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.


ptr = data + 8;
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.


*ptr++ = 0x20;
For PS3 Slim we need a new Linux Gelic device driver which uses Gelic Device Control Interface directly.


data_size = 0xc;


error = usb_wlan_cmd_send(0x110b, data, data_size);
==USB WLAN Interface (Codename Jupiter 2)==
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x110b (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
* 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.


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


ptr = data;
* 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.
*ptr++ = 0x1;
* '''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'''.
ptr = data + 0x4;
* 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 !!!'''


*ptr++ = 0x15;
<pre>
*ptr++ = 0x27;
Bus 002 Device 002: ID 054c:036f Sony Corp.
 
Device Descriptor:
*ptr++ = 0x12;
  bLength                18
*ptr++ = 0x0;
  bDescriptorType        1
 
  bcdUSB              2.00
*ptr++ = 0x6;
  bDeviceClass          224 Wireless
*ptr++ = 0x0;
  bDeviceSubClass        1 Radio Frequency
 
  bDeviceProtocol        1 Bluetooth
ptr = data + 0xc;
  bMaxPacketSize0        64
 
  idVendor          0x054c Sony Corp.
*ptr++ = 0x9;
  idProduct          0x036f
*ptr++ = 0x0;
  bcdDevice          20.12
*ptr++ = 0x1;
  iManufacturer          1
 
  iProduct                2
ptr = data + 0x10;
  iSerial                0
 
  bNumConfigurations      1
*ptr++ = 0xff;
    Interface Descriptor:
*ptr++ = 0xff;
      bLength                9
*ptr++ = 0xff;
      bDescriptorType        4
*ptr++ = 0xff;
      bInterfaceNumber        3
*ptr++ = 0xff;
      bAlternateSetting      0
*ptr++ = 0xff;
      bNumEndpoints          2
 
      bInterfaceClass      255 Vendor Specific Class
data_size = 0x16;
      bInterfaceSubClass      2
 
      bInterfaceProtocol      1
error = usb_wlan_cmd_send(0x1109, data, data_size);
      iInterface              0
if (error) {
      Endpoint Descriptor:
fprintf(stderr, "%s:%d: could not send command 0x1109 (%d)\n",
        bLength                7
__func__, __LINE__, error);
        bDescriptorType        5
return error;
        bEndpointAddress    0x85  EP 5 IN
}
        bmAttributes            3
 
          Transfer Type            Interrupt
sleep(2);
          Synch Type              None
 
          Usage Type              Data
memset(data, 0, sizeof(data));
        wMaxPacketSize    0x4000  1x 0 bytes
 
        bInterval              1
ptr = data;
      Endpoint Descriptor:
 
        bLength                7
*ptr++ = 0x1;
        bDescriptorType        5
 
        bEndpointAddress    0x05  EP 5 OUT
data_size = 0x4;
        bmAttributes            3
 
          Transfer Type            Interrupt
error = usb_wlan_cmd_send(0x207, data, data_size);
          Synch Type              None
if (error) {
          Usage Type              Data
fprintf(stderr, "%s:%d: could not send command 0x207 (%d)\n",
        wMaxPacketSize    0x4000  1x 0 bytes
__func__, __LINE__, error);
        bInterval              1
return error;
    Interface Descriptor:
}
      bLength                9
 
      bDescriptorType        4
sleep(2);
      bInterfaceNumber        4
 
      bAlternateSetting      0
memset(data, 0, sizeof(data));
      bNumEndpoints          2
 
      bInterfaceClass      255 Vendor Specific Class
ptr = data;
      bInterfaceSubClass      2  
 
      bInterfaceProtocol      2
*ptr++ = 0x4;
      iInterface              0  
 
      Endpoint Descriptor:
data_size = 0x4;
        bLength                7
 
        bDescriptorType        5
error = usb_wlan_cmd_send(0x203, data, data_size);
        bEndpointAddress    0x86  EP 6 IN
if (error) {
        bmAttributes            2
fprintf(stderr, "%s:%d: could not send command 0x203 (%d)\n",
          Transfer Type            Bulk
__func__, __LINE__, error);
          Synch Type              None
return error;
          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>


sleep(2);
===Device Initialization===


/* state 0xf */
* 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.


memset(data, 0, sizeof(data));
==== Magic Data in Control Transfer ====


ptr = data;
<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>


*ptr++ = 0xff;
==== Initialization State Machine ====
*ptr++ = 0x1f;


memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));
* Implemented in LV2.


ptr = data + 0x8;
=====State 1=====


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


data_size = 0xa;
=====State 2=====


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


return 0;
=====State 3=====
}


/*
* LV2 waits for an event from WLAN device.
* usb_wlan_cmd_thread
*/
static void *usb_wlan_cmd_thread(void *arg)
{
int error;


error = usb_wlan_init();
=====State 4=====
if (error) {
fprintf(stderr, "%s:%d: could not initialize device (%d)\n",
__func__, __LINE__, error);
goto done;
}


sleep(5);
* Command '''0x116f''' is sent to WLAN device.


error = usb_wlan_cmd_0x99();
=====State 5=====
if (error) {
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
__func__, __LINE__, error);
goto done;
}


error = usb_wlan_cmd_start_scan();
* Command '''0x115b''' is sent to WLAN device.
if (error) {
* Command data sent to WLAN device contains MAC address.
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
__func__, __LINE__, error);
goto done;
}


sleep(10);
=====State 6=====


error = usb_wlan_cmd_get_scan_results();
* Command '''0x1161''' is sent to WLAN device.
if (error) {
* Sets multicast address filter.
fprintf(stderr, "%s:%d: could not get scan results (%d)\n",
__func__, __LINE__, error);
goto done;
}


sleep(10);
=====State 7=====


done:
* Command '''0x110d''' is sent to WLAN device.


usb_wlan_cmd_thread_done = 1;
=====State 8=====


return NULL;
* Command '''0x1031''' is sent to WLAN device.
}


/*
=====State 9=====
* 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);
* Command '''0x1041''' is sent to WLAN device.
pthread_cond_init(&usb_wlan_cmd_cond, NULL);
* Command data sent to WLAN device contains MAC address.


error = libusb_init(&usb_ctx);
=====State 10=====
if (error) {
fprintf(stderr, "%s:%d: libusb_init failed (%d)\n", __func__, __LINE__, error);
exit(1);
}


libusb_set_debug(usb_ctx, 5);
* Command '''0x29''' is sent to WLAN device.
* Sets antenna.


usb_dev_handle = libusb_open_device_with_vid_pid(usb_ctx, USB_VENDOR_ID, USB_PRODUCT_ID);
=====State 11=====
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)) {
* Command '''0x110b''' is sent to WLAN device.
fprintf(stdout, "%s:%d: kernel driver is attached\n", __func__, __LINE__);


error = libusb_detach_kernel_driver(usb_dev_handle, USB_IFACE_NUMBER);
=====State 12=====
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__);
* Command '''0x1109''' is sent to WLAN device.
}


error = libusb_claim_interface(usb_dev_handle, USB_IFACE_NUMBER);
=====State 13=====
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,
* Command '''0x207''' is sent to WLAN device.
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);
=====State 14=====


error = libusb_control_transfer(usb_dev_handle, 0xc0, 0x0, 0x2, 0x0, buf, 2, 0);
* Command '''0x203''' is sent to WLAN device.
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);
=====State 15=====


fprintf(stdout, "%s:%d: 0x%02x 0x%02x\n", __func__, __LINE__, buf[0], buf[1]);
* Command '''0x105f''' is sent to WLAN device.
* Command data sent to WLAN device contains MAC address, channel info and region code.


usb_intr_transfer_ep5_in = libusb_alloc_transfer(0);
=====State 16=====
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));
* LV2 waits for an event from WLAN device.


libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
=====State 17=====
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);
* LV2 accepts commands sent by LV2 syscall 726.
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);
===Test Program===
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) {
* Here is a small program which executes a WLAN scan.
tv.tv_sec = 1;
* I used libusb.
tv.tv_usec = 0;


error = libusb_handle_events_timeout(usb_ctx, &tv);
====Source Code====
if (error) {
<pre>
fprintf(stderr, "%s:%d: could not handle events (%d)\n",
__func__, __LINE__, error);
exit(1);
}
}


libusb_free_transfer(usb_intr_transfer_ep5_in);
/*
* 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.
*/


error = libusb_release_interface(usb_dev_handle, USB_IFACE_NUMBER);
#include <stdio.h>
if (error)
#include <stdlib.h>
fprintf(stderr, "%s:%d: could not release interface (%d)\n",
#include <string.h>
__func__, __LINE__, error);
#include <ctype.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>


libusb_close(usb_dev_handle);
#include <libusb-1.0/libusb.h>


libusb_exit(usb_ctx);
#define USB_VENDOR_ID 0x054c /* $ONY */
#define USB_PRODUCT_ID 0x036f
#define USB_IFACE_NUMBER 3


exit(0);
#define USB_INTR_TRANSFER_EP5_IN_BUF_SIZE 0x800
}
#define USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE 0x800
</pre>


====Output====
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));


<pre>
struct wlan_cmd_hdr {
glevand@debian-hdd:~/ps3_usb_wlan$ sudo ./ps3_usb_wlan
uint16_t command;
sudo: unable to resolve host debian-hdd
uint16_t tag;
main:824: number of bytes transferred (32)
uint16_t status;
main:833: number of bytes received (2)
uint16_t payload_size;
main:835: 0x20 0x31
uint8_t res[4];
usb_wlan_cmd_send:288: sending command (0x114f) data size (0x0518) command size (0x0524)
} __attribute__ ((packed));
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
struct wlan_event_pkt_hdr {
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
uint8_t unknown1;
usb_handle_wlan_cmd_response:191: command header:
uint8_t unknown2;
usb_handle_wlan_cmd_response:192: command (0x1150)
uint8_t unknown3;
usb_handle_wlan_cmd_response:199: tag (0xcafe)
uint8_t event_count;
usb_handle_wlan_cmd_response:201: status (0x0006)
} __attribute__ ((packed));
usb_handle_wlan_cmd_response:205: ==> command status != 0x1
 
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
static libusb_context *usb_ctx;
usb_handle_wlan_cmd_response:210: command payload:
static libusb_device_handle *usb_dev_handle;
usb_wlan_cmd_send:288: sending command (0x1171) data size (0x0000) command size (0x000c)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
static struct libusb_transfer *usb_intr_transfer_ep5_in;
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
static unsigned char usb_intr_transfer_ep5_in_buf[USB_INTR_TRANSFER_EP5_IN_BUF_SIZE];
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
static unsigned char usb_intr_transfer_ep5_out_buf[USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE];
usb_handle_wlan_cmd_response:192: command (0x1172)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
static pthread_mutex_t usb_wlan_cmd_mutex;
usb_handle_wlan_cmd_response:201: status (0x0001)
static pthread_cond_t usb_wlan_cmd_cond;
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
static int volatile usb_wlan_cmd_busy;
usb_handle_wlan_cmd_response:210: command payload:
static uint16_t usb_wlan_cmd;
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
static void *usb_wlan_cmd_data;
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
 
usb_handle_wlan_event:133: === got WLAN event ===
static int volatile usb_wlan_cmd_thread_done;
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 |................|
* WLAN won't work without this magic !!!
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 |................|
static unsigned char usb_magic_data[] = {
usb_wlan_cmd_send:288: sending command (0x116f) data size (0x0004) command size (0x0010)
0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8,
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d,
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:
static unsigned char my_mac_addr[] = {
usb_handle_wlan_cmd_response:192: command (0x1170)
0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
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:
* hexdump
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 hexdump(const unsigned char *data, unsigned int data_size)
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
{
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
int i, j;
usb_handle_wlan_cmd_response:191: command header:
 
usb_handle_wlan_cmd_response:192: command (0x115c)
for (i = 0; i < data_size; i += 16) {
usb_handle_wlan_cmd_response:199: tag (0xcafe)
fprintf(stdout, "%08x:", i);
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
for (j = 0; j < 16; j++) {
usb_handle_wlan_cmd_response:210: command payload:
if (i + j < data_size) {
usb_wlan_cmd_send:288: sending command (0x1161) data size (0x0020) command size (0x002c)
fprintf(stdout, " %02x", data[i + j]);
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
} else {
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
fprintf(stdout, "  ");
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)
fprintf(stdout, " |");
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
for (j = 0; j < 16; j++) {
usb_handle_wlan_cmd_response:210: command payload:
if (i + j < data_size) {
usb_wlan_cmd_send:288: sending command (0x110d) data size (0x0080) command size (0x008c)
if (isprint(data[i + j]))
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
fprintf(stdout, "%c", data[i + j]);
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
else
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
fprintf(stdout, ".");
usb_handle_wlan_cmd_response:191: command header:
} else {
usb_handle_wlan_cmd_response:192: command (0x110e)
fprintf(stdout, " ");
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:
fprintf(stdout, "|\n");
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_event
usb_handle_wlan_cmd_response:192: command (0x1032)
*/
usb_handle_wlan_cmd_response:199: tag (0xcafe)
static void usb_handle_wlan_event(struct wlan_event_pkt_hdr *wlan_event_pkt_hdr)
usb_handle_wlan_cmd_response:201: status (0x0001)
{
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
fprintf(stdout, "%s:%d: === got WLAN event ===\n", __func__, __LINE__);
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)
fprintf(stdout, "%s:%d: event packet header:\n", __func__, __LINE__);
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__,
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (42)
wlan_event_pkt_hdr->unknown1);
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__,
usb_handle_wlan_cmd_response:191: command header:
wlan_event_pkt_hdr->unknown2);
usb_handle_wlan_cmd_response:192: command (0x1042)
fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__,
usb_handle_wlan_cmd_response:199: tag (0xcafe)
wlan_event_pkt_hdr->unknown3);
usb_handle_wlan_cmd_response:201: status (0x0001)
*/
usb_handle_wlan_cmd_response:207: payload_size (0x0006)
fprintf(stdout, "%s:%d: event_count (0x%02x)\n", __func__, __LINE__,
usb_handle_wlan_cmd_response:210: command payload:
wlan_event_pkt_hdr->event_count);
00000000: 00 11 22 33 44 55                              |.."3DU          |
 
usb_wlan_cmd_send:288: sending command (0x0029) data size (0x0002) command size (0x000e)
hexdump((unsigned char *) (wlan_event_pkt_hdr + 1), wlan_event_pkt_hdr->event_count * 64);
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
usb_handle_wlan_cmd_response:192: command (0x002a)
*/
usb_handle_wlan_cmd_response:199: tag (0xcafe)
static void usb_handle_wlan_cmd_response(struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr)
usb_handle_wlan_cmd_response:201: status (0x0001)
{
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
struct wlan_cmd_hdr *wlan_cmd_hdr;
usb_handle_wlan_cmd_response:210: command payload:
uint8_t *wlan_cmd_payload;
00000000: 02 02                                          |..              |
 
usb_wlan_cmd_send:288: sending command (0x110b) data size (0x000c) command size (0x0018)
fprintf(stdout, "%s:%d: === got WLAN command response ===\n", __func__, __LINE__);
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (48)
wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);
usb_handle_wlan_cmd_response:191: command header:
 
usb_handle_wlan_cmd_response:192: command (0x110c)
/* convert all header fields to big-endian byte order !!! */
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
wlan_cmd_pkt_hdr->unknown5 = le16toh(wlan_cmd_pkt_hdr->unknown5);
usb_handle_wlan_cmd_response:207: payload_size (0x000c)
wlan_cmd_pkt_hdr->tag = le16toh(wlan_cmd_pkt_hdr->tag); /* returned from request */
usb_handle_wlan_cmd_response:210: command payload:
 
00000000: 01 00 00 00 00 00 00 00 20 00 00 00            |........ ...    |
wlan_cmd_hdr->command = le16toh(wlan_cmd_hdr->command); /* request command + 1 */
usb_wlan_cmd_send:288: sending command (0x1109) data size (0x0016) command size (0x0022)
wlan_cmd_hdr->tag = le16toh(wlan_cmd_hdr->tag); /* returned from request */
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
wlan_cmd_hdr->status = le16toh(wlan_cmd_hdr->status); /* 1 - success
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (58)
  2 - invalid parameters ???
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
  3 - invalid command ??? */
usb_handle_wlan_cmd_response:191: command header:
wlan_cmd_hdr->payload_size = le16toh(wlan_cmd_hdr->payload_size); /* length of data that follows the 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)
fprintf(stdout, "%s:%d: command packet header:\n", __func__, __LINE__);
usb_handle_wlan_cmd_response:207: payload_size (0x0016)
fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__,
usb_handle_wlan_cmd_response:210: command payload:
wlan_cmd_pkt_hdr->unknown1);
00000000: 01 00 00 00 15 27 12 00 06 00 00 00 09 00 01 00 |.....'..........|
fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__,
00000010: ff ff ff ff ff ff                              |......          |
wlan_cmd_pkt_hdr->unknown2);
usb_wlan_cmd_send:288: sending command (0x0207) data size (0x0004) command size (0x0010)
fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__,
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
wlan_cmd_pkt_hdr->unknown3);
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40)
fprintf(stdout, "%s:%d: unknown4 (0x%02x)\n", __func__, __LINE__,
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
wlan_cmd_pkt_hdr->unknown4);
usb_handle_wlan_cmd_response:191: command header:
fprintf(stdout, "%s:%d: unknown5 (0x%04x)\n", __func__, __LINE__,
usb_handle_wlan_cmd_response:192: command (0x0208)
wlan_cmd_pkt_hdr->unknown5);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__,
usb_handle_wlan_cmd_response:201: status (0x0001)
wlan_cmd_pkt_hdr->tag);
usb_handle_wlan_cmd_response:207: payload_size (0x0004)
*/
usb_handle_wlan_cmd_response:210: command payload:
 
00000000: 01 00 00 00                                    |....            |
fprintf(stdout, "%s:%d: command header:\n", __func__, __LINE__);
usb_wlan_cmd_send:288: sending command (0x0203) data size (0x0004) command size (0x0010)
fprintf(stdout, "%s:%d: command (0x%04x)\n", __func__, __LINE__,
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
wlan_cmd_hdr->command);
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
if ((usb_wlan_cmd + 1) != wlan_cmd_hdr->command)
usb_handle_wlan_cmd_response:191: command header:
fprintf(stdout, "%s:%d: ==> command does not match, got (0x%04x) expected (0x%04x)\n",
usb_handle_wlan_cmd_response:192: command (0x0204)
__func__, __LINE__, wlan_cmd_hdr->command, usb_wlan_cmd + 1);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__,
usb_handle_wlan_cmd_response:207: payload_size (0x0004)
wlan_cmd_hdr->tag);
usb_handle_wlan_cmd_response:210: command payload:
fprintf(stdout, "%s:%d: status (0x%04x)\n", __func__, __LINE__,
00000000: 04 00 00 00                                    |....            |
wlan_cmd_hdr->status);
usb_wlan_cmd_send:288: sending command (0x105f) data size (0x000a) command size (0x0016)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
if (wlan_cmd_hdr->status != 0x1)
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
fprintf(stdout, "%s:%d: ==> command status != 0x1\n", __func__, __LINE__);
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
fprintf(stdout, "%s:%d: payload_size (0x%04x)\n", __func__, __LINE__,
usb_handle_wlan_cmd_response:192: command (0x1060)
wlan_cmd_hdr->payload_size);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
fprintf(stdout, "%s:%d: command payload:\n", __func__, __LINE__);
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
 
usb_handle_wlan_cmd_response:210: command payload:
hexdump(wlan_cmd_payload, wlan_cmd_hdr->payload_size);
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
memcpy(usb_wlan_cmd_data, wlan_cmd_payload, wlan_cmd_hdr->payload_size);
usb_handle_wlan_event:133: === got WLAN event ===
 
usb_handle_wlan_event:144: event_count (0x01)
pthread_mutex_lock(&usb_wlan_cmd_mutex);
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 |................|
usb_wlan_cmd_busy = 0;
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>


===Associate with AP===
pthread_cond_signal(&usb_wlan_cmd_cond);


* I got association with AP working.
pthread_mutex_unlock(&usb_wlan_cmd_mutex);
* 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====
/*
* Set common configuration (command 0x1005)
* usb_intr_transfer_ep5_in_cb
* Set WPA configuration (command 0x1019)
*/
* Set rate configuration (command 0x1ed)
static void usb_intr_transfer_ep5_in_cb(struct libusb_transfer *transfer)
* Associate (command 0x1001)
{
struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr;
int error;


===Packet Reception===
fprintf(stdout, "%s:%d: === got interrupt transfer ===\n", __func__, __LINE__);


* EP6 IN and EP7 IN endpoints are used for packet reception
fprintf(stdout, "%s:%d: transfer status (%d) length (%d)\n",
* LV2 sends bulk transfers to both endpoints
__func__, __LINE__, transfer->status, transfer->actual_length);
* '''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====
wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) transfer->buffer;


<pre>
if (wlan_cmd_pkt_hdr->unknown3 == 0x6)
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
usb_handle_wlan_cmd_response(wlan_cmd_pkt_hdr);
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (98)
else if (wlan_cmd_pkt_hdr->unknown3 == 0x8)
00000000: ff ff ff ff ff ff ?? ?? ?? ?? ?? ?? 08 00 45 00 |..............E.|
usb_handle_wlan_event((struct wlan_event_pkt_hdr *) transfer->buffer);
00000010: 00 54 00 00 40 00 40 01 b5 fe c0 a8 01 5b c0 a8 |.T..@.@......[..|
else
00000020: 01 ff 08 00 9c 69 0d 45 00 e2 4e 5d 34 26 00 07 |.....i.E..N]4&..|
fprintf(stdout, "%s:%d: got unknown packet (0x%02x)\n",
00000030: df e1 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 |................|
__func__, __LINE__, wlan_cmd_pkt_hdr->unknown3);
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>


====Multicast Address Filter====
memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf));


* WLAN Gelic device supports hardware multicast address filtering
libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
* Multicast address filtering is implemented with MAC address hashing and filter bitmap
usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf),
* Filter bitmap is of size '''4 * 8''' bytes
usb_intr_transfer_ep5_in_cb, NULL, 0);
* Multicast address filter is set with command '''0x1161'''


=====MAC Address Hash Function=====
error = libusb_submit_transfer(usb_intr_transfer_ep5_in);
 
if (error) {
* Used by LV2
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
__func__, __LINE__, error);
exit(1);
}
}


<pre>
/*
unsigned char hash(unsigned char *data, unsigned int size)
* usb_intr_transfer_ep5_out_cb
*/
static void usb_intr_transfer_ep5_out_cb(struct libusb_transfer *transfer)
{
{
        unsigned int hash;
/*
        int i, j;
fprintf(stdout, "%s:%d: sent interrupt transfer\n", __func__, __LINE__);


        /*XXX: reverse data bits */
fprintf(stdout, "%s:%d: transfer status (%d)\n", __func__, __LINE__, transfer->status);
*/


        hash = 0xffffffff;
libusb_free_transfer(transfer);
}


        for (i = 0; i < size; i++) {
/*
                hash = (((unsigned int) data[i]) << 24) ^ hash;
* 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;


                for (j = 0; j < 8; j++) {
fprintf(stdout, "%s:%d: sending command (0x%04x) data size (0x%04x) command size (0x%04x)\n",
                        if (((int) hash) >= 0) {
__func__, __LINE__, command, data_size, data_size + sizeof(struct wlan_cmd_hdr));
                                hash = hash << 1;
                        } else {
                                hash = (hash << 1) ^ 0x04c10000;
                                hash = hash ^ 0x00001db7;
                        }
                }
        }


        hash = ((hash >> 24) & 0xf8) | (hash & 0x7);
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__);
error = -1;
goto fail;
}


        return hash & 0xff;
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);


h = hash(mac_addr, 6);
wlan_cmd_pkt_hdr->unknown1 = 0x1;
v = 1 << (h & 0x1f);   /* word value in filter */
wlan_cmd_pkt_hdr->unknown2 = 0x1;
p = h >> 5;             /* word position in filter */
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;


For broadcast address:
memcpy(wlan_cmd_payload, data, data_size);
------------------------


v = 0x20000000
usb_wlan_cmd = command;
p = 7
usb_wlan_cmd_data = (void *) data;


That's why 0x20 is used with command 0x1161 !!! Without it the device won't deliver broadcast traffic.
libusb_fill_interrupt_transfer(transfer, usb_dev_handle, LIBUSB_ENDPOINT_OUT | 0x5,
Learned it the hard way, after 2 days of trying to get packet reception working :)
usb_intr_transfer_ep5_out_buf,
</pre>
sizeof(struct wlan_cmd_pkt_hdr) + sizeof(struct wlan_cmd_hdr) + wlan_cmd_hdr->payload_size,
usb_intr_transfer_ep5_out_cb, NULL, 0);


===Packet Transmission===
/* convert all header fields to little-endian byte order !!! */


* Tx packets are sent to EP6 OUT
wlan_cmd_pkt_hdr->unknown5 = htole16(wlan_cmd_pkt_hdr->unknown5);
* Tx packets are normal Ethernet frames, they don't contain any WLAN data or other headers
wlan_cmd_pkt_hdr->tag = htole16(wlan_cmd_pkt_hdr->tag);


===AP Mode===
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);


* I got AP mode working with security disabled for now
error = libusb_submit_transfer(transfer);
if (error) {
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
__func__, __LINE__, error);
goto fail_free_transfer;
}


====AP Mode with Security Disabled====
pthread_mutex_lock(&usb_wlan_cmd_mutex);


* Set AP SSID (command 0x5)
usb_wlan_cmd_busy = 1;
* 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===
while (usb_wlan_cmd_busy)
pthread_cond_wait(&usb_wlan_cmd_cond, &usb_wlan_cmd_mutex);


* 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.
pthread_mutex_unlock(&usb_wlan_cmd_mutex);
* 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'''


return 0;


'''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.'''
fail_free_transfer:


====TODO====
libusb_free_transfer(transfer);


* Implement association in STA mode (finished)
fail:
* 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==
return error;
}


* LV2 uses BSD network stack, e.g. '''struct mbuf'''
/*
* It's almost identical to FreeBSD network stack.
* usb_wlan_cmd_start_scan
*/
static int usb_wlan_cmd_start_scan(void)
{
unsigned char data[256], *ptr;
unsigned int data_size;


===Network Device===
memset(data, 0, sizeof(data));


====IOCTLs====
ptr = data;
*ptr++ = 0x0;
*ptr++ = 0x1;
*ptr++ = 0x64;
*ptr++ = 0x0;


=====Set Multicast Address Filter (0x81012000)=====
ptr = data + 0xa;
*ptr++ = 0x3;


* Sets multicast address filter
*ptr++ = 13; /* number of channels */
* Uses LV1 calls '''lv1_net_remove_multicast_address''' and '''lv1_net_add_multicast_address''' for Ethernet Gelic device
*ptr++ = 1; /* channels */
* Uses Eurus commands '''0x1161''', '''0x1163''' and '''0x1165''' for WLAN Gelic device
*ptr++ = 2;
*ptr++ = 3;
*ptr++ = 4;
*ptr++ = 5;
*ptr++ = 6;
*ptr++ = 7;
*ptr++ = 8;
*ptr++ = 9;
*ptr++ = 10;
*ptr++ = 11;
*ptr++ = 12;
*ptr++ = 13;


=====Unknown (0x8101200E)=====
data_size = ptr - data;


* Uses LV1 call '''lv1_net_control(0x8000000000000001)'''
return usb_wlan_cmd_send(0x1035, data, data_size);
}


=====Unknown (0x81040000)=====
/*
* usb_wlan_cmd_get_scan_results
*/
static int usb_wlan_cmd_get_scan_results(void)
{
unsigned char data[1456];
unsigned int data_size;


* Uses LV1 call '''lv1_net_control(0x8, [0x0, 0x1 or 0x2])''' for Ethernet Gelic device
memset(data, 0, sizeof(data));
* Uses Eurus commands '''0x116F''', '''0x115D''' and '''0x115B''' for WLAN Gelic device


=====Enable/Disable WOL Magic Packet (0x81080000)=====
data_size = sizeof(data);


* Enables/Disables WOL Magic Packet
return usb_wlan_cmd_send(0x1033, data, data_size);
* 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)=====
/*
* usb_wlan_cmd_0x99
*/
static int usb_wlan_cmd_0x99(void)
{
unsigned char data[0x3e];
unsigned int data_size;


* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x2)''' for Ethernet Gelic device
memset(data, 0, sizeof(data));
* Uses Eurus commands '''0x113B''' and '''0x1157''' for WLAN Gelic device


=====Unknown (0x81080002)=====
data_size = sizeof(data);


* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x3)''' for Ethernet Gelic device
return usb_wlan_cmd_send(0x99, data, data_size);
* Uses Eurus commands '''0x113D''' and '''0x1159''' for WLAN Gelic device
}


=====Unknown (0x81080003)=====
/*
* usb_wlan_init
*/
static int usb_wlan_init(void)
{
unsigned char data[1456], *ptr;
unsigned int data_size;
int error;


* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x4)''' for Ethernet Gelic device
/* state 0x1 */
* Uses Eurus command '''0x1161''' for WLAN Gelic device


=====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
data_size = 0x518;
* Uses Eurus commands '''0x116D''' and '''0x1167''' for WLAN Gelic device


===Network Packet===
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;
}


* LV2 network packet is represented by '''struct mbuf'''
sleep(2);


=RSX=
/* state 0x2 */
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#RSX gitbrew.org::RSX] <br />


==HV Calls==
memset(data, 0, sizeof(data));


===lv1_gpu_memory_allocate===
data_size = 0;


* LV1 supports 16 memory handles simultaneously.
error = usb_wlan_cmd_send(0x1171, data, data_size);
* LV1 uses a bitmap to manage GPU VRAM.
if (error) {
* The bitmap is located in LV1 memory, 4 double words.
fprintf(stderr, "%s:%d: could not send command 0x1171 (%d)\n",
* Each bit corresponds to 1MB VRAM, 256bit = 256MB VRAM.
__func__, __LINE__, error);
* 2MB at the top of VRAM are preallocated as you can see below.
return error;
}


<pre>
sleep(2);
<memory handle> = 0x5a5a5a5a xor <memory handle index>
</pre>


====Memory Context Object====
/* wait for a WLAN event */


offset 0x8 - memory handle (4 bytes)
/* state 0x4 */


offset 0x10 - VRAM LPAR start address (8 bytes)
memset(data, 0, sizeof(data));


offset 0x18 - VRAM LPAR end address (8 bytes)
ptr = data;


====Test====
*ptr++ = 0x1;


* The offset of bitmap could be different on your system because it's allocated dynamically.
data_size = 0x4;
* '''First 9MB of VRAM were allocated by ps3fb Linux driver.'''


Before allocating VRAM:
error = usb_wlan_cmd_send(0x116f, data, data_size);
<pre>
if (error) {
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C
fprintf(stderr, "%s:%d: could not send command 0x116f (%d)\n",
00000000  00 00 00 00 00 00 01 ff  00 00 00 00 00 00 00 00  |.......ÿ........|
__func__, __LINE__, error);
00000010  00 00 00 00 00 00 00 00  c0 00 00 00 00 00 00 00  |........À.......|
return error;
</pre>
}


After allocating 32 MB VRAM:
sleep(2);
<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>


===lv1_gpu_context_allocate===
/* state 0x5 */


* Register %r4 is flags.
memset(data, 0, sizeof(data));
* '''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.
ptr = data;
* 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.


<pre>
*ptr++ = 0x1;
<context handle> = 0x55555555 xor <context index>
</pre>


* Thats why first created context will have handle 0x55555555.
ptr = data + 0x4;
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));


====Context Object====
data_size = 0x5e;


offset 0x8 - handle (4 bytes)
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;
}


offset 0x48 - IO page size, valid range is 4kB, 64KB and 1MB (8 bytes)
sleep(2);


====Flags====
/* state 0x6 */


'''0x2 - tells LV1 to use 64KB pages for GART memory mapping else LV1 uses 1MB pages'''
memset(data, 0, sizeof(data));


===lv1_gpu_context_iomap===
ptr = data + 0x1c;


* Internally uses lv1_put_iopte function
*ptr++ = 0x20;
* 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===
data_size = 0x20;


====Attribute 0x1====
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;
}


=====FIFO Command Buffer Setup=====
sleep(2);


<pre>
memset(data, 0, sizeof(data));
lv1_gpu_context_attribute(context handle, 0x1, PUT offset, GET offset, 0x0, 0x0)
</pre>


====Attribute 0x101====
ptr = data + 0xc;
memset(ptr, 0xff, 7 * 4);


=====Set Flip Mode=====
data_size = 0x80;


<pre>
error = usb_wlan_cmd_send(0x110d, data, data_size);
lv1_gpu_attribute(0x2, 0x1 /* head */, 0x0, 0x0)
if (error) {
lv1_gpu_context_attribute(context handle, 0x101, 0x1 /* head */, sync mode, 0x0, 0x0)
fprintf(stderr, "%s:%d: could not send command 0x110d (%d)\n",
</pre>
__func__, __LINE__, error);
return error;
}


====Attribute 0x104====
sleep(2);


=====Set Display Buffer=====
memset(data, 0, sizeof(data));


<pre>
data_size = 0x2;
lv1_gpu_context_attribute(context handle, 0x104, id, width << 32 | height, pitch << 32 | offset, 0x0)
</pre>


====Attribute 0x10a====
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;
}


=====Get Flip Status=====
sleep(2);


* Reads a value at offset '''0x10C0 + 0x1 * 0x40''' in lpar_reports memory.
memset(data, 0, sizeof(data));


=====Reset Flip Status=====
ptr = data;
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));


<pre>
data_size = 0x6;
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'''.
error = usb_wlan_cmd_send(0x1041, data, data_size);
* Offset into lpar_reports is '''0x10C0 + id * 0x40 = 0x10C0 + 0x1 * 0x40'''.
if (error) {
* Why not access lpar_reports memory directly and use LV1 call instead ???
fprintf(stderr, "%s:%d: could not send command 0x1041 (%d)\n",
__func__, __LINE__, error);
return error;
}


====Attribute 0x10b====
sleep(2);


* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''
/* state 0xa */


=====Set Cursor Position=====
memset(data, 0, sizeof(data));


<pre>
ptr = data;
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x3, x, y)
</pre>


=====Set Cursor Image Offset=====
*ptr++ = 0x2;
*ptr++ = 0x2;


<pre>
data_size = 0x2;
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x2, offset, 0x0)
</pre>


====Attribute 0x10c====
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;
}


* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''
sleep(2);


=====Cursor Function 1=====
memset(data, 0, sizeof(data));


<pre>
ptr = data;
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x1, 0x0, 0x0)
</pre>


=====Cursor Function 2=====
*ptr++ = 0x1;


<pre>
ptr = data + 8;
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x2, 0x0, 0x0)
</pre>


====Attribute 0x10d====
*ptr++ = 0x20;


* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''
data_size = 0xc;


=====Cursor Function 1=====
error = usb_wlan_cmd_send(0x110b, data, data_size);
 
if (error) {
<pre>
fprintf(stderr, "%s:%d: could not send command 0x110b (%d)\n",
lv1_gpu_context_attribute(context handle, 0x10d, 0x1, 0x1, 0x0, 0x0)
__func__, __LINE__, error);
</pre>
return error;
}


====Attribute 0x300====
sleep(2);


=====Set Tile=====
memset(data, 0, sizeof(data));


=====Set Invalidate Tile=====
ptr = data;


=====Bind Tile=====
*ptr++ = 0x1;


=====Unbind Tile=====
ptr = data + 0x4;


====Attribute 0x301====
*ptr++ = 0x15;
*ptr++ = 0x27;


=====Set Zcull=====
*ptr++ = 0x12;
*ptr++ = 0x0;


=====Bind Zcull=====
*ptr++ = 0x6;
*ptr++ = 0x0;


=====Unbind Zcull=====
ptr = data + 0xc;


====Attribute 0x601====
*ptr++ = 0x9;
*ptr++ = 0x0;
*ptr++ = 0x1;


* Copies data from GART memory to VRAM.
ptr = data + 0x10;
* LV1 uses internally the FIFO command buffer passed by ps3fb driver with lv1_gpu_context_iomap.


FIFO commands:
*ptr++ = 0xff;
<pre>
*ptr++ = 0xff;
0x0004C184
*ptr++ = 0xff;
0xFEED0001
*ptr++ = 0xff;
*ptr++ = 0xff;
*ptr++ = 0xff;


0x0004C198
data_size = 0x16;
0x313371C3


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


for ()
sleep(2);
{
    for ()
    {
        0x0004630C
        <param>


        0x00046304
memset(data, 0, sizeof(data));
        <param>


        0x0024C2FC
ptr = data;
        0x00000001
        0x00000003
        0x00000003
        <param1>
        <param2>
        <param3>
        <param4>
        0x00010000
        0x00010000


        0x0001C400
*ptr++ = 0x1;
        <param1>
        <param2>
        <param3>
        0x00000000
    }
}


0x00040110
data_size = 0x4;
0x00000000
</pre>


==FIFO Command Buffer==
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;
}


===FIFO Control Registers===
sleep(2);


* LV1 call '''lv1_gpu_context_allocate''' returns LPAR address of FIFO control registers.
memset(data, 0, sizeof(data));
* 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===
ptr = data;


* As long as values of GET and PUT FIFO control registers are equal, RSX doesn't process commands from the FIFO command buffer.
*ptr++ = 0x4;
* 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===
data_size = 0x4;


* [[PS3:HvReverseEngineering:emer_init.self:Program 1]]
error = usb_wlan_cmd_send(0x203, data, data_size);
* [[PS3:HvReverseEngineering:emer_init.self:Program 2]]
if (error) {
* [[PS3:HvReverseEngineering:emer_init.self:Program 3]]
fprintf(stderr, "%s:%d: could not send command 0x203 (%d)\n",
__func__, __LINE__, error);
return error;
}


===FIFO Commands===
sleep(2);


[[PS3:HvReverseEngineering:RSXFIFOCommands]]
/* state 0xf */


===Example How to Use FIFO Command Buffer===
memset(data, 0, sizeof(data));


Here is a small Linux kernel module which shows you how to use FIFO command buffer on Linux.
ptr = data;


* RSX allows to create multiple contexts.
*ptr++ = 0xff;
* This kernel module should run without problems with '''ps3fb''' driver already running.
*ptr++ = 0x1f;
* 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]
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));


====Source Code====
ptr = data + 0x8;


<pre>
*ptr++ = 0x2;
/*
*ptr++ = 0x2;
* 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>
data_size = 0xa;
#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>
error = usb_wlan_cmd_send(0x105f, data, data_size);
#include <asm/cell-regs.h>
if (error) {
#include <asm/lv1call.h>
fprintf(stderr, "%s:%d: could not send command 0x105f (%d)\n",
#include <asm/ps3.h>
__func__, __LINE__, error);
return error;
}
 
return 0;
}


#define RSX_FIFO_CMD_BUF_SIZE (1 * 1024 * 1024)
/*
* usb_wlan_cmd_thread
*/
static void *usb_wlan_cmd_thread(void *arg)
{
int error;


#define RSX_MEM_SIZE (32 * 1024 * 1024)
error = usb_wlan_init();
if (error) {
fprintf(stderr, "%s:%d: could not initialize device (%d)\n",
__func__, __LINE__, error);
goto done;
}


#define RSX_GPU_IOIF (0x0e000000ul)
sleep(5);


#define RSX_FIFO_CTRL_SIZE (4 * 1024)
error = usb_wlan_cmd_0x99();
if (error) {
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
__func__, __LINE__, error);
goto done;
}


struct rsx_fifo_ctrl {
error = usb_wlan_cmd_start_scan();
u8 res[0x40];
if (error) {
u32 put;
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
u32 get;
__func__, __LINE__, error);
};
goto done;
}


static u32 *rsx_fifo_cmd_buf;
sleep(10);
static u64 rsx_fifo_cmd_buf_lpar;


static u64 rsx_mem_handle, rsx_mem_lpar;
error = usb_wlan_cmd_get_scan_results();
static u64 rsx_ctx_handle;
if (error) {
static u64 rsx_fifo_ctrl_lpar;
fprintf(stderr, "%s:%d: could not get scan results (%d)\n",
static u64 rsx_drv_info_lpar;
__func__, __LINE__, error);
static u64 rsx_reports_lpar, rsx_reports_size;
goto done;
}


static struct rsx_fifo_ctrl *rsx_fifo_ctrl;
sleep(10);


/*
done:
* FIFO program
 
*/
usb_wlan_cmd_thread_done = 1;
static u32 rsx_fifo_prg[] = {
 
0x00000000, /* nop */
return NULL;
0x00000000, /* nop */
}
0x00000000, /* nop */
};


/*
/*
  * ps3rsx_init
  * main
  */
  */
static int __init ps3rsx_init(void)
int main(int argc, char **argv)
{
{
unsigned long timeout;
unsigned char buf[256];
int res;
pthread_t tid;
struct timeval tv;
int error;


/* FIFO command buffer must be allocated in XDR memory */
pthread_mutex_init(&usb_wlan_cmd_mutex, NULL);
pthread_cond_init(&usb_wlan_cmd_cond, NULL);


rsx_fifo_cmd_buf = kmalloc(RSX_FIFO_CMD_BUF_SIZE, GFP_KERNEL);
error = libusb_init(&usb_ctx);
if (!rsx_fifo_cmd_buf) {
if (error) {
printk(KERN_INFO"could not allocate FIFO command buffer\n");
fprintf(stderr, "%s:%d: libusb_init failed (%d)\n", __func__, __LINE__, error);
res = -ENOMEM;
exit(1);
goto fail;
}
}


res = lv1_gpu_memory_allocate(RSX_MEM_SIZE, 0, 0, 0, 0,
libusb_set_debug(usb_ctx, 5);
&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,
usb_dev_handle = libusb_open_device_with_vid_pid(usb_ctx, USB_VENDOR_ID, USB_PRODUCT_ID);
&rsx_ctx_handle, &rsx_fifo_ctrl_lpar, &rsx_drv_info_lpar,
if (!usb_dev_handle) {
&rsx_reports_lpar, &rsx_reports_size);
fprintf(stderr, "%s:%d: could not open device\n", __func__, __LINE__);
if (res) {
exit(1);
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));
if(libusb_kernel_driver_active(usb_dev_handle, USB_IFACE_NUMBER)) {
fprintf(stdout, "%s:%d: kernel driver is attached\n", __func__, __LINE__);


res = lv1_gpu_context_iomap(rsx_ctx_handle,
error = libusb_detach_kernel_driver(usb_dev_handle, USB_IFACE_NUMBER);
RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar, RSX_FIFO_CMD_BUF_SIZE,
if (error) {
CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M);
fprintf(stderr, "%s:%d: could not detach kernel driver (%d)\n",
if (res) {
__func__, __LINE__, error);
printk(KERN_INFO"lv1_gpu_context_iomap failed (%d)\n", res);
exit(1);
res = -ENXIO;
}
goto fail_free_gpu_mem;
 
fprintf(stdout, "%s:%d: kernel driver dettached\n", __func__, __LINE__);
}
}


/* map RSX FIFO control registers */
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);
}


rsx_fifo_ctrl = (struct rsx_fifo_ctrl *) ioremap(rsx_fifo_ctrl_lpar, RSX_FIFO_CTRL_SIZE);
error = libusb_control_transfer(usb_dev_handle, 0x40, 0x1, 0x9, 0x0,
if (!rsx_fifo_ctrl) {
usb_magic_data, sizeof(usb_magic_data), 0);
printk(KERN_INFO"could not map FIFO control\n");
if (error < 0) {
res = -ENXIO;
fprintf(stderr, "%s:%d: could not do control transfer (%d)\n",
goto fail_free_gpu_mem;
__func__, __LINE__, error);
exit(1);
}
}


/* PUT and GET offsets are in RSX address space */
fprintf(stdout, "%s:%d: number of bytes transferred (%d)\n", __func__, __LINE__, error);


res = lv1_gpu_context_attribute(rsx_ctx_handle, 0x1,
error = libusb_control_transfer(usb_dev_handle, 0xc0, 0x0, 0x2, 0x0, buf, 2, 0);
RSX_GPU_IOIF + 0x0 /* PUT offset */, RSX_GPU_IOIF + 0x0 /* GET offset */,
if (error < 0) {
0x0, 0x0);
fprintf(stderr, "%s:%d: could not do control transfer (%d)\n",
if (res) {
__func__, __LINE__, error);
printk(KERN_INFO"lv1_gpu_context_attribute(0x1) failed (%d)\n", res);
exit(1);
res = -ENXIO;
goto fail_unmap_fifo_ctrl;
}
}


/* copy FIFO commands to FIFO command buffer */
fprintf(stdout, "%s:%d: number of bytes received (%d)\n", __func__, __LINE__, error);


memcpy(rsx_fifo_cmd_buf, rsx_fifo_prg, sizeof(rsx_fifo_prg));
fprintf(stdout, "%s:%d: 0x%02x 0x%02x\n", __func__, __LINE__, buf[0], buf[1]);


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 = libusb_alloc_transfer(0);
if (!usb_intr_transfer_ep5_in) {
fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__);
exit(1);
}


/* kick FIFO */
memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf));


rsx_fifo_ctrl->put = RSX_GPU_IOIF + sizeof(rsx_fifo_prg);
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);


/* poll until RSX is done processing FIFO commands */
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);
}


timeout = 100;
error = pthread_create(&tid, NULL, usb_wlan_cmd_thread, NULL);
 
if (error) {
while (timeout--) {
fprintf(stderr, "%s:%d: could not create WLAN command thread (%d)\n",
if (rsx_fifo_ctrl->get == rsx_fifo_ctrl->put)
__func__, __LINE__, error);
break;
exit(1);
 
msleep(1);
}
}


printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);
while (!usb_wlan_cmd_thread_done) {
tv.tv_sec = 1;
tv.tv_usec = 0;


if (rsx_fifo_ctrl->get != rsx_fifo_ctrl->put) {
error = libusb_handle_events_timeout(usb_ctx, &tv);
printk(KERN_INFO"FIFO command buffer timeout\n");
if (error) {
res = -ENXIO;
fprintf(stderr, "%s:%d: could not handle events (%d)\n",
goto fail_unmap_fifo_ctrl;
__func__, __LINE__, error);
exit(1);
}
}
}


return 0;
libusb_free_transfer(usb_intr_transfer_ep5_in);


fail_unmap_fifo_ctrl:
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);


iounmap(rsx_fifo_ctrl);
libusb_close(usb_dev_handle);


libusb_exit(usb_ctx);


fail_free_gpu_mem:
exit(0);
 
lv1_gpu_memory_free(rsx_mem_handle);
 
fail_free_fifo_cmd_buf_mem:
 
kfree(rsx_fifo_cmd_buf);
 
fail:
 
return res;
}
}
</pre>


/*
====Output====
* 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,
<pre>
RSX_FIFO_CMD_BUF_SIZE, CBE_IOPTE_M);
glevand@debian-hdd:~/ps3_usb_wlan$ sudo ./ps3_usb_wlan
 
sudo: unable to resolve host debian-hdd
lv1_gpu_context_free(rsx_ctx_handle);
main:824: number of bytes transferred (32)
 
main:833: number of bytes received (2)
lv1_gpu_memory_free(rsx_mem_handle);
main:835: 0x20 0x31
 
usb_wlan_cmd_send:288: sending command (0x114f) data size (0x0518) command size (0x0524)
kfree(rsx_fifo_cmd_buf);
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 ===
module_init(ps3rsx_init);
usb_handle_wlan_cmd_response:191: command header:
module_exit(ps3rsx_exit);
usb_handle_wlan_cmd_response:192: command (0x1150)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
MODULE_LICENSE("GPL");
usb_handle_wlan_cmd_response:201: status (0x0006)
MODULE_DESCRIPTION("PS3 RSX");
usb_handle_wlan_cmd_response:205: ==> command status != 0x1
MODULE_AUTHOR("glevand");
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
</pre>
usb_handle_wlan_cmd_response:210: command payload:
 
usb_wlan_cmd_send:288: sending command (0x1171) data size (0x0000) command size (0x000c)
====Test====
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
<pre>
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
# insmod ./ps3rsx.ko
usb_handle_wlan_cmd_response:191: command header:
# dmesg
usb_handle_wlan_cmd_response:192: command (0x1172)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
GET offset (0x0e000000) PUT offset (0x0e000000) # GET and PUT offsets before kicking FIFO
usb_handle_wlan_cmd_response:201: status (0x0001)
GET offset (0x0e00000c) PUT offset (0x0e00000c) # GET and PUT offsets after kicking FIFO
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
</pre>
usb_handle_wlan_cmd_response:210: command payload:
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
As you see, RSX processed our FIFO commands :)
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
 
usb_handle_wlan_event:133: === got WLAN event ===
==Linux Driver==
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 |........<"......|
* '''DRI/DRM is the ONLY way to go !!! No hacks like kernel modules with tons of IOCTLs !!!'''
00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................|
* First implement 2D acceleration and then add 3D support
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............|
* The driver consists of 2 parts: '''DDX driver''' for X11 (user space) and '''DRM driver''' for Linux Kernel (kernel space)
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
* First implement DRM driver and test it from user space without DDX and libdrm by talking to it directly
usb_wlan_cmd_send:288: sending command (0x116f) data size (0x0004) command size (0x0010)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
===DDX Driver===
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
* Use '''libdrm'''
usb_handle_wlan_cmd_response:191: command header:
* Use '''EXA API''' for 2D acceleration on X11 (or maybe use '''XAA API''')
usb_handle_wlan_cmd_response:192: command (0x1170)
* Use '''Kernel Mode Setting'''
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
===DRM Driver===
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
 
usb_handle_wlan_cmd_response:210: command payload:
* Extend '''nouveau''' driver or create a new one ???
usb_wlan_cmd_send:288: sending command (0x115b) data size (0x005e) command size (0x006a)
* '''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_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* The driver is very low level and allows direct access to almost all RSX funtions, e.g. FIFO buffer, to achieve maximum performance.
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
* 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.
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
* Provides an interface to manage graphic objects in VRAM.
usb_handle_wlan_cmd_response:191: command header:
* 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.
usb_handle_wlan_cmd_response:192: command (0x115c)
* Extend '''libdrm''' library to support new DRM driver.
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* Fences can be implemented with '''RSX REF Control Register'''
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
====Memory Management====
usb_handle_wlan_cmd_response:210: command payload:
 
usb_wlan_cmd_send:288: sending command (0x1161) data size (0x0020) command size (0x002c)
* Size of all memory objects must be multiple of the page size (4096 bytes) even if a smaller size is requested by user
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* 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.
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
* 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]
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
====Video RAM====
usb_handle_wlan_cmd_response:192: command (0x1162)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* VRAM is allocated once during context creating and cannot be changed during the whole life of the context.
usb_handle_wlan_cmd_response:201: status (0x0001)
* '''lv1_gpu_memory_allocate''' returns LPAR address of allocated VRAM which can be mapped into kernel address space.
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
* '''VRAM starts at offset 0x0 in GPU address space.'''
usb_handle_wlan_cmd_response:210: command payload:
* VRAM heap management is necessary, use e.g. TTM (ttm_bo_init_mm).
usb_wlan_cmd_send:288: sending command (0x110d) data size (0x0080) command size (0x008c)
* This memory type is used e.g. for vertices or textures.
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* It should be mappable from user space in order to allow user to put data there.
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
* GameOS calls it '''Local Memory'''.
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
* VRAM can be mapped into kernel-space with '''ioremap'''.
usb_handle_wlan_cmd_response:191: command header:
* 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.
usb_handle_wlan_cmd_response:192: command (0x110e)
* Use '''VM_IO''' flag for this kind of memory when mapping it into user-space.
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* Mapping examples: [http://www.scs.ch/~frey/linux/memorymap.html] [http://www.cs.fsu.edu/~baker/devices/projects/antgeo/avnet_june19/pci_avnet.c]
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
====GART Memory====
usb_handle_wlan_cmd_response:210: command payload:
 
usb_wlan_cmd_send:288: sending command (0x1031) data size (0x0002) command size (0x000e)
* GART memory region is a memory region in System Memory but accessible by RSX through GART [http://dri.freedesktop.org/wiki/GART].
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* GameOS calls it '''Main Memory'''.
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38)
* '''Problem: lv1_gpu_context_iomap supports ONLY 1MB and 64kB pages'''
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
* 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.
usb_handle_wlan_cmd_response:191: command header:
* 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.
usb_handle_wlan_cmd_response:192: command (0x1032)
* 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.
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* 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.
usb_handle_wlan_cmd_response:201: status (0x0001)
* 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.
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
* '''TTM: TTM_PL_FLAG_TT for GART memory'''
usb_handle_wlan_cmd_response:210: command payload:
* '''GameOS applications using GCM library map GART memory beginning at offset 0x10000000 or 0x20000000, just after where the whole VRAM is mapped.'''
00000000: 00 00                                          |..             |
* '''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.'''
usb_wlan_cmd_send:288: sending command (0x1041) data size (0x0006) command size (0x0012)
* TTM uses '''struct ttm_backend_func''' to call driver specific GART mapping functions. '''nouveau_sgdma.c''' handles GART memory mapping.
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (42)
====CPU Memory====
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
* This type of memory cannot be accessed by RSX at all.
usb_handle_wlan_cmd_response:192: command (0x1042)
* Because this type of memory is not mapped into RSX address space through GART we don't need to allocate it in 1MB multiples.
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* What do we need it for ???
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0006)
====Mapping Memory Objects into Kernel-Space====
usb_handle_wlan_cmd_response:210: command payload:
 
00000000: 00 11 22 33 44 55                              |.."3DU          |
* Nouveau driver uses '''ttm_bo_kmap''' to map memory objects into kernel-space (see '''ttm_bo_util.c''').
usb_wlan_cmd_send:288: sending command (0x0029) data size (0x0002) command size (0x000e)
* 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'''.
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* 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.
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38)
* '''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].
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
* 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.
usb_handle_wlan_cmd_response:191: command header:
* '''lv1_gpu_context_iomap uses IOAS_ID 0 and IOID 1.'''
usb_handle_wlan_cmd_response:192: command (0x002a)
* TTM allows to map a buffer multiple times. Mapping information is stored in '''struct ttm_bo_kmap_obj'''.
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* '''To make single allocated pages look contiguous to kernel-space, TTM uses vmap'''.
usb_handle_wlan_cmd_response:201: status (0x0001)
* '''It is possible to use 64KB pages for GART mapping without patching LV1. To enable 4KB pages support we have to patch LV1.'''
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
* Tested with 64kB IO page size. It works fine.
usb_handle_wlan_cmd_response:210: command payload:
 
00000000: 02 02                                          |..             |
====Mapping Memory Objects into User-Space====
usb_wlan_cmd_send:288: sending command (0x110b) data size (0x000c) command size (0x0018)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* User-space programs should be able to allocate memory objects in VRAM or GART and map it with '''mmap syscall'''.
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (48)
* See '''nouveau_ttm.c:nouveau_ttm_mmap'''.
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
* Mapping memory objects into user-space avoids copying of data between user/kernel spaces.
usb_handle_wlan_cmd_response:191: command header:
* Problem: how to identify memory objects ???
usb_handle_wlan_cmd_response:192: command (0x110c)
* '''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.
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* 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.
usb_handle_wlan_cmd_response:201: status (0x0001)
* To map memory objects located in VRAM we have to map it into kernel space first with '''ioremap'''.
usb_handle_wlan_cmd_response:207: payload_size (0x000c)
 
usb_handle_wlan_cmd_response:210: command payload:
====FIFO Command Buffer====
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)
* Every context has its own one main FIFO command buffer which is NOT accessible directly by user space.
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* 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.
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (58)
* Nouveau driver uses IOCTL '''NOUVEAU_GEM_PUSHBUF''' to execute FIFO command buffers. See '''nouveau_gem.c:nouveau_gem_ioctl_pushbuf'''.
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
* By user applications submitted FIFO command buffers are mapped by DRM driver into RSX address space first and then executed with CALL command.
usb_handle_wlan_cmd_response:191: command header:
* '''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 ???'''
usb_handle_wlan_cmd_response:192: command (0x110a)
* 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'''.
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* 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.
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0016)
=====Example=====
usb_handle_wlan_cmd_response:210: command payload:
<pre>
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 ===
    \|/    Main FIFO command buffer (one per allocated context)     |
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:
|   ...   |  CALL  |        ...        |  CALL  |  ...    |  JMP    |
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)
|  ...  |  ...  |  RET  |              |              |
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)
  FIFO command buffer 1                |              |
usb_handle_wlan_cmd_response:199: tag (0xcafe)
  (allocated by user space)           \|/              |
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:
                                    | ... ... |  RET  |
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 ===
                                      FIFO command buffer 2
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
                                    (allocated by user space)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
</pre>
usb_handle_wlan_cmd_response:191: command header:
 
usb_handle_wlan_cmd_response:192: command (0x1060)
====Fences====
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
* Nouveau driver implements DRM fences with REF control register. See '''nouveau_fence.c:nouveau_fence_new'''.
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
* Newer Nvidia chips support semaphores. Nouveau driver uses semaphores for fences if they are supported.
usb_handle_wlan_cmd_response:210: command payload:
* libgcm functions '''SetWriteCommandLabel''' and '''SetWaitLabel''' use semaphores.
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* '''SetWriteCommandLabel''' releases semaphore and '''SetWaitLabel''' acquires semaphore.
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
* Semaphores are placed in VRAM. Nouveau driver creates a small VRAM heap for semaphores. See '''nouveau_fence.c:nouveau_fence_channel_init'''.
usb_handle_wlan_event:133: === got WLAN event ===
 
usb_handle_wlan_event:144: event_count (0x01)
====IOCTLs====
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 |................|
=====Context Create=====
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 |................|
* Creates new RSX context
usb_wlan_cmd_send:288: sending command (0x0099) data size (0x003e) command size (0x004a)
* Allocates VRAM and memory for FIFO buffer
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* Needed VRAM size and FIFO buffer size must be known during context creation
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (98)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
=====Context Destroy=====
usb_handle_wlan_cmd_response:191: command header:
 
usb_handle_wlan_cmd_response:192: command (0x009a)
* Destroys previously allocated context
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
=====Context Attribute=====
usb_handle_wlan_cmd_response:207: payload_size (0x003e)
 
usb_handle_wlan_cmd_response:210: command payload:
* Changes context attributes
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|
=====Graphic Object Creatre=====
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      |..............  |
* Create a graphic object either in VRAM or in XDR
usb_wlan_cmd_send:288: sending command (0x1035) data size (0x0019) command size (0x0025)
* Used to create FIFO command buffers too (only in XDR of course because RSX supoorts FIFO command buffer in XDR only)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (61)
=====Graphic Object Destroy=====
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
* Frees previously created graphic object
usb_handle_wlan_cmd_response:192: command (0x1036)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
=====FIFO Execute=====
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0019)
* Allows user space applications to execute FIFO commands.
usb_handle_wlan_cmd_response:210: command payload:
* 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.
00000000: 00 01 64 00 00 00 00 00 00 00 03 0d 01 02 03 04 |..d.............|
* Several FIFO command buffers can be submitted at once.
00000010: 05 06 07 08 09 0a 0b 0c 0d                      |.........      |
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
=====Framebuffer=====
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
 
usb_handle_wlan_event:133: === got WLAN event ===
* Kernel DRM driver has to implement a frame buffer driver too
usb_handle_wlan_event:144: event_count (0x01)
* 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.
00000000: 80 00 00 00 04 00 00 00 96 2e 02 00 01 00 00 00 |................|
* 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.
00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................|
* '''lv1_gpu_allocate_memory''' returns LPAR address of video RAM allocated for the RSX context.
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............|
* Unfortunately '''lv1_gpu_context_allocate''' doesn't initialize 2D ROP objects but we could use 3D operations to implement 2D ROPs.
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)
===libdrm===
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (1403)
* Add support for RSX DRM to '''libdrm'''
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
===Test Kernel Module and Program===
usb_handle_wlan_cmd_response:192: command (0x1034)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* 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]
usb_handle_wlan_cmd_response:201: status (0x0001)
* 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.
usb_handle_wlan_cmd_response:207: payload_size (0x0557)
* '''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.'''
usb_handle_wlan_cmd_response:210: command payload:
* 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.
Here is scan output (removed by me)
* 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.
</pre>
* 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===
 
* 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
 
=BD Drive=
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#BD_Drive gitbrew.org::HV#BD Drive] <br />
 
 
==Profile==
 
* BD profile can be read with '''GET PROFILE''' device command or SCSI command '''GET CONFIGURATION'''
 
===Profile Table===
 
{| class="wikitable"
|-
! 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==
 
* 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===
 
{| class="wikitable"
|-
! 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===
===Associate with AP===


* Size is 32KB just like AACS specifications prescribes (See AACS Common Specification 3.2.5.2 Host Revocation List Record)
* I got association with AP working.
* '''We could replace HRL with an older one in BD drive flash and restore revoked Host Certificates !!!'''
* 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====
* Set common configuration (command 0x1005)
* Set WPA configuration (command 0x1019)
* Set rate configuration (command 0x1ed)
* Associate (command 0x1001)
 
===Packet Reception===


==Device Commands==
* 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 !!!'''


===Get Profile (0x11)===
====Test with libusb====


* BD profile can be read with LV1 call '''lv1_send_storage_device_command''' and command '''0x11'''
<pre>
* LV1 sends SCSI command '''GET CONFIGURATION''' to BD drive with '''requested type 0x0''', '''starting feature number 0x0''' and '''allocation length 0x8'''
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
* See SCSI command '''GET CONFIGURATION'''
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 |................|
</pre>


===Auto Request Sense Mode On/Off (0x30)===
====Multicast Address Filter====


* LV1 expects a 4 byte value: 0x0 - On, 0x1 - Off
* WLAN Gelic device supports hardware multicast address filtering
* can be get/set via GameOS sc0x25C/604: sys_storage_send_device_command(fd of bdvd,0x30,value,4,0,0 )
* 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'''


==SCSI Commands==
=====MAC Address Hash Function=====


===Get Configuration===
* Used by LV2


Getting the profile of a BD movie disc:
<pre>
<pre>
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00
unsigned char hash(unsigned char *data, unsigned int size)
SCSI Status: Good
{
        unsigned int hash;
        int i, j;


Sense Information:
        /*XXX: reverse data bits */
sense buffer empty


Received 8 bytes of data:
        hash = 0xffffffff;
00    00 00 00 38 00 00 00 40                            ...8...@ 


# 0x40 means BD-ROM
        for (i = 0; i < size; i++) {
</pre>
                hash = (((unsigned int) data[i]) << 24) ^ hash;


Getting the profile of a PS3 game disc:
                for (j = 0; j < 8; j++) {
<pre>
                        if (((int) hash) >= 0) {
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00
                                hash = hash << 1;
SCSI Status: Good
                        } else {
                                hash = (hash << 1) ^ 0x04c10000;
                                hash = hash ^ 0x00001db7;
                        }
                }
        }


Sense Information:
        hash = ((hash >> 24) & 0xf8) | (hash & 0x7);
sense buffer empty


Received 8 bytes of data:
        return hash & 0xff;
00    00 00 00 38 00 00 ff 71                            ...8...q
}
# 0x71 means PS3 BD-ROM
</pre>


===Get SS Key===
h = hash(mac_addr, 6);
v = 1 << (h & 0x1f);    /* word value in filter */
p = h >> 5;            /* word position in filter */


* 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:
For broadcast address:
<pre>
------------------------
# sg_raw -r 8 /dev/sr0 a4 00 00 00 00 00 00 e0 00 08 03 00
SCSI Status: Good


Sense Information:
v = 0x20000000
sense buffer empty
p = 7


Received 8 bytes of data:
That's why 0x20 is used with command 0x1161 !!! Without it the device won't deliver broadcast traffic.
00    00 06 00 00 00 00 00 04                            ........       
Learned it the hard way, after 2 days of trying to get packet reception working :)
</pre>
</pre>


===Eject Media===
===Packet Transmission===


<pre>
* Tx packets are sent to EP6 OUT
sg_raw /dev/sr0 0x1b 00 00 00 02 00
* Tx packets are normal Ethernet frames, they don't contain any WLAN data or other headers
</pre>


===Load Media===
===AP Mode===


<pre>
* I got AP mode working with security disabled for now
sg_raw /dev/sr0 0x1b 00 00 00 03 00
</pre>


===Mode Select 10===
====AP Mode with Security Disabled====
 
* 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


====Enable Buffer Write====
===ps3-jupiter Linux Drivers===


* Uses '''PF 0x1''', '''SP 0x0''' and '''parameter list length 0x10'''
* 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.
* Uses the following parameter list: '''0x00 0x0e 0x00 0x00 0x00 0x00 0x00 0x00 0x2d 0x6 <buffer id> 0x00 0x00 0x00 0x00 0x00'''
* ps3_jupiter_sta.ko is a STA mode implementation.
* '''Enables writing to BD drive flash, e.g. to HRL buffer !!!'''
* 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'''


Test with sg3-utils which enables write to HRL buffer:
<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
</pre>


===Write Buffer===
'''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.'''


* Used e.g. by Update Manager to send BD firmware to BD drive
====TODO====
* '''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==
* 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


===AACS SPU Module===
==LV2 Network Stack==


* BD player on GameOS uses '''AacsModule.spu.isoself''' (/dev_flash/bdplayer) to perform AACS authentication
* LV2 uses BSD network stack, e.g. '''struct mbuf'''
* Tested on OtherOS++ 3.55
* It's almost identical to FreeBSD network stack.
* 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====
===Network Device===


* BD player reads '''EID3''' with '''Indi Info Manager 0x17001/0x17002''' services and passes it to SPU module
====IOCTLs====
* '''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====
=====Set Multicast Address Filter (0x81012000)=====


* The SPU module supports max '''0x78''' (til 4.75, 0x57 since 4.76) commands but not all are implemented
* Sets multicast address filter
* 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.
* 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)=====


{| class="wikitable sortable"
* Uses LV1 call '''lv1_net_control(0x8000000000000001)'''
|+ style="caption-side:bottom; color:#e76700;"|''No full list!''
! 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
|-
! style="background-color:#FFEBAD;"| -4.75 !! style="background-color:#FFEBAD;"| 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)===
=====Unknown (0x81040000)=====


* SHA1 hash is encrypted/decrypted by '''SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)'''
* Uses LV1 call '''lv1_net_control(0x8, [0x0, 0x1 or 0x2])''' for Ethernet Gelic device
* SHA1 hash is read with '''VTRM service 0x2005 (Retrieve)'''
* Uses Eurus commands '''0x116F''', '''0x115D''' and '''0x115B''' for WLAN Gelic device
* SHA1 hash is written with '''VTRM service 0x2003 (Store With Update)'''


===Content Revocation List (CRL)===
=====Enable/Disable WOL Magic Packet (0x81080000)=====
 
* 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)=====
 
* 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


* SHA1 hash is encrypted/decrypted by '''SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)'''
=====Unknown (0x81080002)=====
* SHA1 hash is read with '''VTRM service 0x2005 (Retrieve)'''
* SHA1 hash is written with '''VTRM service 0x2003 (Store With Update)'''


===Host Revocation List (HRL)===
* 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


* Stored in BD drive flash
=====Unknown (0x81080003)=====
* It can be read/written with SCSI commands '''READ/WRITE BUFFER'''. Yeah, it can be written too :D


====Read HRL from BD Drive Flash====
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x4)''' for Ethernet Gelic device
* Uses Eurus command '''0x1161''' for WLAN Gelic device


* It seems that BD drive has several HRL in its flash
=====Unknown (0x81080005)=====
* 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====
* 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


<pre>
===Network Packet===
# 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
* LV2 network packet is represented by '''struct mbuf'''


# as you see this HRL is empty
=RSX=
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#RSX gitbrew.org::RSX] <br />


</pre>


====Current HRL====
==HV Calls==


<pre>
===lv1_gpu_context_attribute===
# 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
====Attribute 0x1====
</pre>


===PS3 BD Player Host Certificate===
=====FIFO Command Buffer Setup=====


<pre>
<pre>
$ hexdump -C aacs_auth/ps3_host_cert.bin
lv1_gpu_context_attribute(context handle, 0x1, PUT offset, GET offset, 0x0, 0x0)
00000000  02 01 00 5c ff ff 80 00  00 39 00 00 65 ea c9 87  |...\ÿÿ...9..eêÉ.|
</pre>
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
====Attribute 0x101====
</pre>


===PS3 BD Player Host Private Key===
=====Set Flip Mode=====


<pre>
<pre>
$ hexdump -C aacs_auth/ps3_host_priv_key.bin
lv1_gpu_attribute(0x2, ???, 0x0, 0x0)
00000000  00 66 8c 9a 75 ee fc 8d  a4 26 19 38 e2 71 28 50  |.f..u....&.8.q(P|
lv1_gpu_context_attribute(context handle, 0x101, ???, sync mode, 0x0, 0x0)
00000010  61 bb 09 f0 dd                                    |a....|
</pre>
</pre>


===AACS Processing Keys===
====Attribute 0x104====


====MKB v1====
=====Set Display Buffer=====


<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_v1.inf
lv1_gpu_context_attribute(context handle, 0x104, id, width << 32 | height, pitch << 32 | offset, 0x0)
</pre>


=MKB=
====Attribute 0x10a====
type:
0x00031003
version:
0x00000001


MKB U masks and UVs: 514
=====Reset Flip Status=====


=applying subset-difference=
<pre>
index: 0
lv1_gpu_context_attribute(context handle, 0x10a, id, 0x7fffffff /* mask */, 0x0 /* value */, 0x0)
UV: 0x00000001
</pre>
U mask: 0xff800000
V mask: 0xfffffffe


=applying device key=
====Attribute 0x300====
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:
=====Set Tile=====
00000000: 09 f9 11 02 9d 74 e3 5b d8 41 56 c5 63 56 88 c0 |.....t.[.AV.cV..|


C value:
=====Set Invalidate Tile=====
00000000: cb 06 90 db e6 54 55 7b 12 62 aa d7 89 f4 9d 92 |.....TU{.b......|


media key:
=====Bind Tile=====
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=
=====Unbind Tile=====
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====
====Attribute 0x301====


<pre>
=====Set Zcull=====
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=
=====Bind Zcull=====
type:
0x00031003
version:
0x00000003


MKB U masks and UVs: 528
=====Unbind Zcull=====


=applying subset-difference=
==FIFO Command Buffer==
index: 14
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


=applying device key=
===FIFO Control Registers===
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:
* LV1 call '''lv1_gpu_context_allocate''' returns LPAR address of FIFO control registers.
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
* 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.


C value:
===Kicking FIFO Command Buffer===
00000000: 0a b7 33 82 85 62 91 d1 91 4a 95 9e 36 18 c7 a1 |..3..b...J..6...|


media key:
* As long as values of GET and PUT FIFO control registers are equal, RSX doesn't process commands from the FIFO command buffer.
00000000: 6e da eb d4 88 aa 38 58 74 26 35 fd fd 36 66 d5 |n.....8Xt&5..6f.|
* 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.


=MKB verify media key data=
===FIFO Setup Program of emer_init.self===
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>


====MKB v4====
<pre>
0x42000
0x31337303


<pre>
0xC2180
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
0x66604200
0xFEED0001
0xFEED0000


=MKB=
0x46000
type:
0x313371C3
0x00031003
version:
0x00000004


MKB U masks and UVs: 526
0xC6180
0x66604200
0xFEED0000
0xFEED0000


=applying subset-difference=
0x4A000
index: 12
0x31337808
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


=applying device key=
0x20A180
index: 244
0x66604200
UV: 0x00000100
0x0
U mask: 0xff800000
0x0
V mask: 0xfffffe00
0x0
device key:
0x0
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|
0x0
0x0
0x313371C3


processing key:
0x8A2FC
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
0x3
0x4


C value:
0x88180
00000000: bf 71 0c 8b 46 a0 24 d8 f0 3a a1 26 37 9d fb fc |.q..F.$..:.&7...|
0x66604200
0xFEED0000


media key:
0x4C000
00000000: ef 18 c0 dd bf 02 32 a1 2f 57 f7 65 79 2c 1c 58 |......2./W.ey,.X|
0x3137AF00


=MKB verify media key data=
0x4C180
encrypted:
0x66604200
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|
</pre>
</pre>


====MKB v7====
===Example How to Use FIFO Command Buffer===


<pre>
Here is a small Linux kernel module which shows you how to use FIFO command buffer on Linux.
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=
* RSX allows to create multiple contexts.
type:
* This kernel module should run without problems with '''ps3fb''' driver already running.
0x00031003
* 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.
version:
* This kernel module lets the RSX execute a simple program which contains only NOP (No Operation) commands.
0x00000007


MKB U masks and UVs: 526
Download source code: [http://lol.notsoldierx.com/~glevand/ps3/linux/ps3rsx.tar.gz]


=applying subset-difference=
====Source Code====
index: 7
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


=applying device key=
<pre>
index: 244
/*
UV: 0x00000100
* PS3 RSX
U mask: 0xff800000
*
V mask: 0xfffffe00
* This program is free software; you can redistribute it and/or modify it
device key:
* under the terms of the GNU General Public License as published
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|
* 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.
*/


processing key:
#include <linux/module.h>
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/delay.h>


C value:
#include <asm/abs_addr.h>
00000000: 21 fd c9 4b 3e 1a f3 fe 9e b4 7a e6 ef 01 75 1b |!..K>.....z...u.|
#include <asm/cell-regs.h>
#include <asm/lv1call.h>
#include <asm/ps3.h>


media key:
#define RSX_FIFO_CMD_BUF_SIZE (1 * 1024 * 1024)
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=
#define RSX_MEM_SIZE (32 * 1024 * 1024)
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====
#define RSX_GPU_IOIF (0x0e000000ul)


<pre>
#define RSX_FIFO_CTRL_SIZE (4 * 1024)
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=
struct rsx_fifo_ctrl {
type:
u8 res[0x40];
0x00031003
u32 put;
version:
u32 get;
0x00000008
};


MKB U masks and UVs: 523
static u32 *rsx_fifo_cmd_buf;
static u64 rsx_fifo_cmd_buf_lpar;


=applying subset-difference=
static u64 rsx_mem_handle, rsx_mem_lpar;
index: 4
static u64 rsx_ctx_handle;
UV: 0x00000080
static u64 rsx_fifo_ctrl_lpar;
U mask: 0xff800000
static u64 rsx_drv_info_lpar;
V mask: 0xffffff00
static u64 rsx_reports_lpar, rsx_reports_size;


=applying device key=
static struct rsx_fifo_ctrl *rsx_fifo_ctrl;
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.|
* FIFO program
*/
static u32 rsx_fifo_prg[] = {
0x00000000, /* nop */
0x00000000, /* nop */
0x00000000, /* nop */
};


C value:
/*
00000000: 73 2d 10 bd f8 b4 87 e2 86 a6 d5 3a 6d db 69 15 |s-.........:m.i.|
* ps3rsx_init
*/
static int __init ps3rsx_init(void)
{
unsigned long timeout;
int res;


media key:
/* FIFO command buffer must be allocated in XDR memory */
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=
rsx_fifo_cmd_buf = kmalloc(RSX_FIFO_CMD_BUF_SIZE, GFP_KERNEL);
encrypted:
if (!rsx_fifo_cmd_buf) {
00000000: c6 f6 f9 54 ce 90 e0 5e 2b 3b e4 1e 24 92 90 b2 |...T...^+;..$...|
printk(KERN_INFO"could not allocate FIFO command buffer\n");
decrypted:
res = -ENOMEM;
00000000: 01 23 45 67 89 ab cd ef 97 e6 61 8b d1 69 3e a0 |.#Eg......a..i>.|
goto fail;
</pre>
}


====MKB v9====
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;
}


<pre>
res = lv1_gpu_context_allocate(rsx_mem_handle, 0,
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
&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 */


=MKB=
rsx_fifo_cmd_buf_lpar = ps3_mm_phys_to_lpar(__pa(rsx_fifo_cmd_buf));
type:
0x00031003
version:
0x00000009


MKB U masks and UVs: 520
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;
}


=applying subset-difference=
/* map RSX FIFO control registers */
index: 2
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


=applying device key=
rsx_fifo_ctrl = (struct rsx_fifo_ctrl *) ioremap(rsx_fifo_ctrl_lpar, RSX_FIFO_CTRL_SIZE);
index: 244
if (!rsx_fifo_ctrl) {
UV: 0x00000100
printk(KERN_INFO"could not map FIFO control\n");
U mask: 0xff800000
res = -ENXIO;
V mask: 0xfffffe00
goto fail_free_gpu_mem;
device key:
}
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|


processing key:
/* PUT and GET offsets are in RSX address space */
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|


C value:
res = lv1_gpu_context_attribute(rsx_ctx_handle, 0x1,
00000000: a4 5a c6 87 43 49 70 bb bf 0c 22 52 83 9e 2a f6 |.Z..CIp..."R..*.|
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;
}


media key:
/* copy FIFO commands to FIFO command buffer */
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=
memcpy(rsx_fifo_cmd_buf, rsx_fifo_prg, sizeof(rsx_fifo_prg));
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====
printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);


<pre>
/* kick FIFO */
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=
rsx_fifo_ctrl->put = RSX_GPU_IOIF + sizeof(rsx_fifo_prg);
type:
0x00031003
version:
0x0000000a


MKB U masks and UVs: 522
/* poll until RSX is done processing FIFO commands */


=applying subset-difference=
timeout = 100;
index: 3
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


=applying device key=
while (timeout--) {
index: 244
if (rsx_fifo_ctrl->get == rsx_fifo_ctrl->put)
UV: 0x00000100
break;
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:
msleep(1);
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
}


C value:
printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);
00000000: d4 77 dd 1a 8a 5c 6d d1 dd 31 2d af f7 d3 14 fa |.w...\m..1-.....|


media key:
if (rsx_fifo_ctrl->get != rsx_fifo_ctrl->put) {
00000000: 38 32 2b 3c 61 b0 35 b4 52 89 84 59 f4 7a 76 e6 |82+<a.5.R..Y.zv.|
printk(KERN_INFO"FIFO command buffer timeout\n");
res = -ENXIO;
goto fail_unmap_fifo_ctrl;
}


=MKB verify media key data=
return 0;
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====
fail_unmap_fifo_ctrl:


<pre>
iounmap(rsx_fifo_ctrl);
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
fail_free_gpu_mem:


=applying subset-difference=
lv1_gpu_memory_free(rsx_mem_handle);
index: 3
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


=applying device key=
fail_free_fifo_cmd_buf_mem:
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:
kfree(rsx_fifo_cmd_buf);
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|


C value:
fail:
00000000: 89 75 89 e6 6f 4a de 95 11 32 57 6a cb 99 dd 69 |.u..oJ...2Wj...i|


media key:
return res;
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:
* ps3rsx_exit
00000000: 8d 43 fd f2 15 fa 58 78 64 db 25 46 62 ab 02 30 |.C....Xxd.%Fb..0|
*/
decrypted:
static void __exit ps3rsx_exit(void)
00000000: 01 23 45 67 89 ab cd ef e6 1c bf 98 45 82 64 d9 |.#Eg........E.d.|
{
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");
</pre>
</pre>


====MKB v14====
====Test====


<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_v14.inf
# insmod ./ps3rsx.ko
# dmesg


=MKB=
GET offset (0x0e000000) PUT offset (0x0e000000)  # GET and PUT offsets before kicking FIFO
type:
GET offset (0x0e00000c) PUT offset (0x0e00000c)  # GET and PUT offsets after kicking FIFO
0x00031003
</pre>
version:
0x0000000e


MKB U masks and UVs: 526
As you see, RSX processed our FIFO commands :)


=applying subset-difference=
=BD Drive=
index: 6
Crossreference: [http://portal.gitbrew.org/wikibrew/PS3:HvReverseEngineering#BD_Drive gitbrew.org::HV#BD Drive] <br />
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:
==Profile==
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|


C value:
* BD profile can be read with '''GET PROFILE''' device command or SCSI command '''GET CONFIGURATION'''
00000000: 8c 7e 31 e8 15 17 7e c3 2c 67 b7 cc 87 e9 39 c3 |.~1...~.,g....9.|


media key:
===Profile Table===
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=
{| class="wikitable"
encrypted:
|-
00000000: 20 03 8c 70 7d ab d0 6f ba 86 39 f0 31 26 86 5f | ..p}..o..9.1&._|
! Profile !! Description
decrypted:
|-
00000000: 01 23 45 67 89 ab cd ef 27 9f e5 35 0b df 3d a5 |.#Eg....'..5..=.|
| 0x0 || No Current Profile
</pre>
|-
| 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)
|}


====MKB v15====
==Buffer==


<pre>
* BD drive has several buffers associated with internal flash
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
* 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===
 
{| class="wikitable"
|-
! 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)
|}


=MKB=
===HRL Buffer===
type:
0x00031003
version:
0x0000000f


MKB U masks and UVs: 527
* 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 !!!'''


=applying subset-difference=
==Device Commands==
index: 6
UV: 0x00000248
U mask: 0xfffffe00
V mask: 0xfffffff0


=applying device key=
===Get Profile (0x11)===
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:
* BD profile can be read with LV1 call '''lv1_send_storage_device_command''' and command '''0x11'''
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|
* 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'''


C value:
===Auto Request Sense Mode On/Off (0x30)===
00000000: 75 da 59 cf 0d c2 c0 95 86 fc 6b 8e 2e e9 cc 85 |u.Y.......k.....|


media key:
* LV1 expects a 4 byte value: 0x0 - On, 0x1 - Off
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=
==SCSI Commands==
encrypted:
 
00000000: 8d d2 69 e0 b7 6a 44 53 03 ad ef 58 44 fc a7 d7 |..i..jDS...XD...|
===Get Configuration===
decrypted:
00000000: 01 23 45 67 89 ab cd ef ff 6a 7d c3 17 bb 19 11 |.#Eg.....j}.....|
</pre>
====MKB v16====


Getting the profile of a BD movie disc:
<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_v16.inf
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00
SCSI Status: Good


=MKB=
Sense Information:
type:
sense buffer empty
0x00031003
version:
0x00000010


MKB U masks and UVs: 531
Received 8 bytes of data:
00    00 00 00 38 00 00 00 40                            ...8...@ 


=applying subset-difference=
# 0x40 means BD-ROM
index: 5
</pre>
UV: 0x00000248
U mask: 0xfffffe00
V mask: 0xfffffff0


=applying device key=
Getting the profile of a PS3 game disc:
index: 28
<pre>
UV: 0x00000280
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00
U mask: 0xfffffe00
SCSI Status: Good
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:
Sense Information:
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|
sense buffer empty


C value:
Received 8 bytes of data:
00000000: f8 49 9b d1 32 f9 6e 8d 33 98 35 a8 54 80 d9 fe |.I..2.n.3.5.T...|
00    00 00 00 38 00 00 ff 71                            ...8...q
 
media key:
# 0x71 means PS3 BD-ROM
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>
</pre>


====MKB v17====
===Get SS Key===


<pre>
* By SCSI standard undocumented parameters are used
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
* '''SCSI Report Key''' command with '''key format 0x3''' and '''key class 0xe0'''
* 8 bytes are returned by BD drive
* Used by VSH


=MKB=
Test with PS3 game disc:
type:
<pre>
0x00031003
# sg_raw -r 8 /dev/sr0 a4 00 00 00 00 00 00 e0 00 08 03 00
version:
SCSI Status: Good
0x00000011


MKB U masks and UVs: 540
Sense Information:
sense buffer empty


=applying subset-difference=
Received 8 bytes of data:
index: 14
00    00 06 00 00 00 00 00 04                            ........       
UV: 0x00000308
</pre>
U mask: 0xffffff00
V mask: 0xfffffff0


=applying device key=
===Eject Media===
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:
<pre>
00000000: 46 5f a8 be 82 85 09 01 4d 05 d2 fc ce ff 35 d2 |F_......M.....5.|
sg_raw /dev/sr0 0x1b 00 00 00 02 00
</pre>


C value:
===Load Media===
00000000: 01 f7 54 0b 34 e8 c1 ce 63 8d ea fa bc ce 6e 7b |..T.4...c.....n{|


media key:
<pre>
00000000: ef 63 4e a8 ca 06 d1 6a c7 21 65 1b 18 b3 04 c6 |.cN....j.!e.....|
sg_raw /dev/sr0 0x1b 00 00 00 03 00
 
=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>
</pre>


====MKB v18====
===Mode Select 10===


<pre>
====Enable Buffer Write====
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=
* Uses '''PF 0x1''', '''SP 0x0''' and '''parameter list length 0x10'''
type:
* Uses the following parameter list: '''0x00 0x0e 0x00 0x00 0x00 0x00 0x00 0x00 0x2d 0x6 <buffer id> 0x00 0x00 0x00 0x00 0x00'''
0x00031003
* '''Enables writing to BD drive flash, e.g. to HRL buffer !!!'''
version:
0x00000012


MKB U masks and UVs: 543
Test with sg3-utils which enables write to HRL buffer:
<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
</pre>


=applying subset-difference=
===Write Buffer===
index: 17
UV: 0x00000320
U mask: 0xffffff00
V mask: 0xffffffc0


=applying device key=
* Used e.g. by Update Manager to send BD firmware to BD drive
index: 21
* '''Mode 0x5 (Download microcode and save)''' is used e.g. to write HRL to BD drive flash
UV: 0x00000340
* '''Mode 0x7 (Download microcode with offsets and save)''' is used e.g. to write BD firmware to BD drive flash
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:
==AACS==
00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..|


C value:
===AACS SPU Module===
00000000: 7a 8f 03 41 27 c4 86 58 05 37 3a 90 de f8 de 26 |z..A'..X.7:....&|


media key:
* BD player on GameOS uses '''AacsModule.spu.isoself''' (/dev_flash/bdplayer) to perform AACS authentication
00000000: e3 ed cd b4 59 b4 12 d4 ae f9 4d 8e 78 7a cd 7d |....Y.....M.xz.}|
* 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.


=MKB verify media key data=
====Communication====
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====
* 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====
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=
* The SPU module supports max '''0x78''' commands but not all are implemented
type:
* 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.
0x00031003
version:
0x00000013


MKB U masks and UVs: 544
=====Read 4 Bytes from XDR Buffer (0x2)=====


=applying subset-difference=
* It just reads 4 bytes of data from the XDR buffer passed to the SPU module.
index: 17
UV: 0x00000320
U mask: 0xffffff00
V mask: 0xffffffc0


=applying device key=
=====Set KCD (0x1e)=====
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:
* Sends KCD (Key Conversion Data) to the SPU module.
00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..|
* KCD is encrypted with the Bus Key which was established previously by AACS authentication.


C value:
=====Init AES_H (0x34)=====
00000000: b9 0b 55 d1 18 3c cc 80 20 1c 9f 26 c3 58 27 18 |..U..<.. ..&.X'.|


media key:
* Initializes AES_H hashing function.
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=
=====Calculate AES_H 1 (0x35)=====
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====
* Calculates AES_H hash of the data stored in XDR buffer.


<pre>
=====Calculate AES_H 2 (0x36)=====
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=
* Calculates AES_H hash of the data stored in XDR buffer.
type:
0x00031003
version:
0x00000014


MKB U masks and UVs: 544
=====Generate Host Nonce (0x3c)=====


=applying subset-difference=
* Generates a nonce which is returned in command '''0x3d'''
index: 19
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8


=applying device key=
=====Get Host Nonce and Certificate (0x3d)=====
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:
* The data returned by this command is of size '''0x14 (Nonce) + 0x5c (Host Certificate)'''
00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k|
* 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.


C value:
=====Set Drive Nonce and Certificate (0x3e)=====
00000000: 10 9f f1 69 36 07 7d 7e ad 8f d2 1a 28 c5 09 ed |...i6.}~....(...|


media key:
* Stores BD drive nonce and certificate in local memory of SPU
00000000: dc 9f 08 f7 cb 1b f8 c4 cf 96 4e 96 df 23 56 58 |..........N..#VX|


=MKB verify media key data=
=====Verify Drive Certificate (0x3f)=====
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====
=====Set Drive Key (0x40)=====


<pre>
=====Sign Host Key (0x44)=====
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=
=====Get Host Key (0x45)=====
type:
0x00031003
version:
0x00000015


MKB U masks and UVs: 552
=====Calculate Bus Key (0x46)=====


=applying subset-difference=
=====Set Volume ID (0x47)=====
index: 19
UV: 0x00000384
U mask: 0xffffff80
V mask: 0xfffffff8


=applying device key=
* Sends volume id and its MAC to the SPU module
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:
=====Calculate Volume ID MAC (0x48)=====
00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k|


C value:
* Calculates MAC of the passed volume id
00000000: c0 0c fa bf f0 fe f2 32 77 19 db c4 d8 f8 60 c9 |.......2w.....`.|


media key:
=====Verify Volume ID MAC (0x49)=====
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=
* Verifies MAC of the passed volume id
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====
=====Set PMSN (0x4a)=====


<pre>
* Sends PMSN and its MAC to the SPU module
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=
=====Calculate PMSN MAC (0x4b)=====
type:
0x00031003
version:
0x00000017


MKB U masks and UVs: 556
* Calculates MAC of the passed PMSN


=applying subset-difference=
=====Verify PMSN (0x4c)=====
index: 17
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8


=applying device key=
* Verifies MAC of the passed PMSN
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:
=====Set Media ID (0x4d)=====
00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr|


C value:
* Sends media id and its MAC to the SPU module
00000000: f0 81 d4 93 aa b5 01 1a a7 ff 8e 18 8a 48 8a 2d |.............H.-|


media key:
=====Calculate Media ID MAC (0x4e)=====
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=
* Calculates MAC of the passed media id
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====
=====Verify Media ID MAC (0x4f)=====


<pre>
* Verifies MAC of the passed media id
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=
=====Unknown (0x54)=====
type:
0x00031003
version:
0x00000019


MKB U masks and UVs: 564
=====Verify Host/Drive Revocation (0x55)=====


=applying subset-difference=
* BD player stores HRL/DRL list entries in XDR buffer and passes it to the SPU module for verification
index: 13
UV: 0x00000384
U mask: 0xffffffe0
V mask: 0xfffffff8


=applying device key=
=====Terminate Session (0xfefefeff)=====
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:
* AACS SPU module runs and processes commands as long as you need
00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr|
* After a command is complete, the SPU module waits for the next command
* This command terminates the current session and stops SPU module


C value:
===Drive Revocation List (DRL)===
00000000: 19 62 23 7d 81 01 c2 55 2f 36 20 1b 3e 69 40 10 |.b#}...U/6 .>i@.|


media key:
* SHA1 hash is encrypted/decrypted by '''SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)'''
00000000: 70 b5 9f 35 86 5d 18 73 bb 80 c3 2b f7 41 f6 14 |p..5.].s...+.A..|
* SHA1 hash is read with '''VTRM service 0x2005 (Retrieve)'''
* SHA1 hash is written with '''VTRM service 0x2003 (Store With Update)'''


=MKB verify media key data=
===Content Revocation List (CRL)===
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===
* 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)'''


* SCSI Specification: [http://www.13thmonkey.org/documentation/SCSI/]
===Host Revocation List (HRL)===
* 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==
* Stored in BD drive flash
* It can be read/written with SCSI commands '''READ/WRITE BUFFER'''. Yeah, it can be written too :D


===CSS SPU Module===
====Read HRL from BD Drive Flash====


* DVD player on GameOS uses '''CssModule.spu.isoself''' (/dev_flash/bdplayer) to perform CSS authentication
* 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'''


====Commands====
====Empty HRL====


* The SPU module supports max '''0x25''' 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.
# 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 


=====Generate Host Challenge Key (0x3)=====
# byte 0x21 at offset 0xc means Record Type HRL


* Generates '''0x10''' bytes of '''host challenge key'''
# as you see this HRL is empty


=====Set Drive Key1 (0x4)=====
</pre>


* Sends '''key1''' of size '''0x5''' returned by DVD drive to the SPU module
====Current HRL====


=====Set Drive Challenge Key (0x5)=====
<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


* Sends '''0x10''' bytes of '''drive challenge key''' to the SPU module
# the current HRL is NOT empty and is from MKBv9 because the only BD movie i played on my PS3 has MKBv9
</pre>


=====Calculate Host Key2 (0x6)=====
===PS3 BD Player Host Certificate===


* Calculates '''key2''' of size '''0x5'''
<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


=====Get Host Key2 (0x7)=====
# Host ID is 0xffff80000039
</pre>


* Returns '''key2''' of size '''0x5'''
===PS3 BD Player Host Private Key===


=====Set Disc Key (0x8)=====
<pre>
$ 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>


* Sends Disc Key block of size '''0x800''' to the SPU module
===AACS Processing Keys===


=====Decrypt Sector (0xc)=====
====MKB v1====


* Decrypts the passed sector
<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


===CSS Salt===
=MKB=
 
type:
* It's NOT in clear text in the SPU module, it's obfuscated by xoring '''0xDEAF''' (SONY employees have a sense of humor).
0x00031003
* There are 2 bytes for every salt byte
version:
0x00000001


Obfuscated:
MKB U masks and UVs: 514
<pre>
71 ED 3F A3 DA FE E4 94  40 8C
</pre>


Clear text:
=applying subset-difference=
<pre>
index: 0
F4  10  45  A3  E2
UV: 0x00000001
</pre>
U mask: 0xff800000
V mask: 0xfffffffe


===PS3 DVD Player Key Index===
=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..>!.|


<pre>
processing key:
0x69
00000000: 09 f9 11 02 9d 74 e3 5b d8 41 56 c5 63 56 88 c0 |.....t.[.AV.cV..|
</pre>


===Documentation===
C value:
00000000: cb 06 90 db e6 54 55 7b 12 62 aa d7 89 f4 9d 92 |.....TU{.b......|


* The Content Scrambling System (CSS): [http://www.tinyted.net/eddie/css_auth.html]
media key:
00000000: b4 6c 48 5e f7 51 ae 29 ef 87 bc 58 28 f3 2a 8d |.lH^.Q.)...X(.*.|


* Cryptanalysis of Contents Scrambling System: [http://www.cs.cmu.edu/~dst/DeCSS/FrankStevenson/analysis.html]
=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............|
</pre>


* Cryptography in Home Entertainment: [http://www.math.ucsd.edu/~crypto/Projects/MarkBarry/index.htm]
====MKB v3====


* Patching DVD Firmware: [http://xvi.rpc1.org/Patching%20DVD%20firmware.pdf]
<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


==CPRM==
=MKB=
type:
0x00031003
version:
0x00000003


===Commands===
MKB U masks and UVs: 528


* The SPU module supports '''0x13''' commands.
=applying subset-difference=
index: 14
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


===4C Secret Constant (S-Box)===
=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..>!.|


===Documentation===
processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|


* Cryptomeria C2 Specification: [http://edipermadi.files.wordpress.com/2008/08/cryptomeria-c2-spec.pdf]
C value:
00000000: 0a b7 33 82 85 62 91 d1 91 4a 95 9e 36 18 c7 a1 |..3..b...J..6...|


* Cryptoanalysis of C2: [http://caislab.kaist.ac.kr/lecture/2010/spring/cs548/advanced/A05.pdf]
media key:
00000000: 6e da eb d4 88 aa 38 58 74 26 35 fd fd 36 66 d5 |n.....8Xt&5..6f.|


==Remarrying BD Drive on OtherOS++==
=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>


===fdm_spu_module.self===
====MKB v4====


* This SPU module can create either P- or S-Block which are sent to BD drive
<pre>
* '''EID2''' is passed to the SPU module
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
* 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====
=MKB=
type:
0x00031003
version:
0x00000004


{| class="wikitable"
MKB U masks and UVs: 526
|-
! Type !! Description !! BD Drive Buffer ID
|-
| 0x1 || P-Block || 0x2
|-
| 0x2 || S-Block || 0x3
|}


===Remarrying===
=applying subset-difference=
index: 12
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


====Preparations====
=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..>!.|


* You will need '''ps3dm-utils''' and '''sg3-utils'''
processing key:
* Dump your '''EID2''' from '''flash''' or with '''ps3dm-utils'''
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
* First create P- and S-Blocks from your EID2 with kernel module '''fdm_spu_module'''


====P-Block====
C value:
00000000: bf 71 0c 8b 46 a0 24 d8 f0 3a a1 26 37 9d fb fc |.q..F.$..:.&7...|


Decrypted P-Block (and EID4) contains region settings (see below)
media key:
00000000: ef 18 c0 dd bf 02 32 a1 2f 57 f7 65 79 2c 1c 58 |......2./W.ey,.X|


In decrypted P-Block(bytes 0x30 and 0x32) and in EID4(first byte) these bytes match [[Product Code]]:
=MKB verify media key data=
{| class="wikitable sortable" style="font-size:small; border:2px ridge #999999;"
encrypted:
|-
00000000: 54 85 08 a9 6a 70 2a c9 32 e3 74 a6 55 78 6c 01 |T...jp*.2.t.Uxl.|
! Hex !! bitflag !! [[Product Code]] !! Console Type !! Remarks
decrypted:
|-
00000000: 01 23 45 67 89 ab cd ef da 90 cf 2a e5 b2 6c 45 |.#Eg.......*..lE|
| 0xFF || '''11111111''' || {{TID80}} || No BD playback on that [[Product Code]]
</pre>
|-
| 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]]
|-
|}


=====Creating=====
====MKB v7====


=====Sending to BD Drive=====
<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_v7.inf


====S-Block====
=MKB=
type:
0x00031003
version:
0x00000007


=====Creating=====
MKB U masks and UVs: 526


=====Sending to BD Drive=====
=applying subset-difference=
index: 7
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


====HRL====
=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..>!.|


=====Empty HRL=====
processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|


=====Sending to BD Drive=====
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.|


=VTRM=
=MKB verify media key data=
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#VTRM gitbrew.org::HV#VTRM] <br />
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>


==VTRM Services==
====MKB v8====


===Store With Update (0x2003)===
<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


* Used by GameOS BD player to update DRL/CRL hashes
=MKB=
type:
0x00031003
version:
0x00000008


===Retrieve (0x2005)===
MKB U masks and UVs: 523


* '''Product mode''' is NOT required
=applying subset-difference=
* '''0x40''' bytes of data are read from NOR flash, decrypted by SYSCON and returned to the caller
index: 4
* Used e.g. by GameOS BD player to read SHA1 hashes of DRL and CRL
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00


====DRL and CRL Hashes====
=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..>!.|


* Written by GameOS BD player during DRL/CRL update
processing key:
* Read by GameOS BD player
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
* Hashes are stored encrypted on NOR flash
* Encryption/decryption is done by SYSCON (SYSCON Manager)


Test with ps3dm-utils:
C value:
<pre>
00000000: 73 2d 10 bd f8 b4 87 e2 86 a6 d5 3a 6d db 69 15 |s-.........:m.i.|
# mount dev_flash3


glevand@debian-hdd:~/ps3dm-utils$ sudo mount /dev/ps3vflashe /mnt
media key:
00000000: dd 46 d4 0d 26 54 5a ce 6c 59 0c 65 b7 2b 3a 9f |.F..&TZ.lY.e.+:.|


# DRL SHA1 hash
=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>


glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/drl/DRL1
====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
8f0652bc6162a240362f90f1b2e5405bb82ee502  /mnt/data-revoke/drl/DRL1
 
 
# CRL SHA1 hash
# CRL SHA1 hash
 
 
glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/crl/CRL1  
glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/crl/CRL1  
96791f41f9a76f4d895dd5820db108ec03d19250  /mnt/data-revoke/crl/CRL1
96791f41f9a76f4d895dd5820db108ec03d19250  /mnt/data-revoke/crl/CRL1
 
 
# Retrieve DRL and CRL SHA1 hashes from VTRM
# Retrieve DRL and CRL SHA1 hashes from VTRM
# DRL hash is first and then follows CRL hash
# 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
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
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
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
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>
</pre>
 
 
===Backup Flash (0x2012)===
===Backup Flash (0x2012)===
 
 
* Requires enabled '''product mode''' or else service returns always error '''0x5'''
* Requires enabled '''product mode''' or else service returns always error '''0x5'''
* Reads and returns data from NOR flash beginning at NOR flash offset '''0xec0000'''
* Reads and returns data from NOR flash beginning at NOR flash offset '''0xec0000'''
 
 
Test with ps3dm-utils:
Test with ps3dm-utils:
<pre>
<pre>
# enable product mode
# enable product mode
 
 
# ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0
# ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0
/dev/ps3dmproxy: SS retval 0
/dev/ps3dmproxy: SS retval 0
 
 
# ps3dm_vtrm /dev/ps3dmproxy  backup_flash 0 0x200 | hexdump -C
# 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ÿÿÿÿÿÿÿÿÿÿÿÿ|
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  |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|
*
*
00000200
00000200
 
 
# dd if=/dev/ps3nflasha bs=1 count=$((0x100)) skip=$((0xec0000)) | hexdump -C
# 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ÿÿÿÿÿÿÿÿÿÿÿÿ|
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  |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|
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>


===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>


=Revoke List=
=Revoke List=
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#Revoke_List gitbrew::Revoke List]<br />
Crossreference: [http://portal.gitbrew.org/wikibrew/PS3:HvReverseEngineering#Revoke_List gitbrew::Revoke List]<br />


==LPAR 1 System Call 0x1004A==
==LPAR 1 System Call 0x1004A==
Line 10,750: Line 11,967:
lv1_destruct_logical_spe (0x00000000)
lv1_destruct_logical_spe (0x00000000)
</pre>
</pre>
{{Reverse engineering}}<noinclude>[[Category:Main]]</noinclude>
Please note that all contributions to PS3 Developer wiki are considered to be released under the GNU Free Documentation License 1.2 (see PS3 Developer wiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

To protect the wiki against automated edit spam, we kindly ask you to solve the following hCaptcha:

Cancel Editing help (opens in new window)