Editing Hypervisor Reverse Engineering

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

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

Latest revision Your text
Line 1: Line 1:
<span style="background:red; color:#ffffff;">Warning, this page is way too long and is voted to be split into seperate sections</span>
[[Category:Software]]
<span style="background:red; color:#ffffff;">Warning, this page way too long and voted to be split into seperate sections</span>


----
----
Line 19: Line 20:
LPAR = Logical Partition  
LPAR = Logical Partition  


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


0x24 - ?
0x24 - _unmap_pages


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


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


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


0x10001 - query_logical_partition_address_region_info
0x10001 - lpar_query_address_region_info


0x10002 - translate_logical_partition_to_physical_address(LPAR id, LPAR address, physical addr)  
0x10002 - lpar_memory_addr_to_phys_addr(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 320: Line 290:


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


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


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


0x10033 - get_remote_file_name
0x10032 - accesses system console


0x10034 - allocate_cp_channel
0x10034 - ?


0x10035 - release_cp_channel
0x10035 - ?


0x10036 - power_down
0x10036 - accesses system console


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


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


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


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


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


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


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


0x1004B - disable_spe_loading  
0x1004B - disable_spe_loading  
0x1004C - install_access_control_table?
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  


0x1007F - pause
0x10081 - accesses system console
 
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 415: Line 351:
0x100C2 - modify_repository_node_value(LPAR id)  
0x100C2 - modify_repository_node_value(LPAR id)  


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


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


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


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


= PThread  =
= PThread  =
Line 671: Line 591:
== Member variables  ==
== Member variables  ==


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


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


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


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


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


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


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


= ProtectionPage  =
= ProtectionPage  =
Line 840: Line 760:
=== vtable  ===
=== vtable  ===


0x003569F8 (3.15)
0x003569F8 (3.15)  


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


0x352308 (3.15)
0x000x352308 (3.15)  


=== Member variables  ===
=== Member variables  ===
Line 1,705: Line 1,625:


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


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


Line 1,836: Line 1,756:


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


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


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


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


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


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


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


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


Line 3,593: Line 3,503:
=== appldr  ===
=== appldr  ===


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


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


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


Line 3,989: Line 3,899:
offset 0x90 - LPAR image path  
offset 0x90 - LPAR image path  


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


=== Types of System Manager  ===
=== Types of System Manager  ===
Line 4,449: Line 4,359:
| 0xA  
| 0xA  
| 0x1B6  
| 0x1B6  
| Makes a triple beep
| Makes a double beep
|-
|-
| 0x29  
| 0x29  
Line 4,461: Line 4,371:
| 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,656: Line 4,565:
| 0x8000  
| 0x8000  
| 8  
| 8  
| 0x8001 - 0x8005
|  
| [[Updater_Frontend|Updater Frontend]]
|  
|-
|-
| 0x9000  
| 0x9000  
Line 4,666: Line 4,575:
| 0x10000  
| 0x10000  
| 0x23  
| 0x23  
| 0x10001-0x10007
| -  
| [[SB_Manager|SBM (South Bridge Manager)]]
| -
|-
|-
| 0x11000  
| 0x11000  
Line 4,702: Line 4,611:
| 0x16  
| 0x16  
| 0x22001 - 0x22004
| 0x22001 - 0x22004
| [[Factory_Data_Manager|Factory Data Manager]]
|  
|-
|-
| 0x24000  
| 0x24000  
Line 4,740: Line 4,649:
     uint32_t retval;
     uint32_t retval;
     uint8_t res[4];
     uint8_t res[4];
     uint64_t laid;            /* LPAR Authority ID */
     uint64_t laid;            /* LPAR authority id */
     uint64_t paid;            /* Program Authority ID */
     uint64_t paid;            /* Program authority id */
}
}
</pre>
</pre>
Line 4,772: Line 4,681:
*The size of the body depends on a used service.
*The size of the body depends on a used service.


= LPAR Memory Management =
== 0x9000 - SC Manager ==


== Memory Region class  ==
*SC Manager cannot be accessed directly by using DM unfortunately (DM discards all requests) but it's used by other services that are accessable through DM
*E.g. Update Manager services "Read EEPROM" and "Write EEPROM" send requests to SC Manager services "Read EEPROM" and "Write EEPROM"
*SC Manager runs '''sc_iso.self'''
* With full HV rights you could patch Dispatcher Manager and enable access to SC Manager from GameOS.


This class is the base class for different memory region types.
{| class="wikitable FCK__ShowTableBorders"
 
|-
=== vtable  ===
! Packet ID
 
! Description
0x003578B0 (3.15)
|-
 
| 0x9001
=== Member variables  ===
| Get SRH
|-
| 0x9002
| Set SRH
|-
| 0x9003
| Encrypt
|-
| 0x9004
| Decrypt
|-
| 0x9005
| Init For VTRM
|-
| 0x9006
| Get Region Data
|-
| 0x9007
| Set Region Data
|-
| 0x9008
| Set RTC
|-
| 0x9009
| Get Time
|-
| 0x900A
| Set Time
|-
| 0x900B
| Read EPROM
|-
| 0x900C
| Write EPROM
|-
| 0x900D
| Init For Updater
|-
| 0x900E
| Get SC Status
|-
| 0x9011
| SC Binary Patch
|-
| 0x9012
| SC RTC Factory
|-
| 0x9013
| Correct RTC Factory
|-
| 0x9014
| Set SC Status
|-
| 0x9015
| Backup Root Info
|-
| 0x9016
| Restore Root Info
|}


offset 0x40 - pointer to LPAR object that owns this memory region
=== 0x9001 - SC Get SRH  ===


offset 0x48 - type of memory region (8 bytes)
<pre>
struct ss_sc_mgr_get_srh
{
    u8 field0[20];
    u8 res1[4];
    u8 field18[20];
    u8 res2[4];
};
</pre>


offset 0x50 - LPAR start address of memory region
=== 0x9003 - SC Encrypt  ===


offset 0x58 - size of memory region (8 bytes)
*There are 5 different types/kinds of encryption: 1 - 5.


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


offset 0xA0 - log2 of page size
=== 0x9004 - SC Decrypt  ===


=== Generating New LPAR Memory Region Addresses ===
*There are 5 different types/kinds of decryption: 1 - 5.
*'''Virtual TRM Decrypt Master (0x200E)''' service uses e.g. decryption type 4.


generate_new_lpar_mem_region_address(?, memory region size, log2(page size), ?, ?) - 002C82E8 (3.15)
=== 0x9006 - SC Get Region Data  ===


generate_new_lpar_mem_region_address - 002C6570 (3.41)
*This service expects an ID. The valid range of ID is 0 - 15.
*E.g. Update Manager uses this service to retrieve hash and version of some SELFs and firmwares, e.g. '''lv0''' and '''lv1'''.


*The function returns a new LPAR memory region address.
<pre>
*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'''.
struct ss_sc_mgr_get_region_data
{
    u64 id;
    u64 data_size;    /* max 0x30 bytes */
    u8 data[0];
};
</pre>


==== Encoding LPAR Memory Region Start Addresses and Sizes ====
==== Update Package Type - ID Mapping Table ====


*Size of LPAR memory region is encoded in the LPAR memory region start address.
{| class="wikitable FCK__ShowTableBorders"
*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.
! Update Package Type
*Before incrementing, the counter is shifted left by '''log2(LPAR Memory Region Size)''' and ored with '''log2(LPAR Memory Region Size) << 42'''.
! ID
|-
| 1
| 0
|-
| 2
| 2
|-
| 3
| 4
|-
| 4
| 6
|-
| 5
| 7
|-
| 6
| 8
|}


  LPAR Memory Region Start Address >> 42 = log2(LPAR Memory Region Size)
=== 0x9007 - SC Set Region Data ===


LPAR Memory Region Start Address = (log2(LPAR Memory Region Size) << 42) |
*This service expects an ID. The valid range of ID is 0 - 15.
    (counter << log2(LPAR Memory Region Size))
*E.g. Update Manager uses this service to store hash and version of some SELFs and firmwares, e.g. '''lv0''' and '''lv1'''.


===== LPAR Memory Region Address Counter =====
<pre>
struct ss_sc_mgr_set_region_data
{
    u64 id;
    u64 data_size;    /* max 0x30 bytes */
    u8 data[0];
};
</pre>
 
=== 0x900B - SC Read EPROM  ===
 
* There are 2 ways to access SC EPROM: '''NVS Service''' and '''Device Access Service'''.
* '''NVS Service''' uses '''Block ID''' and '''Block Offset'''.
* Not all EPROM offsets can be accessed through SC Manager.
 
<pre>
struct ss_sc_mgr_read_eprom
{
    u32 offset;
    u8 res1[4];
    u32 nread;              /* max 0x100 bytes */
    u8 res2[4];
    u64 buf_size;
    u8 buf[0];
    /* here follows buf */
};
</pre>
 
==== EPROM Offset - Block ID and Block Offset Mapping Table (NVS Service) ====
 
{| class="wikitable FCK__ShowTableBorders"
|-
! EPROM Offset
! Block ID
! Block Offset
! Notes
|-
| 0x48000 - 0x480FF
| 0x00
| 0x48000 - 0x480FF
|
|-
| 0x48800 - 0x488FF
| 0x01
| 0x48800 - 0x488FF
|
|-
| 0x48C00 - 0x48CFF
| 0x02
| 0x48C00 - 0x48CFF
|
|-
| 0x48D00 - 0x48DFF
| 0x03
| 0x48D00 - 0x48DFF
|
|-
| 0x2F00 - 0x2FFF
| 0x10
| 0x2F00 - 0x2FFF
| "Industry Area" aka OS Version Area
|-
| 0x3000 - 0x30FF
| 0x20
| 0x3000 - 0x30FF
| "CS Area"
|-
| All other offsets
| Invalid
| Invalid
|}


*LPAR Memory Region Address Counter is stored at address: '''0x38(LPAR ptr) + 0x9E8'''
=== 0x900C - SC Write EPROM  ===
*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  ==
<pre>
struct ss_sc_mgr_write_eprom
{
    u32 offset;
    u8 res1[4];
    u32 nwrite;
    u8 res2[4];
    u64 buf_size;
    u8 buf[0];
    /* here follows buf */
};
</pre>


This type of memory region is created e.g. in '''lv1_allocate_memory''' HV call or in '''syscall 0x10000'''.
=== 0x900E - SC Get Status ===


=== vtable  ===
Here is what the service returned on my fat PS3:
<pre>
0x00 0x00 0x00 0x03 0x00 0x00 0x00 0x00 0xC0 0x00 0x00 0xFF 0x00 0x00 0x00 0x00
</pre>


0x00357D08 (3.15)
So, '''version''' is '''0x00000003''' and '''mode''' is '''0xC00000FF'''.


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


offset 0xB0 - pointer to object that stores a list of addresses of physical pages owned by this memory region
=== 0x9011 - SC Binary Patch  ===


offset 0xB8 - pointer to LPAR object that owns this memory region
*This service is used by Update Manager to send a new SC firmware version to SYSCON.


offset 0xC0 - reference counter (8 bytes)
==== SC Isolation DMA Buffer Header  ====
<pre>struct sc_iso_header
{
    u32 seqno;
    u32 mbmsg;
    u32 cmd;
    u32 cmd_size;
    u8 cmd_data[0];
};
</pre>


=== Objects ===
== 0x11000 - SPM (Security Policy Manager) ==


Here is the list of physical memory region objects i found in HV 3.15.
*Packet ID is mapped to '''SS id'''
*SS id value range is 0x0 - 0x84


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Address in HV dump
! Packet ID
! LPAR id
! Description
! LPAR Start Address
|-
! Size
| 0x11001
! Flags
| Request
! log2(Page Size)
! Physical Page Addresses
|-
|-
| 0x006B5510
| 0x11002
| 1
| Load Additional Policy
| 0x300000001000
|}
| 0x1000
 
| 0x0
== 0x14000 - SLL (Secure LPAR Loader)  ==
| 0xC
 
| 0x672000
*SLL opens '''lv2_kernel.self''', parses ELF header and determines the size of initial memory region for GameOS LPAR
*SLL creates a memory region for GameOS LPAR by using '''syscall 0x10000'''.
*SLL opens '''/proc/partitions/&lt;LPAR id&gt;/mem''' file and maps it with mmap syscall into it's address space.
*Then it authenticates, decrypts and copies the SELF file of GameOS to LPAR's memory region by using '''SPE syscalls 0x10040 and 0x10042'''.
*Linux is not loaded by SLL, it's loaded in Process 9 by Linux System Manager
*GameOS file image '''lv2_kernel.self''' is stored on '''/dev/rflash1'''
 
{| class="wikitable FCK__ShowTableBorders"
|-
! Packet ID
! Description
|-
|-
| 0x006B5E50
| 0x14004
| 1
| Load GOS
| 0x440000040000
| 0x20000
| 0x0
| 0x11
| 0x6C0000
|-
|-
| 0x006B6980
| 0x14005
| 1
| Unload GOS
| 0x440000060000
|}
| 0x20000
 
| 0x0
== 0x15000 - SPL (Secure Profile Loader)  ==
| 0x11
 
| 0x6E0000
*DEFAULT.SPP file is stored on '''/dev/rflash1'''
 
{| class="wikitable FCK__ShowTableBorders"
|-
|-
| 0x006B7F00
! Packet ID
| 1
! Description
| 0x400000040000
| 0x10000
| 0x0
| 0x10
| 0x100000
|-
|-
| 0x003A80F0
| 0x15001
| 2
| Get LPAR Parameter Size/Get LPAR Parameter
| 0x6C0058000000
| 0x7000000
| 0x4
| 0x18
| 0x1000000 - 0x7000000
|-
|-
| 0x003BE800
| 0x15003
| 2
| Get Contents Size/Get Contents
| 0x300000047000
| 0x1000
| 0x0
| 0xC
| 0x1FA000
|-
|-
| 0x006BDAA0
| 0x15009
| 2
| Get Component
| 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.
=== SPP File  ===
 
*The file is encrypted but can be read by using 0x15003 service of SPL
*SPL reads SPP file, parses SPP header and checks some fields
*SPP file is verified and decrypted by SPU module '''spp_verifier.self''' that cab be executed with HV SPE calls
*Even old default.spp from PS3 Firmware 1.10 can be decrypted with spp_verifier.self from PS3 Firmware 3.41
*Header format version should be '''5''' or else the header check fails
*If (SPP header size&nbsp;% 256&nbsp;!= 0) then header check fails
*'''Finally i was able to decrypt profile file from 3.41 but by using SPE HV calls only&nbsp;!!! And Linux Manager is still there&nbsp;!!!'''
*The decrypted file is a binary file


=== GameOS Physical Memory Regions  ===
Here are the contents of [[Default.spp#3.56_RETAIL.2FCEX]] from 3.55. <br />
Here are the contents of [[Default.spp#3.55_RETAIL.2FCEX]] from 3.55. <br />
Here are the contents of [[Default.spp#3.41_RETAIL.2FCEX]] from 3.41. <br />
Here are the contents of [[Default.spp#3.15_RETAIL.2FCEX]] from 3.15. <br />
Here are the contents of [[Default.spp#1.00_DEBUG.2FDEX]] from 1.00 Debug Firmware. <br />


*GameOS allocates nearly all physical memory of PS3 for itself&nbsp;!!! That is why new HV calls '''lv1_allocate_memory''' with large memory region sizes will fail.
==== SPP Header  ====
*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.
 
offset 0x2 - header format version (2 bytes)
 
offset 0x4 - header size (4 bytes)
 
offset 0x18 - number of segments (4 bytes)
 
==== Segments  ====
 
*Segments follow after the header
*SPP file contains several segments.


Here is the list of physical memory regions of GameOS i found in HV 3.41:  
Here is the list of profile segments from 3.41:  


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Start Address
! Name
! Size
! auth id/authority id
! Access Right
|-
! Max Page Size
|*SCE_CELLOS_PME               
! Flags
|0x1070000001000001
! Real Addresses
|-
|*PS3_LPAR                     
|0x1070000002000001
|-
|*PS2_LPAR                     
|0x1020000003000001
|-
|*PS2_GX_LPAR                   
|0x1020000003000001
|-
|*PS2_SW_LPAR                   
|0x1020000003000001
|-
|*LINUX_LPAR                   
|0x1080000004000001
|-
|*SCE_CELLOS_SYSTEM_MGR         
|0x107000001D000001
|-
|*SCE_CELLOS_SYSTEM_MGR_LINUX   
|0x107000001D000001
|-
|*SCE_CELLOS_SYSTEM_MGR_PS2     
|0x107000001D000001
|-
|*SCE_CELLOS_SYSTEM_MGR_PS2_SW 
|0x107000001D000001
|-
|*SCE_CELLOS_SYSTEM_MGR_PS2_GX 
|0x107000001D000001
|-
|-
| 0x0
|*SCE_CELLOS_SS_SECURE_RTC     
| 0x1000000
|0x1070000033000001
| 0x3
| 0x18
| 0x8
| 0x1000000 - 0x1FFF000
|-
|-
| 0x500000300000
|*SCE_CELLOS_SS_INDI_INFO_EID
| 0xA0000
|
| 0x3
| 0x10
| 0x8
| 0x380000 - 0x38F000, 0x3B0000 - 0x3BF000, 0x1E0000 - 0x1FF000, 0x3C0000 - 0x3FF000, 0xFF00000 - 0xFF1F000
|-
|-
| 0x700020000000
|*SCE_CELLOS_SS_INIT_LV1_ACL   
| 0xE900000 (huge memory region)
|0x1070000017000001
| 0x3
| 0x14
| 0x0
| 0x400000 - 0x5FF000, 0x800000 - 0xFFF000, 0x2000000 - 0xFEFF000
|}
|}


== HTAB Memory Region class  ==
== 0x15003 - Get Contents Size/Get Contents ==
 
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  ===
*This service provides the contents of a segment specified by a service requester
*I have got access to this service through DM but couldn't get through access policy yet, the service returns error code 0x00000005 that means '''Access Violation'''
*But i still could test with this service which segment names are valid
*I need valid '''laid''' and '''paid''' to get through it


Here is the list of HTAB memory region objects i found in HV 3.15.
== 0x17000 - Indi Info Manager  ==


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Address in HV dump
! Packet ID
! LPAR id
! Description
! VAS id
! LPAR Start Address
! Size
! Flags
! log2(Page Size)
|-
|-
| 0x001FE0F0
| 0x17001
| 2
| Read EID Data Size By Index/Read metldr Size
| 3
|-
| 0x500000C00000
| 0x17002
| 0x100000
| Read EID Data By Index/Read metldr
| 0xC000000000000000
|-
| 0x14
| 0x17003
| Read ID Data
|-
| 0x17004
| Read System Data
|-
| 0x17005
| Write System Data?
|-
| 0x17006
| Write smth?
|-
| 0x17007
| Read System Data From EEPROM
|-
| 0x17008
| not implemented
|-
| 0x17009
| unknown
|-
| 0x1700A
| not implemented
|-
| 0x1700B
| not implemented
|-
| 0x1700C
| not implemented
|-
| 0x1700D
| not implemented
|-
| 0x1700E
| not implemented
|-
| 0x1700F
| not implemented
|-
| 0x17010
| unknown
|-
| 0x17011
| unknown
|-
| 0x17012
| unknown
|-
| 0x17013
| Read eEID Size
|-
| 0x17014
| Write eEID/Write metldr
|-
| 0x17015
| Read cISD Size
|-
|-
| 0x003BD850
| 0x17016
| 2
| Read cISD
| 3
| 0x500004300000
| 0x100000
| 0xC000000000000000
| 0x14
|-
|-
| 0x003BDEA0
| 0x17017
| 2
| Write cISD
| 3
| 0x500004500000
| 0x100000
| 0xC000000000000000
| 0x14
|}
|}


=== GameOS HTAB  ===
*Indi Info Manager is accessed e.g. in '''syscall 868''' on GameOS


*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'''
=== 0x17001 - Read EID Data Size By Index  ===
*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  ===
*I have got access to this service through DM and tested it
*This service is used e.g. by Update Manager, User Token Manager or Storage Manager
*The service expects 2 additional parameters, each parameter is 8 bytes
*I tested it with values: 0x0, 0x4 and 0x1000 for the 1st parameter. I extracted this values from HV Processes which use this service
*The 2nd parameter is not used in a request but in a response. It contains EID size.


Here is the dump of SLB entries from GameOS 3.41:
{| class="wikitable FCK__ShowTableBorders"
<pre>0x8000000008000000 0x0000000000000500
|-
0x8000000208000000  0x0000000000020500
! Index
0x8000000300000000  0x0000000000030510
! Size Of Data
0x0000000000000000  0x0000000000000000
! Description
0x0000000080000000  0x0000000000038C00
|-
0x00000000A0000000  0x000000000003AC00
| 0
0x00000000C0000000  0x000000000003CC00
| 0x860
0x0000000000000000  0x0000000000000000
| EID0
0x0000000000000000  0x0000000000000000
|-
0x0000000000000000  0x0000000000000000
| 1
0x0000000000000000  0x0000000000000000
| 0x2A0
0x0000000000000000  0x0000000000000000
| EID1
0x0000000000000000 0x0000000000000000
|-
0x0000000000000000  0x0000000000000000
| 2
0x0000000000000000  0x0000000000000000
| 0x730
0x0000000000000000  0x0000000000000000
| EID2
0x0000000000000000  0x0000000000000000
|-
0x0000000000000000 0x0000000000000000
| 3
0x0000000000000000  0x0000000000000000
| 0x100
0x0000000000000000  0x0000000000000000
| EID3
0x0000000000000000  0x0000000000000000
|-
0x0000000000000000  0x0000000000000000
| 4
0x0000000000000000  0x0000000000000000
| 0x030
0x0000000000000000  0x0000000000000000
| EID4
0x0000000000000000  0x0000000000000000
|-
0x0000000000000000  0x0000000000000000
| 5
0x0000000000000000  0x0000000000000000
| 0xA00
0x0000000000000000  0x0000000000000000
| EID5
0x0000000000000000  0x0000000000000000
|-
0x0000000000000000  0x0000000000000000
| 6
0x0000000000000000  0x0000000000000000
| 0x020
0x0000000000000000  0x0000000000000000
| cISD0
0x8000000010057960  0x8000000000313E78
|-
0x8000000010057940  0x0000000000000000
| 7
0x800000000001B698  0x0000000000000000
| 0x200
0x8000000010057930  0x8000000000490708
| cISD1
0x80000000002B6C68  0x80000000003DE928
|-
0x8000000010057EC0  0x80000000003DE920
| 8
0x0000000000000000  0x8000000000309810
| 0x010
0x80000000004B3000  0x0000000000000000
| cISD2
0x8000000010057CC0  0x0000000000000000
|-
0x80000000004AF000  0x80000000004E1F00
| 9
0x80000000100579C8  0x80000000100579C0
| 0x030
0x80000000100579E0  0x2400002200000000
| cCSD0
0x80000000004CF5B0  0x8000000200012000
|-
0x80000000100579F8  0x80000000100579F0
| 0x1000
0x8000000010057A10  0x80000000004A3A00
| 0xe960
0x80000000004CF5B0  0x80000000004C8D00
| metldr - size is version dependand
0x800000000001BF6C  0x80000000004CD400
|}
0x800000000001B698  0x80000000004C8100
 
