Hypervisor Reverse Engineering: Difference between revisions
(→HSPRG) |
|||
(206 intermediate revisions by 26 users not shown) | |||
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> | |||
---- | |||
= Difference between Debug Firmware HV and Retail HV = | |||
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. | |||
[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 14: | Line 19: | ||
LPAR = Logical Partition | LPAR = Logical Partition | ||
lpar1 starts at 0x<unknown>, and | lpar1 starts at 0x<unknown>, and it's believed to be the memory space where lv1 stores its variables, flags and other data. | ||
lpar2 starts at 0x80000000000 and it's | lpar2 starts at 0x80000000000 and it's believed to be the memory space where lv2 stores its variables, flags and other data. | ||
<br> | <br> | ||
Line 104: | Line 109: | ||
== HV call == | == HV call == | ||
''editorial note: The table listed here was moved/merged to a seperate page : [[HV Syscalls]]'' | |||
=== Initializing HV Call Table === | === Initializing HV Call Table === | ||
Line 239: | Line 166: | ||
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 253: | Line 180: | ||
There are 2 system call tables in HV. The first one stores system calls 0 - 36. The second one stores system calls 0x10000 - 0x100FF. | There are 2 system call tables in HV. The first one stores system calls 0 - 36. The second one stores system calls 0x10000 - 0x100FF. | ||
== System call table 0 - 36 == | == UX System call table 0 - 36 == | ||
0x0035FAE8 (3.15) | 0x0035FAE8 (3.15) | ||
Line 260: | Line 187: | ||
=== System call numbers === | === System call numbers === | ||
0x0 - void eosh(void) //end_of_signal_handling(void) | |||
0x1 - getpid(void) | 0x1 - pid_t getpid(void) | ||
0x2 - getppid(void) | 0x2 - pid_t getppid(void) | ||
0x3 - fork(void) | 0x3 - pid_t fork(void) | ||
0x4 - exit | 0x4 - void exit(int status) | ||
0x5 - | 0x5 - void execv(const char *path, char *const argv[]) | ||
0x6 - wait(status) | 0x6 - void wait(int *status) | ||
0x7 - open( | 0x7 - int open(const char *path, int flags) | ||
0x8 - close(fd) | 0x8 - void close(int fd) | ||
0x9 - read | 0x9 - ssize_t read(int fd, void *buf, unsigned int nbyte) | ||
0xA - write | 0xA - ssize_t write(int fd, const void *buf, unsigned int nbyte) | ||
0xB - | 0xB - void lseek(int fd, long offset, int whence) | ||
0xC - unlink( | 0xC - unlink(const char *path) | ||
0xD - signal | 0xD - void signal(int sig, void *func(int sig)) | ||
0xE - kill(pid, | 0xE - int kill(int pid, int signal_type) | ||
0xF - brk | 0xF - int brk(void *) | ||
0x10 - socket(af, type, protocol) (supports only address family 0x1F, type 0x0 and protocol 0x0) | 0x10 - int socket(int af, int type, int protocol) (supports only address family 0x1F, type 0x0 and protocol 0x0) | ||
0x11 - bind | 0x11 - int bind(int sockfd , const sockaddr *addr, unsigned int addrlen) | ||
0x12 - listen( | 0x12 - int listen(int sockfd, int backlog) | ||
0x13 - accept | 0x13 - int accept(int sockfd, sockaddr *addr, unsigned int *addrlen) | ||
0x14 - connect | 0x14 - int connect(int sockfd, const sockaddr *serv_addr, unsigned int addrlen) | ||
0x15 - | 0x15 - void putchar(int c) | ||
0x16 - pause(void) | 0x16 - int pause(void) | ||
0x17 - sleep(seconds) | 0x17 - int sleep(unsigned int seconds) | ||
0x18 - mmap(addr, size, prot, flags, fd, offset) | 0x18 - int mmap(void *addr, unsigned long size, int prot, int flags, int fd, long offset, void *mapped_addr) | ||
0x19 - munmap | 0x19 - int munmap (void *addr, unsigned long size) | ||
0x1A - | 0x1A - int chdir(const char *path) | ||
0x1B - | 0x1B - void getchar(char *c) | ||
0x1C - map_pages (used for alloc) | 0x1C - map_pages(...) (used for alloc) | ||
0x1D - unmap_pages (used for free) | 0x1D - unmap_pages(...) (used for free) | ||
0x1E - select | 0x1E - select(...) | ||
0x1F - getcwd | 0x1F - getcwd(...) | ||
0x20 - | 0x20 - Not used | ||
0x21 - alarm | 0x21 - alarm(...) | ||
0x22 - ioctl | 0x22 - ioctl(...) | ||
0x23 - | 0x23 - pme_memalign(...) | ||
0x24 - | 0x24 - ? | ||
== System call table 0x10000 - 0x100FF == | == PMI System call table 0x10000 - 0x100FF == | ||
0x0035DE78 (3.15) | 0x0035DE78 (3.15) | ||
Line 341: | Line 269: | ||
=== System call numbers === | === System call numbers === | ||
0x10000 - | 0x10000 - allocate_memory(LPAR id, size, log2 of page size, ?, ?) / construct_memory_segment | ||
0x10001 - query_logical_partition_address_region_info | |||
0x10002 - translate_logical_partition_to_physical_address(LPAR id, LPAR address, physical addr) | |||
0x10003 - map_physical_address_region | |||
0x10004 - unmap_physical_address_region | |||
0x10005 - construct_logical_pu | 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 362: | Line 320: | ||
0x10026 - get_logical_partition_info | 0x10026 - get_logical_partition_info | ||
0x10027 - read_privilege_set | |||
0x10028 - modify_privilege_set | |||
0x10029 - get_remote_file_size_long_name | |||
0x1002A - read_remote_file_long_name | |||
0x1002B - write_remote_file_long_name | |||
0x1002C - construct_scheduling_table | 0x1002C - construct_scheduling_table | ||
Line 367: | Line 335: | ||
0x1002D - set_scheduling_slot | 0x1002D - set_scheduling_slot | ||
0x1002E - load_scheduling_table | |||
0x10032 - poweroff | |||
0x10033 - get_remote_file_name | |||
0x10034 - allocate_cp_channel | |||
0x10035 - release_cp_channel | |||
0x10036 - power_down | |||
0x10037 - ? | |||
0x10038 - ? | |||
0x10039 - ? | |||
0x10040 - construct_spe_type_1(SPE id, shaddow_addr) / construct_logical_spu | |||
0x1004B - disable_spe_loading | 0x10041 - destruct_spe(SPE id) / destruct_logical_spu | ||
0x10042 - decrypt_lv2_self(spe id, LPAR auth id, SELF file image ptr, LPAR memory address) | |||
0x10043 - load_spe_module(spe id, SCE module ptr, arg1, arg2, arg3, arg4) | |||
0x10044 - disable_spe_execution | |||
0x10045 - read_spu_puint_mb(unsigned long spu_id, unsigned long msg) | |||
0x10046 - read_spe_problem_state_register(spe id, register offset, value) / read_spu_problem_state_area_register | |||
0x10047 - write_spe_problem_state_register(spe id, register offset, value) / write_spu_problem_state_area_register | |||
0x1004A - install_revoke_list | |||
0x1004B - disable_spe_loading | |||
0x1004C - install_access_control_table? | |||
0x1004D - get_storage_status? | |||
0x1004E - get_region_table_bits? | |||
0x1004F - commit_region_update? | |||
0x10050 - abort_region_update? | |||
0x10051 - set_storage_tampered? | |||
0x10053 - pmi_set_guest_os_mode | 0x10053 - pmi_set_guest_os_mode | ||
0x10081 - | 0x1007F - pause | ||
0x10080 - get_total_execution_time | |||
0x10081 - reset | |||
0x10083 - construct_logical_rsx | |||
0x10084 - construct_virtual_uart(LPAR id, VUART id, VUART data buffer size) | 0x10084 - construct_virtual_uart(LPAR id, VUART id, VUART data buffer size) | ||
0x10085 - destruct_virtual_uart(LPAR id, VUART id) | 0x10085 - destruct_virtual_uart(LPAR id, VUART id) | ||
0x10086 - establish_virtual_uart_channel | |||
0x10088 - RSX_syscall_10088(LPAR id) | 0x10088 - RSX_syscall_10088(LPAR id) | ||
Line 411: | Line 415: | ||
0x100C2 - modify_repository_node_value(LPAR id) | 0x100C2 - modify_repository_node_value(LPAR id) | ||
0x100C3 - | 0x100C3 - remove_repository_node(LPAR id) | ||
= Process = | = Process = | ||
Line 419: | Line 423: | ||
HV supports only 32 processes simultaneously. The number of processes currently running in HV is stored at address 0x0035EA54 (3.15) and 0x00357E3C (2.60). | HV supports only 32 processes simultaneously. The number of processes currently running in HV is stored at address 0x0035EA54 (3.15) and 0x00357E3C (2.60). | ||
The process table is an array of 32 process table entries. | The process table is an array of 32 process table entries. | ||
0x0036C930 (4.30) | |||
0x0036C8B0 (4.21) | |||
0x00365458 (4.11) | |||
0x0035F8D0 (3.55) | |||
0x0035C550 (3.41) | |||
0x0035E850 (3.15) | 0x0035E850 (3.15) | ||
Line 429: | Line 443: | ||
offset 0x0 - process status ? (8 bytes) | offset 0x0 - process status ? (8 bytes) | ||
offset 0x8 - pointer to Process object | offset 0x8 - pointer to Process object | ||
== create_new_proc == | == create_new_proc == | ||
Line 556: | Line 570: | ||
*0x000A9870 (PID 6) | *0x000A9870 (PID 6) | ||
*0x00084B80 (PID 9) | *0x00084B80 (PID 9) | ||
In JIG 2.43: | |||
*(PID3) <- ss_server3 | |||
*(PID4) <- ss_sc_init_pu | |||
*(PID5) <- ss_server2 | |||
*(PID6) <- ss_server1 | |||
*(PID7) <- factory_data_mngr_server | |||
*(PID8) <- updater_frontend | |||
(see [http://pastie.org/pastes/9407461/text?key=f6bk5lof0g4bgeu6xrn5ua this]) | |||
= PThread = | = PThread = | ||
Line 647: | Line 671: | ||
== Member variables == | == Member variables == | ||
offset | offset 0x0 - pointer to previous AddressProtectionDomain object | ||
offset | offset 0x8 - pointer to next AddressProtectionDomain object | ||
offset | offset 0x10 - poiinter to pointer to SLB entries | ||
offset | offset 0x18 - pointer to AddressSpace object that owns this object | ||
offset | offset 0x2A - pointer to previous ProtectionPage | ||
offset | offset 0x34 - pointer to next ProtectionPage | ||
offset | offset 0x40 - Mutex object | ||
= ProtectionPage = | = ProtectionPage = | ||
Line 816: | Line 840: | ||
=== vtable === | === vtable === | ||
0x003569F8 (3.15) | 0x003569F8 (3.15) | ||
== IOIF device file objects == | == IOIF device file objects == | ||
Line 952: | Line 976: | ||
== 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] | ||
== 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] | ||
= Buses = | = Buses = | ||
Line 1,002: | Line 1,026: | ||
=== vtable === | === vtable === | ||
0x352308 (3.15) | |||
=== Member variables === | === Member variables === | ||
Line 1,037: | Line 1,061: | ||
interrupt index = 8 | interrupt index = 8 | ||
The Gelic device is similar to the spider_net device from Toshiba. There are some differences with mmio initialization values within LV1 in comparison to the spider_net.c linux driver. | |||
Gelic defines: | |||
{| class="wikitable sortable" | |||
|- | |||
! DEFINE !! Value | |||
|- | |||
| GELIC_CKRCTRL_REGISTER || 0xFF0 | |||
|- | |||
| GELIC_CKRCTRL_STOP_VALUE || 0x00000105 | |||
|- | |||
| GELIC_CKRCTRL_RUN_VALUE || 0x1D7F0105 | |||
|- | |||
| GELIC_MACADDR_HIGH_REG || 0x500 | |||
|- | |||
| GELIC_MACADDR_LOW_REG || 0x504 | |||
|- | |||
|} | |||
=== MMIO regions === | === MMIO regions === | ||
Line 1,598: | Line 1,641: | ||
|- | |- | ||
| 41 | | 41 | ||
| EBUS (Flash | | EBUS (Flash StarShip) | ||
| 0x002814EC (3.15) | | 0x002814EC (3.15) | ||
|- | |- | ||
Line 1,662: | Line 1,705: | ||
*Before a storage region is accessed, HV checks access rights of the caller. | *Before a storage region is accessed, HV checks access rights of the caller. | ||
*Repository node '''ss.laid''' (LPAR | *Repository node '''ss.laid''' ([[Authority ID|LPAR Authority ID]]) is evaluated for this purpose. | ||
*If LPAR has a repository node '''ios.ata.region0.access''' (value doesn't matter) then the access rights check never fails. After System Manager sets ATA keys it removes this repository node from LPAR 1. If we add this repository node again or patch System Manager so it's not removed then we will be able to access all storage regions of all storage devices. | *If LPAR has a repository node '''ios.ata.region0.access''' (value doesn't matter) then the access rights check never fails. After System Manager sets ATA keys it removes this repository node from LPAR 1. If we add this repository node again or patch System Manager so it's not removed then we will be able to access all storage regions of all storage devices. | ||
*'''ALL storage accesses from LPAR 1 are allowed''' | *'''ALL storage accesses from LPAR 1 are allowed''' | ||
Line 1,715: | Line 1,758: | ||
*The storage subsystem is a storage device itself. | *The storage subsystem is a storage device itself. | ||
*It's a | *It's a pseudo device used to notify a LPAR when storage devices become e.g. ready. | ||
*Linux implements a loop and reads from this device and process notifications (adds new devices dynamically). | *Linux implements a loop and reads from this device and process notifications (adds new devices dynamically). | ||
Line 1,793: | Line 1,836: | ||
*The commands can be used with HV call '''lv1_storage_send_device_command'''. | *The commands can be used with HV call '''lv1_storage_send_device_command'''. | ||
*However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node '''ss.laid''' or also called '''LPAR | *However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node '''ss.laid''' or also called '''[[Authority ID|LPAR Authority ID]]'''. If this test fails then the command is NOT executed. | ||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
Line 2,046: | Line 2,089: | ||
*The commands can be used with HV call '''lv1_storage_send_device_command'''. | *The commands can be used with HV call '''lv1_storage_send_device_command'''. | ||
*However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node '''ss.laid''' or also called '''LPAR | *However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node '''ss.laid''' or also called '''[[Authority ID|LPAR Authority ID]]'''. If this test fails then the command is NOT executed. | ||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
Line 2,646: | Line 2,689: | ||
*The commands can be used with HV call '''lv1_storage_send_device_command'''. | *The commands can be used with HV call '''lv1_storage_send_device_command'''. | ||
*However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node '''ss.laid''' or also called '''LPAR | *However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node '''ss.laid''' or also called '''[[Authority ID|LPAR Authority ID]]'''. If this test fails then the command is NOT executed. | ||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
Line 2,687: | Line 2,730: | ||
|- | |- | ||
| 0x32 | | 0x32 | ||
| ATA | | ATA Standby Immediate | ||
|- | |- | ||
| 0x33 | | 0x33 | ||
Line 2,699: | Line 2,742: | ||
block size = 512 | block size = 512 | ||
*It's a | *It's a pseudo device. | ||
*'''This storage device redirects all requests to the region 1 of HDD storage device !!!''' | *'''This storage device redirects all requests to the region 1 of HDD storage device !!!''' | ||
Line 3,432: | Line 3,475: | ||
! Address of Data in HV Dump | ! Address of Data in HV Dump | ||
! Size of Data | ! Size of Data | ||
! Entry Id | |||
|- | |- | ||
| 0 | | 0 | ||
| | | lv1ldr | ||
| 0x0C150000 | | 0x0C150000 | ||
| 0x1E5CC | | 0x1E5CC | ||
| 0x01 | |||
|- | |- | ||
| 1 | | 1 | ||
Line 3,442: | Line 3,487: | ||
| 0x00011000 | | 0x00011000 | ||
| 0xE8D0 | | 0xE8D0 | ||
| 0x00 | |||
|- | |- | ||
| 2 | | 2 | ||
Line 3,447: | Line 3,493: | ||
| 0x00020000 | | 0x00020000 | ||
| 0x16DA0 | | 0x16DA0 | ||
| 0x02 | |||
|- | |- | ||
| 3 | | 3 | ||
Line 3,452: | Line 3,499: | ||
| 0x00055000 | | 0x00055000 | ||
| 0x12E44 | | 0x12E44 | ||
| 0x04 | |||
|- | |- | ||
| 4 | | 4 | ||
Line 3,457: | Line 3,505: | ||
| 0x00037000 | | 0x00037000 | ||
| 0x1DAE4 | | 0x1DAE4 | ||
| 0x03 | |||
|- | |- | ||
| 5 | | 5 | ||
Line 3,462: | Line 3,511: | ||
| 0x00068000 | | 0x00068000 | ||
| 0x860 | | 0x860 | ||
| 0x0C | |||
|- | |- | ||
| 6 | | 6 | ||
| | | QA Flag | ||
| 0x00069010 | | 0x00069010 | ||
| 0x8 | | 0x8 | ||
| 0x0F | |||
|- | |- | ||
| 7 | | 7 | ||
| | | QA Flag Token | ||
| 0x00069020 | | 0x00069020 | ||
| 0x50 | | 0x50 | ||
| 0x10 | |||
|- | |- | ||
| 8 | | 8 | ||
| | | Trace Level | ||
| 0x00069070 | | 0x00069070 | ||
| 0x8 | | 0x8 | ||
| 0x11 | |||
|} | |} | ||
Line 3,540: | Line 3,593: | ||
=== appldr === | === appldr === | ||
*'''appldr''' is used for decryption of SELFs | *'''appldr''' is used for decryption of SELFs or EDATs | ||
*HV call '''lv1_authenticate_program_segment''' loads '''appldr''' | *HV call '''lv1_authenticate_program_segment''' loads '''appldr''' | ||
Line 3,549: | Line 3,602: | ||
==== Loading appldr ==== | ==== Loading appldr ==== | ||
*64 bit memory address of ''' | *64 bit memory address of '''appldr''' is written into 32 bit SPU register '''SPU_In_Mbox''' | ||
*'''metldr''' is loaded | *'''metldr''' is loaded | ||
Line 3,888: | Line 3,941: | ||
*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 = | ||
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'''. | * It communicates with Guest OS through '''/proc/partitions/0/vuart/0 file'''. | ||
*GameOS accesses | * GameOS accesses AV Manager through '''syscalls 367 - 370'''. | ||
*PS2 Soft EMU accesses | * PS2 Soft EMU accesses AV Manager also. | ||
= System Manager (SM) = | * 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 | |||
* Mode is set by SYSCON | |||
* Disabling HDCP | |||
= System Manager (SM) = | |||
*System Manager (SM) is running in Process 9 of HV. | *System Manager (SM) is running in Process 9 of HV. | ||
Line 3,913: | Line 3,989: | ||
offset 0x90 - LPAR image path | offset 0x90 - LPAR image path | ||
offset 0x1C0 - LPAR ability (8 bytes) | offset 0x1C0 - LPAR ability (8 bytes) | ||
=== Types of System Manager === | === Types of System Manager === | ||
Line 4,373: | Line 4,449: | ||
| 0xA | | 0xA | ||
| 0x1B6 | | 0x1B6 | ||
| Makes a | | Makes a triple beep | ||
|- | |- | ||
| 0x29 | | 0x29 | ||
Line 4,385: | Line 4,461: | ||
| Makes a continuous beep | | Makes a continuous beep | ||
|} | |} | ||
field 1 seems relative to beep tone, as 0x25 sounds different | |||
=== Active System Managers in HV dump 3.15 === | === Active System Managers in HV dump 3.15 === | ||
Line 4,560: | Line 4,637: | ||
| 0x23 | | 0x23 | ||
| 0x2001 - 0x2017 | | 0x2001 - 0x2017 | ||
| Virtual TRM Manager | | [[Virtual_TRM_Manager|Virtual TRM Manager]] | ||
|- | |- | ||
| 0x3000 | | 0x3000 | ||
| 0x24 | | 0x24 | ||
| 0x3001 - 0x3003 | | 0x3001 - 0x3003 | ||
| Secure RTC | | [[Secure_RTC_Manager|Secure RTC]] | ||
|- | |- | ||
| 0x5000 | | 0x5000 | ||
| 0x23 | | 0x23 | ||
| 0x5001 - 0x500A | | 0x5001 - 0x500A | ||
| Storage Manager | | [[Storage_Manager|Storage Manager]] | ||
|- | |- | ||
| 0x6000 | | 0x6000 | ||
| 0x23 | | 0x23 | ||
| 0x6001 - 0x6011 | | 0x6001 - 0x6011 | ||
| Update Manager | | [[Update_Manager|Update Manager]] | ||
|- | |||
| 0x8000 | |||
| 8 | |||
| 0x8001 - 0x8005 | |||
| [[Updater_Frontend|Updater Frontend]] | |||
|- | |- | ||
| 0x9000 | | 0x9000 | ||
| 0x24 | | 0x24 | ||
| 0x9001 - 0x9016 | | 0x9001 - 0x9016 | ||
| SC Manager | | [[SC_Manager|SC Manager]] | ||
|- | |- | ||
| 0x10000 | | 0x10000 | ||
| 0x23 | | 0x23 | ||
| - | | 0x10001-0x10007 | ||
| | | [[SB_Manager|SBM (South Bridge Manager)]] | ||
|- | |- | ||
| 0x11000 | | 0x11000 | ||
| 0x25 | | 0x25 | ||
| 0x11001 - 0x11002 | | 0x11001 - 0x11002 | ||
| SPM (Security Policy Manager) | | [[Security_Policy_Manager|SPM (Security Policy Manager)]] | ||
|- | |- | ||
| 0x14000 | | 0x14000 | ||
| 0x25 | | 0x25 | ||
| 0x14004 - 0x14005 | | 0x14004 - 0x14005 | ||
| SLL (Secure LPAR Loader) | | [[Secure_LPAR_Loader|SLL (Secure LPAR Loader)]] | ||
|- | |- | ||
| 0x15000 | | 0x15000 | ||
| 0x24 | | 0x24 | ||
| 0x15001, 0x15003, 0x15009 | | 0x15001, 0x15003, 0x15009 | ||
| SPL (Secure Profile Loader) | | [[Secure_Profile_Loader|SPL (Secure Profile Loader)]] | ||
|- | |- | ||
| 0x17000 | | 0x17000 | ||
| 0x24 | | 0x24 | ||
| 0x17001 - 0x17017 | | 0x17001 - 0x17017 | ||
| Indi Info Manager | | [[Indi_Info_Manager|Indi Info Manager]] | ||
|- | |- | ||
| 0x18000 | | 0x18000 | ||
| 0x25 | | 0x25 | ||
| 0x18001, 0x18002, 0x18004 | | 0x18001, 0x18002, 0x18004 | ||
| Dispatcher Manager | | [[Dispatcher_Manager|Dispatcher Manager]] | ||
|- | |- | ||
| 0x19000 | | 0x19000 | ||
| 0x24 | | 0x24 | ||
| 0x19002 - 0x19005 | | 0x19002 - 0x19005 | ||
| AIM | | [[AIM_Manager|AIM]] | ||
|- | |||
| 0x22000 | |||
| 0x16 | |||
| 0x22001 - 0x22004 | |||
| [[Factory_Data_Manager|Factory Data Manager]] | |||
|- | |- | ||
| 0x24000 | | 0x24000 | ||
| 0x23 | | 0x23 | ||
| 0x24001 - 0x24002 | | 0x24001 - 0x24002 | ||
| USB Dongle Authenticator | | [[USB_Dongle_Authenticator|USB Dongle Authenticator]] | ||
|- | |- | ||
| 0x25000 | | 0x25000 | ||
| 0x23 | | 0x23 | ||
| 0x25001 - 0x25002 | | 0x25001 - 0x25002 | ||
| User Token Manager | | [[User_Token_Manager|User Token Manager]] | ||
|} | |} | ||
Line 4,653: | Line 4,740: | ||
uint32_t retval; | uint32_t retval; | ||
uint8_t res[4]; | uint8_t res[4]; | ||
uint64_t laid; /* LPAR | uint64_t laid; /* LPAR Authority ID */ | ||
uint64_t paid; /* Program | uint64_t paid; /* Program Authority ID */ | ||
} | } | ||
</pre> | </pre> | ||
Line 4,685: | Line 4,772: | ||
*The size of the body depends on a used service. | *The size of the body depends on a used service. | ||
= | = LPAR Memory Management = | ||
== Memory Region class == | |||
This class is the base class for different memory region types. | |||
=== vtable === | |||
0x003578B0 (3.15) | |||
=== Member variables === | |||
offset 0x40 - pointer to LPAR object that owns this memory region | |||
offset 0x48 - type of memory region (8 bytes) | |||
offset 0x50 - LPAR start address of memory region | |||
offset 0x58 - size of memory region (8 bytes) | |||
offset 0x60 - flags (8 bytes) | |||
offset 0xA0 - log2 of page size | |||
=== Generating New LPAR Memory Region Addresses === | |||
generate_new_lpar_mem_region_address(?, memory region size, log2(page size), ?, ?) - 002C82E8 (3.15) | |||
generate_new_lpar_mem_region_address - 002C6570 (3.41) | |||
* | *The function returns a new LPAR memory region address. | ||
*This method is used e.g. in all HV calls which create any kind of memory regions, e.g. '''lv1_allocate_memory''', '''lv1_map_htab''', '''lv1_undocumented_function_114''', '''lv1_construct_logical_spe''', '''lv1_map_device_mmio_region''' or '''syscall 0x10040'''. | |||
==== | ==== Encoding LPAR Memory Region Start Addresses and Sizes ==== | ||
== | *Size of LPAR memory region is encoded in the LPAR memory region start address. | ||
*That is why e.g. the LPAR Memory Region Start Addresses of LPAR Memory Region of size 4096 byte begin with '''0x300000000000''', '''0x300000000000 >> 42 = 0xC = log2(4096)'''. | |||
*Each LPAR has a counter (8 bytes) which is incremented by 1 every time a new LPAR Memory Region is created. | |||
*Before incrementing, the counter is shifted left by '''log2(LPAR Memory Region Size)''' and ored with '''log2(LPAR Memory Region Size) << 42'''. | |||
LPAR Memory Region Start Address >> 42 = log2(LPAR Memory Region Size) | |||
= | LPAR Memory Region Start Address = (log2(LPAR Memory Region Size) << 42) | | ||
(counter << log2(LPAR Memory Region Size)) | |||
===== LPAR Memory Region Address Counter ===== | |||
*LPAR Memory Region Address Counter is stored at address: '''0x38(LPAR ptr) + 0x9E8''' | |||
*LPAR1's Memory Region Address Counter is at address '''0x00677A48''' in HV dump 3.15 | |||
*LPAR2's Memory Region Address Counter is at address '''0x007632D8''' in HV dump 3.15 | |||
*LPAR1's Memory Region Address Counter is at address '''0x00677A48''' in HV dump 3.41 | |||
*LPAR2's Memory Region Address Counter is at address '''0x00161E68''' in HV dump 3.41 | |||
== Physical Memory Region class == | |||
This type of memory region is created e.g. in '''lv1_allocate_memory''' HV call or in '''syscall 0x10000'''. | |||
=== vtable === | |||
0x00357D08 (3.15) | |||
=== Member variables === | |||
offset 0xB0 - pointer to object that stores a list of addresses of physical pages owned by this memory region | |||
offset 0xB8 - pointer to LPAR object that owns this memory region | |||
offset 0xC0 - reference counter (8 bytes) | |||
=== | === Objects === | ||
Here is the list of physical memory region objects i found in HV 3.15. | |||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
|- | |- | ||
! | ! Address in HV dump | ||
! | ! LPAR id | ||
! LPAR Start Address | |||
! Size | |||
! Flags | |||
! log2(Page Size) | |||
! Physical Page Addresses | |||
|- | |- | ||
| | | 0x006B5510 | ||
| | | 1 | ||
| 0x300000001000 | |||
| 0x1000 | |||
| 0x0 | |||
| 0xC | |||
| 0x672000 | |||
|- | |- | ||
| | | 0x006B5E50 | ||
| | | 1 | ||
| 0x440000040000 | |||
| 0x20000 | |||
| 0x0 | |||
| 0x11 | |||
| 0x6C0000 | |||
|- | |- | ||
| | | 0x006B6980 | ||
| | | 1 | ||
| 0x440000060000 | |||
| 0x20000 | |||
| 0x0 | |||
| 0x11 | |||
| 0x6E0000 | |||
|- | |- | ||
| | | 0x006B7F00 | ||
| | | 1 | ||
| | | 0x400000040000 | ||
| | | 0x10000 | ||
| | | 0x0 | ||
| 0x10 | |||
| 0x100000 | |||
|- | |- | ||
| | | 0x003A80F0 | ||
| | | 2 | ||
| 0x6C0058000000 | |||
| 0x7000000 | |||
| 0x4 | |||
| 0x18 | |||
| 0x1000000 - 0x7000000 | |||
|- | |- | ||
| | | 0x003BE800 | ||
| | | 2 | ||
| 0x300000047000 | |||
| 0x1000 | |||
| 0x0 | |||
| 0xC | |||
| 0x1FA000 | |||
|- | |- | ||
| | | 0x006BDAA0 | ||
| | | 2 | ||
| | | 0x0 | ||
| | | 0x8000000 | ||
| | | 0x8 | ||
| 0x1B (single huge page) | |||
| 0x8000000 | |||
|} | |} | ||
So, Linux kernel should be located at physical address 0x8000000 and Linux syscall handler at 0x8000C00. Too bad that the HV dump is not large enough. | |||
=== GameOS Physical Memory Regions === | |||
* | *GameOS allocates nearly all physical memory of PS3 for itself !!! That is why new HV calls '''lv1_allocate_memory''' with large memory region sizes will fail. | ||
*So when someone wants a large piece of physical memory, he can borrow it from GameOS's LPAR memory region that starts at '''0x700020000000'''. It can be used for example to send update packages to Update Manager which are very large. | |||
Here is the list of physical memory regions of GameOS i found in HV 3.41: | |||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
|- | |- | ||
! | ! Start Address | ||
! | ! Size | ||
! Access Right | |||
! Max Page Size | |||
! Flags | |||
! Real Addresses | |||
|- | |- | ||
| | | 0x0 | ||
| | | 0x1000000 | ||
| 0x3 | |||
| 0x18 | |||
| 0x8 | |||
| 0x1000000 - 0x1FFF000 | |||
|- | |- | ||
| | | 0x500000300000 | ||
| | | 0xA0000 | ||
| 0x3 | |||
| 0x10 | |||
| 0x8 | |||
| 0x380000 - 0x38F000, 0x3B0000 - 0x3BF000, 0x1E0000 - 0x1FF000, 0x3C0000 - 0x3FF000, 0xFF00000 - 0xFF1F000 | |||
|- | |- | ||
| | | 0x700020000000 | ||
| - | | 0xE900000 (huge memory region) | ||
| 0x3 | |||
| 0x14 | |||
| 0x0 | |||
| 0x400000 - 0x5FF000, 0x800000 - 0xFFF000, 0x2000000 - 0xFEFF000 | |||
|} | |} | ||
== | == HTAB Memory Region class == | ||
This memory region is created when a HTAB is mapped into LPAR's address space. It's created in '''lv1_map_htab''' HV call. | |||
=== vtable === | |||
Here | 0x00357C98 (3.15) | ||
=== Member variables === | |||
offset 0xB0 - pointer to VAS object that owns the HTAB | |||
=== Objects === | |||
Here is the list of HTAB memory region objects i found in HV 3.15. | |||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
|- | |- | ||
! | ! Address in HV dump | ||
! | ! LPAR id | ||
! | ! VAS id | ||
! | ! LPAR Start Address | ||
! Size | |||
! Flags | |||
! log2(Page Size) | |||
|- | |- | ||
| 0x001FE0F0 | |||
| 2 | | 2 | ||
| 3 | | 3 | ||
| | | 0x500000C00000 | ||
| | | 0x100000 | ||
| | | 0xC000000000000000 | ||
| 0x14 | |||
|- | |- | ||
| | | 0x003BD850 | ||
| | | 2 | ||
| | | 3 | ||
| | | 0x500004300000 | ||
| 0x100000 | |||
| 0xC000000000000000 | |||
| 0x14 | |||
|- | |- | ||
| | | 0x003BDEA0 | ||
| | | 2 | ||
| | | 3 | ||
| | | 0x500004500000 | ||
| | | 0x100000 | ||
| | | 0xC000000000000000 | ||
| | | 0x14 | ||
|} | |} | ||
=== | === GameOS HTAB === | ||
* | *HTAB of GameOS is already mapped into address space of GameOS so that is why HV call '''lv1_map_htab''' will fail until you unmap it with '''lv1_unmap_htab''' | ||
*Effective address of GameOS HTAB is '''0x800000000F000000''' | |||
*Virtual address of GameOS HTAB is '''0xF000000''' | |||
*Size of GameOS HTAB is '''0x40000''' | |||
*GameOS HTAB supports large pages of size '''64K''' and '''1M''' | |||
*''' | *GameOS HTAB can be easily dumped by reading 0x40000 bytes at EA 0x800000000F000000 | ||
* | |||
* | |||
* | |||
* | |||
=== | === GameOS SLB === | ||
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 | |||
Here is | 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 == | ||
This type of memory region represents MMIO memory region of a SPE. It's created e.g. in '''lv1_construct_logical_spe''' or in '''syscall 0x10040'''. | |||
=== vtable === | |||
0x003583F8 (3.15) | |||
=== Member variables === | |||
=== | === Objects === | ||
Here is the | Here is the list of SPE memory region objects i found in HV 3.15. | ||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
|- | |- | ||
! | ! Address in HV dump | ||
! LPAR id | |||
! SPE | |||
! LPAR Start Address | |||
! Size | ! Size | ||
! | ! Physical Address | ||
! Flags | |||
! log2(Page Size) | |||
|- | |- | ||
| | | 0x003ABC20 | ||
| 2 | |||
| 1 | | 1 | ||
| | | 0x4C0000880000 | ||
| 0x80000 | |||
| 0x20000080000 | |||
| 0xA000000000000000 | |||
| 0xC | |||
|- | |- | ||
| | | 0x003AAD70 | ||
| | | 2 | ||
| | | 2 | ||
| 0x4C0000980000 | |||
| 0x80000 | |||
| 0x20000100000 | |||
| 0xA000000000000000 | |||
| 0xC | |||
|- | |- | ||
| | | 0x003A8880 | ||
| | | 2 | ||
| | | 3 | ||
| 0x4C0000780000 | |||
| 0x80000 | |||
| 0x20000180000 | |||
| 0xA000000000000000 | |||
| 0xC | |||
|- | |- | ||
| | | 0x003B4F70 | ||
| | | 2 | ||
| | | 4 | ||
| 0x4C0000A80000 | |||
| 0x80000 | |||
| 0x20000200000 | |||
| 0xA000000000000000 | |||
| 0xC | |||
|- | |- | ||
| | | 0x003AB700 | ||
| | | 2 | ||
| | | 5 | ||
| 0x4C0000680000 | |||
| 0x80000 | |||
| 0x20000280000 | |||
| 0xA000000000000000 | |||
| 0xC | |||
|- | |- | ||
| | | 0x003B5BE0 | ||
| | | 2 | ||
| | | 6 | ||
| | | 0x4C0000B80000 | ||
| | | 0x80000 | ||
| | | 0x20000300000 | ||
| | | 0xA000000000000000 | ||
| | | 0xC | ||
|} | |} | ||
== | == SPE Shadow Registers Memory Region class == | ||
This type of memory region represents shadow registers memory region of a SPE. It's created e.g. in '''lv1_construct_logical_spe''' or in '''syscall 0x10040'''. | |||
=== | === vtable === | ||
0x00358448 (3.15) | |||
=== | === Objects === | ||
Here is the list of SPE Shadow Registers memory region objects i found in HV 3.15. | |||
Here is the | |||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
|- | |- | ||
! | ! Address in HV dump | ||
! | ! LPAR id | ||
! | ! SPE | ||
! LPAR Start Address | |||
! Size | |||
! Physical Address | |||
! Flags | |||
! log2(Page Size) | |||
|- | |- | ||
| 0x003ABDA0 | |||
| 2 | |||
| 1 | | 1 | ||
| | | 0x300000012000 | ||
| | | 0x1000 | ||
| - | |||
| 0xA000000000000000 | |||
| 0xC | |||
|- | |- | ||
| 0x003B4290 | |||
| 2 | |||
| 2 | | 2 | ||
| | | 0x300000014000 | ||
| | | 0x1000 | ||
| - | |||
| 0xA000000000000000 | |||
| 0xC | |||
|- | |- | ||
| 0x003A8A00 | |||
| 2 | |||
| 3 | | 3 | ||
| | | 0x300000010000 | ||
| | | 0x1000 | ||
| - | |||
| 0xA000000000000000 | |||
| 0xC | |||
|- | |- | ||
| 0x003B50F0 | |||
| 2 | |||
| 4 | | 4 | ||
| | | 0x300000016000 | ||
| | | 0x1000 | ||
| - | |||
| 0xA000000000000000 | |||
| 0xC | |||
|- | |- | ||
| 0x001FFC90 | |||
| 2 | |||
| 5 | | 5 | ||
| | | 0x30000000E000 | ||
| | | 0x1000 | ||
| - | |||
| 0xA000000000000000 | |||
| 0xC | |||
|- | |- | ||
| 0x003AE5B0 | |||
| 2 | |||
| 6 | | 6 | ||
| | | 0x300000018000 | ||
| 0x1000 | |||
| - | |||
| 0xA000000000000000 | |||
| 0xC | |||
| | |||
| | |||
| | |||
| | |||
|} | |} | ||
== | == Device MMIO Memory Region class == | ||
This type of memory region is created when a device MMIO region is mapped into LPAR address space, e.g. in '''lv1_map_device_mmio_region'''. | |||
=== vtable === | |||
0x00352468 (3.15) | |||
=== Member variables === | |||
offset 0xA8 - physical address where the device MMIO region is mapped to | |||
=== Objects === | |||
Here is the list of Device MMIO memory region objects i found in HV 3.15. | |||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
|- | |- | ||
! | ! Address in HV dump | ||
! | ! LPAR id | ||
! LPAR Start Address | |||
! Size | |||
! Flags | |||
! log2(Page Size) | |||
! Physical Address | |||
! Device | |||
|- | |- | ||
| | | 0x001FDF00 | ||
| | | 2 | ||
| 0x4000001D0000 | |||
| 0x10000 | |||
| 0x8000000000000000 | |||
| 0xC | |||
| 0x24003010000 | |||
| USB controller | |||
|- | |- | ||
| | | 0x003B3850 | ||
| | | 2 | ||
| 0x400000200000 | |||
| 0x10000 | |||
| 0x8000000000000000 | |||
| 0xC | |||
| 0x24003020000 | |||
| USB controller | |||
|- | |- | ||
| | | 0x003B6E50 | ||
| | | 2 | ||
| 0x4000001E0000 | |||
| 0x10000 | |||
| 0x8000000000000000 | |||
| 0xC | |||
| 0x24003810000 | |||
| USB controller | |||
|- | |- | ||
| | | 0x003B9950 | ||
| | | 2 | ||
| | | 0x4000001F0000 | ||
| | | 0x10000 | ||
| | | 0x8000000000000000 | ||
| | | 0xC | ||
| | | 0x24003820000 | ||
| | | USB controller | ||
|} | |} | ||
== | == GPU Device Memory Region class == | ||
This type of memory region is created e.g. in '''lv1_gpu_open''', '''lv1_gpu_device_map''' and '''lv1_undocumented_function_114'''. | |||
=== | === vtable === | ||
0x00357C48 (3.15) | |||
=== Member variables === | |||
offset 0xA8 - physical address | |||
=== Objects === | |||
Here is the list of Device GPU memory region objects i found in HV 3.15. | |||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
|- | |- | ||
! | ! Address in HV dump | ||
! | ! LPAR id | ||
! LPAR Start Address | |||
! Size | |||
! Flags | |||
! log2(Page Size) | |||
! Physical Address | |||
|- | |- | ||
| | | 0x003AF380 | ||
| | | 2 | ||
| 0x700190000000 | |||
| 0xFE00000 | |||
| 0x8000000000000000 | |||
| 0x14 | |||
| 0x28080000000 | |||
|- | |- | ||
| 2 | | 0x003AF500 | ||
| | | 2 | ||
| 0x4000001A0000 | |||
| 0xC000 | |||
| 0x8000000000000000 | |||
| 0xC | |||
| 0x3C0000 | |||
|- | |- | ||
| | | 0x003AF680 | ||
| | | 2 | ||
| 0x4800006C0000 | |||
| 0x40000 | |||
| 0x8000000000000000 | |||
| 0xC | |||
| 0x2808FE00000 | |||
|- | |- | ||
| | | 0x003AFC30 | ||
| | | 2 | ||
| 0x440000380000 | |||
| 0x20000 | |||
| 0x8000000000000000 | |||
| 0xC | |||
| 0x28000C00000 | |||
|- | |- | ||
| | | 0x003BB420 | ||
| | | 2 | ||
| | | 0x3C0000108000 | ||
| | | 0x8000 | ||
| | | 0x8000000000000000 | ||
| 0xC | |||
| 0x28000080100 | |||
|} | |} | ||
== | == Direct Map Memory Region class == | ||
This type of memory region is created in HV call '''lv1_undocumented_function_114'''. | |||
'''lv1_undocumented_function_114''' allows you to map any memory address into LPAR's memory address. | |||
* The HV call '''lv1_undocumented_function_115''' destroys a memory region of this type. | |||
* HV allows GameOS to create objects of this type of size 0 only !!! But it can be exploited with a dangling HTAB entry. | |||
=== | === vtable === | ||
0x00357C48 (3.15) | |||
=== Member variables === | |||
offset 0xA8 - physical address | |||
=== Exploiting HV with memory glitching and HV call lv1_undocumented_function_114 === | |||
Here is a short description of the method i used to exploit HV from GameOS 3.15 and 3.41. | |||
* First i used the Geohot's method to create a dangling HTAB entry. | |||
* Making memory glitch work on GameOS was the largest of my obstacles but i solved it and i'm able to create a dangling HTAB entry from GameOS within 1-3 minutes. | |||
* Then i created many '''Direct Map Memory Region''' objects of size 0 with HV call '''lv1_undocumented_function_114''' and checked if they are within the page to which the dangling HTAB entry points to. | |||
* When i found one such '''Direct Map Memory Region''' object i patched the size of this object to 0x1000. Then i pointed this memory region object to the code of HV call '''lv1_undocumented_function_114''' and patched 4 bytes in this HV call which allows me to create any '''Direct Map Memory Region''' objects without any restrictions. | |||
* Function '''LPAR_construct_direct_mapping_mem_region''' which is used by HV call '''lv1_undocumented_function_114''' has a parameter (register %r9) and when this parameter is not 0 then HV will allow you to create any '''Direct Map Memory Region''' objects without restrictions, but unfortunately the HV call '''lv1_undocumented_function_114''' passes 0 in this parameter, so i just patched it. | |||
* Then i mapped whole HV memory range with the patched HV call '''lv1_undocumented_function_114''' into the address space of GameOS. | |||
* And now you have read/write access to the whole HV. | |||
* $ONY could fix this exploit by disallowing creating of '''Direct Map Memory Region''' objects of size 0, but i know tons of other HV C++ classes which will allow me to exploit the HV in a similar way, so it wouldn't bring $ONY anything :-) And they have to change member variable offsets in those objects to make sure that i cannot patch them easily :-) | |||
== | == Methods == | ||
LPAR_get_memory_region_by_start_address - 0x002C7C40 (3.15) | |||
LPAR_get_memory_region_by_address - 0x002C7DA8 (3.15) | |||
LPAR_mem_addr_to_phys_addr(LPAR id, LPAR address, phys_addr) - 0x002FB8F0 (3.15) | |||
LPAR_construct_direct_mapping_mem_region - 0x002D4D04 (3.15) | |||
= Network Devices = | |||
== | == Ethernet Gelic Device == | ||
= | device id = 0 | ||
MAC Address: 00:1F:A7:C6:2A:C5 | |||
device memory base address = 0x24003004000 (size = 0x1000) | |||
== WLAN Gelic Device == | |||
= | device id = 0 | ||
MAC Address: 02:1F:A7:C6:2A:C5 (locally administered) | |||
=== Net Manager === | |||
*Net Manager runs in Process 9 | |||
*It sends commands to '''/dev/sc1''' to reset WLAN Gelic device | |||
*It opens '''/dev/net0''', sets MAC address and writes device firmware '''eurus_fw.bin''' to WLAN device by using '''ioctl''' syscall | |||
=== /dev/net0 === | |||
The device supports 3 ioctl commands: | |||
*0 - 0x002AC10C (3.15) | |||
*1 - 0x002AC250 (3.15) | |||
*2 - EURUS_STAT 0x002AC320 (3.15) | |||
=== Methods === | |||
net_control_cmd_GELIC_LV1_POST_WLAN_CMD - 0x0024A55C (3.15) | |||
net_control_wlan_cmd_GELIC_EURUS_CMD_ASSOC - 0x00246C78 (3.15) | |||
net_control_wlan_cmd_GELIC_EURUS_CMD_START_SCAN - 0x00248A14 (3.15) | |||
net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WEP_CFG - 0x00249F24 (3.15) | |||
net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WPA_CFG - 0x002497B8 (3.15) | |||
= Event Notification = | |||
*Event Notfication is used e.g. to notify a LPAR about some event, e.g. device interrupt or notify a LPAR about destruction of another LPAR. | |||
*For example Process 9 is notified through Event Notification when LPAR 2 is destructed. | |||
*During LPAR construction, Process 9 creates an Outlet object with '''syscall 0x1001A''' and then passes the outlet ID to the '''syscall 0x10009''' that constructs the LINUX LPAR. In this way Process 9 is notified when LINUX LPAR is destructed. | |||
== Outlet class == | |||
This is the base Outlet class. There are different types of Outlet and they derive from this base class. | |||
=== vtable === | |||
0x00357DC0 (3.15) | |||
=== Member variables === | |||
offset 0x30 - type (8 bytes) | |||
offset 0x38 - pointer to LPAR that owns this Outlet object | |||
offset 0x48 - outlet id (8 bytes) | |||
offset 0x90 - VIRQ assigned to this Outlet object (4 bytes) | |||
== Event Receive Port class == | |||
*This type of Outlet is created e.g. in '''lv1_construct_event_receive_port''' and in '''syscall 0x1001A'''. | |||
*HV calls '''lv1_connect_irq_plug''' and '''lv1_connect_irq_plug_ext''' assigns a VIRQ to Event Receive Port object. | |||
=== | === vtable === | ||
0x00357E88 | |||
== | == VUART Outlet == | ||
* | *HV supports only one VUART Outlet per LPAR | ||
*'''lv1_configure_virtual_uart_irq''' constructs a VUART Outlet object and passes the address of LPAR's VUART IRQ Bitmap to HV | |||
=== | === vtable === | ||
0x00357DC0 | |||
=== VUART IRQ Bitmap === | |||
*At address 0x38(LPAR ptr) + 0x158 is the VUART IRQ Bitmap owned by HV for LPAR (4 * 8 bytes = 256 bits) | |||
*At address 0x38(LPAR ptr) + 0x150 is stored the physical address of LPAR's VUART IRQ Bitmap that was passed to '''lv1_configure_virtual_uart_irq''' | |||
*When a VUART interrupt is generated by HV then first the VUART IRQ Bitmap owned by HV is updated and then this bitmap is copied to LPAR's VUART IRQ Bitmap, so VUART IRQ Bitmap is stored twice, once in HV and once in LPAR, just like IRQ State Bitmap. | |||
*VUART IRQ Bitmap is not allowed to cross page boundary of LPAR memory region where it is stored. HV checks it and makes sure that it doesn't happen. | |||
*'''GameOS 3.41''' VUART IRQ bitmap is at address '''0x80000000003556E8''' and of size '''32 bytes (256 bits, each bit corresponds to a VUART port)'''. | |||
*'''GameOS 3.15''' VUART IRQ bitmap is at address '''0x8000000000354768'''. | |||
= | = Logical PPE = | ||
*''' | *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 == | ||
0x00357DF0 (3.15) | |||
== | == Member variables == | ||
offset 0x90 - pointer to an object that contains VIRQ-Outlet mapping table for thread 0 | |||
offset 0x98 - pointer to an object that contains VIRQ-Outlet mapping table for thread 1 | |||
== Objects == | |||
Here is the list of Logical PPE objects i found in HV 3.15. | |||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
|- | |- | ||
! | ! Address in HV dump | ||
! | ! LPAR id | ||
! PPE id | |||
|- | |- | ||
| | | 0x0069C7F0 | ||
| | | 1 | ||
| 1 | |||
|- | |- | ||
| | | 0x007A8900 | ||
| | | 2 | ||
| 1 | |||
|} | |} | ||
== | == Virtual IRQ - Outlet Mapping == | ||
*HV maintains 2 tables per PPE that map a VIRQ to an Outlet object. | |||
*The table has 256 entries and is indexed by VIRQ. | |||
*Each entry is a pointer to Outlet object. | |||
*Each Logical PPE object has 2 tables, one for each thread of Cell CPU. | |||
=== LPAR 1 PPE 1 Thread 0 === | |||
=== | |||
0x0069C990 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 0''' (not empty) | |||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
|- | |- | ||
! | ! VIRQ | ||
! | ! Address of Outlet object in HV dump | ||
! Description | |||
|- | |- | ||
| | | 58 | ||
| | | 0x00090D10 | ||
| - | |||
|- | |- | ||
| | | 59 | ||
| | | 0x006BAC50 | ||
| - | |||
|- | |- | ||
| | | 60 | ||
| | | 0x006B3ED0 | ||
| FLASH storage device / Storage device notification for LPAR 1 | |||
|- | |- | ||
| | | 61 | ||
| | | 0x00697E70 | ||
| VUART interrupts | |||
|- | |- | ||
| | | 62 | ||
| | | 0x001C8F20 | ||
|- | | - | ||
|} | |} | ||
== | === LPAR 1 PPE 1 Thread 1 === | ||
0x0069D9B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 1''' (empty) | |||
=== LPAR 2 PPE 1 Thread 0 === | |||
0x000A06B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 0''' (not empty) | |||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
|- | |- | ||
! | ! VIRQ | ||
! Address of Outlet object in HV dump | |||
! Description | ! Description | ||
|- | |- | ||
| | | 20 | ||
| | | 0x003AA210 | ||
| - | |||
|- | |- | ||
| | | 21 | ||
| | | 0x003AFEC0 | ||
| - | |||
|- | |- | ||
| | | 22 | ||
| | | 0x001FC010 | ||
| - | |||
|- | |- | ||
| | | 23 | ||
| | | 0x003A8E50 | ||
| | | - | ||
|- | |- | ||
| 24 | |||
| 0x001FFED0 | |||
| SPE 0 Class 0 Interrupt | |||
|- | |- | ||
| | | 25 | ||
| | | 0x003AE160 | ||
| SPE 0 Class 1 Interrupt | |||
|- | |||
| 26 | |||
| 0x003AE350 | |||
| SPE 0 Class 2 Interrupt | |||
|- | |||
| 27 | |||
| 0x003AB100 | |||
| SPE 1 Class 0 Interrupt | |||
|- | |- | ||
| | | 28 | ||
| | | 0x003AB2F0 | ||
| | | SPE 1 Class 1 Interrupt | ||
|- | |- | ||
| 29 | |||
| 0x003AB4E0 | |||
| SPE 1 Class 2 Interrupt | |||
|- | |- | ||
| | | 30 | ||
| | | 0x003AA6A0 | ||
| SPE 2 Class 0 Interrupt | |||
|- | |- | ||
| | | 31 | ||
| | | 0x003AA890 | ||
| | | SPE 2 Class 1 Interrupt | ||
|- | |||
| 32 | |||
| 0x003AAA80 | |||
| SPE 2 Class 2 Interrupt | |||
|- | |||
| 33 | |||
| 0x003B44A0 | |||
| SPE 3 Class 0 Interrupt | |||
|- | |||
| 34 | |||
| 0x003B4690 | |||
| SPE 3 Class 1 Interrupt | |||
|- | |||
| 35 | |||
| 0x003B4AD0 | |||
| SPE 3 Class 2 Interrupt | |||
|- | |||
| 36 | |||
| 0x003B5300 | |||
| SPE 4 Class 0 Interrupt | |||
|- | |||
| 37 | |||
| 0x003B54F0 | |||
| SPE 4 Class 1 Interrupt | |||
|- | |||
| 38 | |||
| 0x003B56E0 | |||
| SPE 4 Class 2 Interrupt | |||
|- | |- | ||
| 39 | |||
| 0x003AE7C0 | |||
| SPE 5 Class 0 Interrupt | |||
|- | |- | ||
| | | 40 | ||
| | | 0x003AE9B0 | ||
| | | SPE 5 Class 1 Interrupt | ||
|- | |- | ||
| | | 41 | ||
| | | 0x003AEBA0 | ||
| | | SPE 5 Class 2 Interrupt | ||
|- | |- | ||
| | | 42 | ||
| | | 0x003B2040 | ||
| | | Storage device notification for LPAR 2 | ||
|- | |- | ||
| | | 43 | ||
| | | 0x003AEE30 | ||
| | | VUART interrupts | ||
|- | |- | ||
| | | 44 | ||
| | | 0x001FEAA0 | ||
| | | - | ||
| | |- | ||
| | | 45 | ||
| | | 0x001FEED0 | ||
| | | HDD storage device | ||
|- | |||
| 46 | |||
| 0x003B5E20 | |||
| - | |||
|- | |||
| 47 | |||
| 0x003B7040 | |||
| - | |||
|- | |||
| 48 | |||
| 0x003B9B40 | |||
| - | |||
|- | |||
| 49 | |||
| 0x003B3A40 | |||
| - | |||
|- | |||
| 50 | |||
| 0x003BACA0 | |||
| Gelic device | |||
|- | |- | ||
| | | 51 | ||
| | | 0x003BAE10 | ||
| | | UNKNOWN storage device | ||
|- | |- | ||
| | | 52 | ||
| | | 0x003B8350 | ||
| | | - | ||
|} | |} | ||
=== LPAR 2 PPE 1 Thread 1 === | |||
0x007A89E0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 1''' (not empty) | |||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
|- | |- | ||
! | ! VIRQ | ||
! | ! Address of Outlet object in HV dump | ||
! Description | |||
|- | |||
| 16 | |||
| 0x003B2480 | |||
| - | |||
|- | |- | ||
| | | 17 | ||
| | | 0x003B2590 | ||
| | | - | ||
|- | |- | ||
| | | 18 | ||
| | | 0x003B26A0 | ||
| | | - | ||
|- | |- | ||
| | | 19 | ||
| | | 0x003B27B0 | ||
| | | - | ||
|} | |} | ||
== | == IRQ State Bitmap == | ||
*There is one IRQ State Bitmap (256 bits = 32 bytes) per thread of Logical PPE | |||
*'''HSPRG0 value is per thread''', so there are 2 HSPRG0 values in HV dump !!! | |||
*The IRQ State Bitmap of a thread is stored at -0x68E0(HSPRG0) | |||
*When an Event or Interrupt happens then the bitmap at 0x68E0(HSPRG0) is updated | |||
*The physical address of '''LPAR's IRQ State Bitmap''' of thread is stored at offset -0x68C0(HSPRG0) | |||
*The address of LPAR's IRQ State Bitmap is passed to Hypervisor through HV call '''lv1_configure_irq_state_bitmap''' | |||
*'''lv1_detect_pending_interrupts''' returns value of current IRQ State Bitmap. | |||
*The IRQ State Bitmap is updated if an Outlet object is assigned to VIRQ and when Outlet generates an event | |||
*After IRQ State Bitmap update, it's copied to LPAR's IRQ State Bitmap and a hardware interrupt is generated so that LPAR can read it's IRQ State Bitmap and handle interrupts. | |||
*So, IRQ State Bitmap is stored twice, once in HV and once in LPAR, just like VUART IRQ Bitmap. | |||
*'''GameOS''' IRQ state bitmap is stored at address '''SPRG0 + 0x1C0 and of size 64 bytes (256 bits state + 256 bits mask) per thread of Cell CPU'''. So there are 2 IRQ state bitmaps. | |||
0x8941FC0 - physical address of LPAR's IRQ State Bitmap for Thread 0 of LINUX LPAR | |||
0x8948FC0 - physical address of LPAR's IRQ State Bitmap for Thread 1 of LINUX LPAR | |||
= System Controller (SC or SYSCON) = | |||
*Data received from SC is sent to a VUART | |||
*'''lv1_get_rtc''' and '''syscall 0x10036''' communicate with '''SC VUART 4'''. | |||
=== VUART Table === | |||
*Address of SC VUART Table - 0x00610410 (3.15). | |||
*There are 5 VUARTs for SC in HV 3.15 | |||
Here is the | Here is the SC VUART table from HV 3.15: | ||
{| class="wikitable FCK__ShowTableBorders" | {| class="wikitable FCK__ShowTableBorders" | ||
|- | |- | ||
! Address in HV dump | ! 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 | | 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 | | 3 | ||
| | | 0x006124E0 | ||
| | | This VUART is connected with the '''VUART 3 (/dev/sc3)''' of LPAR 1 | ||
|- | |- | ||
| | | 4 | ||
| | | 0x00612DF0 | ||
| | | '''lv1_get_rtc''' and '''syscall 0x10036''' communicate with this VUART. | ||
|} | |} | ||
=== | == Interrupt Handling == | ||
spider_sc_interrupt_handler - 0x0020A68C (3.15) | |||
== Methods == | |||
sc_vuart_4_get_peer_vuart - 0x002ED384 (3.15) | |||
sc_send - 0x0020A908 (3.15) | |||
sc_receive - 0x0020A354 (3.15) | |||
sc_vuart_rx_trigger_callback - 0x002ED470 (3.15) | |||
== | == lv1_get_rtc == | ||
*'''lv1_get_rtc''' communicates with SC VUART 4. | |||
*20 bytes are written to the peer VUART of SC VUART 4. | |||
*After a request is sent to SC VUART 4, '''lv1_get_rtc''' busy waits until SC VUART 4 receive data buffer is not empty. | |||
*When SC VUART 4 receive data buffer is not empty, '''lv1_get_rtc''' reads 24 bytes from the VUART. | |||
== SYSCON Protocol == | |||
* I was able to enable SYSCON Manager debug messages in HV Process 5 | |||
* Messages sent to SYSCON are at least '''0x10''' bytes of size. SC VUARTs check it before sending the messages to SYSCON. | |||
* The header size of the SYSCON messages is '''0x10''' bytes. | |||
=== Packet Header === | |||
* 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. | |||
<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> | </pre> | ||
==== Calculating Packet Header Checksum ==== | |||
<pre> | |||
/* calculating SC packet header checksum */ | |||
/* | |||
* sc_hdr_cksum | |||
*/ | |||
uint16_t sc_hdr_cksum(struct sc_hdr *sc_hdr) | |||
{ | |||
uint8_t *ptr; | |||
uint32_t sum; | |||
== | ptr = (uint8_t *) sc_hdr; | ||
sum = 0; | |||
== | for (i = 0; i < 6; i++) | ||
sum += *ptr++; | |||
sum += 0x8000; | |||
return sum & 0xffff; | |||
} | |||
struct sc_hdr sc_hdr; | |||
memset(&sc_hdr, 0, sizeof(sc_hdr)); | |||
sc_hdr.cksum = sc_hdr_cksum(sc_hdr); | |||
/* fill sc header here */ | |||
sc_hdr.cksum = sc_hdr_cksum(sc_hdr); | |||
</pre> | |||
=== Packet Body === | |||
* Packet body follows packet header | |||
* Packet body size is stored at offset '''0xC''' and '''0xE''' in packet header and is of size 2 bytes | |||
=== Reading SYSCON EPROM (NVS Service) === | |||
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 | |||
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 | |||
=== PCI Bus Power === | |||
* '''Used by PS2EMU System Manager in HV process 9 when PS2 EMU is booted''' | |||
== | ==== PCI Bus Power On ==== | ||
'''Request to SC1:''' | |||
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x01 | |||
=== | ==== PCI Bus Power Off ==== | ||
'''Request to SC1:''' | |||
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x00 | |||
=== | === Ring Buzzer === | ||
'''Request:''' | |||
0x16 0x01 0x00 0x00 0x00 0x00 0x80 0x17 0x00 0x00 0x00 0x00 0x00 0x08 0x00 0x08 0x20 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
=SYSCON= | |||
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#SYSCON gitbrew.org::SYSCON] <br /> | |||
SYSCON MMIO registers can be accessed on Linux with a driver using lv1_undocumented_function_114, e.g. '''ps3sbmmio'''. | |||
Use ps3sbmmio device driver carefully, an access at some addresses could shutdown your PS3. | |||
==Packet Header== | |||
* Size is '''0x10'''. | |||
<pre> | |||
struct sc_hdr { | |||
uint8_t service_id; | |||
uint8_t version; /* must be 1 !!! */ | |||
uint16_t transaction_id; /* returned in response */ | |||
uint8_t res[2]; | |||
uint16_t cksum; /* checksum of first 6 header bytes */ | |||
uint32_t communication_tag; /* SYSCON tag: 0-4 */ | |||
uint16_t payload_size[2]; /* body size */ | |||
}; | |||
</pre> | |||
==Sending Packets== | |||
* 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; | |||
for (i = 0; i < packet_size; i++) | |||
cksum -= packet[i]; | |||
| | 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. | |||
==Receiving Packets== | |||
* The Hypervisor installs an interrupt handler for the SYSCON. | |||
* First, the Hypervisor reads a word from address 0x2400008E000, ors it with 0xFFFFFFFD and writes the value back. | |||
* Then, the Hypervisor reads a word from address 0x2400008E004 and tests if bit 0x2 is set or not. The bit 0x2 should be not 0 or else the Hypervisor panics. | |||
* After that, the Hypervisor reads a word at address 0x2400008CFF0 and 0x2400008DFF4. If there is a new packet pending from SYSCON, then the (value + 1) at 0x2400008CFF0 should be equal the value at 0x2400008DFF4. | |||
* The Hypervisor reads the header of the packet beginning at the address 0x2400008C000. | |||
* The header is read with 4 word transfers by the Hypervisor. | |||
* The byte at offset 1 in the packet header must be 1 or else the Hypervisor discards the packet as invalid. | |||
* The Hypervisor calculates the checksum of the packet header and checks it with the checksum stored in the header. If they don't match then the Hypervisor discards the packet. | |||
* The Hypervisor reads the body of the packet beginning at the address 0x2400008C010. | |||
* The header and the body of the received packet can be read as many times as you want !!! They remain until next SYSCON packet is received | |||
|- | which gives us the possibility to communicate with SYSCON on Linux easily :) | ||
| | ==Test== | ||
| | |||
'''1. Before sending SYSCON packet''': | |||
<pre> | |||
| - | root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C | ||
| | 00000000 01 18 01 18 |....| | ||
| | 00000004 | ||
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C | |||
| | |||
00000000 01 18 01 18 |....| | |||
| | 00000004 | ||
| | |||
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C | |||
|- | 00000000 01 24 01 24 |.$.$| | ||
00000004 | |||
| | root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C | ||
| | |||
00000000 01 24 01 24 |.$.$| | |||
00000004 | |||
</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 | |||
</pre> | |||
'''5. Received Body''' | |||
=== | <pre> | ||
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=8 skip=$((0x8c010)) status=noxfer | hexdump -C | |||
00000000 00 00 c7 01 ff 00 00 00 |..Ç.ÿ...| | |||
00000008 | |||
</pre> | |||
==Examples== | |||
===Get RTC=== | |||
* Used by LV1 call '''lv1_get_rtc''' | |||
* Communication with SYSCON 4 | |||
Request: | |||
<pre> | |||
# write packet | |||
# echo "0: 13 01 0000 0000 8014 00000004 0001 0001 33 00 00 00 0000ff1f" | xxd -c256 -r | \ | |||
dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer | |||
# dump packet counter | |||
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C | |||
00000000 00 c0 00 c0 |.À.À| | |||
00000004 | |||
# increment packet counter | |||
echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer | |||
# kick packet | |||
# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer | |||
</pre> | |||
Response: | |||
<pre> | |||
# dump packet counter | |||
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C | |||
00000000 00 c1 00 c1 |.Á.Á| | |||
00000004 | |||
# dump response packet | |||
# dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C | |||
00000000 13 01 00 00 00 00 80 14 00 00 00 04 00 08 00 08 |................| | |||
00000010 00 00 00 00 15 af 47 6b |.....¯Gk| | |||
00000018 | |||
</pre> | |||
===Ring Buzzer=== | |||
* Used by System Manager | |||
* Communication with SYSCON 1 | |||
Request: | |||
<pre> | |||
# write packet | |||
== | # echo "0: 16 01 1620 0000 804d 00000001 0008 0008 20 29 0a 00 000001b6 0000fdcb" | xxd -c256 -r | \ | ||
dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer | |||
# dump packet counter | |||
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C | |||
00000000 00 c0 00 c0 |.À.À| | |||
00000004 | |||
# increment packet counter | |||
=== | echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer | ||
# kick packet | |||
=== | # echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer | ||
# you should hear a beep | |||
</pre> | |||
Response: | |||
<pre> | |||
# dump packet counter | |||
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C | |||
00000000 00 c1 00 c1 |.Á.Á| | |||
00000004 | |||
# dump response packet | |||
= | # dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C | ||
00000000 16 01 16 20 00 00 80 4d 00 00 00 01 00 01 00 01 |... ...M........| | |||
00000010 00 00 00 00 00 00 fe e3 |......þã| | |||
00000018 | |||
</pre> | |||
=Isolation= | |||
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#Isolation gitbrew.org::Isolation] <br /> | |||
==Running Isolated SPE Modules On OtherOS++ Linux== | |||
* 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. | |||
<pre> | |||
root@debian-hdd:/home/glevand/spp_verifier# cat spp_verifier_355.self > /proc/spp_verifier/spu | |||
root@debian-hdd:/home/glevand/spp_verifier# cat default_355.spp > /proc/spp_verifier/profile | |||
root@debian-hdd:/home/glevand/spp_verifier# echo 1 > /proc/spp_verifier/run | |||
root@debian-hdd:/home/glevand/spp_verifier# cat /proc/spp_verifier/debug | |||
PPE id (0x0000000000000001) VAS id (0x0000000000000002) | |||
lv1_construct_logical_spe (0x00000000) | |||
SPE id (0x000000000000002b) | |||
lv1_undocumented_function_209 (0x00000000) | |||
shadow execution status (0x0000000000000002) | |||
lv1_get_spe_interrupt_status(1) (0x00000000) | |||
interrupt status 1 (0x0000000000000000) | |||
sleep | |||
shadow execution status (0x0000000000000002) | |||
lv1_get_spe_interrupt_status(1) (0x00000000) | |||
interrupt status 1 (0x0000000000000001) | |||
ea (0xc000000002920000) esid (0xc000000008000000) vsid (0x0000408f92c94500) | |||
lv1_undocumented_function_62 (0x00000000) | |||
lv1_clear_spe_interrupt_status(1) (0x00000000) | |||
lv1_undocumented_function_168 (0x00000000) | |||
sleep | |||
shadow execution status (0x0000000000000007) | |||
lv1_get_spe_interrupt_status(1) (0x00000000) | |||
interrupt status 1 (0x0000000000000000) | |||
lv1_get_spe_interrupt_status(2) (0x00000000) | |||
interrupt status 2 (0x0000000000000000) | |||
out interrupt mbox (0x0000000000000002) | |||
out interrupt mbox (0x0000000000000002) | |||
lv1_undocumented_function_167 (0x00000000) | |||
lv1_clear_spe_interrupt_status (0x00000000) | |||
lv1_undocumented_function_200 (0x00000000) | |||
sleep | |||
shadow execution status (0x000000000000000b) | |||
lv1_get_spe_interrupt_status(1) (0x00000000) | |||
interrupt status 1 (0x0000000000000000) | |||
shadow execution status (0x000000000000000b) | |||
problem status (0x01000082) | |||
lv1_destruct_logical_spe (0x00000000) | |||
root@debian-hdd:/home/glevand/spp_verifier# hexdump -C /proc/spp_verifier/profile | less | |||
... | |||
... | |||
00000200 00 02 00 05 00 00 20 a0 00 00 00 01 00 03 00 00 |...... ........| | |||
00000210 00 00 00 00 00 00 00 01 00 00 00 0e 00 00 00 00 |................| | |||
00000220 00 00 02 88 00 00 00 01 10 70 00 00 01 00 00 01 |.........p......| | |||
00000230 00 00 00 00 00 00 00 00 53 43 45 5f 43 45 4c 4c |........SCE_CELL| | |||
00000240 4f 53 5f 50 4d 45 00 00 00 00 00 00 00 00 00 00 |OS_PME..........| | |||
00000250 00 00 00 00 00 00 00 00 00 00 00 06 00 00 02 50 |...............P| | |||
00000260 10 70 00 00 01 00 00 01 2f 66 6c 68 2f 6f 73 2f |.p....../flh/os/| | |||
00000270 74 68 69 73 5f 69 73 5f 64 75 6d 6d 79 00 00 00 |this_is_dummy...| | |||
00000280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| | |||
... | |||
... | |||
</pre> | |||
== | ==Using metldr On OtherOS++ Linux== | ||
* | * spp_verifier_direct is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux by using metldr directly. | ||
*It | * It decrypts default.spp profile. | ||
* | * Tested on 3.41 and 3.55. | ||
* You can modify it easily to run other SPE modules. | |||
<pre> | |||
root@debian-hdd:/home/glevand/spp_verifier_direct# insmod ./spp_verifier_direct.ko | |||
root@debian-hdd:/home/glevand/spp_verifier_direct# cat metldr > /proc/spp_verifier_direct/metldr | |||
root@debian-hdd:/home/glevand/spp_verifier_direct# cat isoldr_355 > /proc/spp_verifier_direct/isoldr | |||
root@debian-hdd:/home/glevand/spp_verifier_direct# cat RL_FOR_PROGRAM_355.img > /proc/spp_verifier_direct/rvkprg | |||
root@debian-hdd:/home/glevand/spp_verifier_direct# cat EID0 > /proc/spp_verifier_direct/eid0 | |||
root@debian-hdd:/home/glevand/spp_verifier_direct# cat spp_verifier_355.self > /proc/spp_verifier_direct/spu | |||
root@debian-hdd:/home/glevand/spp_verifier_direct# cat default_355.spp > /proc/spp_verifier_direct/profile | |||
root@debian-hdd:/home/glevand/spp_verifier_direct# echo 1 > /proc/spp_verifier_direct/run | |||
root@debian-hdd:/home/glevand/spp_verifier_direct# cat /proc/spp_verifier_direct/debug | |||
PPE id (0x0000000000000001) VAS id (0x0000000000000002) | |||
lv1_construct_logical_spe (0x00000000) | |||
SPE id (0x0000000000000033) | |||
lv1_enable_logical_spe (0x00000000) | |||
lv1_set_spe_interrupt_mask(0) (0x00000000) | |||
lv1_set_spe_interrupt_mask(1) (0x00000000) | |||
lv1_set_spe_interrupt_mask(2) (0x00000000) | |||
lv1_set_spe_privilege_state_area_1_register (0x00000000) | |||
ea (0xc000000002680000) esid (0xc000000008000000) vsid (0x0000408f92c94500) | |||
lv1_get_spe_interrupt_status(0) (0x00000000) | |||
lv1_get_spe_interrupt_status(1) (0x00000000) | |||
lv1_get_spe_interrupt_status(2) (0x00000000) | |||
sleep | |||
lv1_get_spe_interrupt_status(0) (0x00000000) | |||
lv1_get_spe_interrupt_status(1) (0x00000000) | |||
lv1_get_spe_interrupt_status(2) (0x00000000) | |||
out interrupt mbox (0x0000000000000001) | |||
lv1_clear_spe_interrupt_status(2) (0x00000000) | |||
transferring EID0, ldr args and revoke list to LS | |||
waiting until MFC transfers are finished | |||
MFC transfers done | |||
out mbox (0x00000001) | |||
sleep | |||
lv1_get_spe_interrupt_status(0) (0x00000000) | |||
lv1_get_spe_interrupt_status(1) (0x00000000) | |||
lv1_get_spe_interrupt_status(2) (0x00000000) | |||
out interrupt mbox (0x0000000000000002) | |||
lv1_clear_spe_interrupt_status(2) (0x00000000) | |||
out mbox (0x00000002) | |||
lv1_clear_spe_interrupt_status(2) (0x00000000) | |||
sleep | |||
lv1_get_spe_interrupt_status(0) (0x00000000) | |||
lv1_get_spe_interrupt_status(1) (0x00000000) | |||
lv1_get_spe_interrupt_status(2) (0x00000000) | |||
problem status (0x01000082) | |||
lv1_destruct_logical_spe (0x00000000) | |||
root@debian-hdd:/home/glevand/spp_verifier_direct# hexdump -C /proc/spp_verifier_direct/profile | less | |||
... | |||
... | |||
00000200 00 02 00 05 00 00 20 a0 00 00 00 01 00 03 00 00 |...... ........| | |||
00000210 00 00 00 00 00 00 00 01 00 00 00 0e 00 00 00 00 |................| | |||
00000220 00 00 02 88 00 00 00 01 10 70 00 00 01 00 00 01 |.........p......| | |||
00000230 00 00 00 00 00 00 00 00 53 43 45 5f 43 45 4c 4c |........SCE_CELL| | |||
00000240 4f 53 5f 50 4d 45 00 00 00 00 00 00 00 00 00 00 |OS_PME..........| | |||
00000250 00 00 00 00 00 00 00 00 00 00 00 06 00 00 02 50 |...............P| | |||
00000260 10 70 00 00 01 00 00 01 2f 66 6c 68 2f 6f 73 2f |.p....../flh/os/| | |||
00000270 74 68 69 73 5f 69 73 5f 64 75 6d 6d 79 00 00 00 |this_is_dummy...| | |||
00000280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| | |||
... | |||
... | |||
</pre> | |||
= Gelic Device = | |||
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#Gelic_Device gitbrew.org::Gelic Device] <br /> | |||
== | ==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. | |||
''Note:[http://www.ps3devwiki.com/index.php?title=Wifi old vs. new]: Old == CECHA up to CECHK, New == CECHL and later'' | |||
== Control Interface == | |||
HV calls 195 and 196 are used by GameOS to send commands to Gelic device directly. | |||
=== lv1_undocumented_function_196 === | |||
= | ==== Parameters ==== | ||
r3 - LPAR address of data buffer | |||
r4 - size of data buffer | |||
r5 - must be 0 | |||
=== | === lv1_undocumented_function_195 === | ||
==== Parameters ==== | |||
r3 - command (16 bit value) | |||
r4 - command data size | |||
r5 - must be 0 | |||
=== Data Buffer === | |||
* Data Buffer passed to HV call 196 is divided into 2 parts. | |||
* The first 0x800 bytes are for sending and receiving command data | |||
* The remaining 0x800 bytes are for event notification. | |||
== | === Command Data Buffer === | ||
* | * Every command data sent to Gelic device contains header of size '''0xC''' | ||
* | * After the header follows the command data | ||
* After the Gelic device processed the command, it notifies LV2 kernel about command completion by sending an interrupt | |||
=== | ==== Header ==== | ||
* Size is '''0xc'''. | |||
* Byte order is little-endian. | |||
* Header data in a request command buffer is always all 0s. | |||
= | 0x0 - command = request command + 1 (2 bytes) | ||
0x4 - result, 0x1 - success ??? 0x2 - buffer too small ??? (2 bytes) | |||
0x6 - body size (2 bytes) | |||
=== Event Data Buffer === | |||
* The Gelic device notifies LV2 kernel by sending an interrupt when new events are available | |||
* Event Data Buffer has 8 bytes header | |||
* The remaining bytes are divided into event slots | |||
* Each event slot is of size 64 bytes | |||
* Events are in little-endian format | |||
==== Header ==== | |||
* | |||
* | offset 0x0 - GET index (4 bytes) | ||
* | |||
offset 0x4 - PUT index (4 bytes) | |||
* GET index is updated by Gelic driver. The Gelic driver reads events beginning with the event slot at index GET. | |||
* PUT index is the index of event entry where next Gelic event will be stored by the Gelic device. | |||
* If GET index is equal to PUT index then there are no Gelic events. | |||
=== GameOS === | |||
* 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 | |||
== | === Commands === | ||
====Unknown (0x1)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x10'''. | |||
* Used in AP mode. | |||
* Enables AP mode ??? | |||
====Get AP SSID (0x3)==== | |||
* Command buffer is of size '''0x30'''. | |||
* Returns SSID in AP mode. | |||
offset 0xC - SSID (32 bytes) | |||
====Set AP SSID (0x5)==== | |||
* Used by VSH. | |||
* Command buffer is of size '''0x30'''. | |||
* Sets SSID in AP mode. | |||
offset 0xC - SSID (32 bytes) | |||
====Get Channel (0xf)==== | |||
* Used by VSH. | |||
* Command buffer is of size '''0x31'''. | |||
* Data is returned from the device. | |||
* Returns list of channels and active channel. | |||
offset 0x2F - active channel (2 bytes) | |||
====Set Channel (0x11)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0xd''' | |||
* Valid channels: '''0 - 13'''. '''0''' means that the channel is selected '''automatically'''. | |||
offset 0xC - channel (1 byte) | |||
====Unknown (0x27)==== | |||
* Command buffer size is '''0xF'''. | |||
====Set Antenna (0x29)==== | |||
* Command buffer size is '''0xe''' | |||
offset 0xC - 0,1 or 2 (1 byte) | |||
offset 0xD - 2 (1 byte) | |||
====Set AP WEP Configuration (0x5b)==== | |||
* Used by VSH. | |||
* Command buffer is of size '''0x56'''. | |||
* Sets WEP security type and WEP key. | |||
* Security types: 0 - none, 1 - wep64, 2 - wep128 | |||
offset 0xE - security mode: 0 - none, 1 - wep64, 2 - wep128 (1 byte) | |||
offset 0x10 - WEP key (64 bytes) | |||
====Unknown (0x61)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0xd''' | |||
=== | ====Unknown (0x65)==== | ||
* Used by VSH. | |||
* Command uffer size is '''0xd'''. | |||
* Used in AP mode. | |||
=== | ====Get Eurus Firmware Version (0x99)==== | ||
* Used by VSH. | |||
Here is the response on my PS3 Slim: | |||
|- | <pre> | ||
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 |.............. | | ||
| 20 | </pre> | ||
| | |||
====Get AP Operating Mode (0xb7)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x10''' | |||
* Returns AP operating mode (mixed, 11b or 11g). | |||
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes) | |||
====Set AP Operating Mode (0xb9)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x10''' | |||
* Sets AP operating mode (mixed, 11b or 11g). | |||
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes) | |||
====Unknown (0xc5)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x10'''. | |||
* Used in AP mode. | |||
offset 0xC - ??? (4 bytes) | |||
====Set AP WPA AKM Suite (0xc9)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x11'''. | |||
* Sets WPA AKM suite in AP mode. | |||
offset 0xC - AKM suite (4 bytes) | |||
====Set AP WPA Group Cipher Suite (0xcf)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x10''' | |||
* Used in AP + WPA mode. | |||
offset 0xC - group cipher suite: group (4 bytes) | |||
====Set AP WPA PSK Binary (0xd3)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x4c''' | |||
* Sets WPA PSK binary | |||
offset 0xC - PSK (64 bytes) | |||
====Set AP WPA Reauthentication Timeout (0xd5)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x10''' | |||
* Sets WPA Reauth timeout value in AP WPA mode. | |||
* VSH uses 36000 as timeout. | |||
offset 0xC - timeout value in seconds (2 bytes) | |||
====Unknown (0x127)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x10'''. | |||
* Used in AP + WPA mode. | |||
====Unknown (0x12b)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x10'''. | |||
* Used in AP + WPA mode. | |||
====Set AP WPA PSK Passphrase (0x17d)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x2D''' | |||
offset 0xD - passphrase (32 bytes) | |||
====Set AP WPA Pairwise Cipher Suite (0x1bf)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x11''' | |||
* Used in AP + WPA mode. | |||
offset 0xC - pairwise cipher suite (4 bytes) | |||
offset 0x10 - ??? (1 byte) | |||
====Unknown (0x1d9)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x10''' | |||
====Unknown (0x1dd)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0xd''' | |||
====Unknown (0x1ed)==== | |||
* Used by VSH. | |||
* Command buffer is of size '''0x17'''. | |||
* Rate control ??? | |||
====Get Eurus HW Revision (0x1fb)==== | |||
* Command buffer size is '''0x10'''. | |||
====Associate (0x1001)==== | |||
* Used by VSH. | |||
* Used by LV1 on FAT models. | |||
* Command buffer size is '''0xd''' | |||
* Data passed to Gelic device is all 0s | |||
====Get Common Configuration (0x1003)==== | |||
* Used by VSH. | |||
* Used by LV1 on FAT models. | |||
* Command buffer size is '''0x18''' | |||
* Data passed to Gelic device is all 0s | |||
====Set Common Configuration (0x1005)==== | |||
* Used by VSH. | |||
* Used by LV1 on FAT models. | |||
* Command buffer size is '''0x18''' | |||
* Hmm, VSH always removes QOS bit from capability, that means Jupiter doesn't support QOS ??? | |||
offset 0xC - BSS type: 0 - infrastructure, 1 - ???, 2 - adhoc (1 byte) | |||
offset 0xD - authentication mode: 0 - open, 1 - shared key | |||
offset 0xE - opmode: 0 - 11bg, 1 - 11b, 2 - 11g (1 byte) | |||
offset 0xF - ??? (1 byte) | |||
offset 0x10 - BSSID (6 bytes) | |||
offset 0x16 - capability (2 bytes) | |||
====Get WEP Configuration (0x1013)==== | |||
* Used by VSH. | |||
* Used by LV1 on FAT models. | |||
* Command buffer size is '''0x50''' | |||
* Data passed to Gelic device is all 0s | |||
====Set WEP Configuration (0x1015)==== | |||
* Used by VSH. | |||
* Used by LV1 on FAT models. | |||
* Command buffer size is '''0x50''' | |||
====Get WPA Configuration (0x1017)==== | |||
* Used by VSH. | |||
* Used by LV1 on FAT models. | |||
* Command buffer size is '''0x5b''' | |||
* Data passed to Gelic device is all 0s | |||
====Set WPA Configuration (0x1019)==== | |||
* Used by VSH. | |||
* Used by LV1 on FAT models. | |||
* Command buffer size is '''0x5b''' | |||
offset 0xE - security type: 0 - WPA, 1 - RSNA (1 byte) | |||
offset 0xF - psk type: 0 - hex, 1 - bin (1 byte) | |||
offset 0x10 - psk key: hex or bin (64 bytes) | |||
offset 0x50 - group cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes) | |||
offset 0x54 - pairwise cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes) | |||
offset 0x58 - AKM suite: 0x0050f202 - WPA PSK, 0x000fac02 - RSNA PSK (4 bytes) | |||
'''See IEEE 802.11 specification for more details about cipher/AKM suites | |||
''' | |||
802.11 spec: [http://standards.ieee.org/getieee802/download/802.11-2007.pdf] | |||
====Unknown (0x1025)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x10'''. | |||
* Sets preamble type, something else ??? | |||
offset 0xC - preamble mode: 0 - short, 1 - long (1 byte) | |||
====Unknown (0x1031)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0xe''' | |||
====Get Scan Results (0x1033)==== | |||
* Used by VSH. | |||
* Used by LV1 on FAT models. | |||
* Command buffer size is '''0x5b0''' | |||
* Data passed to Gelic device is all 0s | |||
=====Scan Results===== | |||
offset 0x0 - number of scan entries (1 byte) | |||
offset 0x1 - array of scan entries | |||
======Scan Entry====== | |||
offset 0x0 - size of this entry in bytes, this field is NOT included (2 bytes) | |||
offset 0x2 - BSSID (6 bytes) | |||
offset 0x8 - RSSI (1 byte) | |||
offset 0x9 - timestamp (8 bytes) | |||
offset 0x11 - beacon period (2 bytes) | |||
offset 0x13 - capability (2 bytes) | |||
offset 0x15 - information elements (see 802.11 specification) | |||
====Start Scan (0x1035)==== | |||
* Used by VSH. | |||
* Used by LV1 on FAT models. | |||
* Command buffer size depends on size of channel list and ESSID string length | |||
* Data passed to Gelic device contains channel list and ESSID string | |||
* First '''0x16''' bytes in command data buffer are all 0s, then follows the channel list and after that ESSID | |||
====Diassociate (0x1037)==== | |||
* Used by VSH. | |||
* Used by LV1 on FAT models. | |||
* Command buffer size is '''0xd''' | |||
* Data passed to Gelic device is all 0s | |||
====Get RSSI (0x103d)==== | |||
* Used by VSH. | |||
* Used by LV1 on FAT models. | |||
* Command buffer size is '''0x17''' | |||
offset 0x10 - MAC address of node (6 bytes) | |||
offset 0x16 - RSSI (1 byte) | |||
====Get MAC Address (0x103f)==== | |||
* Command buffer size is '''0x13''' | |||
offset 0xD - MAC address (6 bytes) | |||
====Set MAC Address (0x1041)==== | |||
* Used by VSH. | |||
* Used by LV1 too. | |||
* Command buffer size is '''0x12''' | |||
====Unknown (0x104d)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0xd'''. | |||
offset 0xC - 0 - ???, 1 - ??? (1 byte) | |||
====Unknown (0x104f)==== | |||
* Command buffer size is '''0xd'''. | |||
* Returns 1 byte. | |||
offset 0xC - 0 - ???, 1 - ??? (1 byte) | |||
====Unknown (0x1051)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x5b3'''. | |||
* Returns '''0x5a7''' bytes. | |||
offset 0xC - number of entries | |||
offset 0x10 - entries (each entry is 0xd bytes) | |||
====Unknown (0x1053)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x70'''. | |||
offset 0xC - ??? (4 bytes) | |||
offset 0x10 - MAC address (6 bytes) | |||
====Unknown (0x1059)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x2a8'''. | |||
====Unknown (0x105f)==== | |||
* Used by LV2. | |||
====Get Zephyr HW Revision (0x1101)==== | |||
* Used by VSH. | |||
* Not a Gelic device command, handled by LV2 kernel. | |||
* LV2 uses LV1 call '''lv1_net_control(0x8000000000000002)''' | |||
* Command buffer size is '''0x18'''. | |||
====Get MAC Address List (0x1117)==== | |||
* Command buffer size is '''0xce'''. | |||
* Returns several MAC addresses. | |||
offset 0xC - number of MAC addresses (2 bytes) | |||
offset 0xE - MAC addresses (6 * number of MAC addresses) | |||
====Unknown (0x1133)==== | |||
* Used by VSH. | |||
* Command buffer size is '''0x1A'''. | |||
====Set WOL MAC Address Filter (0x1139)==== | |||
* Used by LV2 internally. | |||
* Command buffer is of size '''0x28'''. | |||
====Unknown (0x113b)==== | |||
* Used by LV2 internally. | |||
* Command buffer size is '''0x20'''. | |||
====Set WOL Multicast Address Filter (0x113d)==== | |||
* Used by LV2 internally. | |||
* Command buffer is of size '''0x2c'''. | |||
====Clear WOL Multicast Address Filter (0x113f)==== | |||
* Used by LV2 internally. | |||
* Command buffer is of size '''0x28'''. | |||
====Unknown (0x1141)==== | |||
* Used by LV2 internally. | |||
* Command buffer is of size 0x12. | |||
== Interrupt | ====Clear WOL Address Filter (0x1143)==== | ||
* Used by LV2 internally. | |||
* Command buffer size is '''0x2c'''. | |||
== | |||
====Unknown (0x114b)==== | |||
* Used by LV2 internally. | |||
====Set WOL Magic Packet Mode (0x1155)==== | |||
* Used by LV2 internally. | |||
* Command buffer is of size '''0x10'''. | |||
* Enables/Disables WOL magic packet. | |||
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes) | |||
====Unknown (0x1157)==== | |||
* Used by LV2 internally. | |||
* Command buffer size is '''0x10'''. | |||
====Set WOL Multicast Address Filter Mode (0x1159)==== | |||
* Used by LV2 internally. | |||
* Command buffer size is '''0x10'''. | |||
* WOL function | |||
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes) | |||
====Set Unicast Address Filter (0x115b)==== | |||
* Used by LV2 internally. | |||
* Command buffer is of size '''0x6a'''. | |||
* This command should be used to set proper MAC address or else device won't be able to receive packets destined to its own MAC address | |||
offset 0xC - ??? (2 bytes) | |||
offset 0xE - ??? (2 bytes) | |||
offset 0x10 - MAC address (6 bytes) | |||
====Clear Unicast Address Filter (0x115d)==== | |||
* Used by LV2 internally. | |||
* Command buffer size is '''0x6a'''. | |||
====Get Unicast Address Filter (0x115f)==== | |||
* Used by LV2 internally. | |||
* Command buffer is of size '''0x6a'''. | |||
====Set Multicast Address Filter (0x1161)==== | |||
* Used by LV2 internally. | |||
* Command buffer size is '''0x2c'''. | |||
====Clear Multicast Address Filter (0x1163)==== | |||
* Used by LV2 internally. | |||
* Command buffer size is '''0x2c''' | |||
* To clear all multicast addresses send command with all 0s. | |||
offset 0xC - multicast address filter (4 * 8 bytes) | |||
====Get Multicast Address Filter (0x1165)==== | |||
* Used by LV2 internally. | |||
* Command buffer is of size '''0x2c'''. | |||
====Set WOL Address Filter (0x1167)==== | |||
* Used by LV2 internally. | |||
* Command buffer size is '''0x70'''. | |||
====Set WOL Address Filter Mode (0x116d)==== | |||
* Used by LV2 internally. | |||
* Command buffer size is '''0x10'''. | |||
* Enables/Disables WOL address matching | |||
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes) | |||
====Set Unicast Address Filter Mode (0x116f)==== | |||
* Used by LV2 internally. | |||
* Command buffer size is '''0x10'''. | |||
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes) | |||
====Get Device Status (0xfffb)==== | |||
* Used by VSH. | |||
* Not a Gelic device command, handled by LV2 kernel. | |||
* Returned data size in command buffer is '''0x10'''. | |||
====Unknown (0xfffc)==== | |||
* Used by VSH. | |||
* Not a Gelic device command, handled by LV2 kernel. | |||
* LV2 uses LV1 call '''lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x4, 0x0, 0x0)''' | |||
====Get Channel Information (0xfffd)==== | |||
* Used by VSH. | |||
* Not a Gelic device command, handled by LV2 kernel. | |||
* LV2 uses LV1 call '''lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x0, 0x0, 0x0)''' | |||
* Returns supported WLAN channels | |||
====Set Response Timeout (0xfffe)==== | |||
* Used by VSH. | |||
* Not a Gelic device command, handled by LV2 kernel. | |||
* Sets timeout value which is used to wait for a response from Gelic device. | |||
* Typical value used by VSH is '''0x989680'''. | |||
* Command buffer size is '''0x14'''. | |||
====Unknown (0xffff)==== | |||
* Used by VSH. | |||
* Not a Gelic device command, handled by LV2 kernel. | |||
* Returns 0x10 bytes in command buffer. | |||
* Returns gelic device state ??? | |||
=== Events === | |||
<pre> | |||
struct ps3_eurus_event_hdr { | |||
__le32 type; | |||
__le32 id; | |||
__le32 timestamp; | |||
__le32 payload_length; | |||
__le32 unknown; | |||
} __packed; | |||
struct ps3_eurus_event { | |||
struct ps3_eurus_event_hdr hdr; | |||
u8 payload[44]; | |||
} __packed; | |||
</pre> | |||
====Event Type 0x00000040==== | |||
{| class="wikitable" | |||
|- | |||
! Id !! Description | |||
|- | |||
| 0x00000001 || Deauthenticated | |||
|} | |||
====Event Type 0x00000080==== | |||
{| class="wikitable" | |||
|- | |||
! Id !! Description | |||
|- | |||
| 0x00000001 || Beacon Lost | |||
|- | |||
| 0x00000002 || Connected | |||
|- | |||
| 0x00000004 || Scan Completed | |||
|- | |||
| 0x00000020 || WPA Connected | |||
|- | |||
| 0x00000040 || WPA Error (MIC Error) | |||
|} | |||
====Event Type 0x80000000==== | |||
{| class="wikitable" | |||
|- | |||
! Id !! Description | |||
|- | |||
| 0x00000001 || Device Ready | |||
|} | |||
== Enabling WLAN Gelic On FAT == | |||
Linux kernel doesn't use Gelic Device Control Interface like GameOS does it. | |||
To get WLAN working on Linux booted with GameOS rights, we have to disable | |||
Gelic Device Control Interface first because it's enabled for GameOS by default. | |||
The value of repository node "ios.net.eurus.lpar" controls access to Gelic Device Control Interface. | |||
It's a bitmap. The position of a bit corresponds to LPAR id. During GameOS booting, HV process 9 (System Manager) sets bit at postion 2 to 1 which means enable Gelic Device Control Interface for LPAR 2. | |||
To disable Gelic Device Control Interface on Linux, first unload Gelic device driver, then set | |||
value of repository node "ios.net.eurus.lpar" to 0 and load Gelic device driver again. After that WLAN should work again but only on FATs. | |||
For PS3 Slim we need a new Linux Gelic device driver which uses Gelic Device Control Interface directly. | |||
==USB WLAN Interface (Codename Jupiter 2)== | |||
* On new PS3 models, WLAN interface is USB. | |||
* '''Good news is that the same commands are used as with LV1 calls 196 and 195'''. | |||
* There are 2 wireless devices: Station and AP. | |||
* I got WLAN scan working. | |||
===Endpoints=== | |||
* LV2 uses 3 USB endpoints of interface 3,4 and 5 to communicate with WLAN. | |||
* Endpoints EP5 IN/OUT, EP6 IN/OUT and EP7 IN/OUT. | |||
* '''WLAN commands''' are sent to endpoint '''EP5 OUT''' with '''interrupt transfers'''. | |||
* '''WLAN events''' and '''WLAN command responses''' are received on endpoint '''EP5 IN''' with '''interrupt transfers'''. | |||
* LV2 opens a USB communication pipe to endpoint EP5 IN and EP5 OUT. | |||
* In my LV2 3.55 dump, pipe to EP5 IN has id '''0x2''' and pipe to EP5 OUT has id '''0x3'''. Array of all opened USB pipes is at address '''0x80000000004bd000''' in my LV2 3.55 dump. | |||
* EP5 is used to send commands to Jupiter and receive events from it. | |||
* EP6 is used to send/receive data packets to/from the 1st WLAN device. | |||
* EP7 is used to send/receive data packets to/from the 2nd WLAN device. | |||
* '''lsusb is buggy on big-endian arch and shows some fields with bytes swapped !!!''' | |||
<pre> | |||
Bus 002 Device 002: ID 054c:036f Sony Corp. | |||
Device Descriptor: | |||
bLength 18 | |||
bDescriptorType 1 | |||
bcdUSB 2.00 | |||
bDeviceClass 224 Wireless | |||
bDeviceSubClass 1 Radio Frequency | |||
bDeviceProtocol 1 Bluetooth | |||
bMaxPacketSize0 64 | |||
idVendor 0x054c Sony Corp. | |||
idProduct 0x036f | |||
bcdDevice 20.12 | |||
iManufacturer 1 | |||
iProduct 2 | |||
iSerial 0 | |||
bNumConfigurations 1 | |||
Interface Descriptor: | |||
bLength 9 | |||
bDescriptorType 4 | |||
bInterfaceNumber 3 | |||
bAlternateSetting 0 | |||
bNumEndpoints 2 | |||
bInterfaceClass 255 Vendor Specific Class | |||
bInterfaceSubClass 2 | |||
bInterfaceProtocol 1 | |||
iInterface 0 | |||
Endpoint Descriptor: | |||
bLength 7 | |||
bDescriptorType 5 | |||
bEndpointAddress 0x85 EP 5 IN | |||
bmAttributes 3 | |||
Transfer Type Interrupt | |||
Synch Type None | |||
Usage Type Data | |||
wMaxPacketSize 0x4000 1x 0 bytes | |||
bInterval 1 | |||
Endpoint Descriptor: | |||
bLength 7 | |||
bDescriptorType 5 | |||
bEndpointAddress 0x05 EP 5 OUT | |||
bmAttributes 3 | |||
Transfer Type Interrupt | |||
Synch Type None | |||
Usage Type Data | |||
wMaxPacketSize 0x4000 1x 0 bytes | |||
bInterval 1 | |||
Interface Descriptor: | |||
bLength 9 | |||
bDescriptorType 4 | |||
bInterfaceNumber 4 | |||
bAlternateSetting 0 | |||
bNumEndpoints 2 | |||
bInterfaceClass 255 Vendor Specific Class | |||
bInterfaceSubClass 2 | |||
bInterfaceProtocol 2 | |||
iInterface 0 | |||
Endpoint Descriptor: | |||
bLength 7 | |||
bDescriptorType 5 | |||
bEndpointAddress 0x86 EP 6 IN | |||
bmAttributes 2 | |||
Transfer Type Bulk | |||
Synch Type None | |||
Usage Type Data | |||
wMaxPacketSize 0x0002 1x 2 bytes | |||
bInterval 0 | |||
Endpoint Descriptor: | |||
bLength 7 | |||
bDescriptorType 5 | |||
bEndpointAddress 0x06 EP 6 OUT | |||
bmAttributes 2 | |||
Transfer Type Bulk | |||
Synch Type None | |||
Usage Type Data | |||
wMaxPacketSize 0x0002 1x 2 bytes | |||
bInterval 255 | |||
Interface Descriptor: | |||
bLength 9 | |||
bDescriptorType 4 | |||
bInterfaceNumber 5 | |||
bAlternateSetting 0 | |||
bNumEndpoints 2 | |||
bInterfaceClass 255 Vendor Specific Class | |||
bInterfaceSubClass 2 | |||
bInterfaceProtocol 3 | |||
iInterface 0 | |||
Endpoint Descriptor: | |||
bLength 7 | |||
bDescriptorType 5 | |||
bEndpointAddress 0x87 EP 7 IN | |||
bmAttributes 2 | |||
Transfer Type Bulk | |||
Synch Type None | |||
Usage Type Data | |||
wMaxPacketSize 0x0002 1x 2 bytes | |||
bInterval 0 | |||
Endpoint Descriptor: | |||
bLength 7 | |||
bDescriptorType 5 | |||
bEndpointAddress 0x07 EP 7 OUT | |||
bmAttributes 2 | |||
Transfer Type Bulk | |||
Synch Type None | |||
Usage Type Data | |||
wMaxPacketSize 0x0002 1x 2 bytes | |||
bInterval 255 | |||
</pre> | |||
===Device Initialization=== | |||
* LV2 does 2 control transfers to EP0 during WLAN initialization | |||
* First control transfer sends magic '''0x20''' data to device as '''CLEAR_FEATURE''' request. | |||
* Second control transfer reads '''0x2''' bytes device status. On my PS3 slim, the status data is always '''0x2031''' if you send the right magic. | |||
* Magic data sent in first control transfer is stored in LV2. | |||
* '''If you send wrong magic, the first control transfer will fail !!!''' | |||
* LV2 uses a state machine to initialize the Jupiter device. The state machine has 17 states. | |||
==== Magic Data in Control Transfer ==== | |||
<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> | |||
==== Initialization State Machine ==== | |||
* Implemented in LV2. | |||
=====State 1===== | |||
* Command '''0x114f''' is sent to WLAN device. | |||
=====State 2===== | |||
* Command '''0x1171''' is sent to WLAN device. | |||
=====State 3===== | |||
* LV2 waits for an event from WLAN device. | |||
=====State 4===== | |||
* Command '''0x116f''' is sent to WLAN device. | |||
=====State 5===== | |||
* Command '''0x115b''' is sent to WLAN device. | |||
* Command data sent to WLAN device contains MAC address. | |||
=====State 6===== | |||
* Command '''0x1161''' is sent to WLAN device. | |||
* Sets multicast address filter. | |||
=====State 7===== | |||
* Command '''0x110d''' is sent to WLAN device. | |||
=====State 8===== | |||
* Command '''0x1031''' is sent to WLAN device. | |||
=====State 9===== | |||
* Command '''0x1041''' is sent to WLAN device. | |||
* Command data sent to WLAN device contains MAC address. | |||
=====State 10===== | |||
* Command '''0x29''' is sent to WLAN device. | |||
* Sets antenna. | |||
=====State 11===== | |||
* Command '''0x110b''' is sent to WLAN device. | |||
=====State 12===== | |||
* Command '''0x1109''' is sent to WLAN device. | |||
=====State 13===== | |||
* Command '''0x207''' is sent to WLAN device. | |||
=====State 14===== | |||
* Command '''0x203''' is sent to WLAN device. | |||
=====State 15===== | |||
* Command '''0x105f''' is sent to WLAN device. | |||
* Command data sent to WLAN device contains MAC address, channel info and region code. | |||
=====State 16===== | |||
* LV2 waits for an event from WLAN device. | |||
=====State 17===== | |||
* LV2 accepts commands sent by LV2 syscall 726. | |||
===Test Program=== | |||
* Here is a small program which executes a WLAN scan. | |||
* I used libusb. | |||
====Source Code==== | |||
<pre> | |||
/* | |||
* PS3 USB WLAN | |||
* | |||
* Copyright (C) 2011 glevand ([email protected]) | |||
* All rights reserved. | |||
* | |||
* This program is free software; you can redistribute it and/or modify it | |||
* under the terms of the GNU General Public License as published | |||
* by the Free Software Foundation; version 2 of the License. | |||
* | |||
* This program is distributed in the hope that it will be useful, but | |||
* WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License along | |||
* with this program; if not, write to the Free Software Foundation, Inc., | |||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |||
*/ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <ctype.h> | |||
#include <stdint.h> | |||
#include <unistd.h> | |||
#include <pthread.h> | |||
#include <libusb-1.0/libusb.h> | |||
#define USB_VENDOR_ID 0x054c /* $ONY */ | |||
#define USB_PRODUCT_ID 0x036f | |||
#define USB_IFACE_NUMBER 3 | |||
#define USB_INTR_TRANSFER_EP5_IN_BUF_SIZE 0x800 | |||
#define USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE 0x800 | |||
struct wlan_cmd_pkt_hdr { | |||
uint8_t unknown1; | |||
uint8_t unknown2; | |||
uint8_t unknown3; | |||
uint8_t unknown4; | |||
uint16_t unknown5; | |||
uint8_t res1[2]; | |||
uint16_t tag; | |||
uint8_t res2[14]; | |||
} __attribute__ ((packed)); | |||
struct wlan_cmd_hdr { | |||
uint16_t command; | |||
uint16_t tag; | |||
uint16_t status; | |||
uint16_t payload_size; | |||
uint8_t res[4]; | |||
} __attribute__ ((packed)); | |||
struct wlan_event_pkt_hdr { | |||
uint8_t unknown1; | |||
uint8_t unknown2; | |||
uint8_t unknown3; | |||
uint8_t event_count; | |||
} __attribute__ ((packed)); | |||
static libusb_context *usb_ctx; | |||
static libusb_device_handle *usb_dev_handle; | |||
static struct libusb_transfer *usb_intr_transfer_ep5_in; | |||
static unsigned char usb_intr_transfer_ep5_in_buf[USB_INTR_TRANSFER_EP5_IN_BUF_SIZE]; | |||
static unsigned char usb_intr_transfer_ep5_out_buf[USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE]; | |||
static pthread_mutex_t usb_wlan_cmd_mutex; | |||
static pthread_cond_t usb_wlan_cmd_cond; | |||
static int volatile usb_wlan_cmd_busy; | |||
static uint16_t usb_wlan_cmd; | |||
static void *usb_wlan_cmd_data; | |||
static int volatile usb_wlan_cmd_thread_done; | |||
/* | |||
* WLAN won't work without this magic !!! | |||
*/ | |||
static unsigned char usb_magic_data[] = { | |||
0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8, | |||
0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d, | |||
}; | |||
static unsigned char my_mac_addr[] = { | |||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, | |||
}; | |||
/* | |||
* hexdump | |||
*/ | |||
static void hexdump(const unsigned char *data, unsigned int data_size) | |||
{ | |||
int i, j; | |||
for (i = 0; i < data_size; i += 16) { | |||
fprintf(stdout, "%08x:", i); | |||
for (j = 0; j < 16; j++) { | |||
if (i + j < data_size) { | |||
fprintf(stdout, " %02x", data[i + j]); | |||
} else { | |||
fprintf(stdout, " "); | |||
} | |||
} | |||
fprintf(stdout, " |"); | |||
for (j = 0; j < 16; j++) { | |||
if (i + j < data_size) { | |||
if (isprint(data[i + j])) | |||
fprintf(stdout, "%c", data[i + j]); | |||
else | |||
fprintf(stdout, "."); | |||
} else { | |||
fprintf(stdout, " "); | |||
} | |||
} | |||
fprintf(stdout, "|\n"); | |||
} | |||
} | |||
/* | |||
* usb_handle_wlan_event | |||
*/ | |||
static void usb_handle_wlan_event(struct wlan_event_pkt_hdr *wlan_event_pkt_hdr) | |||
{ | |||
fprintf(stdout, "%s:%d: === got WLAN event ===\n", __func__, __LINE__); | |||
/* | |||
fprintf(stdout, "%s:%d: event packet header:\n", __func__, __LINE__); | |||
fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__, | |||
wlan_event_pkt_hdr->unknown1); | |||
fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__, | |||
wlan_event_pkt_hdr->unknown2); | |||
fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__, | |||
wlan_event_pkt_hdr->unknown3); | |||
*/ | |||
fprintf(stdout, "%s:%d: event_count (0x%02x)\n", __func__, __LINE__, | |||
wlan_event_pkt_hdr->event_count); | |||
hexdump((unsigned char *) (wlan_event_pkt_hdr + 1), wlan_event_pkt_hdr->event_count * 64); | |||
} | |||
/* | |||
* usb_handle_wlan_cmd_response | |||
*/ | |||
static void usb_handle_wlan_cmd_response(struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr) | |||
{ | |||
struct wlan_cmd_hdr *wlan_cmd_hdr; | |||
uint8_t *wlan_cmd_payload; | |||
fprintf(stdout, "%s:%d: === got WLAN command response ===\n", __func__, __LINE__); | |||
wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1); | |||
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1); | |||
/* convert all header fields to big-endian byte order !!! */ | |||
wlan_cmd_pkt_hdr->unknown5 = le16toh(wlan_cmd_pkt_hdr->unknown5); | |||
wlan_cmd_pkt_hdr->tag = le16toh(wlan_cmd_pkt_hdr->tag); /* returned from request */ | |||
wlan_cmd_hdr->command = le16toh(wlan_cmd_hdr->command); /* request command + 1 */ | |||
wlan_cmd_hdr->tag = le16toh(wlan_cmd_hdr->tag); /* returned from request */ | |||
wlan_cmd_hdr->status = le16toh(wlan_cmd_hdr->status); /* 1 - success | |||
2 - invalid parameters ??? | |||
3 - invalid command ??? */ | |||
wlan_cmd_hdr->payload_size = le16toh(wlan_cmd_hdr->payload_size); /* length of data that follows the header */ | |||
/* | |||
fprintf(stdout, "%s:%d: command packet header:\n", __func__, __LINE__); | |||
fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__, | |||
wlan_cmd_pkt_hdr->unknown1); | |||
fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__, | |||
wlan_cmd_pkt_hdr->unknown2); | |||
fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__, | |||
wlan_cmd_pkt_hdr->unknown3); | |||
fprintf(stdout, "%s:%d: unknown4 (0x%02x)\n", __func__, __LINE__, | |||
wlan_cmd_pkt_hdr->unknown4); | |||
fprintf(stdout, "%s:%d: unknown5 (0x%04x)\n", __func__, __LINE__, | |||
wlan_cmd_pkt_hdr->unknown5); | |||
fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__, | |||
wlan_cmd_pkt_hdr->tag); | |||
*/ | |||
fprintf(stdout, "%s:%d: command header:\n", __func__, __LINE__); | |||
fprintf(stdout, "%s:%d: command (0x%04x)\n", __func__, __LINE__, | |||
wlan_cmd_hdr->command); | |||
if ((usb_wlan_cmd + 1) != wlan_cmd_hdr->command) | |||
fprintf(stdout, "%s:%d: ==> command does not match, got (0x%04x) expected (0x%04x)\n", | |||
__func__, __LINE__, wlan_cmd_hdr->command, usb_wlan_cmd + 1); | |||
fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__, | |||
wlan_cmd_hdr->tag); | |||
fprintf(stdout, "%s:%d: status (0x%04x)\n", __func__, __LINE__, | |||
wlan_cmd_hdr->status); | |||
if (wlan_cmd_hdr->status != 0x1) | |||
fprintf(stdout, "%s:%d: ==> command status != 0x1\n", __func__, __LINE__); | |||
fprintf(stdout, "%s:%d: payload_size (0x%04x)\n", __func__, __LINE__, | |||
wlan_cmd_hdr->payload_size); | |||
fprintf(stdout, "%s:%d: command payload:\n", __func__, __LINE__); | |||
hexdump(wlan_cmd_payload, wlan_cmd_hdr->payload_size); | |||
memcpy(usb_wlan_cmd_data, wlan_cmd_payload, wlan_cmd_hdr->payload_size); | |||
pthread_mutex_lock(&usb_wlan_cmd_mutex); | |||
usb_wlan_cmd_busy = 0; | |||
pthread_cond_signal(&usb_wlan_cmd_cond); | |||
pthread_mutex_unlock(&usb_wlan_cmd_mutex); | |||
} | |||
/* | |||
* usb_intr_transfer_ep5_in_cb | |||
*/ | |||
static void usb_intr_transfer_ep5_in_cb(struct libusb_transfer *transfer) | |||
{ | |||
struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr; | |||
int error; | |||
fprintf(stdout, "%s:%d: === got interrupt transfer ===\n", __func__, __LINE__); | |||
fprintf(stdout, "%s:%d: transfer status (%d) length (%d)\n", | |||
__func__, __LINE__, transfer->status, transfer->actual_length); | |||
wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) transfer->buffer; | |||
if (wlan_cmd_pkt_hdr->unknown3 == 0x6) | |||
usb_handle_wlan_cmd_response(wlan_cmd_pkt_hdr); | |||
else if (wlan_cmd_pkt_hdr->unknown3 == 0x8) | |||
usb_handle_wlan_event((struct wlan_event_pkt_hdr *) transfer->buffer); | |||
else | |||
fprintf(stdout, "%s:%d: got unknown packet (0x%02x)\n", | |||
__func__, __LINE__, wlan_cmd_pkt_hdr->unknown3); | |||
memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf)); | |||
libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5, | |||
usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf), | |||
usb_intr_transfer_ep5_in_cb, NULL, 0); | |||
error = libusb_submit_transfer(usb_intr_transfer_ep5_in); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n", | |||
__func__, __LINE__, error); | |||
exit(1); | |||
} | |||
} | |||
/* | |||
* usb_intr_transfer_ep5_out_cb | |||
*/ | |||
static void usb_intr_transfer_ep5_out_cb(struct libusb_transfer *transfer) | |||
{ | |||
/* | |||
fprintf(stdout, "%s:%d: sent interrupt transfer\n", __func__, __LINE__); | |||
fprintf(stdout, "%s:%d: transfer status (%d)\n", __func__, __LINE__, transfer->status); | |||
*/ | |||
libusb_free_transfer(transfer); | |||
} | |||
/* | |||
* usb_wlan_cmd_send | |||
*/ | |||
static int usb_wlan_cmd_send(uint16_t command, const uint8_t *data, unsigned int data_size) | |||
{ | |||
struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr; | |||
struct wlan_cmd_hdr *wlan_cmd_hdr; | |||
uint8_t *wlan_cmd_payload; | |||
struct libusb_transfer *transfer; | |||
int error; | |||
fprintf(stdout, "%s:%d: sending command (0x%04x) data size (0x%04x) command size (0x%04x)\n", | |||
__func__, __LINE__, command, data_size, data_size + sizeof(struct wlan_cmd_hdr)); | |||
transfer = libusb_alloc_transfer(0); | |||
if (!transfer) { | |||
fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__); | |||
error = -1; | |||
goto fail; | |||
} | |||
wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) usb_intr_transfer_ep5_out_buf; | |||
wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1); | |||
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1); | |||
wlan_cmd_pkt_hdr->unknown1 = 0x1; | |||
wlan_cmd_pkt_hdr->unknown2 = 0x1; | |||
wlan_cmd_pkt_hdr->unknown3 = 0x6; | |||
wlan_cmd_pkt_hdr->unknown4 = 0x0; | |||
wlan_cmd_pkt_hdr->unknown5 = 0x1; | |||
wlan_cmd_pkt_hdr->tag = 0xf00d; /* returned in response */ | |||
wlan_cmd_hdr->command = command; | |||
wlan_cmd_hdr->tag = 0xcafe; /* returned in response */ | |||
wlan_cmd_hdr->status = 0xa; | |||
wlan_cmd_hdr->payload_size = data_size; | |||
memcpy(wlan_cmd_payload, data, data_size); | |||
usb_wlan_cmd = command; | |||
usb_wlan_cmd_data = (void *) data; | |||
libusb_fill_interrupt_transfer(transfer, usb_dev_handle, LIBUSB_ENDPOINT_OUT | 0x5, | |||
usb_intr_transfer_ep5_out_buf, | |||
sizeof(struct wlan_cmd_pkt_hdr) + sizeof(struct wlan_cmd_hdr) + wlan_cmd_hdr->payload_size, | |||
usb_intr_transfer_ep5_out_cb, NULL, 0); | |||
/* convert all header fields to little-endian byte order !!! */ | |||
wlan_cmd_pkt_hdr->unknown5 = htole16(wlan_cmd_pkt_hdr->unknown5); | |||
wlan_cmd_pkt_hdr->tag = htole16(wlan_cmd_pkt_hdr->tag); | |||
wlan_cmd_hdr->command = htole16(wlan_cmd_hdr->command); | |||
wlan_cmd_hdr->tag = htole16(wlan_cmd_hdr->tag); | |||
wlan_cmd_hdr->status = htole16(wlan_cmd_hdr->status); | |||
wlan_cmd_hdr->payload_size = htole16(wlan_cmd_hdr->payload_size); | |||
error = libusb_submit_transfer(transfer); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n", | |||
__func__, __LINE__, error); | |||
goto fail_free_transfer; | |||
} | |||
pthread_mutex_lock(&usb_wlan_cmd_mutex); | |||
usb_wlan_cmd_busy = 1; | |||
while (usb_wlan_cmd_busy) | |||
pthread_cond_wait(&usb_wlan_cmd_cond, &usb_wlan_cmd_mutex); | |||
pthread_mutex_unlock(&usb_wlan_cmd_mutex); | |||
return 0; | |||
fail_free_transfer: | |||
libusb_free_transfer(transfer); | |||
fail: | |||
return error; | |||
} | |||
/* | |||
* usb_wlan_cmd_start_scan | |||
*/ | |||
static int usb_wlan_cmd_start_scan(void) | |||
{ | |||
unsigned char data[256], *ptr; | |||
unsigned int data_size; | |||
memset(data, 0, sizeof(data)); | |||
ptr = data; | |||
*ptr++ = 0x0; | |||
*ptr++ = 0x1; | |||
*ptr++ = 0x64; | |||
*ptr++ = 0x0; | |||
ptr = data + 0xa; | |||
*ptr++ = 0x3; | |||
*ptr++ = 13; /* number of channels */ | |||
*ptr++ = 1; /* channels */ | |||
*ptr++ = 2; | |||
*ptr++ = 3; | |||
*ptr++ = 4; | |||
*ptr++ = 5; | |||
*ptr++ = 6; | |||
*ptr++ = 7; | |||
*ptr++ = 8; | |||
*ptr++ = 9; | |||
*ptr++ = 10; | |||
*ptr++ = 11; | |||
*ptr++ = 12; | |||
*ptr++ = 13; | |||
data_size = ptr - data; | |||
return usb_wlan_cmd_send(0x1035, data, data_size); | |||
} | |||
/* | |||
* usb_wlan_cmd_get_scan_results | |||
*/ | |||
static int usb_wlan_cmd_get_scan_results(void) | |||
{ | |||
unsigned char data[1456]; | |||
unsigned int data_size; | |||
memset(data, 0, sizeof(data)); | |||
data_size = sizeof(data); | |||
return usb_wlan_cmd_send(0x1033, data, data_size); | |||
} | |||
/* | |||
* usb_wlan_cmd_0x99 | |||
*/ | |||
static int usb_wlan_cmd_0x99(void) | |||
{ | |||
unsigned char data[0x3e]; | |||
unsigned int data_size; | |||
memset(data, 0, sizeof(data)); | |||
data_size = sizeof(data); | |||
return usb_wlan_cmd_send(0x99, data, data_size); | |||
} | |||
/* | |||
* usb_wlan_init | |||
*/ | |||
static int usb_wlan_init(void) | |||
{ | |||
unsigned char data[1456], *ptr; | |||
unsigned int data_size; | |||
int error; | |||
/* state 0x1 */ | |||
memset(data, 0, sizeof(data)); | |||
data_size = 0x518; | |||
error = usb_wlan_cmd_send(0x114f, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x114f (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
/* state 0x2 */ | |||
memset(data, 0, sizeof(data)); | |||
data_size = 0; | |||
error = usb_wlan_cmd_send(0x1171, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x1171 (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
/* wait for a WLAN event */ | |||
/* state 0x4 */ | |||
memset(data, 0, sizeof(data)); | |||
ptr = data; | |||
*ptr++ = 0x1; | |||
data_size = 0x4; | |||
error = usb_wlan_cmd_send(0x116f, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x116f (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
/* state 0x5 */ | |||
memset(data, 0, sizeof(data)); | |||
ptr = data; | |||
*ptr++ = 0x1; | |||
ptr = data + 0x4; | |||
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr)); | |||
data_size = 0x5e; | |||
error = usb_wlan_cmd_send(0x115b, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x115b (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
/* state 0x6 */ | |||
memset(data, 0, sizeof(data)); | |||
ptr = data + 0x1c; | |||
*ptr++ = 0x20; | |||
data_size = 0x20; | |||
error = usb_wlan_cmd_send(0x1161, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x1161 (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
memset(data, 0, sizeof(data)); | |||
ptr = data + 0xc; | |||
memset(ptr, 0xff, 7 * 4); | |||
data_size = 0x80; | |||
error = usb_wlan_cmd_send(0x110d, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x110d (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
memset(data, 0, sizeof(data)); | |||
data_size = 0x2; | |||
error = usb_wlan_cmd_send(0x1031, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x1031 (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
memset(data, 0, sizeof(data)); | |||
ptr = data; | |||
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr)); | |||
data_size = 0x6; | |||
error = usb_wlan_cmd_send(0x1041, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x1041 (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
/* state 0xa */ | |||
memset(data, 0, sizeof(data)); | |||
ptr = data; | |||
*ptr++ = 0x2; | |||
*ptr++ = 0x2; | |||
data_size = 0x2; | |||
error = usb_wlan_cmd_send(0x29, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x29 (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
memset(data, 0, sizeof(data)); | |||
ptr = data; | |||
*ptr++ = 0x1; | |||
ptr = data + 8; | |||
*ptr++ = 0x20; | |||
data_size = 0xc; | |||
error = usb_wlan_cmd_send(0x110b, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x110b (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
memset(data, 0, sizeof(data)); | |||
ptr = data; | |||
*ptr++ = 0x1; | |||
ptr = data + 0x4; | |||
*ptr++ = 0x15; | |||
*ptr++ = 0x27; | |||
*ptr++ = 0x12; | |||
*ptr++ = 0x0; | |||
*ptr++ = 0x6; | |||
*ptr++ = 0x0; | |||
ptr = data + 0xc; | |||
*ptr++ = 0x9; | |||
*ptr++ = 0x0; | |||
*ptr++ = 0x1; | |||
ptr = data + 0x10; | |||
*ptr++ = 0xff; | |||
*ptr++ = 0xff; | |||
*ptr++ = 0xff; | |||
*ptr++ = 0xff; | |||
*ptr++ = 0xff; | |||
*ptr++ = 0xff; | |||
data_size = 0x16; | |||
error = usb_wlan_cmd_send(0x1109, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x1109 (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
memset(data, 0, sizeof(data)); | |||
ptr = data; | |||
*ptr++ = 0x1; | |||
data_size = 0x4; | |||
error = usb_wlan_cmd_send(0x207, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x207 (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
memset(data, 0, sizeof(data)); | |||
ptr = data; | |||
*ptr++ = 0x4; | |||
data_size = 0x4; | |||
error = usb_wlan_cmd_send(0x203, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x203 (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
sleep(2); | |||
/* state 0xf */ | |||
memset(data, 0, sizeof(data)); | |||
ptr = data; | |||
*ptr++ = 0xff; | |||
*ptr++ = 0x1f; | |||
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr)); | |||
ptr = data + 0x8; | |||
*ptr++ = 0x2; | |||
*ptr++ = 0x2; | |||
data_size = 0xa; | |||
error = usb_wlan_cmd_send(0x105f, data, data_size); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not send command 0x105f (%d)\n", | |||
__func__, __LINE__, error); | |||
return error; | |||
} | |||
return 0; | |||
} | |||
/* | |||
* usb_wlan_cmd_thread | |||
*/ | |||
static void *usb_wlan_cmd_thread(void *arg) | |||
{ | |||
int error; | |||
error = usb_wlan_init(); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not initialize device (%d)\n", | |||
__func__, __LINE__, error); | |||
goto done; | |||
} | |||
sleep(5); | |||
error = usb_wlan_cmd_0x99(); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not start scanning (%d)\n", | |||
__func__, __LINE__, error); | |||
goto done; | |||
} | |||
error = usb_wlan_cmd_start_scan(); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not start scanning (%d)\n", | |||
__func__, __LINE__, error); | |||
goto done; | |||
} | |||
sleep(10); | |||
error = usb_wlan_cmd_get_scan_results(); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not get scan results (%d)\n", | |||
__func__, __LINE__, error); | |||
goto done; | |||
} | |||
sleep(10); | |||
done: | |||
usb_wlan_cmd_thread_done = 1; | |||
return NULL; | |||
} | |||
/* | |||
* main | |||
*/ | |||
int main(int argc, char **argv) | |||
{ | |||
unsigned char buf[256]; | |||
pthread_t tid; | |||
struct timeval tv; | |||
int error; | |||
pthread_mutex_init(&usb_wlan_cmd_mutex, NULL); | |||
pthread_cond_init(&usb_wlan_cmd_cond, NULL); | |||
error = libusb_init(&usb_ctx); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: libusb_init failed (%d)\n", __func__, __LINE__, error); | |||
exit(1); | |||
} | |||
libusb_set_debug(usb_ctx, 5); | |||
usb_dev_handle = libusb_open_device_with_vid_pid(usb_ctx, USB_VENDOR_ID, USB_PRODUCT_ID); | |||
if (!usb_dev_handle) { | |||
fprintf(stderr, "%s:%d: could not open device\n", __func__, __LINE__); | |||
exit(1); | |||
} | |||
if(libusb_kernel_driver_active(usb_dev_handle, USB_IFACE_NUMBER)) { | |||
fprintf(stdout, "%s:%d: kernel driver is attached\n", __func__, __LINE__); | |||
error = libusb_detach_kernel_driver(usb_dev_handle, USB_IFACE_NUMBER); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not detach kernel driver (%d)\n", | |||
__func__, __LINE__, error); | |||
exit(1); | |||
} | |||
fprintf(stdout, "%s:%d: kernel driver dettached\n", __func__, __LINE__); | |||
} | |||
error = libusb_claim_interface(usb_dev_handle, USB_IFACE_NUMBER); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not claim interface (%d)\n", | |||
__func__, __LINE__, error); | |||
exit(1); | |||
} | |||
error = libusb_control_transfer(usb_dev_handle, 0x40, 0x1, 0x9, 0x0, | |||
usb_magic_data, sizeof(usb_magic_data), 0); | |||
if (error < 0) { | |||
fprintf(stderr, "%s:%d: could not do control transfer (%d)\n", | |||
__func__, __LINE__, error); | |||
exit(1); | |||
} | |||
fprintf(stdout, "%s:%d: number of bytes transferred (%d)\n", __func__, __LINE__, error); | |||
error = libusb_control_transfer(usb_dev_handle, 0xc0, 0x0, 0x2, 0x0, buf, 2, 0); | |||
if (error < 0) { | |||
fprintf(stderr, "%s:%d: could not do control transfer (%d)\n", | |||
__func__, __LINE__, error); | |||
exit(1); | |||
} | |||
fprintf(stdout, "%s:%d: number of bytes received (%d)\n", __func__, __LINE__, error); | |||
fprintf(stdout, "%s:%d: 0x%02x 0x%02x\n", __func__, __LINE__, buf[0], buf[1]); | |||
usb_intr_transfer_ep5_in = libusb_alloc_transfer(0); | |||
if (!usb_intr_transfer_ep5_in) { | |||
fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__); | |||
exit(1); | |||
} | |||
memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf)); | |||
libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5, | |||
usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf), | |||
usb_intr_transfer_ep5_in_cb, NULL, 0); | |||
error = libusb_submit_transfer(usb_intr_transfer_ep5_in); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n", | |||
__func__, __LINE__, error); | |||
exit(1); | |||
} | |||
error = pthread_create(&tid, NULL, usb_wlan_cmd_thread, NULL); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not create WLAN command thread (%d)\n", | |||
__func__, __LINE__, error); | |||
exit(1); | |||
} | |||
while (!usb_wlan_cmd_thread_done) { | |||
tv.tv_sec = 1; | |||
tv.tv_usec = 0; | |||
error = libusb_handle_events_timeout(usb_ctx, &tv); | |||
if (error) { | |||
fprintf(stderr, "%s:%d: could not handle events (%d)\n", | |||
__func__, __LINE__, error); | |||
exit(1); | |||
} | |||
} | |||
libusb_free_transfer(usb_intr_transfer_ep5_in); | |||
error = libusb_release_interface(usb_dev_handle, USB_IFACE_NUMBER); | |||
if (error) | |||
fprintf(stderr, "%s:%d: could not release interface (%d)\n", | |||
__func__, __LINE__, error); | |||
libusb_close(usb_dev_handle); | |||
libusb_exit(usb_ctx); | |||
exit(0); | |||
} | |||
</pre> | |||
====Output==== | |||
<pre> | |||
glevand@debian-hdd:~/ps3_usb_wlan$ sudo ./ps3_usb_wlan | |||
sudo: unable to resolve host debian-hdd | |||
main:824: number of bytes transferred (32) | |||
main:833: number of bytes received (2) | |||
main:835: 0x20 0x31 | |||
usb_wlan_cmd_send:288: sending command (0x114f) data size (0x0518) command size (0x0524) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x1150) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0006) | |||
usb_handle_wlan_cmd_response:205: ==> command status != 0x1 | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0000) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
usb_wlan_cmd_send:288: sending command (0x1171) data size (0x0000) command size (0x000c) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x1172) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0000) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68) | |||
usb_handle_wlan_event:133: === got WLAN event === | |||
usb_handle_wlan_event:144: event_count (0x01) | |||
00000000: 00 04 00 00 10 00 00 00 3c 22 02 00 00 00 00 00 |........<"......| | |||
00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................| | |||
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............| | |||
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| | |||
usb_wlan_cmd_send:288: sending command (0x116f) data size (0x0004) command size (0x0010) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x1170) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0000) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
usb_wlan_cmd_send:288: sending command (0x115b) data size (0x005e) command size (0x006a) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x115c) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0000) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
usb_wlan_cmd_send:288: sending command (0x1161) data size (0x0020) command size (0x002c) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x1162) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0000) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
usb_wlan_cmd_send:288: sending command (0x110d) data size (0x0080) command size (0x008c) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x110e) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0000) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
usb_wlan_cmd_send:288: sending command (0x1031) data size (0x0002) command size (0x000e) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x1032) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0002) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
00000000: 00 00 |.. | | |||
usb_wlan_cmd_send:288: sending command (0x1041) data size (0x0006) command size (0x0012) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (42) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x1042) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0006) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
00000000: 00 11 22 33 44 55 |.."3DU | | |||
usb_wlan_cmd_send:288: sending command (0x0029) data size (0x0002) command size (0x000e) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x002a) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0002) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
00000000: 02 02 |.. | | |||
usb_wlan_cmd_send:288: sending command (0x110b) data size (0x000c) command size (0x0018) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (48) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x110c) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x000c) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
00000000: 01 00 00 00 00 00 00 00 20 00 00 00 |........ ... | | |||
usb_wlan_cmd_send:288: sending command (0x1109) data size (0x0016) command size (0x0022) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (58) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x110a) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0016) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
00000000: 01 00 00 00 15 27 12 00 06 00 00 00 09 00 01 00 |.....'..........| | |||
00000010: ff ff ff ff ff ff |...... | | |||
usb_wlan_cmd_send:288: sending command (0x0207) data size (0x0004) command size (0x0010) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x0208) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0004) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
00000000: 01 00 00 00 |.... | | |||
usb_wlan_cmd_send:288: sending command (0x0203) data size (0x0004) command size (0x0010) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x0204) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0004) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
00000000: 04 00 00 00 |.... | | |||
usb_wlan_cmd_send:288: sending command (0x105f) data size (0x000a) command size (0x0016) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x1060) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0000) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68) | |||
usb_handle_wlan_event:133: === got WLAN event === | |||
usb_handle_wlan_event:144: event_count (0x01) | |||
00000000: 80 00 00 00 00 10 00 00 9e 2b 02 00 04 00 00 00 |.........+......| | |||
00000010: fc 90 02 c0 01 00 00 00 00 00 00 00 00 00 00 00 |................| | |||
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............| | |||
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| | |||
usb_wlan_cmd_send:288: sending command (0x0099) data size (0x003e) command size (0x004a) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (98) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x009a) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x003e) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2| | |||
00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19| | |||
00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).| | |||
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |.............. | | |||
usb_wlan_cmd_send:288: sending command (0x1035) data size (0x0019) command size (0x0025) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (61) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x1036) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0019) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
00000000: 00 01 64 00 00 00 00 00 00 00 03 0d 01 02 03 04 |..d.............| | |||
00000010: 05 06 07 08 09 0a 0b 0c 0d |......... | | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68) | |||
usb_handle_wlan_event:133: === got WLAN event === | |||
usb_handle_wlan_event:144: event_count (0x01) | |||
00000000: 80 00 00 00 04 00 00 00 96 2e 02 00 01 00 00 00 |................| | |||
00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................| | |||
00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............| | |||
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| | |||
usb_wlan_cmd_send:288: sending command (0x1033) data size (0x05b0) command size (0x05bc) | |||
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === | |||
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (1403) | |||
usb_handle_wlan_cmd_response:158: === got WLAN command response === | |||
usb_handle_wlan_cmd_response:191: command header: | |||
usb_handle_wlan_cmd_response:192: command (0x1034) | |||
usb_handle_wlan_cmd_response:199: tag (0xcafe) | |||
usb_handle_wlan_cmd_response:201: status (0x0001) | |||
usb_handle_wlan_cmd_response:207: payload_size (0x0557) | |||
usb_handle_wlan_cmd_response:210: command payload: | |||
... | |||
Here is scan output (removed by me) | |||
... | |||
</pre> | |||
===Associate with AP=== | |||
* I got association with AP working. | |||
* If WLAN device is connected to an AP then the green LED is on, when data is received then the LED blinks. | |||
* '''Data reception works finally !!!''' | |||
====How to Associate with WPA AP==== | |||
* Set common configuration (command 0x1005) | |||
* Set WPA configuration (command 0x1019) | |||
* Set rate configuration (command 0x1ed) | |||
* Associate (command 0x1001) | |||
===Packet Reception=== | |||
* EP6 IN and EP7 IN endpoints are used for packet reception | |||
* LV2 sends bulk transfers to both endpoints | |||
* '''4''' bulk transfers are sent simultaneously for each enpoint | |||
* Every bulk transfer is of size '''0x620''' | |||
* '''Make sure you set multicast address filter properly or else you won't receive broadcast packets !!!''' | |||
* Bulk transfers returned by the host controller which do not contain any data have size of '''0x10''' bytes else transfers contain valid Ethernet frame. All 802.11 related data is stripped by the WLAN Gelic device. | |||
* '''Make sure you set right MAC address with command 0x115b else device won't be able to receive packets destined to its own MAC address !!!''' | |||
====Test with libusb==== | |||
<pre> | |||
usb_bulk_transfer_ep6_in_cb:318: === got data transfer === | |||
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (98) | |||
00000000: ff ff ff ff ff ff ?? ?? ?? ?? ?? ?? 08 00 45 00 |..............E.| | |||
00000010: 00 54 00 00 40 00 40 01 b5 fe c0 a8 01 5b c0 a8 |.T..@.@......[..| | |||
00000020: 01 ff 08 00 9c 69 0d 45 00 e2 4e 5d 34 26 00 07 |.....i.E..N]4&..| | |||
00000030: df e1 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 |................| | |||
00000040: 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 |.......... !"#$%| | |||
00000050: 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 |&'()*+,-./012345| | |||
00000060: 36 37 |67 | | |||
usb_bulk_transfer_ep6_in_cb:318: === got data transfer === | |||
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16) | |||
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 |................| | |||
usb_bulk_transfer_ep6_in_cb:318: === got data transfer === | |||
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16) | |||
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 |................| | |||
usb_bulk_transfer_ep6_in_cb:318: === got data transfer === | |||
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16) | |||
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 |................| | |||
</pre> | |||
====Multicast Address Filter==== | |||
* WLAN Gelic device supports hardware multicast address filtering | |||
* Multicast address filtering is implemented with MAC address hashing and filter bitmap | |||
* Filter bitmap is of size '''4 * 8''' bytes | |||
* Multicast address filter is set with command '''0x1161''' | |||
=====MAC Address Hash Function===== | |||
* Used by LV2 | |||
<pre> | |||
unsigned char hash(unsigned char *data, unsigned int size) | |||
{ | |||
unsigned int hash; | |||
int i, j; | |||
/*XXX: reverse data bits */ | |||
hash = 0xffffffff; | |||
for (i = 0; i < size; i++) { | |||
hash = (((unsigned int) data[i]) << 24) ^ hash; | |||
for (j = 0; j < 8; j++) { | |||
if (((int) hash) >= 0) { | |||
hash = hash << 1; | |||
} else { | |||
hash = (hash << 1) ^ 0x04c10000; | |||
hash = hash ^ 0x00001db7; | |||
} | |||
} | |||
} | |||
hash = ((hash >> 24) & 0xf8) | (hash & 0x7); | |||
return hash & 0xff; | |||
} | |||
h = hash(mac_addr, 6); | |||
v = 1 << (h & 0x1f); /* word value in filter */ | |||
p = h >> 5; /* word position in filter */ | |||
For broadcast address: | |||
------------------------ | |||
v = 0x20000000 | |||
p = 7 | |||
That's why 0x20 is used with command 0x1161 !!! Without it the device won't deliver broadcast traffic. | |||
Learned it the hard way, after 2 days of trying to get packet reception working :) | |||
</pre> | |||
===Packet Transmission=== | |||
* Tx packets are sent to EP6 OUT | |||
* Tx packets are normal Ethernet frames, they don't contain any WLAN data or other headers | |||
===AP Mode=== | |||
* I got AP mode working with security disabled for now | |||
====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 | |||
===ps3-jupiter Linux Drivers=== | |||
* ps3_jupiter.ko is the common part of STA and AP mode. It implements a command interface to WLAN Gelic device and disptaches events to STA and AP drivers. | |||
* ps3_jupiter_sta.ko is a STA mode implementation. | |||
* ps3_jupiter_ap.ko is a AP mode implementation. | |||
* Simple scanning works already in STA mode (try it out with '''iwlist scan''') | |||
* Packet reception works | |||
* Packet transmission works | |||
* '''WPA/WPA2''' fully working and usable with '''wpa_supplicant''' | |||
'''Finally, after several weeks of hard programming and reversing, the WLAN driver ps3_jupiter_sta achieved the milestone where i can use it with WPA2 :) I actually use it currently with WPA2 on my PS3 slim. It works damn !!! Try it out and report bugs and problems to me.''' | |||
====TODO==== | |||
* Implement association in STA mode (finished) | |||
* Implement packet reception and transmission in STA mode (finished) | |||
* Implement WEP support | |||
* Implement AP mode | |||
* Find out if Jupiter supports Monitor mode and if yes how to enable it | |||
* Implement EURUS driver for PHATs (has many advantages over the old OtherOS approach, e.g. AP mode) | |||
* Port to FreeBSD | |||
==LV2 Network Stack== | |||
* LV2 uses BSD network stack, e.g. '''struct mbuf''' | |||
* It's almost identical to FreeBSD network stack. | |||
===Network Device=== | |||
====IOCTLs==== | |||
=====Set Multicast Address Filter (0x81012000)===== | |||
* Sets multicast address filter | |||
* Uses LV1 calls '''lv1_net_remove_multicast_address''' and '''lv1_net_add_multicast_address''' for Ethernet Gelic device | |||
* Uses Eurus commands '''0x1161''', '''0x1163''' and '''0x1165''' for WLAN Gelic device | |||
=====Unknown (0x8101200E)===== | |||
* Uses LV1 call '''lv1_net_control(0x8000000000000001)''' | |||
=====Unknown (0x81040000)===== | |||
* Uses LV1 call '''lv1_net_control(0x8, [0x0, 0x1 or 0x2])''' for Ethernet Gelic device | |||
* Uses Eurus commands '''0x116F''', '''0x115D''' and '''0x115B''' for WLAN Gelic device | |||
=====Enable/Disable WOL Magic Packet (0x81080000)===== | |||
* 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 | |||
=====Unknown (0x81080002)===== | |||
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x3)''' for Ethernet Gelic device | |||
* Uses Eurus commands '''0x113D''' and '''0x1159''' for WLAN Gelic device | |||
=====Unknown (0x81080003)===== | |||
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x4)''' for Ethernet Gelic device | |||
* Uses Eurus command '''0x1161''' for WLAN Gelic device | |||
=====Unknown (0x81080005)===== | |||
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x6 /* GELIC_LV1_WOL_ADD_MATCH_ADDR */)''' for Ethernet Gelic device | |||
* Uses Eurus commands '''0x116D''' and '''0x1167''' for WLAN Gelic device | |||
===Network Packet=== | |||
* LV2 network packet is represented by '''struct mbuf''' | |||
=RSX= | |||
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#RSX gitbrew.org::RSX] <br /> | |||
==HV Calls== | |||
===lv1_gpu_memory_allocate=== | |||
* LV1 supports 16 memory handles simultaneously. | |||
* LV1 uses a bitmap to manage GPU VRAM. | |||
* The bitmap is located in LV1 memory, 4 double words. | |||
* Each bit corresponds to 1MB VRAM, 256bit = 256MB VRAM. | |||
* 2MB at the top of VRAM are preallocated as you can see below. | |||
<pre> | |||
<memory handle> = 0x5a5a5a5a xor <memory handle index> | |||
</pre> | |||
====Memory Context Object==== | |||
offset 0x8 - memory handle (4 bytes) | |||
offset 0x10 - VRAM LPAR start address (8 bytes) | |||
offset 0x18 - VRAM LPAR end address (8 bytes) | |||
====Test==== | |||
* The offset of bitmap could be different on your system because it's allocated dynamically. | |||
* '''First 9MB of VRAM were allocated by ps3fb Linux driver.''' | |||
Before allocating VRAM: | |||
<pre> | |||
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C | |||
00000000 00 00 00 00 00 00 01 ff 00 00 00 00 00 00 00 00 |.......ÿ........| | |||
00000010 00 00 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 |........À.......| | |||
</pre> | |||
After allocating 32 MB VRAM: | |||
<pre> | |||
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C | |||
00000000 00 00 01 ff ff ff ff ff 00 00 00 00 00 00 00 00 |...ÿÿÿÿÿ........| | |||
00000010 00 00 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 |........À.......| | |||
</pre> | |||
===lv1_gpu_context_allocate=== | |||
* Register %r4 is flags. | |||
* '''Found the place in LV1 where LV1 sets IO page size for GART memory mapping. We could patch it and set to 4KB. That would make a lot of things easier for RSX developers on Linux.''' | |||
* 1MB pages make RSX driver for Linux hard to implement because allocating 1Mb contiguous memory chunk on Linux is very very hard especially on a system with only 256MB and which was running for some time. | |||
* LV1 supports 16 contexts simultaneously. | |||
* LV1 has an array of context pointers. | |||
* Each context has an index and a handle. The handle is derived from the index of the context. | |||
<pre> | |||
<context handle> = 0x55555555 xor <context index> | |||
</pre> | |||
* Thats why first created context will have handle 0x55555555. | |||
====Context Object==== | |||
offset 0x8 - handle (4 bytes) | |||
offset 0x48 - IO page size, valid range is 4kB, 64KB and 1MB (8 bytes) | |||
====Flags==== | |||
'''0x2 - tells LV1 to use 64KB pages for GART memory mapping else LV1 uses 1MB pages''' | |||
===lv1_gpu_context_iomap=== | |||
* Internally uses lv1_put_iopte function | |||
* IO page size is the one set during lv1_gpu_context_allocate | |||
* IO address space id is 0x0. IO id is 0x1. | |||
===lv1_gpu_context_attribute=== | |||
====Attribute 0x1==== | |||
=====FIFO Command Buffer Setup===== | |||
<pre> | |||
lv1_gpu_context_attribute(context handle, 0x1, PUT offset, GET offset, 0x0, 0x0) | |||
</pre> | |||
====Attribute 0x101==== | |||
=====Set Flip Mode===== | |||
<pre> | |||
lv1_gpu_attribute(0x2, 0x1 /* head */, 0x0, 0x0) | |||
lv1_gpu_context_attribute(context handle, 0x101, 0x1 /* head */, sync mode, 0x0, 0x0) | |||
</pre> | |||
====Attribute 0x104==== | |||
=====Set Display Buffer===== | |||
<pre> | |||
lv1_gpu_context_attribute(context handle, 0x104, id, width << 32 | height, pitch << 32 | offset, 0x0) | |||
</pre> | |||
====Attribute 0x10a==== | |||
=====Get Flip Status===== | |||
* Reads a value at offset '''0x10C0 + 0x1 * 0x40''' in lpar_reports memory. | |||
=====Reset Flip Status===== | |||
<pre> | |||
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'''. | |||
* Offset into lpar_reports is '''0x10C0 + id * 0x40 = 0x10C0 + 0x1 * 0x40'''. | |||
* Why not access lpar_reports memory directly and use LV1 call instead ??? | |||
====Attribute 0x10b==== | |||
* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.''' | |||
=====Set Cursor Position===== | |||
<pre> | |||
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x3, x, y) | |||
</pre> | |||
=====Set Cursor Image Offset===== | |||
<pre> | |||
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x2, offset, 0x0) | |||
</pre> | |||
====Attribute 0x10c==== | |||
* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.''' | |||
=====Cursor Function 1===== | |||
<pre> | |||
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x1, 0x0, 0x0) | |||
</pre> | |||
=====Cursor Function 2===== | |||
<pre> | |||
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x2, 0x0, 0x0) | |||
</pre> | |||
====Attribute 0x10d==== | |||
* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.''' | |||
=====Cursor Function 1===== | |||
<pre> | |||
lv1_gpu_context_attribute(context handle, 0x10d, 0x1, 0x1, 0x0, 0x0) | |||
</pre> | |||
====Attribute 0x300==== | |||
=====Set Tile===== | |||
=====Set Invalidate Tile===== | |||
=====Bind Tile===== | |||
=====Unbind Tile===== | |||
====Attribute 0x301==== | |||
=====Set Zcull===== | |||
=====Bind Zcull===== | |||
=====Unbind Zcull===== | |||
====Attribute 0x601==== | |||
* Copies data from GART memory to VRAM. | |||
* LV1 uses internally the FIFO command buffer passed by ps3fb driver with lv1_gpu_context_iomap. | |||
FIFO commands: | |||
<pre> | |||
0x0004C184 | |||
0xFEED0001 | |||
0x0004C198 | |||
0x313371C3 | |||
0x00046300 | |||
0x0000000A | |||
for () | |||
{ | |||
for () | |||
{ | |||
0x0004630C | |||
<param> | |||
0x00046304 | |||
<param> | |||
0x0024C2FC | |||
0x00000001 | |||
0x00000003 | |||
0x00000003 | |||
<param1> | |||
<param2> | |||
<param3> | |||
<param4> | |||
0x00010000 | |||
0x00010000 | |||
0x0001C400 | |||
<param1> | |||
<param2> | |||
<param3> | |||
0x00000000 | |||
} | |||
} | |||
0x00040110 | |||
0x00000000 | |||
</pre> | |||
==FIFO Command Buffer== | |||
===FIFO Control Registers=== | |||
* LV1 call '''lv1_gpu_context_allocate''' returns LPAR address of FIFO control registers. | |||
* You have to map it into Linux address space before you can access FIFO control registers. | |||
* Value of PUT and GET registers are NOT expressed in Linux address space but in RSX address space. You have to convert it to RSX address space. | |||
* GET register is read-only and is modified by RSX while it's processing FIFO commands. | |||
===Kicking FIFO Command Buffer=== | |||
* As long as values of GET and PUT FIFO control registers are equal, RSX doesn't process commands from the FIFO command buffer. | |||
* When the value of PUT register is not equal to the value of GET register, RSX starts processing commands in the FIFO command buffer. | |||
* To execute FIFO commands, place them in the FIFO command buffer and change the value of PUT register. | |||
===FIFO Setup Programs of emer_init.self=== | |||
* [[PS3:HvReverseEngineering:emer_init.self:Program 1]] | |||
* [[PS3:HvReverseEngineering:emer_init.self:Program 2]] | |||
* [[PS3:HvReverseEngineering:emer_init.self:Program 3]] | |||
===FIFO Commands=== | |||
[[PS3:HvReverseEngineering:RSXFIFOCommands]] | |||
===Example How to Use FIFO Command Buffer=== | |||
Here is a small Linux kernel module which shows you how to use FIFO command buffer on Linux. | |||
* RSX allows to create multiple contexts. | |||
* This kernel module should run without problems with '''ps3fb''' driver already running. | |||
* Make sure you unload '''ps3vram''' driver before running this module because '''ps3vram''' allocates all available RSX memory for itself and because of this, '''lv1_gpu_memory_allocate''' will always fail. | |||
* This kernel module lets the RSX execute a simple program which contains only NOP (No Operation) commands. | |||
Download source code: [http://lol.notsoldierx.com/~glevand/ps3/linux/ps3rsx.tar.gz] | |||
====Source Code==== | |||
<pre> | |||
/* | |||
* PS3 RSX | |||
* | |||
* This program is free software; you can redistribute it and/or modify it | |||
* under the terms of the GNU General Public License as published | |||
* by the Free Software Foundation; version 2 of the License. | |||
* | |||
* This program is distributed in the hope that it will be useful, but | |||
* WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License along | |||
* with this program; if not, write to the Free Software Foundation, Inc., | |||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |||
*/ | |||
#include <linux/module.h> | |||
#include <linux/kernel.h> | |||
#include <linux/init.h> | |||
#include <linux/slab.h> | |||
#include <linux/io.h> | |||
#include <linux/delay.h> | |||
#include <asm/abs_addr.h> | |||
#include <asm/cell-regs.h> | |||
#include <asm/lv1call.h> | |||
#include <asm/ps3.h> | |||
#define RSX_FIFO_CMD_BUF_SIZE (1 * 1024 * 1024) | |||
#define RSX_MEM_SIZE (32 * 1024 * 1024) | |||
#define RSX_GPU_IOIF (0x0e000000ul) | |||
#define RSX_FIFO_CTRL_SIZE (4 * 1024) | |||
struct rsx_fifo_ctrl { | |||
u8 res[0x40]; | |||
u32 put; | |||
u32 get; | |||
}; | |||
static u32 *rsx_fifo_cmd_buf; | |||
static u64 rsx_fifo_cmd_buf_lpar; | |||
static u64 rsx_mem_handle, rsx_mem_lpar; | |||
static u64 rsx_ctx_handle; | |||
static u64 rsx_fifo_ctrl_lpar; | |||
static u64 rsx_drv_info_lpar; | |||
static u64 rsx_reports_lpar, rsx_reports_size; | |||
static struct rsx_fifo_ctrl *rsx_fifo_ctrl; | |||
/* | |||
* FIFO program | |||
*/ | |||
static u32 rsx_fifo_prg[] = { | |||
0x00000000, /* nop */ | |||
0x00000000, /* nop */ | |||
0x00000000, /* nop */ | |||
}; | |||
/* | |||
* ps3rsx_init | |||
*/ | |||
static int __init ps3rsx_init(void) | |||
{ | |||
unsigned long timeout; | |||
int res; | |||
/* FIFO command buffer must be allocated in XDR memory */ | |||
rsx_fifo_cmd_buf = kmalloc(RSX_FIFO_CMD_BUF_SIZE, GFP_KERNEL); | |||
if (!rsx_fifo_cmd_buf) { | |||
printk(KERN_INFO"could not allocate FIFO command buffer\n"); | |||
res = -ENOMEM; | |||
goto fail; | |||
} | |||
res = lv1_gpu_memory_allocate(RSX_MEM_SIZE, 0, 0, 0, 0, | |||
&rsx_mem_handle, &rsx_mem_lpar); | |||
if (res) { | |||
printk(KERN_INFO"lv1_gpu_memory_allocate failed (%d)\n", res); | |||
res = -ENXIO; | |||
goto fail_free_fifo_cmd_buf_mem; | |||
} | |||
res = lv1_gpu_context_allocate(rsx_mem_handle, 0, | |||
&rsx_ctx_handle, &rsx_fifo_ctrl_lpar, &rsx_drv_info_lpar, | |||
&rsx_reports_lpar, &rsx_reports_size); | |||
if (res) { | |||
printk(KERN_INFO"lv1_gpu_context_allocate failed (%d)\n", res); | |||
res = -ENXIO; | |||
goto fail_free_gpu_mem; | |||
} | |||
/* map FIFO command buffer into RSX address space */ | |||
rsx_fifo_cmd_buf_lpar = ps3_mm_phys_to_lpar(__pa(rsx_fifo_cmd_buf)); | |||
res = lv1_gpu_context_iomap(rsx_ctx_handle, | |||
RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar, RSX_FIFO_CMD_BUF_SIZE, | |||
CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M); | |||
if (res) { | |||
printk(KERN_INFO"lv1_gpu_context_iomap failed (%d)\n", res); | |||
res = -ENXIO; | |||
goto fail_free_gpu_mem; | |||
} | |||
/* map RSX FIFO control registers */ | |||
rsx_fifo_ctrl = (struct rsx_fifo_ctrl *) ioremap(rsx_fifo_ctrl_lpar, RSX_FIFO_CTRL_SIZE); | |||
if (!rsx_fifo_ctrl) { | |||
printk(KERN_INFO"could not map FIFO control\n"); | |||
res = -ENXIO; | |||
goto fail_free_gpu_mem; | |||
} | |||
/* PUT and GET offsets are in RSX address space */ | |||
res = lv1_gpu_context_attribute(rsx_ctx_handle, 0x1, | |||
RSX_GPU_IOIF + 0x0 /* PUT offset */, RSX_GPU_IOIF + 0x0 /* GET offset */, | |||
0x0, 0x0); | |||
if (res) { | |||
printk(KERN_INFO"lv1_gpu_context_attribute(0x1) failed (%d)\n", res); | |||
res = -ENXIO; | |||
goto fail_unmap_fifo_ctrl; | |||
} | |||
/* copy FIFO commands to FIFO command buffer */ | |||
memcpy(rsx_fifo_cmd_buf, rsx_fifo_prg, sizeof(rsx_fifo_prg)); | |||
printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put); | |||
/* kick FIFO */ | |||
rsx_fifo_ctrl->put = RSX_GPU_IOIF + sizeof(rsx_fifo_prg); | |||
/* poll until RSX is done processing FIFO commands */ | |||
timeout = 100; | |||
while (timeout--) { | |||
if (rsx_fifo_ctrl->get == rsx_fifo_ctrl->put) | |||
break; | |||
msleep(1); | |||
} | |||
printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put); | |||
if (rsx_fifo_ctrl->get != rsx_fifo_ctrl->put) { | |||
printk(KERN_INFO"FIFO command buffer timeout\n"); | |||
res = -ENXIO; | |||
goto fail_unmap_fifo_ctrl; | |||
} | |||
return 0; | |||
fail_unmap_fifo_ctrl: | |||
iounmap(rsx_fifo_ctrl); | |||
fail_free_gpu_mem: | |||
lv1_gpu_memory_free(rsx_mem_handle); | |||
fail_free_fifo_cmd_buf_mem: | |||
kfree(rsx_fifo_cmd_buf); | |||
fail: | |||
return res; | |||
} | |||
/* | |||
* ps3rsx_exit | |||
*/ | |||
static void __exit ps3rsx_exit(void) | |||
{ | |||
iounmap(rsx_fifo_ctrl); | |||
lv1_gpu_context_iomap(rsx_ctx_handle, RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar, | |||
RSX_FIFO_CMD_BUF_SIZE, CBE_IOPTE_M); | |||
lv1_gpu_context_free(rsx_ctx_handle); | |||
lv1_gpu_memory_free(rsx_mem_handle); | |||
kfree(rsx_fifo_cmd_buf); | |||
} | |||
module_init(ps3rsx_init); | |||
module_exit(ps3rsx_exit); | |||
MODULE_LICENSE("GPL"); | |||
MODULE_DESCRIPTION("PS3 RSX"); | |||
MODULE_AUTHOR("glevand"); | |||
</pre> | |||
====Test==== | |||
<pre> | |||
# insmod ./ps3rsx.ko | |||
# dmesg | |||
GET offset (0x0e000000) PUT offset (0x0e000000) # GET and PUT offsets before kicking FIFO | |||
GET offset (0x0e00000c) PUT offset (0x0e00000c) # GET and PUT offsets after kicking FIFO | |||
</pre> | |||
As you see, RSX processed our FIFO commands :) | |||
==Linux Driver== | |||
* '''DRI/DRM is the ONLY way to go !!! No hacks like kernel modules with tons of IOCTLs !!!''' | |||
* First implement 2D acceleration and then add 3D support | |||
* The driver consists of 2 parts: '''DDX driver''' for X11 (user space) and '''DRM driver''' for Linux Kernel (kernel space) | |||
* First implement DRM driver and test it from user space without DDX and libdrm by talking to it directly | |||
===DDX Driver=== | |||
* Use '''libdrm''' | |||
* Use '''EXA API''' for 2D acceleration on X11 (or maybe use '''XAA API''') | |||
* Use '''Kernel Mode Setting''' | |||
===DRM Driver=== | |||
* Extend '''nouveau''' driver or create a new one ??? | |||
* '''Decision: create new DRM driver in order to learn how DRM framework in Linux kernel works and because we have to use LV1 calls to access RSX (and because it's a lot more fun to do it on my own). But use nouveau as an example for DRM driver. Maybe i should better use radeon DRM driver as an example beacuse it seems to be better designed and implemnted !!!''' | |||
* The driver is very low level and allows direct access to almost all RSX funtions, e.g. FIFO buffer, to achieve maximum performance. | |||
* All data buffers, e.g. vertices and textures, are managed by DRM framework (Linux kernel). To avoid copying from user to kernel space, the buffers will be mmaped into user space. | |||
* Provides an interface to manage graphic objects in VRAM. | |||
* Use '''TTM''' or '''GEM''' ??? TTM is used by radeon and nouvea drivers, so i guess we could use it too. GEM is for Intel chips. | |||
* Extend '''libdrm''' library to support new DRM driver. | |||
* Fences can be implemented with '''RSX REF Control Register''' | |||
====Memory Management==== | |||
* Size of all memory objects must be multiple of the page size (4096 bytes) even if a smaller size is requested by user | |||
* Nouveau driver uses IOCTL '''DRM_NOUVEAU_GEM_NEW''' to allocate memory objects in VRAM or GART. The IOCTL returns the handle of the newly allocated memory object. | |||
* An example from Mesa how memory objects are used: [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] | |||
====Video RAM==== | |||
* VRAM is allocated once during context creating and cannot be changed during the whole life of the context. | |||
* '''lv1_gpu_memory_allocate''' returns LPAR address of allocated VRAM which can be mapped into kernel address space. | |||
* '''VRAM starts at offset 0x0 in GPU address space.''' | |||
* VRAM heap management is necessary, use e.g. TTM (ttm_bo_init_mm). | |||
* This memory type is used e.g. for vertices or textures. | |||
* It should be mappable from user space in order to allow user to put data there. | |||
* GameOS calls it '''Local Memory'''. | |||
* VRAM can be mapped into kernel-space with '''ioremap'''. | |||
* To map VRAM into user-space map it first into kernel-space with '''ioremap''' and then use '''remap_pfn_range''' to map into user-space. | |||
* Use '''VM_IO''' flag for this kind of memory when mapping it into user-space. | |||
* Mapping examples: [http://www.scs.ch/~frey/linux/memorymap.html] [http://www.cs.fsu.edu/~baker/devices/projects/antgeo/avnet_june19/pci_avnet.c] | |||
====GART Memory==== | |||
* GART memory region is a memory region in System Memory but accessible by RSX through GART [http://dri.freedesktop.org/wiki/GART]. | |||
* GameOS calls it '''Main Memory'''. | |||
* '''Problem: lv1_gpu_context_iomap supports ONLY 1MB and 64kB pages''' | |||
* Size of system memory objects mapped into GPU address space should be either multiple of 1MB which means wasting lots of RAM and we don't have enough of it anyways. This solution is NOT suitable. | |||
* Or place several GART memory objects into 1 MB page and map it. That would mean we have to use memory manager for each 1MB page. | |||
* That means, we have to allocate 1MB page even if user requested a smaller memory region. Then initialize a heap manager for this 1MB page and return ONLY requested size. The following requests for GART memory regions can be satisfied from the previously allocated 1MB pages which still have enough free memory. | |||
* FIFO command buffer is an example of a GART memory object which has to be mapped into GPU address space with lv1_gpu_context_iomap before it can be used by RSX. | |||
* User allocates FIFO command buffer in GART address space, maps it into user space, write commands into it and then pushes it to DRM driver which maps it into RSX address space and CALLs it. | |||
* '''TTM: TTM_PL_FLAG_TT for GART memory''' | |||
* '''GameOS applications using GCM library map GART memory beginning at offset 0x10000000 or 0x20000000, just after where the whole VRAM is mapped.''' | |||
* '''Don't use kmalloc for this type of memory. Use __get_free_pages and mark pages with flag VM_RESERVED before exporting it to user-space else they can be swapped out.''' | |||
* TTM uses '''struct ttm_backend_func''' to call driver specific GART mapping functions. '''nouveau_sgdma.c''' handles GART memory mapping. | |||
====CPU Memory==== | |||
* This type of memory cannot be accessed by RSX at all. | |||
* Because this type of memory is not mapped into RSX address space through GART we don't need to allocate it in 1MB multiples. | |||
* What do we need it for ??? | |||
====Mapping Memory Objects into Kernel-Space==== | |||
* Nouveau driver uses '''ttm_bo_kmap''' to map memory objects into kernel-space (see '''ttm_bo_util.c'''). | |||
* Nouveau driver uses '''ttm_bo_ioremap''' to map IO memory into kernel-space, e.g. VRAM or GPU registers (see '''ttm_bo_util.c''') which uses '''ioremp_wc''' or '''ioremp_nocache'''. | |||
* TTM uses page-wise allocation for buffers. The buffers are contiguous ONLY in a single page. That has a huge advantage over allocating 1MB contiguous memory blocks in kernel space. It's far easier to allocate a single page in Linux kernel than 1MB memory chunk, especially on PS3 arch which has only 256MB. | |||
* '''Problem: lv1_gpu_context_iomap allows ONLY 1MB pages. Use lv1_put_iopte ???'''. See [http://lwn.net/Articles/304188/], [http://lxr.free-electrons.com/source/arch/powerpc/platforms/ps3/mm.c?a=sh#L562], [http://wiki.ps2dev.org/ps3:hypervisor:lv1_put_iopte ] and [http://wiki.ps2dev.org/ps3:hypervisor:lv1_gpu_context_iomap]. | |||
* Yes, we can use '''lv1_put_iopte''' instead of '''lv1_gpu_context_iomap'''. That would solve the problem with 1MB pages on Linux. Both LV1 calls use the same internal LV1 function to map memory pages. | |||
* '''lv1_gpu_context_iomap uses IOAS_ID 0 and IOID 1.''' | |||
* TTM allows to map a buffer multiple times. Mapping information is stored in '''struct ttm_bo_kmap_obj'''. | |||
* '''To make single allocated pages look contiguous to kernel-space, TTM uses vmap'''. | |||
* '''It is possible to use 64KB pages for GART mapping without patching LV1. To enable 4KB pages support we have to patch LV1.''' | |||
* Tested with 64kB IO page size. It works fine. | |||
====Mapping Memory Objects into User-Space==== | |||
* User-space programs should be able to allocate memory objects in VRAM or GART and map it with '''mmap syscall'''. | |||
* See '''nouveau_ttm.c:nouveau_ttm_mmap'''. | |||
* Mapping memory objects into user-space avoids copying of data between user/kernel spaces. | |||
* Problem: how to identify memory objects ??? | |||
* '''libdrm''' uses handles which are returned by DRM kernel driver when a new memory object is created. The handle is passed to mmap syscall as parameter '''offset'''. DRM driver looks up the handle and identifies the appropriate memory object which is mapped into user-space then. | |||
* Nouveau driver uses TTM framework to map memory objects into user-space. TTM doesn't map all pages owned by the memory object at once but installs '''VM operation fault''' which maps single pages on demand. It makes sense because user application rarely accesses all pages of the mapped memory object at once. | |||
* To map memory objects located in VRAM we have to map it into kernel space first with '''ioremap'''. | |||
====FIFO Command Buffer==== | |||
* Every context has its own one main FIFO command buffer which is NOT accessible directly by user space. | |||
* User-space applications can allocate additional FIFO command buffers in GART memory space, map it into user space, store commands there and submit to DRM driver. | |||
* Nouveau driver uses IOCTL '''NOUVEAU_GEM_PUSHBUF''' to execute FIFO command buffers. See '''nouveau_gem.c:nouveau_gem_ioctl_pushbuf'''. | |||
* By user applications submitted FIFO command buffers are mapped by DRM driver into RSX address space first and then executed with CALL command. | |||
* '''Problem: All references to graphics objects contained in FIFO command buffers must be expressed in RSX address space. How does user space know the right offsets of the referenced objects ???''' | |||
* To solve the above problem, Nouveau driver uses relocations which are submitted to DRM driver together with FIFO command buffers. The DRM driver applies the specified relocations before executing the FIFO command buffer. See '''nouveau_gem.c:nouveau_gem_pushbuf_reloc_apply'''. | |||
* Relocations contain memory object handles which they apply to. The DRM driver looks up the memory object by its handle and the memory objects contain GPU address space offsets. | |||
=====Example===== | |||
<pre> | |||
--------------------------------------------------------------- | |||
| | | |||
| | | |||
\|/ Main FIFO command buffer (one per allocated context) | | |||
------------------------------ ------------------------------------ | |||
| | | | | | | | |||
| ... | CALL | ... | CALL | ... | JMP | | |||
| | | | | | | | |||
------------------------------ ------------------------------------ | |||
| /|\ | /|\ | |||
-------------| | | | | |||
| ------| --------| | | |||
\|/ | | ---| | |||
----------------------- | | | |||
| | | | | | | |||
| ... | ... | RET | | | | |||
| | | | | | | |||
----------------------- | | | |||
FIFO command buffer 1 | | | |||
(allocated by user space) \|/ | | |||
----------------------- | |||
| | | | | |||
| ... | ... | RET | | |||
| | | | | |||
----------------------- | |||
FIFO command buffer 2 | |||
(allocated by user space) | |||
</pre> | |||
====Fences==== | |||
* Nouveau driver implements DRM fences with REF control register. See '''nouveau_fence.c:nouveau_fence_new'''. | |||
* Newer Nvidia chips support semaphores. Nouveau driver uses semaphores for fences if they are supported. | |||
* libgcm functions '''SetWriteCommandLabel''' and '''SetWaitLabel''' use semaphores. | |||
* '''SetWriteCommandLabel''' releases semaphore and '''SetWaitLabel''' acquires semaphore. | |||
* Semaphores are placed in VRAM. Nouveau driver creates a small VRAM heap for semaphores. See '''nouveau_fence.c:nouveau_fence_channel_init'''. | |||
====IOCTLs==== | |||
=====Context Create===== | |||
* Creates new RSX context | |||
* Allocates VRAM and memory for FIFO buffer | |||
* Needed VRAM size and FIFO buffer size must be known during context creation | |||
=====Context Destroy===== | |||
* Destroys previously allocated context | |||
=====Context Attribute===== | |||
* Changes context attributes | |||
=====Graphic Object Creatre===== | |||
* Create a graphic object either in VRAM or in XDR | |||
* Used to create FIFO command buffers too (only in XDR of course because RSX supoorts FIFO command buffer in XDR only) | |||
=====Graphic Object Destroy===== | |||
* Frees previously created graphic object | |||
=====FIFO Execute===== | |||
* Allows user space applications to execute FIFO commands. | |||
* To avoid copying of buffers allocated by user space to main FIFO command buffer use CALL and RET RSX FIFO commands to execute FIFO commands in buffers allocated by user space. | |||
* Several FIFO command buffers can be submitted at once. | |||
=====Framebuffer===== | |||
* Kernel DRM driver has to implement a frame buffer driver too | |||
* Nouvea driver allocates frame buffer in video RAM and maps it into kernel address space (see '''nouveau_fbcon.c:nouveau_fbcon_create'''). Current ps3fb Linux driver doesn't allocate frame buffer in vide RAM but in system RAM. | |||
* Direct access to video RAM from kernel is very very slow but some of frame buffer functions in Nouvea driver are hardware accelerated. We could do it the same way on Linux and get a hardware accelerated frame buffer this way. Not sure why ps3fb authors didn't add hardware acceleration to frame buffer. The reason why it was not implemnted in ps3fb is because LV1 doesn't create 2D graphic objects needed for 2D hardware acceleration. | |||
* '''lv1_gpu_allocate_memory''' returns LPAR address of video RAM allocated for the RSX context. | |||
* Unfortunately '''lv1_gpu_context_allocate''' doesn't initialize 2D ROP objects but we could use 3D operations to implement 2D ROPs. | |||
===libdrm=== | |||
* Add support for RSX DRM to '''libdrm''' | |||
===Test Kernel Module and Program=== | |||
* I uploaded here a test kernel module and a test user application: [http://www.gitbrew.org/~glevand/ps3/linux/ps3rsx_kernel.tar.gz] and [http://www.gitbrew.org/~glevand/ps3/linux/ps3rsx_user.tar.gz] | |||
* I used a similar technique for mapping GPU resources into user-space like Linux kernel DRM drivers do it, e.g. Nouveau. But of course everything is very simplified in comparison with Nouveau driver. All GPU resources are mapped to user-space with mmap and there is no data copying between user and kernel space, for performance reasons. Mapping GPU resources into user-space like this is more flexible than IOCTLs. | |||
* '''The purpose of the kernel module and the user application is to test how RSX works, to test FIFO commands and other stuff i reversed from Lv2. It's NOT for end users.''' | |||
* Before loading the kernel module make sure ps3vram kernel module is NOT loaded. | |||
* I used 64kB IO pages for GPU context. 4kB IO page size would be definitely a lot better for that we have to patch LV1. I will add this patch to my ps3mfw tasks for LV1. | |||
* Just load the kernel module and then run the user application. | |||
* The user application maps all context resources and executes some simple FIFO commands, like JMP or SET REF. | |||
* I will add more examples later. | |||
* By default, the kernel module allocates 8MB VRAM, 64kB FIFO and 1MB GART memory. You can change it by using kernel module parameters. | |||
* Take a look at how i made non-contiguous allocated GART memory look contiguous to GPU, kernel-space and user-space. | |||
* The kernel module needs some IOCTLs, e.g. for setting display buffers or flip status, because it can be done ONLY with LV1 calls. I will add it later. | |||
===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=== | |||
* Size is 32KB just like AACS specifications prescribes (See AACS Common Specification 3.2.5.2 Host Revocation List Record) | |||
* '''We could replace HRL with an older one in BD drive flash and restore revoked Host Certificates !!!''' | |||
==Device Commands== | |||
===Get Profile (0x11)=== | |||
* BD profile can be read with LV1 call '''lv1_send_storage_device_command''' and command '''0x11''' | |||
* LV1 sends SCSI command '''GET CONFIGURATION''' to BD drive with '''requested type 0x0''', '''starting feature number 0x0''' and '''allocation length 0x8''' | |||
* See SCSI command '''GET CONFIGURATION''' | |||
===Auto Request Sense Mode On/Off (0x30)=== | |||
* LV1 expects a 4 byte value: 0x0 - On, 0x1 - Off | |||
* can be get/set via GameOS sc0x25C/604: sys_storage_send_device_command(fd of bdvd,0x30,value,4,0,0 ) | |||
==SCSI Commands== | |||
===Get Configuration=== | |||
Getting the profile of a BD movie disc: | |||
<pre> | |||
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00 | |||
SCSI Status: Good | |||
Sense Information: | |||
sense buffer empty | |||
Received 8 bytes of data: | |||
00 00 00 00 38 00 00 00 40 ...8...@ | |||
# 0x40 means BD-ROM | |||
</pre> | |||
Getting the profile of a PS3 game disc: | |||
<pre> | |||
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00 | |||
SCSI Status: Good | |||
Sense Information: | |||
sense buffer empty | |||
Received 8 bytes of data: | |||
00 00 00 00 38 00 00 ff 71 ...8...q | |||
# 0x71 means PS3 BD-ROM | |||
</pre> | |||
===Get SS Key=== | |||
* By SCSI standard undocumented parameters are used | |||
* '''SCSI Report Key''' command with '''key format 0x3''' and '''key class 0xe0''' | |||
* 8 bytes are returned by BD drive | |||
* Used by VSH | |||
Test with PS3 game disc: | |||
<pre> | |||
# sg_raw -r 8 /dev/sr0 a4 00 00 00 00 00 00 e0 00 08 03 00 | |||
SCSI Status: Good | |||
Sense Information: | |||
sense buffer empty | |||
Received 8 bytes of data: | |||
00 00 06 00 00 00 00 00 04 ........ | |||
</pre> | |||
===Eject Media=== | |||
<pre> | |||
sg_raw /dev/sr0 0x1b 00 00 00 02 00 | |||
</pre> | |||
===Load Media=== | |||
<pre> | |||
sg_raw /dev/sr0 0x1b 00 00 00 03 00 | |||
</pre> | |||
===Mode Select 10=== | |||
====Enable Buffer Write==== | |||
* Uses '''PF 0x1''', '''SP 0x0''' and '''parameter list length 0x10''' | |||
* Uses the following parameter list: '''0x00 0x0e 0x00 0x00 0x00 0x00 0x00 0x00 0x2d 0x6 <buffer id> 0x00 0x00 0x00 0x00 0x00''' | |||
* '''Enables writing to BD drive flash, e.g. to HRL buffer !!!''' | |||
Test with sg3-utils which enables write to HRL buffer: | |||
<pre> | |||
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=== | |||
* Used e.g. by Update Manager to send BD firmware to BD drive | |||
* '''Mode 0x5 (Download microcode and save)''' is used e.g. to write HRL to BD drive flash | |||
* '''Mode 0x7 (Download microcode with offsets and save)''' is used e.g. to write BD firmware to BD drive flash | |||
==AACS== | |||
===AACS SPU Module=== | |||
* BD player on GameOS uses '''AacsModule.spu.isoself''' (/dev_flash/bdplayer) to perform AACS authentication | |||
* Tested on OtherOS++ 3.55 | |||
* Host certificate, host private key and AACS LA public key are stored encrypted with AES-256-CTR in the SPU module and are decrypted when the SPU module is loaded or when it's accessed first. The AES-256-CTR key and IV are in the SPU module too. | |||
* 4.76 uses new Host certificate | |||
====Communication==== | |||
* BD player reads '''EID3''' with '''Indi Info Manager 0x17001/0x17002''' services and passes it to SPU module | |||
* '''EID3 is NEVER used in the SPU module although BD player passes it to the SPU module''' | |||
* Data is exchanged with the SPU module through '''SPU In Mbox''', '''SPU Out Intr Mbox''' and a data buffer in XDR memory of size '''0x2000''' bytes. | |||
====Commands==== | |||
* The SPU module supports max '''0x78''' (til 4.75, 0x57 since 4.76) commands but not all are implemented | |||
* After a command is finished by the SPU module, it sends the status of the command to PPU through '''SPU Out Intr Mbox'''. Value 0 means success. | |||
{| class="wikitable sortable" | |||
|+ 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)=== | |||
* SHA1 hash is encrypted/decrypted by '''SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)''' | |||
* SHA1 hash is read with '''VTRM service 0x2005 (Retrieve)''' | |||
* SHA1 hash is written with '''VTRM service 0x2003 (Store With Update)''' | |||
===Content Revocation List (CRL)=== | |||
* SHA1 hash is encrypted/decrypted by '''SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)''' | |||
* SHA1 hash is read with '''VTRM service 0x2005 (Retrieve)''' | |||
* SHA1 hash is written with '''VTRM service 0x2003 (Store With Update)''' | |||
===Host Revocation List (HRL)=== | |||
* Stored in BD drive flash | |||
* It can be read/written with SCSI commands '''READ/WRITE BUFFER'''. Yeah, it can be written too :D | |||
====Read HRL from BD Drive Flash==== | |||
* It seems that BD drive has several HRL in its flash | |||
* Empty HRL stored on BD drive flash can be read with SCSI command '''READ BUFFER''' by using as '''mode 0x2''', '''buffer id 0x4''' and '''allocation length 0x40''' | |||
* Current HRL stored on BD drive flash can be read with SCSI command '''READ BUFFER''' by using as '''mode 0x2''', '''buffer id 0x5''' | |||
====Empty HRL==== | |||
<pre> | |||
# sg_read_buffer -m 2 -i 4 -o 0 -l $((0x40)) /dev/sr0 | |||
00 10 00 00 0c 00 03 10 03 00 00 00 01 21 00 00 34 | |||
10 00 00 00 00 00 00 00 00 1b 0b f2 6d 47 9e 77 62 | |||
20 3d 91 fc 78 b1 59 c9 52 ca a4 c7 41 85 24 96 64 | |||
30 8d 1d 95 8e 9b 84 c6 fa 4a dd 43 9b 42 98 fe ff | |||
# byte 0x21 at offset 0xc means Record Type HRL | |||
# as you see this HRL is empty | |||
</pre> | |||
====Current HRL==== | |||
<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 | |||
# the current HRL is NOT empty and is from MKBv9 because the only BD movie i played on my PS3 has MKBv9 | |||
</pre> | |||
===PS3 BD Player Host Certificate=== | |||
<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 | |||
# Host ID is 0xffff80000039 | |||
</pre> | |||
===PS3 BD Player Host Private Key=== | |||
<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> | |||
===AACS Processing Keys=== | |||
====MKB v1==== | |||
<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 | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000001 | |||
MKB U masks and UVs: 514 | |||
=applying subset-difference= | |||
index: 0 | |||
UV: 0x00000001 | |||
U mask: 0xff800000 | |||
V mask: 0xfffffffe | |||
=applying device key= | |||
index: 244 | |||
UV: 0x00000100 | |||
U mask: 0xff800000 | |||
V mask: 0xfffffe00 | |||
device key: | |||
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| | |||
processing key: | |||
00000000: 09 f9 11 02 9d 74 e3 5b d8 41 56 c5 63 56 88 c0 |.....t.[.AV.cV..| | |||
C value: | |||
00000000: cb 06 90 db e6 54 55 7b 12 62 aa d7 89 f4 9d 92 |.....TU{.b......| | |||
media key: | |||
00000000: b4 6c 48 5e f7 51 ae 29 ef 87 bc 58 28 f3 2a 8d |.lH^.Q.)...X(.*.| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 46 32 5b 42 48 b4 86 5a fc ef 75 25 47 b1 b5 12 |F2[BH..Z..u%G...| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef 0d ac 14 b9 ee f4 bd cc |.#Eg............| | |||
</pre> | |||
====MKB v3==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v3.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000003 | |||
MKB U masks and UVs: 528 | |||
=applying subset-difference= | |||
index: 14 | |||
UV: 0x00000080 | |||
U mask: 0xff800000 | |||
V mask: 0xffffff00 | |||
=applying device key= | |||
index: 244 | |||
UV: 0x00000100 | |||
U mask: 0xff800000 | |||
V mask: 0xfffffe00 | |||
device key: | |||
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| | |||
processing key: | |||
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| | |||
C value: | |||
00000000: 0a b7 33 82 85 62 91 d1 91 4a 95 9e 36 18 c7 a1 |..3..b...J..6...| | |||
media key: | |||
00000000: 6e da eb d4 88 aa 38 58 74 26 35 fd fd 36 66 d5 |n.....8Xt&5..6f.| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 99 76 96 b0 6f 49 37 9b c4 b9 2b be 73 ce 96 1a |.v..oI7...+.s...| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef fb 01 cc 85 eb e5 bf 0a |.#Eg............| | |||
</pre> | |||
====MKB v4==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v4.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000004 | |||
MKB U masks and UVs: 526 | |||
=applying subset-difference= | |||
index: 12 | |||
UV: 0x00000080 | |||
U mask: 0xff800000 | |||
V mask: 0xffffff00 | |||
=applying device key= | |||
index: 244 | |||
UV: 0x00000100 | |||
U mask: 0xff800000 | |||
V mask: 0xfffffe00 | |||
device key: | |||
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| | |||
processing key: | |||
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| | |||
C value: | |||
00000000: bf 71 0c 8b 46 a0 24 d8 f0 3a a1 26 37 9d fb fc |.q..F.$..:.&7...| | |||
media key: | |||
00000000: ef 18 c0 dd bf 02 32 a1 2f 57 f7 65 79 2c 1c 58 |......2./W.ey,.X| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 54 85 08 a9 6a 70 2a c9 32 e3 74 a6 55 78 6c 01 |T...jp*.2.t.Uxl.| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef da 90 cf 2a e5 b2 6c 45 |.#Eg.......*..lE| | |||
</pre> | |||
====MKB v7==== | |||
<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 | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000007 | |||
MKB U masks and UVs: 526 | |||
=applying subset-difference= | |||
index: 7 | |||
UV: 0x00000080 | |||
U mask: 0xff800000 | |||
V mask: 0xffffff00 | |||
=applying device key= | |||
index: 244 | |||
UV: 0x00000100 | |||
U mask: 0xff800000 | |||
V mask: 0xfffffe00 | |||
device key: | |||
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| | |||
processing key: | |||
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| | |||
C value: | |||
00000000: 21 fd c9 4b 3e 1a f3 fe 9e b4 7a e6 ef 01 75 1b |!..K>.....z...u.| | |||
media key: | |||
00000000: af cd e2 c8 67 12 a4 b6 a8 58 0c 15 ef 07 6e f8 |....g....X....n.| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 4b 21 29 a5 0f db 96 bc bc 01 04 71 42 79 00 e5 |K!)........qBy..| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef 4e f9 d2 05 6e 19 c1 79 |.#Eg....N...n..y| | |||
</pre> | |||
====MKB v8==== | |||
<pre> | |||
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v8.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000008 | |||
MKB U masks and UVs: 523 | |||
=applying subset-difference= | |||
index: 4 | |||
UV: 0x00000080 | |||
U mask: 0xff800000 | |||
V mask: 0xffffff00 | |||
=applying device key= | |||
index: 244 | |||
UV: 0x00000100 | |||
U mask: 0xff800000 | |||
V mask: 0xfffffe00 | |||
device key: | |||
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| | |||
processing key: | |||
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| | |||
C value: | |||
00000000: 73 2d 10 bd f8 b4 87 e2 86 a6 d5 3a 6d db 69 15 |s-.........:m.i.| | |||
media key: | |||
00000000: dd 46 d4 0d 26 54 5a ce 6c 59 0c 65 b7 2b 3a 9f |.F..&TZ.lY.e.+:.| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: c6 f6 f9 54 ce 90 e0 5e 2b 3b e4 1e 24 92 90 b2 |...T...^+;..$...| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef 97 e6 61 8b d1 69 3e a0 |.#Eg......a..i>.| | |||
</pre> | |||
====MKB v9==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v9.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000009 | |||
MKB U masks and UVs: 520 | |||
=applying subset-difference= | |||
index: 2 | |||
UV: 0x00000080 | |||
U mask: 0xff800000 | |||
V mask: 0xffffff00 | |||
=applying device key= | |||
index: 244 | |||
UV: 0x00000100 | |||
U mask: 0xff800000 | |||
V mask: 0xfffffe00 | |||
device key: | |||
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| | |||
processing key: | |||
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| | |||
C value: | |||
00000000: a4 5a c6 87 43 49 70 bb bf 0c 22 52 83 9e 2a f6 |.Z..CIp..."R..*.| | |||
media key: | |||
00000000: 37 02 bd fc 96 dc a2 18 2e 55 b0 79 6d ad 36 6b |7........U.ym.6k| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 4d 5b 7b 9c 5d ee 55 a6 94 de e1 db 8d 08 c7 a2 |M[{.].U.........| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef cd 1d a8 8a 42 5a 10 43 |.#Eg........BZ.C| | |||
</pre> | |||
====MKB v10==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v10.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x0000000a | |||
MKB U masks and UVs: 522 | |||
=applying subset-difference= | |||
index: 3 | |||
UV: 0x00000080 | |||
U mask: 0xff800000 | |||
V mask: 0xffffff00 | |||
=applying device key= | |||
index: 244 | |||
UV: 0x00000100 | |||
U mask: 0xff800000 | |||
V mask: 0xfffffe00 | |||
device key: | |||
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| | |||
processing key: | |||
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| | |||
C value: | |||
00000000: d4 77 dd 1a 8a 5c 6d d1 dd 31 2d af f7 d3 14 fa |.w...\m..1-.....| | |||
media key: | |||
00000000: 38 32 2b 3c 61 b0 35 b4 52 89 84 59 f4 7a 76 e6 |82+<a.5.R..Y.zv.| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 3f d3 d5 fb 42 37 d9 05 b8 db 6b 03 a0 fe 2e 48 |?...B7....k....H| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef 65 b1 87 8c eb 0d 60 0f |.#Eg....e.....`.| | |||
</pre> | |||
====MKB v12==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v12.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x0000000c | |||
MKB U masks and UVs: 522 | |||
=applying subset-difference= | |||
index: 3 | |||
UV: 0x00000080 | |||
U mask: 0xff800000 | |||
V mask: 0xffffff00 | |||
=applying device key= | |||
index: 244 | |||
UV: 0x00000100 | |||
U mask: 0xff800000 | |||
V mask: 0xfffffe00 | |||
device key: | |||
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| | |||
processing key: | |||
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| | |||
C value: | |||
00000000: 89 75 89 e6 6f 4a de 95 11 32 57 6a cb 99 dd 69 |.u..oJ...2Wj...i| | |||
media key: | |||
00000000: 4b dd 69 9d 32 98 d7 b0 ad 32 71 6b 3d 9c e3 c2 |K.i.2....2qk=...| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 8d 43 fd f2 15 fa 58 78 64 db 25 46 62 ab 02 30 |.C....Xxd.%Fb..0| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef e6 1c bf 98 45 82 64 d9 |.#Eg........E.d.| | |||
</pre> | |||
====MKB v14==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v14.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x0000000e | |||
MKB U masks and UVs: 526 | |||
=applying subset-difference= | |||
index: 6 | |||
UV: 0x00000248 | |||
U mask: 0xfffffe00 | |||
V mask: 0xfffffff0 | |||
=applying device key= | |||
index: 28 | |||
UV: 0x00000280 | |||
U mask: 0xfffffe00 | |||
V mask: 0xffffff00 | |||
device key: | |||
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...| | |||
processing key: | |||
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........| | |||
C value: | |||
00000000: 8c 7e 31 e8 15 17 7e c3 2c 67 b7 cc 87 e9 39 c3 |.~1...~.,g....9.| | |||
media key: | |||
00000000: 4b b1 31 d1 6e 0e 86 45 89 07 a2 68 91 c4 e5 38 |K.1.n..E...h...8| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 20 03 8c 70 7d ab d0 6f ba 86 39 f0 31 26 86 5f | ..p}..o..9.1&._| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef 27 9f e5 35 0b df 3d a5 |.#Eg....'..5..=.| | |||
</pre> | |||
====MKB v15==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v15.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x0000000f | |||
MKB U masks and UVs: 527 | |||
=applying subset-difference= | |||
index: 6 | |||
UV: 0x00000248 | |||
U mask: 0xfffffe00 | |||
V mask: 0xfffffff0 | |||
=applying device key= | |||
index: 28 | |||
UV: 0x00000280 | |||
U mask: 0xfffffe00 | |||
V mask: 0xffffff00 | |||
device key: | |||
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...| | |||
processing key: | |||
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........| | |||
C value: | |||
00000000: 75 da 59 cf 0d c2 c0 95 86 fc 6b 8e 2e e9 cc 85 |u.Y.......k.....| | |||
media key: | |||
00000000: 28 46 25 38 3d cc 4f 1f 90 be 7d f7 8a ba 7b fd |(F%8=.O...}...{.| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 8d d2 69 e0 b7 6a 44 53 03 ad ef 58 44 fc a7 d7 |..i..jDS...XD...| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef ff 6a 7d c3 17 bb 19 11 |.#Eg.....j}.....| | |||
</pre> | |||
====MKB v16==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v16.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000010 | |||
MKB U masks and UVs: 531 | |||
=applying subset-difference= | |||
index: 5 | |||
UV: 0x00000248 | |||
U mask: 0xfffffe00 | |||
V mask: 0xfffffff0 | |||
=applying device key= | |||
index: 28 | |||
UV: 0x00000280 | |||
U mask: 0xfffffe00 | |||
V mask: 0xffffff00 | |||
device key: | |||
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...| | |||
processing key: | |||
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........| | |||
C value: | |||
00000000: f8 49 9b d1 32 f9 6e 8d 33 98 35 a8 54 80 d9 fe |.I..2.n.3.5.T...| | |||
media key: | |||
00000000: 3a bf bf d7 7e b8 01 43 a9 3c 15 3f ba 47 8c e1 |:...~..C.<.?.G..| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 8a 67 86 b6 9d 0d 22 dd 5d c2 88 1f 08 f3 ab b4 |.g....".].......| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef d6 32 1f 17 c4 2f e2 4a |.#Eg.....2.../.J| | |||
</pre> | |||
====MKB v17==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v17.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000011 | |||
MKB U masks and UVs: 540 | |||
=applying subset-difference= | |||
index: 14 | |||
UV: 0x00000308 | |||
U mask: 0xffffff00 | |||
V mask: 0xfffffff0 | |||
=applying device key= | |||
index: 21 | |||
UV: 0x00000340 | |||
U mask: 0xffffff00 | |||
V mask: 0xffffff80 | |||
device key: | |||
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss| | |||
processing key: | |||
00000000: 46 5f a8 be 82 85 09 01 4d 05 d2 fc ce ff 35 d2 |F_......M.....5.| | |||
C value: | |||
00000000: 01 f7 54 0b 34 e8 c1 ce 63 8d ea fa bc ce 6e 7b |..T.4...c.....n{| | |||
media key: | |||
00000000: ef 63 4e a8 ca 06 d1 6a c7 21 65 1b 18 b3 04 c6 |.cN....j.!e.....| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: d3 b9 d4 9c b6 94 47 d5 3d cc 42 fe 3e 47 40 04 |......G.=.B.>G@.| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef f6 b4 c8 6a b7 b8 39 fc |.#Eg.......j..9.| | |||
</pre> | |||
====MKB v18==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v18.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000012 | |||
MKB U masks and UVs: 543 | |||
=applying subset-difference= | |||
index: 17 | |||
UV: 0x00000320 | |||
U mask: 0xffffff00 | |||
V mask: 0xffffffc0 | |||
=applying device key= | |||
index: 21 | |||
UV: 0x00000340 | |||
U mask: 0xffffff00 | |||
V mask: 0xffffff80 | |||
device key: | |||
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss| | |||
processing key: | |||
00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..| | |||
C value: | |||
00000000: 7a 8f 03 41 27 c4 86 58 05 37 3a 90 de f8 de 26 |z..A'..X.7:....&| | |||
media key: | |||
00000000: e3 ed cd b4 59 b4 12 d4 ae f9 4d 8e 78 7a cd 7d |....Y.....M.xz.}| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: ea 45 fa 35 65 70 56 6f 6a 86 65 ad 52 e7 71 a4 |.E.5epVoj.e.R.q.| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef bd 36 f9 ce 60 54 80 3c |.#Eg.....6..`T.<| | |||
</pre> | |||
====MKB v19==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v19.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000013 | |||
MKB U masks and UVs: 544 | |||
=applying subset-difference= | |||
index: 17 | |||
UV: 0x00000320 | |||
U mask: 0xffffff00 | |||
V mask: 0xffffffc0 | |||
=applying device key= | |||
index: 21 | |||
UV: 0x00000340 | |||
U mask: 0xffffff00 | |||
V mask: 0xffffff80 | |||
device key: | |||
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss| | |||
processing key: | |||
00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..| | |||
C value: | |||
00000000: b9 0b 55 d1 18 3c cc 80 20 1c 9f 26 c3 58 27 18 |..U..<.. ..&.X'.| | |||
media key: | |||
00000000: 75 a9 79 9c 67 50 13 89 98 62 34 5b eb 54 34 dd |u.y.gP...b4[.T4.| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: c4 f0 ce 75 1b 12 b9 f0 22 2f 31 70 66 a9 6a b8 |...u...."/1pf.j.| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef 66 5c 65 d3 c4 4c c7 b0 |.#Eg....f\e..L..| | |||
</pre> | |||
====MKB v20==== | |||
<pre> | |||
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v20.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000014 | |||
MKB U masks and UVs: 544 | |||
=applying subset-difference= | |||
index: 19 | |||
UV: 0x00000384 | |||
U mask: 0xffffff80 | |||
V mask: 0xfffffff8 | |||
=applying device key= | |||
index: 18 | |||
UV: 0x00000384 | |||
U mask: 0xffffff80 | |||
V mask: 0xfffffff8 | |||
device key: | |||
00000000: fb 4a c3 90 09 e8 21 13 d4 5e cf 4b 7e ae a4 67 |.J....!..^.K~..g| | |||
processing key: | |||
00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k| | |||
C value: | |||
00000000: 10 9f f1 69 36 07 7d 7e ad 8f d2 1a 28 c5 09 ed |...i6.}~....(...| | |||
media key: | |||
00000000: dc 9f 08 f7 cb 1b f8 c4 cf 96 4e 96 df 23 56 58 |..........N..#VX| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 18 ca f5 51 8f 36 ef 2f 7a 49 78 ff 54 40 a5 f1 |...Q.6./zIx.T@..| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef c5 5d 11 08 c3 26 db 48 |.#Eg.....]...&.H| | |||
</pre> | |||
====MKB v21==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v21.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000015 | |||
MKB U masks and UVs: 552 | |||
=applying subset-difference= | |||
index: 19 | |||
UV: 0x00000384 | |||
U mask: 0xffffff80 | |||
V mask: 0xfffffff8 | |||
=applying device key= | |||
index: 18 | |||
UV: 0x00000384 | |||
U mask: 0xffffff80 | |||
V mask: 0xfffffff8 | |||
device key: | |||
00000000: fb 4a c3 90 09 e8 21 13 d4 5e cf 4b 7e ae a4 67 |.J....!..^.K~..g| | |||
processing key: | |||
00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k| | |||
C value: | |||
00000000: c0 0c fa bf f0 fe f2 32 77 19 db c4 d8 f8 60 c9 |.......2w.....`.| | |||
media key: | |||
00000000: 55 83 aa 69 ff 52 16 83 c2 93 b3 48 03 2a 57 38 |U..i.R.....H.*W8| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 12 5b f2 75 c8 f8 05 6b 4f 31 a5 ea 4a 12 9f a9 |.[.u...kO1..J...| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef dc 46 45 b4 79 8d 4f 68 |.#Eg.....FE.y.Oh| | |||
</pre> | |||
====MKB v23==== | |||
<pre> | |||
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v23.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000017 | |||
MKB U masks and UVs: 556 | |||
=applying subset-difference= | |||
index: 17 | |||
UV: 0x00000384 | |||
U mask: 0xffffffe0 | |||
V mask: 0xfffffff8 | |||
=applying device key= | |||
index: 7 | |||
UV: 0x00000384 | |||
U mask: 0xffffffe0 | |||
V mask: 0xfffffff8 | |||
device key: | |||
00000000: 8b f4 fb d9 1a 7f b7 db 85 76 d1 e5 a1 5a 85 44 |.........v...Z.D| | |||
processing key: | |||
00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr| | |||
C value: | |||
00000000: f0 81 d4 93 aa b5 01 1a a7 ff 8e 18 8a 48 8a 2d |.............H.-| | |||
media key: | |||
00000000: 02 04 59 d0 7c b5 54 94 bf 46 9b 98 91 1e 43 1f |..Y.|.T..F....C.| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 24 a1 27 f9 30 70 25 67 07 2f 2a d4 13 89 0d aa |$.'.0p%g./*.....| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef 21 00 20 84 c4 5f 36 78 |.#Eg....!. .._6x| | |||
</pre> | |||
====MKB v25==== | |||
<pre> | |||
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v25.inf | |||
=MKB= | |||
type: | |||
0x00031003 | |||
version: | |||
0x00000019 | |||
MKB U masks and UVs: 564 | |||
=applying subset-difference= | |||
index: 13 | |||
UV: 0x00000384 | |||
U mask: 0xffffffe0 | |||
V mask: 0xfffffff8 | |||
=applying device key= | |||
index: 7 | |||
UV: 0x00000384 | |||
U mask: 0xffffffe0 | |||
V mask: 0xfffffff8 | |||
device key: | |||
00000000: 8b f4 fb d9 1a 7f b7 db 85 76 d1 e5 a1 5a 85 44 |.........v...Z.D| | |||
processing key: | |||
00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr| | |||
C value: | |||
00000000: 19 62 23 7d 81 01 c2 55 2f 36 20 1b 3e 69 40 10 |.b#}...U/6 .>i@.| | |||
media key: | |||
00000000: 70 b5 9f 35 86 5d 18 73 bb 80 c3 2b f7 41 f6 14 |p..5.].s...+.A..| | |||
=MKB verify media key data= | |||
encrypted: | |||
00000000: 89 be 1e 1e b1 93 4c f2 2d ac c3 ce ed 10 07 f0 |......L.-.......| | |||
decrypted: | |||
00000000: 01 23 45 67 89 ab cd ef 3f 5d 87 7a 88 09 db c4 |.#Eg....?].z....| | |||
</pre> | |||
===Documentation=== | |||
* SCSI Specification: [http://www.13thmonkey.org/documentation/SCSI/] | |||
* AACS Specification Common: [http://www.aacsla.com/specifications/specs091/AACS_Spec_Common_0.91.pdf] | |||
* AACS Specification Pre-recorded Video Book [http://www.aacsla.com/specifications/AACS_Spec_Prerecorded_Final_0.951.pdf] | |||
* AACS Tutorial: [http://forum.doom9.org/showthread.php?t=122363] | |||
* AACS Overview: [http://www.cacr.math.uwaterloo.ca/techreports/2007/cacr2007-25.pdf] | |||
==CSS== | |||
===CSS SPU Module=== | |||
* DVD player on GameOS uses '''CssModule.spu.isoself''' (/dev_flash/bdplayer) to perform CSS authentication | |||
====Commands==== | |||
* The SPU module supports max '''0x25''' commands but not all are implemented | |||
* After a command is finished by the SPU module, it sends the status of the command to PPU through '''SPU Out Intr Mbox'''. Value 0 means success. | |||
=====Generate Host Challenge Key (0x3)===== | |||
* Generates '''0x10''' bytes of '''host challenge key''' | |||
=====Set Drive Key1 (0x4)===== | |||
* Sends '''key1''' of size '''0x5''' returned by DVD drive to the SPU module | |||
=====Set Drive Challenge Key (0x5)===== | |||
* Sends '''0x10''' bytes of '''drive challenge key''' to the SPU module | |||
=====Calculate Host Key2 (0x6)===== | |||
* Calculates '''key2''' of size '''0x5''' | |||
=====Get Host Key2 (0x7)===== | |||
* Returns '''key2''' of size '''0x5''' | |||
=====Set Disc Key (0x8)===== | |||
* Sends Disc Key block of size '''0x800''' to the SPU module | |||
=====Decrypt Sector (0xc)===== | |||
* Decrypts the passed sector | |||
===CSS Salt=== | |||
* It's NOT in clear text in the SPU module, it's obfuscated by xoring '''0xDEAF''' (SONY employees have a sense of humor). | |||
* There are 2 bytes for every salt byte | |||
Obfuscated: | |||
<pre> | |||
71 ED 3F A3 DA FE E4 94 40 8C | |||
</pre> | |||
Clear text: | |||
<pre> | |||
F4 10 45 A3 E2 | |||
</pre> | |||
===PS3 DVD Player Key Index=== | |||
<pre> | |||
0x69 | |||
</pre> | |||
===Documentation=== | |||
* The Content Scrambling System (CSS): [http://www.tinyted.net/eddie/css_auth.html] | |||
* Cryptanalysis of Contents Scrambling System: [http://www.cs.cmu.edu/~dst/DeCSS/FrankStevenson/analysis.html] | |||
* Cryptography in Home Entertainment: [http://www.math.ucsd.edu/~crypto/Projects/MarkBarry/index.htm] | |||
* Patching DVD Firmware: [http://xvi.rpc1.org/Patching%20DVD%20firmware.pdf] | |||
==CPRM== | |||
===Commands=== | |||
* The SPU module supports '''0x13''' commands. | |||
===4C Secret Constant (S-Box)=== | |||
===Documentation=== | |||
* Cryptomeria C2 Specification: [http://edipermadi.files.wordpress.com/2008/08/cryptomeria-c2-spec.pdf] | |||
* Cryptoanalysis of C2: [http://caislab.kaist.ac.kr/lecture/2010/spring/cs548/advanced/A05.pdf] | |||
==Remarrying BD Drive on OtherOS++== | |||
===fdm_spu_module.self=== | |||
* This SPU module can create either P- or S-Block which are sent to BD drive | |||
* '''EID2''' is passed to the SPU module | |||
* A XDR memory buffer of size '''0x1000''' is passed to the SPU module | |||
* 4 bytes at offset 0x10 of the XDR memory buffer contain the type of block which should be produced by the SPU module | |||
* When the SPU module is finished, the XDR memory buffer contains the needed block | |||
* After the S- and P-Blocks are produced by the SPU module, they are 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==== | |||
{| 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==== | |||
Decrypted P-Block (and EID4) contains region settings (see below) | |||
In decrypted P-Block(bytes 0x30 and 0x32) and in EID4(first byte) these bytes match [[Product Code]]: | |||
{| class="wikitable sortable" style="font-size:small; border:2px ridge #999999;" | |||
|- | |||
! Hex !! bitflag !! [[Product Code]] !! Console Type !! Remarks | |||
|- | |||
| 0xFF || '''11111111''' || {{TID80}} || No BD playback on that [[Product Code]] | |||
|- | |||
| 0xFF || '''11111111''' || {{TID81}} || No BD playback on that [[Product Code]] | |||
|- | |||
| 0xFF || '''11111111''' || {{TID82}} || No BD playback on that [[Product Code]] | |||
|- | |||
| 0x01 || 0000000'''1''' || {{TID83}} || bit 0 (Region 0: Japan?) | |||
|- | |||
| 0x02 || 000000'''1'''0 || {{TID84}} || bit 1 (Region 1: USA & Canada, Bermuda, and US Territories) | |||
|- | |||
| 0x04 || 00000'''1'''00 || {{TID85}} || bit 2 (Region 2: Europe (with the exceptions of Russia, Ukraine, Belarus), South Africa, Swaziland, Middle East, Egypt, Lesotho, and Greenland) | |||
|- | |||
| 0x10 || 000'''1'''0000 || {{TID86}} || bit 4 (Region 3: Southeastern Asia) | |||
|- | |||
| 0x04 || 00000'''1'''00 || {{TID87}} || bit 2 (Region 2: Europe (with the exceptions of Russia, Ukraine, Belarus), South Africa, Swaziland, Middle East, Egypt, Lesotho, and Greenland) | |||
|- | |||
| 0x08 || 0000'''1'''000 || {{TID88}} || bit 3 (Region 4: Latin America and Australia) | |||
|- | |||
| 0x08 || 0000'''1'''000 || {{TID89}} || bit 3 (Region 4: Latin America and Australia) | |||
|- | |||
| 0x20 || 00'''1'''00000 || {{TID8A}} || bit 5 (Region 5: Russia, Asia (non-southeast), and Africa) | |||
|- | |||
| 0x10 || 000'''1'''0000 || {{TID8B}} || bit 4 (Region 3: Southeastern Asia) | |||
|- | |||
| 0x20 || 00'''1'''00000 || {{TID8C}} || bit 5 (Region 5: Russia, Asia (non-southeast), and Africa) | |||
|- | |||
| 0x40 || 0'''1'''000000 || {{TID8D}} || bit 6? (Region 6: China) | |||
|- | |||
| 0x10 || 000'''1'''0000 || {{TID8E}} || bit 4 (Region 3: Southeastern Asia) | |||
|- | |||
| 0x08 || 0000'''1'''000 || {{TID8F}} || bit 3 (Region 4: Latin America and Australia) | |||
|- | |||
| 0xFF || '''11111111''' || {{TIDA0}} || No BD playback on that [[Product Code]] | |||
|- | |||
|} | |||
=====Creating===== | |||
=====Sending to BD Drive===== | |||
====S-Block==== | |||
=====Creating===== | |||
=====Sending to BD Drive===== | |||
====HRL==== | |||
=====Empty HRL===== | |||
=====Sending to BD Drive===== | |||
=VTRM= | |||
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#VTRM gitbrew.org::HV#VTRM] <br /> | |||
==VTRM Services== | |||
===Store With Update (0x2003)=== | |||
* Used by GameOS BD player to update DRL/CRL hashes | |||
===Retrieve (0x2005)=== | |||
* '''Product mode''' is NOT required | |||
* '''0x40''' bytes of data are read from NOR flash, decrypted by SYSCON and returned to the caller | |||
* Used e.g. by GameOS BD player to read SHA1 hashes of DRL and CRL | |||
====DRL and CRL Hashes==== | |||
* Written by GameOS BD player during DRL/CRL update | |||
* Read by GameOS BD player | |||
* Hashes are stored encrypted on NOR flash | |||
* Encryption/decryption is done by SYSCON (SYSCON Manager) | |||
Test with ps3dm-utils: | |||
<pre> | |||
# mount dev_flash3 | |||
glevand@debian-hdd:~/ps3dm-utils$ sudo mount /dev/ps3vflashe /mnt | |||
# DRL SHA1 hash | |||
glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/drl/DRL1 | |||
8f0652bc6162a240362f90f1b2e5405bb82ee502 /mnt/data-revoke/drl/DRL1 | |||
# CRL SHA1 hash | |||
glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/crl/CRL1 | |||
96791f41f9a76f4d895dd5820db108ec03d19250 /mnt/data-revoke/crl/CRL1 | |||
# Retrieve DRL and CRL SHA1 hashes from VTRM | |||
# DRL hash is first and then follows CRL hash | |||
glevand@debian-hdd:~/ps3dm-utils$ sudo ./ps3dm_vtrm -l 0x0 -p 0x1070000034000001 /dev/ps3dmproxy retrieve 0 | |||
0x8f 0x06 0x52 0xbc 0x61 0x62 0xa2 0x40 0x36 0x2f 0x90 0xf1 0xb2 0xe5 0x40 0x5b 0xb8 0x2e 0xe5 0x02 | |||
0x96 0x79 0x1f 0x41 0xf9 0xa7 0x6f 0x4d 0x89 0x5d 0xd5 0x82 0x0d 0xb1 0x08 0xec 0x03 0xd1 0x92 0x50 | |||
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
</pre> | |||
===Backup Flash (0x2012)=== | |||
* Requires enabled '''product mode''' or else service returns always error '''0x5''' | |||
* Reads and returns data from NOR flash beginning at NOR flash offset '''0xec0000''' | |||
Test with ps3dm-utils: | |||
<pre> | |||
# enable product mode | |||
# ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0 | |||
/dev/ps3dmproxy: SS retval 0 | |||
# ps3dm_vtrm /dev/ps3dmproxy backup_flash 0 0x200 | hexdump -C | |||
00000000 53 43 45 49 ff ff ff ff ff ff ff ff ff ff ff ff |SCEIÿÿÿÿÿÿÿÿÿÿÿÿ| | |||
00000010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ| | |||
* | |||
00000200 | |||
# dd if=/dev/ps3nflasha bs=1 count=$((0x100)) skip=$((0xec0000)) | hexdump -C | |||
00000000 53 43 45 49 ff ff ff ff ff ff ff ff ff ff ff ff |SCEIÿÿÿÿÿÿÿÿÿÿÿÿ| | |||
00000010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ| | |||
* | |||
</pre> | |||
===Flash Address Size (0x2016)=== | |||
* Requires enabled '''product mode''' or else service returns always error '''0x5''' | |||
* Returns 2 64bit values: offset and size of NOR flash region | |||
Test with ps3dm-utils: | |||
<pre> | |||
# ps3dm_um /dev/ps3dmproxy read_eprom 0x48c07 | |||
0xff | |||
# ps3dm_vtrm /dev/ps3dmproxy flash_addr_size | |||
/dev/ps3dmproxy: SS retval 5 | |||
# enable product mode | |||
# ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0 | |||
/dev/ps3dmproxy: SS retval 0 | |||
# ps3dm_um /dev/ps3dmproxy read_eprom 0x48c07 | |||
0x00 | |||
# ps3dm_vtrm /dev/ps3dmproxy flash_addr_size | |||
0x0000000000000000 0x0000000000040000 | |||
</pre> | |||
=Revoke List= | |||
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#Revoke_List gitbrew::Revoke List]<br /> | |||
==LPAR 1 System Call 0x1004A== | |||
* Installs new revoke list in LV1 | |||
* LPAR 1 processes can use this syscall to install new revoke lists at runtime | |||
* '''lv2ldr''' is loaded by LV1 and used to verify the passed revoke list | |||
* After lv2ldr is done verifying the passed revoke list, it checks for stop code and if it's '''0xB''' then LV1 replaces the old revoke list with the new one | |||
* If the verification of the revoke list was successfull then LV1 installs new revoke list and replaces the old one in the ISO loader table at address '''0x10100''' | |||
== | ==rvk_list_verifier== | ||
* | * Stop code 0xB means that the passed revoke list is valid. | ||
<pre> | <pre> | ||
/ | root@debian-hdd:/home/glevand/rvk_list_verifier# cat /proc/rvk_list_verifier/debug | ||
PPE id (0x0000000000000001) VAS id (0x0000000000000002) | |||
lv1_construct_logical_spe (0x00000000) | |||
SPE id (0x0000000000000033) | |||
lv1_enable_logical_spe (0x00000000) | |||
lv1_set_spe_interrupt_mask(0) (0x00000000) | |||
lv1_set_spe_interrupt_mask(1) (0x00000000) | |||
lv1_set_spe_interrupt_mask(2) (0x00000000) | |||
lv1_set_spe_privilege_state_area_1_register (0x00000000) | |||
ea (0xc000000003f40000) esid (0xc000000008000000) vsid (0x0000408f92c94500) | |||
lv1_get_spe_interrupt_status(0) (0x00000000) | |||
lv1_get_spe_interrupt_status(1) (0x00000000) | |||
lv1_get_spe_interrupt_status(2) (0x00000000) | |||
sleep | |||
lv1_get_spe_interrupt_status(0) (0x00000000) | |||
lv1_get_spe_interrupt_status(1) (0x00000000) | |||
lv1_get_spe_interrupt_status(2) (0x00000000) | |||
out interrupt mbox (0x0000000000000001) | |||
lv1_clear_spe_interrupt_status(2) (0x00000000) | |||
transferring ldr args to LS | |||
waiting until MFC transfers are finished | |||
MFC transfers done | |||
out mbox (0x00000001) | |||
sleep | |||
lv1_get_spe_interrupt_status(0) (0x00000000) | |||
lv1_get_spe_interrupt_status(1) (0x00000000) | |||
lv1_get_spe_interrupt_status(2) (0x00000000) | |||
problem status (0x000b0082) | |||
lv1_destruct_logical_spe (0x00000000) | |||
</pre> | </pre> | ||
{{Reverse engineering}}<noinclude>[[Category:Main]]</noinclude> | |||
Revision as of 06:26, 20 August 2021
Warning, this page is way too long and is voted to be split into seperate sections
Difference between Debug Firmware HV and Retail HV
There is no difference between debug firmware lv1.self and retail firmware lv1.self The differences reside on the repository nodes loaded because of policies/flags.
DECR/Tool + CEX/Retail LV1.self 1.60 1.92 3.41 3.55
HSPRG
- The hypervisor stores a pointer to some structures per LPAR in HSPRG0 register.
- There are actually 2 HSPRG0 values: one for each thread of Cell CPU !!!
- There is a HSPRG0 array at 0x8(-0x69A0(HSPRG0)) + 0x20.
LPAR
LPAR = Logical Partition
lpar1 starts at 0x<unknown>, and it's believed to be the memory space where lv1 stores its variables, flags and other data.
lpar2 starts at 0x80000000000 and it's believed to be the memory space where lv2 stores its variables, flags and other data.
The pointer to active LPAR is stored at -0x67E8(HSPRG0).
vtable
0x0033CA40 (3.15)
Member variables
offset 0x38 - some pointer
offset 0x50 - LPAR id (8 bytes)
offset 0x70 - pointer to VAS id bitmap
offset 0x78 - power of 2 of word size from VAS id bitmap (4 bytes), equal to 6
offset 0x7C - number of 64-bit words in VAS id bitmap(4 bytes)
Interrupt handling
The pointer to the interrupt handler that is called e.g. when an external interrupt occurs is at -0x69F0(HSPRG0).
0x00001930 (3.15 and 2.60)
Interrupt vector tables
There are 2 interrupt vector tables. One for each thread. The pointer to these tables is at -0x6950(HSPRG0).
offset 0x8 - IIC memory base address (8 bytes)
offset 0x10 - thread register offset (8 bytes)
offset 0x18 - start of interrupt vector table (19 entries, each entry 32 bytes)
Interrupt vector table entry
offset 0x0 - pointer to interrupt handler
offset 0x8 - TOC
offset 0x10 - 0
offset 0x18 - parameter to interrupt handler
Interrupt handlers
Spurious interrupt handler
0x002BC174 (3.15)
RSX
0x00219A44 (3.15)
0x002176FC (2.60)
SB bus
0x002B9CC4 (3.15)
I/O address translation
0x002CD7D8 (3.15)
0x002C9214 (2.60)
Performance monitor
0x002F0584 (3.15)
0x002EB1B0 (2.60)
Token manager
0x002BBA9C (3.15)
0x002B754C (2.60)
HV call
- The address of HV table is stored at -0x6FC8(HSPRG0).
- The address of HV table size is stored at -0x6FD0(HSPRG0).
HV call
editorial note: The table listed here was moved/merged to a seperate page : HV Syscalls
Initializing HV Call Table
set_lv1_hvcall_table
0x002C02B4 (3.41)
0x002C1F04 (3.15)
0x002C2B4C (3.55)
This function sets pointer to HV Call Table and the size of HV Call Table in HSPRG context of a LPAR.
set_lv1_hvcall_table_entry_invalid
0x002C1F28 (3.15)
0x002C02D8 (3.41)
0x002C2B70 (3.55)
This function initializes an entry in HV Call Table to the Invalid HV Call function.
Memory HV call
- All memory HV calls branch to lv1_mm_call
- lv1_mm_call has it's own function table
- Memory HV call number = HV call number
Memory HV call table
- Each entry is a pointer to a function TOC entry.
- table size = 256
0x00364208 (3.15)
0x00362308 (3.41)
0x00366088 (3.55)
Memory HV calls
lv1_map_htab - 0x002D595C (3.15)
lv1_unmap_htab - 0x002D56B8 (3.15)
lv1_allocate_memory - 0x002D72F0 (3.15)
lv1_release_memory - 0x002D66A4 (3.15)
lv1_query_logical_partition_address_region_info - 0x002C9B24 (3.15)
lv1_create_repository_node - 0x002DD014 (3.15)
lv1_get_repository_node_value - 0x002DD260 (3.15)
lv1_undocumented_function_231 - 0x0030B560 (3.15)
System call
HV Processes do not use HV calls. They use syscalls only.
System call handler
0x002974D8 (3.15)
0x00292F6C (2.60)
There are 2 system call tables in HV. The first one stores system calls 0 - 36. The second one stores system calls 0x10000 - 0x100FF.
UX System call table 0 - 36
0x0035FAE8 (3.15)
0x00358ED0 (2.60)
System call numbers
0x0 - void eosh(void) //end_of_signal_handling(void)
0x1 - pid_t getpid(void)
0x2 - pid_t getppid(void)
0x3 - pid_t fork(void)
0x4 - void exit(int status)
0x5 - void execv(const char *path, char *const argv[])
0x6 - void wait(int *status)
0x7 - int open(const char *path, int flags)
0x8 - void close(int fd)
0x9 - ssize_t read(int fd, void *buf, unsigned int nbyte)
0xA - ssize_t write(int fd, const void *buf, unsigned int nbyte)
0xB - void lseek(int fd, long offset, int whence)
0xC - unlink(const char *path)
0xD - void signal(int sig, void *func(int sig))
0xE - int kill(int pid, int signal_type)
0xF - int brk(void *)
0x10 - int socket(int af, int type, int protocol) (supports only address family 0x1F, type 0x0 and protocol 0x0)
0x11 - int bind(int sockfd , const sockaddr *addr, unsigned int addrlen)
0x12 - int listen(int sockfd, int backlog)
0x13 - int accept(int sockfd, sockaddr *addr, unsigned int *addrlen)
0x14 - int connect(int sockfd, const sockaddr *serv_addr, unsigned int addrlen)
0x15 - void putchar(int c)
0x16 - int pause(void)
0x17 - int sleep(unsigned int seconds)
0x18 - int mmap(void *addr, unsigned long size, int prot, int flags, int fd, long offset, void *mapped_addr)
0x19 - int munmap (void *addr, unsigned long size)
0x1A - int chdir(const char *path)
0x1B - void getchar(char *c)
0x1C - map_pages(...) (used for alloc)
0x1D - unmap_pages(...) (used for free)
0x1E - select(...)
0x1F - getcwd(...)
0x20 - Not used
0x21 - alarm(...)
0x22 - ioctl(...)
0x23 - pme_memalign(...)
0x24 - ?
PMI System call table 0x10000 - 0x100FF
0x0035DE78 (3.15)
0x00357260 (2.60)
System call numbers
0x10000 - allocate_memory(LPAR id, size, log2 of page size, ?, ?) / construct_memory_segment
0x10001 - query_logical_partition_address_region_info
0x10002 - translate_logical_partition_to_physical_address(LPAR id, LPAR address, physical addr)
0x10003 - map_physical_address_region
0x10004 - unmap_physical_address_region
0x10005 - construct_logical_pu
0x10006 - destruct_logical_pu
0x10007 - activate_logical_pu(LPAR id, PPE id)
0x10009 - construct_logical_partition(0, LPAR id, outlet)
0x1000A - get_logical_console_info
0x1000B - get_remote_file_size
0x1000C - read_remote_file
0x1000D - write_remote_file
0x1000E - release_memory_region(LPAR id, memory region address)
0x1001A - construct_event_receive_port
0x1001B - destruct_event_receive_port
0x1001C - request_to_connect_event_ports
0x1001D - connect_event_ports
0x1001E - destruct_event_send_port
0x1001F - send_event_externally
0x10020 - get_status_of_event_send_port
0x10021 - get_event_port_connection_request
0x10022 - end_of_control_signal_processing
0x10024 - shutdown_logical_partition(LPAR id, shutdown command)
0x10025 - destruct_logical_partition(LPAR id)
0x10026 - get_logical_partition_info
0x10027 - read_privilege_set
0x10028 - modify_privilege_set
0x10029 - get_remote_file_size_long_name
0x1002A - read_remote_file_long_name
0x1002B - write_remote_file_long_name
0x1002C - construct_scheduling_table
0x1002D - set_scheduling_slot
0x1002E - load_scheduling_table
0x10032 - poweroff
0x10033 - get_remote_file_name
0x10034 - allocate_cp_channel
0x10035 - release_cp_channel
0x10036 - power_down
0x10037 - ?
0x10038 - ?
0x10039 - ?
0x10040 - construct_spe_type_1(SPE id, shaddow_addr) / construct_logical_spu
0x10041 - destruct_spe(SPE id) / destruct_logical_spu
0x10042 - decrypt_lv2_self(spe id, LPAR auth id, SELF file image ptr, LPAR memory address)
0x10043 - load_spe_module(spe id, SCE module ptr, arg1, arg2, arg3, arg4)
0x10044 - disable_spe_execution
0x10045 - read_spu_puint_mb(unsigned long spu_id, unsigned long msg)
0x10046 - read_spe_problem_state_register(spe id, register offset, value) / read_spu_problem_state_area_register
0x10047 - write_spe_problem_state_register(spe id, register offset, value) / write_spu_problem_state_area_register
0x1004A - install_revoke_list
0x1004B - disable_spe_loading
0x1004C - install_access_control_table?
0x1004D - get_storage_status?
0x1004E - get_region_table_bits?
0x1004F - commit_region_update?
0x10050 - abort_region_update?
0x10051 - set_storage_tampered?
0x10053 - pmi_set_guest_os_mode
0x1007F - pause
0x10080 - get_total_execution_time
0x10081 - reset
0x10083 - construct_logical_rsx
0x10084 - construct_virtual_uart(LPAR id, VUART id, VUART data buffer size)
0x10085 - destruct_virtual_uart(LPAR id, VUART id)
0x10086 - establish_virtual_uart_channel
0x10088 - RSX_syscall_10088(LPAR id)
0x10089 - RSX_syscall_10089
0x1008A - RSX_syscall_1008A
0x100BE - lv1_ioctl
0x100C0 - create_repository_node(LPAR id)
0x100C1 - get_repository_node_value(LPAR id)
0x100C2 - modify_repository_node_value(LPAR id)
0x100C3 - remove_repository_node(LPAR id)
Process
Process table
HV supports only 32 processes simultaneously. The number of processes currently running in HV is stored at address 0x0035EA54 (3.15) and 0x00357E3C (2.60).
The process table is an array of 32 process table entries.
0x0036C930 (4.30)
0x0036C8B0 (4.21)
0x00365458 (4.11)
0x0035F8D0 (3.55)
0x0035C550 (3.41)
0x0035E850 (3.15)
0x00357C38 (2.60)
Process table entry
offset 0x0 - process status ? (8 bytes)
offset 0x8 - pointer to Process object
create_new_proc
This function creates a new Process object.
0x00298E2C (3.15)
0x002948BC (2.60)
Parameters
r3 - pointer to parent Process object
r4 - ?
copy_user_data
This function copies data to/from user space.
0x00299688 (3.15)
0x00295118 (2.60)
Parameters
r3 - pointer to Process object
r4 - some address in address space of Process
r5 - pointer to buffer in HV space
r6 - size to copy
r7 - ?
r8 - direction of copy (0 - copy from user space, != 0 - copy to user space)
r9 - ?
vtable
Processes have no vtables. That means they have no virtual functions.
Member variables
offset 0x0 - PID (4 bytes)
offset 0x8 - pointer to parent Process object
offset 0x10 - pointer to AddressSpace object
offset 0x30 - pointer to first PThread object of process
offset 0x38 - array of signal handlers (192 * 8 bytes)
offset 0x638 - pointer to pointer to ELF image
offset 0x640 - start of file table (20 * 24 bytes)
offset 0x820 - exit status (4 bytes)
offset 0x898 - pointer to Inode object of current directory
offset 0x8A8 - some pointer
Signals
A process can have upto 192 signal handlers. For example, signal 9 is SIGKILL. A signal handler for SIGKILL cannot be installed and it cannot be ignored.
A process does not have a signal mask. Every thread of a process has it's own signal mask.
Signal constants
0x9 - SIGKILL
0xE - SIGALRM
0x20 - SIGSPUMB
0x21 - SIGSPUMB_SL
0x22 - SIGSPUSTOP
0x23 - SIGSPUSTOP_SL
0x24 - SIGSPUDMA
0x26 - SIGSPUTIMEOUT
0x27 - SIGSPUERR
0x41 - SIGSHUTDOWN
File table
The file table has 20 entries. So, a process can have at most 20 files opened simultaneously. Each entry is 24 bytes large.
offset 0x0 - entry valid or invalid (1 byte), 0 - invalid, 1 - valid
offset 0x8 - pointer to object with File interface
offset 0x10 - current file position (8 bytes)
Process_EA_to_RA
This function translates an effective process address to real address.
0x00297E08 (3.15)
Objects
Here are the addresses of Process objects i could identify in HV dump 3.15:
- 0x006BB0D0 (PID 0)
- 0x0012C010 (PID 3) - ss_server3.fself
- 0x000915D0 (PID 5) - ss_server2.fself
- 0x000E4D70 (PID 6) - ss_server1.fself
- 0x0012C8D0 (PID 9) - sysmgr_ss.fself
Here are the addresses of Process objects i could identify in HV dump 2.60:
- 0x006B7580 (PID 0)
- 0x00135F90 (PID 3)
- 0x000862D0 (PID 5)
- 0x000A9870 (PID 6)
- 0x00084B80 (PID 9)
In JIG 2.43:
- (PID3) <- ss_server3
- (PID4) <- ss_sc_init_pu
- (PID5) <- ss_server2
- (PID6) <- ss_server1
- (PID7) <- factory_data_mngr_server
- (PID8) <- updater_frontend
(see this)
PThread
All PThread objects of the same Process object are linked together in a list.
vtable
0x003556D8 (3.15)
0x0034ECC0 (2.60)
offset 0x60 - pointer to TOC entry of system call handler
Member variables
offset 0x10 - pointer to next PThread object of Process
offset 0x18 - Thread object
offset 0x2B8 - ? (4 bytes)
offset 0x2C0 - pointer to TOC of some function
offset 0x2C8 - pointer to TOC of some function
offset 0x348 - some conter (4 bytes)
offset 0x3C0 - pointer to Process object that owns PThread object
offset 0x3F8 - signal pending mask (3 * 8 bytes = 192 signals)
offset 0x440 - ConditionVariable object
Signals
A PThread has it's own signal mask, independant of all other PThreads in the same process.
Methods
wait_for_my_turn(Pthread ptr, ?, sleep interruptible flag) = wakeup status - 0x00296FB0 (3.15)
Thread
get_current_thread
This function returns the pointer to current running thread.
0x0028B994 (3.15)
0x0028744C (2.60)
vtable
0x00355750 (3.15)
Member variables
offset 0x288 - some pointer
offset 0x290 - some pointer
AddressSpace
vtable
0x003549A0 (3.15)
0x0034DF88 (2.60)
Member variables
offset 0x8 - Mutex object
offset 0x40 - AddressProtectionDomain object
offset 0x50 - some pointer
offset 0xC0 - some counter (4 bytes)
AddressSpace_EA_to_RA
0x002874D0 (3.15)
AddressProtectionDomain
vtable
0x00354980 (3.15)
Member variables
offset 0x0 - pointer to previous AddressProtectionDomain object
offset 0x8 - pointer to next AddressProtectionDomain object
offset 0x10 - poiinter to pointer to SLB entries
offset 0x18 - pointer to AddressSpace object that owns this object
offset 0x2A - pointer to previous ProtectionPage
offset 0x34 - pointer to next ProtectionPage
offset 0x40 - Mutex object
ProtectionPage
vtable
none
Member variables
offset 0x0 - RA (8 bytes)
offset 0x8 - EA (4 bytes)
offset 0x10 - pointer to previous ProtectionPage (4 bytes)
offset 0x14 - pointer to next ProtectionPage (4 bytes)
Mutex
vtable
0x00354D08 (3.15)
0x0034E2F0 (2.60)
Member variables
offset 0x18 - ? (4 bytes)
offset 0x1C - ? (4 bytes)
ConditionVariable
vtable
0x003549C0 (3.15)
offset 0x20 - wait
Member variables
offset 0x20 - pointer to Mutex object
File interface
vtable
offset 0x8 - ?
offset 0x28 - open
offset 0x30 - close
offset 0x38 - read
offset 0x40 - write
offset 0x50 - mmap
offset 0x58 - ioctl
StorageRegionFile
Flash device file class.
vtable
0x003569F8 (3.15)
VUARTFile
VUART device file class.
vtable
0x00356458 (3.15)
STDLCFile
Console device file class.
vtable
0x003561F8 (3.15)
Member variables
offset 0x20 - reference counter (8 bytes)
offset 0x28 - free buffer space ? (8 bytes)
SocketFile
vtable
0x00355DB0 (3.15)
offset 0xB0 - bind
RegionManager
vtable
0x00355F80 (3.15)
Inode
DirectoryInode
vtable
0x00355788 (3.15)
offset 0x20 - link
offset 0x28 - unlink
get_root_inode
This function returns the pointer to the Inode object of the root directory.
0x0029C124 (3.15)
0x00297BB4 (2.60)
vtable
0x00334E50 (3.15)
offset 0x30 - lookup
File system
Console device file objects
Here is the list of console device file objects i found in HV dump 3.15:
- console
vtable
0x003561F8 (3.15)
Flash device file objects
Here is the list of flash device file objects i found in HV dump 3.15:
- /dev/eflash0
- /dev/eflash1
- /dev/rflash0
- /dev/rflash1
- /dev/rflash_1x
- /dev/rflash_1xp
vtable
0x003569F8 (3.15)
IOIF device file objects
Here is the list of IOIF device file objects i found in HV dump 3.15:
- /dev/ioif0
vtable
0x00356688 (3.15)
Member variables
0x360 = MMIO base address
SD detector device file objects
Here is the list of SD detector device file objects i found in HV dump 3.15:
- /dev/sd_detector
vtable
0x00356B48 (3.15)
NET device file objects
Here is the list of NET device file objects i found in HV dump 3.15:
- /dev/net0
vtable
0x00356DE8 (3.15)
INODES
INODE OBJECT
+0x04: previos inode
+0x08: next inodes
+ 0x38: path
+ 0x358: childer_inode
MFS_ROOT_INODE
(2.60) 0x3580B0
+ 0x60 = ROOT_INODE
SOME ADDRESSES IN 2.60
0x60C010: "/dev" inode
0x6AA580: "/proc" inode
using linked list you can follow all inodes
Repository
- Each LPAR has it's own node repository
- Repository nodes are stored in a hash table which can have several sub-hash tables.
RepositoryNode
vtable
0x00357F58 (3.15)
Member variables
offset 0x30 - pointer to next RepositoryNode obj
offset 0x38 - 2nd hash value of name (4 bytes)
offset 0x40 - 1st field name (8 bytes)
offset 0x48 - 2nd field name (8 bytes)
offset 0x50 - 3rd field name (8 bytes)
offset 0x58 - 4th field name (8 bytes)
offset 0x60 - ? (4 bytes)
offset 0x68 - 1st field value (8 bytes)
offset 0x70 - 2nd field value (8 bytes)
Hash Function
- The name of a repository node is hashed and 2 hash values (2 32bit values) are produced.
- The 1st hash value is used to select a sub-hash table.
- The 2nd hash value is used to find a sub-hash table bucket.
- Repository nodes in a hash bucket are ordered by the 2nd hash value.
void hash(unsigned long long n1, unsigned long long n2, unsigned long long n3, unsigned long long n4, unsigned long *h1, unsigned long *h2) { unsigned long long h; unsigned long hl; h = ((((n1 ^ n4) >> 32) ^ (n2 ^ n3)) ^ (((n2 ^ n3) >> 32) ^ (n1 ^ n4))) & ~0xC0000000ULL; *h1 = h & 0xFFFFFFFFULL; h = ((h & 0x55555555ULL) << 1) | ((h & 0xAAAAAAAAULL) >> 1); h = ((h & 0x33333333ULL) << 2) | ((h & 0xCCCCCCCCULL) >> 2); h = ((h & 0xF0F0F0FULL) << 4) | ((h & 0xF0F0F0F0ULL) >> 4); hl = (h << 8) | ((h & 0xFF000000ULL) >> 24); hl = (hl & ~0xFF000000UL) | ((h & 0xFFULL) << 24); hl = (hl & ~0x0000FF00UL) | (((h << 24) | (h >> 8)) & 0x0000FF00ULL); hl |= 0x1; *h2 = hl; }
Repository nodes from HV 3.15
Dump of all repository nodes from HV 3.15
Repository nodes from HV 3.41 dump made from GameOS
Dump of all repository nodes from HV 3.41 dump made from GameOS
Buses
SB bus
type - 4
index - 1
num_devices - 4 (repository node says this but there are more devices !!!)
Storage bus
type - 5
index - 4
num_devices - 4
SB bus subsystem
vtable
0x00352600 (3.15)
Member variables
offset 0x10 - MMIO memory base address
offset 0x20 - array of 16 pointers to SB devices (0 - Gelic device, 1 - USB device)
Objects
0x00349528 - pointer to pointer to SB bus subsystem object
Memory base address
0x24000000000
All SB bus device MMIO addresses are relative to this memory address.
SB device MMIO/DMA memory region
vtable
0x352308 (3.15)
Member variables
offset 0x18 - pointer to previous bus memory region object
offset 0x20 - pointer to next bus memory region object
offset 0x30 - relative bus memory start address
offset 0x38 - size of bus memory region
SB bus device
vtable
0x00352620 (3.15)
Member variables
offset 0x18 - array of pointers to MMIO memory region objects owned by device (8 * 8 bytes)
offset 0x60 - pointer to first DMA region object
offset 0x6C - device opened flag (1 byte, 0 - not opened, 1 - already opened)
offset 0x70 - id of LPAR that opened this device
offset 0x90 - pointer to an object that contains the address of interrupt handler for this device and SB bus interrupt index
Gelic device (Network Interface)
device id = 0
interrupt index = 8
The Gelic device is similar to the spider_net device from Toshiba. There are some differences with mmio initialization values within LV1 in comparison to the spider_net.c linux driver.
Gelic defines:
DEFINE | Value |
---|---|
GELIC_CKRCTRL_REGISTER | 0xFF0 |
GELIC_CKRCTRL_STOP_VALUE | 0x00000105 |
GELIC_CKRCTRL_RUN_VALUE | 0x1D7F0105 |
GELIC_MACADDR_HIGH_REG | 0x500 |
GELIC_MACADDR_LOW_REG | 0x504 |
MMIO regions
Index | Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|---|
0 | 0x2800 | 0x24000002800 | 0x200 |
1 | 0x3004000 | 0x24003004000 | 0x1000 |
2 | - | - | - |
3 | - | - | - |
4 | - | - | - |
5 | - | - | - |
6 | - | - | - |
7 | - | - | - |
DMA regions
Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|
0xA0000000 | - | 0x8000 |
0xC0000000 | - | 0x10000000 |
SATA Controller 1 device
device id = 1
interrupt index = 49
MMIO regions
Index | Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|---|
0 | 0x2000 | 0x24000002000 | 0x200 |
1 | 0x3000000 | 0x24003000000 | 0x1000 |
2 | 0x3800000 | 0x24003800000 | 0x1000 |
3 | 0x3802000 | 0x24003802000 | 0x1000 |
4 | - | - | - |
5 | - | - | - |
6 | - | - | - |
7 | - | - | - |
DMA regions
Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|
0xA0000000 | - | 0x1000 |
0xA0001000 | - | 0x1000 |
0xA0002000 | - | 0x1000 |
SATA Controller 2 device
device id = 2
interrupt index = 13
MMIO regions
Index | Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|---|
0 | 0x2200 | 0x24000002200 | 0x200 |
1 | 0x3001000 | 0x24003001000 | 0x1000 |
2 | 0x3801000 | 0x24003801000 | 0x1000 |
3 | 0x3803000 | 0x24003803000 | 0x1000 |
4 | - | - | - |
5 | - | - | - |
6 | - | - | - |
7 | - | - | - |
DMA regions
Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|
0xA0000000 | - | 0x1000 |
0xA0001000 | - | 0x1000 |
0xA0002000 | - | 0x1000 |
USB Controller 1 device
device id = 3
MMIO regions
Index | Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|---|
0 | 0x2400 | 0x24000002400 | 0x200 |
1 | 0x3010000 | 0x24003010000 | 0x10000 |
2 | 0x3810000 | 0x24003810000 | 0x10000 |
3 | - | - | - |
4 | - | - | - |
5 | - | - | - |
6 | - | - | - |
7 | - | - | - |
DMA regions
Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|
0xC0000000 | - | 0x10000000 |
0xD0000000 | - | 0x10000000 |
USB Controller 2 device
device id = 4
MMIO regions
Index | Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|---|
0 | 0x2600 | 0x24000002600 | 0x200 |
1 | 0x3020000 | 0x24003020000 | 0x10000 |
2 | 0x3820000 | 0x24003820000 | 0x10000 |
3 | - | - | - |
4 | - | - | - |
5 | - | - | - |
6 | - | - | - |
7 | - | - | - |
DMA regions
Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|
0xC0000000 | - | 0x10000000 |
0xD0000000 | - | 0x10000000 |
ENCDEC device
device id = 7
interrupt index = 5
MMIO regions
Index | Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|---|
0 | 0x2C00 | 0x24000002C00 | 0x200 |
1 | 0x3005000 | 0x24003005000 | 0x1000 |
2 | 0x3006000 | 0x24003006000 | 0x1000 |
3 | - | - | - |
4 | - | - | - |
5 | - | - | - |
6 | - | - | - |
7 | - | - | - |
DMA regions
Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|
0x80010000 | - | 0x10000 |
0x80004000 | - | 0x4000 |
0x80001000 | - | 0x1000 |
0x80003000 | - | 0x1000 |
0x80008000 | - | 0x1000 |
0x80009000 | - | 0x1000 |
0x80040000 | - | 0x10000 |
0x8000A000 | - | 0x1000 |
0x90020000 | - | 0x20000 |
0xC0000000 | - | 0x10000 |
0xC0040000 | - | 0x40000 |
FLASH Controller device (StarShip - SS)
device id = 9
interrupt index = 41
MMIO regions
FLASH controller doesn't have MMIO regions.
DMA regions
Relative Bus Start Address | Absolute Bus Start Address | Size |
---|---|---|
0x80000000 | - | 0x1000 |
0x80020000 | - | 0x20000 |
0x80002000 | - | 0x1000 |
0x90000000 | - | 0x20000 |
SB Bus Interrupt Handling
- There is a table of interrupt handlers for SB devices
- The size of table is 64
- The main SB bus interrupt handler is at 0x002B9CC4 (3.15)
- The main interrupt handler reads interrupt index and dispatches interrupts
Interrupt Index
- The main SB bus interrupt handler reads 2 32-bit values from addresses 0x24000008100 and 0x0x24000008104
- The interrupt index is calculated from these values
Interrupt Handler Table
Interrupt | Description | Address in HV |
---|---|---|
5 | ENCDEC device | 0x00275C60 (3.15) |
6 | EH EPCIC internal | 0x0023B6B0 (3.15) |
8 | Gelic device | 0x00245330 (3.15) |
12 | ATA interrupt handler | 0x0026B984 (3.15) |
13 | ATA interrupt handler | 0x0026B984 (3.15) |
14 | Spider SC | 0x0020A68C (3.15) |
29 | SBERR | 0x0023AA50 (3.15) |
30 | SBERR | 0x0023AA50 (3.15) |
41 | EBUS (Flash StarShip) | 0x002814EC (3.15) |
49 | ATA media interrupt handler | 0x00268A8C (3.15) |
50 | Flash ? | 0x00280B24 (3.15) |
55 | EH EPCIC SERR | 0x0023B67C (3.15) |
Storage bus subsystem
vtable
0x00353AC8 (3.15)
Member variables
offset 0xEE8 - table of pointers to storage device objects (7 * 8 bytes, max 7 devices)
Storage device class
Member variables
offset 0x8 - device id (8 bytes)
offset 0xD50 - device id (8 bytes)
offset 0xD60 - pointer to ENCDEC SB bus device object
Region Manager
- Each storage device has a Region Manager (i call it like that)
- Region Manager stores information about each Region of the storage device
- All Regions of a Region Manager are linked together
- Free Regions of a Region Manager are linked together also
- A Region Manager can have at most 8 Regions
Region
- Each storage device can have at most 8 regions (0-7)
- Each region has ACL table
- HV checks region ACLs before allowing access to the region
- Each region has a start sector that is an offset from the physical first sector of the storage device and a number of sectors
- The start sector passed to lv1 storage hvcalls is relative to the start sector of the region passed to the lv1 storage hvcall
Region ACL
offset 0x0 - LPAR AUTH ID (8 bytes)
offset 0x8 - access rigths (8 bytes)
offset 0x10 - entry valid flag: 0 - invalid, 1 - valid (1 byte)
Region Access Protection
- Before a storage region is accessed, HV checks access rights of the caller.
- Repository node ss.laid (LPAR Authority ID) is evaluated for this purpose.
- If LPAR has a repository node ios.ata.region0.access (value doesn't matter) then the access rights check never fails. After System Manager sets ATA keys it removes this repository node from LPAR 1. If we add this repository node again or patch System Manager so it's not removed then we will be able to access all storage regions of all storage devices.
- ALL storage accesses from LPAR 1 are allowed
- If (flags & 0x100000002) != 0 then access rights check is skipped !!!.
I tested on HV 3.41 with flags 0x2 and got access to regions which were denied by policy (LV1_DENIED_BY_POLICY result).
Storage Device Partition Table
- Each storage device has a Partition Table
- Partition Table contains information about each region on the storage device
Methods
lv1_storage_create_region (lv1_undocumented_function_250) - 0x00301328 (3.15)
lv1_storage_delete_region (lv1_undocumented_function_251) - 0x003011E8 (3.15)
lv1_storage_set_region_acl (lv1_undocumented_function_252) - 0x00300F3C (3.15)
lv1_storage_get_region_acl (lv1_undocumented_function_253) - 0x00301090 (3.15)
storage_device_create_region - 0x00253988 (3.15)
storage_device_delete_region - 0x00253BE8 (3.15)
storage_device_region_set_acl - 0x00252C80 (3.15)
storage_device_region_get_acl - 0x00252710 (3.15)
storage_region_mgr_create_region - 0x0025A530 (3.15)
storage_region_mgr_delete_region - 0x0025BA64 (3.15)
storage_region_mgr_set_acl - 0x0025A140 (3.15)
storage_region_mgr_get_acl - 0x0025A298 (3.15)
storage_region_mgr_update_partition_table - 0x00259924 (3.15)
storage_region_acl_entry_reset - 0x0025C1A8 (3.15)
storage_region_acl_entry_check_laid - 0x0025C1FC (3.15)
storage_region_overlap - 0x0025C094 (3.15)
storage_region_check_access - 0x00259EC8 (3.15)
Storage subsystem device
device id = -1
- The storage subsystem is a storage device itself.
- It's a pseudo device used to notify a LPAR when storage devices become e.g. ready.
- Linux implements a loop and reads from this device and process notifications (adds new devices dynamically).
Notification Events
List of supported notification events:
- Notify Device Ready (0x1)
- Notify Region Probe (0x2)
- Notify Region Update (0x4)
RBD device
- On Linux, ENCDEC and RBD devices are mapped to the storage device with device id 0.
- On GameOS, ENCDEC device has device id 0 and RBD device has device id 2.
device id = 0
block size = 2048
/dev/rbd0
- The RBD storage device uses ENCDEC device.
vtable
0x00354288 (3.15)
Member variables
offset 0x1808 - request table (0x58 * 32 bytes)
Regions
Index | Start sector | Number of sectors |
---|---|---|
0 | 0x0 | 0x7FFFFFFF |
1 | - | - |
2 | - | - |
3 | - | - |
4 | - | - |
5 | - | - |
6 | - | - |
7 | - | - |
Supported Device Commands
Here is the list of commands supported by RBD storage device.
- The commands can be used with HV call lv1_storage_send_device_command.
- However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node ss.laid or also called LPAR Authority ID. If this test fails then the command is NOT executed.
Command | Description |
---|---|
0x1 | LV1_STORAGE_SEND_ATAPI_COMMAND |
0x10 | ATAPI Read Capacity |
0x11 | ATAPI Get Configuration |
0x13 | ATAPI Read TOC |
0x1A | ATAPI Get Event |
/dev/rbd0
- This LPAR 1 device accesses RBD storage device.
- A write to this device sends a device command to RBD storage device.
ENCDEC Device
bus id = 4
device id = 0
- ENCDEC device has a request table of size 32.
- ENCDEC device supports upto 16 keys simultaneously.
Member variables
offset 0xDC0 - request table (0x58 * 32 bytes)
Methods
encdec_device_initialize - 0x00273524 (3.15)
InitializeENCDEC - 0x00277310 (3.15)
ENCDEC_ConnectBusDriver - 0x00275A98 (3.15)
encdec_interrupt_handler - 0x00275C60 (3.15)
encdec_process_interrupt - 0x0027526C (3.15)
encdec_device_enqueue_decsec_request - 0x00273738 (3.15)
encdec_device_do_request - 0x00273EA8 (3.15)
encdec_device_do_SS_request - 0x00274940 (3.15)
Encdec_KickDMA - 0x00277118 (3.15)
encdec_device_is_in_testmode - 0x002756E0 (3.15)
is_encdec_in_testmode - 0x002732D0 (3.15)
ENCDEC Device Commands
- EdecKgen1 command is used e.g. by Storage Manager Service 0x5003 to generate random numbers. Storage Manager performs this command through LPAR 1 device /dev/encdec0.
Command | Description |
---|---|
0x81 | EdecKgen1 |
0x82 | EdecKgen2 |
0x83 | EdecKset/EdecKset NG |
0x84 | EdecKgenFlash |
0x85 | Encrypts/decrypts sectors (This command cannot be executed through ioctl interface !!!) |
0x86 | Encdec decsec (This command cannot be executed through ioctl interface !!!) |
0x87 | EdecSBClear |
EdecKgen1 Command (0x81)
- First, ENCDEC device key generator is flashed by executing the operation which is also performed during EdecKgenFlash command.
- 0x30 bytes of data are written to MMIO registers of ENCDEC device.
- 0x40 bytes of data are read from MMIO registers of ENCDEC device.
- The base address of MMIO registers used in this command is 0x24003006000.
- I tested this command by directly communicating with ENCDEC device from GameOS by using HV call lv1_storage_send_device_command and it returns random data.
Here is the data i sent to ENCDEC device:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F 00000000 00 01 00 30 72 A7 88 EC FC A4 06 71 4C B1 50 C9 ...0r§ˆìü¤.qL±PÉ 00000010 FB E0 06 C2 74 B5 84 C4 E6 BD 1E 55 4E 36 E9 C9 ûà.Âtµ„Äæ½.UN6éÉ 00000020 D6 09 BC B4 79 A6 BC DE 60 A5 B2 41 C7 15 68 68 Ö.¼´y¦¼Þ`¥²AÇ.hh 00000030 82 1D 8F D6 00 00 00 00 00 00 00 00 00 00 00 00 ‚.Ö............
Here is the data i received back from ENCDEC device:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F 00000000 00 02 00 00 57 CF 06 AF 53 85 1B B8 49 37 06 28 ....WÏ.¯S….¸I7.( 00000010 51 8D 4E F9 EF 76 E2 C7 17 EF 41 14 FA 6C 96 A8 QNùïvâÇ.ïA.úl–¨ 00000020 7E 41 43 96 15 9A 0D 71 A9 B6 A6 B0 F1 96 15 C5 ~AC–.š.q©¶¦°ñ–.Å 00000030 30 25 C3 8E 6F AC FB 7F E7 2A FB E2 36 E1 85 92 0%ÃŽo¬û?ç*ûâ6á…’ 00000040 99 66 DB EC 00 00 00 00 00 00 00 00 00 00 00 00 ™fÛì............
Here is another data i received back from ENCDEC device by using the same command and data:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F 00000000 00 02 00 00 57 CF 06 AF 53 85 1B B8 49 37 06 28 ....WÏ.¯S….¸I7.( 00000010 51 8D 4E F9 EF 76 E2 C7 17 EF 41 14 FA 6C 96 A8 QNùïvâÇ.ïA.úl–¨ 00000020 7E 41 43 96 17 08 75 F6 66 2F 32 5A 9E 3E E7 FD ~AC–..uöf/2Zž>çý 00000030 16 3E 18 CA B2 5E 90 84 29 7F 98 BC 73 36 0E 7B .>.ʲ^„)?˜¼s6.{ 00000040 7D EC B6 37 00 00 00 00 00 00 00 00 00 00 00 00 }ì¶7............
EdecKgen2 Command (0x82)
- The base address of MMIO registers used in this command is 0x24003006000.
EdecKset Command (0x83)
EdecKgenFlash Command (0x84)
- The base address of MMIO registers used in this command is 0x24003006000.
- The command reads 4 bytes from address 0x240030060A0, sets bit 1 to 1 (old value | 0x2) and writes the new value to the same address.
Encdec decsec Command (0x86)
- This command is used to decrypt/encrypt sectors.
- FLASH, HDD and RBD storage devices use this command to decrypt/encrypt sectors.
- This command cannot be executed through lv1_storage_send_device_command HV call, it's used by HV only internally.
EdecSBClear Command (0x87)
- The command expects arg2 to be 4 or else it returns with an error.
- This command is used e.g. by Storage Manager service 0x5002 when ATA keys are deleted.
Test Mode
- ENCDEC device has Test Mode
- Some HV functions test it by reading a 4 byte value from address 0x24003005200. If this value is 0 then ENCDEC device is NOT in Test Mode.
ENCDEC Request
offset 0x34 - start sector (4 bytes)
offset 0x38 - sector count (4 bytes)
offset 0x3C - sector size (4 bytes)
offset 0x40 - key (4 bytes)
offset 0x44 - 0 = decrypt, 1 = encrypt (4 bytes)
Encrypting and Decrypting Sectors
- HV passes to ENCDEC device addresses of 2 buffers: ENCDEC User Buffer and ENCDEC Descriptor Buffer.
- ENCDEC User Buffer contains the following information: Start Sector, Sector Count, Sector Size and Key
ENCDEC User Buffer
offset 0x0 - start sector (4 bytes)
offset 0x4 - sector count (4 bytes)
offset 0x8 - sector size (4 bytes)
offset 0xC - key (4 bytes)
FLASH device
device id = 1
- The FLASH device uses ENCDEC device.
vtable
0x00354450 (3.15)
Member variables
offset 0x18F0 - request table (0x58 * 16 bytes)
Regions
Index | Start sector | Number of sectors |
---|---|---|
0 | 0x0 | 0x8000 |
1 | 0x8 | 0x77F8 |
2 | 0x7900 | 0x100 |
3 | 0x7A00 | 0x400 |
4 | - | - |
5 | - | - |
6 | - | - |
7 | - | - |
Supported Device Commands
Here is the list of commands supported by FLASH StarShip 2 storage device.
- The commands can be used with HV call lv1_storage_send_device_command.
- However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node ss.laid or also called LPAR Authority ID. If this test fails then the command is NOT executed.
Command | Description |
---|---|
0x31 | Dummy (This command does nothing, returns success immediately) |
0xA2 | - |
0xA3 | - |
0xA4 | - |
0xA6 | SS2 HW Reset |
0xAC | - |
0xAD | TEST |
/dev/eflash1 and /dev/rflash1
- These LPAR 1 devices access region 0 of FLASH storage device.
- /dev/rflash1 is 16MB large
- There is no file system on /dev/rflash1
- There is some sort of TOC (Table Of Contents) stored in it. It contains file names, offsets and sizes.
- On /dev/rflash1 you will find lv0, lv1ldr, lv2_lernel.self and all the other important SELFs.
- The files are encryted of course.
Content of /dev/rflash1 (FLASH storage device region 0, size 16 MB)
- There is a main TOC which describes different regions on /dev/rflash1
- It seems that TOC 0xC0000 and TOC 0x7C0000 contain the same files but from different SDK versions.
- TOC 0xC0000 is SDK version 3.41 and TOC 0x7C0000 is SDK version 3.30 (look at the content of files sdk_version).
- I guess it's because when i bought my PS 3 Slim it had Firmware 3.30 and i updated it to 3.41 for PSGroove.
- TOC on /dev/rflash1 is used by HV Processes to locate files and load them into memory, e.g. SPU modules. E.g. Process 6 loads spu_utoken_processor.self to decrypt and verify user tokens or SPL which runs in Process 5 loads spp_verifier.self from there in order to decrypt and verify profile files. And Update Manager stores e.g. there files.
TOC Entry
A TOC entry is 0x30 bytes large.
offset 0x0 - relative offset from this TOC to entry data
offset 0x8 - entry data size
offset 0x10 - entry name (max 32 characters)
Main TOC
Here is a list of regions/files stored on /dev/rflash1 i found in HV 3.41 and dumped with PSGroove:
Entry Name | TOC Offset | Entry TOC Index | Entry Relative Offset | Entry Absolute Offset | Entry Size |
---|---|---|---|---|---|
asecure_loader | 0x400 | 0 | 0x400 | 0x810 | 0x2E800 |
eEID | 0x400 | 1 | 0x2EC00 | 0x2F010 | 0x10000 |
cISD | 0x400 | 2 | 0x3EC00 | 0x3F010 | 0x800 |
cCSD | 0x400 | 3 | 0x3F400 | 0x3F810 | 0x800 |
trvk_prg0 | 0x400 | 4 | 0x3FC00 | 0x40010 | 0x20000 |
trvk_prg1 | 0x400 | 5 | 0x5FC00 | 0x60010 | 0x20000 |
trvk_pkg0 | 0x400 | 6 | 0x7FC00 | 0x80010 | 0x20000 |
trvk_pkg1 | 0x400 | 7 | 0x9FC00 | 0xA0010 | 0x20000 |
ros0 | 0x400 | 8 | 0xBFC00 | 0xC0010 | 0x700000 |
ros1 | 0x400 | 9 | 0x7BFC00 | 0x7C0010 | 0x700000 |
cvtrm | 0x400 | 10 | 0xEBFC00 | 0xEC0010 | 0x40000 |
asecure_loader Region TOC
Here is a list of files stored on /dev/rflash1 i found in HV 3.41 and dumped with PSGroove:
Entry Name | TOC Offset | Entry TOC Index | Entry Relative Offset | Entry Absolute Offset | Entry Size |
---|---|---|---|---|---|
metldr | 0x800 | 0 | 0x40 | 0x840 | 0xE920 |
ros1 Region TOC
Here is a list of files stored on /dev/rflash1 i found in HV 3.41 and dumped with PSGroove:
Entry Name | TOC Offset | Entry TOC Index | Entry Relative Offset | Entry Absolute Offset | Entry Size |
---|---|---|---|---|---|
creserved_0 | 0xC0000 | 0 | 0x460 | 0xC0470 | 0x40000 |
sdk_version | 0xC0000 | 1 | 0x40460 | 0x100470 | 0x8 |
lv1ldr | 0xC0000 | 2 | 0x40480 | 0x100490 | 0x1E948 |
lv2ldr | 0xC0000 | 3 | 0x5EE00 | 0x11EE10 | 0x16FF0 |
isoldr | 0xC0000 | 4 | 0x75E00 | 0x135E10 | 0x13074 |
appldr | 0xC0000 | 5 | 0x88E80 | 0x148E90 | 0x1E254 |
spu_pkg_rvk_verifier.self | 0xC0000 | 6 | 0xA70D4 | 0x1670E4 | 0xFACC |
spu_token_processor.self | 0xC0000 | 7 | 0xB6BA0 | 0x176BB0 | 0x5C94 |
spu_utoken_processor.self | 0xC0000 | 8 | 0xBC834 | 0x17C844 | 0x65D0 |
sc_iso.self | 0xC0000 | 9 | 0xC2E04 | 0x182E14 | 0x1532C |
aim_spu_module.self | 0xC0000 | 10 | 0xD8130 | 0x198140 | 0x4498 |
spp_verifier.self | 0xC0000 | 11 | 0xDC5C8 | 0x19C5D8 | 0xD7F0 |
mc_iso_spu_module.self | 0xC0000 | 12 | 0xE9DB8 | 0x1A9DC8 | 0x808C |
me_iso_spu_module.self | 0xC0000 | 13 | 0xF1E44 | 0x1B1E54 | 0x88B8 |
sv_iso_spu_module.self | 0xC0000 | 14 | 0xFA6FC | 0x1BA70C | 0xC078 |
sb_iso_spu_module.self | 0xC0000 | 15 | 0x106774 | 0x1C6784 | 0x5DB0 |
default.spp | 0xC0000 | 16 | 0x10C524 | 0x1CC534 | 0x22A0 |
lv1.self | 0xC0000 | 17 | 0x10E800 | 0x1CE810 | 0x127DF0 |
lv0 | 0xC0000 | 18 | 0x236600 | 0x2F6610 | 0x3E678 |
lv2_kernel.self | 0xC0000 | 19 | 0x274C78 | 0x334C88 | 0x171B88 |
eurus_fw.bin | 0xC0000 | 20 | 0x3E6800 | 0x4A6810 | 0x70F94 |
emer_init.self | 0xC0000 | 21 | 0x457794 | 0x5177A4 | 0x7CDB8 |
hdd_copy.self | 0xC0000 | 22 | 0x4D454C | 0x59455C | 0x60D68 |
ros2 Region TOC
Here is a list of files stored on /dev/rflash1 i found in HV 3.41 and dumped with PSGroove:
Entry Name | TOC Offset | Entry TOC Index | Entry Relative Offset | Entry Absolute Offset | Entry Size |
---|---|---|---|---|---|
creserved_0 | 0x7C0000 | 0 | 0x460 | 0x7C0470 | 0x40000 |
sdk_version | 0x7C0000 | 1 | 0x40460 | 0x800470 | 0x8 |
lv1ldr | 0x7C0000 | 2 | 0x40480 | 0x800490 | 0x1E64C |
lv2ldr | 0x7C0000 | 3 | 0x5EB00 | 0x81EB10 | 0x16E30 |
isoldr | 0x7C0000 | 4 | 0x75980 | 0x835990 | 0x12EC4 |
appldr | 0x7C0000 | 5 | 0x88880 | 0x848890 | 0x1DB64 |
spu_pkg_rvk_verifier.self | 0x7C0000 | 6 | 0xA63E4 | 0x8663F4 | 0xFACC |
spu_token_processor.self | 0x7C0000 | 7 | 0xB5EB0 | 0x875EC0 | 0x5C94 |
spu_utoken_processor.self | 0x7C0000 | 8 | 0xBBB44 | 0x87BB54 | 0x65D0 |
sc_iso.self | 0x7C0000 | 9 | 0xC2114 | 0x882124 | 0x1532C |
aim_spu_module.self | 0x7C0000 | 10 | 0xD7440 | 0x897450 | 0x4498 |
spp_verifier.self | 0x7C0000 | 11 | 0xDB8D8 | 0x89B8E8 | 0xD7F0 |
mc_iso_spu_module.self | 0x7C0000 | 12 | 0xE90C8 | 0x8A90D8 | 0x808C |
me_iso_spu_module.self | 0x7C0000 | 13 | 0xF1154 | 0x8B1164 | 0x88B8 |
sv_iso_spu_module.self | 0x7C0000 | 14 | 0xF9A0C | 0x8B9A1C | 0xC078 |
sb_iso_spu_module.self | 0x7C0000 | 15 | 0x105A84 | 0x8C5A94 | 0x5DB0 |
default.spp | 0x7C0000 | 16 | 0x10B834 | 0x8CB844 | 0x22A0 |
lv1.self | 0x7C0000 | 17 | 0x10DB00 | 0x8CDB10 | 0x129040 |
lv0 | 0x7C0000 | 18 | 0x236B80 | 0x9F6B90 | 0x3E570 |
lv2_kernel.self | 0x7C0000 | 19 | 0x2750F0 | 0xA35100 | 0x1712D0 |
eurus_fw.bin | 0x7C0000 | 20 | 0x3E63C0 | 0xBA63D0 | 0x70F94 |
emer_init.self | 0x7C0000 | 21 | 0x457354 | 0xC17364 | 0x7FBB8 |
hdd_copy.self | 0x7C0000 | 22 | 0x4D6F0C | 0xC96F1C | 0x61518 |
Methods
initialize_starship - 0x0028298C (3.15)
SSOperation - 0x0027BFB0 (3.15)
SSTransfer - 0x0027BE68 (3.15)
FLASH_Memory_SS2_on_complete - 0x00278E48 (3.15)
_FLASH_read_data - 0x0022D89C (3.15)
_FLASH_write_data - 0x0022D8C8 (3.15)
FLASH_SS2_HW_Reset - 0x0027BD1C (3.15)
HDD device
device id = 2
block size = 512
- The HDD device uses ENCDEC device.
vtable
0x00353F48 (3.15)
Member variables
offset 0x1590 - LBA48 capability flag (4 bytes)
offset 0x17E8 - request table (0x58 * 16 bytes)
offset 0x1DB8 - request timer active flag (1 byte)
Regions
Index | Start sector | Number of sectors |
---|---|---|
0 | 0x0 | 0x950F8B0 |
1 | 0x8 | 0x80000 |
2 | 0x80018 | 0x7C8F898 |
3 | 0x7D0F8B8 | 0x3FFFF8 |
4 | 0x810F8B8 | 0x13FFFF8 |
5 | - | - |
6 | - | - |
7 | - | - |
Supported Device Commands
Here is the list of commands supported by HDD storage device.
- The commands can be used with HV call lv1_storage_send_device_command.
- However, before a command is executed HV does bit manipulation with it and checks it against the value of repository node ss.laid or also called LPAR Authority ID. If this test fails then the command is NOT executed.
Command | Description |
---|---|
0x2 | LV1_STORAGE_SEND_ATA_COMMAND |
0x10 | - |
0x1B | ATA Set UltraDMA Mode |
0x1C | ATA Set Features PIO Flow Control Transfer Mode |
0x21 | - |
0x22 | ATA Identify Device |
0x23 | LV1_STORAGE_ATA_HDDOUT (ATA Flush Cache Ext) |
0x26 | ATA Read Alternative Status |
0x27 | ATA Read Error |
0x28 | - |
0x31 | ATA Flush Cache/ATA Flush Cache Ext |
0x32 | ATA Standby Immediate |
0x33 | - |
Virtual FLASH device (VFLASH)
device id = 3 (on Linux)/ 4 (on GameOS)
block size = 512
- It's a pseudo device.
- This storage device redirects all requests to the region 1 of HDD storage device !!!
vtable
0x00353D88 (3.15)
Member variables
offset 0xD60 - pointer to a storage device that all requests are redirected to
offset 0xD68 - region ID of the storage device that all requests are redirected to
Regions
Index | Start sector | Number of sectors |
---|---|---|
0 | 0x0 | 0x80000 |
1 | 0x8 | 0x75F8 |
2 | 0x7800 | 0x63E00 |
3 | 0x6B600 | 0x8000 |
4 | 0x73600 | 0x400 |
5 | 0x73A00 | 0x2000 |
6 | 0x77C00 | 0x200 |
7 | - | - |
Supported Device Commands
Here is the list of commands supported by VFLASH storage device.
Index | Start sector | Number of sectors |
---|---|---|
0x31 | ATA Flush Cache/ATA Flush Cache Ext | - |
/dev/rflash1_1x and /dev/rflash_1xp
- These LPAR 1 devices access region 5 of UNKNOWN storage device.
- In region 5 of UNKNOWN storage device is e.g. LINUX image stored.
GameOS's dev_flash
- dev_flash has FAT16 file system.
- Accesses to GameOS's dev_flash are routed to UNKNOWN storage device region 2
- To decrypt sectors read from this region use as flags 0x4 !!! Without using flags 0x4 the sectors will be encrypted.
- The sectors are decrypted not by GameOS but by ENCDEC device.
Here is a snippet from raw dev_flash dump made with HV call lv1_storage_read (flags 0x4) from GameOS:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F 00000000 E9 00 00 20 20 20 20 20 20 20 20 00 02 10 10 00 é.. ..... 00000010 02 00 02 00 00 F8 70 00 00 00 00 00 00 00 00 00 .....øp......... 00000020 00 3E 06 00 00 00 29 00 00 00 00 4E 4F 20 4E 41 .>....)....NO NA 00000030 4D 45 20 20 20 20 46 41 54 31 36 20 20 20 00 00 ME FAT16 .. 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA ..............Uª
Methods
initialize_virtual_flash - 0x00282954 (3.15)
Enqueueing and Scheduling of Storage Requests
- HV uses a simple FIFO scheduling algorithm for Storage Requests and a request timeout.
- Each storage device has a table of size 16 to store incomming and pending Storage Requests
- ENCDEC storage device has a table of size 32 to store incomming and pending Storage Requests
- When a new Storage Request is submitted e.g. by HV call lv1_storage_read or lv1_storage_write, the table is scanned for a free slot, if there are no pending Storage Requests then the Storage Request is executed immediately
- When a Storage Request is completed, the finished Storage Reuqest is passed to function storage_device_async_request_complete and the table of Storage Requests is scanned again for the next pending Storage Request which will be executed
- There are 2 types of Storage Requests: Read/Write (1) and Device Command (2).
- Read and Write Storage Requests use the same HV function of a Storage Device to enqueue the request. Before Write Storage Request is inserted into the Request Table of a Storage Device, the flags parameter passed e.g. in lv1_storage_read or lv1_storage_write is ored with 0x8. That is how HV differentiates between Read and Write Storage Requests.
Storage Device Request Table
- Each request slot is of size 0x58
Request Slot
offset 0x0 - state: 1 - free, 2 - ? (4 bytes)
offset 0x4 - type: 1 - Read/Write, 2 - Command, 0x86 - ENCDEC command (4 bytes)
offset 0x10 - request tag (8 bytes)
offset 0x20 - start sector (8 bytes)
offset 0x28 - sector count (4 bytes)
ENCDEC Storage Device
- Request Table begins at offset 0xDC0 of ENCDEC storage device.
RBD Storage Device
- Request Table begins at offset 0x1808 of RBD storage device.
FLASH Storage Device
- Request Table begins at offset 0x18F0 of FLASH storage device.
HDD Storage Device
- Request Table begins at offset 0x17E8 of HDD storage device.
Methods
storage_device_HDD_enqueue_request - 0x0026E21C (3.15)
storage_device_HDD_do_device_command - 0x0026CED0 (3.15)
storage_device_HDD_do_request - 0x0026DED8 (3.15)
storage_device_HDD_request_complete - 0x0026E57C (3.15)
storage_device_FLASH_enqueue_request - 0x0027A518 (3.15)
storage_device_FLASH_do_request - 0x00278D1C (3.15)
storage_device_FLASH_do_device_command - 0x00279250 (3.15)
FLASH_Memory_SS2_on_complete - 0x00278E48 (3.15)
storage_device_async_request_complete - 0x00255184 (3.15)
storage_device_TransLparAddrToPhysAddr - 0x002533B4 (3.15)
storage_device_add_async_request_locked - 0x002527B8 (3.15)
storage_device_RBD_enqueue_request - 0x002723F0 (3.15)
storage_device_RBD_do_request - 0x0025EF70 (3.15)
storage_device_RBD_do_next_request - 0x00270994 (3.15)
storage_device_RBD_request_complete - 0x00271FD4 (3.15)
storage_device_rbd_do_request - 0x0025EE94 (3.41)
storage_device_rbd_do_device_command - 0x0027061C (3.41)
Encryption and Decryption of Storage Devices
HDD
- ENCDEC peripheral device is used for HDD encryption/decryption
- Write request is first passed to ENCDEC device for encryption. When ENCDEC device is done, it calls a callback and passes the encrypted data to the callback. The callback writes the encrypted data with ATA WriteDMAExt command to HDD.
- When a storage device request is processed by HV, Storage Subsystem checks if cryptography is enabled for the storage device.
- HV checks 1 byte of data owned by the storage device and when the value of this flag is not 0 then it uses encryption/decryption.
- By setting this flag to 0 at runtime, encryption/decryption of storage devices can be disabled at runtime.
- We could patch lv1.self so that encryption/decryption of storage devices is disabled permanently.
- HDD sectors can be both decrypted and encrypted with HV calls
UFS2
- Superblock starts at sector 0x80.
- At the end of the superblock structure you will find UFS2 signature 0x19540119.
Here is the decrypted superblock of UFS2 filesystem:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F 00010000 00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 30 ...........(...0 00010010 00 00 00 38 00 00 0B B8 00 00 00 00 00 00 00 00 ...8...¸........ 00010020 00 00 00 00 00 00 00 00 00 00 78 10 00 00 01 5C ..........x....\ 00010030 00 00 40 00 00 00 08 00 00 00 00 08 00 00 00 08 ..@............. 00010040 00 00 00 00 00 00 00 00 FF FF C0 00 FF FF F8 00 ........ÿÿÀ.ÿÿø. 00010050 00 00 00 0E 00 00 00 0B 00 00 00 08 00 00 08 00 ................ 00010060 00 00 00 03 00 00 00 02 00 00 08 00 00 00 00 00 ................ 00010070 00 00 00 00 00 00 08 00 00 00 00 40 00 00 00 00 ...........@.... 00010080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010090 00 00 00 00 F5 35 BD 07 00 00 00 00 00 00 18 00 ....õ5½......... 000100A0 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 ..@............. 000100B0 00 00 00 00 00 00 00 00 00 00 5C 00 00 01 6F 70 ..........\...op 000100C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000100D0 00 00 00 80 2F 63 65 6C 6C 5F 6D 77 5F 63 66 73 ...€/cell_mw_cfs 000100E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000100F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000101A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000101B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000101C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000101D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000101E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000101F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010200 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010250 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010290 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000102A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000102B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000102C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000102D0 00 00 00 00 00 00 00 7C 00 00 00 00 00 00 00 00 .......|........ 000102E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000102F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010300 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010310 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010320 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010330 00 00 00 00 00 00 00 00 80 00 00 00 00 55 FD 70 ........€....Uýp 00010340 80 00 00 00 00 55 E0 00 80 00 00 00 00 55 F8 00 €....Uà.€....Uø. 00010350 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 ..............@. 00010360 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010370 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010380 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010390 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000103A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000103B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000103C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000103D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000103E0 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 ................ 000103F0 00 00 00 00 00 00 00 3C 00 00 00 00 00 3B D3 23 .......<.....;Ó# 00010400 00 00 00 00 00 7D 0F 82 00 00 00 00 00 00 00 9F .....}.‚.......Ÿ 00010410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010430 00 00 00 00 49 B0 5E 3B 00 00 00 00 01 F2 3E 26 ....I°^;.....ò>& 00010440 00 00 00 00 01 E2 86 3B 00 00 00 00 00 00 0B B8 .....â†;.......¸ 00010450 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010460 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010470 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010480 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010490 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000104A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 ..............@. 000104B0 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 ...@............ 000104C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000104D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000104E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000104F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010500 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010520 00 00 00 03 00 00 00 08 00 00 00 78 00 00 00 00 ...........x.... 00010530 00 00 80 10 02 02 FF FF 00 00 00 00 00 00 3F FF ..€...ÿÿ......?ÿ 00010540 00 00 00 00 00 00 07 FF 00 00 00 00 00 00 00 00 .......ÿ........ 00010550 00 00 00 00 00 00 00 00 00 00 00 00 19 54 01 19 .............T..
Other examples:
http://pastebin.com/3L241qu3 http://pastebin.com/WJv40nUQ http://pastie.org/1529241 http://pastie.org/1588747
FLASH
RBD
SATA/ATA/ATAPI
ATA Interrupt Handler
0x0026B984 (3.15)
ATA_SetDMA
0x00268ADC (3.15)
ATA_make_PRD_table
0x00267DB4 (3.15)
This function initializes a PRD (Physical Region Descriptor) table.
ClearPATACInterrupt
0x00267CAC (3.15)
EnablePATACInterrupt
0x00267D44 (3.15)
DisablePATACInterrupt
0x00267AF0 (3.15)
ATA_read_AltStatus_reg
0x00267C40 (3.15)
This function reads the ATA Alternate Status Register and returns it's value.
ATA_write_DATA_reg
0x00268A10 (3.15)
This function writes a 16-bit value to the ATA Data Register.
ATA_read_DATA_reg
0x0026887C (3.15)
ATA_write_DATA
0x0026635C (3.15)
This function writes several 16-bit values to the ATA Data register.
ATA_write_CMD_reg
0x002688A0 (3.15)
ATA_read_Error_reg
0x00267BD4 (3.15)
ATA_write_Features_reg
0x002689F0 (3.15)
ATA_write_DevCtrl_reg
0x00267BB4 (3.15)
ATA_write_TaskFile_regs
0x00266BC8 (3.15) 0x002665A0 (3.15)
ATA_send_ATAPI_cmd
0x002655F4 (3.15)
ATA_send_cmd
0x0026580C (3.15)
ATA_send_ReadSectors_cmd
This function uses LBA28.
0x0025D2B4 (3.15)
ATA_send_WriteSectors_cmd
This function uses LBA28.
0x0025CEF4 (3.15)
ATA_send_ReadDMA_cmd
This function uses LBA28.
0x0025D380 (3.15)
ATA_send_WriteDMA_cmd
This function uses LBA28.
0x0025CFB8 (3.15)
ATA_send_ReadDMAExt_cmd
This function uses LBA48.
0x0025D74C (3.15)
ATA_send_WriteDMAExt_cmd
This function uses LBA48.
0x0025D664 (3.15)
ATA_send_IdentifyDevice_cmd
0x0025D4D8 (3.15)
ATA_send_IdentifyPacketDevice_cmd
0x0025D448 (3.15)
ATA_send_FlushCache_cmd
0x0025D5E8 (3.15)
ATA_send_FlushCacheExt_cmd
0x0025D568 (3.15)
ATA_send_StandbyImmediate_cmd
0x0025D07C (3.15)
ATA_send_SetFeatures_cmd
0x0025D208 (3.15)
ATA_send_SMARTEnable_cmd
0x0025D0F8 (3.15)
ATA_send_SMARTSaveAttributeValue_cmd
0x0025D180 (3.15)
ATA_SetUDMAMode
0x00260EE8 (3.15)
Parameters
r5 - UltraDMA mode (0-5)
Booting a Bootloader from VFLASH
Coming soon !!!
High precision timers
These timers are used e.g. in SATA/ATA/ATAPI driver.
timer_add
0x002C3F2C (3.15)
timer_del
0x002C41AC (3.15)
timer_run_expired
This function is called from HDEC interrupt handler.
0x002C4020 (3.15)
timer_set_HDEC
0x002BCF80 (3.15)
SPE
There are 3 SPE classes.
The HV call lv1_construct_logical_spe can create LogicalSPE, SPEType1 and SPEType2 objects.
The syscall 0x10040 creates only SPEType1 objects.
The SPEType1 and SPEType2 objects cannot be created when isolation mode is disabled. The right most bit of repository node sys.lv1.iso_enbl is checked and when it's not 1 then the SPEType1 and SPEType2 objects cannot be created. In LPAR 1, this check succeedes always. Only in LPARs different from 1, the repository node sys.lv1.iso_enbl is checked.
LogicalSPE
SPE type = 0
Objects of this class are used e.g. on Linux.
vtable
0x00358360 (3.15)
offset 0x20 - pointer to TOC entry of interrupt handler for SPE
Member variables
offset 0x38 - pointer to LPAR obj that owns this SPE obj
offset 0x78 - table of pointers to Outlet objects (3 * 8 bytes, one for each Class 0-2)
offset 0xB0 - pointer to VAS object
offset 0xC8 - pointer to Logical PPE object
offset 0xE0 - SPE id
offset 0x1A0 - pointer to MMIO Memory Region object
offset 0x1A8 - pointer to Shadow Registers Memory Region object
Objects
Here is the list of logical SPE objects i found in HV 3.15:
- 0x003A82E0 - SPE id 0
- 0x003A8660 - SPE id 1
- 0x003ABA00 - SPE id 2
- 0x003B4010 - SPE id 3
- 0x003B4D60 - SPE id 4
- 0x003B5970 - SPE id 5
SPEType1
SPE type = 1
vtable
0x00359750
Member Variables
offset 0x198 - pointer to MMIO Memory Region object
offset 0x1A0 - pointer to Shadow Registers Memory Region object
SPEType2
SPE type = 2
vtable
0x00359790
SPE Register Shadow Area
- HV createas a SPE Register Shadow Area for each contstructed SPE.
- The area is 1 4Kb page of physical memory.
- When SPE state changes then HV updates data in this area.
- The value of shadow_addr that is returned by lv1_construct_logical_spe is a LPAR start address of this area and it cannot be accessed until it's mapped in the HTAB.
- The SPE Register Shadow Area may be mapped only with read-only page protection or else HV call lv1_insert_htab_entry fails. I tested it with PSGroove and could map the whole memory range and read it after i constructed SPE of type 1 with lv1_construct_logical_spe.
- The shadow_addr is also returned by syscall_10040 (that creates SPE of type 1) but it returns already mapped Process address so HV Processes do not have to map it in HTAB.
- When an isoated SPU is done, HV Processes checks the value at offset 0x30 to determine if the SPU execution was successfull or not.
- GameOS checks also the value at offset 0x30 in the SPE Shadow Area.
- When GameOS creates SPE of type 1 then it maps only SPE Register Shadow Area into it's address space.
SPE Register Shadow Area Offsets
0x30 - SPU_Status register value (4 bytes)
0xF10 - ?
0xF18 - ?
Stop Code
- The high-order 16 bit of SPU_Status register value is a Stop Code.
Here is the list of Stop Codes i extracted from HV Processes which read the value at offset 0x30 when SPU is done:
Value | Description |
---|---|
0xA | Success |
0xC | Access Violation (LPAR auth id error) |
0xE | ? |
0xF | Revoked |
0x12 | Invalid Parameter |
0x13 | ? |
0x17 | Invalid Parameter |
0x25 | ? |
SPU_send_MFC_cmd
0x002B09B0 (3.15)
This function programs a MFC.
SPU_write_MFC_cmd_status_reg
0x002AEE70 (3.15)
SPU_write_Sig_Notify1_reg
0x002AEF4C (3.15)
SPU_write_Sig_Notify2_reg
0x002AEF30 (3.15)
SPU_write_Sig_Notify1_and_Notify2
0x002B0A78 (3.15)
SPU_enable_iso_load_request
0x002AEDE0 (3.15)
SPU_iso_load_request
0x002AEED0 (3.15)
SPU_enable_runcntl
0x002AEB24 (3.15)
SPU_stop_request
0x002AEEF0 (3.15)
SPU_run_request
0x002AEF10 (3.15)
SPU_read_status_reg
0x002AE978 (3.15)
SPU_read_Mbox_Stat_reg
0x002AE998 (3.15)
lv1_undocumented_function_62
Updates SLB entry.
Parameters
%r3 - SPE id
%r4 - ? (valid values: 0 - 3)
%r5 - SLB entry index (valid values: 0 - 7)
%r6 - ESID
%r7 - VSID
spe_type1_interrupt_handler
0x0030E238 (3.15)
spe_type2_interrupt_handler
0x003103F8 (3.15)
spe_type3_interrupt_handler
0x002F36F4 (3.15)
Isolation
Loaders Table
- All the binary files needed for isolation and decryption are already stored in HV memory !!!
- They are probably loaded during HV initialization from FLASH.
- The table has 9 entries.
- Each entry is 16 bytes large.
0x00010100 (3.15)
Loaders Table Entry
offset 0x0 - pointer to data in memory
offset 0x8 - size of data
Here are the contents of the Loaders Table from HV 3.15:
Index | Name | Address of Data in HV Dump | Size of Data | Entry Id |
---|---|---|---|---|
0 | lv1ldr | 0x0C150000 | 0x1E5CC | 0x01 |
1 | metldr | 0x00011000 | 0xE8D0 | 0x00 |
2 | lv2ldr | 0x00020000 | 0x16DA0 | 0x02 |
3 | isoldr | 0x00055000 | 0x12E44 | 0x04 |
4 | appldr | 0x00037000 | 0x1DAE4 | 0x03 |
5 | EID0 | 0x00068000 | 0x860 | 0x0C |
6 | QA Flag | 0x00069010 | 0x8 | 0x0F |
7 | QA Flag Token | 0x00069020 | 0x50 | 0x10 |
8 | Trace Level | 0x00069070 | 0x8 | 0x11 |
Methods
get_iso_loaders_tab - 0x002B0B70 (3.15)
iso_loaders_tab_get_entry - 0x002B0CB8 (3.15)
metldr
Loading metldr
- Physical/Virtual memory address of an isolation module that should be loaded by metldr is written into SPU register SPU_In_Mbox. The SPU register SPU_In_Mbox is 32bit, so 64bit memory address is written in 2 steps.
- MFC relocation is turned off by clearing R-bit in SPU register MFC_SR1. By doing this, HV enables real address mode for MFC of SPU.
- On GameOS, it also works with relocation on. You just have to initialize SLB of SPU and insert valid SLB entries.
- Physical/Virtual memory address of metldr is written to SPU registers Sig_Notify1 and Sig_Notify2
- Isolation load request is enabled by writing SPU register SPU_PrivCntl
- Isolation load request is made by writing value 0x3 into SPU register SPU_RunCntl
Methods
SPE_load_request_metldr - 0x002B00A4 (3.15)
lv2ldr
- lv2ldr is used to decrypt lv2_kernel.self
- syscalls 0x10042 and 0x1004A use lv2ldr
- syscall 0x10042 is used by HV Process 3 during LV2 LPAR construction
- syscall 0x1004A uses different parameters as syscall 0x10042
Methods
SPE_load_request_lv2ldr_1 - 0x002AE82C (3.15)
SPE_load_request_lv2ldr_2 - 0x002AE8D8 (3.15)
Loading lv2ldr
- 64 bit memory address of lv2ldr is written into 32 bit SPU register SPU_In_Mbox
- metldr is loaded
isoldr
- isoldr is used for executing isolated SPUs
- syscall 0x10043 and HV call lv1_undocumented_function_209 use isoldr to execute isolated SPUs
- EID0 data is transferred to Local Storage Address 0x3E400 by MFC
- Revoke List For Program is transferred to Local Storage Address 0x3F000 by MFC
Revoke List For Programs
0x00361980 (3.15)
Methods
SPE_load_request_isoldr - 0x002B0394
Loading isoldr
- 64 bit memory address of isoldr is written into 32 bit SPU register SPU_In_Mbox
- metldr is loaded
appldr
- appldr is used for decryption of SELFs or EDATs
- HV call lv1_authenticate_program_segment loads appldr
Methods
SPE_load_request_appldr - 0x002AE900
Loading appldr
- 64 bit memory address of appldr is written into 32 bit SPU register SPU_In_Mbox
- metldr is loaded
Decrypting SELFs with appldr and lv1_authenticate_program_segment
- lv1_authenticate_program_segment loads and prepares appldr for SELF decryption.
- When appldr is ready to decrypt data, it sends a message via mailbox.
- The address and the size of the encrypted data is passed to appldr via a shared memory.
Socket
The socket supports only one address family 0x1F, one socket type 0 and one protocol 0.
Socket address
Socket address is called port ID. Valid port IDs are 0-63. Port ID 0 is reserved.
Socket state
2 - LISTEN
Socket table
The socket table contains 64 entries, one for each port ID. Each entry is 16 bytes large.
The socket table is at 0x0035F6E8 (3.15).
Here is the list of opened sockets i found in HV 3.15:
- 0x00091FE0 (port ID 0x23, accepts connections)
- 0x00127850 (port ID 0x24, accepts connections)
- 0x0012F810 (port ID 0x25, accepts connections)
Socket table entry
offset 0x0 - pointer to Socket obj
offset 0x8 - socket accepts connections or not (0 - does not accept, 1 - accepts, 1 byte)
vtable
0x00355DB0 (3.15)
offset 0xB0 - bind
offset 0xB8 - listen
offset 0xC8 - connect
Member variables
offset 0x360 - socket state (4 bytes)
offset 0x368 - port ID (8 bytes)
offset 0x370 - max backlog queue size (8 bytes)
Virtual Address Space
VAS
vtable
0x00357958 (3.15)
Member variables
offset 0x18 - pointer to LPAR that owns this VAS object
offset 0x48 - VAS id (8 bytes)
offset 0x70 - number of page sizes (4 bytes)
offset 0x74 - log2 of HTAB size
offset 0x78 - pointer to HTAB object
Objects
Here is the list of the VAS objects i found in HV dump 3.15:
- 0x001C8050 (VAS id 2, LPAR 1)
- 0x003B4910 (VAS id 3, LPAR 2)
- 0x003BDB50 (VAS id 48, LPAR 2)
HTAB
0x38(-0x69A8(HSPRG0)) - pointer to the currently active HTAB in LPAR
vtable
0x003575B0 (3.15)
Member variables
offset 0x48 - pointer to first PTE
offset 0x60 - LPID (4 bytes)
offset 0x64 - log2 of HTAB size (4 bytes)
Objects
Here is the list of the HTAB objects i found in HV dump 3.15:
- 0x001C8270 (VAS id 2, LPAR 1)
* 0x00180000 - HTAB PTEs (HTAB size 256 kB)
- 0x003A8050 (VAS id 3, LPAR 2)
* 0x00500000 - HTAB PTEs (HTAB size 1 MB)
- 0x003BC510 (VAS id 48, LPAR 2)
* 0x00800000 - HTAB PTEs (HTAB size 1 MB)
LPAR_change_HTAB
This function changes currently active HTAB. It writes to SDR1 register where HTAB address and size is stored.
0x002BE5D4 (3.15)
Process SLB
Each HV process has 16 SLB entries.
Each SLB entry is 16 bytes large and is in format expected by opcode slbmte.
Most of the entries are zero (invalid).
Each process has 4 valid SLB entries: code, data, heap and stack.
Process 3
SLB entries
0x0012D1F0 (3.15)
Name | ESID | VSID |
---|---|---|
code | 0x8 | 0x38 |
data | 0xC | 0x3C |
heap | 0xA | 0x3A |
stack | 0xF | 0x3F |
Process 5
SLB entries
0x00093120 (3.15)
Name | ESID | VSID |
---|---|---|
code | 0x8 | 0x48 |
data | 0xC | 0x4C |
heap | 0xA | 0x4A |
stack | 0xF | 0x4F |
Process 6
SLB entries
0x000E6960 (3.15)
Name | ESID | VSID |
---|---|---|
code | 0x8 | 0x58 |
data | 0xC | 0x5C |
heap | 0xA | 0x5A |
stack | 0xF | 0x5F |
Process 9
SLB entries
0x00763E20 (3.15)
Name | ESID | VSID |
---|---|---|
code | 0x8 | 0x8 |
data | 0xC | 0xC |
heap | 0xA | 0xA |
stack | 0xF | 0xF |
VUART
VUART is a bi-directional communication link. A VUART object has a peer VUART object.
Data written to a VUART object is stored NOT in the data buffer of the VUART object but in the data buffer of the peer VUART object.
VUART table
Every LPAR has a VUART table. A VUART table has 256 entries. Each entry is a pointer to a VUART object that implements VUART interface.
0x00677218 (3.15) - address of VUART table of LPAR 1
Here is the list of all VUART objects in LPAR 1 i found in HV 3.15:
- 0x006ABD90 - VUART 0
- 0x006ABEB0 - VUART 1
- 0x006A3CB0 - VUART 2
- 0x006A3DD0 - VUART 3
- 0x000A3410 - VUART 5
- 0x000A3250 - VUART 6
VUART [0-3] are used by /dev/sc[0-3] respectively.
VUART [0-3] are linked to VUART objects of different type i could not yet identify. These unknown VUART objects use eieio opcode a lot. So i think, they communicate with hardware peripheral.
A write/read to/from /dev/sc[0-3] is a write/read to/from VUART.
0x00762AA8 (3.15) - address of VUART table of LPAR 2
Here is the list of all VUART objects in LPAR 2 i found in HV 3.15:
- 0x00126660 - VUART 0
- 0x000A3010 - VUART 2
VUART 0 and VUART 2 of LPAR 2 are created by Process 9 during LPAR construction.
VUART class
Member variables
offset 0x48 - pointer to peer VUART object
offset 0x58 - write pointer into data ring buffer
offset 0x60 - read pointer into data ring buffer
offset 0x68 - pointer to data ring buffer
offset 0x70 - size of data ring buffer (8 bytes)
offset 0x78 - size of data stored in data ring buffer currently (8 bytes)
offset 0x88 - tx trigger (8 bytes)
offset 0x90 - rx trigger (8 bytes)
offset 0x98 - interrupt mask (8 bytes)
offset 0xA8 - port number (4 bytes)
Methods
pmpi_read_virtual_uart(port, buf, size, nread) - 0x002EB30C (3.15)
pmpi_write_virtual_uart(port, buf, size, nwritten) - 0x002EB0EC (3.15)
VUART_read(pointer to VUART object, buf, size, nread) - 0x002E8654 (3.15)
VUART_write(pointer to VUART object, buf, size, nwritten) - 0x002E8428 (3.15)
Guest OS VUART 0 (AV Manager)
All data sent to VUART 0 in LPAR 2 is written into the data buffer of VUART 5 of LPAR 1.
VUART 5 of LPAR 1 is accessed by Process 9 in LPAR 1 through the file /proc/partitions/2/vuart/0.
- Process 9 of LPAR 1 uses RSX syscalls to access RSX driver and memory mapped device access (/dev/ioif0).
Guest OS VUART 2 (System Manager)
All data sent to VUART 2 in LPAR 2 is written into the data buffer of VUART 6 of LPAR 1.
VUART 6 of LPAR 1 is accessed by Process 9 in LPAR 1 through the file /proc/partitions/2/vuart/2.
- System manager supports 62 (0-61) service ids.
- Process 9 has a SID table. SID table has 62 entries.
- Each entry is a pointer to a function responsible for processing SID packets.
AV Manager
Crossreference: gitbrew.org::AV Manager
- AV Manager is running in Process 9 of HV.
- It communicates with Guest OS through /proc/partitions/0/vuart/0 file.
- GameOS accesses AV Manager through syscalls 367 - 370.
- PS2 Soft EMU accesses AV Manager also.
- Communicates with SYSCON 0 (/dev/sc0)
- Communicates with IOIF0 (/dev/ioif0 or RSX)
Commands
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:
30 01 0200 0000 8033 00000000 0004 0004 11 00 0000 0000ff01
Set HDMI Mode (0x40001)
- Sets HDMI mode
- Mode is set by SYSCON
- Disabling HDCP
System Manager (SM)
- System Manager (SM) is running in Process 9 of HV.
- It communicates with Guest OS through /proc/partitions/2/vuart/2 file.
- GameOS accesses SM through syscalls 372 - 415
System Manager class
Member variables
offset 0x10 - LPAR state (8 bytes)
offset 0x68 - LPAR auth id
offset 0x70 - LPAR name
offset 0x90 - LPAR image path
offset 0x1C0 - LPAR ability (8 bytes)
Types of System Manager
- There are 6 different SM types
- When Process 9 starts it reads profile file, by default DEFAULT.SPP, by sending requests to SPL (Secure Profile Loader) and constructs System Managers listed in this profile file.
- So, the profile file controls which System Manager types are available later.
Name | LPAR name |
---|---|
SCE_CELLOS_PME | - |
SCE_CELLOS_SYSTEM_MGR | PS3_LPAR |
SCE_CELLOS_SYSTEM_MGR_PS2 | PS2_LPAR |
SCE_CELLOS_SYSTEM_MGR_PS2_SW | PS2_SW_LPAR |
SCE_CELLOS_SYSTEM_MGR_PS2_GX | PS2_GX_LPAR |
SCE_CELLOS_SYSTEM_MGR_LINUX | LINUX_LPAR |
Ability Bitmask
Index | Name | Ability Bitmask (Hex) | Ability Bitmask (Binary) |
---|---|---|---|
0 | SCE_CELLOS_PME | 0x1 | 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 |
1 | SCE_CELLOS_SYSTEM_MGR | 0x3BF7EF | 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0011 1011 1111 0111 1110 1111 |
2 | SCE_CELLOS_SYSTEM_MGR_PS2_SW | 0x1226D | 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0010 0010 0110 1101 |
3 | SCE_CELLOS_SYSTEM_MGR_LINUX | 0x40012 | 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100 0000 0000 0001 0010 |
Bit Position (from right) | SID | Description |
---|---|---|
1 | 5 (SET_NEXT_OP) | Shutdown or Reboot LPAR |
2 | 5 (SET_NEXT_OP) | Boot PS3 LPAR |
3 | 5 (SET_NEXT_OP) | Boot PS2_SW LPAR |
4 | 5 (SET_NEXT_OP) | Boot LINUX LPAR |
5 | 12 (CONTROL_LED) | Control LED |
6 | 21 (RING_BUZZER) | Ring Buzzer |
7 | 19 (SET_CONFIG) | Set Config |
9 | 25 / 50 (FAN_POLICY) | Fan Policy |
10 | 26 (REQUEST_ERROR_LOG) | Request Error Log |
10 | 28 (REQUEST_BE_COUNT) | Request BE Count |
10 | 32 (REQUEST_SYSTEM_EVENT_LOG) | Request System Event Log |
12 | 30 (REQUEST_SC_VERSION) | Request SC Version |
14 | 39 (SET_SHOP_DEMO_MODE) | Set Shop Demo Mode |
Service ID (SID)
SM supports 62 (0-61) SIDs.
The value of SM member variable ability controls which SIDs may be used by LPAR.
SID | Name | Description |
---|---|---|
0 | - | - |
1 | REQUEST | - |
2 | RESPONSE | - |
3 | COMMAND | - |
4 | EXTERN_EVENT | - |
5 | SET_NEXT_OP | - |
6 | - | - |
7 | - | - |
8 | SET_ATTR | - |
9 | GET_INTER_LPAR_PARAM | - |
10 | SET_INTER_LPAR_PARAM | - |
11 | - | - |
12 | CONTROL_LED | - |
13 | TEMPERATURE | - |
14 | - | - |
15 | Shares data with 25 | - |
16 | - | - |
17 | - | - |
18 | - | - |
19 | SET_CONFIG | - |
20 | - | - |
21 | RING_BUZZER | - |
22 | - | - |
23 | - | - |
24 | - | - |
25 | FAN_POLICY | - |
26 | REQUEST_ERROR_LOG | - |
27 | - | - |
28 | REQUEST_BE_COUNT | - |
29 | - | - |
30 | REQUEST_SC_VERSION | - |
31 | - | - |
32 | REQUEST_SYSTEM_EVENT_LOG | - |
33 | - | - |
34 | RTC_ALARM | - |
35 | - | - |
36 | RTC_ALARM | - |
37 | - | - |
38 | RTC_ALARM | - |
39 | SET_SHOP_DEMO_MODE | - |
40 | BOOT_PARAMETER | - |
41 | - | - |
42 | BOOT_PARAMETER | - |
43 | - | - |
44 | FACTORY_PROCESS_COMP | - |
45 | - | - |
46 | FACTORY_PROCESS_COMP | - |
47 | - | - |
48 | FACTORY_PROCESS_COMP | - |
49 | - | - |
50 | FAN_POLICY | - |
51 | - | - |
52 | - | - |
53 | - | - |
54 | - | - |
55 | - | - |
56 | - | - |
57 | - | - |
58 | - | - |
59 | - | - |
60 | - | - |
61 | - | - |
12 - CONTROL_LED
- I have tested this service with PSGroove and GameOS is allowed to use it.
- GameOS syscall 386 uses this service.
Packet Body
struct sysmgr_ctrl_led { u8 field0; u8 field1; u8 field2; u8 res1; u8 field4; u8 field5; u8 res2[10]; };
Parameters
I have tested the following parameters with this service:
field0 | field1 | field2 | field4 | field5 | Description |
---|---|---|---|---|---|
0x1 | 0x0 | 0xFF | 0xFF | 0xFF | Turns off the power button LED |
0x1 | 0x1 | 0xFF | 0xFF | 0xFF | Turns on the power button LED |
21 - RING_BUZZER
- I have tested this service with PSGroove and GameOS is allowed to use it
Packet Body
struct sysmgr_ring_buzzer { u8 res1; u8 field1; u8 field2; u8 res2; u32 field4; };
Parameters
I have tested the following parameters with this service:
field1 | field2 | field4 | Description |
---|---|---|---|
0x29 | 0x4 | 0x6 | Makes a short single beep |
0x29 | 0xA | 0x1B6 | Makes a triple beep |
0x29 | 0x7 | 0x36 | - |
0x29 | 0xA | 0xFFF | Makes a continuous beep |
field 1 seems relative to beep tone, as 0x25 sounds different
Active System Managers in HV dump 3.15
There are 4 active SMs in HV dump.
Index | Name | LPAR auth id | LPAR image pathname | Ability Bitmask (Hex) |
---|---|---|---|---|
0 | SCE_CELLOS_PME | 0x1070000001000001 | /flh/os/this_is_dummy | 0x1 |
1 | SCE_CELLOS_SYSTEM_MGR | 0x1070000002000001 | /flh/os/lv2_kernel.self | 0x3BF7EF |
2 | SCE_CELLOS_SYSTEM_MGR_PS2_SW | 0x1020000003000001 | /local_sys0/ps2emu/ps2_softemu.self | 0x1226D |
3 | SCE_CELLOS_SYSTEM_MGR_LINUX | 0x1080000004000001 | /flh/lx/linux | 0x40012 |
- GameOS file image lv2_kernel.self is stored on /dev/rflash1
- Linux file image is stored on /dev/rflash_1x or /dev/rflash_1xp
Booting Linux LPAR through System Manager
To boot Linux LPAR from GameOS when Linux support was not removed (Ability Mask of PS3 System Manager needs patching !!!):
- Send SID packet SET_NEXT_OP with operation OP_LPAR_REBOOT and the index of Linux system manager to System Manager (VUART 2)
- Send SID packet REQUEST with type SHUTDOWN to System Manager (VUART 2)
- Execute lv1_panic HV call in GameOS
It should also work when Linux support was removed but Linux system manager was not removed from Process 9 and also assumed that a Linux kernel image is stored at the right place in /dev/rflash_1x.
It's just a theory, nothing else, that i gathered during HV reversing. It needs a practical proof. Unfortunately, i don't have access to Hypervisor.
Booting modified and reencrypted lv2_kernel.self
- The System Manager of GameOS sends the path to lv2_kernel.self to SLL (Secure LPAR Loader) and SLL loads it from FLASH device file /dev/rflash1
- I stored a new lv2_kernel.self on FLASH directly by writing FLASH from GameOS. It't risky but if you know what you are doing then it's safe. I warned you guys. You could brick your PS3.
- Then i added a new TOC entry to FLASH device which points to the new lv2_kernel.self
- I patched the path to lv2_kernel.self in the System Manager of GameOS so it points to my new GameOS kernel (You need HV rights to do it)
- Then i rebooted GameOS without rebooting HV, so the patched file path should not change
- This method has the advantage that when the new lv2_kernel.self won't work you can just reboot HV and it will load the original lv2_kernel.self again
- lv2_kernel.self can be also loaded from GameOS dev_flash. For that, you have to change the path to lv2_kernel.self in default.spp from /flh/os/lv2_kernel.self to /local_sys0/lv2_kernel.self and store lv2_kernel.self on dev_flash.
AV Manager
All data sent to VUART 0 in LPAR 2 is written into the data buffer of VUART 5 of LPAR 1.
VUART 5 of LPAR 1 is accessed by Process 9 in LPAR 1 through the file /proc/partitions/2/vuart/0.
- During initialization, AV Manager opens /dev/ioif0 device and maps different address ranges of the device into address space of Process 9
- /dev/ioif0 is NOT opened and mapped if the value of repository node lv1.rsx.enable is less than 1
- /dev/ioif0 is mapped with READ/WRITE protection
- File descriptor of /dev/ioif0 in Process 9 is 4
- AV Manager supports a lot more commands than used on Linux
- Every command is implemented by a class
Mapped Address Ranges From /dev/ioif0
The base address of /dev/ioif0 is 0x28000000000. The device supports only mmap system call, it cannot be read or written. It also doesn't support ioctl.
Index | Absolute Address Range | Size | Mapped Address in Process 9 Address Space |
---|---|---|---|
0 | 0x28000000000 - 0x28000002000 | 0x2000 | 0xA0019000 |
1 | 0x28001800000 - 0x28001801000 | 0x1000 | 0xA0004000 |
2 | 0x28000600000 - 0x28000604000 | 0x4000 | 0xA001A000 |
3 | 0x28000680000 - 0x28000684000 | 0x4000 | 0xA0006000 |
4 | 0x28000080000 - 0x28000088000 | 0x8000 | 0xA000A000 |
5 | 0x28000088000 - 0x28000089000 | 0x1000 | 0xA000E000 |
6 | 0x2800000C000 - 0x2800000D000 | 0x1000 | 0xA0016000 |
7 | 0x2800008A000 - 0x2800008B000 | 0x1000 | 0xA0017000 |
8 | 0x2800008C000 - 0x2800008D000 | 0x1000 | 0xA0018000 |
Process socket services
Function ID and Packet ID
- Processes 3, 5 and 6 provide services (functions) to other Processes through sockets (something like RPC).
- A service is identified by a function ID.
- Each process has a hash table which maps a function ID to socket port ID.
- Services (functions) can be further differentiated by a packet ID.
- To request a service, a Process sends a packet with specified function and packet ID to the Process that provides the service.
- A process that provides a service (function) has a table of objects which handle different packet IDs.
- Services are synchronous, a client sends a request and waits for a response.
- If a Process requests a service that is located in the same Process then the service is called directly and sockets are not used !!! (e.g. SLL requests from DM creating VUART port during GameOS loading, SLL and DM are in the same Process, so SLL calls DM directly)
Port ID - Process ID mapping
Port ID | Process ID |
---|---|
0x23 | 6 |
0x24 | 5 |
0x25 | 3 |
Function ID - Port ID mapping
Function ID | Port ID | Supported Packet IDs | Function Description |
---|---|---|---|
0x2000 | 0x23 | 0x2001 - 0x2017 | Virtual TRM Manager |
0x3000 | 0x24 | 0x3001 - 0x3003 | Secure RTC |
0x5000 | 0x23 | 0x5001 - 0x500A | Storage Manager |
0x6000 | 0x23 | 0x6001 - 0x6011 | Update Manager |
0x8000 | 8 | 0x8001 - 0x8005 | Updater Frontend |
0x9000 | 0x24 | 0x9001 - 0x9016 | SC Manager |
0x10000 | 0x23 | 0x10001-0x10007 | SBM (South Bridge Manager) |
0x11000 | 0x25 | 0x11001 - 0x11002 | SPM (Security Policy Manager) |
0x14000 | 0x25 | 0x14004 - 0x14005 | SLL (Secure LPAR Loader) |
0x15000 | 0x24 | 0x15001, 0x15003, 0x15009 | SPL (Secure Profile Loader) |
0x17000 | 0x24 | 0x17001 - 0x17017 | Indi Info Manager |
0x18000 | 0x25 | 0x18001, 0x18002, 0x18004 | Dispatcher Manager |
0x19000 | 0x24 | 0x19002 - 0x19005 | AIM |
0x22000 | 0x16 | 0x22001 - 0x22004 | Factory Data Manager |
0x24000 | 0x23 | 0x24001 - 0x24002 | USB Dongle Authenticator |
0x25000 | 0x23 | 0x25001 - 0x25002 | User Token Manager |
SS Packet
- SS means Secure Service ?
- Processes send SS Packets to request a service or to reply to a service request.
Member variables
offset 0x8 - packet ID (8 bytes)
offset 0x10 - function ID (8 bytes)
offset 0x18 - return value (4 bytes)
offset 0x20 - subject ID (2 * 8 bytes)
Header
- All services use a common header.
- The header of a SS Packet is 0x28 bytes large.
struct ss_header { uint64_t packet_id; uint64_t function_id; uint32_t retval; uint8_t res[4]; uint64_t laid; /* LPAR Authority ID */ uint64_t paid; /* Program Authority ID */ }
SS Service Return Values
Error Code | Description |
---|---|
0x00000000 | Success |
0x00000005 | Access Violation |
0x00000006 | No Entry ? |
0x00000009 | Invalid Parameter |
0x0000000F | Call Limit Exceeded ? |
Body
- The body of a SS Packet follows after the header.
- The size of the body depends on a used service.
LPAR Memory Management
Memory Region class
This class is the base class for different memory region types.
vtable
0x003578B0 (3.15)
Member variables
offset 0x40 - pointer to LPAR object that owns this memory region
offset 0x48 - type of memory region (8 bytes)
offset 0x50 - LPAR start address of memory region
offset 0x58 - size of memory region (8 bytes)
offset 0x60 - flags (8 bytes)
offset 0xA0 - log2 of page size
Generating New LPAR Memory Region Addresses
generate_new_lpar_mem_region_address(?, memory region size, log2(page size), ?, ?) - 002C82E8 (3.15)
generate_new_lpar_mem_region_address - 002C6570 (3.41)
- The function returns a new LPAR memory region address.
- This method is used e.g. in all HV calls which create any kind of memory regions, e.g. lv1_allocate_memory, lv1_map_htab, lv1_undocumented_function_114, lv1_construct_logical_spe, lv1_map_device_mmio_region or syscall 0x10040.
Encoding LPAR Memory Region Start Addresses and Sizes
- Size of LPAR memory region is encoded in the LPAR memory region start address.
- That is why e.g. the LPAR Memory Region Start Addresses of LPAR Memory Region of size 4096 byte begin with 0x300000000000, 0x300000000000 >> 42 = 0xC = log2(4096).
- Each LPAR has a counter (8 bytes) which is incremented by 1 every time a new LPAR Memory Region is created.
- Before incrementing, the counter is shifted left by log2(LPAR Memory Region Size) and ored with log2(LPAR Memory Region Size) << 42.
LPAR Memory Region Start Address >> 42 = log2(LPAR Memory Region Size)
LPAR Memory Region Start Address = (log2(LPAR Memory Region Size) << 42) | (counter << log2(LPAR Memory Region Size))
LPAR Memory Region Address Counter
- LPAR Memory Region Address Counter is stored at address: 0x38(LPAR ptr) + 0x9E8
- LPAR1's Memory Region Address Counter is at address 0x00677A48 in HV dump 3.15
- LPAR2's Memory Region Address Counter is at address 0x007632D8 in HV dump 3.15
- LPAR1's Memory Region Address Counter is at address 0x00677A48 in HV dump 3.41
- LPAR2's Memory Region Address Counter is at address 0x00161E68 in HV dump 3.41
Physical Memory Region class
This type of memory region is created e.g. in lv1_allocate_memory HV call or in syscall 0x10000.
vtable
0x00357D08 (3.15)
Member variables
offset 0xB0 - pointer to object that stores a list of addresses of physical pages owned by this memory region
offset 0xB8 - pointer to LPAR object that owns this memory region
offset 0xC0 - reference counter (8 bytes)
Objects
Here is the list of physical memory region objects i found in HV 3.15.
Address in HV dump | LPAR id | LPAR Start Address | Size | Flags | log2(Page Size) | Physical Page Addresses |
---|---|---|---|---|---|---|
0x006B5510 | 1 | 0x300000001000 | 0x1000 | 0x0 | 0xC | 0x672000 |
0x006B5E50 | 1 | 0x440000040000 | 0x20000 | 0x0 | 0x11 | 0x6C0000 |
0x006B6980 | 1 | 0x440000060000 | 0x20000 | 0x0 | 0x11 | 0x6E0000 |
0x006B7F00 | 1 | 0x400000040000 | 0x10000 | 0x0 | 0x10 | 0x100000 |
0x003A80F0 | 2 | 0x6C0058000000 | 0x7000000 | 0x4 | 0x18 | 0x1000000 - 0x7000000 |
0x003BE800 | 2 | 0x300000047000 | 0x1000 | 0x0 | 0xC | 0x1FA000 |
0x006BDAA0 | 2 | 0x0 | 0x8000000 | 0x8 | 0x1B (single huge page) | 0x8000000 |
So, Linux kernel should be located at physical address 0x8000000 and Linux syscall handler at 0x8000C00. Too bad that the HV dump is not large enough.
GameOS Physical Memory Regions
- GameOS allocates nearly all physical memory of PS3 for itself !!! That is why new HV calls lv1_allocate_memory with large memory region sizes will fail.
- So when someone wants a large piece of physical memory, he can borrow it from GameOS's LPAR memory region that starts at 0x700020000000. It can be used for example to send update packages to Update Manager which are very large.
Here is the list of physical memory regions of GameOS i found in HV 3.41:
Start Address | Size | Access Right | Max Page Size | Flags | Real Addresses |
---|---|---|---|---|---|
0x0 | 0x1000000 | 0x3 | 0x18 | 0x8 | 0x1000000 - 0x1FFF000 |
0x500000300000 | 0xA0000 | 0x3 | 0x10 | 0x8 | 0x380000 - 0x38F000, 0x3B0000 - 0x3BF000, 0x1E0000 - 0x1FF000, 0x3C0000 - 0x3FF000, 0xFF00000 - 0xFF1F000 |
0x700020000000 | 0xE900000 (huge memory region) | 0x3 | 0x14 | 0x0 | 0x400000 - 0x5FF000, 0x800000 - 0xFFF000, 0x2000000 - 0xFEFF000 |
HTAB Memory Region class
This memory region is created when a HTAB is mapped into LPAR's address space. It's created in lv1_map_htab HV call.
vtable
0x00357C98 (3.15)
Member variables
offset 0xB0 - pointer to VAS object that owns the HTAB
Objects
Here is the list of HTAB memory region objects i found in HV 3.15.
Address in HV dump | LPAR id | VAS id | LPAR Start Address | Size | Flags | log2(Page Size) |
---|---|---|---|---|---|---|
0x001FE0F0 | 2 | 3 | 0x500000C00000 | 0x100000 | 0xC000000000000000 | 0x14 |
0x003BD850 | 2 | 3 | 0x500004300000 | 0x100000 | 0xC000000000000000 | 0x14 |
0x003BDEA0 | 2 | 3 | 0x500004500000 | 0x100000 | 0xC000000000000000 | 0x14 |
GameOS HTAB
- HTAB of GameOS is already mapped into address space of GameOS so that is why HV call lv1_map_htab will fail until you unmap it with lv1_unmap_htab
- Effective address of GameOS HTAB is 0x800000000F000000
- Virtual address of GameOS HTAB is 0xF000000
- Size of GameOS HTAB is 0x40000
- GameOS HTAB supports large pages of size 64K and 1M
- GameOS HTAB can be easily dumped by reading 0x40000 bytes at EA 0x800000000F000000
GameOS SLB
Here is the dump of SLB entries from GameOS 3.41:
0x8000000008000000 0x0000000000000500 0x8000000208000000 0x0000000000020500 0x8000000300000000 0x0000000000030510 0x0000000000000000 0x0000000000000000 0x0000000080000000 0x0000000000038C00 0x00000000A0000000 0x000000000003AC00 0x00000000C0000000 0x000000000003CC00 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x8000000010057960 0x8000000000313E78 0x8000000010057940 0x0000000000000000 0x800000000001B698 0x0000000000000000 0x8000000010057930 0x8000000000490708 0x80000000002B6C68 0x80000000003DE928 0x8000000010057EC0 0x80000000003DE920 0x0000000000000000 0x8000000000309810 0x80000000004B3000 0x0000000000000000 0x8000000010057CC0 0x0000000000000000 0x80000000004AF000 0x80000000004E1F00 0x80000000100579C8 0x80000000100579C0 0x80000000100579E0 0x2400002200000000 0x80000000004CF5B0 0x8000000200012000 0x80000000100579F8 0x80000000100579F0 0x8000000010057A10 0x80000000004A3A00 0x80000000004CF5B0 0x80000000004C8D00 0x800000000001BF6C 0x80000000004CD400 0x800000000001B698 0x80000000004C8100 0x80000000100579D0 0x80000000004B48C0 0x0000000000001C08 0x0000000000000000 0x8000000010057A78 0x8000000010057A70 0x8000000010057A90 0x0000000000000000 0x80000000004CF90C 0x0000000000000000 0x0000000000000000 0x8000000010057A80 0x8000000010057A90 0x8000000000309810 0x80000000004CF62C 0x0000000000000000 0x8000000010057CC0 0x0000000000000000 0x80000000004AF000 0x80000000004B48C0 0x00004000001C0000 0x0000000000000001 0x00000000D0000000 0x0000A8E3EE7D10DA 0x0000000000000000 0x0000000000000000 0x80000000004D8088 0x80000000004D9000
SPE MMIO Memory Region class
This type of memory region represents MMIO memory region of a SPE. It's created e.g. in lv1_construct_logical_spe or in syscall 0x10040.
vtable
0x003583F8 (3.15)
Member variables
Objects
Here is the list of SPE memory region objects i found in HV 3.15.
Address in HV dump | LPAR id | SPE | LPAR Start Address | Size | Physical Address | Flags | log2(Page Size) |
---|---|---|---|---|---|---|---|
0x003ABC20 | 2 | 1 | 0x4C0000880000 | 0x80000 | 0x20000080000 | 0xA000000000000000 | 0xC |
0x003AAD70 | 2 | 2 | 0x4C0000980000 | 0x80000 | 0x20000100000 | 0xA000000000000000 | 0xC |
0x003A8880 | 2 | 3 | 0x4C0000780000 | 0x80000 | 0x20000180000 | 0xA000000000000000 | 0xC |
0x003B4F70 | 2 | 4 | 0x4C0000A80000 | 0x80000 | 0x20000200000 | 0xA000000000000000 | 0xC |
0x003AB700 | 2 | 5 | 0x4C0000680000 | 0x80000 | 0x20000280000 | 0xA000000000000000 | 0xC |
0x003B5BE0 | 2 | 6 | 0x4C0000B80000 | 0x80000 | 0x20000300000 | 0xA000000000000000 | 0xC |
SPE Shadow Registers Memory Region class
This type of memory region represents shadow registers memory region of a SPE. It's created e.g. in lv1_construct_logical_spe or in syscall 0x10040.
vtable
0x00358448 (3.15)
Objects
Here is the list of SPE Shadow Registers memory region objects i found in HV 3.15.
Address in HV dump | LPAR id | SPE | LPAR Start Address | Size | Physical Address | Flags | log2(Page Size) |
---|---|---|---|---|---|---|---|
0x003ABDA0 | 2 | 1 | 0x300000012000 | 0x1000 | - | 0xA000000000000000 | 0xC |
0x003B4290 | 2 | 2 | 0x300000014000 | 0x1000 | - | 0xA000000000000000 | 0xC |
0x003A8A00 | 2 | 3 | 0x300000010000 | 0x1000 | - | 0xA000000000000000 | 0xC |
0x003B50F0 | 2 | 4 | 0x300000016000 | 0x1000 | - | 0xA000000000000000 | 0xC |
0x001FFC90 | 2 | 5 | 0x30000000E000 | 0x1000 | - | 0xA000000000000000 | 0xC |
0x003AE5B0 | 2 | 6 | 0x300000018000 | 0x1000 | - | 0xA000000000000000 | 0xC |
Device MMIO Memory Region class
This type of memory region is created when a device MMIO region is mapped into LPAR address space, e.g. in lv1_map_device_mmio_region.
vtable
0x00352468 (3.15)
Member variables
offset 0xA8 - physical address where the device MMIO region is mapped to
Objects
Here is the list of Device MMIO memory region objects i found in HV 3.15.
Address in HV dump | LPAR id | LPAR Start Address | Size | Flags | log2(Page Size) | Physical Address | Device |
---|---|---|---|---|---|---|---|
0x001FDF00 | 2 | 0x4000001D0000 | 0x10000 | 0x8000000000000000 | 0xC | 0x24003010000 | USB controller |
0x003B3850 | 2 | 0x400000200000 | 0x10000 | 0x8000000000000000 | 0xC | 0x24003020000 | USB controller |
0x003B6E50 | 2 | 0x4000001E0000 | 0x10000 | 0x8000000000000000 | 0xC | 0x24003810000 | USB controller |
0x003B9950 | 2 | 0x4000001F0000 | 0x10000 | 0x8000000000000000 | 0xC | 0x24003820000 | USB controller |
GPU Device Memory Region class
This type of memory region is created e.g. in lv1_gpu_open, lv1_gpu_device_map and lv1_undocumented_function_114.
vtable
0x00357C48 (3.15)
Member variables
offset 0xA8 - physical address
Objects
Here is the list of Device GPU memory region objects i found in HV 3.15.
Address in HV dump | LPAR id | LPAR Start Address | Size | Flags | log2(Page Size) | Physical Address |
---|---|---|---|---|---|---|
0x003AF380 | 2 | 0x700190000000 | 0xFE00000 | 0x8000000000000000 | 0x14 | 0x28080000000 |
0x003AF500 | 2 | 0x4000001A0000 | 0xC000 | 0x8000000000000000 | 0xC | 0x3C0000 |
0x003AF680 | 2 | 0x4800006C0000 | 0x40000 | 0x8000000000000000 | 0xC | 0x2808FE00000 |
0x003AFC30 | 2 | 0x440000380000 | 0x20000 | 0x8000000000000000 | 0xC | 0x28000C00000 |
0x003BB420 | 2 | 0x3C0000108000 | 0x8000 | 0x8000000000000000 | 0xC | 0x28000080100 |
Direct Map Memory Region class
This type of memory region is created in HV call lv1_undocumented_function_114. lv1_undocumented_function_114 allows you to map any memory address into LPAR's memory address.
- The HV call lv1_undocumented_function_115 destroys a memory region of this type.
- HV allows GameOS to create objects of this type of size 0 only !!! But it can be exploited with a dangling HTAB entry.
vtable
0x00357C48 (3.15)
Member variables
offset 0xA8 - physical address
Exploiting HV with memory glitching and HV call lv1_undocumented_function_114
Here is a short description of the method i used to exploit HV from GameOS 3.15 and 3.41.
- First i used the Geohot's method to create a dangling HTAB entry.
- Making memory glitch work on GameOS was the largest of my obstacles but i solved it and i'm able to create a dangling HTAB entry from GameOS within 1-3 minutes.
- Then i created many Direct Map Memory Region objects of size 0 with HV call lv1_undocumented_function_114 and checked if they are within the page to which the dangling HTAB entry points to.
- When i found one such Direct Map Memory Region object i patched the size of this object to 0x1000. Then i pointed this memory region object to the code of HV call lv1_undocumented_function_114 and patched 4 bytes in this HV call which allows me to create any Direct Map Memory Region objects without any restrictions.
- Function LPAR_construct_direct_mapping_mem_region which is used by HV call lv1_undocumented_function_114 has a parameter (register %r9) and when this parameter is not 0 then HV will allow you to create any Direct Map Memory Region objects without restrictions, but unfortunately the HV call lv1_undocumented_function_114 passes 0 in this parameter, so i just patched it.
- Then i mapped whole HV memory range with the patched HV call lv1_undocumented_function_114 into the address space of GameOS.
- And now you have read/write access to the whole HV.
- $ONY could fix this exploit by disallowing creating of Direct Map Memory Region objects of size 0, but i know tons of other HV C++ classes which will allow me to exploit the HV in a similar way, so it wouldn't bring $ONY anything :-) And they have to change member variable offsets in those objects to make sure that i cannot patch them easily :-)
Methods
LPAR_get_memory_region_by_start_address - 0x002C7C40 (3.15)
LPAR_get_memory_region_by_address - 0x002C7DA8 (3.15)
LPAR_mem_addr_to_phys_addr(LPAR id, LPAR address, phys_addr) - 0x002FB8F0 (3.15)
LPAR_construct_direct_mapping_mem_region - 0x002D4D04 (3.15)
Network Devices
Ethernet Gelic Device
device id = 0
MAC Address: 00:1F:A7:C6:2A:C5
device memory base address = 0x24003004000 (size = 0x1000)
WLAN Gelic Device
device id = 0
MAC Address: 02:1F:A7:C6:2A:C5 (locally administered)
Net Manager
- Net Manager runs in Process 9
- It sends commands to /dev/sc1 to reset WLAN Gelic device
- It opens /dev/net0, sets MAC address and writes device firmware eurus_fw.bin to WLAN device by using ioctl syscall
/dev/net0
The device supports 3 ioctl commands:
- 0 - 0x002AC10C (3.15)
- 1 - 0x002AC250 (3.15)
- 2 - EURUS_STAT 0x002AC320 (3.15)
Methods
net_control_cmd_GELIC_LV1_POST_WLAN_CMD - 0x0024A55C (3.15)
net_control_wlan_cmd_GELIC_EURUS_CMD_ASSOC - 0x00246C78 (3.15)
net_control_wlan_cmd_GELIC_EURUS_CMD_START_SCAN - 0x00248A14 (3.15)
net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WEP_CFG - 0x00249F24 (3.15)
net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WPA_CFG - 0x002497B8 (3.15)
Event Notification
- Event Notfication is used e.g. to notify a LPAR about some event, e.g. device interrupt or notify a LPAR about destruction of another LPAR.
- For example Process 9 is notified through Event Notification when LPAR 2 is destructed.
- During LPAR construction, Process 9 creates an Outlet object with syscall 0x1001A and then passes the outlet ID to the syscall 0x10009 that constructs the LINUX LPAR. In this way Process 9 is notified when LINUX LPAR is destructed.
Outlet class
This is the base Outlet class. There are different types of Outlet and they derive from this base class.
vtable
0x00357DC0 (3.15)
Member variables
offset 0x30 - type (8 bytes)
offset 0x38 - pointer to LPAR that owns this Outlet object
offset 0x48 - outlet id (8 bytes)
offset 0x90 - VIRQ assigned to this Outlet object (4 bytes)
Event Receive Port class
- This type of Outlet is created e.g. in lv1_construct_event_receive_port and in syscall 0x1001A.
- HV calls lv1_connect_irq_plug and lv1_connect_irq_plug_ext assigns a VIRQ to Event Receive Port object.
vtable
0x00357E88
VUART Outlet
- HV supports only one VUART Outlet per LPAR
- lv1_configure_virtual_uart_irq constructs a VUART Outlet object and passes the address of LPAR's VUART IRQ Bitmap to HV
vtable
0x00357DC0
VUART IRQ Bitmap
- At address 0x38(LPAR ptr) + 0x158 is the VUART IRQ Bitmap owned by HV for LPAR (4 * 8 bytes = 256 bits)
- At address 0x38(LPAR ptr) + 0x150 is stored the physical address of LPAR's VUART IRQ Bitmap that was passed to lv1_configure_virtual_uart_irq
- When a VUART interrupt is generated by HV then first the VUART IRQ Bitmap owned by HV is updated and then this bitmap is copied to LPAR's VUART IRQ Bitmap, so VUART IRQ Bitmap is stored twice, once in HV and once in LPAR, just like IRQ State Bitmap.
- VUART IRQ Bitmap is not allowed to cross page boundary of LPAR memory region where it is stored. HV checks it and makes sure that it doesn't happen.
- GameOS 3.41 VUART IRQ bitmap is at address 0x80000000003556E8 and of size 32 bytes (256 bits, each bit corresponds to a VUART port).
- GameOS 3.15 VUART IRQ bitmap is at address 0x8000000000354768.
Logical PPE
- 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
0x00357DF0 (3.15)
Member variables
offset 0x90 - pointer to an object that contains VIRQ-Outlet mapping table for thread 0
offset 0x98 - pointer to an object that contains VIRQ-Outlet mapping table for thread 1
Objects
Here is the list of Logical PPE objects i found in HV 3.15.
Address in HV dump | LPAR id | PPE id |
---|---|---|
0x0069C7F0 | 1 | 1 |
0x007A8900 | 2 | 1 |
Virtual IRQ - Outlet Mapping
- HV maintains 2 tables per PPE that map a VIRQ to an Outlet object.
- The table has 256 entries and is indexed by VIRQ.
- Each entry is a pointer to Outlet object.
- Each Logical PPE object has 2 tables, one for each thread of Cell CPU.
LPAR 1 PPE 1 Thread 0
0x0069C990 (3.15) - address of VIRQ-Outlet table for LPAR 1 PPE 1 Thread 0 (not empty)
VIRQ | Address of Outlet object in HV dump | Description |
---|---|---|
58 | 0x00090D10 | - |
59 | 0x006BAC50 | - |
60 | 0x006B3ED0 | FLASH storage device / Storage device notification for LPAR 1 |
61 | 0x00697E70 | VUART interrupts |
62 | 0x001C8F20 | - |
LPAR 1 PPE 1 Thread 1
0x0069D9B0 (3.15) - address of VIRQ-Outlet table for LPAR 1 PPE 1 Thread 1 (empty)
LPAR 2 PPE 1 Thread 0
0x000A06B0 (3.15) - address of VIRQ-Outlet table for LPAR 2 PPE 1 Thread 0 (not empty)
VIRQ | Address of Outlet object in HV dump | Description |
---|---|---|
20 | 0x003AA210 | - |
21 | 0x003AFEC0 | - |
22 | 0x001FC010 | - |
23 | 0x003A8E50 | - |
24 | 0x001FFED0 | SPE 0 Class 0 Interrupt |
25 | 0x003AE160 | SPE 0 Class 1 Interrupt |
26 | 0x003AE350 | SPE 0 Class 2 Interrupt |
27 | 0x003AB100 | SPE 1 Class 0 Interrupt |
28 | 0x003AB2F0 | SPE 1 Class 1 Interrupt |
29 | 0x003AB4E0 | SPE 1 Class 2 Interrupt |
30 | 0x003AA6A0 | SPE 2 Class 0 Interrupt |
31 | 0x003AA890 | SPE 2 Class 1 Interrupt |
32 | 0x003AAA80 | SPE 2 Class 2 Interrupt |
33 | 0x003B44A0 | SPE 3 Class 0 Interrupt |
34 | 0x003B4690 | SPE 3 Class 1 Interrupt |
35 | 0x003B4AD0 | SPE 3 Class 2 Interrupt |
36 | 0x003B5300 | SPE 4 Class 0 Interrupt |
37 | 0x003B54F0 | SPE 4 Class 1 Interrupt |
38 | 0x003B56E0 | SPE 4 Class 2 Interrupt |
39 | 0x003AE7C0 | SPE 5 Class 0 Interrupt |
40 | 0x003AE9B0 | SPE 5 Class 1 Interrupt |
41 | 0x003AEBA0 | SPE 5 Class 2 Interrupt |
42 | 0x003B2040 | Storage device notification for LPAR 2 |
43 | 0x003AEE30 | VUART interrupts |
44 | 0x001FEAA0 | - |
45 | 0x001FEED0 | HDD storage device |
46 | 0x003B5E20 | - |
47 | 0x003B7040 | - |
48 | 0x003B9B40 | - |
49 | 0x003B3A40 | - |
50 | 0x003BACA0 | Gelic device |
51 | 0x003BAE10 | UNKNOWN storage device |
52 | 0x003B8350 | - |
LPAR 2 PPE 1 Thread 1
0x007A89E0 (3.15) - address of VIRQ-Outlet table for LPAR 2 PPE 1 Thread 1 (not empty)
VIRQ | Address of Outlet object in HV dump | Description |
---|---|---|
16 | 0x003B2480 | - |
17 | 0x003B2590 | - |
18 | 0x003B26A0 | - |
19 | 0x003B27B0 | - |
IRQ State Bitmap
- There is one IRQ State Bitmap (256 bits = 32 bytes) per thread of Logical PPE
- HSPRG0 value is per thread, so there are 2 HSPRG0 values in HV dump !!!
- The IRQ State Bitmap of a thread is stored at -0x68E0(HSPRG0)
- When an Event or Interrupt happens then the bitmap at 0x68E0(HSPRG0) is updated
- The physical address of LPAR's IRQ State Bitmap of thread is stored at offset -0x68C0(HSPRG0)
- The address of LPAR's IRQ State Bitmap is passed to Hypervisor through HV call lv1_configure_irq_state_bitmap
- lv1_detect_pending_interrupts returns value of current IRQ State Bitmap.
- The IRQ State Bitmap is updated if an Outlet object is assigned to VIRQ and when Outlet generates an event
- After IRQ State Bitmap update, it's copied to LPAR's IRQ State Bitmap and a hardware interrupt is generated so that LPAR can read it's IRQ State Bitmap and handle interrupts.
- So, IRQ State Bitmap is stored twice, once in HV and once in LPAR, just like VUART IRQ Bitmap.
- GameOS IRQ state bitmap is stored at address SPRG0 + 0x1C0 and of size 64 bytes (256 bits state + 256 bits mask) per thread of Cell CPU. So there are 2 IRQ state bitmaps.
0x8941FC0 - physical address of LPAR's IRQ State Bitmap for Thread 0 of LINUX LPAR
0x8948FC0 - physical address of LPAR's IRQ State Bitmap for Thread 1 of LINUX LPAR
System Controller (SC or SYSCON)
- Data received from SC is sent to a VUART
- lv1_get_rtc and syscall 0x10036 communicate with SC VUART 4.
VUART Table
- Address of SC VUART Table - 0x00610410 (3.15).
- There are 5 VUARTs for SC in HV 3.15
Here is the SC VUART table from HV 3.15:
Index | Address of VUART object in HV dump | Description |
---|---|---|
0 | 0x0060FD20 | This VUART is connected with the VUART 0 (/dev/sc0) of LPAR 1 |
1 | 0x0060FE20 | This VUART is connected with the VUART 1 (/dev/sc1) of LPAR 1 |
2 | 0x0060FF20 | This VUART is not connected to some peer VUART but i guess that it should be connected to VUART 2 (/dev/sc2) of LPAR1 |
3 | 0x006124E0 | This VUART is connected with the VUART 3 (/dev/sc3) of LPAR 1 |
4 | 0x00612DF0 | lv1_get_rtc and syscall 0x10036 communicate with this VUART. |
Interrupt Handling
spider_sc_interrupt_handler - 0x0020A68C (3.15)
Methods
sc_vuart_4_get_peer_vuart - 0x002ED384 (3.15)
sc_send - 0x0020A908 (3.15)
sc_receive - 0x0020A354 (3.15)
sc_vuart_rx_trigger_callback - 0x002ED470 (3.15)
lv1_get_rtc
- lv1_get_rtc communicates with SC VUART 4.
- 20 bytes are written to the peer VUART of SC VUART 4.
- After a request is sent to SC VUART 4, lv1_get_rtc busy waits until SC VUART 4 receive data buffer is not empty.
- When SC VUART 4 receive data buffer is not empty, lv1_get_rtc reads 24 bytes from the VUART.
SYSCON Protocol
- I was able to enable SYSCON Manager debug messages in HV Process 5
- Messages sent to SYSCON are at least 0x10 bytes of size. SC VUARTs check it before sending the messages to SYSCON.
- The header size of the SYSCON messages is 0x10 bytes.
Packet Header
- Packet header is of size 0x10 bytes.
- At offset 0x6 of SYSCON packet is the header checksum which is of size 2 bytes.
- The header checkum is just a sum of first 6 header bytes and 0x8000 constant
- The 2nd byte in every SYSCON message has to be 1 or else the function sc_send fails.
- The word at offset 0x8 is the SC VUART index.
- The half-words at offset 0xC and 0xE have to be equal or the function sc_send fails.
struct sc_hdr { uint8_t field0; uint8_t field1; /* always 1 */ uint8_t field2[4]; uint16_t cksum; /* header checksum */ uint32_t index; /* syscon index (0 - /dev/sc0, 1 - /dev/sc1, 2 - /dev/sc2, 3 - /dev/sc3) */ uint16_t size1; /* body size */ uint16_t size2; /* body size */ };
Calculating Packet Header Checksum
/* calculating SC packet header checksum */ /* * sc_hdr_cksum */ uint16_t sc_hdr_cksum(struct sc_hdr *sc_hdr) { uint8_t *ptr; uint32_t sum; ptr = (uint8_t *) sc_hdr; sum = 0; for (i = 0; i < 6; i++) sum += *ptr++; sum += 0x8000; return sum & 0xffff; } struct sc_hdr sc_hdr; memset(&sc_hdr, 0, sizeof(sc_hdr)); sc_hdr.cksum = sc_hdr_cksum(sc_hdr); /* fill sc header here */ sc_hdr.cksum = sc_hdr_cksum(sc_hdr);
Packet Body
- Packet body follows packet header
- Packet body size is stored at offset 0xC and 0xE in packet header and is of size 2 bytes
Reading SYSCON EPROM (NVS Service)
Here is a command which is sent to SYSCON to read 1 byte of EPROM at offset 0x48C07 (Product Mode):
0x14 0x01 0x00 0x00 0x00 0x00 0x80 0x15 0x00 0x00 0x00 0x00 0x00 0x04 0x00 0x04 0x20 0x02 0x07 0x01
And here is the response to the above request:
0x14 0x01 0x00 0x00 0x00 0x00 0x80 0x15 0x00 0x00 0x00 0x03 0x00 0x05 0x00 0x05 0x00 0x02 0x07 0x01 0xff
PCI Bus Power
- Used by PS2EMU System Manager in HV process 9 when PS2 EMU is booted
PCI Bus Power On
Request to SC1:
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x01
PCI Bus Power Off
Request to SC1:
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x00
Ring Buzzer
Request:
0x16 0x01 0x00 0x00 0x00 0x00 0x80 0x17 0x00 0x00 0x00 0x00 0x00 0x08 0x00 0x08 0x20 0x00 0x00 0x00 0x00 0x00 0x00 0x00
SYSCON
Crossreference: gitbrew.org::SYSCON
SYSCON MMIO registers can be accessed on Linux with a driver using lv1_undocumented_function_114, e.g. ps3sbmmio. Use ps3sbmmio device driver carefully, an access at some addresses could shutdown your PS3.
Packet Header
- Size is 0x10.
struct sc_hdr { uint8_t service_id; uint8_t version; /* must be 1 !!! */ uint16_t transaction_id; /* returned in response */ uint8_t res[2]; uint16_t cksum; /* checksum of first 6 header bytes */ uint32_t communication_tag; /* SYSCON tag: 0-4 */ uint16_t payload_size[2]; /* body size */ };
Sending Packets
- Before sending new packet to SYSCON, the Hypervisor checks 2 words at offsets 0x2400008DFF0 and 0x2400008CFF4.
- The Hypervisor busy waits until (value + 1) at offset 0x2400008CFF4 is NOT equal to value at offset 0x2400008DFF0.
- The packet is sent with 4 byte transfers.
- First, the Hypervisor sends the header of the packet, 4 word transfers.
- The header is written beginning at the address 0x2400008D000.
- After that the Hypervisor sends the body of the packet, with 4 byte transfers too.
- The body is written beginning at the address 0x2400008D010.
- If the packet size is NOT divisible by 4 then the Hypervisor sends the remaining bytes (at most 3) as a word padded with 0s.
- After the packet body was written, the Hypervisor calculates checksum of the whole packet and writes it at the address where the last word of packet body was written + 4.
uint32_t cksum = 0; for (i = 0; i < packet_size; i++) cksum -= packet[i]; cksum = cksum & 0xffff;
- After the packet checksum was written, the Hypervisor reads the value at offset 0x2400008DFF0, modifies it and stores back:
value = value + 1; value &= 0xffff; value = (value << 16) | value;
- To notify the SYSCON about the new packet, the Hypervisor writes 0x1 to address 0x2400008E100.
Receiving Packets
- The Hypervisor installs an interrupt handler for the SYSCON.
- First, the Hypervisor reads a word from address 0x2400008E000, ors it with 0xFFFFFFFD and writes the value back.
- Then, the Hypervisor reads a word from address 0x2400008E004 and tests if bit 0x2 is set or not. The bit 0x2 should be not 0 or else the Hypervisor panics.
- After that, the Hypervisor reads a word at address 0x2400008CFF0 and 0x2400008DFF4. If there is a new packet pending from SYSCON, then the (value + 1) at 0x2400008CFF0 should be equal the value at 0x2400008DFF4.
- The Hypervisor reads the header of the packet beginning at the address 0x2400008C000.
- The header is read with 4 word transfers by the Hypervisor.
- The byte at offset 1 in the packet header must be 1 or else the Hypervisor discards the packet as invalid.
- The Hypervisor calculates the checksum of the packet header and checks it with the checksum stored in the header. If they don't match then the Hypervisor discards the packet.
- The Hypervisor reads the body of the packet beginning at the address 0x2400008C010.
- The header and the body of the received packet can be read as many times as you want !!! They remain until next SYSCON packet is received
which gives us the possibility to communicate with SYSCON on Linux easily :)
Test
1. Before sending SYSCON packet:
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C 00000000 01 18 01 18 |....| 00000004 root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C 00000000 01 18 01 18 |....| 00000004 root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C 00000000 01 24 01 24 |.$.$| 00000004 root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C 00000000 01 24 01 24 |.$.$| 00000004
2. SYSCON packet was sent by using ps3dm_scm read_eprom.
3. After sending SYSCON packet:
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C 00000000 01 19 01 19 |....| 00000004 root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C 00000000 01 19 01 19 |....| 00000004 root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C 00000000 01 25 01 25 |.%.%| 00000004 root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C 00000000 01 25 01 25 |.%.%| 00000004
4. Received Header
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=16 skip=$((0x8c000)) status=noxfer | hexdump -C 00000000 14 01 00 00 00 00 80 15 00 00 00 03 00 05 00 05 |................| 00000010
5. Received Body
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=8 skip=$((0x8c010)) status=noxfer | hexdump -C 00000000 00 00 c7 01 ff 00 00 00 |..Ç.ÿ...| 00000008
Examples
Get RTC
- Used by LV1 call lv1_get_rtc
- Communication with SYSCON 4
Request:
# write packet # echo "0: 13 01 0000 0000 8014 00000004 0001 0001 33 00 00 00 0000ff1f" | xxd -c256 -r | \ dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer # dump packet counter # dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C 00000000 00 c0 00 c0 |.À.À| 00000004 # increment packet counter echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer # kick packet # echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer
Response:
# dump packet counter # dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C 00000000 00 c1 00 c1 |.Á.Á| 00000004 # dump response packet # dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C 00000000 13 01 00 00 00 00 80 14 00 00 00 04 00 08 00 08 |................| 00000010 00 00 00 00 15 af 47 6b |.....¯Gk| 00000018
Ring Buzzer
- Used by System Manager
- Communication with SYSCON 1
Request:
# write packet # echo "0: 16 01 1620 0000 804d 00000001 0008 0008 20 29 0a 00 000001b6 0000fdcb" | xxd -c256 -r | \ dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer # dump packet counter # dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C 00000000 00 c0 00 c0 |.À.À| 00000004 # increment packet counter echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer # kick packet # echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer # you should hear a beep
Response:
# dump packet counter # dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C 00000000 00 c1 00 c1 |.Á.Á| 00000004 # dump response packet # dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C 00000000 16 01 16 20 00 00 80 4d 00 00 00 01 00 01 00 01 |... ...M........| 00000010 00 00 00 00 00 00 fe e3 |......þã| 00000018
Isolation
Crossreference: gitbrew.org::Isolation
Running Isolated SPE Modules On OtherOS++ Linux
- spp_verifier is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux.
- It decrypts default.spp profile
- Tested on 3.41 and 3.55.
- You can modify it easily to run other SPE modules.
root@debian-hdd:/home/glevand/spp_verifier# cat spp_verifier_355.self > /proc/spp_verifier/spu root@debian-hdd:/home/glevand/spp_verifier# cat default_355.spp > /proc/spp_verifier/profile root@debian-hdd:/home/glevand/spp_verifier# echo 1 > /proc/spp_verifier/run root@debian-hdd:/home/glevand/spp_verifier# cat /proc/spp_verifier/debug PPE id (0x0000000000000001) VAS id (0x0000000000000002) lv1_construct_logical_spe (0x00000000) SPE id (0x000000000000002b) lv1_undocumented_function_209 (0x00000000) shadow execution status (0x0000000000000002) lv1_get_spe_interrupt_status(1) (0x00000000) interrupt status 1 (0x0000000000000000) sleep shadow execution status (0x0000000000000002) lv1_get_spe_interrupt_status(1) (0x00000000) interrupt status 1 (0x0000000000000001) ea (0xc000000002920000) esid (0xc000000008000000) vsid (0x0000408f92c94500) lv1_undocumented_function_62 (0x00000000) lv1_clear_spe_interrupt_status(1) (0x00000000) lv1_undocumented_function_168 (0x00000000) sleep shadow execution status (0x0000000000000007) lv1_get_spe_interrupt_status(1) (0x00000000) interrupt status 1 (0x0000000000000000) lv1_get_spe_interrupt_status(2) (0x00000000) interrupt status 2 (0x0000000000000000) out interrupt mbox (0x0000000000000002) out interrupt mbox (0x0000000000000002) lv1_undocumented_function_167 (0x00000000) lv1_clear_spe_interrupt_status (0x00000000) lv1_undocumented_function_200 (0x00000000) sleep shadow execution status (0x000000000000000b) lv1_get_spe_interrupt_status(1) (0x00000000) interrupt status 1 (0x0000000000000000) shadow execution status (0x000000000000000b) problem status (0x01000082) lv1_destruct_logical_spe (0x00000000) root@debian-hdd:/home/glevand/spp_verifier# hexdump -C /proc/spp_verifier/profile | less ... ... 00000200 00 02 00 05 00 00 20 a0 00 00 00 01 00 03 00 00 |...... ........| 00000210 00 00 00 00 00 00 00 01 00 00 00 0e 00 00 00 00 |................| 00000220 00 00 02 88 00 00 00 01 10 70 00 00 01 00 00 01 |.........p......| 00000230 00 00 00 00 00 00 00 00 53 43 45 5f 43 45 4c 4c |........SCE_CELL| 00000240 4f 53 5f 50 4d 45 00 00 00 00 00 00 00 00 00 00 |OS_PME..........| 00000250 00 00 00 00 00 00 00 00 00 00 00 06 00 00 02 50 |...............P| 00000260 10 70 00 00 01 00 00 01 2f 66 6c 68 2f 6f 73 2f |.p....../flh/os/| 00000270 74 68 69 73 5f 69 73 5f 64 75 6d 6d 79 00 00 00 |this_is_dummy...| 00000280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| ... ...
Using metldr On OtherOS++ Linux
- spp_verifier_direct is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux by using metldr directly.
- It decrypts default.spp profile.
- Tested on 3.41 and 3.55.
- You can modify it easily to run other SPE modules.
root@debian-hdd:/home/glevand/spp_verifier_direct# insmod ./spp_verifier_direct.ko root@debian-hdd:/home/glevand/spp_verifier_direct# cat metldr > /proc/spp_verifier_direct/metldr root@debian-hdd:/home/glevand/spp_verifier_direct# cat isoldr_355 > /proc/spp_verifier_direct/isoldr root@debian-hdd:/home/glevand/spp_verifier_direct# cat RL_FOR_PROGRAM_355.img > /proc/spp_verifier_direct/rvkprg root@debian-hdd:/home/glevand/spp_verifier_direct# cat EID0 > /proc/spp_verifier_direct/eid0 root@debian-hdd:/home/glevand/spp_verifier_direct# cat spp_verifier_355.self > /proc/spp_verifier_direct/spu root@debian-hdd:/home/glevand/spp_verifier_direct# cat default_355.spp > /proc/spp_verifier_direct/profile root@debian-hdd:/home/glevand/spp_verifier_direct# echo 1 > /proc/spp_verifier_direct/run root@debian-hdd:/home/glevand/spp_verifier_direct# cat /proc/spp_verifier_direct/debug PPE id (0x0000000000000001) VAS id (0x0000000000000002) lv1_construct_logical_spe (0x00000000) SPE id (0x0000000000000033) lv1_enable_logical_spe (0x00000000) lv1_set_spe_interrupt_mask(0) (0x00000000) lv1_set_spe_interrupt_mask(1) (0x00000000) lv1_set_spe_interrupt_mask(2) (0x00000000) lv1_set_spe_privilege_state_area_1_register (0x00000000) ea (0xc000000002680000) esid (0xc000000008000000) vsid (0x0000408f92c94500) lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) sleep lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) out interrupt mbox (0x0000000000000001) lv1_clear_spe_interrupt_status(2) (0x00000000) transferring EID0, ldr args and revoke list to LS waiting until MFC transfers are finished MFC transfers done out mbox (0x00000001) sleep lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) out interrupt mbox (0x0000000000000002) lv1_clear_spe_interrupt_status(2) (0x00000000) out mbox (0x00000002) lv1_clear_spe_interrupt_status(2) (0x00000000) sleep lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) problem status (0x01000082) lv1_destruct_logical_spe (0x00000000) root@debian-hdd:/home/glevand/spp_verifier_direct# hexdump -C /proc/spp_verifier_direct/profile | less ... ... 00000200 00 02 00 05 00 00 20 a0 00 00 00 01 00 03 00 00 |...... ........| 00000210 00 00 00 00 00 00 00 01 00 00 00 0e 00 00 00 00 |................| 00000220 00 00 02 88 00 00 00 01 10 70 00 00 01 00 00 01 |.........p......| 00000230 00 00 00 00 00 00 00 00 53 43 45 5f 43 45 4c 4c |........SCE_CELL| 00000240 4f 53 5f 50 4d 45 00 00 00 00 00 00 00 00 00 00 |OS_PME..........| 00000250 00 00 00 00 00 00 00 00 00 00 00 06 00 00 02 50 |...............P| 00000260 10 70 00 00 01 00 00 01 2f 66 6c 68 2f 6f 73 2f |.p....../flh/os/| 00000270 74 68 69 73 5f 69 73 5f 64 75 6d 6d 79 00 00 00 |this_is_dummy...| 00000280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| ... ...
Gelic Device
Crossreference: gitbrew.org::Gelic Device
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.
Note:old vs. new: Old == CECHA up to CECHK, New == CECHL and later
Control Interface
HV calls 195 and 196 are used by GameOS to send commands to Gelic device directly.
lv1_undocumented_function_196
Parameters
r3 - LPAR address of data buffer
r4 - size of data buffer
r5 - must be 0
lv1_undocumented_function_195
Parameters
r3 - command (16 bit value)
r4 - command data size
r5 - must be 0
Data Buffer
- Data Buffer passed to HV call 196 is divided into 2 parts.
- The first 0x800 bytes are for sending and receiving command data
- The remaining 0x800 bytes are for event notification.
Command Data Buffer
- Every command data sent to Gelic device contains header of size 0xC
- After the header follows the command data
- After the Gelic device processed the command, it notifies LV2 kernel about command completion by sending an interrupt
Header
- Size is 0xc.
- Byte order is little-endian.
- Header data in a request command buffer is always all 0s.
0x0 - command = request command + 1 (2 bytes)
0x4 - result, 0x1 - success ??? 0x2 - buffer too small ??? (2 bytes)
0x6 - body size (2 bytes)
Event Data Buffer
- The Gelic device notifies LV2 kernel by sending an interrupt when new events are available
- Event Data Buffer has 8 bytes header
- The remaining bytes are divided into event slots
- Each event slot is of size 64 bytes
- Events are in little-endian format
Header
offset 0x0 - GET index (4 bytes)
offset 0x4 - PUT index (4 bytes)
- GET index is updated by Gelic driver. The Gelic driver reads events beginning with the event slot at index GET.
- PUT index is the index of event entry where next Gelic event will be stored by the Gelic device.
- If GET index is equal to PUT index then there are no Gelic events.
GameOS
- 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
Commands
Unknown (0x1)
- Used by VSH.
- Command buffer size is 0x10.
- Used in AP mode.
- Enables AP mode ???
Get AP SSID (0x3)
- Command buffer is of size 0x30.
- Returns SSID in AP mode.
offset 0xC - SSID (32 bytes)
Set AP SSID (0x5)
- Used by VSH.
- Command buffer is of size 0x30.
- Sets SSID in AP mode.
offset 0xC - SSID (32 bytes)
Get Channel (0xf)
- Used by VSH.
- Command buffer is of size 0x31.
- Data is returned from the device.
- Returns list of channels and active channel.
offset 0x2F - active channel (2 bytes)
Set Channel (0x11)
- Used by VSH.
- Command buffer size is 0xd
- Valid channels: 0 - 13. 0 means that the channel is selected automatically.
offset 0xC - channel (1 byte)
Unknown (0x27)
- Command buffer size is 0xF.
Set Antenna (0x29)
- Command buffer size is 0xe
offset 0xC - 0,1 or 2 (1 byte)
offset 0xD - 2 (1 byte)
Set AP WEP Configuration (0x5b)
- Used by VSH.
- Command buffer is of size 0x56.
- Sets WEP security type and WEP key.
- Security types: 0 - none, 1 - wep64, 2 - wep128
offset 0xE - security mode: 0 - none, 1 - wep64, 2 - wep128 (1 byte)
offset 0x10 - WEP key (64 bytes)
Unknown (0x61)
- Used by VSH.
- Command buffer size is 0xd
Unknown (0x65)
- Used by VSH.
- Command uffer size is 0xd.
- Used in AP mode.
Get Eurus Firmware Version (0x99)
- Used by VSH.
Here is the response on my PS3 Slim:
00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2| 00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19| 00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).| 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |.............. |
Get AP Operating Mode (0xb7)
- Used by VSH.
- Command buffer size is 0x10
- Returns AP operating mode (mixed, 11b or 11g).
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)
Set AP Operating Mode (0xb9)
- Used by VSH.
- Command buffer size is 0x10
- Sets AP operating mode (mixed, 11b or 11g).
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)
Unknown (0xc5)
- Used by VSH.
- Command buffer size is 0x10.
- Used in AP mode.
offset 0xC - ??? (4 bytes)
Set AP WPA AKM Suite (0xc9)
- Used by VSH.
- Command buffer size is 0x11.
- Sets WPA AKM suite in AP mode.
offset 0xC - AKM suite (4 bytes)
Set AP WPA Group Cipher Suite (0xcf)
- Used by VSH.
- Command buffer size is 0x10
- Used in AP + WPA mode.
offset 0xC - group cipher suite: group (4 bytes)
Set AP WPA PSK Binary (0xd3)
- Used by VSH.
- Command buffer size is 0x4c
- Sets WPA PSK binary
offset 0xC - PSK (64 bytes)
Set AP WPA Reauthentication Timeout (0xd5)
- Used by VSH.
- Command buffer size is 0x10
- Sets WPA Reauth timeout value in AP WPA mode.
- VSH uses 36000 as timeout.
offset 0xC - timeout value in seconds (2 bytes)
Unknown (0x127)
- Used by VSH.
- Command buffer size is 0x10.
- Used in AP + WPA mode.
Unknown (0x12b)
- Used by VSH.
- Command buffer size is 0x10.
- Used in AP + WPA mode.
Set AP WPA PSK Passphrase (0x17d)
- Used by VSH.
- Command buffer size is 0x2D
offset 0xD - passphrase (32 bytes)
Set AP WPA Pairwise Cipher Suite (0x1bf)
- Used by VSH.
- Command buffer size is 0x11
- Used in AP + WPA mode.
offset 0xC - pairwise cipher suite (4 bytes)
offset 0x10 - ??? (1 byte)
Unknown (0x1d9)
- Used by VSH.
- Command buffer size is 0x10
Unknown (0x1dd)
- Used by VSH.
- Command buffer size is 0xd
Unknown (0x1ed)
- Used by VSH.
- Command buffer is of size 0x17.
- Rate control ???
Get Eurus HW Revision (0x1fb)
- Command buffer size is 0x10.
Associate (0x1001)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0xd
- Data passed to Gelic device is all 0s
Get Common Configuration (0x1003)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x18
- Data passed to Gelic device is all 0s
Set Common Configuration (0x1005)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x18
- Hmm, VSH always removes QOS bit from capability, that means Jupiter doesn't support QOS ???
offset 0xC - BSS type: 0 - infrastructure, 1 - ???, 2 - adhoc (1 byte)
offset 0xD - authentication mode: 0 - open, 1 - shared key
offset 0xE - opmode: 0 - 11bg, 1 - 11b, 2 - 11g (1 byte)
offset 0xF - ??? (1 byte)
offset 0x10 - BSSID (6 bytes)
offset 0x16 - capability (2 bytes)
Get WEP Configuration (0x1013)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x50
- Data passed to Gelic device is all 0s
Set WEP Configuration (0x1015)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x50
Get WPA Configuration (0x1017)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x5b
- Data passed to Gelic device is all 0s
Set WPA Configuration (0x1019)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x5b
offset 0xE - security type: 0 - WPA, 1 - RSNA (1 byte)
offset 0xF - psk type: 0 - hex, 1 - bin (1 byte)
offset 0x10 - psk key: hex or bin (64 bytes)
offset 0x50 - group cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
offset 0x54 - pairwise cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
offset 0x58 - AKM suite: 0x0050f202 - WPA PSK, 0x000fac02 - RSNA PSK (4 bytes)
See IEEE 802.11 specification for more details about cipher/AKM suites
802.11 spec: [1]
Unknown (0x1025)
- Used by VSH.
- Command buffer size is 0x10.
- Sets preamble type, something else ???
offset 0xC - preamble mode: 0 - short, 1 - long (1 byte)
Unknown (0x1031)
- Used by VSH.
- Command buffer size is 0xe
Get Scan Results (0x1033)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x5b0
- Data passed to Gelic device is all 0s
Scan Results
offset 0x0 - number of scan entries (1 byte)
offset 0x1 - array of scan entries
Scan Entry
offset 0x0 - size of this entry in bytes, this field is NOT included (2 bytes)
offset 0x2 - BSSID (6 bytes)
offset 0x8 - RSSI (1 byte)
offset 0x9 - timestamp (8 bytes)
offset 0x11 - beacon period (2 bytes)
offset 0x13 - capability (2 bytes)
offset 0x15 - information elements (see 802.11 specification)
Start Scan (0x1035)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size depends on size of channel list and ESSID string length
- Data passed to Gelic device contains channel list and ESSID string
- First 0x16 bytes in command data buffer are all 0s, then follows the channel list and after that ESSID
Diassociate (0x1037)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0xd
- Data passed to Gelic device is all 0s
Get RSSI (0x103d)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x17
offset 0x10 - MAC address of node (6 bytes)
offset 0x16 - RSSI (1 byte)
Get MAC Address (0x103f)
- Command buffer size is 0x13
offset 0xD - MAC address (6 bytes)
Set MAC Address (0x1041)
- Used by VSH.
- Used by LV1 too.
- Command buffer size is 0x12
Unknown (0x104d)
- Used by VSH.
- Command buffer size is 0xd.
offset 0xC - 0 - ???, 1 - ??? (1 byte)
Unknown (0x104f)
- Command buffer size is 0xd.
- Returns 1 byte.
offset 0xC - 0 - ???, 1 - ??? (1 byte)
Unknown (0x1051)
- Used by VSH.
- Command buffer size is 0x5b3.
- Returns 0x5a7 bytes.
offset 0xC - number of entries
offset 0x10 - entries (each entry is 0xd bytes)
Unknown (0x1053)
- Used by VSH.
- Command buffer size is 0x70.
offset 0xC - ??? (4 bytes)
offset 0x10 - MAC address (6 bytes)
Unknown (0x1059)
- Used by VSH.
- Command buffer size is 0x2a8.
Unknown (0x105f)
- Used by LV2.
Get Zephyr HW Revision (0x1101)
- Used by VSH.
- Not a Gelic device command, handled by LV2 kernel.
- LV2 uses LV1 call lv1_net_control(0x8000000000000002)
- Command buffer size is 0x18.
Get MAC Address List (0x1117)
- Command buffer size is 0xce.
- Returns several MAC addresses.
offset 0xC - number of MAC addresses (2 bytes)
offset 0xE - MAC addresses (6 * number of MAC addresses)
Unknown (0x1133)
- Used by VSH.
- Command buffer size is 0x1A.
Set WOL MAC Address Filter (0x1139)
- Used by LV2 internally.
- Command buffer is of size 0x28.
Unknown (0x113b)
- Used by LV2 internally.
- Command buffer size is 0x20.
Set WOL Multicast Address Filter (0x113d)
- Used by LV2 internally.
- Command buffer is of size 0x2c.
Clear WOL Multicast Address Filter (0x113f)
- Used by LV2 internally.
- Command buffer is of size 0x28.
Unknown (0x1141)
- Used by LV2 internally.
- Command buffer is of size 0x12.
Clear WOL Address Filter (0x1143)
- Used by LV2 internally.
- Command buffer size is 0x2c.
Unknown (0x114b)
- Used by LV2 internally.
Set WOL Magic Packet Mode (0x1155)
- Used by LV2 internally.
- Command buffer is of size 0x10.
- Enables/Disables WOL magic packet.
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
Unknown (0x1157)
- Used by LV2 internally.
- Command buffer size is 0x10.
Set WOL Multicast Address Filter Mode (0x1159)
- Used by LV2 internally.
- Command buffer size is 0x10.
- WOL function
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
Set Unicast Address Filter (0x115b)
- Used by LV2 internally.
- Command buffer is of size 0x6a.
- This command should be used to set proper MAC address or else device won't be able to receive packets destined to its own MAC address
offset 0xC - ??? (2 bytes)
offset 0xE - ??? (2 bytes)
offset 0x10 - MAC address (6 bytes)
Clear Unicast Address Filter (0x115d)
- Used by LV2 internally.
- Command buffer size is 0x6a.
Get Unicast Address Filter (0x115f)
- Used by LV2 internally.
- Command buffer is of size 0x6a.
Set Multicast Address Filter (0x1161)
- Used by LV2 internally.
- Command buffer size is 0x2c.
Clear Multicast Address Filter (0x1163)
- Used by LV2 internally.
- Command buffer size is 0x2c
- To clear all multicast addresses send command with all 0s.
offset 0xC - multicast address filter (4 * 8 bytes)
Get Multicast Address Filter (0x1165)
- Used by LV2 internally.
- Command buffer is of size 0x2c.
Set WOL Address Filter (0x1167)
- Used by LV2 internally.
- Command buffer size is 0x70.
Set WOL Address Filter Mode (0x116d)
- Used by LV2 internally.
- Command buffer size is 0x10.
- Enables/Disables WOL address matching
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
Set Unicast Address Filter Mode (0x116f)
- Used by LV2 internally.
- Command buffer size is 0x10.
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
Get Device Status (0xfffb)
- Used by VSH.
- Not a Gelic device command, handled by LV2 kernel.
- Returned data size in command buffer is 0x10.
Unknown (0xfffc)
- Used by VSH.
- Not a Gelic device command, handled by LV2 kernel.
- LV2 uses LV1 call lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x4, 0x0, 0x0)
Get Channel Information (0xfffd)
- Used by VSH.
- Not a Gelic device command, handled by LV2 kernel.
- LV2 uses LV1 call lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x0, 0x0, 0x0)
- Returns supported WLAN channels
Set Response Timeout (0xfffe)
- Used by VSH.
- Not a Gelic device command, handled by LV2 kernel.
- Sets timeout value which is used to wait for a response from Gelic device.
- Typical value used by VSH is 0x989680.
- Command buffer size is 0x14.
Unknown (0xffff)
- Used by VSH.
- Not a Gelic device command, handled by LV2 kernel.
- Returns 0x10 bytes in command buffer.
- Returns gelic device state ???
Events
struct ps3_eurus_event_hdr { __le32 type; __le32 id; __le32 timestamp; __le32 payload_length; __le32 unknown; } __packed; struct ps3_eurus_event { struct ps3_eurus_event_hdr hdr; u8 payload[44]; } __packed;
Event Type 0x00000040
Id | Description |
---|---|
0x00000001 | Deauthenticated |
Event Type 0x00000080
Id | Description |
---|---|
0x00000001 | Beacon Lost |
0x00000002 | Connected |
0x00000004 | Scan Completed |
0x00000020 | WPA Connected |
0x00000040 | WPA Error (MIC Error) |
Event Type 0x80000000
Id | Description |
---|---|
0x00000001 | Device Ready |
Enabling WLAN Gelic On FAT
Linux kernel doesn't use Gelic Device Control Interface like GameOS does it. To get WLAN working on Linux booted with GameOS rights, we have to disable Gelic Device Control Interface first because it's enabled for GameOS by default.
The value of repository node "ios.net.eurus.lpar" controls access to Gelic Device Control Interface. It's a bitmap. The position of a bit corresponds to LPAR id. During GameOS booting, HV process 9 (System Manager) sets bit at postion 2 to 1 which means enable Gelic Device Control Interface for LPAR 2.
To disable Gelic Device Control Interface on Linux, first unload Gelic device driver, then set value of repository node "ios.net.eurus.lpar" to 0 and load Gelic device driver again. After that WLAN should work again but only on FATs.
For PS3 Slim we need a new Linux Gelic device driver which uses Gelic Device Control Interface directly.
USB WLAN Interface (Codename Jupiter 2)
- On new PS3 models, WLAN interface is USB.
- Good news is that the same commands are used as with LV1 calls 196 and 195.
- There are 2 wireless devices: Station and AP.
- I got WLAN scan working.
Endpoints
- LV2 uses 3 USB endpoints of interface 3,4 and 5 to communicate with WLAN.
- Endpoints EP5 IN/OUT, EP6 IN/OUT and EP7 IN/OUT.
- WLAN commands are sent to endpoint EP5 OUT with interrupt transfers.
- WLAN events and WLAN command responses are received on endpoint EP5 IN with interrupt transfers.
- LV2 opens a USB communication pipe to endpoint EP5 IN and EP5 OUT.
- In my LV2 3.55 dump, pipe to EP5 IN has id 0x2 and pipe to EP5 OUT has id 0x3. Array of all opened USB pipes is at address 0x80000000004bd000 in my LV2 3.55 dump.
- EP5 is used to send commands to Jupiter and receive events from it.
- EP6 is used to send/receive data packets to/from the 1st WLAN device.
- EP7 is used to send/receive data packets to/from the 2nd WLAN device.
- lsusb is buggy on big-endian arch and shows some fields with bytes swapped !!!
Bus 002 Device 002: ID 054c:036f Sony Corp. Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 224 Wireless bDeviceSubClass 1 Radio Frequency bDeviceProtocol 1 Bluetooth bMaxPacketSize0 64 idVendor 0x054c Sony Corp. idProduct 0x036f bcdDevice 20.12 iManufacturer 1 iProduct 2 iSerial 0 bNumConfigurations 1 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 3 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 2 bInterfaceProtocol 1 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x85 EP 5 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x4000 1x 0 bytes bInterval 1 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x05 EP 5 OUT bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x4000 1x 0 bytes bInterval 1 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 4 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 2 bInterfaceProtocol 2 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x86 EP 6 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0002 1x 2 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x06 EP 6 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0002 1x 2 bytes bInterval 255 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 5 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 2 bInterfaceProtocol 3 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x87 EP 7 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0002 1x 2 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x07 EP 7 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0002 1x 2 bytes bInterval 255
Device Initialization
- LV2 does 2 control transfers to EP0 during WLAN initialization
- First control transfer sends magic 0x20 data to device as CLEAR_FEATURE request.
- Second control transfer reads 0x2 bytes device status. On my PS3 slim, the status data is always 0x2031 if you send the right magic.
- Magic data sent in first control transfer is stored in LV2.
- If you send wrong magic, the first control transfer will fail !!!
- LV2 uses a state machine to initialize the Jupiter device. The state machine has 17 states.
Magic Data in Control Transfer
unsigned char ps3_usb_wlan_magic_data[] = { 0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8, 0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d, };
Initialization State Machine
- Implemented in LV2.
State 1
- Command 0x114f is sent to WLAN device.
State 2
- Command 0x1171 is sent to WLAN device.
State 3
- LV2 waits for an event from WLAN device.
State 4
- Command 0x116f is sent to WLAN device.
State 5
- Command 0x115b is sent to WLAN device.
- Command data sent to WLAN device contains MAC address.
State 6
- Command 0x1161 is sent to WLAN device.
- Sets multicast address filter.
State 7
- Command 0x110d is sent to WLAN device.
State 8
- Command 0x1031 is sent to WLAN device.
State 9
- Command 0x1041 is sent to WLAN device.
- Command data sent to WLAN device contains MAC address.
State 10
- Command 0x29 is sent to WLAN device.
- Sets antenna.
State 11
- Command 0x110b is sent to WLAN device.
State 12
- Command 0x1109 is sent to WLAN device.
State 13
- Command 0x207 is sent to WLAN device.
State 14
- Command 0x203 is sent to WLAN device.
State 15
- Command 0x105f is sent to WLAN device.
- Command data sent to WLAN device contains MAC address, channel info and region code.
State 16
- LV2 waits for an event from WLAN device.
State 17
- LV2 accepts commands sent by LV2 syscall 726.
Test Program
- Here is a small program which executes a WLAN scan.
- I used libusb.
Source Code
/* * PS3 USB WLAN * * Copyright (C) 2011 glevand ([email protected]) * All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published * by the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <stdint.h> #include <unistd.h> #include <pthread.h> #include <libusb-1.0/libusb.h> #define USB_VENDOR_ID 0x054c /* $ONY */ #define USB_PRODUCT_ID 0x036f #define USB_IFACE_NUMBER 3 #define USB_INTR_TRANSFER_EP5_IN_BUF_SIZE 0x800 #define USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE 0x800 struct wlan_cmd_pkt_hdr { uint8_t unknown1; uint8_t unknown2; uint8_t unknown3; uint8_t unknown4; uint16_t unknown5; uint8_t res1[2]; uint16_t tag; uint8_t res2[14]; } __attribute__ ((packed)); struct wlan_cmd_hdr { uint16_t command; uint16_t tag; uint16_t status; uint16_t payload_size; uint8_t res[4]; } __attribute__ ((packed)); struct wlan_event_pkt_hdr { uint8_t unknown1; uint8_t unknown2; uint8_t unknown3; uint8_t event_count; } __attribute__ ((packed)); static libusb_context *usb_ctx; static libusb_device_handle *usb_dev_handle; static struct libusb_transfer *usb_intr_transfer_ep5_in; static unsigned char usb_intr_transfer_ep5_in_buf[USB_INTR_TRANSFER_EP5_IN_BUF_SIZE]; static unsigned char usb_intr_transfer_ep5_out_buf[USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE]; static pthread_mutex_t usb_wlan_cmd_mutex; static pthread_cond_t usb_wlan_cmd_cond; static int volatile usb_wlan_cmd_busy; static uint16_t usb_wlan_cmd; static void *usb_wlan_cmd_data; static int volatile usb_wlan_cmd_thread_done; /* * WLAN won't work without this magic !!! */ static unsigned char usb_magic_data[] = { 0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8, 0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d, }; static unsigned char my_mac_addr[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, }; /* * hexdump */ static void hexdump(const unsigned char *data, unsigned int data_size) { int i, j; for (i = 0; i < data_size; i += 16) { fprintf(stdout, "%08x:", i); for (j = 0; j < 16; j++) { if (i + j < data_size) { fprintf(stdout, " %02x", data[i + j]); } else { fprintf(stdout, " "); } } fprintf(stdout, " |"); for (j = 0; j < 16; j++) { if (i + j < data_size) { if (isprint(data[i + j])) fprintf(stdout, "%c", data[i + j]); else fprintf(stdout, "."); } else { fprintf(stdout, " "); } } fprintf(stdout, "|\n"); } } /* * usb_handle_wlan_event */ static void usb_handle_wlan_event(struct wlan_event_pkt_hdr *wlan_event_pkt_hdr) { fprintf(stdout, "%s:%d: === got WLAN event ===\n", __func__, __LINE__); /* fprintf(stdout, "%s:%d: event packet header:\n", __func__, __LINE__); fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__, wlan_event_pkt_hdr->unknown1); fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__, wlan_event_pkt_hdr->unknown2); fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__, wlan_event_pkt_hdr->unknown3); */ fprintf(stdout, "%s:%d: event_count (0x%02x)\n", __func__, __LINE__, wlan_event_pkt_hdr->event_count); hexdump((unsigned char *) (wlan_event_pkt_hdr + 1), wlan_event_pkt_hdr->event_count * 64); } /* * usb_handle_wlan_cmd_response */ static void usb_handle_wlan_cmd_response(struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr) { struct wlan_cmd_hdr *wlan_cmd_hdr; uint8_t *wlan_cmd_payload; fprintf(stdout, "%s:%d: === got WLAN command response ===\n", __func__, __LINE__); wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1); wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1); /* convert all header fields to big-endian byte order !!! */ wlan_cmd_pkt_hdr->unknown5 = le16toh(wlan_cmd_pkt_hdr->unknown5); wlan_cmd_pkt_hdr->tag = le16toh(wlan_cmd_pkt_hdr->tag); /* returned from request */ wlan_cmd_hdr->command = le16toh(wlan_cmd_hdr->command); /* request command + 1 */ wlan_cmd_hdr->tag = le16toh(wlan_cmd_hdr->tag); /* returned from request */ wlan_cmd_hdr->status = le16toh(wlan_cmd_hdr->status); /* 1 - success 2 - invalid parameters ??? 3 - invalid command ??? */ wlan_cmd_hdr->payload_size = le16toh(wlan_cmd_hdr->payload_size); /* length of data that follows the header */ /* fprintf(stdout, "%s:%d: command packet header:\n", __func__, __LINE__); fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__, wlan_cmd_pkt_hdr->unknown1); fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__, wlan_cmd_pkt_hdr->unknown2); fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__, wlan_cmd_pkt_hdr->unknown3); fprintf(stdout, "%s:%d: unknown4 (0x%02x)\n", __func__, __LINE__, wlan_cmd_pkt_hdr->unknown4); fprintf(stdout, "%s:%d: unknown5 (0x%04x)\n", __func__, __LINE__, wlan_cmd_pkt_hdr->unknown5); fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__, wlan_cmd_pkt_hdr->tag); */ fprintf(stdout, "%s:%d: command header:\n", __func__, __LINE__); fprintf(stdout, "%s:%d: command (0x%04x)\n", __func__, __LINE__, wlan_cmd_hdr->command); if ((usb_wlan_cmd + 1) != wlan_cmd_hdr->command) fprintf(stdout, "%s:%d: ==> command does not match, got (0x%04x) expected (0x%04x)\n", __func__, __LINE__, wlan_cmd_hdr->command, usb_wlan_cmd + 1); fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__, wlan_cmd_hdr->tag); fprintf(stdout, "%s:%d: status (0x%04x)\n", __func__, __LINE__, wlan_cmd_hdr->status); if (wlan_cmd_hdr->status != 0x1) fprintf(stdout, "%s:%d: ==> command status != 0x1\n", __func__, __LINE__); fprintf(stdout, "%s:%d: payload_size (0x%04x)\n", __func__, __LINE__, wlan_cmd_hdr->payload_size); fprintf(stdout, "%s:%d: command payload:\n", __func__, __LINE__); hexdump(wlan_cmd_payload, wlan_cmd_hdr->payload_size); memcpy(usb_wlan_cmd_data, wlan_cmd_payload, wlan_cmd_hdr->payload_size); pthread_mutex_lock(&usb_wlan_cmd_mutex); usb_wlan_cmd_busy = 0; pthread_cond_signal(&usb_wlan_cmd_cond); pthread_mutex_unlock(&usb_wlan_cmd_mutex); } /* * usb_intr_transfer_ep5_in_cb */ static void usb_intr_transfer_ep5_in_cb(struct libusb_transfer *transfer) { struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr; int error; fprintf(stdout, "%s:%d: === got interrupt transfer ===\n", __func__, __LINE__); fprintf(stdout, "%s:%d: transfer status (%d) length (%d)\n", __func__, __LINE__, transfer->status, transfer->actual_length); wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) transfer->buffer; if (wlan_cmd_pkt_hdr->unknown3 == 0x6) usb_handle_wlan_cmd_response(wlan_cmd_pkt_hdr); else if (wlan_cmd_pkt_hdr->unknown3 == 0x8) usb_handle_wlan_event((struct wlan_event_pkt_hdr *) transfer->buffer); else fprintf(stdout, "%s:%d: got unknown packet (0x%02x)\n", __func__, __LINE__, wlan_cmd_pkt_hdr->unknown3); memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf)); libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5, usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf), usb_intr_transfer_ep5_in_cb, NULL, 0); error = libusb_submit_transfer(usb_intr_transfer_ep5_in); if (error) { fprintf(stderr, "%s:%d: could not submit transfer (%d)\n", __func__, __LINE__, error); exit(1); } } /* * usb_intr_transfer_ep5_out_cb */ static void usb_intr_transfer_ep5_out_cb(struct libusb_transfer *transfer) { /* fprintf(stdout, "%s:%d: sent interrupt transfer\n", __func__, __LINE__); fprintf(stdout, "%s:%d: transfer status (%d)\n", __func__, __LINE__, transfer->status); */ libusb_free_transfer(transfer); } /* * usb_wlan_cmd_send */ static int usb_wlan_cmd_send(uint16_t command, const uint8_t *data, unsigned int data_size) { struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr; struct wlan_cmd_hdr *wlan_cmd_hdr; uint8_t *wlan_cmd_payload; struct libusb_transfer *transfer; int error; fprintf(stdout, "%s:%d: sending command (0x%04x) data size (0x%04x) command size (0x%04x)\n", __func__, __LINE__, command, data_size, data_size + sizeof(struct wlan_cmd_hdr)); transfer = libusb_alloc_transfer(0); if (!transfer) { fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__); error = -1; goto fail; } wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) usb_intr_transfer_ep5_out_buf; wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1); wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1); wlan_cmd_pkt_hdr->unknown1 = 0x1; wlan_cmd_pkt_hdr->unknown2 = 0x1; wlan_cmd_pkt_hdr->unknown3 = 0x6; wlan_cmd_pkt_hdr->unknown4 = 0x0; wlan_cmd_pkt_hdr->unknown5 = 0x1; wlan_cmd_pkt_hdr->tag = 0xf00d; /* returned in response */ wlan_cmd_hdr->command = command; wlan_cmd_hdr->tag = 0xcafe; /* returned in response */ wlan_cmd_hdr->status = 0xa; wlan_cmd_hdr->payload_size = data_size; memcpy(wlan_cmd_payload, data, data_size); usb_wlan_cmd = command; usb_wlan_cmd_data = (void *) data; libusb_fill_interrupt_transfer(transfer, usb_dev_handle, LIBUSB_ENDPOINT_OUT | 0x5, usb_intr_transfer_ep5_out_buf, sizeof(struct wlan_cmd_pkt_hdr) + sizeof(struct wlan_cmd_hdr) + wlan_cmd_hdr->payload_size, usb_intr_transfer_ep5_out_cb, NULL, 0); /* convert all header fields to little-endian byte order !!! */ wlan_cmd_pkt_hdr->unknown5 = htole16(wlan_cmd_pkt_hdr->unknown5); wlan_cmd_pkt_hdr->tag = htole16(wlan_cmd_pkt_hdr->tag); wlan_cmd_hdr->command = htole16(wlan_cmd_hdr->command); wlan_cmd_hdr->tag = htole16(wlan_cmd_hdr->tag); wlan_cmd_hdr->status = htole16(wlan_cmd_hdr->status); wlan_cmd_hdr->payload_size = htole16(wlan_cmd_hdr->payload_size); error = libusb_submit_transfer(transfer); if (error) { fprintf(stderr, "%s:%d: could not submit transfer (%d)\n", __func__, __LINE__, error); goto fail_free_transfer; } pthread_mutex_lock(&usb_wlan_cmd_mutex); usb_wlan_cmd_busy = 1; while (usb_wlan_cmd_busy) pthread_cond_wait(&usb_wlan_cmd_cond, &usb_wlan_cmd_mutex); pthread_mutex_unlock(&usb_wlan_cmd_mutex); return 0; fail_free_transfer: libusb_free_transfer(transfer); fail: return error; } /* * usb_wlan_cmd_start_scan */ static int usb_wlan_cmd_start_scan(void) { unsigned char data[256], *ptr; unsigned int data_size; memset(data, 0, sizeof(data)); ptr = data; *ptr++ = 0x0; *ptr++ = 0x1; *ptr++ = 0x64; *ptr++ = 0x0; ptr = data + 0xa; *ptr++ = 0x3; *ptr++ = 13; /* number of channels */ *ptr++ = 1; /* channels */ *ptr++ = 2; *ptr++ = 3; *ptr++ = 4; *ptr++ = 5; *ptr++ = 6; *ptr++ = 7; *ptr++ = 8; *ptr++ = 9; *ptr++ = 10; *ptr++ = 11; *ptr++ = 12; *ptr++ = 13; data_size = ptr - data; return usb_wlan_cmd_send(0x1035, data, data_size); } /* * usb_wlan_cmd_get_scan_results */ static int usb_wlan_cmd_get_scan_results(void) { unsigned char data[1456]; unsigned int data_size; memset(data, 0, sizeof(data)); data_size = sizeof(data); return usb_wlan_cmd_send(0x1033, data, data_size); } /* * usb_wlan_cmd_0x99 */ static int usb_wlan_cmd_0x99(void) { unsigned char data[0x3e]; unsigned int data_size; memset(data, 0, sizeof(data)); data_size = sizeof(data); return usb_wlan_cmd_send(0x99, data, data_size); } /* * usb_wlan_init */ static int usb_wlan_init(void) { unsigned char data[1456], *ptr; unsigned int data_size; int error; /* state 0x1 */ memset(data, 0, sizeof(data)); data_size = 0x518; error = usb_wlan_cmd_send(0x114f, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x114f (%d)\n", __func__, __LINE__, error); return error; } sleep(2); /* state 0x2 */ memset(data, 0, sizeof(data)); data_size = 0; error = usb_wlan_cmd_send(0x1171, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x1171 (%d)\n", __func__, __LINE__, error); return error; } sleep(2); /* wait for a WLAN event */ /* state 0x4 */ memset(data, 0, sizeof(data)); ptr = data; *ptr++ = 0x1; data_size = 0x4; error = usb_wlan_cmd_send(0x116f, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x116f (%d)\n", __func__, __LINE__, error); return error; } sleep(2); /* state 0x5 */ memset(data, 0, sizeof(data)); ptr = data; *ptr++ = 0x1; ptr = data + 0x4; memcpy(ptr, my_mac_addr, sizeof(my_mac_addr)); data_size = 0x5e; error = usb_wlan_cmd_send(0x115b, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x115b (%d)\n", __func__, __LINE__, error); return error; } sleep(2); /* state 0x6 */ memset(data, 0, sizeof(data)); ptr = data + 0x1c; *ptr++ = 0x20; data_size = 0x20; error = usb_wlan_cmd_send(0x1161, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x1161 (%d)\n", __func__, __LINE__, error); return error; } sleep(2); memset(data, 0, sizeof(data)); ptr = data + 0xc; memset(ptr, 0xff, 7 * 4); data_size = 0x80; error = usb_wlan_cmd_send(0x110d, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x110d (%d)\n", __func__, __LINE__, error); return error; } sleep(2); memset(data, 0, sizeof(data)); data_size = 0x2; error = usb_wlan_cmd_send(0x1031, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x1031 (%d)\n", __func__, __LINE__, error); return error; } sleep(2); memset(data, 0, sizeof(data)); ptr = data; memcpy(ptr, my_mac_addr, sizeof(my_mac_addr)); data_size = 0x6; error = usb_wlan_cmd_send(0x1041, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x1041 (%d)\n", __func__, __LINE__, error); return error; } sleep(2); /* state 0xa */ memset(data, 0, sizeof(data)); ptr = data; *ptr++ = 0x2; *ptr++ = 0x2; data_size = 0x2; error = usb_wlan_cmd_send(0x29, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x29 (%d)\n", __func__, __LINE__, error); return error; } sleep(2); memset(data, 0, sizeof(data)); ptr = data; *ptr++ = 0x1; ptr = data + 8; *ptr++ = 0x20; data_size = 0xc; error = usb_wlan_cmd_send(0x110b, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x110b (%d)\n", __func__, __LINE__, error); return error; } sleep(2); memset(data, 0, sizeof(data)); ptr = data; *ptr++ = 0x1; ptr = data + 0x4; *ptr++ = 0x15; *ptr++ = 0x27; *ptr++ = 0x12; *ptr++ = 0x0; *ptr++ = 0x6; *ptr++ = 0x0; ptr = data + 0xc; *ptr++ = 0x9; *ptr++ = 0x0; *ptr++ = 0x1; ptr = data + 0x10; *ptr++ = 0xff; *ptr++ = 0xff; *ptr++ = 0xff; *ptr++ = 0xff; *ptr++ = 0xff; *ptr++ = 0xff; data_size = 0x16; error = usb_wlan_cmd_send(0x1109, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x1109 (%d)\n", __func__, __LINE__, error); return error; } sleep(2); memset(data, 0, sizeof(data)); ptr = data; *ptr++ = 0x1; data_size = 0x4; error = usb_wlan_cmd_send(0x207, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x207 (%d)\n", __func__, __LINE__, error); return error; } sleep(2); memset(data, 0, sizeof(data)); ptr = data; *ptr++ = 0x4; data_size = 0x4; error = usb_wlan_cmd_send(0x203, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x203 (%d)\n", __func__, __LINE__, error); return error; } sleep(2); /* state 0xf */ memset(data, 0, sizeof(data)); ptr = data; *ptr++ = 0xff; *ptr++ = 0x1f; memcpy(ptr, my_mac_addr, sizeof(my_mac_addr)); ptr = data + 0x8; *ptr++ = 0x2; *ptr++ = 0x2; data_size = 0xa; error = usb_wlan_cmd_send(0x105f, data, data_size); if (error) { fprintf(stderr, "%s:%d: could not send command 0x105f (%d)\n", __func__, __LINE__, error); return error; } return 0; } /* * usb_wlan_cmd_thread */ static void *usb_wlan_cmd_thread(void *arg) { int error; error = usb_wlan_init(); if (error) { fprintf(stderr, "%s:%d: could not initialize device (%d)\n", __func__, __LINE__, error); goto done; } sleep(5); error = usb_wlan_cmd_0x99(); if (error) { fprintf(stderr, "%s:%d: could not start scanning (%d)\n", __func__, __LINE__, error); goto done; } error = usb_wlan_cmd_start_scan(); if (error) { fprintf(stderr, "%s:%d: could not start scanning (%d)\n", __func__, __LINE__, error); goto done; } sleep(10); error = usb_wlan_cmd_get_scan_results(); if (error) { fprintf(stderr, "%s:%d: could not get scan results (%d)\n", __func__, __LINE__, error); goto done; } sleep(10); done: usb_wlan_cmd_thread_done = 1; return NULL; } /* * main */ int main(int argc, char **argv) { unsigned char buf[256]; pthread_t tid; struct timeval tv; int error; pthread_mutex_init(&usb_wlan_cmd_mutex, NULL); pthread_cond_init(&usb_wlan_cmd_cond, NULL); error = libusb_init(&usb_ctx); if (error) { fprintf(stderr, "%s:%d: libusb_init failed (%d)\n", __func__, __LINE__, error); exit(1); } libusb_set_debug(usb_ctx, 5); usb_dev_handle = libusb_open_device_with_vid_pid(usb_ctx, USB_VENDOR_ID, USB_PRODUCT_ID); if (!usb_dev_handle) { fprintf(stderr, "%s:%d: could not open device\n", __func__, __LINE__); exit(1); } if(libusb_kernel_driver_active(usb_dev_handle, USB_IFACE_NUMBER)) { fprintf(stdout, "%s:%d: kernel driver is attached\n", __func__, __LINE__); error = libusb_detach_kernel_driver(usb_dev_handle, USB_IFACE_NUMBER); if (error) { fprintf(stderr, "%s:%d: could not detach kernel driver (%d)\n", __func__, __LINE__, error); exit(1); } fprintf(stdout, "%s:%d: kernel driver dettached\n", __func__, __LINE__); } error = libusb_claim_interface(usb_dev_handle, USB_IFACE_NUMBER); if (error) { fprintf(stderr, "%s:%d: could not claim interface (%d)\n", __func__, __LINE__, error); exit(1); } error = libusb_control_transfer(usb_dev_handle, 0x40, 0x1, 0x9, 0x0, usb_magic_data, sizeof(usb_magic_data), 0); if (error < 0) { fprintf(stderr, "%s:%d: could not do control transfer (%d)\n", __func__, __LINE__, error); exit(1); } fprintf(stdout, "%s:%d: number of bytes transferred (%d)\n", __func__, __LINE__, error); error = libusb_control_transfer(usb_dev_handle, 0xc0, 0x0, 0x2, 0x0, buf, 2, 0); if (error < 0) { fprintf(stderr, "%s:%d: could not do control transfer (%d)\n", __func__, __LINE__, error); exit(1); } fprintf(stdout, "%s:%d: number of bytes received (%d)\n", __func__, __LINE__, error); fprintf(stdout, "%s:%d: 0x%02x 0x%02x\n", __func__, __LINE__, buf[0], buf[1]); usb_intr_transfer_ep5_in = libusb_alloc_transfer(0); if (!usb_intr_transfer_ep5_in) { fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__); exit(1); } memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf)); libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5, usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf), usb_intr_transfer_ep5_in_cb, NULL, 0); error = libusb_submit_transfer(usb_intr_transfer_ep5_in); if (error) { fprintf(stderr, "%s:%d: could not submit transfer (%d)\n", __func__, __LINE__, error); exit(1); } error = pthread_create(&tid, NULL, usb_wlan_cmd_thread, NULL); if (error) { fprintf(stderr, "%s:%d: could not create WLAN command thread (%d)\n", __func__, __LINE__, error); exit(1); } while (!usb_wlan_cmd_thread_done) { tv.tv_sec = 1; tv.tv_usec = 0; error = libusb_handle_events_timeout(usb_ctx, &tv); if (error) { fprintf(stderr, "%s:%d: could not handle events (%d)\n", __func__, __LINE__, error); exit(1); } } libusb_free_transfer(usb_intr_transfer_ep5_in); error = libusb_release_interface(usb_dev_handle, USB_IFACE_NUMBER); if (error) fprintf(stderr, "%s:%d: could not release interface (%d)\n", __func__, __LINE__, error); libusb_close(usb_dev_handle); libusb_exit(usb_ctx); exit(0); }
Output
glevand@debian-hdd:~/ps3_usb_wlan$ sudo ./ps3_usb_wlan sudo: unable to resolve host debian-hdd main:824: number of bytes transferred (32) main:833: number of bytes received (2) main:835: 0x20 0x31 usb_wlan_cmd_send:288: sending command (0x114f) data size (0x0518) command size (0x0524) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1150) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0006) usb_handle_wlan_cmd_response:205: ==> command status != 0x1 usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_wlan_cmd_send:288: sending command (0x1171) data size (0x0000) command size (0x000c) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1172) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68) usb_handle_wlan_event:133: === got WLAN event === usb_handle_wlan_event:144: event_count (0x01) 00000000: 00 04 00 00 10 00 00 00 3c 22 02 00 00 00 00 00 |........<"......| 00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............| 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| usb_wlan_cmd_send:288: sending command (0x116f) data size (0x0004) command size (0x0010) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1170) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_wlan_cmd_send:288: sending command (0x115b) data size (0x005e) command size (0x006a) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x115c) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_wlan_cmd_send:288: sending command (0x1161) data size (0x0020) command size (0x002c) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1162) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_wlan_cmd_send:288: sending command (0x110d) data size (0x0080) command size (0x008c) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x110e) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_wlan_cmd_send:288: sending command (0x1031) data size (0x0002) command size (0x000e) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1032) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0002) usb_handle_wlan_cmd_response:210: command payload: 00000000: 00 00 |.. | usb_wlan_cmd_send:288: sending command (0x1041) data size (0x0006) command size (0x0012) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (42) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1042) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0006) usb_handle_wlan_cmd_response:210: command payload: 00000000: 00 11 22 33 44 55 |.."3DU | usb_wlan_cmd_send:288: sending command (0x0029) data size (0x0002) command size (0x000e) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x002a) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0002) usb_handle_wlan_cmd_response:210: command payload: 00000000: 02 02 |.. | usb_wlan_cmd_send:288: sending command (0x110b) data size (0x000c) command size (0x0018) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (48) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x110c) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x000c) usb_handle_wlan_cmd_response:210: command payload: 00000000: 01 00 00 00 00 00 00 00 20 00 00 00 |........ ... | usb_wlan_cmd_send:288: sending command (0x1109) data size (0x0016) command size (0x0022) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (58) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x110a) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0016) usb_handle_wlan_cmd_response:210: command payload: 00000000: 01 00 00 00 15 27 12 00 06 00 00 00 09 00 01 00 |.....'..........| 00000010: ff ff ff ff ff ff |...... | usb_wlan_cmd_send:288: sending command (0x0207) data size (0x0004) command size (0x0010) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x0208) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0004) usb_handle_wlan_cmd_response:210: command payload: 00000000: 01 00 00 00 |.... | usb_wlan_cmd_send:288: sending command (0x0203) data size (0x0004) command size (0x0010) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x0204) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0004) usb_handle_wlan_cmd_response:210: command payload: 00000000: 04 00 00 00 |.... | usb_wlan_cmd_send:288: sending command (0x105f) data size (0x000a) command size (0x0016) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1060) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68) usb_handle_wlan_event:133: === got WLAN event === usb_handle_wlan_event:144: event_count (0x01) 00000000: 80 00 00 00 00 10 00 00 9e 2b 02 00 04 00 00 00 |.........+......| 00000010: fc 90 02 c0 01 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............| 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| usb_wlan_cmd_send:288: sending command (0x0099) data size (0x003e) command size (0x004a) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (98) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x009a) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x003e) usb_handle_wlan_cmd_response:210: command payload: 00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2| 00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19| 00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).| 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |.............. | usb_wlan_cmd_send:288: sending command (0x1035) data size (0x0019) command size (0x0025) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (61) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1036) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0019) usb_handle_wlan_cmd_response:210: command payload: 00000000: 00 01 64 00 00 00 00 00 00 00 03 0d 01 02 03 04 |..d.............| 00000010: 05 06 07 08 09 0a 0b 0c 0d |......... | usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68) usb_handle_wlan_event:133: === got WLAN event === usb_handle_wlan_event:144: event_count (0x01) 00000000: 80 00 00 00 04 00 00 00 96 2e 02 00 01 00 00 00 |................| 00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............| 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| usb_wlan_cmd_send:288: sending command (0x1033) data size (0x05b0) command size (0x05bc) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (1403) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1034) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0557) usb_handle_wlan_cmd_response:210: command payload: ... Here is scan output (removed by me) ...
Associate with AP
- I got association with AP working.
- If WLAN device is connected to an AP then the green LED is on, when data is received then the LED blinks.
- Data reception works finally !!!
How to Associate with WPA AP
- Set common configuration (command 0x1005)
- Set WPA configuration (command 0x1019)
- Set rate configuration (command 0x1ed)
- Associate (command 0x1001)
Packet Reception
- EP6 IN and EP7 IN endpoints are used for packet reception
- LV2 sends bulk transfers to both endpoints
- 4 bulk transfers are sent simultaneously for each enpoint
- Every bulk transfer is of size 0x620
- Make sure you set multicast address filter properly or else you won't receive broadcast packets !!!
- Bulk transfers returned by the host controller which do not contain any data have size of 0x10 bytes else transfers contain valid Ethernet frame. All 802.11 related data is stripped by the WLAN Gelic device.
- Make sure you set right MAC address with command 0x115b else device won't be able to receive packets destined to its own MAC address !!!
Test with libusb
usb_bulk_transfer_ep6_in_cb:318: === got data transfer === usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (98) 00000000: ff ff ff ff ff ff ?? ?? ?? ?? ?? ?? 08 00 45 00 |..............E.| 00000010: 00 54 00 00 40 00 40 01 b5 fe c0 a8 01 5b c0 a8 |.T..@.@......[..| 00000020: 01 ff 08 00 9c 69 0d 45 00 e2 4e 5d 34 26 00 07 |.....i.E..N]4&..| 00000030: df e1 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 |................| 00000040: 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 |.......... !"#$%| 00000050: 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 |&'()*+,-./012345| 00000060: 36 37 |67 | usb_bulk_transfer_ep6_in_cb:318: === got data transfer === usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16) 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 |................| usb_bulk_transfer_ep6_in_cb:318: === got data transfer === usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16) 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 |................| usb_bulk_transfer_ep6_in_cb:318: === got data transfer === usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16) 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 |................|
Multicast Address Filter
- WLAN Gelic device supports hardware multicast address filtering
- Multicast address filtering is implemented with MAC address hashing and filter bitmap
- Filter bitmap is of size 4 * 8 bytes
- Multicast address filter is set with command 0x1161
MAC Address Hash Function
- Used by LV2
unsigned char hash(unsigned char *data, unsigned int size) { unsigned int hash; int i, j; /*XXX: reverse data bits */ hash = 0xffffffff; for (i = 0; i < size; i++) { hash = (((unsigned int) data[i]) << 24) ^ hash; for (j = 0; j < 8; j++) { if (((int) hash) >= 0) { hash = hash << 1; } else { hash = (hash << 1) ^ 0x04c10000; hash = hash ^ 0x00001db7; } } } hash = ((hash >> 24) & 0xf8) | (hash & 0x7); return hash & 0xff; } h = hash(mac_addr, 6); v = 1 << (h & 0x1f); /* word value in filter */ p = h >> 5; /* word position in filter */ For broadcast address: ------------------------ v = 0x20000000 p = 7 That's why 0x20 is used with command 0x1161 !!! Without it the device won't deliver broadcast traffic. Learned it the hard way, after 2 days of trying to get packet reception working :)
Packet Transmission
- Tx packets are sent to EP6 OUT
- Tx packets are normal Ethernet frames, they don't contain any WLAN data or other headers
AP Mode
- I got AP mode working with security disabled for now
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
ps3-jupiter Linux Drivers
- ps3_jupiter.ko is the common part of STA and AP mode. It implements a command interface to WLAN Gelic device and disptaches events to STA and AP drivers.
- ps3_jupiter_sta.ko is a STA mode implementation.
- ps3_jupiter_ap.ko is a AP mode implementation.
- Simple scanning works already in STA mode (try it out with iwlist scan)
- Packet reception works
- Packet transmission works
- WPA/WPA2 fully working and usable with wpa_supplicant
Finally, after several weeks of hard programming and reversing, the WLAN driver ps3_jupiter_sta achieved the milestone where i can use it with WPA2 :) I actually use it currently with WPA2 on my PS3 slim. It works damn !!! Try it out and report bugs and problems to me.
TODO
- Implement association in STA mode (finished)
- Implement packet reception and transmission in STA mode (finished)
- Implement WEP support
- Implement AP mode
- Find out if Jupiter supports Monitor mode and if yes how to enable it
- Implement EURUS driver for PHATs (has many advantages over the old OtherOS approach, e.g. AP mode)
- Port to FreeBSD
LV2 Network Stack
- LV2 uses BSD network stack, e.g. struct mbuf
- It's almost identical to FreeBSD network stack.
Network Device
IOCTLs
Set Multicast Address Filter (0x81012000)
- Sets multicast address filter
- Uses LV1 calls lv1_net_remove_multicast_address and lv1_net_add_multicast_address for Ethernet Gelic device
- Uses Eurus commands 0x1161, 0x1163 and 0x1165 for WLAN Gelic device
Unknown (0x8101200E)
- Uses LV1 call lv1_net_control(0x8000000000000001)
Unknown (0x81040000)
- Uses LV1 call lv1_net_control(0x8, [0x0, 0x1 or 0x2]) for Ethernet Gelic device
- Uses Eurus commands 0x116F, 0x115D and 0x115B for WLAN Gelic device
Enable/Disable WOL Magic Packet (0x81080000)
- 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
Unknown (0x81080002)
- Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x3) for Ethernet Gelic device
- Uses Eurus commands 0x113D and 0x1159 for WLAN Gelic device
Unknown (0x81080003)
- Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x4) for Ethernet Gelic device
- Uses Eurus command 0x1161 for WLAN Gelic device
Unknown (0x81080005)
- Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x6 /* GELIC_LV1_WOL_ADD_MATCH_ADDR */) for Ethernet Gelic device
- Uses Eurus commands 0x116D and 0x1167 for WLAN Gelic device
Network Packet
- LV2 network packet is represented by struct mbuf
RSX
Crossreference: gitbrew.org::RSX
HV Calls
lv1_gpu_memory_allocate
- LV1 supports 16 memory handles simultaneously.
- LV1 uses a bitmap to manage GPU VRAM.
- The bitmap is located in LV1 memory, 4 double words.
- Each bit corresponds to 1MB VRAM, 256bit = 256MB VRAM.
- 2MB at the top of VRAM are preallocated as you can see below.
<memory handle> = 0x5a5a5a5a xor <memory handle index>
Memory Context Object
offset 0x8 - memory handle (4 bytes)
offset 0x10 - VRAM LPAR start address (8 bytes)
offset 0x18 - VRAM LPAR end address (8 bytes)
Test
- The offset of bitmap could be different on your system because it's allocated dynamically.
- First 9MB of VRAM were allocated by ps3fb Linux driver.
Before allocating VRAM:
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C 00000000 00 00 00 00 00 00 01 ff 00 00 00 00 00 00 00 00 |.......ÿ........| 00000010 00 00 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 |........À.......|
After allocating 32 MB VRAM:
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C 00000000 00 00 01 ff ff ff ff ff 00 00 00 00 00 00 00 00 |...ÿÿÿÿÿ........| 00000010 00 00 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 |........À.......|
lv1_gpu_context_allocate
- Register %r4 is flags.
- Found the place in LV1 where LV1 sets IO page size for GART memory mapping. We could patch it and set to 4KB. That would make a lot of things easier for RSX developers on Linux.
- 1MB pages make RSX driver for Linux hard to implement because allocating 1Mb contiguous memory chunk on Linux is very very hard especially on a system with only 256MB and which was running for some time.
- LV1 supports 16 contexts simultaneously.
- LV1 has an array of context pointers.
- Each context has an index and a handle. The handle is derived from the index of the context.
<context handle> = 0x55555555 xor <context index>
- Thats why first created context will have handle 0x55555555.
Context Object
offset 0x8 - handle (4 bytes)
offset 0x48 - IO page size, valid range is 4kB, 64KB and 1MB (8 bytes)
Flags
0x2 - tells LV1 to use 64KB pages for GART memory mapping else LV1 uses 1MB pages
lv1_gpu_context_iomap
- Internally uses lv1_put_iopte function
- IO page size is the one set during lv1_gpu_context_allocate
- IO address space id is 0x0. IO id is 0x1.
lv1_gpu_context_attribute
Attribute 0x1
FIFO Command Buffer Setup
lv1_gpu_context_attribute(context handle, 0x1, PUT offset, GET offset, 0x0, 0x0)
Attribute 0x101
Set Flip Mode
lv1_gpu_attribute(0x2, 0x1 /* head */, 0x0, 0x0) lv1_gpu_context_attribute(context handle, 0x101, 0x1 /* head */, sync mode, 0x0, 0x0)
Attribute 0x104
Set Display Buffer
lv1_gpu_context_attribute(context handle, 0x104, id, width << 32 | height, pitch << 32 | offset, 0x0)
Attribute 0x10a
Get Flip Status
- Reads a value at offset 0x10C0 + 0x1 * 0x40 in lpar_reports memory.
Reset Flip Status
lv1_gpu_context_attribute(context handle, 0x10a, 0x1 /* id */, 0x7fffffff /* mask */, 0x0 /* value */, 0x0)
- The LV1 call lv1_gpu_context_attribute(0x10a) accesses LPAR memory returned in lpar_reports by LV1 call lv1_gpu_context_allocate.
- Offset into lpar_reports is 0x10C0 + id * 0x40 = 0x10C0 + 0x1 * 0x40.
- Why not access lpar_reports memory directly and use LV1 call instead ???
Attribute 0x10b
- This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.
Set Cursor Position
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x3, x, y)
Set Cursor Image Offset
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x2, offset, 0x0)
Attribute 0x10c
- This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.
Cursor Function 1
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x1, 0x0, 0x0)
Cursor Function 2
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x2, 0x0, 0x0)
Attribute 0x10d
- This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.
Cursor Function 1
lv1_gpu_context_attribute(context handle, 0x10d, 0x1, 0x1, 0x0, 0x0)
Attribute 0x300
Set Tile
Set Invalidate Tile
Bind Tile
Unbind Tile
Attribute 0x301
Set Zcull
Bind Zcull
Unbind Zcull
Attribute 0x601
- Copies data from GART memory to VRAM.
- LV1 uses internally the FIFO command buffer passed by ps3fb driver with lv1_gpu_context_iomap.
FIFO commands:
0x0004C184 0xFEED0001 0x0004C198 0x313371C3 0x00046300 0x0000000A for () { for () { 0x0004630C <param> 0x00046304 <param> 0x0024C2FC 0x00000001 0x00000003 0x00000003 <param1> <param2> <param3> <param4> 0x00010000 0x00010000 0x0001C400 <param1> <param2> <param3> 0x00000000 } } 0x00040110 0x00000000
FIFO Command Buffer
FIFO Control Registers
- LV1 call lv1_gpu_context_allocate returns LPAR address of FIFO control registers.
- You have to map it into Linux address space before you can access FIFO control registers.
- Value of PUT and GET registers are NOT expressed in Linux address space but in RSX address space. You have to convert it to RSX address space.
- GET register is read-only and is modified by RSX while it's processing FIFO commands.
Kicking FIFO Command Buffer
- As long as values of GET and PUT FIFO control registers are equal, RSX doesn't process commands from the FIFO command buffer.
- When the value of PUT register is not equal to the value of GET register, RSX starts processing commands in the FIFO command buffer.
- To execute FIFO commands, place them in the FIFO command buffer and change the value of PUT register.
FIFO Setup Programs of emer_init.self
- PS3:HvReverseEngineering:emer_init.self:Program 1
- PS3:HvReverseEngineering:emer_init.self:Program 2
- PS3:HvReverseEngineering:emer_init.self:Program 3
FIFO Commands
PS3:HvReverseEngineering:RSXFIFOCommands
Example How to Use FIFO Command Buffer
Here is a small Linux kernel module which shows you how to use FIFO command buffer on Linux.
- RSX allows to create multiple contexts.
- This kernel module should run without problems with ps3fb driver already running.
- Make sure you unload ps3vram driver before running this module because ps3vram allocates all available RSX memory for itself and because of this, lv1_gpu_memory_allocate will always fail.
- This kernel module lets the RSX execute a simple program which contains only NOP (No Operation) commands.
Download source code: [2]
Source Code
/* * PS3 RSX * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published * by the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/delay.h> #include <asm/abs_addr.h> #include <asm/cell-regs.h> #include <asm/lv1call.h> #include <asm/ps3.h> #define RSX_FIFO_CMD_BUF_SIZE (1 * 1024 * 1024) #define RSX_MEM_SIZE (32 * 1024 * 1024) #define RSX_GPU_IOIF (0x0e000000ul) #define RSX_FIFO_CTRL_SIZE (4 * 1024) struct rsx_fifo_ctrl { u8 res[0x40]; u32 put; u32 get; }; static u32 *rsx_fifo_cmd_buf; static u64 rsx_fifo_cmd_buf_lpar; static u64 rsx_mem_handle, rsx_mem_lpar; static u64 rsx_ctx_handle; static u64 rsx_fifo_ctrl_lpar; static u64 rsx_drv_info_lpar; static u64 rsx_reports_lpar, rsx_reports_size; static struct rsx_fifo_ctrl *rsx_fifo_ctrl; /* * FIFO program */ static u32 rsx_fifo_prg[] = { 0x00000000, /* nop */ 0x00000000, /* nop */ 0x00000000, /* nop */ }; /* * ps3rsx_init */ static int __init ps3rsx_init(void) { unsigned long timeout; int res; /* FIFO command buffer must be allocated in XDR memory */ rsx_fifo_cmd_buf = kmalloc(RSX_FIFO_CMD_BUF_SIZE, GFP_KERNEL); if (!rsx_fifo_cmd_buf) { printk(KERN_INFO"could not allocate FIFO command buffer\n"); res = -ENOMEM; goto fail; } res = lv1_gpu_memory_allocate(RSX_MEM_SIZE, 0, 0, 0, 0, &rsx_mem_handle, &rsx_mem_lpar); if (res) { printk(KERN_INFO"lv1_gpu_memory_allocate failed (%d)\n", res); res = -ENXIO; goto fail_free_fifo_cmd_buf_mem; } res = lv1_gpu_context_allocate(rsx_mem_handle, 0, &rsx_ctx_handle, &rsx_fifo_ctrl_lpar, &rsx_drv_info_lpar, &rsx_reports_lpar, &rsx_reports_size); if (res) { printk(KERN_INFO"lv1_gpu_context_allocate failed (%d)\n", res); res = -ENXIO; goto fail_free_gpu_mem; } /* map FIFO command buffer into RSX address space */ rsx_fifo_cmd_buf_lpar = ps3_mm_phys_to_lpar(__pa(rsx_fifo_cmd_buf)); res = lv1_gpu_context_iomap(rsx_ctx_handle, RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar, RSX_FIFO_CMD_BUF_SIZE, CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M); if (res) { printk(KERN_INFO"lv1_gpu_context_iomap failed (%d)\n", res); res = -ENXIO; goto fail_free_gpu_mem; } /* map RSX FIFO control registers */ rsx_fifo_ctrl = (struct rsx_fifo_ctrl *) ioremap(rsx_fifo_ctrl_lpar, RSX_FIFO_CTRL_SIZE); if (!rsx_fifo_ctrl) { printk(KERN_INFO"could not map FIFO control\n"); res = -ENXIO; goto fail_free_gpu_mem; } /* PUT and GET offsets are in RSX address space */ res = lv1_gpu_context_attribute(rsx_ctx_handle, 0x1, RSX_GPU_IOIF + 0x0 /* PUT offset */, RSX_GPU_IOIF + 0x0 /* GET offset */, 0x0, 0x0); if (res) { printk(KERN_INFO"lv1_gpu_context_attribute(0x1) failed (%d)\n", res); res = -ENXIO; goto fail_unmap_fifo_ctrl; } /* copy FIFO commands to FIFO command buffer */ memcpy(rsx_fifo_cmd_buf, rsx_fifo_prg, sizeof(rsx_fifo_prg)); printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put); /* kick FIFO */ rsx_fifo_ctrl->put = RSX_GPU_IOIF + sizeof(rsx_fifo_prg); /* poll until RSX is done processing FIFO commands */ timeout = 100; while (timeout--) { if (rsx_fifo_ctrl->get == rsx_fifo_ctrl->put) break; msleep(1); } printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put); if (rsx_fifo_ctrl->get != rsx_fifo_ctrl->put) { printk(KERN_INFO"FIFO command buffer timeout\n"); res = -ENXIO; goto fail_unmap_fifo_ctrl; } return 0; fail_unmap_fifo_ctrl: iounmap(rsx_fifo_ctrl); fail_free_gpu_mem: lv1_gpu_memory_free(rsx_mem_handle); fail_free_fifo_cmd_buf_mem: kfree(rsx_fifo_cmd_buf); fail: return res; } /* * ps3rsx_exit */ static void __exit ps3rsx_exit(void) { iounmap(rsx_fifo_ctrl); lv1_gpu_context_iomap(rsx_ctx_handle, RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar, RSX_FIFO_CMD_BUF_SIZE, CBE_IOPTE_M); lv1_gpu_context_free(rsx_ctx_handle); lv1_gpu_memory_free(rsx_mem_handle); kfree(rsx_fifo_cmd_buf); } module_init(ps3rsx_init); module_exit(ps3rsx_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("PS3 RSX"); MODULE_AUTHOR("glevand");
Test
# insmod ./ps3rsx.ko # dmesg GET offset (0x0e000000) PUT offset (0x0e000000) # GET and PUT offsets before kicking FIFO GET offset (0x0e00000c) PUT offset (0x0e00000c) # GET and PUT offsets after kicking FIFO
As you see, RSX processed our FIFO commands :)
Linux Driver
- DRI/DRM is the ONLY way to go !!! No hacks like kernel modules with tons of IOCTLs !!!
- First implement 2D acceleration and then add 3D support
- The driver consists of 2 parts: DDX driver for X11 (user space) and DRM driver for Linux Kernel (kernel space)
- First implement DRM driver and test it from user space without DDX and libdrm by talking to it directly
DDX Driver
- Use libdrm
- Use EXA API for 2D acceleration on X11 (or maybe use XAA API)
- Use Kernel Mode Setting
DRM Driver
- Extend nouveau driver or create a new one ???
- Decision: create new DRM driver in order to learn how DRM framework in Linux kernel works and because we have to use LV1 calls to access RSX (and because it's a lot more fun to do it on my own). But use nouveau as an example for DRM driver. Maybe i should better use radeon DRM driver as an example beacuse it seems to be better designed and implemnted !!!
- The driver is very low level and allows direct access to almost all RSX funtions, e.g. FIFO buffer, to achieve maximum performance.
- All data buffers, e.g. vertices and textures, are managed by DRM framework (Linux kernel). To avoid copying from user to kernel space, the buffers will be mmaped into user space.
- Provides an interface to manage graphic objects in VRAM.
- Use TTM or GEM ??? TTM is used by radeon and nouvea drivers, so i guess we could use it too. GEM is for Intel chips.
- Extend libdrm library to support new DRM driver.
- Fences can be implemented with RSX REF Control Register
Memory Management
- Size of all memory objects must be multiple of the page size (4096 bytes) even if a smaller size is requested by user
- Nouveau driver uses IOCTL DRM_NOUVEAU_GEM_NEW to allocate memory objects in VRAM or GART. The IOCTL returns the handle of the newly allocated memory object.
- An example from Mesa how memory objects are used: [3] [4]
Video RAM
- VRAM is allocated once during context creating and cannot be changed during the whole life of the context.
- lv1_gpu_memory_allocate returns LPAR address of allocated VRAM which can be mapped into kernel address space.
- VRAM starts at offset 0x0 in GPU address space.
- VRAM heap management is necessary, use e.g. TTM (ttm_bo_init_mm).
- This memory type is used e.g. for vertices or textures.
- It should be mappable from user space in order to allow user to put data there.
- GameOS calls it Local Memory.
- VRAM can be mapped into kernel-space with ioremap.
- To map VRAM into user-space map it first into kernel-space with ioremap and then use remap_pfn_range to map into user-space.
- Use VM_IO flag for this kind of memory when mapping it into user-space.
- Mapping examples: [5] [6]
GART Memory
- GART memory region is a memory region in System Memory but accessible by RSX through GART [7].
- GameOS calls it Main Memory.
- Problem: lv1_gpu_context_iomap supports ONLY 1MB and 64kB pages
- Size of system memory objects mapped into GPU address space should be either multiple of 1MB which means wasting lots of RAM and we don't have enough of it anyways. This solution is NOT suitable.
- Or place several GART memory objects into 1 MB page and map it. That would mean we have to use memory manager for each 1MB page.
- That means, we have to allocate 1MB page even if user requested a smaller memory region. Then initialize a heap manager for this 1MB page and return ONLY requested size. The following requests for GART memory regions can be satisfied from the previously allocated 1MB pages which still have enough free memory.
- FIFO command buffer is an example of a GART memory object which has to be mapped into GPU address space with lv1_gpu_context_iomap before it can be used by RSX.
- User allocates FIFO command buffer in GART address space, maps it into user space, write commands into it and then pushes it to DRM driver which maps it into RSX address space and CALLs it.
- TTM: TTM_PL_FLAG_TT for GART memory
- GameOS applications using GCM library map GART memory beginning at offset 0x10000000 or 0x20000000, just after where the whole VRAM is mapped.
- Don't use kmalloc for this type of memory. Use __get_free_pages and mark pages with flag VM_RESERVED before exporting it to user-space else they can be swapped out.
- TTM uses struct ttm_backend_func to call driver specific GART mapping functions. nouveau_sgdma.c handles GART memory mapping.
CPU Memory
- This type of memory cannot be accessed by RSX at all.
- Because this type of memory is not mapped into RSX address space through GART we don't need to allocate it in 1MB multiples.
- What do we need it for ???
Mapping Memory Objects into Kernel-Space
- Nouveau driver uses ttm_bo_kmap to map memory objects into kernel-space (see ttm_bo_util.c).
- Nouveau driver uses ttm_bo_ioremap to map IO memory into kernel-space, e.g. VRAM or GPU registers (see ttm_bo_util.c) which uses ioremp_wc or ioremp_nocache.
- TTM uses page-wise allocation for buffers. The buffers are contiguous ONLY in a single page. That has a huge advantage over allocating 1MB contiguous memory blocks in kernel space. It's far easier to allocate a single page in Linux kernel than 1MB memory chunk, especially on PS3 arch which has only 256MB.
- Problem: lv1_gpu_context_iomap allows ONLY 1MB pages. Use lv1_put_iopte ???. See [8], [9], [10] and [11].
- Yes, we can use lv1_put_iopte instead of lv1_gpu_context_iomap. That would solve the problem with 1MB pages on Linux. Both LV1 calls use the same internal LV1 function to map memory pages.
- lv1_gpu_context_iomap uses IOAS_ID 0 and IOID 1.
- TTM allows to map a buffer multiple times. Mapping information is stored in struct ttm_bo_kmap_obj.
- To make single allocated pages look contiguous to kernel-space, TTM uses vmap.
- It is possible to use 64KB pages for GART mapping without patching LV1. To enable 4KB pages support we have to patch LV1.
- Tested with 64kB IO page size. It works fine.
Mapping Memory Objects into User-Space
- User-space programs should be able to allocate memory objects in VRAM or GART and map it with mmap syscall.
- See nouveau_ttm.c:nouveau_ttm_mmap.
- Mapping memory objects into user-space avoids copying of data between user/kernel spaces.
- Problem: how to identify memory objects ???
- libdrm uses handles which are returned by DRM kernel driver when a new memory object is created. The handle is passed to mmap syscall as parameter offset. DRM driver looks up the handle and identifies the appropriate memory object which is mapped into user-space then.
- Nouveau driver uses TTM framework to map memory objects into user-space. TTM doesn't map all pages owned by the memory object at once but installs VM operation fault which maps single pages on demand. It makes sense because user application rarely accesses all pages of the mapped memory object at once.
- To map memory objects located in VRAM we have to map it into kernel space first with ioremap.
FIFO Command Buffer
- Every context has its own one main FIFO command buffer which is NOT accessible directly by user space.
- User-space applications can allocate additional FIFO command buffers in GART memory space, map it into user space, store commands there and submit to DRM driver.
- Nouveau driver uses IOCTL NOUVEAU_GEM_PUSHBUF to execute FIFO command buffers. See nouveau_gem.c:nouveau_gem_ioctl_pushbuf.
- By user applications submitted FIFO command buffers are mapped by DRM driver into RSX address space first and then executed with CALL command.
- Problem: All references to graphics objects contained in FIFO command buffers must be expressed in RSX address space. How does user space know the right offsets of the referenced objects ???
- To solve the above problem, Nouveau driver uses relocations which are submitted to DRM driver together with FIFO command buffers. The DRM driver applies the specified relocations before executing the FIFO command buffer. See nouveau_gem.c:nouveau_gem_pushbuf_reloc_apply.
- Relocations contain memory object handles which they apply to. The DRM driver looks up the memory object by its handle and the memory objects contain GPU address space offsets.
Example
--------------------------------------------------------------- | | | | \|/ Main FIFO command buffer (one per allocated context) | ------------------------------ ------------------------------------ | | | | | | | | ... | CALL | ... | CALL | ... | JMP | | | | | | | | ------------------------------ ------------------------------------ | /|\ | /|\ -------------| | | | | ------| --------| | \|/ | | ---| ----------------------- | | | | | | | | | ... | ... | RET | | | | | | | | | ----------------------- | | FIFO command buffer 1 | | (allocated by user space) \|/ | ----------------------- | | | | | ... | ... | RET | | | | | ----------------------- FIFO command buffer 2 (allocated by user space)
Fences
- Nouveau driver implements DRM fences with REF control register. See nouveau_fence.c:nouveau_fence_new.
- Newer Nvidia chips support semaphores. Nouveau driver uses semaphores for fences if they are supported.
- libgcm functions SetWriteCommandLabel and SetWaitLabel use semaphores.
- SetWriteCommandLabel releases semaphore and SetWaitLabel acquires semaphore.
- Semaphores are placed in VRAM. Nouveau driver creates a small VRAM heap for semaphores. See nouveau_fence.c:nouveau_fence_channel_init.
IOCTLs
Context Create
- Creates new RSX context
- Allocates VRAM and memory for FIFO buffer
- Needed VRAM size and FIFO buffer size must be known during context creation
Context Destroy
- Destroys previously allocated context
Context Attribute
- Changes context attributes
Graphic Object Creatre
- Create a graphic object either in VRAM or in XDR
- Used to create FIFO command buffers too (only in XDR of course because RSX supoorts FIFO command buffer in XDR only)
Graphic Object Destroy
- Frees previously created graphic object
FIFO Execute
- Allows user space applications to execute FIFO commands.
- To avoid copying of buffers allocated by user space to main FIFO command buffer use CALL and RET RSX FIFO commands to execute FIFO commands in buffers allocated by user space.
- Several FIFO command buffers can be submitted at once.
Framebuffer
- Kernel DRM driver has to implement a frame buffer driver too
- Nouvea driver allocates frame buffer in video RAM and maps it into kernel address space (see nouveau_fbcon.c:nouveau_fbcon_create). Current ps3fb Linux driver doesn't allocate frame buffer in vide RAM but in system RAM.
- Direct access to video RAM from kernel is very very slow but some of frame buffer functions in Nouvea driver are hardware accelerated. We could do it the same way on Linux and get a hardware accelerated frame buffer this way. Not sure why ps3fb authors didn't add hardware acceleration to frame buffer. The reason why it was not implemnted in ps3fb is because LV1 doesn't create 2D graphic objects needed for 2D hardware acceleration.
- lv1_gpu_allocate_memory returns LPAR address of video RAM allocated for the RSX context.
- Unfortunately lv1_gpu_context_allocate doesn't initialize 2D ROP objects but we could use 3D operations to implement 2D ROPs.
libdrm
- Add support for RSX DRM to libdrm
Test Kernel Module and Program
- I uploaded here a test kernel module and a test user application: [12] and [13]
- I used a similar technique for mapping GPU resources into user-space like Linux kernel DRM drivers do it, e.g. Nouveau. But of course everything is very simplified in comparison with Nouveau driver. All GPU resources are mapped to user-space with mmap and there is no data copying between user and kernel space, for performance reasons. Mapping GPU resources into user-space like this is more flexible than IOCTLs.
- The purpose of the kernel module and the user application is to test how RSX works, to test FIFO commands and other stuff i reversed from Lv2. It's NOT for end users.
- Before loading the kernel module make sure ps3vram kernel module is NOT loaded.
- I used 64kB IO pages for GPU context. 4kB IO page size would be definitely a lot better for that we have to patch LV1. I will add this patch to my ps3mfw tasks for LV1.
- Just load the kernel module and then run the user application.
- The user application maps all context resources and executes some simple FIFO commands, like JMP or SET REF.
- I will add more examples later.
- By default, the kernel module allocates 8MB VRAM, 64kB FIFO and 1MB GART memory. You can change it by using kernel module parameters.
- Take a look at how i made non-contiguous allocated GART memory look contiguous to GPU, kernel-space and user-space.
- The kernel module needs some IOCTLs, e.g. for setting display buffers or flip status, because it can be done ONLY with LV1 calls. I will add it later.
Links
- 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: [14] [15]
- GEM: [16]
- TTM vs GEM: [17]
- OMAP DRM Driver: https://github.com/robclark/kernel-omap4/tree/omap_gpu-android/drivers/gpu/drm/omap
BD Drive
Crossreference: gitbrew.org::HV#BD Drive
Profile
- BD profile can be read with GET PROFILE device command or SCSI command GET CONFIGURATION
Profile Table
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
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
- Size is 32KB just like AACS specifications prescribes (See AACS Common Specification 3.2.5.2 Host Revocation List Record)
- We could replace HRL with an older one in BD drive flash and restore revoked Host Certificates !!!
Device Commands
Get Profile (0x11)
- BD profile can be read with LV1 call lv1_send_storage_device_command and command 0x11
- LV1 sends SCSI command GET CONFIGURATION to BD drive with requested type 0x0, starting feature number 0x0 and allocation length 0x8
- See SCSI command GET CONFIGURATION
Auto Request Sense Mode On/Off (0x30)
- LV1 expects a 4 byte value: 0x0 - On, 0x1 - Off
- can be get/set via GameOS sc0x25C/604: sys_storage_send_device_command(fd of bdvd,0x30,value,4,0,0 )
SCSI Commands
Get Configuration
Getting the profile of a BD movie disc:
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00 SCSI Status: Good Sense Information: sense buffer empty Received 8 bytes of data: 00 00 00 00 38 00 00 00 40 ...8...@ # 0x40 means BD-ROM
Getting the profile of a PS3 game disc:
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00 SCSI Status: Good Sense Information: sense buffer empty Received 8 bytes of data: 00 00 00 00 38 00 00 ff 71 ...8...q # 0x71 means PS3 BD-ROM
Get SS Key
- By SCSI standard undocumented parameters are used
- SCSI Report Key command with key format 0x3 and key class 0xe0
- 8 bytes are returned by BD drive
- Used by VSH
Test with PS3 game disc:
# sg_raw -r 8 /dev/sr0 a4 00 00 00 00 00 00 e0 00 08 03 00 SCSI Status: Good Sense Information: sense buffer empty Received 8 bytes of data: 00 00 06 00 00 00 00 00 04 ........
Eject Media
sg_raw /dev/sr0 0x1b 00 00 00 02 00
Load Media
sg_raw /dev/sr0 0x1b 00 00 00 03 00
Mode Select 10
Enable Buffer Write
- Uses PF 0x1, SP 0x0 and parameter list length 0x10
- Uses the following parameter list: 0x00 0x0e 0x00 0x00 0x00 0x00 0x00 0x00 0x2d 0x6 <buffer id> 0x00 0x00 0x00 0x00 0x00
- Enables writing to BD drive flash, e.g. to HRL buffer !!!
Test with sg3-utils which enables write to HRL buffer:
sg_raw /dev/sr0 55 10 00 00 00 00 00 00 10 00 00 0e 00 00 00 00 00 00 2d 06 04 00 00 00 00 00
Write Buffer
- Used e.g. by Update Manager to send BD firmware to BD drive
- Mode 0x5 (Download microcode and save) is used e.g. to write HRL to BD drive flash
- Mode 0x7 (Download microcode with offsets and save) is used e.g. to write BD firmware to BD drive flash
AACS
AACS SPU Module
- BD player on GameOS uses AacsModule.spu.isoself (/dev_flash/bdplayer) to perform AACS authentication
- Tested on OtherOS++ 3.55
- Host certificate, host private key and AACS LA public key are stored encrypted with AES-256-CTR in the SPU module and are decrypted when the SPU module is loaded or when it's accessed first. The AES-256-CTR key and IV are in the SPU module too.
- 4.76 uses new Host certificate
Communication
- BD player reads EID3 with Indi Info Manager 0x17001/0x17002 services and passes it to SPU module
- EID3 is NEVER used in the SPU module although BD player passes it to the SPU module
- Data is exchanged with the SPU module through SPU In Mbox, SPU Out Intr Mbox and a data buffer in XDR memory of size 0x2000 bytes.
Commands
- The SPU module supports max 0x78 (til 4.75, 0x57 since 4.76) commands but not all are implemented
- After a command is finished by the SPU module, it sends the status of the command to PPU through SPU Out Intr Mbox. Value 0 means success.
Command in FW | Name | Parameters | Info | |
---|---|---|---|---|
-4.75 | 4.76+ | |||
0x02 | 0x34 | Read 4 Bytes from XDR Buffer |
| |
0x1C | 0x48 | Set KCD |
| |
0x34 | 0x23 | Init AES_H |
| |
0x35 | 0x22 | Calculate AES_H 1 |
| |
0x21 | 2x 4 Bytes |
Signed CSS CheckCRL | ||
0x56 |
Get Random Seed | |||
0x32 |
Unknown | |||
0x36 | 0x24 | Calculate AES_H 2 |
| |
0x3C | 0x12 | Generate Host Nonce |
| |
0x3D | 0x0C | Get Host Nonce and Certificate |
| |
0x3E | 0x0D | Set Drive Nonce and Certificate |
| |
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 |
| |
0x48 | 0x1D | Calculate Volume ID MAC |
| |
0x49 | 0x15 | Verify Volume ID MAC |
| |
0x4A | 0x1A | Set PMSN |
| |
0x4B | 0x1B | Calculate PMSN MAC |
| |
0x4C | 0x16 | Verify PMSN |
| |
0x4D | 0x18 | Set Media ID |
| |
0x4E | 0x19 | Calculate Media ID MAC |
| |
0x4F | 0x17 | Verify Media ID MAC |
| |
0x55 | 0x1F | Verify Host/Drive Revocation |
| |
0x72 | 0x25 | OCRL related, Content Revocation List | ||
0x74 | 0x26 | OCRT related | ||
0x75 | 0x27 | OSIG related | ||
0xFEFEFEFF | 0xFEFEFEFF | Terminate Session |
|
Drive Revocation List (DRL)
- SHA1 hash is encrypted/decrypted by SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)
- SHA1 hash is read with VTRM service 0x2005 (Retrieve)
- SHA1 hash is written with VTRM service 0x2003 (Store With Update)
Content Revocation List (CRL)
- SHA1 hash is encrypted/decrypted by SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)
- SHA1 hash is read with VTRM service 0x2005 (Retrieve)
- SHA1 hash is written with VTRM service 0x2003 (Store With Update)
Host Revocation List (HRL)
- Stored in BD drive flash
- It can be read/written with SCSI commands READ/WRITE BUFFER. Yeah, it can be written too :D
Read HRL from BD Drive Flash
- It seems that BD drive has several HRL in its flash
- Empty HRL stored on BD drive flash can be read with SCSI command READ BUFFER by using as mode 0x2, buffer id 0x4 and allocation length 0x40
- Current HRL stored on BD drive flash can be read with SCSI command READ BUFFER by using as mode 0x2, buffer id 0x5
Empty HRL
# sg_read_buffer -m 2 -i 4 -o 0 -l $((0x40)) /dev/sr0 00 10 00 00 0c 00 03 10 03 00 00 00 01 21 00 00 34 10 00 00 00 00 00 00 00 00 1b 0b f2 6d 47 9e 77 62 20 3d 91 fc 78 b1 59 c9 52 ca a4 c7 41 85 24 96 64 30 8d 1d 95 8e 9b 84 c6 fa 4a dd 43 9b 42 98 fe ff # byte 0x21 at offset 0xc means Record Type HRL # as you see this HRL is empty
Current HRL
# sg_read_buffer -m 2 -i 5 -o 0 -l $((0x7c)) /dev/sr0 00 10 00 00 0c 00 04 10 03 00 00 00 09 21 00 00 6c 10 00 00 00 07 00 00 00 07 00 09 ff ff 00 00 00 0b 20 00 00 ff ff 00 00 00 16 00 08 ff ff 00 00 00 21 30 00 03 ff ff 00 00 00 35 00 04 ff ff 00 00 00 4e 40 00 03 ff ff 00 00 00 54 00 03 ff ff 00 00 00 5e 50 80 93 3a 62 f5 5a 9c 8c 62 ce 7d b8 69 5d d7 b1 60 c3 0f 36 ff 96 a2 3b 32 cb cd 58 d4 12 c9 fd bf 70 f5 16 a6 4a 32 ba 60 f0 5d 71 74 10 # the current HRL is NOT empty and is from MKBv9 because the only BD movie i played on my PS3 has MKBv9
PS3 BD Player Host Certificate
$ hexdump -C aacs_auth/ps3_host_cert.bin 00000000 02 01 00 5c ff ff 80 00 00 39 00 00 65 ea c9 87 |...\ÿÿ...9..eêÉ.| 00000010 8b 85 ef f4 d7 7a 62 b1 d6 00 02 4a ce 68 dd 33 |..ïô×zb±Ö..JÎhÝ3| 00000020 66 88 0e 4f 84 4f 34 b7 7a 05 01 35 a2 0e 73 b6 |f..O.O4·z..5¢.s¶| 00000030 26 da ea 51 57 b3 2e b8 4b c6 e8 7b 0d ee 4d 83 |&ÚêQW³.žKÆè{.îM.| 00000040 3c ea da 86 12 01 51 00 2c 3c 66 d5 25 6f 71 cf |<êÚ...Q.,<fÕ%oqÏ| 00000050 a6 8b 7e 55 ba 1b 35 1f 34 03 43 4e |Š.~Uº.5.4.CN| 0000005c # Host ID is 0xffff80000039
PS3 BD Player Host Private Key
$ hexdump -C aacs_auth/ps3_host_priv_key.bin 00000000 00 66 8c 9a 75 ee fc 8d a4 26 19 38 e2 71 28 50 |.f..u....&.8.q(P| 00000010 61 bb 09 f0 dd |a....|
AACS Processing Keys
MKB v1
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v1.inf =MKB= type: 0x00031003 version: 0x00000001 MKB U masks and UVs: 514 =applying subset-difference= index: 0 UV: 0x00000001 U mask: 0xff800000 V mask: 0xfffffffe =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 09 f9 11 02 9d 74 e3 5b d8 41 56 c5 63 56 88 c0 |.....t.[.AV.cV..| C value: 00000000: cb 06 90 db e6 54 55 7b 12 62 aa d7 89 f4 9d 92 |.....TU{.b......| media key: 00000000: b4 6c 48 5e f7 51 ae 29 ef 87 bc 58 28 f3 2a 8d |.lH^.Q.)...X(.*.| =MKB verify media key data= encrypted: 00000000: 46 32 5b 42 48 b4 86 5a fc ef 75 25 47 b1 b5 12 |F2[BH..Z..u%G...| decrypted: 00000000: 01 23 45 67 89 ab cd ef 0d ac 14 b9 ee f4 bd cc |.#Eg............|
MKB v3
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v3.inf =MKB= type: 0x00031003 version: 0x00000003 MKB U masks and UVs: 528 =applying subset-difference= index: 14 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: 0a b7 33 82 85 62 91 d1 91 4a 95 9e 36 18 c7 a1 |..3..b...J..6...| media key: 00000000: 6e da eb d4 88 aa 38 58 74 26 35 fd fd 36 66 d5 |n.....8Xt&5..6f.| =MKB verify media key data= encrypted: 00000000: 99 76 96 b0 6f 49 37 9b c4 b9 2b be 73 ce 96 1a |.v..oI7...+.s...| decrypted: 00000000: 01 23 45 67 89 ab cd ef fb 01 cc 85 eb e5 bf 0a |.#Eg............|
MKB v4
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v4.inf =MKB= type: 0x00031003 version: 0x00000004 MKB U masks and UVs: 526 =applying subset-difference= index: 12 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: bf 71 0c 8b 46 a0 24 d8 f0 3a a1 26 37 9d fb fc |.q..F.$..:.&7...| media key: 00000000: ef 18 c0 dd bf 02 32 a1 2f 57 f7 65 79 2c 1c 58 |......2./W.ey,.X| =MKB verify media key data= encrypted: 00000000: 54 85 08 a9 6a 70 2a c9 32 e3 74 a6 55 78 6c 01 |T...jp*.2.t.Uxl.| decrypted: 00000000: 01 23 45 67 89 ab cd ef da 90 cf 2a e5 b2 6c 45 |.#Eg.......*..lE|
MKB v7
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v7.inf =MKB= type: 0x00031003 version: 0x00000007 MKB U masks and UVs: 526 =applying subset-difference= index: 7 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: 21 fd c9 4b 3e 1a f3 fe 9e b4 7a e6 ef 01 75 1b |!..K>.....z...u.| media key: 00000000: af cd e2 c8 67 12 a4 b6 a8 58 0c 15 ef 07 6e f8 |....g....X....n.| =MKB verify media key data= encrypted: 00000000: 4b 21 29 a5 0f db 96 bc bc 01 04 71 42 79 00 e5 |K!)........qBy..| decrypted: 00000000: 01 23 45 67 89 ab cd ef 4e f9 d2 05 6e 19 c1 79 |.#Eg....N...n..y|
MKB v8
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v8.inf =MKB= type: 0x00031003 version: 0x00000008 MKB U masks and UVs: 523 =applying subset-difference= index: 4 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: 73 2d 10 bd f8 b4 87 e2 86 a6 d5 3a 6d db 69 15 |s-.........:m.i.| media key: 00000000: dd 46 d4 0d 26 54 5a ce 6c 59 0c 65 b7 2b 3a 9f |.F..&TZ.lY.e.+:.| =MKB verify media key data= encrypted: 00000000: c6 f6 f9 54 ce 90 e0 5e 2b 3b e4 1e 24 92 90 b2 |...T...^+;..$...| decrypted: 00000000: 01 23 45 67 89 ab cd ef 97 e6 61 8b d1 69 3e a0 |.#Eg......a..i>.|
MKB v9
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v9.inf =MKB= type: 0x00031003 version: 0x00000009 MKB U masks and UVs: 520 =applying subset-difference= index: 2 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: a4 5a c6 87 43 49 70 bb bf 0c 22 52 83 9e 2a f6 |.Z..CIp..."R..*.| media key: 00000000: 37 02 bd fc 96 dc a2 18 2e 55 b0 79 6d ad 36 6b |7........U.ym.6k| =MKB verify media key data= encrypted: 00000000: 4d 5b 7b 9c 5d ee 55 a6 94 de e1 db 8d 08 c7 a2 |M[{.].U.........| decrypted: 00000000: 01 23 45 67 89 ab cd ef cd 1d a8 8a 42 5a 10 43 |.#Eg........BZ.C|
MKB v10
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v10.inf =MKB= type: 0x00031003 version: 0x0000000a MKB U masks and UVs: 522 =applying subset-difference= index: 3 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: d4 77 dd 1a 8a 5c 6d d1 dd 31 2d af f7 d3 14 fa |.w...\m..1-.....| media key: 00000000: 38 32 2b 3c 61 b0 35 b4 52 89 84 59 f4 7a 76 e6 |82+<a.5.R..Y.zv.| =MKB verify media key data= encrypted: 00000000: 3f d3 d5 fb 42 37 d9 05 b8 db 6b 03 a0 fe 2e 48 |?...B7....k....H| decrypted: 00000000: 01 23 45 67 89 ab cd ef 65 b1 87 8c eb 0d 60 0f |.#Eg....e.....`.|
MKB v12
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v12.inf =MKB= type: 0x00031003 version: 0x0000000c MKB U masks and UVs: 522 =applying subset-difference= index: 3 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: 89 75 89 e6 6f 4a de 95 11 32 57 6a cb 99 dd 69 |.u..oJ...2Wj...i| media key: 00000000: 4b dd 69 9d 32 98 d7 b0 ad 32 71 6b 3d 9c e3 c2 |K.i.2....2qk=...| =MKB verify media key data= encrypted: 00000000: 8d 43 fd f2 15 fa 58 78 64 db 25 46 62 ab 02 30 |.C....Xxd.%Fb..0| decrypted: 00000000: 01 23 45 67 89 ab cd ef e6 1c bf 98 45 82 64 d9 |.#Eg........E.d.|
MKB v14
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v14.inf =MKB= type: 0x00031003 version: 0x0000000e MKB U masks and UVs: 526 =applying subset-difference= index: 6 UV: 0x00000248 U mask: 0xfffffe00 V mask: 0xfffffff0 =applying device key= index: 28 UV: 0x00000280 U mask: 0xfffffe00 V mask: 0xffffff00 device key: 00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...| processing key: 00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........| C value: 00000000: 8c 7e 31 e8 15 17 7e c3 2c 67 b7 cc 87 e9 39 c3 |.~1...~.,g....9.| media key: 00000000: 4b b1 31 d1 6e 0e 86 45 89 07 a2 68 91 c4 e5 38 |K.1.n..E...h...8| =MKB verify media key data= encrypted: 00000000: 20 03 8c 70 7d ab d0 6f ba 86 39 f0 31 26 86 5f | ..p}..o..9.1&._| decrypted: 00000000: 01 23 45 67 89 ab cd ef 27 9f e5 35 0b df 3d a5 |.#Eg....'..5..=.|
MKB v15
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v15.inf =MKB= type: 0x00031003 version: 0x0000000f MKB U masks and UVs: 527 =applying subset-difference= index: 6 UV: 0x00000248 U mask: 0xfffffe00 V mask: 0xfffffff0 =applying device key= index: 28 UV: 0x00000280 U mask: 0xfffffe00 V mask: 0xffffff00 device key: 00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...| processing key: 00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........| C value: 00000000: 75 da 59 cf 0d c2 c0 95 86 fc 6b 8e 2e e9 cc 85 |u.Y.......k.....| media key: 00000000: 28 46 25 38 3d cc 4f 1f 90 be 7d f7 8a ba 7b fd |(F%8=.O...}...{.| =MKB verify media key data= encrypted: 00000000: 8d d2 69 e0 b7 6a 44 53 03 ad ef 58 44 fc a7 d7 |..i..jDS...XD...| decrypted: 00000000: 01 23 45 67 89 ab cd ef ff 6a 7d c3 17 bb 19 11 |.#Eg.....j}.....|
MKB v16
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v16.inf =MKB= type: 0x00031003 version: 0x00000010 MKB U masks and UVs: 531 =applying subset-difference= index: 5 UV: 0x00000248 U mask: 0xfffffe00 V mask: 0xfffffff0 =applying device key= index: 28 UV: 0x00000280 U mask: 0xfffffe00 V mask: 0xffffff00 device key: 00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...| processing key: 00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........| C value: 00000000: f8 49 9b d1 32 f9 6e 8d 33 98 35 a8 54 80 d9 fe |.I..2.n.3.5.T...| media key: 00000000: 3a bf bf d7 7e b8 01 43 a9 3c 15 3f ba 47 8c e1 |:...~..C.<.?.G..| =MKB verify media key data= encrypted: 00000000: 8a 67 86 b6 9d 0d 22 dd 5d c2 88 1f 08 f3 ab b4 |.g....".].......| decrypted: 00000000: 01 23 45 67 89 ab cd ef d6 32 1f 17 c4 2f e2 4a |.#Eg.....2.../.J|
MKB v17
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v17.inf =MKB= type: 0x00031003 version: 0x00000011 MKB U masks and UVs: 540 =applying subset-difference= index: 14 UV: 0x00000308 U mask: 0xffffff00 V mask: 0xfffffff0 =applying device key= index: 21 UV: 0x00000340 U mask: 0xffffff00 V mask: 0xffffff80 device key: 00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss| processing key: 00000000: 46 5f a8 be 82 85 09 01 4d 05 d2 fc ce ff 35 d2 |F_......M.....5.| C value: 00000000: 01 f7 54 0b 34 e8 c1 ce 63 8d ea fa bc ce 6e 7b |..T.4...c.....n{| media key: 00000000: ef 63 4e a8 ca 06 d1 6a c7 21 65 1b 18 b3 04 c6 |.cN....j.!e.....| =MKB verify media key data= encrypted: 00000000: d3 b9 d4 9c b6 94 47 d5 3d cc 42 fe 3e 47 40 04 |......G.=.B.>G@.| decrypted: 00000000: 01 23 45 67 89 ab cd ef f6 b4 c8 6a b7 b8 39 fc |.#Eg.......j..9.|
MKB v18
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v18.inf =MKB= type: 0x00031003 version: 0x00000012 MKB U masks and UVs: 543 =applying subset-difference= index: 17 UV: 0x00000320 U mask: 0xffffff00 V mask: 0xffffffc0 =applying device key= index: 21 UV: 0x00000340 U mask: 0xffffff00 V mask: 0xffffff80 device key: 00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss| processing key: 00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..| C value: 00000000: 7a 8f 03 41 27 c4 86 58 05 37 3a 90 de f8 de 26 |z..A'..X.7:....&| media key: 00000000: e3 ed cd b4 59 b4 12 d4 ae f9 4d 8e 78 7a cd 7d |....Y.....M.xz.}| =MKB verify media key data= encrypted: 00000000: ea 45 fa 35 65 70 56 6f 6a 86 65 ad 52 e7 71 a4 |.E.5epVoj.e.R.q.| decrypted: 00000000: 01 23 45 67 89 ab cd ef bd 36 f9 ce 60 54 80 3c |.#Eg.....6..`T.<|
MKB v19
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v19.inf =MKB= type: 0x00031003 version: 0x00000013 MKB U masks and UVs: 544 =applying subset-difference= index: 17 UV: 0x00000320 U mask: 0xffffff00 V mask: 0xffffffc0 =applying device key= index: 21 UV: 0x00000340 U mask: 0xffffff00 V mask: 0xffffff80 device key: 00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss| processing key: 00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..| C value: 00000000: b9 0b 55 d1 18 3c cc 80 20 1c 9f 26 c3 58 27 18 |..U..<.. ..&.X'.| media key: 00000000: 75 a9 79 9c 67 50 13 89 98 62 34 5b eb 54 34 dd |u.y.gP...b4[.T4.| =MKB verify media key data= encrypted: 00000000: c4 f0 ce 75 1b 12 b9 f0 22 2f 31 70 66 a9 6a b8 |...u...."/1pf.j.| decrypted: 00000000: 01 23 45 67 89 ab cd ef 66 5c 65 d3 c4 4c c7 b0 |.#Eg....f\e..L..|
MKB v20
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v20.inf =MKB= type: 0x00031003 version: 0x00000014 MKB U masks and UVs: 544 =applying subset-difference= index: 19 UV: 0x00000384 U mask: 0xffffff80 V mask: 0xfffffff8 =applying device key= index: 18 UV: 0x00000384 U mask: 0xffffff80 V mask: 0xfffffff8 device key: 00000000: fb 4a c3 90 09 e8 21 13 d4 5e cf 4b 7e ae a4 67 |.J....!..^.K~..g| processing key: 00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k| C value: 00000000: 10 9f f1 69 36 07 7d 7e ad 8f d2 1a 28 c5 09 ed |...i6.}~....(...| media key: 00000000: dc 9f 08 f7 cb 1b f8 c4 cf 96 4e 96 df 23 56 58 |..........N..#VX| =MKB verify media key data= encrypted: 00000000: 18 ca f5 51 8f 36 ef 2f 7a 49 78 ff 54 40 a5 f1 |...Q.6./zIx.T@..| decrypted: 00000000: 01 23 45 67 89 ab cd ef c5 5d 11 08 c3 26 db 48 |.#Eg.....]...&.H|
MKB v21
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v21.inf =MKB= type: 0x00031003 version: 0x00000015 MKB U masks and UVs: 552 =applying subset-difference= index: 19 UV: 0x00000384 U mask: 0xffffff80 V mask: 0xfffffff8 =applying device key= index: 18 UV: 0x00000384 U mask: 0xffffff80 V mask: 0xfffffff8 device key: 00000000: fb 4a c3 90 09 e8 21 13 d4 5e cf 4b 7e ae a4 67 |.J....!..^.K~..g| processing key: 00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k| C value: 00000000: c0 0c fa bf f0 fe f2 32 77 19 db c4 d8 f8 60 c9 |.......2w.....`.| media key: 00000000: 55 83 aa 69 ff 52 16 83 c2 93 b3 48 03 2a 57 38 |U..i.R.....H.*W8| =MKB verify media key data= encrypted: 00000000: 12 5b f2 75 c8 f8 05 6b 4f 31 a5 ea 4a 12 9f a9 |.[.u...kO1..J...| decrypted: 00000000: 01 23 45 67 89 ab cd ef dc 46 45 b4 79 8d 4f 68 |.#Eg.....FE.y.Oh|
MKB v23
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v23.inf =MKB= type: 0x00031003 version: 0x00000017 MKB U masks and UVs: 556 =applying subset-difference= index: 17 UV: 0x00000384 U mask: 0xffffffe0 V mask: 0xfffffff8 =applying device key= index: 7 UV: 0x00000384 U mask: 0xffffffe0 V mask: 0xfffffff8 device key: 00000000: 8b f4 fb d9 1a 7f b7 db 85 76 d1 e5 a1 5a 85 44 |.........v...Z.D| processing key: 00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr| C value: 00000000: f0 81 d4 93 aa b5 01 1a a7 ff 8e 18 8a 48 8a 2d |.............H.-| media key: 00000000: 02 04 59 d0 7c b5 54 94 bf 46 9b 98 91 1e 43 1f |..Y.|.T..F....C.| =MKB verify media key data= encrypted: 00000000: 24 a1 27 f9 30 70 25 67 07 2f 2a d4 13 89 0d aa |$.'.0p%g./*.....| decrypted: 00000000: 01 23 45 67 89 ab cd ef 21 00 20 84 c4 5f 36 78 |.#Eg....!. .._6x|
MKB v25
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v25.inf =MKB= type: 0x00031003 version: 0x00000019 MKB U masks and UVs: 564 =applying subset-difference= index: 13 UV: 0x00000384 U mask: 0xffffffe0 V mask: 0xfffffff8 =applying device key= index: 7 UV: 0x00000384 U mask: 0xffffffe0 V mask: 0xfffffff8 device key: 00000000: 8b f4 fb d9 1a 7f b7 db 85 76 d1 e5 a1 5a 85 44 |.........v...Z.D| processing key: 00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr| C value: 00000000: 19 62 23 7d 81 01 c2 55 2f 36 20 1b 3e 69 40 10 |.b#}...U/6 .>i@.| media key: 00000000: 70 b5 9f 35 86 5d 18 73 bb 80 c3 2b f7 41 f6 14 |p..5.].s...+.A..| =MKB verify media key data= encrypted: 00000000: 89 be 1e 1e b1 93 4c f2 2d ac c3 ce ed 10 07 f0 |......L.-.......| decrypted: 00000000: 01 23 45 67 89 ab cd ef 3f 5d 87 7a 88 09 db c4 |.#Eg....?].z....|
Documentation
- SCSI Specification: [18]
- AACS Specification Common: [19]
- AACS Specification Pre-recorded Video Book [20]
- AACS Tutorial: [21]
- AACS Overview: [22]
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:
71 ED 3F A3 DA FE E4 94 40 8C
Clear text:
F4 10 45 A3 E2
PS3 DVD Player Key Index
0x69
Documentation
- The Content Scrambling System (CSS): [23]
- Cryptanalysis of Contents Scrambling System: [24]
- Cryptography in Home Entertainment: [25]
- Patching DVD Firmware: [26]
CPRM
Commands
- The SPU module supports 0x13 commands.
4C Secret Constant (S-Box)
Documentation
- Cryptomeria C2 Specification: [27]
- Cryptoanalysis of C2: [28]
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 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
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
Decrypted P-Block (and EID4) contains region settings (see below)
In decrypted P-Block(bytes 0x30 and 0x32) and in EID4(first byte) these bytes match Product Code:
Hex | bitflag | Product Code | Console Type | Remarks |
---|---|---|---|---|
0xFF | 11111111 | 0x80 | NOT IN USE | No BD playback on that Product Code |
0xFF | 11111111 | 0x81 | TOOL Reference Tool or SD System Debugger / DECR | No BD playback on that Product Code |
0xFF | 11111111 | 0x82 | DEX AV TEST DTCP-IP Debug / AV Tool / DTCP-IP Debugger / DECH / DECHS | No BD playback on that Product Code |
0x01 | 00000001 | 0x83 | CEX Retail or SHOP Kiosk - Japan / CECH | bit 0 (Region 0: Japan?) |
0x02 | 00000010 | 0x84 | CEX Retail or SHOP Kiosk - USA / CECH | bit 1 (Region 1: USA & Canada, Bermuda, and US Territories) |
0x04 | 00000100 | 0x85 | CEX Retail or SHOP Kiosk - Europe / CECH | bit 2 (Region 2: Europe (with the exceptions of Russia, Ukraine, Belarus), South Africa, Swaziland, Middle East, Egypt, Lesotho, and Greenland) |
0x10 | 00010000 | 0x86 | CEX Retail or SHOP Kiosk - Korea / CECH | bit 4 (Region 3: Southeastern Asia) |
0x04 | 00000100 | 0x87 | CEX Retail or SHOP Kiosk - United Kingdom / CECH | bit 2 (Region 2: Europe (with the exceptions of Russia, Ukraine, Belarus), South Africa, Swaziland, Middle East, Egypt, Lesotho, and Greenland) |
0x08 | 00001000 | 0x88 | CEX Retail or SHOP Kiosk - Mexico / CECH | bit 3 (Region 4: Latin America and Australia) |
0x08 | 00001000 | 0x89 | CEX Retail or SHOP Kiosk - Australia & New Zealand / CECH | bit 3 (Region 4: Latin America and Australia) |
0x20 | 00100000 | 0x8A | CEX Retail or SHOP Kiosk - South Asia / CECH | bit 5 (Region 5: Russia, Asia (non-southeast), and Africa) |
0x10 | 00010000 | 0x8B | CEX Retail or SHOP Kiosk - Taiwan / CECH | bit 4 (Region 3: Southeastern Asia) |
0x20 | 00100000 | 0x8C | CEX Retail or SHOP Kiosk - Russia / CECH | bit 5 (Region 5: Russia, Asia (non-southeast), and Africa) |
0x40 | 01000000 | 0x8D | CEX Retail or SHOP Kiosk - China / CECH | bit 6? (Region 6: China) |
0x10 | 00010000 | 0x8E | CEX Retail or SHOP Kiosk - Hong Kong / CECH | bit 4 (Region 3: Southeastern Asia) |
0x08 | 00001000 | 0x8F | CEX Retail or SHOP Kiosk - Brazil / CECH | bit 3 (Region 4: Latin America and Australia) |
0xFF | 11111111 | 0xA0 | ARC Arcade / GECR | No BD playback on that Product Code |
Creating
Sending to BD Drive
S-Block
Creating
Sending to BD Drive
HRL
Empty HRL
Sending to BD Drive
VTRM
Crossreference: gitbrew.org::HV#VTRM
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:
# mount dev_flash3 glevand@debian-hdd:~/ps3dm-utils$ sudo mount /dev/ps3vflashe /mnt # DRL SHA1 hash glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/drl/DRL1 8f0652bc6162a240362f90f1b2e5405bb82ee502 /mnt/data-revoke/drl/DRL1 # CRL SHA1 hash glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/crl/CRL1 96791f41f9a76f4d895dd5820db108ec03d19250 /mnt/data-revoke/crl/CRL1 # Retrieve DRL and CRL SHA1 hashes from VTRM # DRL hash is first and then follows CRL hash glevand@debian-hdd:~/ps3dm-utils$ sudo ./ps3dm_vtrm -l 0x0 -p 0x1070000034000001 /dev/ps3dmproxy retrieve 0 0x8f 0x06 0x52 0xbc 0x61 0x62 0xa2 0x40 0x36 0x2f 0x90 0xf1 0xb2 0xe5 0x40 0x5b 0xb8 0x2e 0xe5 0x02 0x96 0x79 0x1f 0x41 0xf9 0xa7 0x6f 0x4d 0x89 0x5d 0xd5 0x82 0x0d 0xb1 0x08 0xec 0x03 0xd1 0x92 0x50 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Backup Flash (0x2012)
- Requires enabled product mode or else service returns always error 0x5
- Reads and returns data from NOR flash beginning at NOR flash offset 0xec0000
Test with ps3dm-utils:
# enable product mode # ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0 /dev/ps3dmproxy: SS retval 0 # ps3dm_vtrm /dev/ps3dmproxy backup_flash 0 0x200 | hexdump -C 00000000 53 43 45 49 ff ff ff ff ff ff ff ff ff ff ff ff |SCEIÿÿÿÿÿÿÿÿÿÿÿÿ| 00000010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ| * 00000200 # dd if=/dev/ps3nflasha bs=1 count=$((0x100)) skip=$((0xec0000)) | hexdump -C 00000000 53 43 45 49 ff ff ff ff ff ff ff ff ff ff ff ff |SCEIÿÿÿÿÿÿÿÿÿÿÿÿ| 00000010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ| *
Flash Address Size (0x2016)
- Requires enabled product mode or else service returns always error 0x5
- Returns 2 64bit values: offset and size of NOR flash region
Test with ps3dm-utils:
# ps3dm_um /dev/ps3dmproxy read_eprom 0x48c07 0xff # ps3dm_vtrm /dev/ps3dmproxy flash_addr_size /dev/ps3dmproxy: SS retval 5 # enable product mode # ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0 /dev/ps3dmproxy: SS retval 0 # ps3dm_um /dev/ps3dmproxy read_eprom 0x48c07 0x00 # ps3dm_vtrm /dev/ps3dmproxy flash_addr_size 0x0000000000000000 0x0000000000040000
Revoke List
Crossreference: gitbrew::Revoke List
LPAR 1 System Call 0x1004A
- Installs new revoke list in LV1
- LPAR 1 processes can use this syscall to install new revoke lists at runtime
- lv2ldr is loaded by LV1 and used to verify the passed revoke list
- After lv2ldr is done verifying the passed revoke list, it checks for stop code and if it's 0xB then LV1 replaces the old revoke list with the new one
- If the verification of the revoke list was successfull then LV1 installs new revoke list and replaces the old one in the ISO loader table at address 0x10100
rvk_list_verifier
- Stop code 0xB means that the passed revoke list is valid.
root@debian-hdd:/home/glevand/rvk_list_verifier# cat /proc/rvk_list_verifier/debug PPE id (0x0000000000000001) VAS id (0x0000000000000002) lv1_construct_logical_spe (0x00000000) SPE id (0x0000000000000033) lv1_enable_logical_spe (0x00000000) lv1_set_spe_interrupt_mask(0) (0x00000000) lv1_set_spe_interrupt_mask(1) (0x00000000) lv1_set_spe_interrupt_mask(2) (0x00000000) lv1_set_spe_privilege_state_area_1_register (0x00000000) ea (0xc000000003f40000) esid (0xc000000008000000) vsid (0x0000408f92c94500) lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) sleep lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) out interrupt mbox (0x0000000000000001) lv1_clear_spe_interrupt_status(2) (0x00000000) transferring ldr args to LS waiting until MFC transfers are finished MFC transfers done out mbox (0x00000001) sleep lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) problem status (0x000b0082) lv1_destruct_logical_spe (0x00000000)