0x80000000100579D0  0x80000000004B48C0
=== 0x17002 - Read EID Data By Index ===
0x0000000000001C08  0x0000000000000000
 
0x8000000010057A78  0x8000000010057A70
*I have got access to this service through DM and tested it
0x8000000010057A90  0x0000000000000000
*This service is used e.g. by Update Manager, User Token Manager or Storage Manager
0x80000000004CF90C  0x0000000000000000
*The service expects 2 additional parameters, each parameter is 8 bytes
0x0000000000000000  0x8000000010057A80
*The 1st parameter is same as the 1st parameter of service '''Read EID Data Size By Index'''
0x8000000010057A90  0x8000000000309810
*The 2nd parameter is '''EID Data Size''' that is returned by the service '''Read EID Data Size By Index'''
0x80000000004CF62C  0x0000000000000000
*The returned data is some binary data.
0x8000000010057CC0  0x0000000000000000
*The data returned by the service with 1st parameter set to 0x0 or 0x4 is from file '''eEID''' stored on FLASH storage device region 0.
0x80000000004AF000 0x80000000004B48C0
*The data returned by the service with 1st parameter set to 0x1000 contains string '''metldr'''.
0x00004000001C0000  0x0000000000000001
*E.g. EID0 data is passed by Update Manager to SPU module '''spu_token_processor.self''' when Update Manager loads and executes it with syscall '''0x10043'''.
0x00000000D0000000  0x0000A8E3EE7D10DA
*E.g. EID4 data is passed by Storage Manager to SPU module '''sb_iso_spu_module.self'''.
0x0000000000000000  0x0000000000000000
 
0x80000000004D8088  0x80000000004D9000
=== 0x17004 - Read System Data ===
</pre>
 
== SPE MMIO Memory Region class ==
*Reads data from '''cISD''' or '''cCSD''' files stored on '''/dev/rflash1'''.
*E.g. Gelic MAC address is stored in file '''cISD'''.
 
=== 0x17007 - Read System Data From EEPROM ===
 
*Reads data from SC EEPROM
*An index is passed to the service. The index is mapped to a specific SC EEPROM offset.
 
Here is the list of possible EEPROM offsets from HV 3.15:
 
{| class="wikitable FCK__ShowTableBorders"
|-
! Index
! SC EEPROM Offset
! Size Of Data
|-
| 0
| 0x48D20
| 6
|-
| 1
| 0x48D28
| 6
|-
| 2
| 0x48D30
| 6
|-
| 3
| 0x48D38
| 6
|-
| 4
| 0x48D00
| 4
|-
| 5
| 0x48D04
| 4
|-
| 6
| 0x48D08
| 4
|}
 
=== 0x17014 - Write eEID/Write metldr ===
 
*'''Holy crap, it writes passed data to the region of FLASH memory where eEID or metldr data is stored&nbsp;!!!'''
*'''And GameOS is allowed to use this service&nbsp;!!!'''
*'''Do not experiment with this service if you don't know what it does or else your PS3 will not work anymore&nbsp;!!!'''
 
=== 0x17015 - Read cISD Size ===
 
*Returns size of data '''cISD''' that is stored on '''FLASH storage device region 0'''


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'''.
=== 0x17016 - Read cISD  ===


=== vtable  ===
*Returns data '''cISD''' that is stored on '''FLASH storage device region 0'''


0x003583F8 (3.15)
=== 0x17017 - Write cISD  ===


=== Member variables  ===
*'''Writes passed data to the region of FLASH memory where cISD data is stored&nbsp;!!!'''


=== Objects ===
== 0x18000 - DM (Dispatcher Manager) ==


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


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Address in HV dump
! Packet ID
! LPAR id
! Description
! SPE
|-
! LPAR Start Address
| 0x18001
! Size
| Construct Service Port
! Physical Address
! Flags
! log2(Page Size)
|-
|-
| 0x003ABC20
| 0x18002
| 2
| Destruct Service Port
| 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 ==
=== Dispatcher Manager Messages ===


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'''.
==== Dispatcher Manager Header  ====


=== vtable ===
*Payload follows after header
 
*Payload is a SS packet
0x00358448 (3.15)
<pre>struct dispmgr_header
{
    uint32_t request_id;
    uint32_t function_id;
    uint32_t request_size;        /* payload size of request */
    uint32_t response_size;        /* payload size of response */
}
</pre>
=== Packet ID - SS ID Mapping ===


=== Objects  ===
*Before DM routes a received request to a service provider (HV Process) it consults SPM
*DM sends a request to SPM
*Request contains SS ID and Subject ID (laid and paid)
*DM obtains SS ID by mapping Packet ID


Here is the list of SPE Shadow Registers memory region objects i found in HV 3.15.
Here is the mapping table i extracted from HV Process 3 where SPM and DM run:


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! Address in HV dump
! Packet ID
! LPAR id
! SS ID
! SPE
|-
! LPAR Start Address
| 0x2001
! Size
| 0x34
! Physical Address
|-
! Flags
| 0x2002
! log2(Page Size)
| 0x35
|-
| 0x2003
| 0x36
|-
| 0x2004
| 0x37
|-
| 0x2005
| 0x38
|-
| 0x2006
| 0x39
|-
| 0x200A
| 0x3D
|-
| 0x200B
| 0x3E
|-
| 0x200C
| 0x3F
|-
| 0x200D
| 0x40
|-
|-
| 0x003ABDA0
| 0x200E
| 2
| 0x41
| 1
| 0x300000012000
| 0x1000
| -
| 0xA000000000000000
| 0xC
|-
|-
| 0x003B4290
| 0x2012
| 2
| 0x7B
| 2
| 0x300000014000
| 0x1000
| -
| 0xA000000000000000
| 0xC
|-
|-
| 0x003A8A00
| 0x2013
| 2
| 0x7C
| 3
| 0x300000010000
| 0x1000
| -
| 0xA000000000000000
| 0xC
|-
|-
| 0x003B50F0
| 0x2014
| 2
| 0x7E
| 4
| 0x300000016000
| 0x1000
| -
| 0xA000000000000000
| 0xC
|-
|-
| 0x001FFC90
| 0x2015
| 2
| 0x7F
| 5
| 0x30000000E000
| 0x1000
| -
| 0xA000000000000000
| 0xC
|-
|-
| 0x003AE5B0
| 0x2016
| 2
| 0x7D
| 6
|-
| 0x300000018000
| 0x2017
| 0x1000
| 0x80
| -  
| 0xA000000000000000
| 0xC
|}
|}


== Device MMIO Memory Region class  ==
== 0x25000 - User Token Manager  ==
 
{| class="wikitable FCK__ShowTableBorders"
|-
! Packet ID
! Description
|-
| 0x25001
| Encrypt User Token
|-
| 0x25002
| Decrypt User Token
|}
 
=== User Token  ===
 
*Before User Token Manager encrypts a received user token it checks it's format.
*User Tokens are processed by '''spu_utoken_processor.self'''
*Before User Token is processed, User Token Manager reads IDPS by sending SS requests to Indi Info Manager (packet ids 0x17001 and 0x17002). Indi Info Manager runs in HV Process 5.
 
==== User Token Format  ====
<pre>stuct user_token_attr
{
    uint32_t type;                                /* 0x00000001, value&nbsp;!= 0x00000001 means attribute list ends here */
    uint32_t size;                                /* 8 + sizeof(data) */
    /* data follows here, size of data may be 0 */
}
 
struct user_token
{
    uint32_t magic;                                /* 0x73757400 = "sut\0" */
    uint32_t format_version;                      /* 0x00000001 */
    uint64_t size;
    uint8_t idps[16];
    uint64_t expire_date;
    uint64_t capability;
    union
    {
        stuct user_token_attr attrs[0];
        uint8_t dummy[3072];
    } attrs;
    /* 0xC30 */
    uint8_t digest[20];
}
</pre>
= LPAR Memory Management  =
 
== 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'''.  
This class is the base class for different memory region types.  


=== vtable  ===
=== vtable  ===


0x00352468 (3.15)  
0x003578B0 (3.15)  


=== Member variables  ===
=== Member variables  ===


offset 0xA8 - physical address where the device MMIO region is mapped to
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)


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


Here is the list of Device MMIO memory region objects i found in HV 3.15.  
*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'''.


{| class="wikitable FCK__ShowTableBorders"
==== Encoding LPAR Memory Region Start Addresses and Sizes ====
|-
 
! Address in HV dump
*Size of LPAR memory region is encoded in the LPAR memory region start address.
! LPAR id
*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)'''.
! LPAR Start Address
*Each LPAR has a counter (8 bytes) which is incremented by 1 every time a new LPAR Memory Region is created.
! Size  
*Before incrementing, the counter is shifted left by '''log2(LPAR Memory Region Size)''' and ored with '''log2(LPAR Memory Region Size) << 42'''.
! Flags
 
! log2(Page Size)  
LPAR Memory Region Start Address >> 42 = log2(LPAR Memory Region Size)
! Physical Address  
 
! Device
LPAR Memory Region Start Address = (log2(LPAR Memory Region Size) << 42) |
|-
    (counter << log2(LPAR Memory Region Size))
| 0x001FDF00
 
| 2
===== LPAR Memory Region Address Counter =====
| 0x4000001D0000
 
| 0x10000
*LPAR Memory Region Address Counter is stored at address: '''0x38(LPAR ptr) + 0x9E8'''
| 0x8000000000000000
*LPAR1's Memory Region Address Counter is at address '''0x00677A48''' in HV dump 3.15
| 0xC
*LPAR2's Memory Region Address Counter is at address '''0x007632D8''' in HV dump 3.15
| 0x24003010000
*LPAR1's Memory Region Address Counter is at address '''0x00677A48''' in HV dump 3.41
| USB controller
*LPAR2's Memory Region Address Counter is at address '''0x00161E68''' in HV dump 3.41
|-
| 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  ==
== Physical 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'''.  
This type of memory region is created e.g. in '''lv1_allocate_memory''' HV call or in '''syscall 0x10000'''.  


=== vtable  ===
=== vtable  ===


0x00357C48 (3.15)  
0x00357D08 (3.15)  


=== Member variables  ===
=== Member variables  ===


offset 0xA8 - physical address
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  ===
=== Objects  ===


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


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
Line 5,326: Line 5,542:
! Flags  
! Flags  
! log2(Page Size)  
! log2(Page Size)  
! Physical Address
! Physical Page Addresses
|-
|-
| 0x003AF380
| 0x006B5510
| 2
| 1
| 0x700190000000
| 0x300000001000
| 0xFE00000
| 0x1000
| 0x8000000000000000
| 0x0
| 0x14
| 0xC
| 0x28080000000
| 0x672000
|-
| 0x006B5E50
| 1
| 0x440000040000
| 0x20000
| 0x0
| 0x11
| 0x6C0000
|-
| 0x006B6980
| 1
| 0x440000060000
| 0x20000
| 0x0
| 0x11
| 0x6E0000
|-
| 0x006B7F00
| 1
| 0x400000040000
| 0x10000
| 0x0
| 0x10
| 0x100000
|-
|-
| 0x003AF500
| 0x003A80F0
| 2  
| 2  
| 0x4000001A0000
| 0x6C0058000000
| 0xC000
| 0x7000000
| 0x8000000000000000
| 0x4
| 0xC
| 0x18
| 0x3C0000
| 0x1000000 - 0x7000000
|-
|-
| 0x003AF680
| 0x003BE800
| 2  
| 2  
| 0x4800006C0000
| 0x300000047000
| 0x40000
| 0x1000
| 0x8000000000000000
| 0x0
| 0xC  
| 0xC  
| 0x2808FE00000
| 0x1FA000
|-
|-
| 0x003AFC30
| 0x006BDAA0
| 2  
| 2  
| 0x440000380000
| 0x0
| 0x20000
| 0x8000000
| 0x8000000000000000
| 0x8
| 0xC
| 0x1B (single huge page)
| 0x28000C00000
| 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&nbsp;!!! That is why new HV calls '''lv1_allocate_memory''' with large memory region sizes will fail.
*So when someone wants a large piece of physical memory, he can borrow it from GameOS's LPAR memory region that starts at '''0x700020000000'''. It can be used for example to send update packages to Update Manager which are very large.
 
Here is the list of physical memory regions of GameOS i found in HV 3.41:
 
{| class="wikitable FCK__ShowTableBorders"
|-
! 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
|-
|-
| 0x003BB420
| 0x700020000000
| 2
| 0xE900000 (huge memory region)
| 0x3C0000108000
| 0x3
| 0x8000
| 0x14
| 0x8000000000000000
| 0x0
| 0xC
| 0x400000 - 0x5FF000, 0x800000 - 0xFFF000, 0x2000000 - 0xFEFF000
| 0x28000080100
|}
|}


== Direct Map Memory Region class ==
== HTAB Memory Region class ==


This type of memory region is created in HV call '''lv1_undocumented_function_114'''.
This memory region is created when a HTAB is mapped into LPAR's address space. It's created in '''lv1_map_htab''' HV call.  
'''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  ===
=== vtable  ===


0x00357C48 (3.15)  
0x00357C98 (3.15)  


=== Member variables  ===
=== Member variables  ===


offset 0xA8 - physical address
offset 0xB0 - pointer to VAS object that owns the HTAB


=== Exploiting HV with memory glitching and HV call lv1_undocumented_function_114 ===
=== Objects  ===


Here is a short description of the method i used to exploit HV from GameOS 3.15 and 3.41.
Here is the list of HTAB memory region objects i found in HV 3.15.  


* First i used the Geohot's method to create a dangling HTAB entry.
{| class="wikitable FCK__ShowTableBorders"
* 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.
! Address in HV dump
* 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.
! LPAR id
* 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.
! VAS id
* Then i mapped whole HV memory range with the patched HV call '''lv1_undocumented_function_114''' into the address space of GameOS.
! LPAR Start Address
* And now you have read/write access to the whole HV.
! Size
* $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 :-)
! 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
|}


== Methods ==
=== GameOS HTAB ===


LPAR_get_memory_region_by_start_address - 0x002C7C40 (3.15)
*HTAB of GameOS is already mapped into address space of GameOS so that is why HV call '''lv1_map_htab''' will fail until you unmap it with '''lv1_unmap_htab'''
*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


LPAR_get_memory_region_by_address - 0x002C7DA8 (3.15)
=== GameOS SLB  ===


LPAR_mem_addr_to_phys_addr(LPAR id, LPAR address, phys_addr) - 0x002FB8F0 (3.15)
Here is the dump of SLB entries from GameOS 3.41:
 
<pre>0x8000000008000000  0x0000000000000500
LPAR_construct_direct_mapping_mem_region - 0x002D4D04 (3.15)
0x8000000208000000  0x0000000000020500
 
0x8000000300000000  0x0000000000030510
= Network Devices =
0x0000000000000000  0x0000000000000000
0x0000000080000000  0x0000000000038C00
0x00000000A0000000  0x000000000003AC00
0x00000000C0000000  0x000000000003CC00
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x0000000000000000  0x0000000000000000
0x8000000010057960  0x8000000000313E78
0x8000000010057940  0x0000000000000000
0x800000000001B698  0x0000000000000000
0x8000000010057930  0x8000000000490708
0x80000000002B6C68  0x80000000003DE928
0x8000000010057EC0  0x80000000003DE920
0x0000000000000000  0x8000000000309810
0x80000000004B3000  0x0000000000000000
0x8000000010057CC0  0x0000000000000000
0x80000000004AF000  0x80000000004E1F00
0x80000000100579C8  0x80000000100579C0
0x80000000100579E0  0x2400002200000000
0x80000000004CF5B0  0x8000000200012000
0x80000000100579F8  0x80000000100579F0
0x8000000010057A10  0x80000000004A3A00
0x80000000004CF5B0  0x80000000004C8D00
0x800000000001BF6C  0x80000000004CD400
0x800000000001B698  0x80000000004C8100
0x80000000100579D0  0x80000000004B48C0
0x0000000000001C08  0x0000000000000000
0x8000000010057A78  0x8000000010057A70
0x8000000010057A90  0x0000000000000000
0x80000000004CF90C  0x0000000000000000
0x0000000000000000  0x8000000010057A80
0x8000000010057A90  0x8000000000309810
0x80000000004CF62C  0x0000000000000000
0x8000000010057CC0  0x0000000000000000
0x80000000004AF000  0x80000000004B48C0
0x00004000001C0000  0x0000000000000001
0x00000000D0000000  0x0000A8E3EE7D10DA
0x0000000000000000  0x0000000000000000
0x80000000004D8088  0x80000000004D9000
</pre>
== SPE MMIO Memory Region class ==


== Ethernet Gelic Device  ==
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'''.  
 
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  ===
=== vtable  ===


0x00357DC0 (3.15)  
0x003583F8 (3.15)  


=== Member variables  ===
=== Member variables  ===


offset 0x30 - type (8 bytes)
=== Objects  ===


offset 0x38 - pointer to LPAR that owns this Outlet object
Here is the list of SPE memory region objects i found in HV 3.15.


offset 0x48 - outlet id (8 bytes)  
{| class="wikitable FCK__ShowTableBorders"
 
|-
offset 0x90 - VIRQ assigned to this Outlet object (4 bytes)
! Address in HV dump
 
! LPAR id  
== Event Receive Port class  ==
! 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 Outlet is created e.g. in '''lv1_construct_event_receive_port''' and in '''syscall 0x1001A'''.
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'''.  
*HV calls '''lv1_connect_irq_plug''' and '''lv1_connect_irq_plug_ext''' assigns a VIRQ to Event Receive Port object.


=== vtable  ===
=== vtable  ===


0x00357E88
0x00358448 (3.15)


== VUART Outlet ==
=== Objects ===


*HV supports only one VUART Outlet per LPAR
Here is the list of SPE Shadow Registers memory region objects i found in HV 3.15.  
*'''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"
Line 5,527: Line 5,865:
! Address in HV dump  
! Address in HV dump  
! LPAR id  
! LPAR id  
! PPE id
! SPE
! LPAR Start Address
! Size
! Physical Address
! Flags
! log2(Page Size)
|-
|-
| 0x0069C7F0
| 0x003ABDA0
| 2
| 1  
| 1  
| 1
| 0x300000012000
| 0x1000
| -
| 0xA000000000000000
| 0xC
|-
|-
| 0x007A8900
| 0x003B4290
| 2
| 2  
| 2  
| 1
| 0x300000014000
|}
| 0x1000
 
| -
== Virtual IRQ - Outlet Mapping ==
| 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 ===


*HV maintains 2 tables per PPE that map a VIRQ to an Outlet object.
offset 0xA8 - physical address where the device MMIO region is mapped to  
*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 ===
=== Objects ===


0x0069C990 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 0''' (not empty)
Here is the list of Device MMIO memory region objects i found in HV 3.15.


{| class="wikitable FCK__ShowTableBorders"
{| class="wikitable FCK__ShowTableBorders"
|-
|-
! VIRQ
! Address in HV dump  
! Address of Outlet object in HV dump  
! LPAR id
! Description
! LPAR Start Address
! Size
! Flags
! log2(Page Size)
! Physical Address
! Device
|-
|-
| 58
| 0x001FDF00
| 0x00090D10
| 2
| -
| 0x4000001D0000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003010000
| USB controller
|-
|-
| 59
| 0x003B3850
| 0x006BAC50
| 2
| -
| 0x400000200000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003020000
| USB controller
|-
|-
| 60
| 0x003B6E50
| 0x006B3ED0
| 2
| FLASH storage device / Storage device notification for LPAR 1
| 0x4000001E0000
| 0x10000
| 0x8000000000000000
| 0xC
| 0x24003810000
| USB controller
|-
|-
| 61
| 0x003B9950
| 0x00697E70
| 2
| VUART interrupts
| 0x4000001F0000
|-
| 0x10000
| 62
| 0x8000000000000000
| 0x001C8F20
| 0xC
| -
| 0x24003820000
| USB controller
|}
|}


=== LPAR 1 PPE 1 Thread 1 ===
== 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'''.


0x0069D9B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 1''' (empty)
=== vtable  ===


=== LPAR 2 PPE 1 Thread 0  ===
0x00357C48 (3.15)


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


{| class="wikitable FCK__ShowTableBorders"
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"
|-
|-
! VIRQ
! Address in HV dump  
! Address of Outlet object in HV dump  
! LPAR id
! Description
! LPAR Start Address
! Size
! Flags
! log2(Page Size)
! Physical Address
|-
|-
| 20
| 0x003AF380
| 0x003AA210
| 2
| -
| 0x700190000000
| 0xFE00000
| 0x8000000000000000
| 0x14
| 0x28080000000
|-
|-
| 21
| 0x003AF500
| 0x003AFEC0
| 2
| -
| 0x4000001A0000
| 0xC000
| 0x8000000000000000
| 0xC
| 0x3C0000
|-
|-
| 22
| 0x003AF680
| 0x001FC010
| 2
| -
| 0x4800006C0000
| 0x40000
| 0x8000000000000000
| 0xC
| 0x2808FE00000
|-
|-
| 23
| 0x003AFC30
| 0x003A8E50
| 2
| -
| 0x440000380000
| 0x20000
| 0x8000000000000000
| 0xC
| 0x28000C00000
|-
|-
| 24
| 0x003BB420
| 0x001FFED0
| 2  
| SPE 0 Class 0 Interrupt
| 0x3C0000108000
|-
| 0x8000
| 25
| 0x8000000000000000
| 0x003AE160
| 0xC
| SPE 0 Class 1 Interrupt
| 0x28000080100
|-
|}
| 26
 
| 0x003AE350
== Direct Map Memory Region class ==
| SPE 0 Class 2 Interrupt
 
|-
This type of memory region is created in HV call '''lv1_undocumented_function_114'''.
| 27
'''lv1_undocumented_function_114''' allows you to map any memory address into LPAR's memory address.
| 0x003AB100
 
| SPE 1 Class 0 Interrupt
* 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.
| 28
 
| 0x003AB2F0
=== vtable  ===
| SPE 1 Class 1 Interrupt
 
|-
0x00357C48 (3.15)
| 29
 
| 0x003AB4E0
=== Member variables  ===
| SPE 1 Class 2 Interrupt
 
|-
offset 0xA8 - physical address
| 30
 
| 0x003AA6A0
=== Exploiting HV with memory glitching and HV call lv1_undocumented_function_114 ===
| SPE 2 Class 0 Interrupt
 
|-
Here is a short description of the method i used to exploit HV from GameOS 3.15 and 3.41.
| 31
 
| 0x003AA890
* First i used the Geohot's method to create a dangling HTAB entry.
| SPE 2 Class 1 Interrupt
* 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.
| 32
* 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.
| 0x003AAA80
* 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.
| SPE 2 Class 2 Interrupt
* 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.
| 33
* $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 :-)
| 0x003B44A0
 
| SPE 3 Class 0 Interrupt
== Methods  ==
|-
 
| 34
LPAR_get_memory_region_by_start_address - 0x002C7C40 (3.15)
| 0x003B4690
 
| SPE 3 Class 1 Interrupt
LPAR_get_memory_region_by_address - 0x002C7DA8 (3.15)
|-
 
| 35
LPAR_mem_addr_to_phys_addr(LPAR id, LPAR address, phys_addr) - 0x002FB8F0 (3.15)
| 0x003B4AD0
 
| SPE 3 Class 2 Interrupt
LPAR_construct_direct_mapping_mem_region - 0x002D4D04 (3.15)
|-
 
| 36
= Network Devices  =
| 0x003B5300
 
| SPE 4 Class 0 Interrupt
== Ethernet Gelic Device  ==
|-
 
| 37
device id = 0
| 0x003B54F0
 
| SPE 4 Class 1 Interrupt
MAC Address: 00:1F:A7:C6:2A:C5
|-
 
| 38
device memory base address = 0x24003004000 (size = 0x1000)
| 0x003B56E0
 
| SPE 4 Class 2 Interrupt
== WLAN Gelic Device  ==
|-
 
| 39
device id = 0
| 0x003AE7C0
 
| SPE 5 Class 0 Interrupt
MAC Address: 02:1F:A7:C6:2A:C5 (locally administered)
|-
 
| 40
=== Net Manager  ===
| 0x003AE9B0
 
| SPE 5 Class 1 Interrupt
*Net Manager runs in Process 9
|-
*It sends commands to '''/dev/sc1''' to reset WLAN Gelic device
| 41
*It opens '''/dev/net0''', sets MAC address and writes device firmware '''eurus_fw.bin''' to WLAN device by using '''ioctl''' syscall
| 0x003AEBA0
 
| SPE 5 Class 2 Interrupt
=== /dev/net0  ===
|-
 
| 42
The device supports 3 ioctl commands:
| 0x003B2040
 
| Storage device notification for LPAR 2
*0 - 0x002AC10C (3.15)
|-
*1 - 0x002AC250 (3.15)
| 43
*2 - EURUS_STAT 0x002AC320 (3.15)
| 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 ===
=== Methods ===


0x007A89E0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 1''' (not empty)  
net_control_cmd_GELIC_LV1_POST_WLAN_CMD - 0x0024A55C (3.15)  


{| class="wikitable FCK__ShowTableBorders"
net_control_wlan_cmd_GELIC_EURUS_CMD_ASSOC - 0x00246C78 (3.15)
|-
 
! VIRQ
net_control_wlan_cmd_GELIC_EURUS_CMD_START_SCAN - 0x00248A14 (3.15)
! Address of Outlet object in HV dump
 
! Description
net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WEP_CFG - 0x00249F24 (3.15)
|-
 
| 16
net_control_wlan_cmd_GELIC_EURUS_CMD_SET_WPA_CFG - 0x002497B8 (3.15)
| 0x003B2480
 
| -
= Event Notification  =
|-
| 17
| 0x003B2590
| -
|-
| 18
| 0x003B26A0
| -
|-
| 19
| 0x003B27B0
| -
|}


== IRQ State Bitmap  ==
*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.


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


0x8941FC0 - physical address of LPAR's IRQ State Bitmap for Thread 0 of LINUX LPAR
This is the base Outlet class. There are different types of Outlet and they derive from this base class.


0x8948FC0 - physical address of LPAR's IRQ State Bitmap for Thread 1 of LINUX LPAR
=== vtable  ===


= System Controller (SC or SYSCON) =
0x00357DC0 (3.15)  


*Data received from SC is sent to a VUART
=== Member variables  ===
*'''lv1_get_rtc''' and '''syscall 0x10036''' communicate with '''SC VUART 4'''.


=== VUART Table  ===
offset 0x30 - type (8 bytes)


*Address of SC VUART Table - 0x00610410 (3.15).
offset 0x38 - pointer to LPAR that owns this Outlet object
*There are 5 VUARTs for SC in HV 3.15


Here is the SC VUART table from HV 3.15:
offset 0x48 - outlet id (8 bytes)


{| class="wikitable FCK__ShowTableBorders"
offset 0x90 - VIRQ assigned to this Outlet object (4 bytes)  
|-
 
! Index
== Event Receive Port class  ==
! 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  ==
*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.


spider_sc_interrupt_handler - 0x0020A68C (3.15)
=== vtable  ===


== Methods  ==
0x00357E88


sc_vuart_4_get_peer_vuart - 0x002ED384 (3.15)
== VUART Outlet  ==


sc_send - 0x0020A908 (3.15)
*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


sc_receive - 0x0020A354 (3.15)
=== vtable  ===


sc_vuart_rx_trigger_callback - 0x002ED470 (3.15)
0x00357DC0


== lv1_get_rtc ==
=== VUART IRQ Bitmap ===


*'''lv1_get_rtc''' communicates with SC VUART 4.
*At address 0x38(LPAR ptr) + 0x158 is the VUART IRQ Bitmap owned by HV for LPAR (4 * 8 bytes = 256 bits)
*20 bytes are written to the peer VUART of SC VUART 4.  
*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'''  
*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 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.  
*When SC VUART 4 receive data buffer is not empty, '''lv1_get_rtc''' reads 24 bytes from the VUART.
*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'''.


== SYSCON Protocol ==
= Logical PPE  =


* I was able to enable SYSCON Manager debug messages in HV Process 5
*Logical PPE is used for interrupt management of LPAR.
* Messages sent to SYSCON are at least '''0x10''' bytes of size. SC VUARTs check it before sending the messages to SYSCON.
*A Logical PPE object is created in '''syscall 0x10005'''. It' used e.g. in Process 9 during LPAR construction.
* The header size of the SYSCON messages is '''0x10''' bytes.
*'''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


=== Packet Header ===
== vtable  ==


* Packet header is of size '''0x10''' bytes.
0x00357DF0 (3.15)
* 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>
== Member variables  ==
struct sc_hdr
{
    uint8_t field0;
    uint8_t field1;          /* always 1 */
    uint8_t field2[4];
    uint16_t cksum;          /* header checksum */
    uint32_t index;          /* syscon index (0 - /dev/sc0, 1 - /dev/sc1, 2 - /dev/sc2, 3 - /dev/sc3) */
    uint16_t size1;          /* body size */
    uint16_t size2;          /* body size */
};
</pre>


==== Calculating Packet Header Checksum ====
offset 0x90 - pointer to an object that contains VIRQ-Outlet mapping table for thread 0


<pre>
offset 0x98 - pointer to an object that contains VIRQ-Outlet mapping table for thread 1
/* calculating SC packet header checksum */


/*
== Objects ==
  * sc_hdr_cksum
*/
uint16_t sc_hdr_cksum(struct sc_hdr *sc_hdr)
{
    uint8_t *ptr;
    uint32_t sum;


    ptr = (uint8_t *) sc_hdr;
Here is the list of Logical PPE objects i found in HV 3.15.
    sum = 0;


    for (i = 0; i < 6; i++)
{| class="wikitable FCK__ShowTableBorders"
        sum += *ptr++;
|-
! Address in HV dump
! LPAR id
! PPE id
|-
| 0x0069C7F0
| 1
| 1
|-
| 0x007A8900
| 2
| 1
|}


    sum += 0x8000;
== Virtual IRQ - Outlet Mapping  ==


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


struct sc_hdr sc_hdr;
=== LPAR 1 PPE 1 Thread 0  ===


memset(&sc_hdr, 0, sizeof(sc_hdr));
0x0069C990 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 0''' (not empty)  


sc_hdr.cksum = sc_hdr_cksum(sc_hdr);
{| class="wikitable FCK__ShowTableBorders"
 
|-
/* fill sc header here */
! 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
| -
|}


sc_hdr.cksum = sc_hdr_cksum(sc_hdr);
=== LPAR 1 PPE 1 Thread 1  ===
</pre>


=== Packet Body ===
0x0069D9B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 1 PPE 1 Thread 1''' (empty)


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


=== Reading SYSCON EPROM (NVS Service) ===
0x000A06B0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 0''' (not empty)  


Here is a command which is sent to SYSCON to read 1 byte of EPROM at offset 0x48C07 (Product Mode):
{| class="wikitable FCK__ShowTableBorders"
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
|-
 
! VIRQ
And here is the response to the above request:
! Address of Outlet object in HV dump
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
! Description
 
|-
=== PCI Bus Power ===
| 20
 
| 0x003AA210
* '''Used by PS2EMU System Manager in HV process 9 when PS2 EMU is booted'''
| -
 
|-
==== PCI Bus Power On ====
| 21
 
| 0x003AFEC0
'''Request to SC1:'''
| -
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x01
|-
 
| 22
==== PCI Bus Power Off ====
| 0x001FC010
 
| -
'''Request to SC1:'''
|-
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x00
| 23
 
| 0x003A8E50
=== Ring Buzzer ===
| -
 
|-
'''Request:'''
| 24
0x16 0x01 0x00 0x00 0x00 0x00 0x80 0x17 0x00 0x00 0x00 0x00 0x00 0x08 0x00 0x08 0x20 0x00 0x00 0x00 0x00 0x00 0x00 0x00
| 0x001FFED0
 
| SPE 0 Class 0 Interrupt
=SYSCON=
|-
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#SYSCON gitbrew.org::SYSCON] <br />
| 25
 
| 0x003AE160
SYSCON MMIO registers can be accessed on Linux with a driver using lv1_undocumented_function_114, e.g. '''ps3sbmmio'''.
| SPE 0 Class 1 Interrupt
Use ps3sbmmio device driver carefully, an access at some addresses could shutdown your PS3.
|-
 
| 26
==Packet Header==
| 0x003AE350
 
| SPE 0 Class 2 Interrupt
* Size is '''0x10'''.
|-
 
| 27
<pre>
| 0x003AB100
struct sc_hdr {
| SPE 1 Class 0 Interrupt
    uint8_t service_id;
|-
    uint8_t version;              /* must be 1 !!! */
| 28
    uint16_t transaction_id;      /* returned in response */
| 0x003AB2F0
    uint8_t res[2];
| SPE 1 Class 1 Interrupt
    uint16_t cksum;              /* checksum of first 6 header bytes */
|-
    uint32_t communication_tag;  /* SYSCON tag: 0-4 */
| 29
    uint16_t payload_size[2];    /* body size */
| 0x003AB4E0
};
| SPE 1 Class 2 Interrupt
</pre>
|-
 
| 30
==Sending Packets==
| 0x003AA6A0
 
| SPE 2 Class 0 Interrupt
* 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.
| 31
* The packet is sent with 4 byte transfers.
| 0x003AA890
* First, the Hypervisor sends the header of the packet, 4 word transfers.
| SPE 2 Class 1 Interrupt
* The header is written beginning at the address 0x2400008D000.
|-
* After that the Hypervisor sends the body of the packet, with 4 byte transfers too.
| 32
* The body is written beginning at the address 0x2400008D010.
| 0x003AAA80
* 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.
| SPE 2 Class 2 Interrupt
* 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>
| 33
uint32_t cksum = 0;
| 0x003B44A0
 
| SPE 3 Class 0 Interrupt
for (i = 0; i < packet_size; i++)
|-
    cksum -= packet[i];
| 34
 
| 0x003B4690
cksum = cksum & 0xffff;
| SPE 3 Class 1 Interrupt
</pre>
|-
* After the packet checksum was written, the Hypervisor reads the value at offset 0x2400008DFF0, modifies it and stores back:
| 35
<pre>
| 0x003B4AD0
value = value + 1;
| SPE 3 Class 2 Interrupt
value &= 0xffff;
|-
value = (value << 16) | value;
| 36
</pre>
| 0x003B5300
* To notify the SYSCON about the new packet, the Hypervisor writes 0x1 to address 0x2400008E100.
| SPE 4 Class 0 Interrupt
 
|-
==Receiving Packets==
| 37
 
| 0x003B54F0
* The Hypervisor installs an interrupt handler for the SYSCON.
| SPE 4 Class 1 Interrupt
* 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.
| 38
* 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.
| 0x003B56E0
* The Hypervisor reads the header of the packet beginning at the address 0x2400008C000.
| SPE 4 Class 2 Interrupt
* 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.
| 39
* 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.
| 0x003AE7C0
* The Hypervisor reads the body of the packet beginning at the address 0x2400008C010.
| SPE 5 Class 0 Interrupt
* 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 :)
| 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
| -
|}


==Test==
=== LPAR 2 PPE 1 Thread 1  ===


'''1. Before sending SYSCON packet''':
0x007A89E0 (3.15) - address of VIRQ-Outlet table for '''LPAR 2 PPE 1 Thread 1''' (not empty)  
<pre>
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C


00000000  01 18 01 18                                      |....|
{| class="wikitable FCK__ShowTableBorders"
00000004
|-
! VIRQ
! Address of Outlet object in HV dump
! Description
|-
| 16
| 0x003B2480
| -
|-
| 17
| 0x003B2590
| -
|-
| 18  
| 0x003B26A0
| -
|-
| 19
| 0x003B27B0
| -
|}


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


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


root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C
0x8941FC0 - physical address of LPAR's IRQ State Bitmap for Thread 0 of LINUX LPAR


00000000  01 24 01 24                                      |.$.$|
0x8948FC0 - physical address of LPAR's IRQ State Bitmap for Thread 1 of LINUX LPAR
00000004


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


00000000  01 24 01 24                                      |.$.$|
*Data received from SC is sent to a VUART
00000004
*'''lv1_get_rtc''' and '''syscall 0x10036''' communicate with '''SC VUART 4'''.
</pre>


'''2. SYSCON packet was sent by using ps3dm_scm read_eprom.'''
=== VUART Table  ===


'''3. After sending SYSCON packet''':
*Address of SC VUART Table - 0x00610410 (3.15).
<pre>
*There are 5 VUARTs for SC in HV 3.15
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C


00000000  01 19 01 19                                      |....|
Here is the SC VUART table from HV 3.15:
00000004


root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
{| class="wikitable FCK__ShowTableBorders"
|-
! Index
! Address of VUART object in HV dump
! Description
|-
| 0
| 0x0060FD20
| This VUART is connected with the '''VUART 0 (/dev/sc0)''' of LPAR 1
|-
| 1
| 0x0060FE20
| This VUART is connected with the '''VUART 1 (/dev/sc1)''' of LPAR 1
|-
| 2
| 0x0060FF20
| This VUART is not connected to some peer VUART but i guess that it should be connected to '''VUART 2 (/dev/sc2)''' of LPAR1
|-
| 3
| 0x006124E0
| This VUART is connected with the '''VUART 3 (/dev/sc3)''' of LPAR 1
|-
| 4
| 0x00612DF0
| '''lv1_get_rtc''' and '''syscall 0x10036''' communicate with this VUART.
|}


00000000 01 19 01 19                                      |....|
== Interrupt Handling ==
00000004


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


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


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


00000000  01 25 01 25                                      |.%.%|
sc_send - 0x0020A908 (3.15)
00000004
</pre>


'''4. Received Header'''
sc_receive - 0x0020A354 (3.15)


<pre>
sc_vuart_rx_trigger_callback - 0x002ED470 (3.15)
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  |................|
== lv1_get_rtc ==
00000010


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


'''5. Received Body'''
== SYSCON Protocol ==


<pre>
* I was able to enable SYSCON Manager debug messages in HV Process 5
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=8 skip=$((0x8c010)) status=noxfer | hexdump -C
* 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.


00000000  00 00 c7 01 ff 00 00 00                          |..Ç.ÿ...|
=== Packet Header ===
00000008
</pre>


==Examples==
* 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.


===Get RTC===
<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>


* Used by LV1 call '''lv1_get_rtc'''
==== Calculating Packet Header Checksum ====
* Communication with SYSCON 4


Request:
<pre>
<pre>
# write packet
/* calculating SC packet header checksum */
 
/*
* sc_hdr_cksum
*/
uint16_t sc_hdr_cksum(struct sc_hdr *sc_hdr)
{
    uint8_t *ptr;
    uint32_t sum;


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


# dump packet counter
    for (i = 0; i < 6; i++)
        sum += *ptr++;


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


00000000  00 c0 00 c0                                      |.À.À|
    return sum & 0xffff;
00000004
}


# increment packet counter
struct sc_hdr sc_hdr;


echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer
memset(&sc_hdr, 0, sizeof(sc_hdr));


# kick packet
sc_hdr.cksum = sc_hdr_cksum(sc_hdr);


# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer
/* fill sc header here */


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


Response:
=== Packet Body ===


<pre>
* Packet body follows packet header
# dump packet counter
* Packet body size is stored at offset '''0xC''' and '''0xE''' in packet header and is of size 2 bytes


# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
=== Reading SYSCON EPROM (NVS Service) ===


00000000 00 c1 00 c1                                      |.Á.Á|
Here is a command which is sent to SYSCON to read 1 byte of EPROM at offset 0x48C07 (Product Mode):
00000004
  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


# dump response packet
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


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


00000000  13 01 00 00 00 00 80 14  00 00 00 04 00 08 00 08  |................|
* '''Used by PS2EMU System Manager in HV process 9 when PS2 EMU is booted'''
00000010  00 00 00 00 15 af 47 6b                          |.....¯Gk|
00000018
</pre>


===Ring Buzzer===
==== PCI Bus Power On ====


* Used by System Manager
'''Request to SC1:'''
* Communication with SYSCON 1
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x01


Request:
==== PCI Bus Power Off ====


<pre>
'''Request to SC1:'''
# write packet
0x10 0x01 0x00 0x00 0x00 0x00 0x80 0x11 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x02 0x31 0x00


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


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


# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
=SYSCON=
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#SYSCON gitbrew.org::SYSCON] <br />


00000000  00 c0 00 c0                                      |.À.À|
SYSCON MMIO registers can be accessed on Linux with a driver using lv1_undocumented_function_114, e.g. '''ps3sbmmio'''.
00000004
Use ps3sbmmio device driver carefully, an access at some addresses could shutdown your PS3.


# increment packet counter
==Packet Header==


echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer
* Size is '''0x10'''.
 
# kick packet
 
# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer
 
# you should hear a beep


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


Response:
==Sending Packets==


<pre>
* Before sending new packet to SYSCON, the Hypervisor checks 2 words at offsets 0x2400008DFF0 and 0x2400008CFF4.
# dump packet counter
* 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.
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
* 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;


00000000  00 c1 00 c1                                      |.Á.Á|
for (i = 0; i < packet_size; i++)
00000004
    cksum -= packet[i];


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


# dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C
==Receiving Packets==
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>
* 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 :)


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


==Running Isolated SPE Modules On OtherOS++ Linux==
'''1. Before sending SYSCON packet''':
<pre>
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C


* spp_verifier is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux.
00000000  01 18 01 18                                      |....|
* It decrypts default.spp profile
00000004
* Tested on 3.41 and 3.55.
* You can modify it easily to run other SPE modules.


<pre>
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
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)
00000000  01 18 01 18                                      |....|
lv1_construct_logical_spe (0x00000000)
00000004
SPE id (0x000000000000002b)
 
lv1_undocumented_function_209 (0x00000000)
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C
shadow execution status (0x0000000000000002)
 
lv1_get_spe_interrupt_status(1) (0x00000000)
00000000  01 24 01 24                                      |.$.$|
interrupt status 1 (0x0000000000000000)
00000004
sleep
 
shadow execution status (0x0000000000000002)
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C
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
00000000 01 24 01 24                                      |.$.$|
...
00000004
...
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>
</pre>


==Using metldr On OtherOS++ Linux==
'''2. SYSCON packet was sent by using ps3dm_scm read_eprom.'''


* spp_verifier_direct is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux by using metldr directly.
'''3. After sending SYSCON packet''':
* It decrypts default.spp profile.
<pre>
* Tested on 3.41 and 3.55.
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C
* You can modify it easily to run other SPE modules.


<pre>
00000000  01 19 01 19                                      |....|
root@debian-hdd:/home/glevand/spp_verifier_direct# insmod ./spp_verifier_direct.ko
00000004
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
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
...
...
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 =
00000000  01 19 01 19                                      |....|
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#Gelic_Device gitbrew.org::Gelic Device] <br />
00000004


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


* Value of the loader parameter "sys.hw.config" controls if Gelic WLAN is enabled or not.
00000000  01 25 01 25                                      |.%.%|
* Value of the loader parameter "sys.hw.config" is stored in the repository node "sys.hw.config" too.
00000004
* 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''
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C


== Control Interface ==
00000000  01 25 01 25                                      |.%.%|
00000004
</pre>


HV calls 195 and 196 are used by GameOS to send commands to Gelic device directly.
'''4. Received Header'''


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


==== Parameters ====
00000000  14 01 00 00 00 00 80 15  00 00 00 03 00 05 00 05  |................|
00000010


r3 - LPAR address of data buffer
</pre>


r4 - size of data buffer
'''5. Received Body'''


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


=== lv1_undocumented_function_195 ===
00000000  00 00 c7 01 ff 00 00 00                          |..Ç.ÿ...|
00000008
</pre>


==== Parameters ====
==Examples==


r3 - command (16 bit value)
===Get RTC===


r4 - command data size
* Used by LV1 call '''lv1_get_rtc'''
* Communication with SYSCON 4


r5 - must be 0
Request:
<pre>
# write packet


=== Data Buffer ===
# 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


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


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


* Every command data sent to Gelic device contains header of size '''0xC'''
00000000  00 c0 00 c0                                      |.À.À|
* After the header follows the command data
00000004
* After the Gelic device processed the command, it notifies LV2 kernel about command completion by sending an interrupt


==== Header ====
# increment packet counter


* Size is '''0xc'''.
echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer
* Byte order is little-endian.
* Header data in a request command buffer is always all 0s.


0x0 - command = request command + 1 (2 bytes)
# kick packet


0x4 - result, 0x1 - success ??? 0x2 - buffer too small ??? (2 bytes)
# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer


0x6 - body size (2 bytes)
</pre>


=== Event Data Buffer ===
Response:


* The Gelic device notifies LV2 kernel by sending an interrupt when new events are available
<pre>
* Event Data Buffer has 8 bytes header
# dump packet counter
* The remaining bytes are divided into event slots
* Each event slot is of size 64 bytes
* Events are in little-endian format


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


offset 0x0 - GET index (4 bytes)
00000000  00 c1 00 c1                                      |.Á.Á|
00000004


offset 0x4 - PUT index (4 bytes)
# dump response packet


* GET index is updated by Gelic driver. The Gelic driver reads events beginning with the event slot at index GET.
# dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C
* 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 ===
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>


* LV2 syscall 726 sends Gelic device command and blocks until a response from the Gelic device arrives
===Ring Buzzer===
* 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 ====
* Used by System Manager
* Communication with SYSCON 1


r3 - command (16 bits)
Request:


r4 - effective address of command data buffer
<pre>
# write packet


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


=== Commands ===
# dump packet counter


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


* Used by VSH.
00000000  00 c0 00 c0                                      |.À.À|
* Command buffer size is '''0x10'''.
00000004
* Used in AP mode.
* Enables AP mode ???


====Get AP SSID (0x3)====
# increment packet counter


* Command buffer is of size '''0x30'''.
echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer
* Returns SSID in AP mode.


offset 0xC - SSID (32 bytes)
# kick packet


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


* Used by VSH.
# you should hear a beep
* Command buffer is of size '''0x30'''.
* Sets SSID in AP mode.


offset 0xC - SSID (32 bytes)
</pre>


====Get Channel (0xf)====
Response:


* Used by VSH.
<pre>
* Command buffer is of size '''0x31'''.
# dump packet counter
* Data is returned from the device.
* Returns list of channels and active channel.


offset 0x2F - active channel (2 bytes)
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C


====Set Channel (0x11)====
00000000  00 c1 00 c1                                      |.Á.Á|
00000004


* Used by VSH.
# dump response packet
* Command buffer size is '''0xd'''
* Valid channels: '''0 - 13'''. '''0''' means that the channel is selected '''automatically'''.


offset 0xC - channel (1 byte)
# 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


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


* Command buffer size is '''0xF'''.
=Isolation=
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#Isolation gitbrew.org::Isolation] <br />


====Set Antenna (0x29)====
==Running Isolated SPE Modules On OtherOS++ Linux==


* Command buffer size is '''0xe'''
* 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.


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


offset 0xD - 2 (1 byte)
PPE id (0x0000000000000001) VAS id (0x0000000000000002)
 
lv1_construct_logical_spe (0x00000000)
====Set AP WEP Configuration (0x5b)====
SPE id (0x000000000000002b)
 
lv1_undocumented_function_209 (0x00000000)
* Used by VSH.
shadow execution status (0x0000000000000002)
* Command buffer is of size '''0x56'''.
lv1_get_spe_interrupt_status(1) (0x00000000)
* Sets WEP security type and WEP key.
interrupt status 1 (0x0000000000000000)
* Security types: 0 - none, 1 - wep64, 2 - wep128
sleep
 
shadow execution status (0x0000000000000002)
offset 0xE - security mode: 0 - none, 1 - wep64, 2 - wep128 (1 byte)
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)


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


====Unknown (0x61)====
==Using metldr On OtherOS++ Linux==


* Used by VSH.
* spp_verifier_direct is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux by using metldr directly.
* Command buffer size is '''0xd'''
* It decrypts default.spp profile.
 
* Tested on 3.41 and 3.55.
====Unknown (0x65)====
* You can modify it easily to run other SPE modules.
 
* Used by VSH.
* Command  uffer size is '''0xd'''.
* Used in AP mode.
 
====Get Eurus Firmware Version (0x99)====
 
* Used by VSH.


Here is the response on my PS3 Slim:
<pre>
<pre>
00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2|
root@debian-hdd:/home/glevand/spp_verifier_direct# insmod ./spp_verifier_direct.ko
00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19|
root@debian-hdd:/home/glevand/spp_verifier_direct# cat metldr > /proc/spp_verifier_direct/metldr
00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).|
root@debian-hdd:/home/glevand/spp_verifier_direct# cat isoldr_355 > /proc/spp_verifier_direct/isoldr
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00       |..............  |
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>
</pre>


====Get AP Operating Mode (0xb7)====
= Gelic Device =
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#Gelic_Device gitbrew.org::Gelic Device] <br />


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


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


====Set AP Operating Mode (0xb9)====
''Note:[http://www.ps3devwiki.com/index.php?title=Wifi old vs. new]: Old == CECHA up to CECHK, New == CECHL and later''


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


offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)
HV calls 195 and 196 are used by GameOS to send commands to Gelic device directly.


====Unknown (0xc5)====
=== lv1_undocumented_function_196 ===


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


offset 0xC - ??? (4 bytes)
r3 - LPAR address of data buffer


====Set AP WPA AKM Suite (0xc9)====
r4 - size of data buffer


* Used by VSH.
r5 - must be 0
* Command buffer size is '''0x11'''.
* Sets WPA AKM suite in AP mode.


offset 0xC - AKM suite (4 bytes)
=== lv1_undocumented_function_195 ===


====Set AP WPA Group Cipher Suite (0xcf)====
==== Parameters ====


* Used by VSH.
r3 - command (16 bit value)
* Command buffer size is '''0x10'''
* Used in AP + WPA mode.


offset 0xC - group cipher suite: group (4 bytes)
r4 - command data size


====Set AP WPA PSK Binary (0xd3)====
r5 - must be 0


* Used by VSH.
=== Data Buffer ===
* Command buffer size is '''0x4c'''
* Sets WPA PSK binary


offset 0xC - PSK (64 bytes)
* 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.


====Set AP WPA Reauthentication Timeout (0xd5)====
=== Command Data Buffer ===


* Used by VSH.
* Every command data sent to Gelic device contains header of size '''0xC'''
* Command buffer size is '''0x10'''
* After the header follows the command data
* Sets WPA Reauth timeout value in AP WPA mode.
* After the Gelic device processed the command, it notifies LV2 kernel about command completion by sending an interrupt
* VSH uses 36000 as timeout.


offset 0xC - timeout value in seconds (2 bytes)
==== Header ====


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


* Used by VSH.
0x0 - command = request command + 1 (2 bytes)
* Command buffer size is '''0x10'''.
* Used in AP + WPA mode.


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


* Used by VSH.
0x6 - body size (2 bytes)
* Command buffer size is '''0x10'''.
* Used in AP + WPA mode.


====Set AP WPA PSK Passphrase (0x17d)====
=== Event Data Buffer ===


* Used by VSH.
* The Gelic device notifies LV2 kernel by sending an interrupt when new events are available
* Command buffer size is '''0x2D'''
* 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


offset 0xD - passphrase (32 bytes)
==== Header ====


====Set AP WPA Pairwise Cipher Suite (0x1bf)====
offset 0x0 - GET index (4 bytes)


* Used by VSH.
offset 0x4 - PUT index (4 bytes)
* Command buffer size is '''0x11'''
* Used in AP + WPA mode.


offset 0xC - pairwise cipher suite (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.


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


====Unknown (0x1d9)====
* 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.


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


====Unknown (0x1dd)====
r3 - command (16 bits)


* Used by VSH.
r4 - effective address of command data buffer
* Command buffer size is '''0xd'''


====Unknown (0x1ed)====
r5 - size of command data buffer


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


====Get Eurus HW Revision (0x1fb)====
====Unknown (0x1)====


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


====Associate (0x1001)====
====Get AP SSID (0x3)====


* Used by VSH.
* Command buffer is of size '''0x30'''.
* Used by LV1 on FAT models.
* Returns SSID in AP mode.
* Command buffer size is '''0xd'''
 
* Data passed to Gelic device is all 0s
offset 0xC - SSID (32 bytes)


====Get Common Configuration (0x1003)====
====Set AP SSID (0x5)====


* Used by VSH.
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer is of size '''0x30'''.
* Command buffer size is '''0x18'''
* Sets SSID in AP mode.
* Data passed to Gelic device is all 0s
 
offset 0xC - SSID (32 bytes)


====Set Common Configuration (0x1005)====
====Get Channel (0xf)====


* Used by VSH.
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer is of size '''0x31'''.
* Command buffer size is '''0x18'''
* Data is returned from the device.
* Hmm, VSH always removes QOS bit from capability, that means Jupiter doesn't support QOS ???
* Returns list of channels and active channel.
 
offset 0x2F - active channel (2 bytes)
 
====Set Channel (0x11)====


offset 0xC - BSS type: 0 - infrastructure, 1 - ???, 2 - adhoc (1 byte)
* Used by VSH.
* Command buffer size is '''0xd'''
* Valid channels: '''0 - 13'''. '''0''' means that the channel is selected '''automatically'''.


offset 0xD - authentication mode: 0 - open, 1 - shared key
offset 0xC - channel (1 byte)


offset 0xE - opmode: 0 - 11bg, 1 - 11b, 2 - 11g (1 byte)  
====Unknown (0x27)====


offset 0xF - ??? (1 byte)
* Command buffer size is '''0xF'''.


offset 0x10 - BSSID (6 bytes)
====Set Antenna (0x29)====


offset 0x16 - capability (2 bytes)
* Command buffer size is '''0xe'''


====Get WEP Configuration (0x1013)====
offset 0xC - ??? (1 byte)


* Used by VSH.
offset 0xD - ??? (1 byte)
* Used by LV1 on FAT models.
* Command buffer size is '''0x50'''
* Data passed to Gelic device is all 0s


====Set WEP Configuration (0x1015)====
====Set AP WEP Configuration (0x5b)====


* Used by VSH.
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer is of size '''0x56'''.
* Command buffer size is '''0x50'''
* Sets WEP security type and WEP key.
* Security types: 0 - none, 1 - wep64, 2 - wep128


====Get WPA Configuration (0x1017)====
offset 0xE - security mode: 0 - none, 1 - wep64, 2 - wep128 (1 byte)


* Used by VSH.
offset 0x10 - WEP key (64 bytes)
* Used by LV1 on FAT models.
* Command buffer size is '''0x5b'''
* Data passed to Gelic device is all 0s


====Set WPA Configuration (0x1019)====
====Unknown (0x61)====


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


offset 0xE - security type: 0 - WPA, 1 - RSNA (1 byte)
====Unknown (0x65)====


offset 0xF - psk type: 0 - hex, 1 - bin (1 byte)
* Used by VSH.
* Command  uffer size is '''0xd'''.
* Used in AP mode.


offset 0x10 - psk key: hex or bin (64 bytes)
====Get Eurus Firmware Version (0x99)====


offset 0x50 - group cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
* Used by VSH.


offset 0x54 - pairwise cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
Here is the response on my PS3 Slim:
<pre>
00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2|
00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19|
00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).|
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00      |..............  |
</pre>


offset 0x58 - AKM suite: 0x0050f202 - WPA PSK, 0x000fac02 - RSNA PSK (4 bytes)
====Get AP Operating Mode (0xb7)====


'''See IEEE 802.11 specification for more details about cipher/AKM suites
* Used by VSH.
'''
* Command buffer size is '''0x10'''
* Returns AP operating mode (mixed, 11b or 11g).


802.11 spec: [http://standards.ieee.org/getieee802/download/802.11-2007.pdf]
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)


====Unknown (0x1025)====
====Set AP Operating Mode (0xb9)====


* Used by VSH.
* Used by VSH.
* Command buffer size is '''0x10'''.
* Command buffer size is '''0x10'''
* Sets preamble type, something else ???
* Sets AP operating mode (mixed, 11b or 11g).


offset 0xC - preamble mode: 0 - short, 1 - long (1 byte)
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)


====Unknown (0x1031)====
====Unknown (0xc5)====


* Used by VSH.
* Used by VSH.
* Command buffer size is '''0xe'''
* Command buffer size is '''0x10'''.
* Used in AP mode.
 
offset 0xC - ??? (4 bytes)


====Get Scan Results (0x1033)====
====Set AP WPA AKM Suite (0xc9)====


* Used by VSH.
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0x11'''.
* Command buffer size is '''0x5b0'''
* Sets WPA AKM suite in AP mode.
* Data passed to Gelic device is all 0s


=====Scan Results=====
offset 0xC - AKM suite (4 bytes)


offset 0x0 - number of scan entries (1 byte)
====Set AP WPA Group Cipher Suite (0xcf)====


offset 0x1 - array of scan entries
* Used by VSH.
* Command buffer size is '''0x10'''
* Used in AP + WPA mode.


======Scan Entry======
offset 0xC - group cipher suite: group (4 bytes)


offset 0x0 - size of this entry in bytes, this field is NOT included (2 bytes)
====Set AP WPA PSK Binary (0xd3)====


offset 0x2 - BSSID (6 bytes)
* Used by VSH.
* Command buffer size is '''0x4c'''
* Sets WPA PSK binary


offset 0x8 - RSSI (1 byte)
offset 0xC - PSK (64 bytes)


offset 0x9 - timestamp (8 bytes)
====Set AP WPA Reauthentication Timeout (0xd5)====


offset 0x11 - beacon period (2 bytes)
* Used by VSH.
* Command buffer size is '''0x10'''
* Sets WPA Reauth timeout value in AP WPA mode.
* VSH uses 36000 as timeout.


offset 0x13 - capability (2 bytes)
offset 0xC - timeout value in seconds (2 bytes)


offset 0x15 - information elements (see 802.11 specification)
====Unknown (0x127)====
 
====Start Scan (0x1035)====


* Used by VSH.
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0x10'''.
* Command buffer size depends on size of channel list and ESSID string length
* Used in AP + WPA mode.
* 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)====
====Unknown (0x12b)====


* Used by VSH.
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0x10'''.
* Command buffer size is '''0xd'''
* Used in AP + WPA mode.
* Data passed to Gelic device is all 0s


====Get RSSI (0x103d)====
====Set AP WPA PSK Passphrase (0x17d)====


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


offset 0x10 - MAC address of node (6 bytes)
offset 0xD - passphrase (32 bytes)


offset 0x16 - RSSI (1 byte)
====Set AP WPA Pairwise Cipher Suite (0x1bf)====


====Get MAC Address (0x103f)====
* Used by VSH.
* Command buffer size is '''0x11'''
* Used in AP + WPA mode.


* Command buffer size is '''0x13'''
offset 0xC - pairwise cipher suite (4 bytes)


offset 0xD - MAC address (6 bytes)
offset 0x10 - ??? (1 byte)


====Set MAC Address (0x1041)====
====Unknown (0x1d9)====


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


====Unknown (0x104d)====
====Unknown (0x1dd)====


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


offset 0xC - 0 - ???, 1 - ??? (1 byte)
====Unknown (0x1ed)====


====Unknown (0x104f)====
* Used by VSH.
* Command buffer is of size '''0x17'''.
* Rate control ???


* Command buffer size is '''0xd'''.
====Get Eurus HW Revision (0x1fb)====
* Returns 1 byte.


offset 0xC - 0 - ???, 1 - ??? (1 byte)
* Command buffer size is '''0x10'''.


====Unknown (0x1051)====
====Associate (0x1001)====


* Used by VSH.
* Used by VSH.
* Command buffer size is '''0x5b3'''.
* Used by LV1 on FAT models.
* Returns '''0x5a7''' bytes.
* Command buffer size is '''0xd'''
* Data passed to Gelic device is all 0s


offset 0xC - number of entries
====Get Common Configuration (0x1003)====


offset 0x10 - entries (each entry is 0xd bytes)
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0x18'''
* Data passed to Gelic device is all 0s


====Unknown (0x1053)====
====Set Common Configuration (0x1005)====


* Used by VSH.
* Used by VSH.
* Command buffer size is '''0x70'''.
* 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 - ??? (4 bytes)
offset 0xC - BSS type: 0 - infrastructure, 1 - ???, 2 - adhoc (1 byte)


offset 0x10 - MAC address (6 bytes)
offset 0xD - authentication mode: 0 - open, 1 - shared key


====Unknown (0x1059)====
offset 0xE - opmode: 0 - 11bg, 1 - 11b, 2 - 11g (1 byte)  


* Used by VSH.
offset 0xF - ??? (1 byte)
* Command buffer size is '''0x2a8'''.


====Unknown (0x105f)====
offset 0x10 - BSSID (6 bytes)


* Used by LV2.
offset 0x16 - capability (2 bytes)


====Get Zephyr HW Revision (0x1101)====
====Get WEP Configuration (0x1013)====


* Used by VSH.
* Used by VSH.
* Not a Gelic device command, handled by LV2 kernel.
* Used by LV1 on FAT models.
* LV2 uses LV1 call '''lv1_net_control(0x8000000000000002)'''
* Command buffer size is '''0x50'''
* Command buffer size is '''0x18'''.
* Data passed to Gelic device is all 0s


====Get MAC Address List (0x1117)====
====Set WEP Configuration (0x1015)====


* Command buffer size is '''0xce'''.
* Used by VSH.
* Returns several MAC addresses.
* Used by LV1 on FAT models.
* Command buffer size is '''0x50'''


offset 0xC - number of MAC addresses (2 bytes)
====Get WPA Configuration (0x1017)====


offset 0xE - MAC addresses (6 * number of MAC addresses)
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0x5b'''
* Data passed to Gelic device is all 0s


====Unknown (0x1133)====
====Set WPA Configuration (0x1019)====


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


====Set WOL MAC Address Filter (0x1139)====
offset 0xE - security type: 0 - WPA, 1 - RSNA (1 byte)


* Used by LV2 internally.
offset 0xF - psk type: 0 - hex, 1 - bin (1 byte)
* Command buffer is of size '''0x28'''.


====Unknown (0x113b)====
offset 0x10 - psk key: hex or bin (64 bytes)


* Used by LV2 internally.
offset 0x50 - group cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
* Command buffer size is '''0x20'''.


====Set WOL Multicast Address Filter (0x113d)====
offset 0x54 - pairwise cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)


* Used by LV2 internally.
offset 0x58 - AKM suite: 0x0050f202 - WPA PSK, 0x000fac02 - RSNA PSK (4 bytes)
* Command buffer is of size '''0x2c'''.


====Clear WOL Multicast Address Filter (0x113f)====
'''See IEEE 802.11 specification for more details about cipher/AKM suites
'''


* Used by LV2 internally.
802.11 spec: [http://standards.ieee.org/getieee802/download/802.11-2007.pdf]
* Command buffer is of size '''0x28'''.


====Unknown (0x1141)====
====Unknown (0x1025)====


* Used by LV2 internally.
* Used by VSH.
* Command buffer is of size 0x12.
* Command buffer size is '''0x10'''.
* Sets preamble type, something else ???


====Clear WOL Address Filter (0x1143)====
offset 0xC - preamble mode: 0 - short, 1 - long (1 byte)


* Used by LV2 internally.
====Unknown (0x1031)====
* Command buffer size is '''0x2c'''.


====Unknown (0x114b)====
* Used by VSH.
* Command buffer size is '''0xe'''


* Used by LV2 internally.
====Get Scan Results (0x1033)====


====Set WOL Magic Packet Mode (0x1155)====
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0x5b0'''
* Data passed to Gelic device is all 0s


* Used by LV2 internally.
=====Scan Results=====
* Command buffer is of size '''0x10'''.
* Enables/Disables WOL magic packet.


offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
offset 0x0 - number of scan entries (1 byte)


====Unknown (0x1157)====
offset 0x1 - array of scan entries


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


====Set WOL Multicast Address Filter Mode (0x1159)====
offset 0x0 - size of this entry in bytes, this field is NOT included (2 bytes)


* Used by LV2 internally.
offset 0x2 - BSSID (6 bytes)
* Command buffer size is '''0x10'''.
* WOL function


offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
offset 0x8 - RSSI (1 byte)


====Set Unicast Address Filter (0x115b)====
offset 0x9 - timestamp (8 bytes)


* Used by LV2 internally.
offset 0x11 - beacon period (2 bytes)
* 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 0x13 - capability (2 bytes)


offset 0xE - ??? (2 bytes)
offset 0x15 - information elements (see 802.11 specification)


offset 0x10 - MAC address (6 bytes)
====Start Scan (0x1035)====


====Clear Unicast Address Filter (0x115d)====
* 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


* Used by LV2 internally.
====Diassociate (0x1037)====
* Command buffer size is '''0x6a'''.


====Get Unicast Address Filter (0x115f)====
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0xd'''
* Data passed to Gelic device is all 0s


* Used by LV2 internally.
====Get RSSI (0x103d)====
* Command buffer is of size '''0x6a'''.


====Set Multicast Address Filter (0x1161)====
* Used by VSH.
* Used by LV1 on FAT models.
* Command buffer size is '''0x17'''


* Used by LV2 internally.
offset 0x10 - MAC address of node (6 bytes)
* Command buffer size is '''0x2c'''.


====Clear Multicast Address Filter (0x1163)====
offset 0x16 - RSSI (1 byte)


* Used by LV2 internally.
====Get MAC Address (0x103f)====
* Command buffer size is '''0x2c'''
* To clear all multicast addresses send command with all 0s.


offset 0xC - multicast address filter (4 * 8 bytes)
* Command buffer size is '''0x13'''


====Get Multicast Address Filter (0x1165)====
offset 0xD - MAC address (6 bytes)


* Used by LV2 internally.
====Set MAC Address (0x1041)====
* Command buffer is of size '''0x2c'''.


====Set WOL Address Filter (0x1167)====
* Used by VSH.
* Used by LV1 too.
* Command buffer size is '''0x12'''


* Used by LV2 internally.
====Unknown (0x104d)====
* Command buffer size is '''0x70'''.


====Set WOL Address Filter Mode (0x116d)====
* Used by VSH.
* Command buffer size is '''0xd'''.


* Used by LV2 internally.
offset 0xC - 0 - ???, 1 - ??? (1 byte)
* Command buffer size is '''0x10'''.
* Enables/Disables WOL address matching


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


====Set Unicast Address Filter Mode (0x116f)====
* Command buffer size is '''0xd'''.
* Returns 1 byte.


* Used by LV2 internally.
offset 0xC - 0 - ???, 1 - ??? (1 byte)
* Command buffer size is '''0x10'''.


offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
====Unknown (0x1051)====
 
====Get Device Status (0xfffb)====


* Used by VSH.
* Used by VSH.
* Not a Gelic device command, handled by LV2 kernel.
* Command buffer size is '''0x5b3'''.
* Returned data size in command buffer is '''0x10'''.
* Returns '''0x5a7''' bytes.
 
offset 0xC - number of entries
 
offset 0x10 - entries (each entry is 0xd bytes)


====Unknown (0xfffc)====
====Unknown (0x1053)====


* Used by VSH.
* Used by VSH.
* Not a Gelic device command, handled by LV2 kernel.
* Command buffer size is '''0x70'''.
* LV2 uses LV1 call '''lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x4, 0x0, 0x0)'''
 
offset 0xC - ??? (4 bytes)
 
offset 0x10 - MAC address (6 bytes)


====Get Channel Information (0xfffd)====
====Unknown (0x1059)====


* Used by VSH.
* Used by VSH.
* Not a Gelic device command, handled by LV2 kernel.
* Command buffer size is '''0x2a8'''.
* 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)====
====Unknown (0x105f)====


* Used by VSH.
* Used by LV2.
* 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)====
====Get Zephyr HW Revision (0x1101)====


* Used by VSH.
* Used by VSH.
* Not a Gelic device command, handled by LV2 kernel.
* Not a Gelic device command, handled by LV2 kernel.
* Returns 0x10 bytes in command buffer.
* LV2 uses LV1 call '''lv1_net_control(0x8000000000000002)'''
* Returns gelic device state ???
* Command buffer size is '''0x18'''.


=== Events ===
====Get MAC Address List (0x1117)====


<pre>
* Command buffer size is '''0xce'''.
struct ps3_eurus_event_hdr {
* Returns several MAC addresses.
__le32 type;
__le32 id;
__le32 timestamp;
__le32 payload_length;
__le32 unknown;
} __packed;


struct ps3_eurus_event {
offset 0xC - number of MAC addresses (2 bytes)
struct ps3_eurus_event_hdr hdr;
u8 payload[44];
} __packed;
</pre>


====Event Type 0x00000040====
offset 0xE - MAC addresses (6 * number of MAC addresses)


{| class="wikitable"
====Unknown (0x1133)====
|-
! Id !! Description
|-
| 0x00000001 || Deauthenticated
|}


====Event Type 0x00000080====
* Used by VSH.
* Command buffer size is '''0x1A'''.
 
====Set WOL MAC Address Filter (0x1139)====
 
* Used by LV2 internally.
* Command buffer is of size '''0x28'''.


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


====Event Type 0x80000000====
* Used by LV2 internally.
* Command buffer size is '''0x20'''.


{| class="wikitable"
====Set WOL Multicast Address Filter (0x113d)====
|-
! Id !! Description
|-
| 0x00000001 || Device Ready
|}


== Enabling WLAN Gelic On FAT ==
* Used by LV2 internally.
* Command buffer is of size '''0x2c'''.


Linux kernel doesn't use Gelic Device Control Interface like GameOS does it.
====Clear WOL Multicast Address Filter (0x113f)====
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.
* Used by LV2 internally.
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.
* Command buffer is of size '''0x28'''.


To disable Gelic Device Control Interface on Linux, first unload Gelic device driver, then set
====Unknown (0x1141)====
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.
* Used by LV2 internally.
* Command buffer is of size 0x12.


====Clear WOL Address Filter (0x1143)====


==USB WLAN Interface (Codename Jupiter 2)==
* Used by LV2 internally.
* Command buffer size is '''0x2c'''.


* On new PS3 models, WLAN interface is USB.
====Unknown (0x114b)====
* '''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===
* Used by LV2 internally.


* LV2 uses 3 USB endpoints of interface 3,4 and 5 to communicate with WLAN.
====Set WOL Magic Packet Mode (0x1155)====
* Endpoints EP5 IN/OUT, EP6 IN/OUT and EP7 IN/OUT.
 
* '''WLAN commands''' are sent to endpoint '''EP5 OUT''' with '''interrupt transfers'''.
* Used by LV2 internally.
* '''WLAN events''' and '''WLAN command responses''' are received on endpoint '''EP5 IN''' with '''interrupt transfers'''.
* Command buffer is of size '''0x10'''.
* LV2 opens a USB communication pipe to endpoint EP5 IN and EP5 OUT.
* Enables/Disables WOL magic packet.
* 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.
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
* 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.
====Unknown (0x1157)====
* '''lsusb is buggy on big-endian arch and shows some fields with bytes swapped !!!'''
 
* 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)


<pre>
offset 0x10 - MAC address (6 bytes)
Bus 002 Device 002: ID 054c:036f Sony Corp.
 
Device Descriptor:
====Clear Unicast Address Filter (0x115d)====
  bLength                18
 
  bDescriptorType        1
* Used by LV2 internally.
  bcdUSB              2.00
* Command buffer size is '''0x6a'''.
  bDeviceClass          224 Wireless
 
  bDeviceSubClass        1 Radio Frequency
====Get Unicast Address Filter (0x115f)====
  bDeviceProtocol        1 Bluetooth
 
  bMaxPacketSize0        64
* Used by LV2 internally.
  idVendor          0x054c Sony Corp.
* Command buffer is of size '''0x6a'''.
  idProduct          0x036f
 
  bcdDevice          20.12
====Set Multicast Address Filter (0x1161)====
  iManufacturer          1
 
  iProduct                2
* Used by LV2 internally.
  iSerial                0
* Command buffer size is '''0x2c'''.
  bNumConfigurations      1
 
    Interface Descriptor:
====Clear Multicast Address Filter (0x1163)====
      bLength                9
 
      bDescriptorType        4
* Used by LV2 internally.
      bInterfaceNumber        3
* Command buffer size is '''0x2c'''
      bAlternateSetting      0
* To clear all multicast addresses send command with all 0s.
      bNumEndpoints          2
 
      bInterfaceClass      255 Vendor Specific Class
offset 0xC - multicast address filter (4 * 8 bytes)
      bInterfaceSubClass      2
 
      bInterfaceProtocol      1
====Get Multicast Address Filter (0x1165)====
      iInterface              0
 
      Endpoint Descriptor:
* Used by LV2 internally.
        bLength                7
* Command buffer is of size '''0x2c'''.
        bDescriptorType        5
 
        bEndpointAddress    0x85  EP 5 IN
====Set WOL Address Filter (0x1167)====
        bmAttributes            3
 
          Transfer Type            Interrupt
* Used by LV2 internally.
          Synch Type              None
* Command buffer size is '''0x70'''.
          Usage Type              Data
 
        wMaxPacketSize    0x4000  1x 0 bytes
====Set WOL Address Filter Mode (0x116d)====
        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===
* Used by LV2 internally.
* Command buffer size is '''0x10'''.
* Enables/Disables WOL address matching


* LV2 does 2 control transfers to EP0 during WLAN initialization
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
* 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 ====
====Set Unicast Address Filter Mode (0x116f)====


<pre>
* Used by LV2 internally.
unsigned char ps3_usb_wlan_magic_data[] = {
* Command buffer size is '''0x10'''.
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 ====
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)


* Implemented in LV2.
====Get Device Status (0xfffb)====


=====State 1=====
* Used by VSH.
* Not a Gelic device command, handled by LV2 kernel.
* Returned data size in command buffer is '''0x10'''.


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


=====State 2=====
* 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)'''


* Command '''0x1171''' is sent to WLAN device.
====Get Channel Information (0xfffd)====


=====State 3=====
* 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


* LV2 waits for an event from WLAN device.
====Set Response Timeout (0xfffe)====


=====State 4=====
* 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'''.


* Command '''0x116f''' is sent to WLAN device.
====Unknown (0xffff)====


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


* Command '''0x115b''' is sent to WLAN device.
=== Events ===
* Command data sent to WLAN device contains MAC address.


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


* Command '''0x1161''' is sent to WLAN device.
struct ps3_eurus_event {
* Sets multicast address filter.
struct ps3_eurus_event_hdr hdr;
u8 payload[44];
} __packed;
</pre>


=====State 7=====
====Event Type 0x00000040====


* Command '''0x110d''' is sent to WLAN device.
{| class="wikitable"
|-
! Id !! Description
|-
| 0x00000001 || Deauthenticated
|}


=====State 8=====
====Event Type 0x00000080====


* Command '''0x1031''' is sent to WLAN device.
{| class="wikitable"
|-
! Id !! Description
|-
| 0x00000001 || Beacon Lost
|-
| 0x00000002 || Connected
|-
| 0x00000004 || Scan Completed
|-
| 0x00000020 || WPA Connected
|-
| 0x00000040 || WPA Error (MIC Error)
|}


=====State 9=====
====Event Type 0x80000000====


* Command '''0x1041''' is sent to WLAN device.
{| class="wikitable"
* Command data sent to WLAN device contains MAC address.
|-
! Id !! Description
|-
| 0x00000001 || Device Ready
|}


=====State 10=====
== Enabling WLAN Gelic On FAT ==


* Command '''0x29''' is sent to WLAN device.
Linux kernel doesn't use Gelic Device Control Interface like GameOS does it.
* Sets antenna.
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.


=====State 11=====
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.


* Command '''0x110b''' is sent to WLAN device.
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.


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


* Command '''0x1109''' is sent to WLAN device.


=====State 13=====
==USB WLAN Interface (Codename Jupiter 2)==


* Command '''0x207''' is sent to WLAN device.
* 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.


=====State 14=====
===Endpoints===


* Command '''0x203''' is sent to WLAN device.
* 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.
=====State 15=====
* '''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'''.
* Command '''0x105f''' is sent to WLAN device.
* LV2 opens a USB communication pipe to endpoint EP5 IN and EP5 OUT.
* Command data sent to WLAN device contains MAC address, channel info and region code.
* 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.
=====State 16=====
* 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 !!!'''


* 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>
<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===
* 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;
* LV2 does 2 control transfers to EP0 during WLAN initialization
static libusb_device_handle *usb_dev_handle;
* 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.


static struct libusb_transfer *usb_intr_transfer_ep5_in;
==== Magic Data in Control Transfer ====
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];
<pre>
 
unsigned char ps3_usb_wlan_magic_data[] = {
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,
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,
0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d,
};
};
</pre>


static unsigned char my_mac_addr[] = {
==== Initialization State Machine ====
0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
};


/*
* Implemented in LV2.
* hexdump
*/
static void hexdump(const unsigned char *data, unsigned int data_size)
{
int i, j;


for (i = 0; i < data_size; i += 16) {
=====State 1=====
fprintf(stdout, "%08x:", i);


for (j = 0; j < 16; j++) {
* Command '''0x114f''' is sent to WLAN device.
if (i + j < data_size) {
fprintf(stdout, " %02x", data[i + j]);
} else {
fprintf(stdout, "  ");
}
}


fprintf(stdout, " |");
=====State 2=====


for (j = 0; j < 16; j++) {
* Command '''0x1171''' is sent to WLAN device.
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");
=====State 3=====
}
}


/*
* LV2 waits for an event from WLAN device.
* 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__);


/*
=====State 4=====
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);
* Command '''0x116f''' is sent to WLAN device.
}


/*
=====State 5=====
* 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__);
* Command '''0x115b''' is sent to WLAN device.
* Command data sent to WLAN device contains MAC address.


wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
=====State 6=====
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);


/* convert all header fields to big-endian byte order !!! */
* Command '''0x1161''' is sent to WLAN device.
* Sets multicast address filter.


wlan_cmd_pkt_hdr->unknown5 = le16toh(wlan_cmd_pkt_hdr->unknown5);
=====State 7=====
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 */
* Command '''0x110d''' is sent to WLAN device.
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 */


/*
=====State 8=====
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__);
* Command '''0x1031''' is sent to WLAN device.
fprintf(stdout, "%s:%d: command (0x%04x)\n", __func__, __LINE__,
wlan_cmd_hdr->command);


if ((usb_wlan_cmd + 1) != wlan_cmd_hdr->command)
=====State 9=====
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__,
* Command '''0x1041''' is sent to WLAN device.
wlan_cmd_hdr->tag);
* Command data sent to WLAN device contains MAC address.
fprintf(stdout, "%s:%d: status (0x%04x)\n", __func__, __LINE__,
wlan_cmd_hdr->status);


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


fprintf(stdout, "%s:%d: payload_size (0x%04x)\n", __func__, __LINE__,
* Command '''0x29''' is sent to WLAN device.
wlan_cmd_hdr->payload_size);
* Sets antenna.


fprintf(stdout, "%s:%d: command payload:\n", __func__, __LINE__);
=====State 11=====


hexdump(wlan_cmd_payload, wlan_cmd_hdr->payload_size);
* Command '''0x110b''' is sent to WLAN device.


memcpy(usb_wlan_cmd_data, wlan_cmd_payload, wlan_cmd_hdr->payload_size);
=====State 12=====


pthread_mutex_lock(&usb_wlan_cmd_mutex);
* Command '''0x1109''' is sent to WLAN device.


usb_wlan_cmd_busy = 0;
=====State 13=====


pthread_cond_signal(&usb_wlan_cmd_cond);
* Command '''0x207''' is sent to WLAN device.


pthread_mutex_unlock(&usb_wlan_cmd_mutex);
=====State 14=====
}


/*
* Command '''0x203''' is sent to WLAN device.
* 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__);
=====State 15=====


fprintf(stdout, "%s:%d: transfer status (%d) length (%d)\n",
* Command '''0x105f''' is sent to WLAN device.
__func__, __LINE__, transfer->status, transfer->actual_length);
* Command data sent to WLAN device contains MAC address, channel info and region code.


wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) transfer->buffer;
=====State 16=====


if (wlan_cmd_pkt_hdr->unknown3 == 0x6)
* LV2 waits for an event from WLAN device.
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));
=====State 17=====


libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
* LV2 accepts commands sent by LV2 syscall 726.
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);
===Test Program===
if (error) {
 
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
* Here is a small program which executes a WLAN scan.
__func__, __LINE__, error);
* I used libusb.
exit(1);
 
}
====Source Code====
}
<pre>


/*
/*
  * usb_intr_transfer_ep5_out_cb
  * 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.
  */
  */
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);
#include <stdio.h>
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>


libusb_free_transfer(transfer);
#include <libusb-1.0/libusb.h>
}


/*
#define USB_VENDOR_ID 0x054c /* $ONY */
* usb_wlan_cmd_send
#define USB_PRODUCT_ID 0x036f
*/
#define USB_IFACE_NUMBER 3
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",
#define USB_INTR_TRANSFER_EP5_IN_BUF_SIZE 0x800
__func__, __LINE__, command, data_size, data_size + sizeof(struct wlan_cmd_hdr));
#define USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE 0x800


transfer = libusb_alloc_transfer(0);
struct wlan_cmd_pkt_hdr {
if (!transfer) {
uint8_t unknown1;
fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__);
uint8_t unknown2;
error = -1;
uint8_t unknown3;
goto fail;
uint8_t unknown4;
}
uint16_t unknown5;
uint8_t res1[2];
uint16_t tag;
uint8_t res2[14];
} __attribute__ ((packed));


wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) usb_intr_transfer_ep5_out_buf;
struct wlan_cmd_hdr {
wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
uint16_t command;
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);
uint16_t tag;
uint16_t status;
uint16_t payload_size;
uint8_t res[4];
} __attribute__ ((packed));


wlan_cmd_pkt_hdr->unknown1 = 0x1;
struct wlan_event_pkt_hdr {
wlan_cmd_pkt_hdr->unknown2 = 0x1;
uint8_t unknown1;
wlan_cmd_pkt_hdr->unknown3 = 0x6;
uint8_t unknown2;
wlan_cmd_pkt_hdr->unknown4 = 0x0;
uint8_t unknown3;
wlan_cmd_pkt_hdr->unknown5 = 0x1;
uint8_t event_count;
wlan_cmd_pkt_hdr->tag = 0xf00d; /* returned in response */
} __attribute__ ((packed));


wlan_cmd_hdr->command = command;
static libusb_context *usb_ctx;
wlan_cmd_hdr->tag = 0xcafe; /* returned in response */
static libusb_device_handle *usb_dev_handle;
wlan_cmd_hdr->status = 0xa;
wlan_cmd_hdr->payload_size = data_size;


memcpy(wlan_cmd_payload, data, data_size);
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];


usb_wlan_cmd = command;
static unsigned char usb_intr_transfer_ep5_out_buf[USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE];
usb_wlan_cmd_data = (void *) data;


libusb_fill_interrupt_transfer(transfer, usb_dev_handle, LIBUSB_ENDPOINT_OUT | 0x5,
static pthread_mutex_t usb_wlan_cmd_mutex;
usb_intr_transfer_ep5_out_buf,
static pthread_cond_t usb_wlan_cmd_cond;
sizeof(struct wlan_cmd_pkt_hdr) + sizeof(struct wlan_cmd_hdr) + wlan_cmd_hdr->payload_size,
static int volatile usb_wlan_cmd_busy;
usb_intr_transfer_ep5_out_cb, NULL, 0);
static uint16_t usb_wlan_cmd;
static void *usb_wlan_cmd_data;


/* convert all header fields to little-endian byte order !!! */
static int volatile usb_wlan_cmd_thread_done;


wlan_cmd_pkt_hdr->unknown5 = htole16(wlan_cmd_pkt_hdr->unknown5);
/*
wlan_cmd_pkt_hdr->tag = htole16(wlan_cmd_pkt_hdr->tag);
* 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,
};


wlan_cmd_hdr->command = htole16(wlan_cmd_hdr->command);
static unsigned char my_mac_addr[] = {
wlan_cmd_hdr->tag = htole16(wlan_cmd_hdr->tag);
0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
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) {
* hexdump
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
*/
__func__, __LINE__, error);
static void hexdump(const unsigned char *data, unsigned int data_size)
goto fail_free_transfer;
{
}
int i, j;


pthread_mutex_lock(&usb_wlan_cmd_mutex);
for (i = 0; i < data_size; i += 16) {
fprintf(stdout, "%08x:", i);


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


while (usb_wlan_cmd_busy)
fprintf(stdout, " |");
pthread_cond_wait(&usb_wlan_cmd_cond, &usb_wlan_cmd_mutex);


pthread_mutex_unlock(&usb_wlan_cmd_mutex);
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, " ");
}
}


return 0;
fprintf(stdout, "|\n");
 
}
fail_free_transfer:
 
libusb_free_transfer(transfer);
 
fail:
 
return error;
}
}


/*
/*
  * usb_wlan_cmd_start_scan
  * usb_handle_wlan_event
  */
  */
static int usb_wlan_cmd_start_scan(void)
static void usb_handle_wlan_event(struct wlan_event_pkt_hdr *wlan_event_pkt_hdr)
{
{
unsigned char data[256], *ptr;
fprintf(stdout, "%s:%d: === got WLAN event ===\n", __func__, __LINE__);
unsigned int data_size;


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


ptr = data;
hexdump((unsigned char *) (wlan_event_pkt_hdr + 1), wlan_event_pkt_hdr->event_count * 64);
*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
  * usb_handle_wlan_cmd_response
  */
  */
static int usb_wlan_cmd_get_scan_results(void)
static void usb_handle_wlan_cmd_response(struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr)
{
{
unsigned char data[1456];
struct wlan_cmd_hdr *wlan_cmd_hdr;
unsigned int data_size;
uint8_t *wlan_cmd_payload;
 
fprintf(stdout, "%s:%d: === got WLAN command response ===\n", __func__, __LINE__);


memset(data, 0, sizeof(data));
wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);


data_size = sizeof(data);
/* convert all header fields to big-endian byte order !!! */


return usb_wlan_cmd_send(0x1033, data, data_size);
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 */
* usb_wlan_cmd_0x99
wlan_cmd_hdr->tag = le16toh(wlan_cmd_hdr->tag); /* returned from request */
*/
wlan_cmd_hdr->status = le16toh(wlan_cmd_hdr->status); /* 1 - success
static int usb_wlan_cmd_0x99(void)
  2 - invalid parameters ???
{
  3 - invalid command ??? */
unsigned char data[0x3e];
wlan_cmd_hdr->payload_size = le16toh(wlan_cmd_hdr->payload_size); /* length of data that follows the header */
unsigned int data_size;


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


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


return usb_wlan_cmd_send(0x99, data, data_size);
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__,
* usb_wlan_init
wlan_cmd_hdr->tag);
*/
fprintf(stdout, "%s:%d: status (0x%04x)\n", __func__, __LINE__,
static int usb_wlan_init(void)
wlan_cmd_hdr->status);
{
unsigned char data[1456], *ptr;
unsigned int data_size;
int error;


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


memset(data, 0, sizeof(data));
fprintf(stdout, "%s:%d: payload_size (0x%04x)\n", __func__, __LINE__,
wlan_cmd_hdr->payload_size);


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


error = usb_wlan_cmd_send(0x114f, data, data_size);
hexdump(wlan_cmd_payload, wlan_cmd_hdr->payload_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x114f (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
memcpy(usb_wlan_cmd_data, wlan_cmd_payload, wlan_cmd_hdr->payload_size);


/* state 0x2 */
pthread_mutex_lock(&usb_wlan_cmd_mutex);


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


data_size = 0;
pthread_cond_signal(&usb_wlan_cmd_cond);


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


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


/* wait for a WLAN event */
fprintf(stdout, "%s:%d: === got interrupt transfer ===\n", __func__, __LINE__);


/* state 0x4 */
fprintf(stdout, "%s:%d: transfer status (%d) length (%d)\n",
__func__, __LINE__, transfer->status, transfer->actual_length);


memset(data, 0, sizeof(data));
wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) transfer->buffer;


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


*ptr++ = 0x1;
memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf));


data_size = 0x4;
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 = usb_wlan_cmd_send(0x116f, data, data_size);
error = libusb_submit_transfer(usb_intr_transfer_ep5_in);
if (error) {
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x116f (%d)\n",
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
__func__, __LINE__, error);
__func__, __LINE__, error);
return error;
exit(1);
}
}
}


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


/* state 0x5 */
fprintf(stdout, "%s:%d: transfer status (%d)\n", __func__, __LINE__, transfer->status);
*/


memset(data, 0, sizeof(data));
libusb_free_transfer(transfer);
}


ptr = data;
/*
 
* usb_wlan_cmd_send
*ptr++ = 0x1;
*/
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;


ptr = data + 0x4;
fprintf(stdout, "%s:%d: sending command (0x%04x) data size (0x%04x) command size (0x%04x)\n",
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));
__func__, __LINE__, command, data_size, data_size + sizeof(struct wlan_cmd_hdr));


data_size = 0x5e;
transfer = libusb_alloc_transfer(0);
 
if (!transfer) {
error = usb_wlan_cmd_send(0x115b, data, data_size);
fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__);
if (error) {
error = -1;
fprintf(stderr, "%s:%d: could not send command 0x115b (%d)\n",
goto fail;
__func__, __LINE__, error);
return error;
}
}


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


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


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


ptr = data + 0x1c;
memcpy(wlan_cmd_payload, data, data_size);


*ptr++ = 0x20;
usb_wlan_cmd = command;
usb_wlan_cmd_data = (void *) data;


data_size = 0x20;
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);


error = usb_wlan_cmd_send(0x1161, data, data_size);
/* convert all header fields to little-endian byte order !!! */
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1161 (%d)\n",
__func__, __LINE__, error);
return error;
}


sleep(2);
wlan_cmd_pkt_hdr->unknown5 = htole16(wlan_cmd_pkt_hdr->unknown5);
wlan_cmd_pkt_hdr->tag = htole16(wlan_cmd_pkt_hdr->tag);


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


ptr = data + 0xc;
error = libusb_submit_transfer(transfer);
memset(ptr, 0xff, 7 * 4);
 
data_size = 0x80;
 
error = usb_wlan_cmd_send(0x110d, data, data_size);
if (error) {
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x110d (%d)\n",
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
__func__, __LINE__, error);
__func__, __LINE__, error);
return error;
goto fail_free_transfer;
}
}


sleep(2);
pthread_mutex_lock(&usb_wlan_cmd_mutex);


memset(data, 0, sizeof(data));
usb_wlan_cmd_busy = 1;


data_size = 0x2;
while (usb_wlan_cmd_busy)
pthread_cond_wait(&usb_wlan_cmd_cond, &usb_wlan_cmd_mutex);


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


sleep(2);
return 0;


memset(data, 0, sizeof(data));
fail_free_transfer:


ptr = data;
libusb_free_transfer(transfer);
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));


data_size = 0x6;
fail:


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


sleep(2);
/*
 
* usb_wlan_cmd_start_scan
/* state 0xa */
*/
static int usb_wlan_cmd_start_scan(void)
{
unsigned char data[256], *ptr;
unsigned int data_size;


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


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


*ptr++ = 0x2;
ptr = data + 0xa;
*ptr++ = 0x2;
*ptr++ = 0x3;


data_size = 0x2;
*ptr++ = 13; /* number of channels */
 
*ptr++ = 1; /* channels */
error = usb_wlan_cmd_send(0x29, data, data_size);
*ptr++ = 2;
if (error) {
*ptr++ = 3;
fprintf(stderr, "%s:%d: could not send command 0x29 (%d)\n",
*ptr++ = 4;
__func__, __LINE__, error);
*ptr++ = 5;
return error;
*ptr++ = 6;
}
*ptr++ = 7;
*ptr++ = 8;
*ptr++ = 9;
*ptr++ = 10;
*ptr++ = 11;
*ptr++ = 12;
*ptr++ = 13;


sleep(2);
data_size = ptr - data;


memset(data, 0, sizeof(data));
return usb_wlan_cmd_send(0x1035, data, data_size);
}


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


*ptr++ = 0x1;
memset(data, 0, sizeof(data));


ptr = data + 8;
data_size = sizeof(data);


*ptr++ = 0x20;
return usb_wlan_cmd_send(0x1033, data, data_size);
}


data_size = 0xc;
/*
 
* usb_wlan_cmd_0x99
error = usb_wlan_cmd_send(0x110b, data, data_size);
*/
if (error) {
static int usb_wlan_cmd_0x99(void)
fprintf(stderr, "%s:%d: could not send command 0x110b (%d)\n",
{
__func__, __LINE__, error);
unsigned char data[0x3e];
return error;
unsigned int data_size;
}
 
sleep(2);


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


ptr = data;
data_size = sizeof(data);


*ptr++ = 0x1;
return usb_wlan_cmd_send(0x99, data, data_size);
}


ptr = data + 0x4;
/*
 
* usb_wlan_init
*ptr++ = 0x15;
*/
*ptr++ = 0x27;
static int usb_wlan_init(void)
{
unsigned char data[1456], *ptr;
unsigned int data_size;
int error;
 
/* state 0x1 */


*ptr++ = 0x12;
memset(data, 0, sizeof(data));
*ptr++ = 0x0;


*ptr++ = 0x6;
data_size = 0x518;
*ptr++ = 0x0;


ptr = data + 0xc;
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;
}


*ptr++ = 0x9;
sleep(2);
*ptr++ = 0x0;
*ptr++ = 0x1;


ptr = data + 0x10;
/* state 0x2 */


*ptr++ = 0xff;
memset(data, 0, sizeof(data));
*ptr++ = 0xff;
*ptr++ = 0xff;
*ptr++ = 0xff;
*ptr++ = 0xff;
*ptr++ = 0xff;


data_size = 0x16;
data_size = 0;


error = usb_wlan_cmd_send(0x1109, data, data_size);
error = usb_wlan_cmd_send(0x1171, data, data_size);
if (error) {
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1109 (%d)\n",
fprintf(stderr, "%s:%d: could not send command 0x1171 (%d)\n",
__func__, __LINE__, error);
__func__, __LINE__, error);
return error;
return error;
Line 7,904: Line 8,402:


sleep(2);
sleep(2);
/* wait for a WLAN event */
/* state 0x4 */


memset(data, 0, sizeof(data));
memset(data, 0, sizeof(data));
Line 7,913: Line 8,415:
data_size = 0x4;
data_size = 0x4;


error = usb_wlan_cmd_send(0x207, data, data_size);
error = usb_wlan_cmd_send(0x116f, data, data_size);
if (error) {
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x207 (%d)\n",
fprintf(stderr, "%s:%d: could not send command 0x116f (%d)\n",
__func__, __LINE__, error);
__func__, __LINE__, error);
return error;
return error;
Line 7,921: Line 8,423:


sleep(2);
sleep(2);
/* state 0x5 */


memset(data, 0, sizeof(data));
memset(data, 0, sizeof(data));
Line 7,926: Line 8,430:
ptr = data;
ptr = data;


*ptr++ = 0x4;
*ptr++ = 0x1;
 
ptr = data + 0x4;
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));


data_size = 0x4;
data_size = 0x5e;


error = usb_wlan_cmd_send(0x203, data, data_size);
error = usb_wlan_cmd_send(0x115b, data, data_size);
if (error) {
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x203 (%d)\n",
fprintf(stderr, "%s:%d: could not send command 0x115b (%d)\n",
__func__, __LINE__, error);
__func__, __LINE__, error);
return error;
return error;
Line 7,939: Line 8,446:
sleep(2);
sleep(2);


/* state 0xf */
/* state 0x6 */


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


ptr = data;
ptr = data + 0x1c;


*ptr++ = 0xff;
*ptr++ = 0x20;
*ptr++ = 0x1f;


memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));
data_size = 0x20;


ptr = data + 0x8;
error = usb_wlan_cmd_send(0x1161, data, data_size);
 
*ptr++ = 0x2;
*ptr++ = 0x2;
 
data_size = 0xa;
 
error = usb_wlan_cmd_send(0x105f, data, data_size);
if (error) {
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x105f (%d)\n",
fprintf(stderr, "%s:%d: could not send command 0x1161 (%d)\n",
__func__, __LINE__, error);
__func__, __LINE__, error);
return error;
return error;
}
}


return 0;
sleep(2);
}


/*
memset(data, 0, sizeof(data));
* usb_wlan_cmd_thread
*/
static void *usb_wlan_cmd_thread(void *arg)
{
int error;


error = usb_wlan_init();
ptr = data + 0xc;
if (error) {
memset(ptr, 0xff, 7 * 4);
fprintf(stderr, "%s:%d: could not initialize device (%d)\n",
__func__, __LINE__, error);
goto done;
}


sleep(5);
data_size = 0x80;


error = usb_wlan_cmd_0x99();
error = usb_wlan_cmd_send(0x110d, data, data_size);
if (error) {
if (error) {
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
fprintf(stderr, "%s:%d: could not send command 0x110d (%d)\n",
__func__, __LINE__, error);
__func__, __LINE__, error);
goto done;
return error;
}
}


error = usb_wlan_cmd_start_scan();
sleep(2);
if (error) {
 
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
memset(data, 0, sizeof(data));
__func__, __LINE__, error);
goto done;
}


sleep(10);
data_size = 0x2;


error = usb_wlan_cmd_get_scan_results();
error = usb_wlan_cmd_send(0x1031, data, data_size);
if (error) {
if (error) {
fprintf(stderr, "%s:%d: could not get scan results (%d)\n",
fprintf(stderr, "%s:%d: could not send command 0x1031 (%d)\n",
__func__, __LINE__, error);
__func__, __LINE__, error);
goto done;
return error;
}
}


sleep(10);
sleep(2);


done:
memset(data, 0, sizeof(data));


usb_wlan_cmd_thread_done = 1;
ptr = data;
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));


return NULL;
data_size = 0x6;
}


/*
error = usb_wlan_cmd_send(0x1041, data, data_size);
* 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) {
if (error) {
fprintf(stderr, "%s:%d: libusb_init failed (%d)\n", __func__, __LINE__, error);
fprintf(stderr, "%s:%d: could not send command 0x1041 (%d)\n",
exit(1);
__func__, __LINE__, error);
return error;
}
}


libusb_set_debug(usb_ctx, 5);
sleep(2);
 
/* state 0xa */


usb_dev_handle = libusb_open_device_with_vid_pid(usb_ctx, USB_VENDOR_ID, USB_PRODUCT_ID);
memset(data, 0, sizeof(data));
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)) {
ptr = data;
fprintf(stdout, "%s:%d: kernel driver is attached\n", __func__, __LINE__);


error = libusb_detach_kernel_driver(usb_dev_handle, USB_IFACE_NUMBER);
*ptr++ = 0x2;
if (error) {
*ptr++ = 0x2;
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__);
data_size = 0x2;
}


error = libusb_claim_interface(usb_dev_handle, USB_IFACE_NUMBER);
error = usb_wlan_cmd_send(0x29, data, data_size);
if (error) {
if (error) {
fprintf(stderr, "%s:%d: could not claim interface (%d)\n",
fprintf(stderr, "%s:%d: could not send command 0x29 (%d)\n",
__func__, __LINE__, error);
__func__, __LINE__, error);
exit(1);
return error;
}
}


error = libusb_control_transfer(usb_dev_handle, 0x40, 0x1, 0x9, 0x0,
sleep(2);
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);
memset(data, 0, sizeof(data));


error = libusb_control_transfer(usb_dev_handle, 0xc0, 0x0, 0x2, 0x0, buf, 2, 0);
ptr = data;
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);
*ptr++ = 0x1;


fprintf(stdout, "%s:%d: 0x%02x 0x%02x\n", __func__, __LINE__, buf[0], buf[1]);
ptr = data + 8;


usb_intr_transfer_ep5_in = libusb_alloc_transfer(0);
*ptr++ = 0x20;
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));
data_size = 0xc;


libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
error = usb_wlan_cmd_send(0x110b, data, data_size);
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) {
if (error) {
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
fprintf(stderr, "%s:%d: could not send command 0x110b (%d)\n",
__func__, __LINE__, error);
__func__, __LINE__, error);
exit(1);
return error;
}
}


error = pthread_create(&tid, NULL, usb_wlan_cmd_thread, NULL);
sleep(2);
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) {
memset(data, 0, sizeof(data));
tv.tv_sec = 1;
tv.tv_usec = 0;


error = libusb_handle_events_timeout(usb_ctx, &tv);
ptr = data;
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);
*ptr++ = 0x1;


error = libusb_release_interface(usb_dev_handle, USB_IFACE_NUMBER);
ptr = data + 0x4;
if (error)
fprintf(stderr, "%s:%d: could not release interface (%d)\n",
__func__, __LINE__, error);


libusb_close(usb_dev_handle);
*ptr++ = 0x15;
*ptr++ = 0x27;


libusb_exit(usb_ctx);
*ptr++ = 0x12;
*ptr++ = 0x0;


exit(0);
*ptr++ = 0x6;
}
*ptr++ = 0x0;
</pre>
 
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;


====Output====
*ptr++ = 0x1;


<pre>
data_size = 0x4;
glevand@debian-hdd:~/ps3_usb_wlan$ sudo ./ps3_usb_wlan
 
sudo: unable to resolve host debian-hdd
error = usb_wlan_cmd_send(0x207, data, data_size);
main:824: number of bytes transferred (32)
if (error) {
main:833: number of bytes received (2)
fprintf(stderr, "%s:%d: could not send command 0x207 (%d)\n",
main:835: 0x20 0x31
__func__, __LINE__, error);
usb_wlan_cmd_send:288: sending command (0x114f) data size (0x0518) command size (0x0524)
return error;
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 ===
sleep(2);
usb_handle_wlan_cmd_response:191: command header:
 
usb_handle_wlan_cmd_response:192: command (0x1150)
memset(data, 0, sizeof(data));
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0006)
ptr = data;
usb_handle_wlan_cmd_response:205: ==> command status != 0x1
 
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
*ptr++ = 0x4;
usb_handle_wlan_cmd_response:210: command payload:
 
usb_wlan_cmd_send:288: sending command (0x1171) data size (0x0000) command size (0x000c)
data_size = 0x4;
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
error = usb_wlan_cmd_send(0x203, data, data_size);
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
if (error) {
usb_handle_wlan_cmd_response:191: command header:
fprintf(stderr, "%s:%d: could not send command 0x203 (%d)\n",
usb_handle_wlan_cmd_response:192: command (0x1172)
__func__, __LINE__, error);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
return error;
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:
sleep(2);
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68)
/* state 0xf */
usb_handle_wlan_event:133: === got WLAN event ===
 
usb_handle_wlan_event:144: event_count (0x01)
memset(data, 0, sizeof(data));
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 |................|
ptr = data;
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 |................|
*ptr++ = 0xff;
usb_wlan_cmd_send:288: sending command (0x116f) data size (0x0004) command size (0x0010)
*ptr++ = 0x1f;
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
ptr = data + 0x8;
usb_handle_wlan_cmd_response:192: command (0x1170)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
*ptr++ = 0x2;
usb_handle_wlan_cmd_response:201: status (0x0001)
*ptr++ = 0x2;
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
 
usb_handle_wlan_cmd_response:210: command payload:
data_size = 0xa;
usb_wlan_cmd_send:288: sending command (0x115b) data size (0x005e) command size (0x006a)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
error = usb_wlan_cmd_send(0x105f, data, data_size);
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
if (error) {
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
fprintf(stderr, "%s:%d: could not send command 0x105f (%d)\n",
usb_handle_wlan_cmd_response:191: command header:
__func__, __LINE__, error);
usb_handle_wlan_cmd_response:192: command (0x115c)
return error;
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)
return 0;
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_wlan_cmd_thread
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
*/
usb_handle_wlan_cmd_response:191: command header:
static void *usb_wlan_cmd_thread(void *arg)
usb_handle_wlan_cmd_response:192: command (0x1162)
{
usb_handle_wlan_cmd_response:199: tag (0xcafe)
int error;
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
error = usb_wlan_init();
usb_handle_wlan_cmd_response:210: command payload:
if (error) {
usb_wlan_cmd_send:288: sending command (0x110d) data size (0x0080) command size (0x008c)
fprintf(stderr, "%s:%d: could not initialize device (%d)\n",
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
__func__, __LINE__, error);
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
goto done;
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)
sleep(5);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
error = usb_wlan_cmd_0x99();
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
if (error) {
usb_handle_wlan_cmd_response:210: command payload:
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
usb_wlan_cmd_send:288: sending command (0x1031) data size (0x0002) command size (0x000e)
__func__, __LINE__, error);
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
goto done;
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:
error = usb_wlan_cmd_start_scan();
usb_handle_wlan_cmd_response:192: command (0x1032)
if (error) {
usb_handle_wlan_cmd_response:199: tag (0xcafe)
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
usb_handle_wlan_cmd_response:201: status (0x0001)
__func__, __LINE__, error);
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
goto done;
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)
sleep(10);
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (42)
error = usb_wlan_cmd_get_scan_results();
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
if (error) {
usb_handle_wlan_cmd_response:191: command header:
fprintf(stderr, "%s:%d: could not get scan results (%d)\n",
usb_handle_wlan_cmd_response:192: command (0x1042)
__func__, __LINE__, error);
usb_handle_wlan_cmd_response:199: tag (0xcafe)
goto done;
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:
sleep(10);
00000000: 00 11 22 33 44 55                              |.."3DU          |
 
usb_wlan_cmd_send:288: sending command (0x0029) data size (0x0002) command size (0x000e)
done:
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38)
usb_wlan_cmd_thread_done = 1;
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
return NULL;
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)
* main
usb_handle_wlan_cmd_response:210: command payload:
*/
00000000: 02 02                                          |..              |
int main(int argc, char **argv)
usb_wlan_cmd_send:288: sending command (0x110b) data size (0x000c) command size (0x0018)
{
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
unsigned char buf[256];
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (48)
pthread_t tid;
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
struct timeval tv;
usb_handle_wlan_cmd_response:191: command header:
int error;
usb_handle_wlan_cmd_response:192: command (0x110c)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
pthread_mutex_init(&usb_wlan_cmd_mutex, NULL);
usb_handle_wlan_cmd_response:201: status (0x0001)
pthread_cond_init(&usb_wlan_cmd_cond, NULL);
usb_handle_wlan_cmd_response:207: payload_size (0x000c)
 
usb_handle_wlan_cmd_response:210: command payload:
error = libusb_init(&usb_ctx);
00000000: 01 00 00 00 00 00 00 00 20 00 00 00            |........ ...    |
if (error) {
usb_wlan_cmd_send:288: sending command (0x1109) data size (0x0016) command size (0x0022)
fprintf(stderr, "%s:%d: libusb_init failed (%d)\n", __func__, __LINE__, error);
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
exit(1);
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:
libusb_set_debug(usb_ctx, 5);
usb_handle_wlan_cmd_response:192: command (0x110a)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
usb_dev_handle = libusb_open_device_with_vid_pid(usb_ctx, USB_VENDOR_ID, USB_PRODUCT_ID);
usb_handle_wlan_cmd_response:201: status (0x0001)
if (!usb_dev_handle) {
usb_handle_wlan_cmd_response:207: payload_size (0x0016)
fprintf(stderr, "%s:%d: could not open device\n", __func__, __LINE__);
usb_handle_wlan_cmd_response:210: command payload:
exit(1);
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)
if(libusb_kernel_driver_active(usb_dev_handle, USB_IFACE_NUMBER)) {
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
fprintf(stdout, "%s:%d: kernel driver is attached\n", __func__, __LINE__);
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===
error = libusb_detach_kernel_driver(usb_dev_handle, USB_IFACE_NUMBER);
 
if (error) {
* I got association with AP working.
fprintf(stderr, "%s:%d: could not detach kernel driver (%d)\n",
* If  WLAN device is connected to an AP then the green LED is on, when data is received then the LED blinks.
__func__, __LINE__, error);
* '''Data reception works finally !!!'''
exit(1);
}


====How to Associate with WPA AP====
fprintf(stdout, "%s:%d: kernel driver dettached\n", __func__, __LINE__);
* Set common configuration (command 0x1005)
}
* Set WPA configuration (command 0x1019)
* Set rate configuration (command 0x1ed)
* Associate (command 0x1001)


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


* EP6 IN and EP7 IN endpoints are used for packet reception
error = libusb_control_transfer(usb_dev_handle, 0x40, 0x1, 0x9, 0x0,
* LV2 sends bulk transfers to both endpoints
usb_magic_data, sizeof(usb_magic_data), 0);
* '''4''' bulk transfers are sent simultaneously for each enpoint
if (error < 0) {
* Every bulk transfer is of size '''0x620'''
fprintf(stderr, "%s:%d: could not do control transfer (%d)\n",
* '''Make sure you set multicast address filter properly or else you won't receive broadcast packets !!!'''
__func__, __LINE__, error);
* 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.
exit(1);
* '''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====
fprintf(stdout, "%s:%d: number of bytes transferred (%d)\n", __func__, __LINE__, error);


<pre>
error = libusb_control_transfer(usb_dev_handle, 0xc0, 0x0, 0x2, 0x0, buf, 2, 0);
usb_bulk_transfer_ep6_in_cb:318: === got data transfer ===
if (error < 0) {
usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (98)
fprintf(stderr, "%s:%d: could not do control transfer (%d)\n",
00000000: ff ff ff ff ff ff ?? ?? ?? ?? ?? ?? 08 00 45 00 |..............E.|
__func__, __LINE__, error);
00000010: 00 54 00 00 40 00 40 01 b5 fe c0 a8 01 5b c0 a8 |.T..@.@......[..|
exit(1);
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====
fprintf(stdout, "%s:%d: number of bytes received (%d)\n", __func__, __LINE__, error);


* WLAN Gelic device supports hardware multicast address filtering
fprintf(stdout, "%s:%d: 0x%02x 0x%02x\n", __func__, __LINE__, buf[0], buf[1]);
* 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=====
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);
}


* Used by LV2
memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf));


<pre>
libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
unsigned char hash(unsigned char *data, unsigned int size)
usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf),
{
usb_intr_transfer_ep5_in_cb, NULL, 0);
        unsigned int hash;
        int i, j;


        /*XXX: reverse data bits */
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);
}


        hash = 0xffffffff;
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);
}


        for (i = 0; i < size; i++) {
while (!usb_wlan_cmd_thread_done) {
                hash = (((unsigned int) data[i]) << 24) ^ hash;
tv.tv_sec = 1;
tv.tv_usec = 0;


                for (j = 0; j < 8; j++) {
error = libusb_handle_events_timeout(usb_ctx, &tv);
                        if (((int) hash) >= 0) {
if (error) {
                                hash = hash << 1;
fprintf(stderr, "%s:%d: could not handle events (%d)\n",
                        } else {
__func__, __LINE__, error);
                                hash = (hash << 1) ^ 0x04c10000;
exit(1);
                                hash = hash ^ 0x00001db7;
}
                        }
}
                }
        }


        hash = ((hash >> 24) & 0xf8) | (hash & 0x7);
libusb_free_transfer(usb_intr_transfer_ep5_in);


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


h = hash(mac_addr, 6);
libusb_close(usb_dev_handle);
v = 1 << (h & 0x1f);    /* word value in filter */
p = h >> 5;            /* word position in filter */


libusb_exit(usb_ctx);


For broadcast address:
exit(0);
------------------------
}
 
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>
</pre>


===Packet Transmission===
====Output====


* Tx packets are sent to EP6 OUT
<pre>
* Tx packets are normal Ethernet frames, they don't contain any WLAN data or other headers
glevand@debian-hdd:~/ps3_usb_wlan$ sudo ./ps3_usb_wlan
 
sudo: unable to resolve host debian-hdd
===AP Mode===
main:824: number of bytes transferred (32)
 
main:833: number of bytes received (2)
* I got AP mode working with security disabled for now
main:835: 0x20 0x31
 
usb_wlan_cmd_send:288: sending command (0x114f) data size (0x0518) command size (0x0524)
====AP Mode with Security Disabled====
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
* Set AP SSID (command 0x5)
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
* Set channel (command 0x11)
usb_handle_wlan_cmd_response:191: command header:
* Set AP opmode (command 0xb9)
usb_handle_wlan_cmd_response:192: command (0x1150)
* Configure rate control (command 0x1ed)
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* Set AP WEP Configuration (command 0x5b, all 0s)
usb_handle_wlan_cmd_response:201: status (0x0006)
* Command 0x61 (param 0x0)
usb_handle_wlan_cmd_response:205: ==> command status != 0x1
* Command 0xc5 (param 0x0)
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
* Command 0x1 (param 0x1)
usb_handle_wlan_cmd_response:210: command payload:
* Command 0x1dd (param 0x2)
usb_wlan_cmd_send:288: sending command (0x1171) data size (0x0000) command size (0x000c)
* Now green LED should be on
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
===ps3-jupiter Linux Drivers===
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
* 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.
usb_handle_wlan_cmd_response:192: command (0x1172)
* ps3_jupiter_sta.ko is a STA mode implementation.
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* ps3_jupiter_ap.ko is a AP mode implementation.
usb_handle_wlan_cmd_response:201: status (0x0001)
* Simple scanning works already in STA mode (try it out with '''iwlist scan''')
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
* Packet reception works
usb_handle_wlan_cmd_response:210: command payload:
* Packet transmission works
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* '''WPA/WPA2''' fully working and usable with '''wpa_supplicant'''
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)
'''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.'''
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 |................|
====TODO====
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 |................|
* Implement association in STA mode (finished)
usb_wlan_cmd_send:288: sending command (0x116f) data size (0x0004) command size (0x0010)
* Implement packet reception and transmission in STA mode (finished)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* Implement WEP support
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
* Implement AP mode
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
* Find out if Jupiter supports Monitor mode and if yes how to enable it
usb_handle_wlan_cmd_response:191: command header:
* Implement EURUS driver for PHATs (has many advantages over the old OtherOS approach, e.g. AP mode)
usb_handle_wlan_cmd_response:192: command (0x1170)
* Port to FreeBSD
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
==LV2 Network Stack==
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
 
usb_handle_wlan_cmd_response:210: command payload:
* LV2 uses BSD network stack, e.g. '''struct mbuf'''
usb_wlan_cmd_send:288: sending command (0x115b) data size (0x005e) command size (0x006a)
* It's almost identical to FreeBSD network stack.
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
===Network Device===
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
====IOCTLs====
usb_handle_wlan_cmd_response:192: command (0x115c)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
=====Set Multicast Address Filter (0x81012000)=====
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
* Sets multicast address filter
usb_handle_wlan_cmd_response:210: command payload:
* Uses LV1 calls '''lv1_net_remove_multicast_address''' and '''lv1_net_add_multicast_address''' for Ethernet Gelic device
usb_wlan_cmd_send:288: sending command (0x1161) data size (0x0020) command size (0x002c)
* Uses Eurus commands '''0x1161''', '''0x1163''' and '''0x1165''' for WLAN Gelic device
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
=====Unknown (0x8101200E)=====
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
* Uses LV1 call '''lv1_net_control(0x8000000000000001)'''
usb_handle_wlan_cmd_response:192: command (0x1162)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
=====Unknown (0x81040000)=====
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
* Uses LV1 call '''lv1_net_control(0x8, [0x0, 0x1 or 0x2])''' for Ethernet Gelic device
usb_handle_wlan_cmd_response:210: command payload:
* Uses Eurus commands '''0x116F''', '''0x115D''' and '''0x115B''' for WLAN Gelic device
usb_wlan_cmd_send:288: sending command (0x110d) data size (0x0080) command size (0x008c)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
=====Enable/Disable WOL Magic Packet (0x81080000)=====
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
* Enables/Disables WOL Magic Packet
usb_handle_wlan_cmd_response:191: command header:
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x1 /* GELIC_LV1_WOL_MAGIC_PACKET */)''' for Ethernet Gelic device
usb_handle_wlan_cmd_response:192: command (0x110e)
* Uses Eurus commands '''0x1139''' and '''0x1155''' for WLAN Gelic device
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
=====Unknown (0x81080001)=====
usb_handle_wlan_cmd_response:207: payload_size (0x0000)
 
usb_handle_wlan_cmd_response:210: command payload:
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x2)''' for Ethernet Gelic device
usb_wlan_cmd_send:288: sending command (0x1031) data size (0x0002) command size (0x000e)
* Uses Eurus commands '''0x113B''' and '''0x1157''' for WLAN Gelic device
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38)
=====Unknown (0x81080002)=====
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x3)''' for Ethernet Gelic device
usb_handle_wlan_cmd_response:192: command (0x1032)
* Uses Eurus commands '''0x113D''' and '''0x1159''' for WLAN Gelic device
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
=====Unknown (0x81080003)=====
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
 
usb_handle_wlan_cmd_response:210: command payload:
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x4)''' for Ethernet Gelic device
00000000: 00 00                                          |..              |
* Uses Eurus command '''0x1161''' for WLAN Gelic device
usb_wlan_cmd_send:288: sending command (0x1041) data size (0x0006) command size (0x0012)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
=====Unknown (0x81080005)=====
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (42)
 
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x6 /* GELIC_LV1_WOL_ADD_MATCH_ADDR */)''' for Ethernet Gelic device
usb_handle_wlan_cmd_response:191: command header:
* Uses Eurus commands '''0x116D''' and '''0x1167''' for WLAN Gelic device
usb_handle_wlan_cmd_response:192: command (0x1042)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
===Network Packet===
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0006)
* LV2 network packet is represented by '''struct mbuf'''
usb_handle_wlan_cmd_response:210: command payload:
 
00000000: 00 11 22 33 44 55                              |.."3DU          |
=RSX=
usb_wlan_cmd_send:288: sending command (0x0029) data size (0x0002) command size (0x000e)
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#RSX gitbrew.org::RSX] <br />
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38)
==HV Calls==
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
===lv1_gpu_memory_allocate===
usb_handle_wlan_cmd_response:192: command (0x002a)
 
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* LV1 supports 16 memory handles simultaneously.
usb_handle_wlan_cmd_response:201: status (0x0001)
* LV1 uses a bitmap to manage GPU VRAM.
usb_handle_wlan_cmd_response:207: payload_size (0x0002)
* The bitmap is located in LV1 memory, 4 double words.
usb_handle_wlan_cmd_response:210: command payload:
* Each bit corresponds to 1MB VRAM, 256bit = 256MB VRAM.
00000000: 02 02                                          |..              |
* 2MB at the top of VRAM are preallocated as you can see below.
usb_wlan_cmd_send:288: sending command (0x110b) data size (0x000c) command size (0x0018)
 
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
<pre>
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (48)
<memory handle> = 0x5a5a5a5a xor <memory handle index>
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
</pre>
usb_handle_wlan_cmd_response:191: command header:
 
usb_handle_wlan_cmd_response:192: command (0x110c)
====Memory Context Object====
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
offset 0x8 - memory handle (4 bytes)
usb_handle_wlan_cmd_response:207: payload_size (0x000c)
 
usb_handle_wlan_cmd_response:210: command payload:
offset 0x10 - VRAM LPAR start address (8 bytes)
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)
offset 0x18 - VRAM LPAR end address (8 bytes)
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (58)
====Test====
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
* The offset of bitmap could be different on your system because it's allocated dynamically.
usb_handle_wlan_cmd_response:192: command (0x110a)
* '''First 9MB of VRAM were allocated by ps3fb Linux driver.'''
usb_handle_wlan_cmd_response:199: tag (0xcafe)
 
usb_handle_wlan_cmd_response:201: status (0x0001)
Before allocating VRAM:
usb_handle_wlan_cmd_response:207: payload_size (0x0016)
<pre>
usb_handle_wlan_cmd_response:210: command payload:
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C
00000000: 01 00 00 00 15 27 12 00 06 00 00 00 09 00 01 00 |.....'..........|
00000000 00 00 00 00 00 00 01 ff  00 00 00 00 00 00 00 00 |.......ÿ........|
00000010: ff ff ff ff ff ff                              |......          |
00000010 00 00 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 |........À.......|
usb_wlan_cmd_send:288: sending command (0x0207) data size (0x0004) command size (0x0010)
</pre>
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
 
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40)
After allocating 32 MB VRAM:
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
<pre>
usb_handle_wlan_cmd_response:191: command header:
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C
usb_handle_wlan_cmd_response:192: command (0x0208)
00000000  00 00 01 ff ff ff ff ff  00 00 00 00 00 00 00 00 |...ÿÿÿÿÿ........|
usb_handle_wlan_cmd_response:199: tag (0xcafe)
00000010  00 00 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 |........À.......|
usb_handle_wlan_cmd_response:201: status (0x0001)
</pre>
usb_handle_wlan_cmd_response:207: payload_size (0x0004)
 
usb_handle_wlan_cmd_response:210: command payload:
===lv1_gpu_context_allocate===
00000000: 01 00 00 00                                    |....            |
 
usb_wlan_cmd_send:288: sending command (0x0203) data size (0x0004) command size (0x0010)
* Register %r4 is flags.
usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer ===
* '''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.'''
usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40)
* 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.
usb_handle_wlan_cmd_response:158: === got WLAN command response ===
 
usb_handle_wlan_cmd_response:191: command header:
* LV1 supports 16 contexts simultaneously.
usb_handle_wlan_cmd_response:192: command (0x0204)
* LV1 has an array of context pointers.
usb_handle_wlan_cmd_response:199: tag (0xcafe)
* Each context has an index and a handle. The handle is derived from the index of the context.
usb_handle_wlan_cmd_response:201: status (0x0001)
 
usb_handle_wlan_cmd_response:207: payload_size (0x0004)
<pre>
usb_handle_wlan_cmd_response:210: command payload:
<context handle> = 0x55555555 xor <context index>
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>
</pre>


* Thats why first created context will have handle 0x55555555.
===Associate with AP===


====Context Object====
* 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 !!!'''


offset 0x8 - handle (4 bytes)
====How to Associate with WPA AP====
* Set common configuration (command 0x1005)
* Set WPA configuration (command 0x1019)
* Set rate configuration (command 0x1ed)
* Associate (command 0x1001)


offset 0x48 - IO page size, valid range is 4kB, 64KB and 1MB (8 bytes)
===Packet Reception===


====Flags====
* 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 !!!'''


'''0x2 - tells LV1 to use 64KB pages for GART memory mapping else LV1 uses 1MB pages'''
====Test with libusb====


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


* Internally uses lv1_put_iopte function
====Multicast Address Filter====
* 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===
* 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'''


====Attribute 0x1====
=====MAC Address Hash Function=====


=====FIFO Command Buffer Setup=====
* Used by LV2


<pre>
<pre>
lv1_gpu_context_attribute(context handle, 0x1, PUT offset, GET offset, 0x0, 0x0)
unsigned char hash(unsigned char *data, unsigned int size)
</pre>
{
        unsigned int hash;
        int i, j;


====Attribute 0x101====
        /*XXX: reverse data bits */


=====Set Flip Mode=====
        hash = 0xffffffff;


<pre>
        for (i = 0; i < size; i++) {
lv1_gpu_attribute(0x2, 0x1 /* head */, 0x0, 0x0)
                hash = (((unsigned int) data[i]) << 24) ^ hash;
lv1_gpu_context_attribute(context handle, 0x101, 0x1 /* head */, sync mode, 0x0, 0x0)
</pre>


====Attribute 0x104====
                for (j = 0; j < 8; j++) {
                        if (((int) hash) >= 0) {
                                hash = hash << 1;
                        } else {
                                hash = (hash << 1) ^ 0x04c10000;
                                hash = hash ^ 0x00001db7;
                        }
                }
        }


=====Set Display Buffer=====
        hash = ((hash >> 24) & 0xf8) | (hash & 0x7);


<pre>
        return hash & 0xff;
lv1_gpu_context_attribute(context handle, 0x104, id, width << 32 | height, pitch << 32 | offset, 0x0)
}
</pre>


====Attribute 0x10a====
h = hash(mac_addr, 6);
v = 1 << (h & 0x1f);    /* word value in filter */
p = h >> 5;            /* word position in filter */


=====Get Flip Status=====


* Reads a value at offset '''0x10C0 + 0x1 * 0x40''' in lpar_reports memory.
For broadcast address:
------------------------


=====Reset Flip Status=====
v = 0x20000000
p = 7


<pre>
That's why 0x20 is used with command 0x1161 !!! Without it the device won't deliver broadcast traffic.
lv1_gpu_context_attribute(context handle, 0x10a, 0x1 /* id */, 0x7fffffff /* mask */, 0x0 /* value */, 0x0)
Learned it the hard way, after 2 days of trying to get packet reception working :)
</pre>
</pre>


* The LV1 call '''lv1_gpu_context_attribute(0x10a)''' accesses LPAR memory returned in '''lpar_reports''' by LV1 call '''lv1_gpu_context_allocate'''.
===Packet Transmission===
* 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====
* Tx packets are sent to EP6 OUT
* Tx packets are normal Ethernet frames, they don't contain any WLAN data or other headers


* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''
===AP Mode===


=====Set Cursor Position=====
* I got AP mode working with security disabled for now


<pre>
====AP Mode with Security Disabled====
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x3, x, y)
</pre>


=====Set Cursor Image Offset=====
* 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


<pre>
===ps3-jupiter Linux Drivers===
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x2, offset, 0x0)
</pre>


====Attribute 0x10c====
* 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'''


* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''


=====Cursor Function 1=====
'''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.'''


<pre>
====TODO====
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x1, 0x0, 0x0)
</pre>


=====Cursor Function 2=====
* 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


<pre>
==LV2 Network Stack==
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x2, 0x0, 0x0)
</pre>


====Attribute 0x10d====
* LV2 uses BSD network stack, e.g. '''struct mbuf'''
* It's almost identical to FreeBSD network stack.


* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''
===Network Device===


=====Cursor Function 1=====
====IOCTLs====


<pre>
=====Set Multicast Address Filter (0x81012000)=====
lv1_gpu_context_attribute(context handle, 0x10d, 0x1, 0x1, 0x0, 0x0)
</pre>


====Attribute 0x300====
* 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


=====Set Tile=====
=====Unknown (0x8101200E)=====


=====Set Invalidate Tile=====
* Uses LV1 call '''lv1_net_control(0x8000000000000001)'''


=====Bind Tile=====
=====Unknown (0x81040000)=====


=====Unbind Tile=====
* 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


====Attribute 0x301====
=====Enable/Disable WOL Magic Packet (0x81080000)=====


=====Set Zcull=====
* 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


=====Bind Zcull=====
=====Unknown (0x81080001)=====


=====Unbind Zcull=====
* 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


====Attribute 0x601====
=====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


* Copies data from GART memory to VRAM.
=====Unknown (0x81080003)=====
* LV1 uses internally the FIFO command buffer passed by ps3fb driver with lv1_gpu_context_iomap.


FIFO commands:
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x4)''' for Ethernet Gelic device
<pre>
* Uses Eurus command '''0x1161''' for WLAN Gelic device
0x0004C184
0xFEED0001


0x0004C198
=====Unknown (0x81080005)=====
0x313371C3


0x00046300
* Uses LV1 call '''lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x6 /* GELIC_LV1_WOL_ADD_MATCH_ADDR */)''' for Ethernet Gelic device
0x0000000A
* Uses Eurus commands '''0x116D''' and '''0x1167''' for WLAN Gelic device


for ()
===Network Packet===
{
    for ()
    {
        0x0004630C
        <param>


        0x00046304
* LV2 network packet is represented by '''struct mbuf'''
        <param>


        0x0024C2FC
=RSX=
        0x00000001
Crossreference: [http://wiki.gitbrew.org/index.php/PS3:HvReverseEngineering#RSX gitbrew.org::RSX] <br />
        0x00000003
        0x00000003
        <param1>
        <param2>
        <param3>
        <param4>
        0x00010000
        0x00010000


        0x0001C400
==HV Calls==
        <param1>
        <param2>
        <param3>
        0x00000000
    }
}


0x00040110
===lv1_gpu_memory_allocate===
0x00000000
</pre>


==FIFO Command Buffer==
* 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.


===FIFO Control Registers===
<pre>
<memory handle> = 0x5a5a5a5a xor <memory handle index>
</pre>


* LV1 call '''lv1_gpu_context_allocate''' returns LPAR address of FIFO control registers.
====Memory Context Object====
* 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.
offset 0x8 - memory handle (4 bytes)
* GET register is read-only and is modified by RSX while it's processing FIFO commands.
 
offset 0x10 - VRAM LPAR start address (8 bytes)
 
offset 0x18 - VRAM LPAR end address (8 bytes)


===Kicking FIFO Command Buffer===
====Test====


* As long as values of GET and PUT FIFO control registers are equal, RSX doesn't process commands from the FIFO command buffer.
* The offset of bitmap could be different on your system because it's allocated dynamically.
* When the value of PUT register is not equal to the value of GET register, RSX starts processing commands in the FIFO command buffer.
* '''First 9MB of VRAM were allocated by ps3fb Linux driver.'''
* 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===
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>


* [[PS3:HvReverseEngineering:emer_init.self:Program 1]]
After allocating 32 MB VRAM:
* [[PS3:HvReverseEngineering:emer_init.self:Program 2]]
<pre>
* [[PS3:HvReverseEngineering:emer_init.self:Program 3]]
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>


===FIFO Commands===
===lv1_gpu_context_allocate===


[[PS3:HvReverseEngineering:RSXFIFOCommands]]
* 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.


===Example How to Use FIFO Command Buffer===
* 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.


Here is a small Linux kernel module which shows you how to use FIFO command buffer on Linux.
<pre>
<context handle> = 0x55555555 xor <context index>
</pre>


* RSX allows to create multiple contexts.
* Thats why first created context will have handle 0x55555555.
* 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]
====Context Object====


====Source Code====
offset 0x8 - handle (4 bytes)


<pre>
offset 0x48 - IO page size, valid range is 4kB, 64KB and 1MB (8 bytes)
/*
 
* PS3 RSX
====Flags====
*
* 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>
'''0x2 - tells LV1 to use 64KB pages for GART memory mapping else LV1 uses 1MB pages'''
#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>
===lv1_gpu_context_iomap===
#include <asm/cell-regs.h>
#include <asm/lv1call.h>
#include <asm/ps3.h>


#define RSX_FIFO_CMD_BUF_SIZE (1 * 1024 * 1024)
* 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.


#define RSX_MEM_SIZE (32 * 1024 * 1024)
===lv1_gpu_context_attribute===


#define RSX_GPU_IOIF (0x0e000000ul)
====Attribute 0x1====


#define RSX_FIFO_CTRL_SIZE (4 * 1024)
=====FIFO Command Buffer Setup=====


struct rsx_fifo_ctrl {
<pre>
u8 res[0x40];
lv1_gpu_context_attribute(context handle, 0x1, PUT offset, GET offset, 0x0, 0x0)
u32 put;
</pre>
u32 get;
};


static u32 *rsx_fifo_cmd_buf;
====Attribute 0x101====
static u64 rsx_fifo_cmd_buf_lpar;


static u64 rsx_mem_handle, rsx_mem_lpar;
=====Set Flip Mode=====
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;
<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====
* FIFO program
*/
static u32 rsx_fifo_prg[] = {
0x00000000, /* nop */
0x00000000, /* nop */
0x00000000, /* nop */
};


/*
=====Set Display Buffer=====
* ps3rsx_init
*/
static int __init ps3rsx_init(void)
{
unsigned long timeout;
int res;


/* FIFO command buffer must be allocated in XDR memory */
<pre>
lv1_gpu_context_attribute(context handle, 0x104, id, width << 32 | height, pitch << 32 | offset, 0x0)
</pre>


rsx_fifo_cmd_buf = kmalloc(RSX_FIFO_CMD_BUF_SIZE, GFP_KERNEL);
====Attribute 0x10a====
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,
=====Get Flip Status=====
&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,
* Reads a value at offset '''0x10C0 + 0x1 * 0x40''' in lpar_reports memory.
&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));
=====Reset Flip Status=====


res = lv1_gpu_context_iomap(rsx_ctx_handle,
<pre>
RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar, RSX_FIFO_CMD_BUF_SIZE,
lv1_gpu_context_attribute(context handle, 0x10a, 0x1 /* id */, 0x7fffffff /* mask */, 0x0 /* value */, 0x0)
CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M);
</pre>
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 */
* 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 ???


rsx_fifo_ctrl = (struct rsx_fifo_ctrl *) ioremap(rsx_fifo_ctrl_lpar, RSX_FIFO_CTRL_SIZE);
====Attribute 0x10b====
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 */
* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''


res = lv1_gpu_context_attribute(rsx_ctx_handle, 0x1,
=====Set Cursor Position=====
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 */
<pre>
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x3, x, y)
</pre>


memcpy(rsx_fifo_cmd_buf, rsx_fifo_prg, sizeof(rsx_fifo_prg));
=====Set Cursor Image Offset=====


printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);
<pre>
lv1_gpu_context_attribute(context handle, 0x10b, 0x1, 0x2, offset, 0x0)
</pre>


/* kick FIFO */
====Attribute 0x10c====


rsx_fifo_ctrl->put = RSX_GPU_IOIF + sizeof(rsx_fifo_prg);
* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''


/* poll until RSX is done processing FIFO commands */
=====Cursor Function 1=====


timeout = 100;
<pre>
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x1, 0x0, 0x0)
</pre>


while (timeout--) {
=====Cursor Function 2=====
if (rsx_fifo_ctrl->get == rsx_fifo_ctrl->put)
break;


msleep(1);
<pre>
}
lv1_gpu_context_attribute(context handle, 0x10c, 0x1, 0x2, 0x0, 0x0)
</pre>


printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);
====Attribute 0x10d====


if (rsx_fifo_ctrl->get != rsx_fifo_ctrl->put) {
* '''This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.'''
printk(KERN_INFO"FIFO command buffer timeout\n");
res = -ENXIO;
goto fail_unmap_fifo_ctrl;
}


return 0;
=====Cursor Function 1=====


fail_unmap_fifo_ctrl:
<pre>
lv1_gpu_context_attribute(context handle, 0x10d, 0x1, 0x1, 0x0, 0x0)
</pre>


iounmap(rsx_fifo_ctrl);
====Attribute 0x300====


=====Set Tile=====


fail_free_gpu_mem:
=====Set Invalidate Tile=====


lv1_gpu_memory_free(rsx_mem_handle);
=====Bind Tile=====


fail_free_fifo_cmd_buf_mem:
=====Unbind Tile=====


kfree(rsx_fifo_cmd_buf);
====Attribute 0x301====


fail:
=====Set Zcull=====


return res;
=====Bind Zcull=====
}
 
=====Unbind Zcull=====


/*
====Attribute 0x601====
* 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,
* Copies data from GART memory to VRAM.
RSX_FIFO_CMD_BUF_SIZE, CBE_IOPTE_M);
* LV1 uses internally the FIFO command buffer passed by ps3fb driver with lv1_gpu_context_iomap.


lv1_gpu_context_free(rsx_ctx_handle);
FIFO commands:
<pre>
0x0004C184
0xFEED0001


lv1_gpu_memory_free(rsx_mem_handle);
0x0004C198
0x313371C3


kfree(rsx_fifo_cmd_buf);
0x00046300
}
0x0000000A


module_init(ps3rsx_init);
for ()
module_exit(ps3rsx_exit);
{
    for ()
    {
        0x0004630C
        <param>


MODULE_LICENSE("GPL");
        0x00046304
MODULE_DESCRIPTION("PS3 RSX");
        <param>
MODULE_AUTHOR("glevand");
</pre>


====Test====
        0x0024C2FC
        0x00000001
        0x00000003
        0x00000003
        <param1>
        <param2>
        <param3>
        <param4>
        0x00010000
        0x00010000


<pre>
        0x0001C400
# insmod ./ps3rsx.ko
        <param1>
# dmesg
        <param2>
        <param3>
        0x00000000
    }
}


GET offset (0x0e000000) PUT offset (0x0e000000)  # GET and PUT offsets before kicking FIFO
0x00040110
GET offset (0x0e00000c) PUT offset (0x0e00000c)  # GET and PUT offsets after kicking FIFO
0x00000000
</pre>
</pre>


As you see, RSX processed our FIFO commands :)
==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.


==Linux Driver==
===Kicking FIFO Command Buffer===


* '''DRI/DRM is the ONLY way to go !!! No hacks like kernel modules with tons of IOCTLs !!!'''
* As long as values of GET and PUT FIFO control registers are equal, RSX doesn't process commands from the FIFO command buffer.
* First implement 2D acceleration and then add 3D support
* When the value of PUT register is not equal to the value of GET register, RSX starts processing commands in the FIFO command buffer.
* The driver consists of 2 parts: '''DDX driver''' for X11 (user space) and '''DRM driver''' for Linux Kernel (kernel space)
* To execute FIFO commands, place them in the FIFO command buffer and change the value of PUT register.
* First implement DRM driver and test it from user space without DDX and libdrm by talking to it directly


===DDX Driver===
===FIFO Setup Programs of emer_init.self===


* Use '''libdrm'''
* [[PS3:HvReverseEngineering:emer_init.self:Program 1]]
* Use '''EXA API''' for 2D acceleration on X11 (or maybe use '''XAA API''')
* [[PS3:HvReverseEngineering:emer_init.self:Program 2]]
* Use '''Kernel Mode Setting'''
* [[PS3:HvReverseEngineering:emer_init.self:Program 3]]


===DRM Driver===
===FIFO Commands===


* Extend '''nouveau''' driver or create a new one ???
[[PS3:HvReverseEngineering:RSXFIFOCommands]]
* '''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====
===Example How to Use FIFO Command Buffer===


* Size of all memory objects must be multiple of the page size (4096 bytes) even if a smaller size is requested by user
Here is a small Linux kernel module which shows you how to use FIFO command buffer on Linux.
* 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====
* 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.


* VRAM is allocated once during context creating and cannot be changed during the whole life of the context.
Download source code: [http://lol.notsoldierx.com/~glevand/ps3/linux/ps3rsx.tar.gz]
* '''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====
====Source Code====


* GART memory region is a memory region in System Memory but accessible by RSX through GART [http://dri.freedesktop.org/wiki/GART].
<pre>
* GameOS calls it '''Main Memory'''.
/*
* '''Problem: lv1_gpu_context_iomap supports ONLY 1MB and 64kB pages'''
* PS3 RSX
* 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.
* This program is free software; you can redistribute it and/or modify it
* 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.
* under the terms of the GNU General Public License as published
* 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.
* by the Free Software Foundation; version 2 of the License.
* 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'''
* This program is distributed in the hope that it will be useful, but
* '''GameOS applications using GCM library map GART memory beginning at offset 0x10000000 or 0x20000000, just after where the whole VRAM is mapped.'''
* WITHOUT ANY WARRANTY; without even the implied warranty of
* '''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.'''
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* TTM uses '''struct ttm_backend_func''' to call driver specific GART mapping functions. '''nouveau_sgdma.c''' handles GART memory mapping.
* 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.
*/


====CPU Memory====
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/delay.h>


* This type of memory cannot be accessed by RSX at all.
#include <asm/abs_addr.h>
* Because this type of memory is not mapped into RSX address space through GART we don't need to allocate it in 1MB multiples.
#include <asm/cell-regs.h>
* What do we need it for ???
#include <asm/lv1call.h>
#include <asm/ps3.h>


====Mapping Memory Objects into Kernel-Space====
#define RSX_FIFO_CMD_BUF_SIZE (1 * 1024 * 1024)


* Nouveau driver uses '''ttm_bo_kmap''' to map memory objects into kernel-space (see '''ttm_bo_util.c''').
#define RSX_MEM_SIZE (32 * 1024 * 1024)
* 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====
#define RSX_GPU_IOIF (0x0e000000ul)


* User-space programs should be able to allocate memory objects in VRAM or GART and map it with '''mmap syscall'''.
#define RSX_FIFO_CTRL_SIZE (4 * 1024)
* 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====
struct rsx_fifo_ctrl {
u8 res[0x40];
u32 put;
u32 get;
};


* Every context has its own one main FIFO command buffer which is NOT accessible directly by user space.
static u32 *rsx_fifo_cmd_buf;
* 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.
static u64 rsx_fifo_cmd_buf_lpar;
* 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=====
static u64 rsx_mem_handle, rsx_mem_lpar;
<pre>
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;
    \|/    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====
static struct rsx_fifo_ctrl *rsx_fifo_ctrl;


* 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.
* FIFO program
* libgcm functions '''SetWriteCommandLabel''' and '''SetWaitLabel''' use semaphores.
*/
* '''SetWriteCommandLabel''' releases semaphore and '''SetWaitLabel''' acquires semaphore.
static u32 rsx_fifo_prg[] = {
* Semaphores are placed in VRAM. Nouveau driver creates a small VRAM heap for semaphores. See '''nouveau_fence.c:nouveau_fence_channel_init'''.
0x00000000, /* nop */
0x00000000, /* nop */
0x00000000, /* nop */
};


====IOCTLs====
/*
* ps3rsx_init
*/
static int __init ps3rsx_init(void)
{
unsigned long timeout;
int res;


=====Context Create=====
/* FIFO command buffer must be allocated in XDR memory */


* Creates new RSX context
rsx_fifo_cmd_buf = kmalloc(RSX_FIFO_CMD_BUF_SIZE, GFP_KERNEL);
* Allocates VRAM and memory for FIFO buffer
if (!rsx_fifo_cmd_buf) {
* Needed VRAM size and FIFO buffer size must be known during context creation
printk(KERN_INFO"could not allocate FIFO command buffer\n");
res = -ENOMEM;
goto fail;
}


=====Context Destroy=====
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;
}


* Destroys previously allocated context
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 */


=====Context Attribute=====
rsx_fifo_cmd_buf_lpar = ps3_mm_phys_to_lpar(__pa(rsx_fifo_cmd_buf));


* Changes context attributes
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;
}


=====Graphic Object Creatre=====
/* map RSX FIFO control registers */


* Create a graphic object either in VRAM or in XDR
rsx_fifo_ctrl = (struct rsx_fifo_ctrl *) ioremap(rsx_fifo_ctrl_lpar, RSX_FIFO_CTRL_SIZE);
* Used to create FIFO command buffers too (only in XDR of course because RSX supoorts FIFO command buffer in XDR only)
if (!rsx_fifo_ctrl) {
printk(KERN_INFO"could not map FIFO control\n");
res = -ENXIO;
goto fail_free_gpu_mem;
}


=====Graphic Object Destroy=====
/* PUT and GET offsets are in RSX address space */


* Frees previously created graphic object
res = lv1_gpu_context_attribute(rsx_ctx_handle, 0x1,
 
RSX_GPU_IOIF + 0x0 /* PUT offset */, RSX_GPU_IOIF + 0x0 /* GET offset */,
=====FIFO Execute=====
0x0, 0x0);
if (res) {
printk(KERN_INFO"lv1_gpu_context_attribute(0x1) failed (%d)\n", res);
res = -ENXIO;
goto fail_unmap_fifo_ctrl;
}


* Allows user space applications to execute FIFO commands.
/* copy FIFO commands to FIFO command buffer */
* 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=====
memcpy(rsx_fifo_cmd_buf, rsx_fifo_prg, sizeof(rsx_fifo_prg));


* Kernel DRM driver has to implement a frame buffer driver too
printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);
* 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===
/* kick FIFO */


* Add support for RSX DRM to '''libdrm'''
rsx_fifo_ctrl->put = RSX_GPU_IOIF + sizeof(rsx_fifo_prg);


===Test Kernel Module and Program===
/* poll until RSX is done processing FIFO commands */


* 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]
timeout = 100;
* 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===
while (timeout--) {
if (rsx_fifo_ctrl->get == rsx_fifo_ctrl->put)
break;


* http://yangman.ca/blog/2009/10/linux-graphics-driver-stack-explained
msleep(1);
* 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=
printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);
Crossreference: [http://wiki.gitbrew.org/wikibrew/PS3:HvReverseEngineering#BD_Drive gitbrew.org::HV#BD Drive] <br />
 
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;
}


==Profile==
/*
* ps3rsx_exit
*/
static void __exit ps3rsx_exit(void)
{
iounmap(rsx_fifo_ctrl);


* BD profile can be read with '''GET PROFILE''' device command or SCSI command '''GET CONFIGURATION'''
lv1_gpu_context_iomap(rsx_ctx_handle, RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar,
RSX_FIFO_CMD_BUF_SIZE, CBE_IOPTE_M);


===Profile Table===
lv1_gpu_context_free(rsx_ctx_handle);


{| class="wikitable"
lv1_gpu_memory_free(rsx_mem_handle);
|-
! 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==
kfree(rsx_fifo_cmd_buf);
}


* BD drive has several buffers associated with internal flash
module_init(ps3rsx_init);
* Buffer can be read and written with SCSI commands '''READ/WRITE BUFFER'''
module_exit(ps3rsx_exit);
* Writing buffer is enabled with SCSI command '''MODE SELECT 10''' first


===Buffer Table===
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PS3 RSX");
MODULE_AUTHOR("glevand");
</pre>


{| class="wikitable"
====Test====
|-
! 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===
<pre>
# insmod ./ps3rsx.ko
# dmesg


* Size is 32KB just like AACS specifications prescribes (See AACS Common Specification 3.2.5.2 Host Revocation List Record)
GET offset (0x0e000000) PUT offset (0x0e000000)  # GET and PUT offsets before kicking FIFO
* '''We could replace HRL with an older one in BD drive flash and restore revoked Host Certificates !!!'''
GET offset (0x0e00000c) PUT offset (0x0e00000c)  # GET and PUT offsets after kicking FIFO
</pre>


==Device Commands==
As you see, RSX processed our FIFO commands :)


===Get Profile (0x11)===
==Linux Driver==


* BD profile can be read with LV1 call '''lv1_send_storage_device_command''' and command '''0x11'''
* '''DRI/DRM is the ONLY way to go !!! No hacks like kernel modules with tons of IOCTLs !!!'''
* LV1 sends SCSI command '''GET CONFIGURATION''' to BD drive with '''requested type 0x0''', '''starting feature number 0x0''' and '''allocation length 0x8'''
* First implement 2D acceleration and then add 3D support
* See SCSI command '''GET CONFIGURATION'''
* 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


===Auto Request Sense Mode On/Off (0x30)===
===DDX Driver===


* LV1 expects a 4 byte value: 0x0 - On, 0x1 - Off
* Use '''libdrm'''
* can be get/set via GameOS sc0x25C/604: sys_storage_send_device_command(fd of bdvd,0x30,value,4,0,0 )
* Use '''EXA API''' for 2D acceleration on X11 (or maybe use '''XAA API''')
* Use '''Kernel Mode Setting'''


==SCSI Commands==
===DRM Driver===


===Get Configuration===
* 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'''


Getting the profile of a BD movie disc:
====Memory Management====
<pre>
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00
SCSI Status: Good


Sense Information:
* Size of all memory objects must be multiple of the page size (4096 bytes) even if a smaller size is requested by user
sense buffer empty
* 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]


Received 8 bytes of data:
====Video RAM====
00    00 00 00 38 00 00 00 40                            ...8...@ 


# 0x40 means BD-ROM
* VRAM is allocated once during context creating and cannot be changed during the whole life of the context.
</pre>
* '''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]


Getting the profile of a PS3 game disc:
====GART Memory====
<pre>
 
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00
* GART memory region is a memory region in System Memory but accessible by RSX through GART [http://dri.freedesktop.org/wiki/GART].
SCSI Status: Good
* 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====


Sense Information:
* This type of memory cannot be accessed by RSX at all.
sense buffer empty
* 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 ???


Received 8 bytes of data:
====Mapping Memory Objects into Kernel-Space====
00    00 00 00 38 00 00 ff 71                            ...8...q
# 0x71 means PS3 BD-ROM
</pre>


===Get SS Key===
* 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.


* By SCSI standard undocumented parameters are used
====Mapping Memory Objects into User-Space====
* '''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:
* User-space programs should be able to allocate memory objects in VRAM or GART and map it with '''mmap syscall'''.
<pre>
* See '''nouveau_ttm.c:nouveau_ttm_mmap'''.
# sg_raw -r 8 /dev/sr0 a4 00 00 00 00 00 00 e0 00 08 03 00
* Mapping memory objects into user-space avoids copying of data between user/kernel spaces.
SCSI Status: Good
* 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'''.


Sense Information:
====FIFO Command Buffer====
sense buffer empty


Received 8 bytes of data:
* Every context has its own one main FIFO command buffer which is NOT accessible directly by user space.
  00    00 06 00 00 00 00 00 04                            ........         
* 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.
</pre>
* 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.
 
====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''' 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.
 
=====Read 4 Bytes from XDR Buffer (0x2)=====
 
* It just reads 4 bytes of data from the XDR buffer passed to the SPU module.
 
=====Set KCD (0x1e)=====
 
* Sends KCD (Key Conversion Data) to the SPU module.
* KCD is encrypted with the Bus Key which was established previously by AACS authentication.
 
=====Init AES_H (0x34)=====
 
* Initializes AES_H hashing function.
 
=====Calculate AES_H 1 (0x35)=====
 
* Calculates AES_H hash of the data stored in XDR buffer.
 
=====Calculate AES_H 2 (0x36)=====
 
* Calculates AES_H hash of the data stored in XDR buffer.
 
=====Generate Host Nonce (0x3c)=====
 
* Generates a nonce which is returned in command '''0x3d'''
 
=====Get Host Nonce and Certificate (0x3d)=====
 
* 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.
 
=====Set Drive Nonce and Certificate (0x3e)=====
 
* Stores BD drive nonce and certificate in local memory of SPU
 
=====Verify Drive Certificate (0x3f)=====
 
=====Set Drive Key (0x40)=====
 
=====Sign Host Key (0x44)=====
 
=====Get Host Key (0x45)=====
 
=====Calculate Bus Key (0x46)=====
 
=====Set Volume ID (0x47)=====
 
* Sends volume id and its MAC to the SPU module
 
=====Calculate Volume ID MAC (0x48)=====
 
* Calculates MAC of the passed volume id
 
=====Verify Volume ID MAC (0x49)=====
 
* Verifies MAC of the passed volume id
 
=====Set PMSN (0x4a)=====
 
* Sends PMSN and its MAC to the SPU module
 
=====Calculate PMSN MAC (0x4b)=====
 
* Calculates MAC of the passed PMSN
 
=====Verify PMSN (0x4c)=====
 
* Verifies MAC of the passed PMSN
 
=====Set Media ID (0x4d)=====
 
* Sends media id and its MAC to the SPU module
 
=====Calculate Media ID MAC (0x4e)=====
 
* Calculates MAC of the passed media id
 
=====Verify Media ID MAC (0x4f)=====
 
* Verifies MAC of the passed media id
 
=====Unknown (0x54)=====
 
=====Verify Host/Drive Revocation (0x55)=====
 
* BD player stores HRL/DRL list entries in XDR buffer and passes it to the SPU module for verification


===Eject Media===
=====Terminate Session (0xfefefeff)=====


<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
* 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
* After a command is complete, the SPU module waits for the next command
* This command terminates the current session and stops SPU module
* This command terminates the current session and stops SPU module
|-
|}


===Drive Revocation List (DRL)===
===Drive Revocation List (DRL)===
Line 10,547: Line 11,232:


====P-Block====
====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=====
=====Creating=====
Line 10,750: Line 11,392:
lv1_destruct_logical_spe (0x00000000)
lv1_destruct_logical_spe (0x00000000)
</pre>
</pre>
{{Reverse engineering}}<noinclude>[[Category:Main]]</noinclude>
Please note that all contributions to PS3 Developer wiki are considered to be released under the GNU Free Documentation License 1.2 (see PS3 Developer wiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

To protect the wiki against automated edit spam, we kindly ask you to solve the following hCaptcha:

Cancel Editing help (opens in new window)