PS3:HvReverseEngineering
Repository Nodes
Gelic Device
Crossreference: ps3devwiki.com::Gelic Device
sys.hw.config
- Value of the loader parameter "sys.hw.config" controls if Gelic WLAN is enabled or not.
- Value of the loader parameter "sys.hw.config" is stored in the repository node "sys.hw.config" too.
- If bit 0x40000 is set then LV1 allows using Gelic WLAN interface from LV2.
- Value on my PS3 slim 0x4e00ffff0a03bc3c with Gelic WLAN interface disabled. As you can see, the Gelic WLAN interface is disabled and LV1 doesn't allow using of LV1 calls 196 and 195. It returns LV1_CONDITION_NOT_SATISFIED.
- GameOS checks bit 0x40000 of the repository node "sys.hw.config" during network initialization and if it's set then LV2 initializes Gelic WLAN interface.
- Check your "sys.hw.config" repository node and if bit 0x40000 is set then you are a lucky owner of a PS3 model with the old WLAN interface.
- On newer PS3 models, GameOS uses USB interface to communicate with WLAN.
- On PS3 models, where bit 0x40000 is NOT set in "sys.hw.config" repository node, the new USB interface is used.
Note:old vs. new: Old == CECHA up to CECHK, New == CECHL and later
Control Interface
HV calls 195 and 196 are used by GameOS to send commands to Gelic device directly.
lv1_undocumented_function_196
Parameters
r3 - LPAR address of data buffer
r4 - size of data buffer
r5 - must be 0
lv1_undocumented_function_195
Parameters
r3 - command (16 bit value)
r4 - command data size
r5 - must be 0
Data Buffer
- Data Buffer passed to HV call 196 is divided into 2 parts.
- The first 0x800 bytes are for sending and receiving command data
- The remaining 0x800 bytes are for event notification.
Command Data Buffer
- Every command data sent to Gelic device contains header of size 0xC
- After the header follows the command data
- After the Gelic device processed the command, it notifies LV2 kernel about command completion by sending an interrupt
Header
- Size is 0xc.
- Byte order is little-endian.
- Header data in a request command buffer is always all 0s.
0x0 - command = request command + 1 (2 bytes)
0x4 - result, 0x1 - success, 0x2 - invalid command length, 0x3 - invalid command, 0x4 - invalid parameter (2 bytes)
0x6 - body size (2 bytes)
Event Data Buffer
- The Gelic device notifies LV2 kernel by sending an interrupt when new events are available
- Event Data Buffer has 8 bytes header
- The remaining bytes are divided into event slots
- Each event slot is of size 64 bytes
- Events are in little-endian format
Header
offset 0x0 - GET index (4 bytes)
offset 0x4 - PUT index (4 bytes)
- GET index is updated by Gelic driver. The Gelic driver reads events beginning with the event slot at index GET.
- PUT index is the index of event entry where next Gelic event will be stored by the Gelic device.
- If GET index is equal to PUT index then there are no Gelic events.
GameOS
- LV2 syscall 726 sends Gelic device command and blocks until a response from the Gelic device arrives
- LV2 kernel uses this LV1 interface to send commands to Gelic device internally too, probably for wireless controllers and Wake On WLAN.
- The system call 726 is used heavily by VSH.
Parameters
r3 - command (16 bits)
r4 - effective address of command data buffer
r5 - size of command data buffer
Commands
Unknown (0x1)
- Used by VSH.
- Command buffer size is 0x10.
- Used in AP mode.
- Enables AP mode ???
Get AP SSID (0x3)
- Command buffer is of size 0x30.
- Returns SSID in AP mode.
offset 0xC - SSID (32 bytes)
Set AP SSID (0x5)
- Used by VSH.
- Command buffer is of size 0x30.
- Sets SSID in AP mode.
offset 0xC - SSID (32 bytes)
Get Channel (0xf)
- Used by VSH.
- Command buffer is of size 0x31.
- Data is returned from the device.
- Returns list of channels and active channel.
offset 0x2F - active channel (2 bytes)
Set Channel (0x11)
- Used by VSH.
- Command buffer size is 0xd
- Valid channels: 0 - 13. 0 means that the channel is selected automatically.
offset 0xC - channel (1 byte)
Unknown (0x27)
- Command buffer size is 0xF.
Set Antenna (0x29)
- Command buffer size is 0xe
offset 0xC - ??? (1 byte)
offset 0xD - ??? (1 byte)
Set AP WEP Configuration (0x5b)
- Used by VSH.
- Command buffer is of size 0x56.
- Sets WEP security type and WEP key.
- Security types: 0 - none, 1 - wep64, 2 - wep128
offset 0xE - security mode: 0 - none, 1 - wep64, 2 - wep128 (1 byte)
offset 0x10 - WEP key (4 * 18 bytes)
Unknown (0x61)
- Used by VSH.
- Command buffer size is 0xd
Unknown (0x65)
- Used by VSH.
- Command uffer size is 0xd.
- Used in AP mode.
Get Eurus Firmware Version (0x99)
- Used by VSH.
Here is the response on my PS3 Slim:
00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2| 00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19| 00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).| 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |.............. |
Get AP Operating Mode (0xb7)
- Used by VSH.
- Command buffer size is 0x10
- Returns AP operating mode (mixed, 11b or 11g).
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)
Set AP Operating Mode (0xb9)
- Used by VSH.
- Command buffer size is 0x10
- Sets AP operating mode (mixed, 11b or 11g).
offset 0xC - opmode: 0 - 11b, 1 - 11g, 2 - 11bg (4 bytes)
Unknown (0xc5)
- Used by VSH.
- Command buffer size is 0x10.
- Used in AP mode.
offset 0xC - ??? (4 bytes)
Set AP WPA AKM Suite (0xc9)
- Used by VSH.
- Command buffer size is 0x11.
- Sets WPA AKM suite in AP mode.
offset 0xC - AKM suite (4 bytes)
Set AP WPA Group Cipher Suite (0xcf)
- Used by VSH.
- Command buffer size is 0x10
- Used in AP + WPA mode.
offset 0xC - group cipher suite: group (4 bytes)
Set AP WPA PSK Binary (0xd3)
- Used by VSH.
- Command buffer size is 0x4c
- Sets WPA PSK binary
offset 0xC - PSK (64 bytes)
Set AP WPA Reauthentication Timeout (0xd5)
- Used by VSH.
- Command buffer size is 0x10
- Sets WPA Reauth timeout value in AP WPA mode.
- VSH uses 36000 as timeout.
offset 0xC - timeout value in seconds (2 bytes)
Unknown (0x127)
- Used by VSH.
- Command buffer size is 0x10.
- Used in AP + WPA mode.
Unknown (0x12b)
- Used by VSH.
- Command buffer size is 0x10.
- Used in AP + WPA mode.
Set AP WPA PSK Passphrase (0x17d)
- Used by VSH.
- Command buffer size is 0x2D
offset 0xD - passphrase (32 bytes)
Set AP WPA Pairwise Cipher Suite (0x1bf)
- Used by VSH.
- Command buffer size is 0x11
- Used in AP + WPA mode.
offset 0xC - pairwise cipher suite (4 bytes)
offset 0x10 - ??? (1 byte)
Unknown (0x1d9)
- Used by VSH.
- Command buffer size is 0x10
Start AP (0x1dd)
- Used by VSH.
- Command buffer size is 0xd
- 0x1 - stop AP, 0x2 - start AP
Unknown (0x1ed)
- Used by VSH.
- Command buffer is of size 0x17.
- Rate control ???
Get Eurus HW Revision (0x1fb)
- Command buffer size is 0x10.
Associate (0x1001)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0xd
- Data passed to Gelic device is all 0s
Get Common Configuration (0x1003)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x18
- Data passed to Gelic device is all 0s
Set Common Configuration (0x1005)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x18
- Hmm, VSH always removes QOS bit from capability, that means Jupiter doesn't support QOS ???
offset 0xC - BSS type: 0 - infrastructure, 1 - ???, 2 - adhoc (1 byte)
offset 0xD - authentication mode: 0 - open, 1 - shared key
offset 0xE - opmode: 0 - 11bg, 1 - 11b, 2 - 11g (1 byte)
offset 0xF - ??? (1 byte)
offset 0x10 - BSSID (6 bytes)
offset 0x16 - capability (2 bytes)
Get WEP Configuration (0x1013)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x50
- Data passed to Gelic device is all 0s
Set WEP Configuration (0x1015)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x50
Get WPA Configuration (0x1017)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x5b
- Data passed to Gelic device is all 0s
Set WPA Configuration (0x1019)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x5b
offset 0xE - security type: 0 - WPA, 1 - RSNA (1 byte)
offset 0xF - psk type: 0 - hex, 1 - bin (1 byte)
offset 0x10 - psk key: hex or bin (64 bytes)
offset 0x50 - group cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
offset 0x54 - pairwise cipher suite: 0x0050f202 - WPA TKIP, 0x0050f204 - WPA AES, 0x000fac02 - RSNA TKIP, 0x000fac04 - RSNA CCMP (4 bytes)
offset 0x58 - AKM suite: 0x0050f202 - WPA PSK, 0x000fac02 - RSNA PSK (4 bytes)
See IEEE 802.11 specification for more details about cipher/AKM suites
802.11 spec: [1]
Unknown (0x1025)
- Used by VSH.
- Command buffer size is 0x10.
- Sets short preamble or PureG mode ???
offset 0xC - 0 - off, 1 - on (1 byte)
Unknown (0x1031)
- Used by VSH.
- Command buffer size is 0xe
Get Scan Results (0x1033)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x5b0
- Data passed to Gelic device is all 0s
Scan Results
offset 0x0 - number of scan entries (1 byte)
offset 0x1 - array of scan entries
Scan Entry
offset 0x0 - size of this entry in bytes, this field is NOT included (2 bytes)
offset 0x2 - BSSID (6 bytes)
offset 0x8 - RSSI (1 byte)
offset 0x9 - timestamp (8 bytes)
offset 0x11 - beacon period (2 bytes)
offset 0x13 - capability (2 bytes)
offset 0x15 - information elements (see 802.11 specification)
Start Scan (0x1035)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size depends on size of channel list and ESSID string length
- Data passed to Gelic device contains channel list and ESSID string
- First 0x16 bytes in command data buffer are all 0s, then follows the channel list and after that ESSID
Diassociate (0x1037)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0xd
- Data passed to Gelic device is all 0s
Get RSSI (0x103d)
- Used by VSH.
- Used by LV1 on FAT models.
- Command buffer size is 0x17
offset 0x10 - MAC address of node (6 bytes)
offset 0x16 - RSSI (1 byte)
Get MAC Address (0x103f)
- Command buffer size is 0x13
offset 0xD - MAC address (6 bytes)
Set MAC Address (0x1041)
- Used by VSH.
- Used by LV1 too.
- Command buffer size is 0x12
Unsupported command (0x104b)
Set Short Slot Time (0x104d)
- Used by VSH.
- Command buffer size is 0xd.
- Enables/disables Short Slot Time
offset 0xC - enable (1 byte)
Get Short Slot Time (0x104f)
- Used by VSH.
- Command buffer size is 0xd.
- Returns current Short Slot Time configuration
offset 0xC - enabled (1 byte)
Unknown (0x1051)
- Used by VSH.
- Command buffer size is 0x5b3.
offset 0xE - ??? (1 byte)
offset 0xF - ??? (1 byte)
offset 0x10 - array of some data (each entry is 0xD bytes)
Unknown (0x1053)
- Used by VSH.
- Command buffer size is 0x70.
offset 0xC - ??? (4 bytes)
offset 0x10 - MAC address (6 bytes)
Unknown (0x1059)
- Used by VSH.
- Used in AP mode.
- Client access control ???
- Command buffer size is 0x2a8.
offset 0xC - ??? (1 byte)
offset 0xD - number of MAC addresses
offset 0xE - MAC address list (each MAC address is 6 bytes)
Unknown (0x105f)
- Used by LV2.
Get Zephyr HW Revision (0x1101)
- Used by VSH.
- Not a Gelic device command, handled by LV2 kernel.
- LV2 uses LV1 call lv1_net_control(0x8000000000000002)
- Command buffer size is 0x18.
Get MAC Address List (0x1117)
- Command buffer size is 0xce.
- Returns several MAC addresses.
offset 0xC - number of MAC addresses (2 bytes)
offset 0xE - MAC addresses (6 * number of MAC addresses)
Unknown (0x1133)
- Used by VSH.
- Command buffer size is 0x1A.
Set WOL MAC Address Filter (0x1139)
- Used by LV2 internally.
- Command buffer is of size 0x28.
Unknown (0x113b)
- Used by LV2 internally.
- Command buffer size is 0x20.
Set WOL Multicast Address Filter (0x113d)
- Used by LV2 internally.
- Command buffer is of size 0x2c.
Clear WOL Multicast Address Filter (0x113f)
- Used by LV2 internally.
- Command buffer is of size 0x28.
Unknown (0x1141)
- Used by LV2 internally.
Clear WOL Address Filter (0x1143)
- Used by LV2 internally.
- Command buffer size is 0x2c.
Unknown (0x114b)
- Used by LV2 internally.
Set WOL Magic Packet Mode (0x1155)
- Used by LV2 internally.
- Command buffer is of size 0x10.
- Enables/Disables WOL magic packet.
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
Unknown (0x1157)
- Used by LV2 internally.
- Command buffer size is 0x10.
Set WOL Multicast Address Filter Mode (0x1159)
- Used by LV2 internally.
- Command buffer size is 0x10.
- WOL function
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
Set Unicast Address Filter (0x115b)
- Used by LV2 internally.
- Command buffer is of size 0x6a.
- This command should be used to set proper MAC address or else device won't be able to receive packets destined to its own MAC address
offset 0xC - ??? (2 bytes)
offset 0xE - ??? (2 bytes)
offset 0x10 - MAC address (6 bytes)
Clear Unicast Address Filter (0x115d)
- Used by LV2 internally.
- Command buffer size is 0x6a.
Get Unicast Address Filter (0x115f)
- Used by LV2 internally.
- Command buffer is of size 0x6a.
Set Multicast Address Filter (0x1161)
- Used by LV2 internally.
- Command buffer size is 0x2c.
Clear Multicast Address Filter (0x1163)
- Used by LV2 internally.
- Command buffer size is 0x2c
- To clear all multicast addresses send command with all 0s.
offset 0xC - multicast address filter (4 * 8 bytes)
Get Multicast Address Filter (0x1165)
- Used by LV2 internally.
- Command buffer is of size 0x2c.
Set WOL Address Filter (0x1167)
- Used by LV2 internally.
- Command buffer size is 0x70.
Set WOL Address Filter Mode (0x116d)
- Used by LV2 internally.
- Command buffer size is 0x10.
- Enables/Disables WOL address matching
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
Set Unicast Address Filter Mode (0x116f)
- Used by LV2 internally.
- Command buffer size is 0x10.
offset 0xC - mode: 0 - disable, 1 - enable (4 bytes)
Get Device Status (0xfffb)
- Used by VSH.
- Not a Gelic device command, handled by LV2 kernel.
- Returned data size in command buffer is 0x10.
Unknown (0xfffc)
- Used by VSH.
- Not a Gelic device command, handled by LV2 kernel.
- LV2 uses LV1 call lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x4, 0x0, 0x0)
Get Channel Information (0xfffd)
- Used by VSH.
- Not a Gelic device command, handled by LV2 kernel.
- LV2 uses LV1 call lv1_net_control(0x1 /* bus id */, 0x0 /* dev id */, 0x6 /* get channel info command */, 0x0, 0x0, 0x0)
- Returns supported WLAN channels
Set Response Timeout (0xfffe)
- Used by VSH.
- Not a Gelic device command, handled by LV2 kernel.
- Sets timeout value which is used to wait for a response from Gelic device.
- Typical value used by VSH is 0x989680.
- Command buffer size is 0x14.
Unknown (0xffff)
- Used by VSH.
- Not a Gelic device command, handled by LV2 kernel.
- Returns 0x10 bytes in command buffer.
- Returns gelic device state ???
Events
struct ps3_eurus_event_hdr {
__le32 type;
__le32 id;
__le32 timestamp;
__le32 payload_length;
__le32 unknown;
} __packed;
struct ps3_eurus_event {
struct ps3_eurus_event_hdr hdr;
u8 payload[44];
} __packed;
Event Type 0x00000040
| Id | Description |
|---|---|
| 0x00000001 | Deauthenticated |
Event Type 0x00000008
| Id | Description |
|---|---|
| 0x00000010 | Client associated |
Event Type 0x00000010
| Id | Description |
|---|---|
| 0x00000002 | Client disassociated |
Event Type 0x00000080
| Id | Description |
|---|---|
| 0x00000001 | Beacon Lost |
| 0x00000002 | Connected |
| 0x00000004 | Scan Completed |
| 0x00000020 | WPA Connected |
| 0x00000040 | WPA Error (MIC Error) |
Event Type 0x00000100
- AP mode
| Id | Description |
|---|---|
| 0x00000002 | ??? |
| 0x00000010 | ??? |
Event Type 0x80000000
| Id | Description |
|---|---|
| 0x00000001 | Device Ready |
Enabling WLAN Gelic On FAT
Linux kernel doesn't use Gelic Device Control Interface like GameOS does it. To get WLAN working on Linux booted with GameOS rights, we have to disable Gelic Device Control Interface first because it's enabled for GameOS by default.
The value of repository node "ios.net.eurus.lpar" controls access to Gelic Device Control Interface. It's a bitmap. The position of a bit corresponds to LPAR id. During GameOS booting, HV process 9 (System Manager) sets bit at postion 2 to 1 which means enable Gelic Device Control Interface for LPAR 2.
To disable Gelic Device Control Interface on Linux, first unload Gelic device driver, then set value of repository node "ios.net.eurus.lpar" to 0 and load Gelic device driver again. After that WLAN should work again but only on FATs.
For PS3 Slim we need a new Linux Gelic device driver which uses Gelic Device Control Interface directly.
USB WLAN Interface (Codename Jupiter 2)
- On new PS3 models, WLAN interface is USB.
- Good news is that the same commands are used as with LV1 calls 196 and 195.
- There are 2 wireless devices: Station and AP.
- I got WLAN scan working.
Endpoints
- LV2 uses 3 USB endpoints of interface 3,4 and 5 to communicate with WLAN.
- Endpoints EP5 IN/OUT, EP6 IN/OUT and EP7 IN/OUT.
- WLAN commands are sent to endpoint EP5 OUT with interrupt transfers.
- WLAN events and WLAN command responses are received on endpoint EP5 IN with interrupt transfers.
- LV2 opens a USB communication pipe to endpoint EP5 IN and EP5 OUT.
- In my LV2 3.55 dump, pipe to EP5 IN has id 0x2 and pipe to EP5 OUT has id 0x3. Array of all opened USB pipes is at address 0x80000000004bd000 in my LV2 3.55 dump.
- EP5 is used to send commands to Jupiter and receive events from it.
- EP6 is used to send/receive data packets to/from the 1st WLAN device.
- EP7 is used to send/receive data packets to/from the 2nd WLAN device.
- lsusb is buggy on big-endian arch and shows some fields with bytes swapped !!!
Bus 002 Device 002: ID 054c:036f Sony Corp.
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 224 Wireless
bDeviceSubClass 1 Radio Frequency
bDeviceProtocol 1 Bluetooth
bMaxPacketSize0 64
idVendor 0x054c Sony Corp.
idProduct 0x036f
bcdDevice 20.12
iManufacturer 1
iProduct 2
iSerial 0
bNumConfigurations 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 2
bInterfaceProtocol 1
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x85 EP 5 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x4000 1x 0 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x05 EP 5 OUT
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x4000 1x 0 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 4
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 2
bInterfaceProtocol 2
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x86 EP 6 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0002 1x 2 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x06 EP 6 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0002 1x 2 bytes
bInterval 255
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 5
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 2
bInterfaceProtocol 3
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x87 EP 7 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0002 1x 2 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x07 EP 7 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0002 1x 2 bytes
bInterval 255
Device Initialization
- LV2 does 2 control transfers to EP0 during WLAN initialization
- First control transfer sends magic 0x20 data to device as CLEAR_FEATURE request.
- Second control transfer reads 0x2 bytes device status. On my PS3 slim, the status data is always 0x2031 if you send the right magic.
- Magic data sent in first control transfer is stored in LV2.
- If you send wrong magic, the first control transfer will fail !!!
- LV2 uses a state machine to initialize the Jupiter device. The state machine has 17 states.
Magic Data in Control Transfer
unsigned char ps3_usb_wlan_magic_data[] = {
0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8,
0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d,
};
Initialization State Machine
- Implemented in LV2.
State 1
- Command 0x114f is sent to WLAN device.
State 2
- Command 0x1171 is sent to WLAN device.
State 3
- LV2 waits for an event from WLAN device.
State 4
- Command 0x116f is sent to WLAN device.
State 5
- Command 0x115b is sent to WLAN device.
- Command data sent to WLAN device contains MAC address.
State 6
- Command 0x1161 is sent to WLAN device.
- Sets multicast address filter.
State 7
- Command 0x110d is sent to WLAN device.
State 8
- Command 0x1031 is sent to WLAN device.
State 9
- Command 0x1041 is sent to WLAN device.
- Command data sent to WLAN device contains MAC address.
State 10
- Command 0x29 is sent to WLAN device.
- Sets antenna.
State 11
- Command 0x110b is sent to WLAN device.
State 12
- Command 0x1109 is sent to WLAN device.
State 13
- Command 0x207 is sent to WLAN device.
State 14
- Command 0x203 is sent to WLAN device.
State 15
- Command 0x105f is sent to WLAN device.
- Command data sent to WLAN device contains MAC address, channel info and region code.
State 16
- LV2 waits for an event from WLAN device.
State 17
- LV2 accepts commands sent by LV2 syscall 726.
Test Program
- Here is a small program which executes a WLAN scan.
- I used libusb.
Source Code
/*
* PS3 USB WLAN
*
* Copyright (C) 2011 glevand (geoffrey.levand@mail.ru)
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>
#include <libusb-1.0/libusb.h>
#define USB_VENDOR_ID 0x054c /* $ONY */
#define USB_PRODUCT_ID 0x036f
#define USB_IFACE_NUMBER 3
#define USB_INTR_TRANSFER_EP5_IN_BUF_SIZE 0x800
#define USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE 0x800
struct wlan_cmd_pkt_hdr {
uint8_t unknown1;
uint8_t unknown2;
uint8_t unknown3;
uint8_t unknown4;
uint16_t unknown5;
uint8_t res1[2];
uint16_t tag;
uint8_t res2[14];
} __attribute__ ((packed));
struct wlan_cmd_hdr {
uint16_t command;
uint16_t tag;
uint16_t status;
uint16_t payload_size;
uint8_t res[4];
} __attribute__ ((packed));
struct wlan_event_pkt_hdr {
uint8_t unknown1;
uint8_t unknown2;
uint8_t unknown3;
uint8_t event_count;
} __attribute__ ((packed));
static libusb_context *usb_ctx;
static libusb_device_handle *usb_dev_handle;
static struct libusb_transfer *usb_intr_transfer_ep5_in;
static unsigned char usb_intr_transfer_ep5_in_buf[USB_INTR_TRANSFER_EP5_IN_BUF_SIZE];
static unsigned char usb_intr_transfer_ep5_out_buf[USB_INTR_TRANSFER_EP5_OUT_BUF_SIZE];
static pthread_mutex_t usb_wlan_cmd_mutex;
static pthread_cond_t usb_wlan_cmd_cond;
static int volatile usb_wlan_cmd_busy;
static uint16_t usb_wlan_cmd;
static void *usb_wlan_cmd_data;
static int volatile usb_wlan_cmd_thread_done;
/*
* WLAN won't work without this magic !!!
*/
static unsigned char usb_magic_data[] = {
0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8,
0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d,
};
static unsigned char my_mac_addr[] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
};
/*
* hexdump
*/
static void hexdump(const unsigned char *data, unsigned int data_size)
{
int i, j;
for (i = 0; i < data_size; i += 16) {
fprintf(stdout, "%08x:", i);
for (j = 0; j < 16; j++) {
if (i + j < data_size) {
fprintf(stdout, " %02x", data[i + j]);
} else {
fprintf(stdout, " ");
}
}
fprintf(stdout, " |");
for (j = 0; j < 16; j++) {
if (i + j < data_size) {
if (isprint(data[i + j]))
fprintf(stdout, "%c", data[i + j]);
else
fprintf(stdout, ".");
} else {
fprintf(stdout, " ");
}
}
fprintf(stdout, "|\n");
}
}
/*
* usb_handle_wlan_event
*/
static void usb_handle_wlan_event(struct wlan_event_pkt_hdr *wlan_event_pkt_hdr)
{
fprintf(stdout, "%s:%d: === got WLAN event ===\n", __func__, __LINE__);
/*
fprintf(stdout, "%s:%d: event packet header:\n", __func__, __LINE__);
fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__,
wlan_event_pkt_hdr->unknown1);
fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__,
wlan_event_pkt_hdr->unknown2);
fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__,
wlan_event_pkt_hdr->unknown3);
*/
fprintf(stdout, "%s:%d: event_count (0x%02x)\n", __func__, __LINE__,
wlan_event_pkt_hdr->event_count);
hexdump((unsigned char *) (wlan_event_pkt_hdr + 1), wlan_event_pkt_hdr->event_count * 64);
}
/*
* usb_handle_wlan_cmd_response
*/
static void usb_handle_wlan_cmd_response(struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr)
{
struct wlan_cmd_hdr *wlan_cmd_hdr;
uint8_t *wlan_cmd_payload;
fprintf(stdout, "%s:%d: === got WLAN command response ===\n", __func__, __LINE__);
wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);
/* convert all header fields to big-endian byte order !!! */
wlan_cmd_pkt_hdr->unknown5 = le16toh(wlan_cmd_pkt_hdr->unknown5);
wlan_cmd_pkt_hdr->tag = le16toh(wlan_cmd_pkt_hdr->tag); /* returned from request */
wlan_cmd_hdr->command = le16toh(wlan_cmd_hdr->command); /* request command + 1 */
wlan_cmd_hdr->tag = le16toh(wlan_cmd_hdr->tag); /* returned from request */
wlan_cmd_hdr->status = le16toh(wlan_cmd_hdr->status); /* 1 - success
2 - invalid parameters ???
3 - invalid command ??? */
wlan_cmd_hdr->payload_size = le16toh(wlan_cmd_hdr->payload_size); /* length of data that follows the header */
/*
fprintf(stdout, "%s:%d: command packet header:\n", __func__, __LINE__);
fprintf(stdout, "%s:%d: unknown1 (0x%02x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown1);
fprintf(stdout, "%s:%d: unknown2 (0x%02x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown2);
fprintf(stdout, "%s:%d: unknown3 (0x%02x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown3);
fprintf(stdout, "%s:%d: unknown4 (0x%02x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown4);
fprintf(stdout, "%s:%d: unknown5 (0x%04x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->unknown5);
fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__,
wlan_cmd_pkt_hdr->tag);
*/
fprintf(stdout, "%s:%d: command header:\n", __func__, __LINE__);
fprintf(stdout, "%s:%d: command (0x%04x)\n", __func__, __LINE__,
wlan_cmd_hdr->command);
if ((usb_wlan_cmd + 1) != wlan_cmd_hdr->command)
fprintf(stdout, "%s:%d: ==> command does not match, got (0x%04x) expected (0x%04x)\n",
__func__, __LINE__, wlan_cmd_hdr->command, usb_wlan_cmd + 1);
fprintf(stdout, "%s:%d: tag (0x%04x)\n", __func__, __LINE__,
wlan_cmd_hdr->tag);
fprintf(stdout, "%s:%d: status (0x%04x)\n", __func__, __LINE__,
wlan_cmd_hdr->status);
if (wlan_cmd_hdr->status != 0x1)
fprintf(stdout, "%s:%d: ==> command status != 0x1\n", __func__, __LINE__);
fprintf(stdout, "%s:%d: payload_size (0x%04x)\n", __func__, __LINE__,
wlan_cmd_hdr->payload_size);
fprintf(stdout, "%s:%d: command payload:\n", __func__, __LINE__);
hexdump(wlan_cmd_payload, wlan_cmd_hdr->payload_size);
memcpy(usb_wlan_cmd_data, wlan_cmd_payload, wlan_cmd_hdr->payload_size);
pthread_mutex_lock(&usb_wlan_cmd_mutex);
usb_wlan_cmd_busy = 0;
pthread_cond_signal(&usb_wlan_cmd_cond);
pthread_mutex_unlock(&usb_wlan_cmd_mutex);
}
/*
* usb_intr_transfer_ep5_in_cb
*/
static void usb_intr_transfer_ep5_in_cb(struct libusb_transfer *transfer)
{
struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr;
int error;
fprintf(stdout, "%s:%d: === got interrupt transfer ===\n", __func__, __LINE__);
fprintf(stdout, "%s:%d: transfer status (%d) length (%d)\n",
__func__, __LINE__, transfer->status, transfer->actual_length);
wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) transfer->buffer;
if (wlan_cmd_pkt_hdr->unknown3 == 0x6)
usb_handle_wlan_cmd_response(wlan_cmd_pkt_hdr);
else if (wlan_cmd_pkt_hdr->unknown3 == 0x8)
usb_handle_wlan_event((struct wlan_event_pkt_hdr *) transfer->buffer);
else
fprintf(stdout, "%s:%d: got unknown packet (0x%02x)\n",
__func__, __LINE__, wlan_cmd_pkt_hdr->unknown3);
memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf));
libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf),
usb_intr_transfer_ep5_in_cb, NULL, 0);
error = libusb_submit_transfer(usb_intr_transfer_ep5_in);
if (error) {
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
__func__, __LINE__, error);
exit(1);
}
}
/*
* usb_intr_transfer_ep5_out_cb
*/
static void usb_intr_transfer_ep5_out_cb(struct libusb_transfer *transfer)
{
/*
fprintf(stdout, "%s:%d: sent interrupt transfer\n", __func__, __LINE__);
fprintf(stdout, "%s:%d: transfer status (%d)\n", __func__, __LINE__, transfer->status);
*/
libusb_free_transfer(transfer);
}
/*
* usb_wlan_cmd_send
*/
static int usb_wlan_cmd_send(uint16_t command, const uint8_t *data, unsigned int data_size)
{
struct wlan_cmd_pkt_hdr *wlan_cmd_pkt_hdr;
struct wlan_cmd_hdr *wlan_cmd_hdr;
uint8_t *wlan_cmd_payload;
struct libusb_transfer *transfer;
int error;
fprintf(stdout, "%s:%d: sending command (0x%04x) data size (0x%04x) command size (0x%04x)\n",
__func__, __LINE__, command, data_size, data_size + sizeof(struct wlan_cmd_hdr));
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__);
error = -1;
goto fail;
}
wlan_cmd_pkt_hdr = (struct wlan_cmd_pkt_hdr *) usb_intr_transfer_ep5_out_buf;
wlan_cmd_hdr = (struct wlan_cmd_hdr *) (wlan_cmd_pkt_hdr + 1);
wlan_cmd_payload = (uint8_t *) (wlan_cmd_hdr + 1);
wlan_cmd_pkt_hdr->unknown1 = 0x1;
wlan_cmd_pkt_hdr->unknown2 = 0x1;
wlan_cmd_pkt_hdr->unknown3 = 0x6;
wlan_cmd_pkt_hdr->unknown4 = 0x0;
wlan_cmd_pkt_hdr->unknown5 = 0x1;
wlan_cmd_pkt_hdr->tag = 0xf00d; /* returned in response */
wlan_cmd_hdr->command = command;
wlan_cmd_hdr->tag = 0xcafe; /* returned in response */
wlan_cmd_hdr->status = 0xa;
wlan_cmd_hdr->payload_size = data_size;
memcpy(wlan_cmd_payload, data, data_size);
usb_wlan_cmd = command;
usb_wlan_cmd_data = (void *) data;
libusb_fill_interrupt_transfer(transfer, usb_dev_handle, LIBUSB_ENDPOINT_OUT | 0x5,
usb_intr_transfer_ep5_out_buf,
sizeof(struct wlan_cmd_pkt_hdr) + sizeof(struct wlan_cmd_hdr) + wlan_cmd_hdr->payload_size,
usb_intr_transfer_ep5_out_cb, NULL, 0);
/* convert all header fields to little-endian byte order !!! */
wlan_cmd_pkt_hdr->unknown5 = htole16(wlan_cmd_pkt_hdr->unknown5);
wlan_cmd_pkt_hdr->tag = htole16(wlan_cmd_pkt_hdr->tag);
wlan_cmd_hdr->command = htole16(wlan_cmd_hdr->command);
wlan_cmd_hdr->tag = htole16(wlan_cmd_hdr->tag);
wlan_cmd_hdr->status = htole16(wlan_cmd_hdr->status);
wlan_cmd_hdr->payload_size = htole16(wlan_cmd_hdr->payload_size);
error = libusb_submit_transfer(transfer);
if (error) {
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
__func__, __LINE__, error);
goto fail_free_transfer;
}
pthread_mutex_lock(&usb_wlan_cmd_mutex);
usb_wlan_cmd_busy = 1;
while (usb_wlan_cmd_busy)
pthread_cond_wait(&usb_wlan_cmd_cond, &usb_wlan_cmd_mutex);
pthread_mutex_unlock(&usb_wlan_cmd_mutex);
return 0;
fail_free_transfer:
libusb_free_transfer(transfer);
fail:
return error;
}
/*
* usb_wlan_cmd_start_scan
*/
static int usb_wlan_cmd_start_scan(void)
{
unsigned char data[256], *ptr;
unsigned int data_size;
memset(data, 0, sizeof(data));
ptr = data;
*ptr++ = 0x0;
*ptr++ = 0x1;
*ptr++ = 0x64;
*ptr++ = 0x0;
ptr = data + 0xa;
*ptr++ = 0x3;
*ptr++ = 13; /* number of channels */
*ptr++ = 1; /* channels */
*ptr++ = 2;
*ptr++ = 3;
*ptr++ = 4;
*ptr++ = 5;
*ptr++ = 6;
*ptr++ = 7;
*ptr++ = 8;
*ptr++ = 9;
*ptr++ = 10;
*ptr++ = 11;
*ptr++ = 12;
*ptr++ = 13;
data_size = ptr - data;
return usb_wlan_cmd_send(0x1035, data, data_size);
}
/*
* usb_wlan_cmd_get_scan_results
*/
static int usb_wlan_cmd_get_scan_results(void)
{
unsigned char data[1456];
unsigned int data_size;
memset(data, 0, sizeof(data));
data_size = sizeof(data);
return usb_wlan_cmd_send(0x1033, data, data_size);
}
/*
* usb_wlan_cmd_0x99
*/
static int usb_wlan_cmd_0x99(void)
{
unsigned char data[0x3e];
unsigned int data_size;
memset(data, 0, sizeof(data));
data_size = sizeof(data);
return usb_wlan_cmd_send(0x99, data, data_size);
}
/*
* usb_wlan_init
*/
static int usb_wlan_init(void)
{
unsigned char data[1456], *ptr;
unsigned int data_size;
int error;
/* state 0x1 */
memset(data, 0, sizeof(data));
data_size = 0x518;
error = usb_wlan_cmd_send(0x114f, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x114f (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
/* state 0x2 */
memset(data, 0, sizeof(data));
data_size = 0;
error = usb_wlan_cmd_send(0x1171, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1171 (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
/* wait for a WLAN event */
/* state 0x4 */
memset(data, 0, sizeof(data));
ptr = data;
*ptr++ = 0x1;
data_size = 0x4;
error = usb_wlan_cmd_send(0x116f, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x116f (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
/* state 0x5 */
memset(data, 0, sizeof(data));
ptr = data;
*ptr++ = 0x1;
ptr = data + 0x4;
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));
data_size = 0x5e;
error = usb_wlan_cmd_send(0x115b, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x115b (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
/* state 0x6 */
memset(data, 0, sizeof(data));
ptr = data + 0x1c;
*ptr++ = 0x20;
data_size = 0x20;
error = usb_wlan_cmd_send(0x1161, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1161 (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
memset(data, 0, sizeof(data));
ptr = data + 0xc;
memset(ptr, 0xff, 7 * 4);
data_size = 0x80;
error = usb_wlan_cmd_send(0x110d, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x110d (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
memset(data, 0, sizeof(data));
data_size = 0x2;
error = usb_wlan_cmd_send(0x1031, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1031 (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
memset(data, 0, sizeof(data));
ptr = data;
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));
data_size = 0x6;
error = usb_wlan_cmd_send(0x1041, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1041 (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
/* state 0xa */
memset(data, 0, sizeof(data));
ptr = data;
*ptr++ = 0x2;
*ptr++ = 0x2;
data_size = 0x2;
error = usb_wlan_cmd_send(0x29, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x29 (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
memset(data, 0, sizeof(data));
ptr = data;
*ptr++ = 0x1;
ptr = data + 8;
*ptr++ = 0x20;
data_size = 0xc;
error = usb_wlan_cmd_send(0x110b, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x110b (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
memset(data, 0, sizeof(data));
ptr = data;
*ptr++ = 0x1;
ptr = data + 0x4;
*ptr++ = 0x15;
*ptr++ = 0x27;
*ptr++ = 0x12;
*ptr++ = 0x0;
*ptr++ = 0x6;
*ptr++ = 0x0;
ptr = data + 0xc;
*ptr++ = 0x9;
*ptr++ = 0x0;
*ptr++ = 0x1;
ptr = data + 0x10;
*ptr++ = 0xff;
*ptr++ = 0xff;
*ptr++ = 0xff;
*ptr++ = 0xff;
*ptr++ = 0xff;
*ptr++ = 0xff;
data_size = 0x16;
error = usb_wlan_cmd_send(0x1109, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x1109 (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
memset(data, 0, sizeof(data));
ptr = data;
*ptr++ = 0x1;
data_size = 0x4;
error = usb_wlan_cmd_send(0x207, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x207 (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
memset(data, 0, sizeof(data));
ptr = data;
*ptr++ = 0x4;
data_size = 0x4;
error = usb_wlan_cmd_send(0x203, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x203 (%d)\n",
__func__, __LINE__, error);
return error;
}
sleep(2);
/* state 0xf */
memset(data, 0, sizeof(data));
ptr = data;
*ptr++ = 0xff;
*ptr++ = 0x1f;
memcpy(ptr, my_mac_addr, sizeof(my_mac_addr));
ptr = data + 0x8;
*ptr++ = 0x2;
*ptr++ = 0x2;
data_size = 0xa;
error = usb_wlan_cmd_send(0x105f, data, data_size);
if (error) {
fprintf(stderr, "%s:%d: could not send command 0x105f (%d)\n",
__func__, __LINE__, error);
return error;
}
return 0;
}
/*
* usb_wlan_cmd_thread
*/
static void *usb_wlan_cmd_thread(void *arg)
{
int error;
error = usb_wlan_init();
if (error) {
fprintf(stderr, "%s:%d: could not initialize device (%d)\n",
__func__, __LINE__, error);
goto done;
}
sleep(5);
error = usb_wlan_cmd_0x99();
if (error) {
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
__func__, __LINE__, error);
goto done;
}
error = usb_wlan_cmd_start_scan();
if (error) {
fprintf(stderr, "%s:%d: could not start scanning (%d)\n",
__func__, __LINE__, error);
goto done;
}
sleep(10);
error = usb_wlan_cmd_get_scan_results();
if (error) {
fprintf(stderr, "%s:%d: could not get scan results (%d)\n",
__func__, __LINE__, error);
goto done;
}
sleep(10);
done:
usb_wlan_cmd_thread_done = 1;
return NULL;
}
/*
* main
*/
int main(int argc, char **argv)
{
unsigned char buf[256];
pthread_t tid;
struct timeval tv;
int error;
pthread_mutex_init(&usb_wlan_cmd_mutex, NULL);
pthread_cond_init(&usb_wlan_cmd_cond, NULL);
error = libusb_init(&usb_ctx);
if (error) {
fprintf(stderr, "%s:%d: libusb_init failed (%d)\n", __func__, __LINE__, error);
exit(1);
}
libusb_set_debug(usb_ctx, 5);
usb_dev_handle = libusb_open_device_with_vid_pid(usb_ctx, USB_VENDOR_ID, USB_PRODUCT_ID);
if (!usb_dev_handle) {
fprintf(stderr, "%s:%d: could not open device\n", __func__, __LINE__);
exit(1);
}
if(libusb_kernel_driver_active(usb_dev_handle, USB_IFACE_NUMBER)) {
fprintf(stdout, "%s:%d: kernel driver is attached\n", __func__, __LINE__);
error = libusb_detach_kernel_driver(usb_dev_handle, USB_IFACE_NUMBER);
if (error) {
fprintf(stderr, "%s:%d: could not detach kernel driver (%d)\n",
__func__, __LINE__, error);
exit(1);
}
fprintf(stdout, "%s:%d: kernel driver dettached\n", __func__, __LINE__);
}
error = libusb_claim_interface(usb_dev_handle, USB_IFACE_NUMBER);
if (error) {
fprintf(stderr, "%s:%d: could not claim interface (%d)\n",
__func__, __LINE__, error);
exit(1);
}
error = libusb_control_transfer(usb_dev_handle, 0x40, 0x1, 0x9, 0x0,
usb_magic_data, sizeof(usb_magic_data), 0);
if (error < 0) {
fprintf(stderr, "%s:%d: could not do control transfer (%d)\n",
__func__, __LINE__, error);
exit(1);
}
fprintf(stdout, "%s:%d: number of bytes transferred (%d)\n", __func__, __LINE__, error);
error = libusb_control_transfer(usb_dev_handle, 0xc0, 0x0, 0x2, 0x0, buf, 2, 0);
if (error < 0) {
fprintf(stderr, "%s:%d: could not do control transfer (%d)\n",
__func__, __LINE__, error);
exit(1);
}
fprintf(stdout, "%s:%d: number of bytes received (%d)\n", __func__, __LINE__, error);
fprintf(stdout, "%s:%d: 0x%02x 0x%02x\n", __func__, __LINE__, buf[0], buf[1]);
usb_intr_transfer_ep5_in = libusb_alloc_transfer(0);
if (!usb_intr_transfer_ep5_in) {
fprintf(stderr, "%s:%d: could not allocate transfer\n", __func__, __LINE__);
exit(1);
}
memset(usb_intr_transfer_ep5_in_buf, 0, sizeof(usb_intr_transfer_ep5_in_buf));
libusb_fill_interrupt_transfer(usb_intr_transfer_ep5_in, usb_dev_handle, LIBUSB_ENDPOINT_IN | 0x5,
usb_intr_transfer_ep5_in_buf, sizeof(usb_intr_transfer_ep5_in_buf),
usb_intr_transfer_ep5_in_cb, NULL, 0);
error = libusb_submit_transfer(usb_intr_transfer_ep5_in);
if (error) {
fprintf(stderr, "%s:%d: could not submit transfer (%d)\n",
__func__, __LINE__, error);
exit(1);
}
error = pthread_create(&tid, NULL, usb_wlan_cmd_thread, NULL);
if (error) {
fprintf(stderr, "%s:%d: could not create WLAN command thread (%d)\n",
__func__, __LINE__, error);
exit(1);
}
while (!usb_wlan_cmd_thread_done) {
tv.tv_sec = 1;
tv.tv_usec = 0;
error = libusb_handle_events_timeout(usb_ctx, &tv);
if (error) {
fprintf(stderr, "%s:%d: could not handle events (%d)\n",
__func__, __LINE__, error);
exit(1);
}
}
libusb_free_transfer(usb_intr_transfer_ep5_in);
error = libusb_release_interface(usb_dev_handle, USB_IFACE_NUMBER);
if (error)
fprintf(stderr, "%s:%d: could not release interface (%d)\n",
__func__, __LINE__, error);
libusb_close(usb_dev_handle);
libusb_exit(usb_ctx);
exit(0);
}
Output
glevand@debian-hdd:~/ps3_usb_wlan$ sudo ./ps3_usb_wlan sudo: unable to resolve host debian-hdd main:824: number of bytes transferred (32) main:833: number of bytes received (2) main:835: 0x20 0x31 usb_wlan_cmd_send:288: sending command (0x114f) data size (0x0518) command size (0x0524) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1150) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0006) usb_handle_wlan_cmd_response:205: ==> command status != 0x1 usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_wlan_cmd_send:288: sending command (0x1171) data size (0x0000) command size (0x000c) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1172) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68) usb_handle_wlan_event:133: === got WLAN event === usb_handle_wlan_event:144: event_count (0x01) 00000000: 00 04 00 00 10 00 00 00 3c 22 02 00 00 00 00 00 |........<"......| 00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............| 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| usb_wlan_cmd_send:288: sending command (0x116f) data size (0x0004) command size (0x0010) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1170) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_wlan_cmd_send:288: sending command (0x115b) data size (0x005e) command size (0x006a) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x115c) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_wlan_cmd_send:288: sending command (0x1161) data size (0x0020) command size (0x002c) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1162) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_wlan_cmd_send:288: sending command (0x110d) data size (0x0080) command size (0x008c) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x110e) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_wlan_cmd_send:288: sending command (0x1031) data size (0x0002) command size (0x000e) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1032) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0002) usb_handle_wlan_cmd_response:210: command payload: 00000000: 00 00 |.. | usb_wlan_cmd_send:288: sending command (0x1041) data size (0x0006) command size (0x0012) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (42) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1042) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0006) usb_handle_wlan_cmd_response:210: command payload: 00000000: 00 11 22 33 44 55 |.."3DU | usb_wlan_cmd_send:288: sending command (0x0029) data size (0x0002) command size (0x000e) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (38) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x002a) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0002) usb_handle_wlan_cmd_response:210: command payload: 00000000: 02 02 |.. | usb_wlan_cmd_send:288: sending command (0x110b) data size (0x000c) command size (0x0018) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (48) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x110c) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x000c) usb_handle_wlan_cmd_response:210: command payload: 00000000: 01 00 00 00 00 00 00 00 20 00 00 00 |........ ... | usb_wlan_cmd_send:288: sending command (0x1109) data size (0x0016) command size (0x0022) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (58) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x110a) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0016) usb_handle_wlan_cmd_response:210: command payload: 00000000: 01 00 00 00 15 27 12 00 06 00 00 00 09 00 01 00 |.....'..........| 00000010: ff ff ff ff ff ff |...... | usb_wlan_cmd_send:288: sending command (0x0207) data size (0x0004) command size (0x0010) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x0208) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0004) usb_handle_wlan_cmd_response:210: command payload: 00000000: 01 00 00 00 |.... | usb_wlan_cmd_send:288: sending command (0x0203) data size (0x0004) command size (0x0010) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (40) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x0204) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0004) usb_handle_wlan_cmd_response:210: command payload: 00000000: 04 00 00 00 |.... | usb_wlan_cmd_send:288: sending command (0x105f) data size (0x000a) command size (0x0016) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (36) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1060) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0000) usb_handle_wlan_cmd_response:210: command payload: usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68) usb_handle_wlan_event:133: === got WLAN event === usb_handle_wlan_event:144: event_count (0x01) 00000000: 80 00 00 00 00 10 00 00 9e 2b 02 00 04 00 00 00 |.........+......| 00000010: fc 90 02 c0 01 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............| 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| usb_wlan_cmd_send:288: sending command (0x0099) data size (0x003e) command size (0x004a) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (98) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x009a) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x003e) usb_handle_wlan_cmd_response:210: command payload: 00000000: 4a 55 50 49 54 45 52 2d 54 57 4f 2d 46 57 2d 32 |JUPITER-TWO-FW-2| 00000010: 30 2e 30 2e 31 32 2e 70 30 28 4a 61 6e 20 31 39 |0.0.12.p0(Jan 19| 00000020: 20 32 30 31 30 20 32 31 3a 32 30 3a 35 33 29 00 | 2010 21:20:53).| 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |.............. | usb_wlan_cmd_send:288: sending command (0x1035) data size (0x0019) command size (0x0025) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (61) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1036) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0019) usb_handle_wlan_cmd_response:210: command payload: 00000000: 00 01 64 00 00 00 00 00 00 00 03 0d 01 02 03 04 |..d.............| 00000010: 05 06 07 08 09 0a 0b 0c 0d |......... | usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (68) usb_handle_wlan_event:133: === got WLAN event === usb_handle_wlan_event:144: event_count (0x01) 00000000: 80 00 00 00 04 00 00 00 96 2e 02 00 01 00 00 00 |................| 00000010: fc 90 02 c0 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020: 13 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 |... ............| 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| usb_wlan_cmd_send:288: sending command (0x1033) data size (0x05b0) command size (0x05bc) usb_intr_transfer_ep5_in_cb:233: === got interrupt transfer === usb_intr_transfer_ep5_in_cb:236: transfer status (0) length (1403) usb_handle_wlan_cmd_response:158: === got WLAN command response === usb_handle_wlan_cmd_response:191: command header: usb_handle_wlan_cmd_response:192: command (0x1034) usb_handle_wlan_cmd_response:199: tag (0xcafe) usb_handle_wlan_cmd_response:201: status (0x0001) usb_handle_wlan_cmd_response:207: payload_size (0x0557) usb_handle_wlan_cmd_response:210: command payload: ... Here is scan output (removed by me) ...
Associate with AP
- I got association with AP working.
- If WLAN device is connected to an AP then the green LED is on, when data is received then the LED blinks.
- Data reception works finally !!!
How to Associate with WPA AP
- Set common configuration (command 0x1005)
- Set WPA configuration (command 0x1019)
- Set rate configuration (command 0x1ed)
- Associate (command 0x1001)
Packet Reception
- EP6 IN and EP7 IN endpoints are used for packet reception
- LV2 sends bulk transfers to both endpoints
- 4 bulk transfers are sent simultaneously for each enpoint
- Every bulk transfer is of size 0x620
- Make sure you set multicast address filter properly or else you won't receive broadcast packets !!!
- Bulk transfers returned by the host controller which do not contain any data have size of 0x10 bytes else transfers contain valid Ethernet frame. All 802.11 related data is stripped by the WLAN Gelic device.
- Make sure you set right MAC address with command 0x115b else device won't be able to receive packets destined to its own MAC address !!!
Test with libusb
usb_bulk_transfer_ep6_in_cb:318: === got data transfer === usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (98) 00000000: ff ff ff ff ff ff ?? ?? ?? ?? ?? ?? 08 00 45 00 |..............E.| 00000010: 00 54 00 00 40 00 40 01 b5 fe c0 a8 01 5b c0 a8 |.T..@.@......[..| 00000020: 01 ff 08 00 9c 69 0d 45 00 e2 4e 5d 34 26 00 07 |.....i.E..N]4&..| 00000030: df e1 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 |................| 00000040: 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 |.......... !"#$%| 00000050: 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 |&'()*+,-./012345| 00000060: 36 37 |67 | usb_bulk_transfer_ep6_in_cb:318: === got data transfer === usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16) 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 |................| usb_bulk_transfer_ep6_in_cb:318: === got data transfer === usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16) 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 |................| usb_bulk_transfer_ep6_in_cb:318: === got data transfer === usb_bulk_transfer_ep6_in_cb:321: transfer status (0) length (16) 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 |................|
Multicast Address Filter
- WLAN Gelic device supports hardware multicast address filtering
- Multicast address filtering is implemented with MAC address hashing and filter bitmap
- Filter bitmap is of size 4 * 8 bytes
- Multicast address filter is set with command 0x1161
MAC Address Hash Function
- Used by LV2
unsigned char hash(unsigned char *data, unsigned int size)
{
unsigned int hash;
int i, j;
/*XXX: reverse data bits */
hash = 0xffffffff;
for (i = 0; i < size; i++) {
hash = (((unsigned int) data[i]) << 24) ^ hash;
for (j = 0; j < 8; j++) {
if (((int) hash) >= 0) {
hash = hash << 1;
} else {
hash = (hash << 1) ^ 0x04c10000;
hash = hash ^ 0x00001db7;
}
}
}
hash = ((hash >> 24) & 0xf8) | (hash & 0x7);
return hash & 0xff;
}
h = hash(mac_addr, 6);
v = 1 << (h & 0x1f); /* word value in filter */
p = h >> 5; /* word position in filter */
For broadcast address:
------------------------
v = 0x20000000
p = 7
That's why 0x20 is used with command 0x1161 !!! Without it the device won't deliver broadcast traffic.
Learned it the hard way, after 2 days of trying to get packet reception working :)
Packet Transmission
- Tx packets are sent to EP6 OUT
- Tx packets are normal Ethernet frames, they don't contain any WLAN data or other headers
AP Mode
- I got AP mode working with security disabled for now
- On EP7 IN i'm able to receive Ethernet frames which were sent from clients.
AP Mode with Security Disabled
- Set AP SSID (command 0x5)
- Set channel (command 0x11)
- Set AP opmode (command 0xb9)
- Configure rate control (command 0x1ed)
- Set AP WEP Configuration (command 0x5b, all 0s)
- Command 0x61 (param 0x0)
- Command 0xc5 (param 0x0)
- Command 0x1 (param 0x1)
- Command 0x1dd (param 0x2) --- starts AP mode
- Now green LED should be on
Test:
[glevand@arch ps3_jupiter_libusb]$ sudo ./ps3_jupiter_ap EURUS command 0x114f status (0x0006) got event (0x00000400 0x00000010 0x00023968 0x00000000 0xc00290fc) got event (0x00000080 0x00001000 0x0002396b 0x00000004 0xc00290fc) firmware version: JUPITER-TWO-FW-20.0.12.p0(Jan 19 2010 21:20:53) got event (0x00000100 0x00000010 0x0002396d 0x00000001 0xc00290fc) got event (0x00000008 0x00000010 0x00023d80 0x00000008 0xc00290fc) RX transfer completed with length 42 RX transfer completed with length 42 RX transfer completed with length 42 RX transfer completed with length 42 RX transfer completed with length 42 RX transfer completed with length 42 RX transfer completed with length 42 RX transfer completed with length 42 <----------- 42 is length of ARP packet
ps3-jupiter Linux Drivers
- ps3_jupiter.ko is the common part of STA and AP mode. It implements a command interface to WLAN Gelic device and disptaches events to STA and AP drivers.
- ps3_jupiter_sta.ko is a STA mode implementation.
- ps3_jupiter_ap.ko is a AP mode implementation.
- Simple scanning works already in STA mode (try it out with iwlist scan)
- Packet reception works
- Packet transmission works
- WPA/WPA2 fully working and usable with wpa_supplicant
Finally, after several weeks of hard programming and reversing, the WLAN driver ps3_jupiter_sta achieved the milestone where i can use it with WPA2 :) I actually use it currently with WPA2 on my PS3 slim. It works damn !!! Try it out and report bugs and problems to me.
TODO
- Implement association in STA mode (finished)
- Implement packet reception and transmission in STA mode (finished)
- Implement WEP support
- Implement AP mode
- Find out if Jupiter supports Monitor mode and if yes how to enable it
- Implement EURUS driver for PHATs (has many advantages over the old OtherOS approach, e.g. AP mode)
- Port to FreeBSD
LV2 Network Stack
- LV2 uses BSD network stack, e.g. struct mbuf
- It's almost identical to FreeBSD network stack.
Network Device
IOCTLs
Set Multicast Address Filter (0x81012000)
- Sets multicast address filter
- Uses LV1 calls lv1_net_remove_multicast_address and lv1_net_add_multicast_address for Ethernet Gelic device
- Uses Eurus commands 0x1161, 0x1163 and 0x1165 for WLAN Gelic device
Unknown (0x8101200E)
- Uses LV1 call lv1_net_control(0x8000000000000001)
Unknown (0x81040000)
- Uses LV1 call lv1_net_control(0x8, [0x0, 0x1 or 0x2]) for Ethernet Gelic device
- Uses Eurus commands 0x116F, 0x115D and 0x115B for WLAN Gelic device
Enable/Disable WOL Magic Packet (0x81080000)
- Enables/Disables WOL Magic Packet
- Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x1 /* GELIC_LV1_WOL_MAGIC_PACKET */) for Ethernet Gelic device
- Uses Eurus commands 0x1139 and 0x1155 for WLAN Gelic device
Unknown (0x81080001)
- Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x2) for Ethernet Gelic device
- Uses Eurus commands 0x113B and 0x1157 for WLAN Gelic device
Unknown (0x81080002)
- Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x3) for Ethernet Gelic device
- Uses Eurus commands 0x113D and 0x1159 for WLAN Gelic device
Unknown (0x81080003)
- Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x4) for Ethernet Gelic device
- Uses Eurus command 0x1161 for WLAN Gelic device
Unknown (0x81080005)
- Uses LV1 call lv1_net_control(0x5 /* GELIC_LV1_SET_WOL */, 0x6 /* GELIC_LV1_WOL_ADD_MATCH_ADDR */) for Ethernet Gelic device
- Uses Eurus commands 0x116D and 0x1167 for WLAN Gelic device
Network Packet
- LV2 network packet is represented by struct mbuf
SYSCON
Crossreference: ps3devwiki.com::SYSCON
SYSCON MMIO registers can be accessed on Linux with a driver using lv1_undocumented_function_114, e.g. ps3sbmmio. Use ps3sbmmio device driver carefully, an access at some addresses could shutdown your PS3.
Packet Header
- Size is 0x10.
struct sc_hdr {
uint8_t service_id;
uint8_t version; /* must be 1 !!! */
uint16_t transaction_id; /* returned in response */
uint8_t res[2];
uint16_t cksum; /* checksum of first 6 header bytes */
uint32_t index; /* SYSCON index: 0-4 */
uint16_t payload_size[2]; /* body size */
};
Sending Packets
- Before sending new packet to SYSCON, the Hypervisor checks 2 words at offsets 0x2400008DFF0 and 0x2400008CFF4.
- The Hypervisor busy waits until (value + 1) at offset 0x2400008CFF4 is NOT equal to value at offset 0x2400008DFF0.
- The packet is sent with 4 byte transfers.
- First, the Hypervisor sends the header of the packet, 4 word transfers.
- The header is written beginning at the address 0x2400008D000.
- After that the Hypervisor sends the body of the packet, with 4 byte transfers too.
- The body is written beginning at the address 0x2400008D010.
- If the packet size is NOT divisible by 4 then the Hypervisor sends the remaining bytes (at most 3) as a word padded with 0s.
- After the packet body was written, the Hypervisor calculates checksum of the whole packet and writes it at the address where the last word of packet body was written + 4.
uint32_t cksum = 0;
for (i = 0; i < packet_size; i++)
cksum -= packet[i];
cksum = cksum & 0xffff;
- After the packet checksum was written, the Hypervisor reads the value at offset 0x2400008DFF0, modifies it and stores back:
value = value + 1; value &= 0xffff; value = (value << 16) | value;
- To notify the SYSCON about the new packet, the Hypervisor writes 0x1 to address 0x2400008E100.
Receiving Packets
- The Hypervisor installs an interrupt handler for the SYSCON.
- First, the Hypervisor reads a word from address 0x2400008E000, ors it with 0xFFFFFFFD and writes the value back.
- Then, the Hypervisor reads a word from address 0x2400008E004 and tests if bit 0x2 is set or not. The bit 0x2 should be not 0 or else the Hypervisor panics.
- After that, the Hypervisor reads a word at address 0x2400008CFF0 and 0x2400008DFF4. If there is a new packet pending from SYSCON, then the (value + 1) at 0x2400008CFF0 should be equal the value at 0x2400008DFF4.
- The Hypervisor reads the header of the packet beginning at the address 0x2400008C000.
- The header is read with 4 word transfers by the Hypervisor.
- The byte at offset 1 in the packet header must be 1 or else the Hypervisor discards the packet as invalid.
- The Hypervisor calculates the checksum of the packet header and checks it with the checksum stored in the header. If they don't match then the Hypervisor discards the packet.
- The Hypervisor reads the body of the packet beginning at the address 0x2400008C010.
- The header and the body of the received packet can be read as many times as you want !!! They remain until next SYSCON packet is received
which gives us the possibility to communicate with SYSCON on Linux easily :)
Test
1. Before sending SYSCON packet:
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C 00000000 01 18 01 18 |....| 00000004 root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C 00000000 01 18 01 18 |....| 00000004 root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C 00000000 01 24 01 24 |.$.$| 00000004 root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C 00000000 01 24 01 24 |.$.$| 00000004
2. SYSCON packet was sent by using ps3dm_scm read_eprom.
3. After sending SYSCON packet:
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff4)) status=noxfer | hexdump -C 00000000 01 19 01 19 |....| 00000004 root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C 00000000 01 19 01 19 |....| 00000004 root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8cff0)) status=noxfer | hexdump -C 00000000 01 25 01 25 |.%.%| 00000004 root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff4)) status=noxfer | hexdump -C 00000000 01 25 01 25 |.%.%| 00000004
4. Received Header
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=16 skip=$((0x8c000)) status=noxfer | hexdump -C 00000000 14 01 00 00 00 00 80 15 00 00 00 03 00 05 00 05 |................| 00000010
5. Received Body
root@debian-hdd:~# dd if=/dev/ps3sbmmio bs=1 count=8 skip=$((0x8c010)) status=noxfer | hexdump -C 00000000 00 00 c7 01 ff 00 00 00 |..Ç.ÿ...| 00000008
Examples
Get RTC
- Used by LV1 call lv1_get_rtc
- Communication with SYSCON 4
Request:
# write packet
# echo "0: 13 01 0000 0000 8014 00000004 0001 0001 33 00 00 00 0000ff1f" | xxd -c256 -r | \
dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer
# dump packet counter
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
00000000 00 c0 00 c0 |.À.À|
00000004
# increment packet counter
echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer
# kick packet
# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer
Response:
# dump packet counter # dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C 00000000 00 c1 00 c1 |.Á.Á| 00000004 # dump response packet # dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C 00000000 13 01 00 00 00 00 80 14 00 00 00 04 00 08 00 08 |................| 00000010 00 00 00 00 15 af 47 6b |.....¯Gk| 00000018
Ring Buzzer
- Used by System Manager
- Communication with SYSCON 1
Request:
# write packet
# echo "0: 16 01 1620 0000 804d 00000001 0008 0008 20 29 0a 00 000001b6 0000fdcb" | xxd -c256 -r | \
dd of=/dev/ps3sbmmio bs=1 seek=$((0x8d000)) status=noxfer
# dump packet counter
# dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C
00000000 00 c0 00 c0 |.À.À|
00000004
# increment packet counter
echo "0: 00c1 00c1" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8dff0)) status=noxfer
# kick packet
# echo "0: 00000001" | xxd -c256 -r | dd of=/dev/ps3sbmmio bs=1 seek=$((0x8e100)) status=noxfer
# you should hear a beep
Response:
# dump packet counter # dd if=/dev/ps3sbmmio bs=1 count=4 skip=$((0x8dff0)) status=noxfer | hexdump -C 00000000 00 c1 00 c1 |.Á.Á| 00000004 # dump response packet # dd if=/dev/ps3sbmmio bs=1 count=24 skip=$((0x8c000)) status=noxfer | hexdump -C 00000000 16 01 16 20 00 00 80 4d 00 00 00 01 00 01 00 01 |... ...M........| 00000010 00 00 00 00 00 00 fe e3 |......þã| 00000018
Isolation
Crossreference: ps3devwiki::Isolation
Running Isolated SPE Modules On OtherOS++ Linux
- spp_verifier is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux.
- It decrypts default.spp profile
- Tested on 3.41 and 3.55.
- You can modify it easily to run other SPE modules.
root@debian-hdd:/home/glevand/spp_verifier# cat spp_verifier_355.self > /proc/spp_verifier/spu root@debian-hdd:/home/glevand/spp_verifier# cat default_355.spp > /proc/spp_verifier/profile root@debian-hdd:/home/glevand/spp_verifier# echo 1 > /proc/spp_verifier/run root@debian-hdd:/home/glevand/spp_verifier# cat /proc/spp_verifier/debug PPE id (0x0000000000000001) VAS id (0x0000000000000002) lv1_construct_logical_spe (0x00000000) SPE id (0x000000000000002b) lv1_undocumented_function_209 (0x00000000) shadow execution status (0x0000000000000002) lv1_get_spe_interrupt_status(1) (0x00000000) interrupt status 1 (0x0000000000000000) sleep shadow execution status (0x0000000000000002) lv1_get_spe_interrupt_status(1) (0x00000000) interrupt status 1 (0x0000000000000001) ea (0xc000000002920000) esid (0xc000000008000000) vsid (0x0000408f92c94500) lv1_undocumented_function_62 (0x00000000) lv1_clear_spe_interrupt_status(1) (0x00000000) lv1_undocumented_function_168 (0x00000000) sleep shadow execution status (0x0000000000000007) lv1_get_spe_interrupt_status(1) (0x00000000) interrupt status 1 (0x0000000000000000) lv1_get_spe_interrupt_status(2) (0x00000000) interrupt status 2 (0x0000000000000000) out interrupt mbox (0x0000000000000002) out interrupt mbox (0x0000000000000002) lv1_undocumented_function_167 (0x00000000) lv1_clear_spe_interrupt_status (0x00000000) lv1_undocumented_function_200 (0x00000000) sleep shadow execution status (0x000000000000000b) lv1_get_spe_interrupt_status(1) (0x00000000) interrupt status 1 (0x0000000000000000) shadow execution status (0x000000000000000b) problem status (0x01000082) lv1_destruct_logical_spe (0x00000000) root@debian-hdd:/home/glevand/spp_verifier# hexdump -C /proc/spp_verifier/profile | less ... ... 00000200 00 02 00 05 00 00 20 a0 00 00 00 01 00 03 00 00 |...... ........| 00000210 00 00 00 00 00 00 00 01 00 00 00 0e 00 00 00 00 |................| 00000220 00 00 02 88 00 00 00 01 10 70 00 00 01 00 00 01 |.........p......| 00000230 00 00 00 00 00 00 00 00 53 43 45 5f 43 45 4c 4c |........SCE_CELL| 00000240 4f 53 5f 50 4d 45 00 00 00 00 00 00 00 00 00 00 |OS_PME..........| 00000250 00 00 00 00 00 00 00 00 00 00 00 06 00 00 02 50 |...............P| 00000260 10 70 00 00 01 00 00 01 2f 66 6c 68 2f 6f 73 2f |.p....../flh/os/| 00000270 74 68 69 73 5f 69 73 5f 64 75 6d 6d 79 00 00 00 |this_is_dummy...| 00000280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| ... ...
Using metldr On OtherOS++ Linux
- spp_verifier_direct is a kernel module which shows you how to run isolated SPE modules on OtherOS++ Linux by using metldr directly.
- It decrypts default.spp profile.
- Tested on 3.41 and 3.55.
- You can modify it easily to run other SPE modules.
root@debian-hdd:/home/glevand/spp_verifier_direct# insmod ./spp_verifier_direct.ko root@debian-hdd:/home/glevand/spp_verifier_direct# cat metldr > /proc/spp_verifier_direct/metldr root@debian-hdd:/home/glevand/spp_verifier_direct# cat isoldr_355 > /proc/spp_verifier_direct/isoldr root@debian-hdd:/home/glevand/spp_verifier_direct# cat RL_FOR_PROGRAM_355.img > /proc/spp_verifier_direct/rvkprg root@debian-hdd:/home/glevand/spp_verifier_direct# cat EID0 > /proc/spp_verifier_direct/eid0 root@debian-hdd:/home/glevand/spp_verifier_direct# cat spp_verifier_355.self > /proc/spp_verifier_direct/spu root@debian-hdd:/home/glevand/spp_verifier_direct# cat default_355.spp > /proc/spp_verifier_direct/profile root@debian-hdd:/home/glevand/spp_verifier_direct# echo 1 > /proc/spp_verifier_direct/run root@debian-hdd:/home/glevand/spp_verifier_direct# cat /proc/spp_verifier_direct/debug PPE id (0x0000000000000001) VAS id (0x0000000000000002) lv1_construct_logical_spe (0x00000000) SPE id (0x0000000000000033) lv1_enable_logical_spe (0x00000000) lv1_set_spe_interrupt_mask(0) (0x00000000) lv1_set_spe_interrupt_mask(1) (0x00000000) lv1_set_spe_interrupt_mask(2) (0x00000000) lv1_set_spe_privilege_state_area_1_register (0x00000000) ea (0xc000000002680000) esid (0xc000000008000000) vsid (0x0000408f92c94500) lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) sleep lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) out interrupt mbox (0x0000000000000001) lv1_clear_spe_interrupt_status(2) (0x00000000) transferring EID0, ldr args and revoke list to LS waiting until MFC transfers are finished MFC transfers done out mbox (0x00000001) sleep lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) out interrupt mbox (0x0000000000000002) lv1_clear_spe_interrupt_status(2) (0x00000000) out mbox (0x00000002) lv1_clear_spe_interrupt_status(2) (0x00000000) sleep lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) problem status (0x01000082) lv1_destruct_logical_spe (0x00000000) root@debian-hdd:/home/glevand/spp_verifier_direct# hexdump -C /proc/spp_verifier_direct/profile | less ... ... 00000200 00 02 00 05 00 00 20 a0 00 00 00 01 00 03 00 00 |...... ........| 00000210 00 00 00 00 00 00 00 01 00 00 00 0e 00 00 00 00 |................| 00000220 00 00 02 88 00 00 00 01 10 70 00 00 01 00 00 01 |.........p......| 00000230 00 00 00 00 00 00 00 00 53 43 45 5f 43 45 4c 4c |........SCE_CELL| 00000240 4f 53 5f 50 4d 45 00 00 00 00 00 00 00 00 00 00 |OS_PME..........| 00000250 00 00 00 00 00 00 00 00 00 00 00 06 00 00 02 50 |...............P| 00000260 10 70 00 00 01 00 00 01 2f 66 6c 68 2f 6f 73 2f |.p....../flh/os/| 00000270 74 68 69 73 5f 69 73 5f 64 75 6d 6d 79 00 00 00 |this_is_dummy...| 00000280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| ... ...
RSX
Crossreference: ps3devwiki.com::RSX
HV Calls
lv1_gpu_memory_allocate
- LV1 supports 16 memory handles simultaneously.
- LV1 uses a bitmap to manage GPU VRAM.
- The bitmap is located in LV1 memory, 4 double words.
- Each bit corresponds to 1MB VRAM, 256bit = 256MB VRAM.
- 2MB at the top of VRAM are preallocated as you can see below.
<memory handle> = 0x5a5a5a5a xor <memory handle index>
Memory Context Object
offset 0x8 - memory handle (4 bytes)
offset 0x10 - VRAM LPAR start address (8 bytes)
offset 0x18 - VRAM LPAR end address (8 bytes)
Test
- The offset of bitmap could be different on your system because it's allocated dynamically.
- First 9MB of VRAM were allocated by ps3fb Linux driver.
Before allocating VRAM:
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C 00000000 00 00 00 00 00 00 01 ff 00 00 00 00 00 00 00 00 |.......ÿ........| 00000010 00 00 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 |........À.......|
After allocating 32 MB VRAM:
glevand@debian-hdd:~$ sudo dd if=/dev/ps3ram bs=1 count=$((0x20)) skip=$((0x1f85b0)) | hexdump -C 00000000 00 00 01 ff ff ff ff ff 00 00 00 00 00 00 00 00 |...ÿÿÿÿÿ........| 00000010 00 00 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 |........À.......|
lv1_gpu_context_allocate
- LV1 supports at most 3 RSX contexts per LPAR !!! And 16 RSX contexts totally.
- Register %r4 is flags.
- Found the place in LV1 where LV1 sets IO page size for GART memory mapping. We could patch it and set to 4KB. That would make a lot of things easier for RSX developers on Linux.
- 1MB pages make RSX driver for Linux hard to implement because allocating 1Mb contiguous memory chunk on Linux is very very hard especially on a system with only 256MB and which was running for some time.
- LV1 supports 16 contexts simultaneously.
- LV1 has an array of context pointers.
- Each context has an index and a handle. The handle is derived from the index of the context.
<context handle> = 0x55555555 xor <context index>
- Thats why first created context will have handle 0x55555555.
Context Object
offset 0x8 - handle (4 bytes)
offset 0x48 - IO page size, valid range is 4kB, 64KB and 1MB (8 bytes)
Graphic Objects
- Every context has an array of graphic objects which are created by lv1_gpu_context_allocate.
- Graphic objects are stored in VRAM too. Each graphic object occupies 32 bytes in VRAM.
Dump of graphic objects from VRAM:
glevand@debian-hdd:~/ps3rsx_user$ sudo dd if=/dev/ps3rsxmmio bs=1 count=32 skip=$((0x2050000)) status=noxfer | hexdump -C 00000000 00 00 00 30 00 00 00 00 01 00 00 00 00 00 00 00 |...0............| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| # 3D object glevand@debian-hdd:~/ps3rsx_user$ sudo dd if=/dev/ps3rsxmmio bs=1 count=32 skip=$((0x2050020)) status=noxfer | hexdump -C 00000000 00 00 40 97 00 00 00 00 01 00 00 00 00 00 00 00 |..@.............| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| glevand@debian-hdd:~/ps3rsx_user$ sudo dd if=/dev/ps3rsxmmio bs=1 count=32 skip=$((0x2050040)) status=noxfer | hexdump -C 00000000 02 00 00 39 00 00 40 00 01 00 40 0e 00 00 40 0d |...9..@...@...@.| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| glevand@debian-hdd:~/ps3rsx_user$ sudo dd if=/dev/ps3rsxmmio bs=1 count=32 skip=$((0x2050060)) status=noxfer | hexdump -C 00000000 02 00 00 39 00 00 40 00 01 00 40 0d 00 00 40 0e |...9..@...@...@.| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 |................| glevand@debian-hdd:~/ps3rsx_user$ sudo dd if=/dev/ps3rsxmmio bs=1 count=32 skip=$((0x2050080)) status=noxfer | hexdump -C 00000000 00 00 30 9e 00 00 40 00 01 00 00 00 00 00 00 00 |..0...@.........| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| glevand@debian-hdd:~/ps3rsx_user$ sudo dd if=/dev/ps3rsxmmio bs=1 count=32 skip=$((0x20500a0)) status=noxfer | hexdump -C 00000000 00 00 30 62 00 00 40 00 01 00 00 00 00 00 00 00 |..0b..@.........| 00000010 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 |................| glevand@debian-hdd:~/ps3rsx_user$ sudo dd if=/dev/ps3rsxmmio bs=1 count=32 skip=$((0x20500c0)) status=noxfer | hexdump -C 00000000 04 98 30 89 34 00 40 00 01 00 40 0e 00 00 00 00 |..0.4.@...@.....| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| glevand@debian-hdd:~/ps3rsx_user$ sudo dd if=/dev/ps3rsxmmio bs=1 count=32 skip=$((0x20500e0)) status=noxfer | hexdump -C 00000000 04 18 30 8a 34 00 40 00 01 00 00 00 00 00 00 00 |..0.4.@.........| 00000010 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 |................| glevand@debian-hdd:~/ps3rsx_user$ sudo dd if=/dev/ps3rsxmmio bs=1 count=32 skip=$((0x2050100)) status=noxfer | hexdump -C 00000000 ca fe ba be 7f ff ff ff ff fb eb ff ff ff ff ff |ÊþºŸ.ÿÿÿÿûëÿÿÿÿÿ| 00000010 40 00 00 00 00 00 02 00 00 00 00 00 80 00 20 00 |@............. .|
Flags
0x2 - tells LV1 to use 64KB pages for GART memory mapping else LV1 uses 1MB pages (with patched LV1 4KB pages are possible too)
0x4 - create DMA memory objects 0xFEED0003 and 0xFEED0004
0x20 - create DMA memory object 0xBAD68000
0x400 - create 512MB video RAM IO address space, else 256MB video RAM IO address space
0x800 - set IRQ mask to 0x00000000 else it's set to 0xFFFFFFFF which enables all IRQ types
lv1_gpu_context_iomap
- Internally uses lv1_put_iopte function
- IO page size is the one set during lv1_gpu_context_allocate
- IO address space id is 0x0. IO id is 0x1.
lv1_gpu_context_attribute
Attribute 0x1
FIFO Command Buffer Setup
- LV1 checks the currently active channel and if necessary then switches contexts.
- Currently active channel is read from RSX register 0x3204.
- This LV1 call will never return error code. In case of a problem it just panics else it returns always 0.
lv1_gpu_context_attribute(context handle, 0x1, PUT offset, GET offset, REF value, 0x0)
Attribute 0x2
- Pause FIFO processing
lv1_gpu_context_attribute(context handle, 0x2, 0x0, 0x0, 0x0, 0x0)
Attribute 0x3
- Resume FIFO processing
lv1_gpu_context_attribute(context handle, 0x3, 0x0, 0x0, 0x0, 0x0)
Attribute 0x100
- It doesn't do anything, just returns. Dummy.
- Hmm, maybe i should add some nice code here and implement something useful.
Attribute 0x101
Set Flip Mode
lv1_gpu_attribute(0x2, 0x1 /* head */, 0x0, 0x0) lv1_gpu_context_attribute(context handle, 0x101, 0x1 /* head */, sync mode, 0x0, 0x0)
Attribute 0x102
- Flip display buffer
lv1_gpu_context_attribute(context handle, 0x102, head, 0x80000000 | (channel_id << 8) | buffer_id, 0, 0)
Attribute 0x103
- Prepare display buffer flip
lv1_gpu_context_attribute(context handle, 0x103, head, display buffer id, 0x0, 0x0)
Attribute 0x104
* Found a bug in LV1. Setting display buffer returns always LV1_SUCCESS because LV1 discards the return value of function which initialized display buffer.
Snippet from 3.15:
ROM:0021076C li %r31, 0 <----- BUG !!! ROM:00210770 bl rsx_set_display_buffer ROM:00210774 b loc_210518
It should be:
ROM:0021076C bl rsx_set_display_buffer ROM:00210770 mr %r31, %r3 ROM:00210774 b loc_210518
- The bug exists in 3.41 and 3.55 too.
- @SONY: Fix it please although i could do it too, not very hard, maybe i will create a ps3mfw task for this.
Set Display Buffer
lv1_gpu_context_attribute(context handle, 0x104, id, width << 32 | height, pitch << 32 | offset, 0x0)
Attribute 0x105
- Free display buffer which was set with 0x104 attribute
lv1_gpu_context_attribute(context handle, 0x105, buffer_id, 0x0, 0x0, 0x0)
Attribute 0x106
- Enable profiling ???
Attribute 0x108
Set V Blank Frequency
- Modes: 0x2 - the same frequency as VSYNC, 0x3 - 59.94Hz, 0x4- disabled.
lv1_gpu_context_attribute(context handle, 0x108, head, mode, 0x1, 0x0)
Set Second V Frequency
- Modes: 0x2 - the same frequency as VSYNC, 0x3 - 59.94Hz, 0x4- disabled (default)
- After enabling it, i can see second V-sync interrupts disptached by LV1:
ps3rsx_intr:291: interrupt status 0x00000c1b
lv1_gpu_context_attribute(context handle, 0x108, head, mode, 0x2, 0x0)
Attribute 0x109
- It does the same job as attribute 0x3.
Attribute 0x10a
Get Flip Status
- Reads a value at offset 0x10C0 + 0x1 * 0x40 in lpar_driver_info memory.
Reset Flip Status
lv1_gpu_context_attribute(context handle, 0x10a, 0x1 /* id */, 0x7fffffff /* mask */, 0x0 /* value */, 0x0)
- The LV1 call lv1_gpu_context_attribute(0x10a) accesses LPAR memory returned in lpar_driver_info by LV1 call lv1_gpu_context_allocate.
- Offset into lpar_driver_info is 0x10C0 + id * 0x40 = 0x10C0 + 0x1 * 0x40.
- Why not access lpar_driver_info memory directly and use LV1 call instead ???
Attribute 0x10b
- This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.
Initialize Cursor
lv1_gpu_context_attribute(context handle, 0x10b, head, 0x1, 0x0, 0x0)
Set Cursor Image Offset
- You should disable cursor first, then update offset and enable it again.
lv1_gpu_context_attribute(context handle, 0x10b, head, 0x2, offset, 0x0)
Set Cursor Position
- You should disable cursor first, then update its position and enable it again.
lv1_gpu_context_attribute(context handle, 0x10b, head, 0x3, x, y)
???
lv1_gpu_context_attribute(context handle, 0x10b, head, 0x4, ???, 0x0)
Attribute 0x10c
- This attribute is NOT available on 3.15 LV1 e.g. but on 3.41 it's implemented.
Enable Cursor
lv1_gpu_context_attribute(context handle, 0x10c, head, 0x1, 0x0, 0x0)
Disable Cursor
lv1_gpu_context_attribute(context handle, 0x10c, head, 0x2, 0x0, 0x0)
Attribute 0x201
- Enable FIFO access
- LV1 writes 0x1 to address 0x28000400720
lv1_gpu_context_attribute(context handle, 0x201, 0x0, 0x0, 0x0, 0x0)
Attribute 0x202
- Generates RSX exception with cause 0x300.
- It reads 4 byte value from RSX address 0x28000002100. The address can be accessed by mapping it with lv1_gpu_device_map(0xE) too.
lv1_gpu_context_attribute(context handle, 0x202, 0x0, 0x0, 0x0, 0x0)
Attribute 0x300
Set Tile
Set Invalidate Tile
Bind Tile
Unbind Tile
Attribute 0x301
Set Zcull
Bind Zcull
Unbind Zcull
Attribute 0x600 (L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP)
- Used by Linux ps3fb driver to setup frame buffer.
- LV1 executes a FIFO program on behalf of the RSX context. The FIFO program is stored in LV1 memory and is always the same.
- The whole purpouse of this attribute is to hide RSX FIFO mechanism on Linux i think. Why bother implementing it in LV1 if we can do it on Linux by accessing FIFO directly else ???
Here is the FIFO program executed by LV1 during this LV1 call:
0x42000 0x31337303 0x42180 0x66604200 0x82184 0xFEED0001 0xFEED0000 0x44000 0x3137C0DE 0x44180 0x66604200 0x84184 0xFEED0000 0xFEED0001 0x46000 0x313371C3 0x46180 0x66604200 0x46184 0xFEED0000 0x46188 0xFEED0000 0x4A000 0x31337808 0x20A180 0x66604200 0x0 0x0 0x0 0x0 0x0 0x0 0x313371C3 0x8A2FC 0x3 0x4 0x48000 0x31337A73 0x48180 0x66604200 0x48184 0xFEED0000 0x4C000 0x3137AF00 0x4C180 0x66604200 0x0
Attribute 0x601 (L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT)
- Used by Linux ps3fb driver to copy data from frame buffer in system RAM to video RAM.
- Copies data from GART memory to VRAM using block size 1024x1024 bytes.
- LV1 uses internally the FIFO command buffer passed by ps3fb driver with lv1_gpu_context_iomap.
- Rewritten ps3fb driver to use direct FIFO access.
FIFO commands:
0x0004C184 # set source location
0xFEED0001 # GART memory, that's where ps3fb frame buffer is located
0x0004C198
0x313371C3
0x00046300
0x0000000A
h = height
y = 0
bpp = 4
blen = 0x400
while (h)
{
dy = min(h, blen)
w = width
x = 0
while(w)
{
dx = min(w, blen)
dst = dst offset + (y & ~(blen - 1)) * dst pitch + (x & ~(blen - 1)) * bpp
src = src offset + y * src pitch + x * bpp
0x0004630C
<dst>
0x00046304
<dst pitch << 16 | dst pitch>
0x0024C2FC
0x00000001
0x00000003
0x00000003
<(x & (blen - 1)) << 16 | y & (blen - 1)>
<dy << 16 | dx>
<(x & (blen - 1)) << 16 | y & (blen - 1)>
<dy << 16 | dx>
0x00100000
0x00100000
if (dx < 0x10)
tmp = 0x10
else
tmp = (dx + 1) & ~0x1
0x0010C400
<dy << 16 | tmp>
<0x00020000 | src pitch>
<src>
0x00000000
w -= dx
x += dx
}
h -= dy
y += dy
}
0x00040110
0x00000000
Attribute 0x602 (L1GPU_CONTEXT_ATTRIBUTE_FB_WAIT_FOR_IDLE)
- Waits until FIFO command buffer that was initialized by 0x600 attribute is idle.
Attribute 0x603 (L1GPU_CONTEXT_ATTRIBUTE_FB_DEINIT)
- Deinitializes frame buffer that was initialized by 0x600 attribute.
lv1_gpu_attribute
Attribute 0x1
lv1_gpu_attribute(0x1, head, ???, 0x0, 0x0)
Attribute 0x2
- Used for setting flip mode too.
lv1_gpu_attribute(0x2, head, ???, 0x0, 0x0)
Attribute 0x3
- This attribute does the same job as context attribute 0x109.
lv1_gpu_attribute(0x3, head, 0x0, 0x0, 0x0)
Attribute 0x100
- Writes passed 4 bytes to address 0x28000088280 + index * 4.
- Valid values for index: 14-16, 22-24.
- This attribute is NOT allowed on old OtherOS, disabled by LPAR1 syscall 0x10088.
lv1_gpu_attribute(0x100, index, value, 0x0, 0x0)
Attribute 0x105
lv1_gpu_attribute(0x105, 0x0, 0x0, 0x0, 0x0)
Attribute 0x202
lv1_gpu_attribute(0x202, head, ???, 0x0, 0x0)
Attribute 0x203
lv1_gpu_attribute(0x203, 0x0, 0x0, 0x0, 0x0)
Attribute 0x204
- Valid param range: 0 - 1.
- Clears bit 0 either at address 0x28000680404 or 0x28000682404.
- Some CRTC function.
lv1_gpu_attribute(0x204, param, 0x0, 0x0, 0x0)
Attribute 0x400
- Stores 4 byte value (param2) at address 0x28000001234 + param1 << 2.
- Valid range for param1: 0 - 2.
lv1_gpu_attribute(0x400, param1, param2, 0x0, 0x0)
Attribute 0x401
- Reads 4 byte value at address 0x28000001234 + param << 2.
- Valid range for param: 0 - 2.
lv1_gpu_attribute(0x401, param, 0x0, 0x0, 0x0)
Attribute 0x402
- Stores 4 byte value (param) at address 0x28000001284.
lv1_gpu_attribute(0x402, param, 0x0, 0x0, 0x0)
Attribute 0x403
- Reads 4 byte value at address 0x28000001284.
lv1_gpu_attribute(0x403, 0x0, 0x0, 0x0, 0x0)
Attribute 0x500
- Changes bit 23 of 4 byte value at address 0x28000001284.
lv1_gpu_attribute(0x500, param, 0x0, 0x0, 0x0)
Attribute 0x501
- Changes bit 27 of 4 byte value at address 0x28000000200.
lv1_gpu_attribute(0x501, param, 0x0, 0x0, 0x0)
lv1_gpu_device_map
Device Table
| Device ID | Physical Start Address | Size | Description |
|---|---|---|---|
| 0x0 | 0x28000000000 | 0x2000000 | This region can be mapped if RSX debugging is enabled and LPAR1 syscall 0x10088 was not used previously. |
| 0x4 | 0x28001800000 | 0x200000 | - |
| 0x8 | 0x2808FF10000 | 0x1000 | Video RAM. This region of video RAM is shared by all RSX contexts. |
| 0x9 | 0x28000400000 | 0x8000 | - |
| 0xA | 0x28000100000 | 0x1000 | - |
| 0xB | 0x2800000A000 | 0x1000 | - |
| 0xC | 0x28000680000 | 0x3000 | - |
| 0xD | 0x28000090000 | 0x1000 | FIFO and FIFO cache. Useful for debugging e.g. to see what commands were processed last by RSX. |
| 0xE | 0x28000002000 | 0x2000 | - |
| 0xF | 0x28000088000 | 0x1000 | - |
Device 0x8
- This region of video RAM is shared by all RSX contexts.
- This region of video RAM can be accessed from FIFO by using DMA objects 0x56616660 (read-write) and 0x56616661 (read-only).
- RSX contexts use this shared memory region to synchronize with each other on GameOS. For example, before a RSX context on GameOS starts executing FIFO commands, it acquires the semaphore (or label) at offset 0x30 (index 0x3) in order to prevent the interferences from other RSX contexts which run in parallel. I think that's why i have problems on Linux with several RSX contexts simultaneously, because ps3fb driver doesn't do this. Again huge "thanks" to SONY for this.
lv1_undocumented_function_222
- Just disables and enables again the RSX hardware master interrupt.
lv1_undocumented_function_230
- This LV1 call accepts a single parameter. Valid range: 1 - 3.
- It's enabled ONLY on GameOS LPAR2 but NOT on old OtherOS LPAR2. LPAR1 syscall 0x10088 disables this LV1 call.
Parameter 0x1
Parameter 0x2
Parameter 0x3
- Returns memory and core frequencies and value of RSX register 0x280000001540.
Video RAM
- 0x28080000000 is the physical address where RSX video RAM is mapped. You can access the whole video RAM without restrictions on Linux with ps3rsxmmio driver e.g. That means full access to RAMIN and RAMHT memory without video RAM blitting like ps2dev guys did it.
- 0x700190000000 is the LPAR address of RSX video RAM.
RAMHT
- Physical address is 0x28002010000. It's NOT in video RAM like ps2dev guys claimed.
- Each entry is 8 bytes.
RAMHT Entry
offset 0x0 - object handle (4 bytes) offset 0x4 - (channel id << 23) | (engine type << 20) | (object offset >> 4) (4 bytes) engine types: 0x0 - software, 0x1 - graphics
Example
cafebabe 00805022 0xcafebabe - object handle 0x05022 << 4 = 0x050220 - object offset 0x008 >> 3 = 0x1 - channel id
Dump of RAMHT
# dd if=/dev/ps3rsxmmio bs=1 count=$((0x4000)) skip=$((0x2010000)) | hexdump -C 00000000 00 00 00 00 00 10 50 00 00 00 00 00 00 00 00 00 |......P.........| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000400 00 00 00 00 00 90 50 12 00 00 00 00 00 00 00 00 |......P.........| 00000410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000005d0 00 00 00 00 00 00 00 00 00 00 00 00 7f 90 00 00 |................| 000005e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000980 00 00 00 00 00 00 00 00 31 37 af 00 00 10 50 0c |........17¯...P.| 00000990 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000d80 00 00 00 00 00 00 00 00 31 37 af 00 00 90 50 1e |........17¯...P.| 00000d90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000018a0 56 61 66 61 00 10 40 0b 56 61 66 60 00 10 40 0a |Vafa..@.Vaf`..@.| 000018b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000019a0 00 00 00 00 00 00 00 00 66 62 66 60 00 90 40 23 |........fbf`..@#| 000019b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00001aa0 66 61 66 61 00 90 40 20 00 00 00 00 00 00 00 00 |fafa..@ ........| 00001ab0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00001ba0 00 00 00 00 00 00 00 00 66 60 66 60 00 90 40 1f |........f`f`..@.| 00001bb0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00001ca0 56 61 66 61 00 90 40 22 56 61 66 60 00 90 40 21 |Vafa..@"Vaf`..@!| 00001cb0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00001da0 00 00 00 00 00 00 00 00 66 62 66 60 00 10 40 0c |........fbf`..@.| 00001db0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00001ea0 66 61 66 61 00 10 40 09 00 00 00 00 00 00 00 00 |fafa..@.........| 00001eb0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00001fa0 00 00 00 00 00 00 00 00 66 60 66 60 00 10 40 08 |........f`f`..@.| 00001fb0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000022c0 31 33 7a 73 00 90 50 1a 00 00 00 00 00 00 00 00 |13zs..P.........| 000022d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000026c0 31 33 7a 73 00 10 50 08 00 00 00 00 00 00 00 00 |13zs..P.........| 000026d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00002940 00 00 00 00 00 00 00 00 31 33 73 03 00 90 50 16 |........13s...P.| 00002950 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00002d40 00 00 00 00 00 00 00 00 31 33 73 03 00 10 50 04 |........13s...P.| 00002d50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00003110 00 00 00 00 00 00 00 00 31 33 78 08 00 90 50 20 |........13x...P | 00003120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00003150 31 33 70 00 00 90 50 14 00 00 00 00 00 00 00 00 |13p...P.........| 00003160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00003210 ca fe ba be 00 00 50 10 00 00 00 00 00 00 00 00 |ÊþºŸ..P.........| <-- The famous 0xCAFEBABE sw object of context 0 00003220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000032d0 fe ed 00 01 00 10 40 0e fe ed 00 00 00 10 40 0d |þí....@.þí....@.| 000032e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00003310 31 37 c0 de 00 90 50 18 00 00 00 00 00 00 00 00 |17ÀÞ..P.........| 00003320 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00003510 00 00 00 00 00 00 00 00 31 33 78 08 00 10 50 0e |........13x...P.| 00003520 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00003550 31 33 70 00 00 10 50 02 00 00 00 00 00 00 00 00 |13p...P.........| 00003560 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000035d0 00 00 00 00 7f 90 00 00 00 00 00 00 00 00 00 00 |................| * 000035f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00003610 ca fe ba be 00 80 50 22 00 00 00 00 00 00 00 00 |ÊþºŸ..P"........| <-- The famous 0xCAFEBABE sw object of context 1 00003620 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000036c0 00 00 00 00 7f 90 00 00 00 00 00 00 00 00 00 00 |................| 000036d0 fe ed 00 01 00 90 40 25 fe ed 00 00 00 90 40 24 |þí....@%þí....@$| 000036e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000036f0 00 00 00 00 00 00 00 00 00 00 00 00 7f 90 00 00 |................| 00003700 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00003710 31 37 c0 de 00 10 50 06 00 00 00 00 00 00 00 00 |17ÀÞ..P.........| 00003720 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00003880 66 60 42 01 00 90 40 18 66 60 42 00 00 90 40 17 |f`B...@.f`B...@.| 00003890 66 60 42 03 00 90 40 1a 66 60 42 02 00 90 40 19 |f`B...@.f`B...@.| 000038a0 66 60 42 05 00 90 40 1c 66 60 42 04 00 90 40 1b |f`B...@.f`B...@.| 000038b0 66 60 42 07 00 90 40 1e 66 60 42 06 00 90 40 1d |f`B...@.f`B...@.| 000038c0 66 60 42 09 00 90 40 2c 66 60 42 08 00 90 40 2d |f`B...@,f`B...@-| 000038d0 66 60 42 0b 00 90 40 2a 66 60 42 0a 00 90 40 2b |f`B...@*f`B...@+| 000038e0 66 60 42 0d 00 90 40 28 66 60 42 0c 00 90 40 29 |f`B...@(f`B...@)| 000038f0 66 60 42 0f 00 90 40 26 66 60 42 0e 00 90 40 27 |f`B...@&f`B...@'| 00003900 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00003b40 00 00 00 00 00 00 00 00 31 33 71 c3 00 10 50 0a |........13qÃ..P.| 00003b50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00003c80 66 60 42 01 00 10 40 01 66 60 42 00 00 10 40 00 |f`B...@.f`B...@.| 00003c90 66 60 42 03 00 10 40 03 66 60 42 02 00 10 40 02 |f`B...@.f`B...@.| 00003ca0 66 60 42 05 00 10 40 05 66 60 42 04 00 10 40 04 |f`B...@.f`B...@.| 00003cb0 66 60 42 07 00 10 40 07 66 60 42 06 00 10 40 06 |f`B...@.f`B...@.| 00003cc0 66 60 42 09 00 10 40 15 66 60 42 08 00 10 40 16 |f`B...@.f`B...@.| 00003cd0 66 60 42 0b 00 10 40 13 66 60 42 0a 00 10 40 14 |f`B...@.f`B...@.| 00003ce0 66 60 42 0d 00 10 40 11 66 60 42 0c 00 10 40 12 |f`B...@.f`B...@.| 00003cf0 66 60 42 0f 00 10 40 0f 66 60 42 0e 00 10 40 10 |f`B...@.f`B...@.| 00003d00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00003f40 00 00 00 00 00 00 00 00 31 33 71 c3 00 90 50 1c |........13qÃ..P.| 00003f50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00004000
RAMIN
- Physical address is 0x28002050000. It's NOT in video RAM like ps2dev guys claimed.
- Total size is 0x1000 bytes. Each entry is 0x20 bytes.
Dump of RAMIN
# dd if=/dev/ps3rsxmmio bs=1 count=$((0x1000)) skip=$((0x2050000)) | hexdump -C 00000000 00 00 00 30 00 00 00 00 01 00 00 00 00 00 00 00 |...0............| 00000010 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |....ÿÿÿÿÿÿÿÿÿÿÿÿ| 00000020 00 00 40 97 00 00 00 00 01 00 00 00 00 00 00 00 |..@.............| 00000030 00 00 00 00 7f ff ff ff ff ff ff ff ff ef ff ff |.....ÿÿÿÿÿÿÿÿïÿÿ| 00000040 02 00 00 39 00 00 40 00 01 00 40 0e 00 00 40 0d |...9..@...@...@.| 00000050 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |....ÿÿÿÿÿÿÿÿÿÿÿÿ| 00000060 02 00 00 39 00 00 40 00 01 00 40 0d 00 00 40 0e |...9..@...@...@.| 00000070 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |....ÿÿÿÿÿÿÿÿÿÿÿÿ| 00000080 00 00 30 9e 00 00 40 00 01 00 00 00 00 00 00 00 |..0...@.........| 00000090 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |....ÿÿÿÿÿÿÿÿÿÿÿÿ| 000000a0 00 00 30 62 00 00 40 00 01 00 00 00 00 00 00 00 |..0b..@.........| 000000b0 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |....ÿÿÿÿÿÿÿÿÿÿÿÿ| 000000c0 04 98 30 89 34 00 40 00 01 00 40 0e 00 00 00 00 |..0.4.@...@.....| 000000d0 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |....ÿÿÿÿÿÿÿÿÿÿÿÿ| 000000e0 04 18 30 8a 34 00 40 00 01 00 00 00 00 00 00 00 |..0.4.@.........| 000000f0 00 00 00 00 ff ff ff ff fd ff fb 7f ff ff ff ff |....ÿÿÿÿýÿû.ÿÿÿÿ| 00000100 ca fe ba be ff ff ff ff ff ff ff ff ff ff ff ff |ÊþºŸÿÿÿÿÿÿÿÿÿÿÿÿ| 00000110 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ| 00000120 00 00 00 30 00 00 00 00 01 00 00 00 00 00 00 00 |...0............| 00000130 00 00 00 00 ff ff ff ff fd ff ff ff f3 ff ff ef |....ÿÿÿÿýÿÿÿóÿÿï| 00000140 00 00 40 97 00 00 00 00 01 00 00 00 00 00 00 00 |..@.............| 00000150 00 00 00 00 fe ff fe ff ff ff ff 7f ff ff ff ff |....þÿþÿÿÿÿ.ÿÿÿÿ| 00000160 02 00 00 39 00 00 00 00 01 00 00 00 00 00 00 00 |...9............| 00000170 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |....ÿÿÿÿÿÿÿÿÿÿÿÿ| 00000180 02 00 00 39 00 00 00 00 01 00 00 00 00 00 00 00 |...9............| 00000190 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff df |....ÿÿÿÿÿÿÿÿÿÿÿß| 000001a0 00 00 30 9e 00 00 00 00 01 00 00 00 00 00 00 00 |..0.............| 000001b0 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |....ÿÿÿÿÿÿÿÿÿÿÿÿ| 000001c0 00 00 30 62 00 00 00 00 01 00 00 00 00 00 00 00 |..0b............| 000001d0 00 00 00 00 ff ff ff ff ff ff fe ff f7 ff ff ff |....ÿÿÿÿÿÿþÿ÷ÿÿÿ| 000001e0 00 00 30 89 18 00 00 00 01 00 00 00 00 00 00 00 |..0.............| 000001f0 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |....ÿÿÿÿÿÿÿÿÿÿÿÿ| 00000200 00 00 30 8a 18 00 00 00 01 00 00 00 00 00 00 00 |..0.............| 00000210 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |....ÿÿÿÿÿÿÿÿÿÿÿÿ| 00000220 ca fe ba be ff ff ff ff ff ff ff ff ff ff ff ff |ÊþºŸÿÿÿÿÿÿÿÿÿÿÿÿ| 00000230 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ| *
lpar_dma_control
- LPAR memory region mapped to RSX channel control register memory region.
- Physical address is 0x28000C00000 + (channel_id << 12).
- PUT, GET and REF registers start at offset 0x40.
Example:
# RSX channel 0 # sudo dd if=/dev/ps3rsxmmio bs=1 count=$((0x1000)) skip=$((0xC00000)) | hexdump -C 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000040 0d 00 00 b8 0d 00 00 b8 00 00 00 00 00 00 00 00 |...ž...ž........| 00000050 00 00 00 00 0d 00 00 b8 00 00 00 00 00 00 00 00 |.......ž........| 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| # RSX channel 1 # sudo dd if=/dev/ps3rsxmmio bs=1 count=$((0x1000)) skip=$((0xC01000)) | hexdump -C 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000040 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................| 00000050 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................| 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
lpar_driver_info
- A 16KB block of system memory for every context allocated by LV1.
- LV1 initializes, fills and updates it with current flip status after a RSX interrupt e.g.
- When a graphics exception occurs then LV1 stores lots of information here like the cause of the exception and register values.
lpar_reports
Semaphore Area
- It begins at offset 0x0 of lpar_reports.
Flipping
- Just before LV1 notifies all RSX contexts about flip interrupt, it resets semaphores at offset 0x0 (for head 0) or at offset 0x10 (for head 1) to value 0x0 in every lpar_reports of each allocated context. This way RSX can wait on the semaphores and synchronize with flipping.
Notify Area
- It begins at offset 0x1000 of lpar_reports.
Display Buffer
- RSX supports 8 display buffers per context (0-7)
- LV1 creates DMA objects in VRAM for every created display buffer
Calculating DMA Object Handle
const uin32_t disp_buf_id_to_handle[] = {
0xDAC10000,
0xDAC20000,
0xDAC30000,
0xDAC03000,
0xDAC04000,
0xDAC50000,
0xDAC06000,
0xDAC70000,
};
<display buffer handle> = disp_buf_id_to_handle[buffer_id] | (buffer_id << 4) | <channel_id>
Graphics Objects
0×31337000
- 3D object
- LPAR1 syscall 0x10088 once called disables 3D object !!!. It's used by LPAR1 system manager during LPAR2 boot and called ONLY for old OtherOS LPAR2. That's how 3D is disabled for old OtherOS.
0x66604200-0x66604208
- Notification DMA context objects.
- They target memory in lpar_reports region starting at offset 0x1000.
- Every DMA context object targets 64 byte memory chunk.
- Used e.g. in cellGcmSetNotify and cellGcmSetNotifyIndex.
0x66626660
- Reports DMA context object.
- It targets memory in lpar_reports region starting at offset 0x1400 and of size 0x8000 bytes.
0x66606660
- Semaphores (labels) DMA context object.
- It targets memory in lpar_reports region starting at offset 0x0 and of size 0x1000 bytes. Read-write access.
0x66616661
- Semaphores (labels) DMA context object.
- It targets memory in lpar_reports region starting at offset 0x0 and of size 0x1000 bytes. Read-only access.
0x56616660
- DMA context object for video RAM mapped with lv1_gpu_device_map(8).
- It targets memory starting at offset 0x0 and of size 0x1000 bytes. Read-write access.
0x56616661
- DMA context object for video RAM mapped with lv1_gpu_device_map(8).
- It targets memory starting at offset 0x0 and of size 0x1000 bytes. Read-only access.
0xFEED0000
- DMA context object for video RAM allocated with lv1_gpu_memory_allocate.
0xFEED0001
- DMA context object for GART memory mapped with lv1_gpu_context_iomap.
0xCAFE000F
- Hmm, very interesting, found it in GameOS VSH.
- Some kind of texture memory context, maybe overlay ???
- But i couldn't find it in LV1, odd.
- Used by VSH to make screenshots.
0xCAFEBABE
- Handle is 0xCAFEBABE.
- Software class graphics object.
- Created by LV1 for every allocated context.
- LV1 has place ONLY for 3 instances of software class objects. If a 4th software class object is tried to be allocated then LV1 panics. That means ONLY 3 RSX contexts can be created simultaneously by LV1 where RSX can handle 8 contexts simultaneously. Another obstacle on the road to Linux RSX driver. "Thanks" to SONY.
- Method processing is done NOT by RSX but by LV1, LV1 catches all RSX interrupts for this object and handles all methods of this object.
- For example, with FIFO command buffer and 0xCAFEBABE object display buffer flips can be done with FIFO commands, without LV1 calls.
- This graphics object is used extensively by libgcm for buffer flipping. libgcm binds it to subchannel 7.
- Currently i'm having problems with this object on Linux and the 2nd RSX context. I'm not able to bind it to the 7th subchannel of my 2nd RSX context. Trying to figure out why it doesn't work. Maybe the problem is that LV1 cannot handle 2 RSX contexts properly. If i try to bind it then FIFO command processing stops.
- Finally, solved the issue with 0xCAFEBABE object. The problem was ps3fb driver which uses FB LV1 calls to blit image from GART memory to VRAM. I have rewritten it to use direct FIFO access instead and now it works. I can run several RSX contexts on Linux simultaneously now and flip display with 0xCAFEBABE object.
Here is the proof of working 0xCAFEBABE object on Linux (FW 3.55):
ROM:0035AD20 .quad 0x3AFFB0 # 0 ROM:0035AD20 .quad 0x1E8B60 # 1 ROM:0035AD20 .quad 0 # 2 ROM:0035AD38 .quad 0 # 0 ROM:0035AD38 .quad 0 # 1 ROM:0035AD38 .quad 0 # 2 ROM:0035AD38 .quad 0 # 3 ROM:0035AD38 .quad 0 # 4 ROM:0035AD38 .quad 0 # 5 ROM:0035AD38 .quad 0 # 6 ROM:0035AD38 .quad 0 # 7 ROM:0035AD78 .quad 0 # 0 ROM:0035AD78 .quad 0 # 1 ROM:0035AD78 .quad 0 # 2 ROM:0035AD78 .quad 0 # 3 ROM:0035AD78 .quad 0 # 4 ROM:0035AD78 .quad 0 # 5 ROM:0035AD78 .quad 0 # 6 ROM:0035AD78 .quad 0x3AFFB0 # 7 <-------- 0xCAFEBABE object bound to subchannel 7 of context 1 ROM:0035ADB8 .quad 0 # 0 ROM:0035ADB8 .quad 0 # 1 ROM:0035ADB8 .quad 0 # 2 ROM:0035ADB8 .quad 0 # 3 ROM:0035ADB8 .quad 0 # 4 ROM:0035ADB8 .quad 0 # 5 ROM:0035ADB8 .quad 0 # 6 ROM:0035ADB8 .quad 0 # 7 ROM:003AFFB0 .long 0 ROM:003AFFB4 .long 0xCAFEBABE ROM:003AFFB8 .quad 0x324970 ROM:003AFFC0 .quad 0x28002050100
- Another odd thing (or maybe another LV1 bug that i found) is that LV1 creates a 0xCAFEBABE object for EVERY RSX context. Every RSX context has its own 0xCAFEBABE object but LV1 binds always ONLY the one from the first allocated RSX contexts. If you take a look at the picture above then you will see an array of size 3 with 2 pointers
ROM:0035AD20 .quad 0x3AFFB0 # 0 ROM:0035AD20 .quad 0x1E8B60 # 1 ROM:0035AD20 .quad 0 # 2
- These pointers point to 0xCAFEBABE objects. The first one points to the 0xCAFEBABE object of the first RSX context (created by ps3fb).
- The second one points to the 0xCAFEBABE object of the second RSX context (created by my ps3rsx kernel driver).
- And if you take a look at this now
ROM:0035AD78 .quad 0 # 6 ROM:0035AD78 .quad 0x3AFFB0 # 7 <-------- 0xCAFEBABE object bound to subchannel 7 of context 1 ROM:0035ADB8 .quad 0 # 0
- Then you will see that RSX context 1 created by ps3rsx driver actually uses the 0xCAFEBABE object from RSX context 0 which was created by ps3fb. It doesn't really matter which one is used because it works anyways but why then does LV1 waste precious RAM and allocates more than one 0xCAFEBABE object. It doesn't make sense to me. Another odd BUG in LV1.
Methods
0x920
- Flip HEAD 0
0x924
- Flip HEAD 1
0x940 + head << 2
- Prepare flip display buffer for specified head
0xB00
- Generate user interrupt
0xB04
- Generate user interrupt
Interrupt Handling
- Every RSX context has its own IRQ outlet.
- After LV1 received a RSX interrupt, it dispatches it to all RSX contexts.
IRQ Bits
(1 << 11) second v-sync head 1 (1 << 10) second v-sync head 0 (1 << 8) ??? - LV1 dispatches this interrupt too but for what reason no clue (1 << 7) user (0xcafebabe sw object methods 0xb00 and 0xb04) (1 << 6) queue head 1 (RSX read flip command for head 1) (1 << 5) queue head 0 (RSX read flip command for head 0) (1 << 4) flip head 1 (head 1 was flipped) (1 << 3) flip head 0 (head 0 was flipped) (1 << 2) graphics exception (1 << 1) v-sync head 1 (1 << 0) v-sync head 0
Graphics Exception
- And finally, after rewriting ps3fb driver, graphics exception can be catched on Linux too.
ps3fb ioc0_01: ps3fb_vsync_interrupt: graphics exception ps3rsx_intr:291: interrupt status 0x00000004
- I can catch it on Linux but still having trouble with recovering from it. ps3fb FIFO stops working after a graphics exception.
Cause
- Cause is stored by LV1 in lpar_driver_info memory region at offset 0x12F4.
| ID | Description |
|---|---|
| 0x1 | FIFO parser error. Unsupported or invalid command was encountered by FIFO. |
| 0x3 | Flip preparation error. |
| 0x102 | RSX tried to fetch vertices from invalid address. |
| 0x103 | VRAM texture fetch protection fault. |
| 0x104 | GART memory texture fetch protection fault. |
| 0x105 | Invalid graphics state. Illegal combination of graphics settings. |
| 0x106 | Invalid graphics command data. Illegal parameters. |
| 0x107 | Invalid DMA write address. |
| 0x108 | Invalid DMA read address. |
| 0x109 | Undefined graphics error. |
Exception Information
- The exception information is stored in lpar_driver_info by LV1 before dispatching the exception to RSX contexts.
Fields
| Offset (in lpar_driver_info) | Size (bytes) | Description |
|---|---|---|
| 0x12F0 | 4 | Channel ID of the RSX context which caused the exception. |
| 0x12F4 | 4 | Exception cause. |
| 0x12F8 | 4 | ??? |
| 0x12FC | 4 | ??? |
| 0x1318 | 4 | DMA PUT. |
| 0x131C | 4 | DMA GET. |
| 0x132C | 4 | FIFO PUT. |
| 0x1330 | 4 | FIFO GET. |
Example
- I extended ps3fb driver to handle graphics exception and here is an example output.
- I tried to execute RET from subroutine without executing a CALL previously.
- RSX dispatched a FIFO parser exception.
- ps3fb uses RSX context channel 0 but the exception is caused by RSX context channel 1. It's my ps3rsx driver which runs simultaneously with ps3fb on Linux. As you see Linux can handle several RSX contexts simultaneously now and the exception is dispatched to all active RSX contexts, not only the one which caused the exception.
ps3fb ioc0_01: ps3fb_vsync_interrupt: graphics exception ps3fb ioc0_01: channel id 0x00000001 cause 0x00000001 ps3fb ioc0_01: fifo: ps3fb ioc0_01: call 0x00002644 jump 0x00002644 ps3fb ioc0_01: get 0x000000aa put 0x0000018e ps3fb ioc0_01: [000] 00000060:56616661 00000064:00000030 0000006c:00000001 00000060:66616661 ps3fb ioc0_01: [004] 0000019c:feed0000 000001a0:feed0001 000001a4:66606660 000001a8:66626660 ps3fb ioc0_01: [008] 0000021c:00000040 00000220:00000001 00000224:00000080 00000228:00000100 ps3fb ioc0_01: [00c] 000002cc:0fff0000 000002d0:0fff0000 000002d4:0fff0000 000002d8:0fff0000 ps3fb ioc0_01: [010] 000003b0:00100000 00001454:00000000 00001ff4:003fffff 00001fc0:00000000 ps3fb ioc0_01: [014] 00000b5c:00000000 00000b60:00000000 00000b64:00000000 00000a0c:00000000 ps3fb ioc0_01: [018] 00000b00:00002dc8 00000b04:00002dc8 00000b08:00002dc8 00000b0c:00002dc8 ps3fb ioc0_01: [01c] 000008cc:00000800 000008d0:00000000 000008d4:00000000 000008d8:00000000 ps3fb ioc0_01: [020] 000003e0:00010101 000003e4:00010101 000003e8:00010101 000003ec:00010101 ps3fb ioc0_01: [024] 00000420:00007421 00000424:00007421 00000428:00007421 0000042c:00007421 ps3fb ioc0_01: [028] 00000460:56676654 00000464:33333345 00000468:54333333 0000046c:45667665 ps3fb ioc0_01: [02c] 00000358:000000ff 0000034c:000000ff 0000035c:00001e00 00000360:00001e00 ps3fb ioc0_01: [030] 0000183c:00000000 00001830:00000405 00000384:00000000 00000388:3f800000 ps3fb ioc0_01: [034] 00000a68:00000000 00000a78:00000000 00000a7c:00000000 00001dac:00000000 ps3fb ioc0_01: [038] 00001a08:00030101 00001a1c:00000000 00001a0c:00060000 00001a14:02052000 ps3fb ioc0_01: [03c] 00001a88:00030101 00001a9c:00000000 00001a8c:00060000 00001a94:02052000 ps3fb ioc0_01: [040] 00001b08:00030101 00001b1c:00000000 00001b0c:00060000 00001b14:02052000 ps3fb ioc0_01: [044] 00001b88:00030101 00001b9c:00000000 00001b8c:00060000 00001b94:02052000 ps3fb ioc0_01: [048] 00000348:00000000 00001740:00000002 00001680:00000000 00001744:00000002 ps3fb ioc0_01: [04c] 0000169c:00000000 00001760:00000002 000016a0:00000000 00001764:00000002 ps3fb ioc0_01: [050] 000016bc:00000000 00000a00:10000000 00000a04:10000000 00000394:00000000 ps3fb ioc0_01: [054] 00000a2c:00000000 00000a30:45000000 00000a34:45000000 00000a38:3f000000 ps3fb ioc0_01: [058] 00001e98:01000000 00001478:00000000 00001ff0:0000ffff 000017cc:00000000 ps3fb ioc0_01: [05c] 00000968:00000101 0000097c:00000000 0000096c:00060000 00000974:00000000 ps3fb ioc0_01: [060] 0000a184:00000000 0000a188:00000000 0000a18c:00000000 0000a190:00000000 ps3fb ioc0_01: [064] 0000c31c:00100000 0000c400:03c40298 0000c404:00021a80 0000c408:0d011000 ps3fb ioc0_01: [068] 0000c314:03c40400 0000c318:00100000 0000c31c:00100000 0000c400:03c40400 ps3fb ioc0_01: [06c] 0000c318:00100000 0000c31c:00100000 0000c400:03c40298 0000c404:00021a80 ps3fb ioc0_01: [070] 0000c310:00000000 0000c314:03c40400 0000c318:00100000 0000c31c:00100000 ps3fb ioc0_01: [074] 0000c314:03c40298 0000c318:00100000 0000c31c:00100000 0000c400:03c40298 ps3fb ioc0_01: [078] 0000c30c:03c40400 0000c310:00000000 0000c314:03c40400 0000c318:00100000 ps3fb ioc0_01: [07c] 0000c310:00000000 0000c314:03c40298 0000c318:00100000 0000c31c:00100000 ps3fb ioc0_01: [080] 56616661:00000000 00000030:66604200 00000001:00000000 66616661:00000000 ps3fb ioc0_01: [084] feed0000:00900000 feed0001:00000000 66606660:00000000 66626660:00000000 ps3fb ioc0_01: [088] 00000040:01ffffc0 00000001:00000000 00000080:00000000 00000100:00000000 ps3fb ioc0_01: [08c] 0fff0000:00000000 0fff0000:46400000 0fff0000:ffff0000 0fff0000:00000008 ps3fb ioc0_01: [090] 00100000:00000000 00000000:005aaae4 003fffff:00100008 00000000:01012000 ps3fb ioc0_01: [094] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [098] 00002dc8:00000000 00002dc8:00000000 00002dc8:00000000 00002dc8:00000000 ps3fb ioc0_01: [09c] 00000800:98766666 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [0a0] 00010101:00000000 00010101:00000111 00010101:00000f11 00010101:89aabaa9 ps3fb ioc0_01: [0a4] 00007421:00018488 00007421:00428a02 00007421:00080008 00007421:00000000 ps3fb ioc0_01: G [0a8] 56676654:005aaae4 33333345:00100008 54333333:01012000 45667665:00018488 ps3fb ioc0_01: [0ac] 000000ff:00000000 000000ff:00000000 00001e00:00000000 00001e00:005aaae4 ps3fb ioc0_01: [0b0] 00000000:00000000 00000405:00000000 00000000:00000000 3f800000:00000000 ps3fb ioc0_01: [0b4] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [0b8] 00030101:00000111 00000000:00000f11 00060000:56676654 02052000:00000000 ps3fb ioc0_01: [0bc] 00030101:00428a02 00000000:00080008 00060000:00000000 02052000:00000111 ps3fb ioc0_01: [0c0] 00030101:00100008 00000000:01012000 00060000:00018488 02052000:00428a02 ps3fb ioc0_01: [0c4] 00030101:00000000 00000000:00000000 00060000:005aaae4 02052000:00100008 ps3fb ioc0_01: [0c8] 00000000:00000000 00000002:00000000 00000000:edcba987 00000002:00000000 ps3fb ioc0_01: [0cc] 00000000:00000000 00000002:00000000 00000000:00000000 00000002:00000000 ps3fb ioc0_01: [0d0] 00000000:00000f11 10000000:66667899 10000000:00000000 00000000:00000000 ps3fb ioc0_01: [0d4] 00000000:00080008 45000000:00000000 45000000:00000111 3f000000:00000f11 ps3fb ioc0_01: [0d8] 01000000:01012000 00000000:00018488 0000ffff:00428a02 00000000:00080008 ps3fb ioc0_01: [0dc] 00000101:00000000 00000000:00000000 00060000:00000000 00000000:00000000 ps3fb ioc0_01: [0e0] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [0e4] 00100000:00000000 03c40298:00000000 00021a80:00000000 0d011000:00000001 ps3fb ioc0_01: [0e8] 03c40400:00000000 00100000:40100000 00100000:00000000 03c40400:00000000 ps3fb ioc0_01: [0ec] 00100000:00000000 00100000:00000000 03c40298:00000000 00021a80:00000000 ps3fb ioc0_01: [0f0] 00000000:00000000 03c40400:00000000 00100000:00000000 00100000:00000000 ps3fb ioc0_01: [0f4] 03c40298:ffffffff 00100000:00000000 00100000:00000000 03c40298:00000000 ps3fb ioc0_01: [0f8] 03c40400:00000000 00000000:00000000 03c40400:00000000 00100000:00000000 ps3fb ioc0_01: [0fc] 00000000:00000000 03c40298:00000000 00100000:0001bc80 00100000:00000202 ps3fb ioc0_01: [100] 00000000:00000000 66604200:00000008 00000000:00000000 00000000:00080008 ps3fb ioc0_01: [104] 00900000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [108] 01ffffc0:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [10c] 00000000:00000000 46400000:00000000 ffff0000:00000000 00000008:00000000 ps3fb ioc0_01: [110] 00000000:00000000 005aaae4:00000000 00100008:00000000 01012000:00000000 ps3fb ioc0_01: [114] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [118] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [11c] 98766666:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [120] 00000000:00000000 00000111:00000000 00000f11:00000000 89aabaa9:00000000 ps3fb ioc0_01: [124] 00018488:00000000 00428a02:00000000 00080008:00000000 00000000:00000000 ps3fb ioc0_01: [128] 005aaae4:00000000 00100008:00000000 01012000:00000000 00018488:00000000 ps3fb ioc0_01: [12c] 00000000:00000000 00000000:00000000 00000000:00000000 005aaae4:00000000 ps3fb ioc0_01: [130] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [134] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [138] 00000111:00000000 00000f11:00000000 56676654:00000000 00000000:00000000 ps3fb ioc0_01: [13c] 00428a02:00000000 00080008:00000000 00000000:00000000 00000111:00000000 ps3fb ioc0_01: [140] 00100008:00000000 01012000:00000000 00018488:00000000 00428a02:00000000 ps3fb ioc0_01: [144] 00000000:00000000 00000000:00000000 005aaae4:00000000 00100008:00000000 ps3fb ioc0_01: [148] 00000000:00000000 00000000:00000000 edcba987:00000000 00000000:00000000 ps3fb ioc0_01: [14c] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [150] 00000f11:00000000 66667899:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [154] 00080008:00000000 00000000:00000000 00000111:00000000 00000f11:00000000 ps3fb ioc0_01: [158] 01012000:00000000 00018488:00000000 00428a02:00000000 00080008:00000000 ps3fb ioc0_01: [15c] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [160] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [164] 00000000:00000000 00000000:00000000 00000000:00000000 00000001:00000000 ps3fb ioc0_01: [168] 00000000:00000000 40100000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [16c] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [170] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [174] ffffffff:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [178] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [17c] 00000000:00000000 00000000:00000000 0001bc80:00000000 00000202:00000000 ps3fb ioc0_01: [180] 00000000:00000000 00000008:00000000 00000000:00000000 00080008:00000000 ps3fb ioc0_01: [184] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [188] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: P [18c] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [190] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [194] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [198] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [19c] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1a0] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1a4] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1a8] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1ac] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1b0] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1b4] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1b8] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1bc] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1c0] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1c4] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1c8] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1cc] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1d0] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1d4] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1d8] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1dc] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1e0] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1e4] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1e8] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1ec] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1f0] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1f4] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1f8] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000 ps3fb ioc0_01: [1fc] 00000000:00000000 00000000:00000000 00000000:00000000 00000000:00000000
User Exception
- Generated with 0xCAFEBABE methods 0xB00 and 0xB04.
- And finally, after rewriting ps3fb driver, user exception can be catched on Linux too.
The following FIFO command was executed: 0x0004eb00 0xdeadbabe Result: ps3rsx_intr:293: interrupt status 0x00000080 ps3rsx_intr:303: user exception: cause 0xdeadbabe
- Cause is specified by user and stored at offset 0x12CC in lpar_driver_info by LV1.
- Only the context which called method 0xB00 or 0xB04 will be notified about the exception by LV1.
Debugging
- Several device IO regions are mapped into LV2 address space with LV1 call lv1_gpu_device_map.
- Following device IDs are mapped: 0x9, 0xC, 0xD, 0xE and 0xF.
Profiling
- Several device IO regions are mapped into LV2 address space with LV1 call lv1_gpu_device_map.
- Following device IDs are mapped: 0xC, 0xB, 0xA, 0x9 and 0x7.
- After mapping the device IDs, LV1 GPU call 0x106 is used. It probably activates the profiling in RSX chip:
lv1_gpu_context_attribute(0x55555555 /* context handle of the first created GPU context */, 0x106, 0x1, 0x0, 0x0, 0x0)
- LV1 context attribute 0x106 modifies RSX registers 0x28000001588 and 0x28000001590.
Shaders
- Now when i can use RSX on Linux properly and create multiple contexts, it is time to take a closer look at the vertex and fragment shader.
- Currently, 3D acceleration works on Linux, i can draw vertex buffers but having trouble with vertex colors. The only colors i managed to get working is green or black. It doesn't matter which color i specify in vertex or fragment program, polygons are rendered always either black or green. Trying to figure out why. See example below.
- Fixed problem with vertex colors :)
PS3:HvReverseEngineering:RSX:VertexShader
PS3:HvReverseEngineering:RSX:FragmentShader
Example
- Here is an example i rendered on Linux with 3D acceleration and simple vertex and fragment shaders.
- I used display buffers in video RAM (double buffering) and a vertex buffer with a simple triangle.
- The image was rendered with my ps3rsx driver, ps3fb driver was running in parallel.
- And finally vertex colors working properly on Linux.
- It was my error, i wrongly set output from H0 flag in fragment program control command to RSX.
- Next step - textures :)
Hardware Registers
- Offsets are relative to address 0x28000000000. That's where RSX is memory mapped on PS3.
- Use Linux driver ps3rsxmmio e.g. to access these registers.
| Offset | Description |
|---|---|
| 0x140 | Hardware master interrupt control |
| 0x3204 | Active channel id |
FIFO Command Buffer
FIFO Control Registers
- LV1 call lv1_gpu_context_allocate returns LPAR address of FIFO control registers.
- You have to map it into Linux address space before you can access FIFO control registers.
- Value of PUT and GET registers are NOT expressed in Linux address space but in RSX address space. You have to convert it to RSX address space.
- GET register is read-only and is modified by RSX while it's processing FIFO commands.
Kicking FIFO Command Buffer
- As long as values of GET and PUT FIFO control registers are equal, RSX doesn't process commands from the FIFO command buffer.
- When the value of PUT register is not equal to the value of GET register, RSX starts processing commands in the FIFO command buffer.
- To execute FIFO commands, place them in the FIFO command buffer and change the value of PUT register.
FIFO Setup Programs of emer_init.self
- PS3:HvReverseEngineering:emer_init.self:Program 1
- PS3:HvReverseEngineering:emer_init.self:Program 2
- PS3:HvReverseEngineering:emer_init.self:Program 3
cellGcmInit FIFO Buffer Dump
FIFO Commands
Example How to Use FIFO Command Buffer
Here is a small Linux kernel module which shows you how to use FIFO command buffer on Linux.
- RSX allows to create multiple contexts.
- This kernel module should run without problems with ps3fb driver already running.
- Make sure you unload ps3vram driver before running this module because ps3vram allocates all available RSX memory for itself and because of this, lv1_gpu_memory_allocate will always fail.
- This kernel module lets the RSX execute a simple program which contains only NOP (No Operation) commands.
Download source code: [2]
Source Code
/*
* PS3 RSX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <asm/abs_addr.h>
#include <asm/cell-regs.h>
#include <asm/lv1call.h>
#include <asm/ps3.h>
#define RSX_FIFO_CMD_BUF_SIZE (1 * 1024 * 1024)
#define RSX_MEM_SIZE (32 * 1024 * 1024)
#define RSX_GPU_IOIF (0x0e000000ul)
#define RSX_FIFO_CTRL_SIZE (4 * 1024)
struct rsx_fifo_ctrl {
u8 res[0x40];
u32 put;
u32 get;
};
static u32 *rsx_fifo_cmd_buf;
static u64 rsx_fifo_cmd_buf_lpar;
static u64 rsx_mem_handle, rsx_mem_lpar;
static u64 rsx_ctx_handle;
static u64 rsx_fifo_ctrl_lpar;
static u64 rsx_drv_info_lpar;
static u64 rsx_reports_lpar, rsx_reports_size;
static struct rsx_fifo_ctrl *rsx_fifo_ctrl;
/*
* FIFO program
*/
static u32 rsx_fifo_prg[] = {
0x00000000, /* nop */
0x00000000, /* nop */
0x00000000, /* nop */
};
/*
* ps3rsx_init
*/
static int __init ps3rsx_init(void)
{
unsigned long timeout;
int res;
/* FIFO command buffer must be allocated in XDR memory */
rsx_fifo_cmd_buf = kmalloc(RSX_FIFO_CMD_BUF_SIZE, GFP_KERNEL);
if (!rsx_fifo_cmd_buf) {
printk(KERN_INFO"could not allocate FIFO command buffer\n");
res = -ENOMEM;
goto fail;
}
res = lv1_gpu_memory_allocate(RSX_MEM_SIZE, 0, 0, 0, 0,
&rsx_mem_handle, &rsx_mem_lpar);
if (res) {
printk(KERN_INFO"lv1_gpu_memory_allocate failed (%d)\n", res);
res = -ENXIO;
goto fail_free_fifo_cmd_buf_mem;
}
res = lv1_gpu_context_allocate(rsx_mem_handle, 0,
&rsx_ctx_handle, &rsx_fifo_ctrl_lpar, &rsx_drv_info_lpar,
&rsx_reports_lpar, &rsx_reports_size);
if (res) {
printk(KERN_INFO"lv1_gpu_context_allocate failed (%d)\n", res);
res = -ENXIO;
goto fail_free_gpu_mem;
}
/* map FIFO command buffer into RSX address space */
rsx_fifo_cmd_buf_lpar = ps3_mm_phys_to_lpar(__pa(rsx_fifo_cmd_buf));
res = lv1_gpu_context_iomap(rsx_ctx_handle,
RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar, RSX_FIFO_CMD_BUF_SIZE,
CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M);
if (res) {
printk(KERN_INFO"lv1_gpu_context_iomap failed (%d)\n", res);
res = -ENXIO;
goto fail_free_gpu_mem;
}
/* map RSX FIFO control registers */
rsx_fifo_ctrl = (struct rsx_fifo_ctrl *) ioremap(rsx_fifo_ctrl_lpar, RSX_FIFO_CTRL_SIZE);
if (!rsx_fifo_ctrl) {
printk(KERN_INFO"could not map FIFO control\n");
res = -ENXIO;
goto fail_free_gpu_mem;
}
/* PUT and GET offsets are in RSX address space */
res = lv1_gpu_context_attribute(rsx_ctx_handle, 0x1,
RSX_GPU_IOIF + 0x0 /* PUT offset */, RSX_GPU_IOIF + 0x0 /* GET offset */,
0x0, 0x0);
if (res) {
printk(KERN_INFO"lv1_gpu_context_attribute(0x1) failed (%d)\n", res);
res = -ENXIO;
goto fail_unmap_fifo_ctrl;
}
/* copy FIFO commands to FIFO command buffer */
memcpy(rsx_fifo_cmd_buf, rsx_fifo_prg, sizeof(rsx_fifo_prg));
printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);
/* kick FIFO */
rsx_fifo_ctrl->put = RSX_GPU_IOIF + sizeof(rsx_fifo_prg);
/* poll until RSX is done processing FIFO commands */
timeout = 100;
while (timeout--) {
if (rsx_fifo_ctrl->get == rsx_fifo_ctrl->put)
break;
msleep(1);
}
printk(KERN_INFO"GET offset (0x%08x) PUT offset (0x%08x)\n", rsx_fifo_ctrl->get, rsx_fifo_ctrl->put);
if (rsx_fifo_ctrl->get != rsx_fifo_ctrl->put) {
printk(KERN_INFO"FIFO command buffer timeout\n");
res = -ENXIO;
goto fail_unmap_fifo_ctrl;
}
return 0;
fail_unmap_fifo_ctrl:
iounmap(rsx_fifo_ctrl);
fail_free_gpu_mem:
lv1_gpu_memory_free(rsx_mem_handle);
fail_free_fifo_cmd_buf_mem:
kfree(rsx_fifo_cmd_buf);
fail:
return res;
}
/*
* ps3rsx_exit
*/
static void __exit ps3rsx_exit(void)
{
iounmap(rsx_fifo_ctrl);
lv1_gpu_context_iomap(rsx_ctx_handle, RSX_GPU_IOIF, rsx_fifo_cmd_buf_lpar,
RSX_FIFO_CMD_BUF_SIZE, CBE_IOPTE_M);
lv1_gpu_context_free(rsx_ctx_handle);
lv1_gpu_memory_free(rsx_mem_handle);
kfree(rsx_fifo_cmd_buf);
}
module_init(ps3rsx_init);
module_exit(ps3rsx_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PS3 RSX");
MODULE_AUTHOR("glevand");
Test
# insmod ./ps3rsx.ko # dmesg GET offset (0x0e000000) PUT offset (0x0e000000) # GET and PUT offsets before kicking FIFO GET offset (0x0e00000c) PUT offset (0x0e00000c) # GET and PUT offsets after kicking FIFO
As you see, RSX processed our FIFO commands :)
Linux Driver
- DRI/DRM is the ONLY way to go !!! No hacks like kernel modules with tons of IOCTLs !!!
- First implement 2D acceleration and then add 3D support
- The driver consists of 2 parts: DDX driver for X11 (user space) and DRM driver for Linux Kernel (kernel space)
- First implement DRM driver and test it from user space without DDX and libdrm by talking to it directly
DDX Driver
- Use libdrm
- Use EXA API for 2D acceleration on X11 (or maybe use XAA API)
- Use Kernel Mode Setting
Design
- To implement accelerated X11 RSX driver we have to use video RAM for rendering BUT we are NOT allowed to access it directly by mmaping it into CPU address space. That means we have to implement all kind of EXA API functions which will allow X11 server e.g. upload pixmaps to video RAM without accessing it directly.
- Nouveau driver e.g. tries to use inline transfers to video RAM through FIFO command buffer to upload pixmaps if possible. If it's not possible because the pixmap is too large then the nouveau driver falls back to GART memory and DMA transfers from GART memory to video RAM.
- X11 doesn't use double buffering and display buffer flipping. Everything is drawn to the front buffer. Nouveau DDX driver e.g. creates a single scanout buffer in video RAM of screen size and then makes it active by using function drmModeSetCrtc which uses DRM ioctl DRM_IOCTL_MODE_SETCRTC.
- Impelemnt EXA API.
DRM Driver
- Extend nouveau driver or create a new one ???
- Decision: create new DRM driver in order to learn how DRM framework in Linux kernel works and because we have to use LV1 calls to access RSX (and because it's a lot more fun to do it on my own). But use nouveau as an example for DRM driver. Maybe i should better use radeon DRM driver as an example beacuse it seems to be better designed and implemnted !!!
- Nouveau fork https://github.com/pathscale/pscnv/wiki which uses totally different memory management.
- The driver is very low level and allows direct access to almost all RSX funtions, e.g. FIFO buffer, to achieve maximum performance.
- All data buffers, e.g. vertices and textures, are managed by DRM framework (Linux kernel). To avoid copying from user to kernel space, the buffers will be mmaped into user space.
- Provides an interface to manage graphic objects in VRAM.
- Use TTM or GEM ??? TTM is used by radeon and nouvea drivers, so i guess we could use it too. GEM is for Intel chips.
- Extend libdrm library to support new DRM driver.
- Fences can be implemented with RSX REF Control Register
Memory Management
- Size of all memory objects must be multiple of the page size (4096 bytes) even if a smaller size is requested by user
- Nouveau driver uses IOCTL DRM_NOUVEAU_GEM_NEW to allocate memory objects in VRAM or GART. The IOCTL returns the handle of the newly allocated memory object.
- An example from Mesa how memory objects are used: [3] [4]
Video RAM
- VRAM is allocated once during context creating and cannot be changed during the whole life of the context.
- lv1_gpu_memory_allocate returns LPAR address of allocated VRAM which can be mapped into kernel address space.
- VRAM starts at offset 0x0 in GPU address space.
- VRAM heap management is necessary, use e.g. TTM (ttm_bo_init_mm).
- This memory type is used e.g. for vertices or textures.
- It should be mappable from user space in order to allow user to put data there.
- GameOS calls it Local Memory.
- VRAM can be mapped into kernel-space with ioremap.
- To map VRAM into user-space map it first into kernel-space with ioremap and then use remap_pfn_range to map into user-space.
- Use VM_IO flag for this kind of memory when mapping it into user-space.
- Mapping examples: [5] [6]
GART Memory
- GART memory region is a memory region in System Memory but accessible by RSX through GART [7].
- GameOS calls it Main Memory.
- Problem: lv1_gpu_context_iomap supports ONLY 1MB and 64kB pages
- Size of system memory objects mapped into GPU address space should be either multiple of 1MB which means wasting lots of RAM and we don't have enough of it anyways. This solution is NOT suitable.
- Or place several GART memory objects into 1 MB page and map it. That would mean we have to use memory manager for each 1MB page.
- That means, we have to allocate 1MB page even if user requested a smaller memory region. Then initialize a heap manager for this 1MB page and return ONLY requested size. The following requests for GART memory regions can be satisfied from the previously allocated 1MB pages which still have enough free memory.
- FIFO command buffer is an example of a GART memory object which has to be mapped into GPU address space with lv1_gpu_context_iomap before it can be used by RSX.
- User allocates FIFO command buffer in GART address space, maps it into user space, write commands into it and then pushes it to DRM driver which maps it into RSX address space and CALLs it.
- TTM: TTM_PL_FLAG_TT for GART memory
- GameOS applications using GCM library map GART memory beginning at offset 0x10000000 or 0x20000000, just after where the whole VRAM is mapped.
- Don't use kmalloc for this type of memory. Use __get_free_pages and mark pages with flag VM_RESERVED before exporting it to user-space else they can be swapped out.
- TTM uses struct ttm_backend_func to call driver specific GART mapping functions. nouveau_sgdma.c handles GART memory mapping.
CPU Memory
- This type of memory cannot be accessed by RSX at all.
- Because this type of memory is not mapped into RSX address space through GART we don't need to allocate it in 1MB multiples.
- What do we need it for ???
Mapping Memory Objects into Kernel-Space
- Nouveau driver uses ttm_bo_kmap to map memory objects into kernel-space (see ttm_bo_util.c).
- Nouveau driver uses ttm_bo_ioremap to map IO memory into kernel-space, e.g. VRAM or GPU registers (see ttm_bo_util.c) which uses ioremp_wc or ioremp_nocache.
- TTM uses page-wise allocation for buffers. The buffers are contiguous ONLY in a single page. That has a huge advantage over allocating 1MB contiguous memory blocks in kernel space. It's far easier to allocate a single page in Linux kernel than 1MB memory chunk, especially on PS3 arch which has only 256MB.
- Problem: lv1_gpu_context_iomap allows ONLY 1MB pages. Use lv1_put_iopte ???. See [8], [9], [10] and [11].
- Yes, we can use lv1_put_iopte instead of lv1_gpu_context_iomap. That would solve the problem with 1MB pages on Linux. Both LV1 calls use the same internal LV1 function to map memory pages.
- lv1_gpu_context_iomap uses IOAS_ID 0 and IOID 1.
- TTM allows to map a buffer multiple times. Mapping information is stored in struct ttm_bo_kmap_obj.
- To make single allocated pages look contiguous to kernel-space, TTM uses vmap.
- It is possible to use 64KB pages for GART mapping without patching LV1. To enable 4KB pages support we have to patch LV1.
- Tested with 64kB IO page size. It works fine.
Mapping Memory Objects into User-Space
- User-space programs should be able to allocate memory objects in VRAM or GART and map it with mmap syscall.
- See nouveau_ttm.c:nouveau_ttm_mmap.
- Mapping memory objects into user-space avoids copying of data between user/kernel spaces.
- Problem: how to identify memory objects ???
- libdrm uses handles which are returned by DRM kernel driver when a new memory object is created. The handle is passed to mmap syscall as parameter offset. DRM driver looks up the handle and identifies the appropriate memory object which is mapped into user-space then.
- Nouveau driver uses TTM framework to map memory objects into user-space. TTM doesn't map all pages owned by the memory object at once but installs VM operation fault which maps single pages on demand. It makes sense because user application rarely accesses all pages of the mapped memory object at once.
- To map memory objects located in VRAM we have to map it into kernel space first with ioremap.
GEM
- GEM design document: [12].
- It doesn't handle video RAM, only GART memory.
TTM
- TTM design document: [13].
- It's a nightmare, guys. That's my honest opinion. But we don't have anything better unfortunately.
- After studying nouveau and readeon drivers (both use TTM as kernel backend for GEM user interface) for some time, it seems that TTM is a really good framework and i take the previous words back and apologize for it :). It is worth to spend some time studying it and learning GPU programming concepts in Linux kernel. It's fun. Too bad FreeBSD still has the old DRM framework.
- TTM is very powerful. It can e.g. move buffer objects between video RAM and GART memory automatically.
- Nouveau fork pscvn e.g. doesn't use TTM at all. They implemented their own memory manager for video RAM and GART memory.
- Validating buffer means placing it where we want it to be (VRAM or GART) and making sure it cannot be evicted while GPU accesses it. E.g. when we send FIFO commands to GPU in a memory buffer, TTM will validate it and make sure it's accessible by GPU while it's accessed.
- Pining buffer means that the buffer cannot be evicted by TTM to make room for other buffers in case there is not enough VRAM/GART memory.
- Memory types: TTM_PL_VRAM - video RAM, TTM_PL_TT - GART memory, TTM_PL_SYSTEM - system memory not accessible by GPU.
- ttm_bo_mmap maps buffer objects into user-space. TTM maintains a RB tree of all allocated buffer objects and uses ttm_bo_vm_lookup_rb to look up a buffer object by mmap handle. Not all pages of a buffer object are mapped at once, TTM uses delayed page mapping through page faults.
- ttm_bo_mmap just installs ttm_bo_vm_ops as vm_ops for the specified VMA. And ttm_bo_vm_fault maps BO pages on demand if user-space accesses them. If not then pages are not mapped at all which is an optimization of TTM.
- ttm_bo_device_init initializes buffer object driver.
- Nouveau driver uses ttm_bo_manager_func struct to manage VRAM/GART memory on older Nvidia chips. We could do it too.
- ttm_bo_init_mm initializes management of VRAM or GART memory.
- For GART memory, BO driver should implement create_ttm_backend_entry which returns function pointers for GART backend. Nouveau driver implements it with nouveau_sgdma_init_ttm which returns nv04_sgdma_backend struct.
- GART memory backend implements functions: populate, clear, bind, unbind and destroy. bind function fills GPU's IOMMU with pages, for RSX, just use lv1_gpu_context_iomap here. unbind removes pages from GPU's IOMMU.
DRM Display
- CRTC -> Encoder -> Connector
- Each CRTC may have one or more connectors attached to it.
- drm_connector_helper_funcs->get_modes returns supported modes.
FIFO Command Buffer
- Every context has its own one main FIFO command buffer which is NOT accessible directly by user space.
- User-space applications can allocate additional FIFO command buffers in GART memory space, map it into user space, store commands there and submit to DRM driver.
- Nouveau driver uses IOCTL NOUVEAU_GEM_PUSHBUF to execute FIFO command buffers. See nouveau_gem.c:nouveau_gem_ioctl_pushbuf.
- By user applications submitted FIFO command buffers are mapped by DRM driver into RSX address space first and then executed with CALL command.
- Problem: All references to graphics objects contained in FIFO command buffers must be expressed in RSX address space. How does user space know the right offsets of the referenced objects ???
- To solve the above problem, Nouveau driver uses relocations which are submitted to DRM driver together with FIFO command buffers. The DRM driver applies the specified relocations before executing the FIFO command buffer. See nouveau_gem.c:nouveau_gem_pushbuf_reloc_apply.
- Relocations contain memory object handles which they apply to. The DRM driver looks up the memory object by its handle and the memory objects contain GPU address space offsets.
Example
---------------------------------------------------------------
| |
| |
\|/ Main FIFO command buffer (one per allocated context) |
------------------------------ ------------------------------------
| | | | | | |
| ... | CALL | ... | CALL | ... | JMP |
| | | | | | |
------------------------------ ------------------------------------
| /|\ | /|\
-------------| | | |
| ------| --------| |
\|/ | | ---|
----------------------- | |
| | | | | |
| ... | ... | RET | | |
| | | | | |
----------------------- | |
FIFO command buffer 1 | |
(allocated by user space) \|/ |
-----------------------
| | | |
| ... | ... | RET |
| | | |
-----------------------
FIFO command buffer 2
(allocated by user space)
Fences
- Nouveau driver implements DRM fences with REF control register. See nouveau_fence.c:nouveau_fence_new.
- Nouveau driver uses shared video RAM region for syncing between channels. GameOS uses it also by mapping dev id 0x9 with lv1_gpu_device_mao.
- 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.
- FreeBSD console driver e.g. uses ONLY video RAM for console driver, it just maps it into CPU address space and accesses it directly. No buffer flips are done at all.
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: [14] and [15]
- 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: [16] [17]
- GEM: [18]
- TTM vs GEM: [19]
- OMAP DRM Driver: https://github.com/robclark/kernel-omap4/tree/omap_gpu-android/drivers/gpu/drm/omap
- Xv: http://www.xfree86.org/current/DESIGN16.html
- Xv protocol: http://www.x.org/releases/current/doc/videoproto/xv-protocol-v2.txt
- http://hoegsberg.blogspot.com/2007/08/redirected-direct-rendering.html
- http://www.x.org/releases/current/doc/dri2proto/dri2proto.txt
- http://www.faqs.org/docs/Linux-HOWTO/Framebuffer-HOWTO.html
- http://tldp.org/HOWTO/Framebuffer-HOWTO/x1010.html
- http://alinux.tv/Kernel-2.6.34/driver-model/platform.txt
- http://kernel.org/doc/htmldocs/drm/drmInternals.html
- http://groups.google.com/group/wayland-display-server/browse_thread/thread/3e67362c76d3d251
- http://kernelplanet.org/
- http://lists.linaro.org/pipermail/linaro-mm-sig/2011-May/000236.html
- http://git.openmoko.org/?p=kernel.git;a=tree;f=drivers/mfd/glamo;h=0e707c1893a52d9c845246acd844525b5a71419f;hb=e62a4ae1c6783f41b41a9ac3d258786586b65a40
BD Drive
Crossreference: ps3devwiki.com::HV#BD Drive
Profile
- BD profile can be read with GET PROFILE device command or SCSI command GET CONFIGURATION
Profile Table
| Profile | Description |
|---|---|
| 0x0 | No Current Profile |
| 0x2 | Removable Disk |
| 0x8 | CD-ROM |
| 0x9 | CD-R |
| 0xa | CD-RW |
| 0x10 | DVD-ROM |
| 0x11 | DVD-R Sequential recording |
| 0x12 | DVD-RAM |
| 0x13 | DVD-RW Restricted Overwrite |
| 0x14 | DVD-RW Sequential recording |
| 0x1a | DVD+RW |
| 0x1b | DVD+R |
| 0x40 | BD-ROM |
| 0x41 | BD-R Sequential Recording(TBD) |
| 0x42 | BD-R Random Recording(TBD) |
| 0x43 | BD-RE |
| 0x50 | PS1 CD-ROM |
| 0x60 | PS2 CD-ROM |
| 0x61 | PS2 DVD-ROM |
| 0x70 | PS3 DVD-ROM |
| 0x71 | PS3 BD-ROM |
| 0x10000 | CD-DA |
| 0x20000 | SACD |
| 0x100000 | Dual Layer (Parallel) |
| 0x200000 | Dual Layer (else Parallel) |
Buffer
- BD drive has several buffers associated with internal flash
- Buffer can be read and written with SCSI commands READ/WRITE BUFFER
- Writing buffer is enabled with SCSI command MODE SELECT 10 first
Buffer Table
| ID | Size | Description |
|---|---|---|
| 0x0 | 0x8000 | Used to transfer firmware to BD drive |
| 0x1 | 0x800 | Serial Flash |
| 0x2 | 0x60 | P-Block |
| 0x3 | 0x670 | S-Block |
| 0x4 | 0x8000 | Host Revocation List (HRL) Empty |
| 0x5 | 0x8000 | Host Revocation List (HRL) Current |
| 0x6 | 0x670 | S-Block |
| 0x7 | 0x8000 | Host Revocation List (HRL) |
HRL Buffer
- Size is 32KB just like AACS specifications prescribes (See AACS Common Specification 3.2.5.2 Host Revocation List Record)
- We could replace HRL with an older one in BD drive flash and restore revoked Host Certificates !!!
Device Commands
Get Profile (0x11)
- BD profile can be read with LV1 call lv1_send_storage_device_command and command 0x11
- LV1 sends SCSI command GET CONFIGURATION to BD drive with requested type 0x0, starting feature number 0x0 and allocation length 0x8
- See SCSI command GET CONFIGURATION
Auto Request Sense Mode On/Off (0x30)
- LV1 expects a 4 byte value: 0x0 - On, 0x1 - Off
SCSI Commands
Get Configuration
Getting the profile of a BD movie disc:
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00 SCSI Status: Good Sense Information: sense buffer empty Received 8 bytes of data: 00 00 00 00 38 00 00 00 40 ...8...@ # 0x40 means BD-ROM
Getting the profile of a PS3 game disc:
# sg_raw -r 0x8 /dev/sr0 46 02 00 00 00 00 00 00 08 00 SCSI Status: Good Sense Information: sense buffer empty Received 8 bytes of data: 00 00 00 00 38 00 00 ff 71 ...8...q # 0x71 means PS3 BD-ROM
Get SS Key
- By SCSI standard undocumented parameters are used
- SCSI Report Key command with key format 0x3 and key class 0xe0
- 8 bytes are returned by BD drive
- Used by VSH
Test with PS3 game disc:
# sg_raw -r 8 /dev/sr0 a4 00 00 00 00 00 00 e0 00 08 03 00 SCSI Status: Good Sense Information: sense buffer empty Received 8 bytes of data: 00 00 06 00 00 00 00 00 04 ........
Eject Media
sg_raw /dev/sr0 0x1b 00 00 00 02 00
Load Media
sg_raw /dev/sr0 0x1b 00 00 00 03 00
Mode Select 10
Enable Buffer Write
- Uses PF 0x1, SP 0x0 and parameter list length 0x10
- Uses the following parameter list: 0x00 0x0e 0x00 0x00 0x00 0x00 0x00 0x00 0x2d 0x6 <buffer id> 0x00 0x00 0x00 0x00 0x00
- Enables writing to BD drive flash, e.g. to HRL buffer !!!
Test with sg3-utils which enables write to HRL buffer:
sg_raw /dev/sr0 55 10 00 00 00 00 00 00 10 00 00 0e 00 00 00 00 00 00 2d 06 04 00 00 00 00 00
Write Buffer
- Used e.g. by Update Manager to send BD firmware to BD drive
- Mode 0x5 (Download microcode and save) is used e.g. to write HRL to BD drive flash
- Mode 0x7 (Download microcode with offsets and save) is used e.g. to write BD firmware to BD drive flash
AACS
AACS SPU Module
- BD player on GameOS uses AacsModule.spu.isoself (/dev_flash/bdplayer) to perform AACS authentication
- Tested on OtherOS++ 3.55
- Host certificate, host private key and AACS LA public key are stored encrypted with AES-256-CTR in the SPU module and are decrypted when the SPU module is loaded or when it's accessed first. The AES-256-CTR key and IV are in the SPU module too.
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
Terminate Session (0xfefefeff)
- AACS SPU module runs and processes commands as long as you need
- After a command is complete, the SPU module waits for the next command
- This command terminates the current session and stops SPU module
Drive Revocation List (DRL)
- SHA1 hash is encrypted/decrypted by SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)
- SHA1 hash is read with VTRM service 0x2005 (Retrieve)
- SHA1 hash is written with VTRM service 0x2003 (Store With Update)
Content Revocation List (CRL)
- SHA1 hash is encrypted/decrypted by SYSCON services 0x9003/0x9004 (Encrypt/Decrypt)
- SHA1 hash is read with VTRM service 0x2005 (Retrieve)
- SHA1 hash is written with VTRM service 0x2003 (Store With Update)
Host Revocation List (HRL)
- Stored in BD drive flash
- It can be read/written with SCSI commands READ/WRITE BUFFER. Yeah, it can be written too :D
Read HRL from BD Drive Flash
- It seems that BD drive has several HRL in its flash
- Empty HRL stored on BD drive flash can be read with SCSI command READ BUFFER by using as mode 0x2, buffer id 0x4 and allocation length 0x40
- Current HRL stored on BD drive flash can be read with SCSI command READ BUFFER by using as mode 0x2, buffer id 0x5
Empty HRL
# sg_read_buffer -m 2 -i 4 -o 0 -l $((0x40)) /dev/sr0 00 10 00 00 0c 00 03 10 03 00 00 00 01 21 00 00 34 10 00 00 00 00 00 00 00 00 1b 0b f2 6d 47 9e 77 62 20 3d 91 fc 78 b1 59 c9 52 ca a4 c7 41 85 24 96 64 30 8d 1d 95 8e 9b 84 c6 fa 4a dd 43 9b 42 98 fe ff # byte 0x21 at offset 0xc means Record Type HRL # as you see this HRL is empty
Current HRL
# sg_read_buffer -m 2 -i 5 -o 0 -l $((0x7c)) /dev/sr0 00 10 00 00 0c 00 04 10 03 00 00 00 09 21 00 00 6c 10 00 00 00 07 00 00 00 07 00 09 ff ff 00 00 00 0b 20 00 00 ff ff 00 00 00 16 00 08 ff ff 00 00 00 21 30 00 03 ff ff 00 00 00 35 00 04 ff ff 00 00 00 4e 40 00 03 ff ff 00 00 00 54 00 03 ff ff 00 00 00 5e 50 80 93 3a 62 f5 5a 9c 8c 62 ce 7d b8 69 5d d7 b1 60 c3 0f 36 ff 96 a2 3b 32 cb cd 58 d4 12 c9 fd bf 70 f5 16 a6 4a 32 ba 60 f0 5d 71 74 10 # the current HRL is NOT empty and is from MKBv9 because the only BD movie i played on my PS3 has MKBv9
PS3 BD Player Host Certificate
$ hexdump -C aacs_auth/ps3_host_cert.bin
00000000 02 01 00 5c ff ff 80 00 00 39 00 00 65 ea c9 87 |...\ÿÿ...9..eêÉ.|
00000010 8b 85 ef f4 d7 7a 62 b1 d6 00 02 4a ce 68 dd 33 |..ïô×zb±Ö..JÎhÝ3|
00000020 66 88 0e 4f 84 4f 34 b7 7a 05 01 35 a2 0e 73 b6 |f..O.O4·z..5¢.s¶|
00000030 26 da ea 51 57 b3 2e b8 4b c6 e8 7b 0d ee 4d 83 |&ÚêQW³.žKÆè{.îM.|
00000040 3c ea da 86 12 01 51 00 2c 3c 66 d5 25 6f 71 cf |<êÚ...Q.,<fÕ%oqÏ|
00000050 a6 8b 7e 55 ba 1b 35 1f 34 03 43 4e |Š.~Uº.5.4.CN|
0000005c
# Host ID is 0xffff80000039
PS3 BD Player Host Private Key
$ hexdump -C aacs_auth/ps3_host_priv_key.bin 00000000 00 66 8c 9a 75 ee fc 8d a4 26 19 38 e2 71 28 50 |.f..u....&.8.q(P| 00000010 61 bb 09 f0 dd |a....|
AACS Processing Keys
MKB v1
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v1.inf
=MKB=
type:
0x00031003
version:
0x00000001
MKB U masks and UVs: 514
=applying subset-difference=
index: 0
UV: 0x00000001
U mask: 0xff800000
V mask: 0xfffffffe
=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|
processing key:
00000000: 09 f9 11 02 9d 74 e3 5b d8 41 56 c5 63 56 88 c0 |.....t.[.AV.cV..|
C value:
00000000: cb 06 90 db e6 54 55 7b 12 62 aa d7 89 f4 9d 92 |.....TU{.b......|
media key:
00000000: b4 6c 48 5e f7 51 ae 29 ef 87 bc 58 28 f3 2a 8d |.lH^.Q.)...X(.*.|
=MKB verify media key data=
encrypted:
00000000: 46 32 5b 42 48 b4 86 5a fc ef 75 25 47 b1 b5 12 |F2[BH..Z..u%G...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef 0d ac 14 b9 ee f4 bd cc |.#Eg............|
MKB v3
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v3.inf =MKB= type: 0x00031003 version: 0x00000003 MKB U masks and UVs: 528 =applying subset-difference= index: 14 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: 0a b7 33 82 85 62 91 d1 91 4a 95 9e 36 18 c7 a1 |..3..b...J..6...| media key: 00000000: 6e da eb d4 88 aa 38 58 74 26 35 fd fd 36 66 d5 |n.....8Xt&5..6f.| =MKB verify media key data= encrypted: 00000000: 99 76 96 b0 6f 49 37 9b c4 b9 2b be 73 ce 96 1a |.v..oI7...+.s...| decrypted: 00000000: 01 23 45 67 89 ab cd ef fb 01 cc 85 eb e5 bf 0a |.#Eg............|
MKB v4
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v4.inf =MKB= type: 0x00031003 version: 0x00000004 MKB U masks and UVs: 526 =applying subset-difference= index: 12 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: bf 71 0c 8b 46 a0 24 d8 f0 3a a1 26 37 9d fb fc |.q..F.$..:.&7...| media key: 00000000: ef 18 c0 dd bf 02 32 a1 2f 57 f7 65 79 2c 1c 58 |......2./W.ey,.X| =MKB verify media key data= encrypted: 00000000: 54 85 08 a9 6a 70 2a c9 32 e3 74 a6 55 78 6c 01 |T...jp*.2.t.Uxl.| decrypted: 00000000: 01 23 45 67 89 ab cd ef da 90 cf 2a e5 b2 6c 45 |.#Eg.......*..lE|
MKB v7
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v7.inf =MKB= type: 0x00031003 version: 0x00000007 MKB U masks and UVs: 526 =applying subset-difference= index: 7 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: 21 fd c9 4b 3e 1a f3 fe 9e b4 7a e6 ef 01 75 1b |!..K>.....z...u.| media key: 00000000: af cd e2 c8 67 12 a4 b6 a8 58 0c 15 ef 07 6e f8 |....g....X....n.| =MKB verify media key data= encrypted: 00000000: 4b 21 29 a5 0f db 96 bc bc 01 04 71 42 79 00 e5 |K!)........qBy..| decrypted: 00000000: 01 23 45 67 89 ab cd ef 4e f9 d2 05 6e 19 c1 79 |.#Eg....N...n..y|
MKB v8
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v8.inf =MKB= type: 0x00031003 version: 0x00000008 MKB U masks and UVs: 523 =applying subset-difference= index: 4 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: 73 2d 10 bd f8 b4 87 e2 86 a6 d5 3a 6d db 69 15 |s-.........:m.i.| media key: 00000000: dd 46 d4 0d 26 54 5a ce 6c 59 0c 65 b7 2b 3a 9f |.F..&TZ.lY.e.+:.| =MKB verify media key data= encrypted: 00000000: c6 f6 f9 54 ce 90 e0 5e 2b 3b e4 1e 24 92 90 b2 |...T...^+;..$...| decrypted: 00000000: 01 23 45 67 89 ab cd ef 97 e6 61 8b d1 69 3e a0 |.#Eg......a..i>.|
MKB v9
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v9.inf
=MKB=
type:
0x00031003
version:
0x00000009
MKB U masks and UVs: 520
=applying subset-difference=
index: 2
UV: 0x00000080
U mask: 0xff800000
V mask: 0xffffff00
=applying device key=
index: 244
UV: 0x00000100
U mask: 0xff800000
V mask: 0xfffffe00
device key:
00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.|
processing key:
00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.|
C value:
00000000: a4 5a c6 87 43 49 70 bb bf 0c 22 52 83 9e 2a f6 |.Z..CIp..."R..*.|
media key:
00000000: 37 02 bd fc 96 dc a2 18 2e 55 b0 79 6d ad 36 6b |7........U.ym.6k|
=MKB verify media key data=
encrypted:
00000000: 4d 5b 7b 9c 5d ee 55 a6 94 de e1 db 8d 08 c7 a2 |M[{.].U.........|
decrypted:
00000000: 01 23 45 67 89 ab cd ef cd 1d a8 8a 42 5a 10 43 |.#Eg........BZ.C|
MKB v10
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v10.inf =MKB= type: 0x00031003 version: 0x0000000a MKB U masks and UVs: 522 =applying subset-difference= index: 3 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: d4 77 dd 1a 8a 5c 6d d1 dd 31 2d af f7 d3 14 fa |.w...\m..1-.....| media key: 00000000: 38 32 2b 3c 61 b0 35 b4 52 89 84 59 f4 7a 76 e6 |82+<a.5.R..Y.zv.| =MKB verify media key data= encrypted: 00000000: 3f d3 d5 fb 42 37 d9 05 b8 db 6b 03 a0 fe 2e 48 |?...B7....k....H| decrypted: 00000000: 01 23 45 67 89 ab cd ef 65 b1 87 8c eb 0d 60 0f |.#Eg....e.....`.|
MKB v12
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v12.inf =MKB= type: 0x00031003 version: 0x0000000c MKB U masks and UVs: 522 =applying subset-difference= index: 3 UV: 0x00000080 U mask: 0xff800000 V mask: 0xffffff00 =applying device key= index: 244 UV: 0x00000100 U mask: 0xff800000 V mask: 0xfffffe00 device key: 00000000: 81 08 27 a7 6e 5b 2c c1 68 5e 32 17 a2 3e 21 86 |..'.n[,.h^2..>!.| processing key: 00000000: 97 39 40 bb 18 0e 83 26 62 31 ee 59 6c ef 65 b2 |.9@....&b1.Yl.e.| C value: 00000000: 89 75 89 e6 6f 4a de 95 11 32 57 6a cb 99 dd 69 |.u..oJ...2Wj...i| media key: 00000000: 4b dd 69 9d 32 98 d7 b0 ad 32 71 6b 3d 9c e3 c2 |K.i.2....2qk=...| =MKB verify media key data= encrypted: 00000000: 8d 43 fd f2 15 fa 58 78 64 db 25 46 62 ab 02 30 |.C....Xxd.%Fb..0| decrypted: 00000000: 01 23 45 67 89 ab cd ef e6 1c bf 98 45 82 64 d9 |.#Eg........E.d.|
MKB v14
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v14.inf =MKB= type: 0x00031003 version: 0x0000000e MKB U masks and UVs: 526 =applying subset-difference= index: 6 UV: 0x00000248 U mask: 0xfffffe00 V mask: 0xfffffff0 =applying device key= index: 28 UV: 0x00000280 U mask: 0xfffffe00 V mask: 0xffffff00 device key: 00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...| processing key: 00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........| C value: 00000000: 8c 7e 31 e8 15 17 7e c3 2c 67 b7 cc 87 e9 39 c3 |.~1...~.,g....9.| media key: 00000000: 4b b1 31 d1 6e 0e 86 45 89 07 a2 68 91 c4 e5 38 |K.1.n..E...h...8| =MKB verify media key data= encrypted: 00000000: 20 03 8c 70 7d ab d0 6f ba 86 39 f0 31 26 86 5f | ..p}..o..9.1&._| decrypted: 00000000: 01 23 45 67 89 ab cd ef 27 9f e5 35 0b df 3d a5 |.#Eg....'..5..=.|
MKB v15
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v15.inf
=MKB=
type:
0x00031003
version:
0x0000000f
MKB U masks and UVs: 527
=applying subset-difference=
index: 6
UV: 0x00000248
U mask: 0xfffffe00
V mask: 0xfffffff0
=applying device key=
index: 28
UV: 0x00000280
U mask: 0xfffffe00
V mask: 0xffffff00
device key:
00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...|
processing key:
00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........|
C value:
00000000: 75 da 59 cf 0d c2 c0 95 86 fc 6b 8e 2e e9 cc 85 |u.Y.......k.....|
media key:
00000000: 28 46 25 38 3d cc 4f 1f 90 be 7d f7 8a ba 7b fd |(F%8=.O...}...{.|
=MKB verify media key data=
encrypted:
00000000: 8d d2 69 e0 b7 6a 44 53 03 ad ef 58 44 fc a7 d7 |..i..jDS...XD...|
decrypted:
00000000: 01 23 45 67 89 ab cd ef ff 6a 7d c3 17 bb 19 11 |.#Eg.....j}.....|
MKB v16
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v16.inf =MKB= type: 0x00031003 version: 0x00000010 MKB U masks and UVs: 531 =applying subset-difference= index: 5 UV: 0x00000248 U mask: 0xfffffe00 V mask: 0xfffffff0 =applying device key= index: 28 UV: 0x00000280 U mask: 0xfffffe00 V mask: 0xffffff00 device key: 00000000: 44 14 5a 84 6f 19 d0 96 f2 c8 4a 2e 50 c5 c4 f5 |D.Z.o.....J.P...| processing key: 00000000: 58 eb da df 88 dc c9 33 04 cb be db 9e e0 95 f6 |X......3........| C value: 00000000: f8 49 9b d1 32 f9 6e 8d 33 98 35 a8 54 80 d9 fe |.I..2.n.3.5.T...| media key: 00000000: 3a bf bf d7 7e b8 01 43 a9 3c 15 3f ba 47 8c e1 |:...~..C.<.?.G..| =MKB verify media key data= encrypted: 00000000: 8a 67 86 b6 9d 0d 22 dd 5d c2 88 1f 08 f3 ab b4 |.g....".].......| decrypted: 00000000: 01 23 45 67 89 ab cd ef d6 32 1f 17 c4 2f e2 4a |.#Eg.....2.../.J|
MKB v17
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v17.inf
=MKB=
type:
0x00031003
version:
0x00000011
MKB U masks and UVs: 540
=applying subset-difference=
index: 14
UV: 0x00000308
U mask: 0xffffff00
V mask: 0xfffffff0
=applying device key=
index: 21
UV: 0x00000340
U mask: 0xffffff00
V mask: 0xffffff80
device key:
00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss|
processing key:
00000000: 46 5f a8 be 82 85 09 01 4d 05 d2 fc ce ff 35 d2 |F_......M.....5.|
C value:
00000000: 01 f7 54 0b 34 e8 c1 ce 63 8d ea fa bc ce 6e 7b |..T.4...c.....n{|
media key:
00000000: ef 63 4e a8 ca 06 d1 6a c7 21 65 1b 18 b3 04 c6 |.cN....j.!e.....|
=MKB verify media key data=
encrypted:
00000000: d3 b9 d4 9c b6 94 47 d5 3d cc 42 fe 3e 47 40 04 |......G.=.B.>G@.|
decrypted:
00000000: 01 23 45 67 89 ab cd ef f6 b4 c8 6a b7 b8 39 fc |.#Eg.......j..9.|
MKB v18
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v18.inf =MKB= type: 0x00031003 version: 0x00000012 MKB U masks and UVs: 543 =applying subset-difference= index: 17 UV: 0x00000320 U mask: 0xffffff00 V mask: 0xffffffc0 =applying device key= index: 21 UV: 0x00000340 U mask: 0xffffff00 V mask: 0xffffff80 device key: 00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss| processing key: 00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..| C value: 00000000: 7a 8f 03 41 27 c4 86 58 05 37 3a 90 de f8 de 26 |z..A'..X.7:....&| media key: 00000000: e3 ed cd b4 59 b4 12 d4 ae f9 4d 8e 78 7a cd 7d |....Y.....M.xz.}| =MKB verify media key data= encrypted: 00000000: ea 45 fa 35 65 70 56 6f 6a 86 65 ad 52 e7 71 a4 |.E.5epVoj.e.R.q.| decrypted: 00000000: 01 23 45 67 89 ab cd ef bd 36 f9 ce 60 54 80 3c |.#Eg.....6..`T.<|
MKB v19
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v19.inf =MKB= type: 0x00031003 version: 0x00000013 MKB U masks and UVs: 544 =applying subset-difference= index: 17 UV: 0x00000320 U mask: 0xffffff00 V mask: 0xffffffc0 =applying device key= index: 21 UV: 0x00000340 U mask: 0xffffff00 V mask: 0xffffff80 device key: 00000000: eb 55 a4 75 08 0f bc f1 85 34 ef a0 83 9a 73 73 |.U.u.....4....ss| processing key: 00000000: ad 5e 54 6c 46 d7 2d c0 83 ae b5 68 69 24 e1 b3 |.^TlF.-....hi$..| C value: 00000000: b9 0b 55 d1 18 3c cc 80 20 1c 9f 26 c3 58 27 18 |..U..<.. ..&.X'.| media key: 00000000: 75 a9 79 9c 67 50 13 89 98 62 34 5b eb 54 34 dd |u.y.gP...b4[.T4.| =MKB verify media key data= encrypted: 00000000: c4 f0 ce 75 1b 12 b9 f0 22 2f 31 70 66 a9 6a b8 |...u...."/1pf.j.| decrypted: 00000000: 01 23 45 67 89 ab cd ef 66 5c 65 d3 c4 4c c7 b0 |.#Eg....f\e..L..|
MKB v20
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v20.inf =MKB= type: 0x00031003 version: 0x00000014 MKB U masks and UVs: 544 =applying subset-difference= index: 19 UV: 0x00000384 U mask: 0xffffff80 V mask: 0xfffffff8 =applying device key= index: 18 UV: 0x00000384 U mask: 0xffffff80 V mask: 0xfffffff8 device key: 00000000: fb 4a c3 90 09 e8 21 13 d4 5e cf 4b 7e ae a4 67 |.J....!..^.K~..g| processing key: 00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k| C value: 00000000: 10 9f f1 69 36 07 7d 7e ad 8f d2 1a 28 c5 09 ed |...i6.}~....(...| media key: 00000000: dc 9f 08 f7 cb 1b f8 c4 cf 96 4e 96 df 23 56 58 |..........N..#VX| =MKB verify media key data= encrypted: 00000000: 18 ca f5 51 8f 36 ef 2f 7a 49 78 ff 54 40 a5 f1 |...Q.6./zIx.T@..| decrypted: 00000000: 01 23 45 67 89 ab cd ef c5 5d 11 08 c3 26 db 48 |.#Eg.....]...&.H|
MKB v21
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v21.inf =MKB= type: 0x00031003 version: 0x00000015 MKB U masks and UVs: 552 =applying subset-difference= index: 19 UV: 0x00000384 U mask: 0xffffff80 V mask: 0xfffffff8 =applying device key= index: 18 UV: 0x00000384 U mask: 0xffffff80 V mask: 0xfffffff8 device key: 00000000: fb 4a c3 90 09 e8 21 13 d4 5e cf 4b 7e ae a4 67 |.J....!..^.K~..g| processing key: 00000000: 53 fc e7 8e cd 35 2d a5 0d 52 6b 5e e3 d3 d9 6b |S....5-..Rk^...k| C value: 00000000: c0 0c fa bf f0 fe f2 32 77 19 db c4 d8 f8 60 c9 |.......2w.....`.| media key: 00000000: 55 83 aa 69 ff 52 16 83 c2 93 b3 48 03 2a 57 38 |U..i.R.....H.*W8| =MKB verify media key data= encrypted: 00000000: 12 5b f2 75 c8 f8 05 6b 4f 31 a5 ea 4a 12 9f a9 |.[.u...kO1..J...| decrypted: 00000000: 01 23 45 67 89 ab cd ef dc 46 45 b4 79 8d 4f 68 |.#Eg.....FE.y.Oh|
MKB v23
glevand@debian-hdd:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v23.inf =MKB= type: 0x00031003 version: 0x00000017 MKB U masks and UVs: 556 =applying subset-difference= index: 17 UV: 0x00000384 U mask: 0xffffffe0 V mask: 0xfffffff8 =applying device key= index: 7 UV: 0x00000384 U mask: 0xffffffe0 V mask: 0xfffffff8 device key: 00000000: 8b f4 fb d9 1a 7f b7 db 85 76 d1 e5 a1 5a 85 44 |.........v...Z.D| processing key: 00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr| C value: 00000000: f0 81 d4 93 aa b5 01 1a a7 ff 8e 18 8a 48 8a 2d |.............H.-| media key: 00000000: 02 04 59 d0 7c b5 54 94 bf 46 9b 98 91 1e 43 1f |..Y.|.T..F....C.| =MKB verify media key data= encrypted: 00000000: 24 a1 27 f9 30 70 25 67 07 2f 2a d4 13 89 0d aa |$.'.0p%g./*.....| decrypted: 00000000: 01 23 45 67 89 ab cd ef 21 00 20 84 c4 5f 36 78 |.#Eg....!. .._6x|
MKB v25
glevand@bastion:~/aacs_proc_key$ ./aacs_proc_key -n 0x389 -k ps3_device_keys -u ps3_device_key_u_masks_uvs mkbs/MKB_RW_v25.inf =MKB= type: 0x00031003 version: 0x00000019 MKB U masks and UVs: 564 =applying subset-difference= index: 13 UV: 0x00000384 U mask: 0xffffffe0 V mask: 0xfffffff8 =applying device key= index: 7 UV: 0x00000384 U mask: 0xffffffe0 V mask: 0xfffffff8 device key: 00000000: 8b f4 fb d9 1a 7f b7 db 85 76 d1 e5 a1 5a 85 44 |.........v...Z.D| processing key: 00000000: c3 22 38 97 6f f4 4a 51 e2 d3 35 53 cf e8 57 72 |."8.o.JQ..5S..Wr| C value: 00000000: 19 62 23 7d 81 01 c2 55 2f 36 20 1b 3e 69 40 10 |.b#}...U/6 .>i@.| media key: 00000000: 70 b5 9f 35 86 5d 18 73 bb 80 c3 2b f7 41 f6 14 |p..5.].s...+.A..| =MKB verify media key data= encrypted: 00000000: 89 be 1e 1e b1 93 4c f2 2d ac c3 ce ed 10 07 f0 |......L.-.......| decrypted: 00000000: 01 23 45 67 89 ab cd ef 3f 5d 87 7a 88 09 db c4 |.#Eg....?].z....|
Documentation
- SCSI Specification: [20]
- AACS Specification Common: [21]
- AACS Specification Pre-recorded Video Book [22]
- AACS Tutorial: [23]
- AACS Overview: [24]
CSS
CSS SPU Module
- DVD player on GameOS uses CssModule.spu.isoself (/dev_flash/bdplayer) to perform CSS authentication
Commands
- The SPU module supports max 0x25 commands but not all are implemented
- After a command is finished by the SPU module, it sends the status of the command to PPU through SPU Out Intr Mbox. Value 0 means success.
Generate Host Challenge Key (0x3)
- Generates 0x10 bytes of host challenge key
Set Drive Key1 (0x4)
- Sends key1 of size 0x5 returned by DVD drive to the SPU module
Set Drive Challenge Key (0x5)
- Sends 0x10 bytes of drive challenge key to the SPU module
Calculate Host Key2 (0x6)
- Calculates key2 of size 0x5
Get Host Key2 (0x7)
- Returns key2 of size 0x5
Set Disc Key (0x8)
- Sends Disc Key block of size 0x800 to the SPU module
Decrypt Sector (0xc)
- Decrypts the passed sector
CSS Salt
- It's NOT in clear text in the SPU module, it's obfuscated by xoring 0xDEAF (SONY employees have a sense of humor).
- There are 2 bytes for every salt byte
Obfuscated:
71 ED 3F A3 DA FE E4 94 40 8C
Clear text:
F4 10 45 A3 E2
PS3 DVD Player Key Index
0x69
Documentation
- The Content Scrambling System (CSS): [25]
- Cryptanalysis of Contents Scrambling System: [26]
- Cryptography in Home Entertainment: [27]
- Patching DVD Firmware: [28]
CPRM
Commands
- The SPU module supports 0x13 commands.
4C Secret Constant (S-Box)
Documentation
- Cryptomeria C2 Specification: [29]
- Cryptoanalysis of C2: [30]
Remarrying BD Drive on OtherOS++
fdm_spu_module.self
- This SPU module can create either P- or S-Block which are sent to BD drive
- EID2 is passed to the SPU module
- A XDR memory buffer of size 0x1000 is passed to the SPU module
- 4 bytes at offset 0x10 of the XDR memory buffer contain the type of block which should be produced by the SPU module
- When the SPU module is finished, the XDR memory buffer contains the needed block
- After the S- and P-Blocks are produced by the SPU module, they are encrypted with DES before they are sent to BD drive
Block types
| Type | Description | BD Drive Buffer ID |
|---|---|---|
| 0x1 | P-Block | 0x2 |
| 0x2 | S-Block | 0x3 |
Remarrying
Preparations
- You will need ps3dm-utils and sg3-utils
- Dump your EID2 from flash or with ps3dm-utils
- First create P- and S-Blocks from your EID2 with kernel module fdm_spu_module
P-Block
Creating
Sending to BD Drive
S-Block
Creating
Sending to BD Drive
HRL
Empty HRL
Sending to BD Drive
VTRM
Crossreference: ps3devwiki.com::HV#VTRM
VTRM Services
Store With Update (0x2003)
- Used by GameOS BD player to update DRL/CRL hashes
Retrieve (0x2005)
- Product mode is NOT required
- 0x40 bytes of data are read from NOR flash, decrypted by SYSCON and returned to the caller
- Used e.g. by GameOS BD player to read SHA1 hashes of DRL and CRL
DRL and CRL Hashes
- Written by GameOS BD player during DRL/CRL update
- Read by GameOS BD player
- Hashes are stored encrypted on NOR flash
- Encryption/decryption is done by SYSCON (SYSCON Manager)
Test with ps3dm-utils:
# mount dev_flash3 glevand@debian-hdd:~/ps3dm-utils$ sudo mount /dev/ps3vflashe /mnt # DRL SHA1 hash glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/drl/DRL1 8f0652bc6162a240362f90f1b2e5405bb82ee502 /mnt/data-revoke/drl/DRL1 # CRL SHA1 hash glevand@debian-hdd:~/ps3dm-utils$ sha1sum /mnt/data-revoke/crl/CRL1 96791f41f9a76f4d895dd5820db108ec03d19250 /mnt/data-revoke/crl/CRL1 # Retrieve DRL and CRL SHA1 hashes from VTRM # DRL hash is first and then follows CRL hash glevand@debian-hdd:~/ps3dm-utils$ sudo ./ps3dm_vtrm -l 0x0 -p 0x1070000034000001 /dev/ps3dmproxy retrieve 0 0x8f 0x06 0x52 0xbc 0x61 0x62 0xa2 0x40 0x36 0x2f 0x90 0xf1 0xb2 0xe5 0x40 0x5b 0xb8 0x2e 0xe5 0x02 0x96 0x79 0x1f 0x41 0xf9 0xa7 0x6f 0x4d 0x89 0x5d 0xd5 0x82 0x0d 0xb1 0x08 0xec 0x03 0xd1 0x92 0x50 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Backup Flash (0x2012)
- Requires enabled product mode or else service returns always error 0x5
- Reads and returns data from NOR flash beginning at NOR flash offset 0xec0000
Test with ps3dm-utils:
# enable product mode # ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0 /dev/ps3dmproxy: SS retval 0 # ps3dm_vtrm /dev/ps3dmproxy backup_flash 0 0x200 | hexdump -C 00000000 53 43 45 49 ff ff ff ff ff ff ff ff ff ff ff ff |SCEIÿÿÿÿÿÿÿÿÿÿÿÿ| 00000010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ| * 00000200 # dd if=/dev/ps3nflasha bs=1 count=$((0x100)) skip=$((0xec0000)) | hexdump -C 00000000 53 43 45 49 ff ff ff ff ff ff ff ff ff ff ff ff |SCEIÿÿÿÿÿÿÿÿÿÿÿÿ| 00000010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ| *
Flash Address Size (0x2016)
- Requires enabled product mode or else service returns always error 0x5
- Returns 2 64bit values: offset and size of NOR flash region
Test with ps3dm-utils:
# ps3dm_um /dev/ps3dmproxy read_eprom 0x48c07 0xff # ps3dm_vtrm /dev/ps3dmproxy flash_addr_size /dev/ps3dmproxy: SS retval 5 # enable product mode # ps3dm_um /dev/ps3dmproxy write_eprom 0x48c07 0x0 /dev/ps3dmproxy: SS retval 0 # ps3dm_um /dev/ps3dmproxy read_eprom 0x48c07 0x00 # ps3dm_vtrm /dev/ps3dmproxy flash_addr_size 0x0000000000000000 0x0000000000040000
Revoke List
Crossreference: ps3devwiki::Revoke List
LPAR 1 System Call 0x1004A
- Installs new revoke list in LV1
- LPAR 1 processes can use this syscall to install new revoke lists at runtime
- lv2ldr is loaded by LV1 and used to verify the passed revoke list
- After lv2ldr is done verifying the passed revoke list, it checks for stop code and if it's 0xB then LV1 replaces the old revoke list with the new one
- If the verification of the revoke list was successfull then LV1 installs new revoke list and replaces the old one in the ISO loader table at address 0x10100
rvk_list_verifier
- Stop code 0xB means that the passed revoke list is valid.
root@debian-hdd:/home/glevand/rvk_list_verifier# cat /proc/rvk_list_verifier/debug PPE id (0x0000000000000001) VAS id (0x0000000000000002) lv1_construct_logical_spe (0x00000000) SPE id (0x0000000000000033) lv1_enable_logical_spe (0x00000000) lv1_set_spe_interrupt_mask(0) (0x00000000) lv1_set_spe_interrupt_mask(1) (0x00000000) lv1_set_spe_interrupt_mask(2) (0x00000000) lv1_set_spe_privilege_state_area_1_register (0x00000000) ea (0xc000000003f40000) esid (0xc000000008000000) vsid (0x0000408f92c94500) lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) sleep lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) out interrupt mbox (0x0000000000000001) lv1_clear_spe_interrupt_status(2) (0x00000000) transferring ldr args to LS waiting until MFC transfers are finished MFC transfers done out mbox (0x00000001) sleep lv1_get_spe_interrupt_status(0) (0x00000000) lv1_get_spe_interrupt_status(1) (0x00000000) lv1_get_spe_interrupt_status(2) (0x00000000) problem status (0x000b0082) lv1_destruct_logical_spe (0x00000000)
AV Manager
Crossreference: ps3devwiki::AV Manager
- AV Manager is running in Process 9 of HV.
- It communicates with Guest OS through /proc/partitions/0/vuart/0 file.
- GameOS accesses AV Manager through syscalls 367 - 370.
- PS2 Soft EMU accesses AV Manager also.
- Communicates with SYSCON 0 (/dev/sc0)
- Communicates with IOIF0 (/dev/ioif0 or RSX)
Commands
Get HDCP KSV (0xC)
- Returns HDCP KSV
- HDMI KSV is read from SYSCON
- KSV is stored in memory dump of HV process 9 (where AV Manager runs)
SYSCON request packet:
30 01 0200 0000 8033 00000000 0004 0004 11 00 0000 0000ff01
Set HDMI Mode (0x40001)
- Sets HDMI mode
- Mode is set by SYSCON
- Disabling HDCP
IRQ Handling
- Due to problems with VUART IRQs on FreeBSD i decided to take a closer look at HV IRQ handling and outlets. And here i intend to write what i could dig out.
Outlet
- HV uses outlets to notify a LPAR about outstanding IRQs.
- LPAR maps an Outlet to a virtual IRQ number (VIRQ).
- Every LPAR has a PPE object.
- PPE object has a VIRQ-2-Outlet map for every PPE thread.
Outlet Object
offset 0x90 - virtual IRQ number to which outlet is mapped to (4 bytes)
offset 0x94 - ??? (1 byte)
offset 0x95 - flag which signals if IRQ was EOIed or NOT: 0 - EOIed, 1 - NOT EOIed (1 byte)
End Of Interrupt
- lv1_end_of_interrupt_ext is used to EOI an outstanding IRQ.
- Outlet cannot be triggered again until it was EOIed by LPAR. HV checks it and if Outlet is still NOT EOIed then Outlet doesn't notify LPAR about IRQ.
- LPAR passes VIRQ to LV1 call lv1_end_of_interrupt_ext. How does HV knows which Outlet it should EOI then ??? HV knows it because PPE object contains a VIRQ-2-Outlet map for every thread. And PPE object is knownn through PPE ID which is passed by LPAR to HV too. That's how.
- When LPAR EOIs IRQ then HV clears IRQ bit in IRQ state bitmap and resets EOI flag of the corresponding Outlet.
Links
- http://uuu.enseirb.fr/~pelegrin/enseignement/enseirb/archsys/documents/processeurs/cell_johns.pdf
- https://www.power.org/resources/reading/PowerISA_V2.05.pdf
VUART
- HV maintains a bitmap of VUART ports per LPAR. The bitmap is of size 4 x 64bit.
- Each bit in bitmap corresponds to a VUART port. 0 means VUART port is occupied, 1 means that VUART port is free.
- HV allocates a new VUART port on behalf of LPAR1 every time a process from LPAR1 opens a file descriptor in directory /proc/partitions/2/vuart/*.
- To find a free VUART port for LPAR1, HV uses a static variable which contains the next free VUART port number. Every time a new VUART port is created this variable is incremented by one.
- Every time LPAR2 is rebooted, HV destroys VUART ports for AV, SM and DM in LPAR1. And when LPAR2 is booted again, HV assigns different VUART ports to AV, SM and DM in LPAR1.
Interrupt
Status
- Linux kernel remains silent about CONNECT interrupt status (naughty SONY). Anyways, HV sets this bit always if VUART is connected.
enum ps3vuart_port_intr {
PS3VUART_PORT_INTR_TX = (1ul << 0),
PS3VUART_PORT_INTR_RX = (1ul << 1),
PS3VUART_PORT_INTR_DISCONNECT = (1ul << 2),
PS3VUART_PORT_INTR_CONNECT = (1ul << 3),
};
Rx IRQ Trigger
- An Rx interrupt is generated whenever the amount of free space in VUART Rx buffer becomes less than or equal the Rx trigger level.
Tx IRQ Trigger
- An Tx interrupt is generated whenever the amount of occupied space in VUART Tx buffer becomes less than or equal the Tx trigger level.
Physical Memory
Boot Memory Region
- Size is always a power of 2 and is specified in the profile file default.spp.
- LPAR can read the size of the boot memory region from repository node bi.pu.<ppe_id>.rm_size.
- It's allocated by LPAR1's System Manager during LPAR2 booting. SystemManager/Secure LPAR Loader copies/loads LPAR2's image into this memory region.
- 128 MB for old OtherOS.
- 128 MB for OtherOS++ (patched default.spp, that's how OtherOS++ is able to boot any Linux kernel like old OtherOS).
- 16MB for GameOS.
Extended Memory Region
- Linux/FreeBSD calculate the size of this memory region by reading the value of repository node bi.rgntotal and subtracting the size of the boot memory size from it.
- The value of repository node bi.rgntotal is dynamic and is calculated by the LPAR1's process 9 (where e.g. System Manager runs).
<bi.rgntotal> = <total physical memory size> - <memory size reserved for LPAR1> <memory size reserved for LPAR1> - stored in default.spp and is 6MB by default.
- Actually, HV doesn't forbid LPAR2 to allocate more memory than bi.rgntotal specifies. We could allocate more but then LPAR1 will have less memory for heap allocation and who knows what happens e.g. while LPAR1 updates CoreOS flash and mo more heap memory can be allocated by Update Manager, dangerous if you ask me.
LPAR Switching/Scheduling
- Due to problems with LPAR switching on FreeBSD, i took a closer look at LPAR switching in LV1.
- LPAR1 is scheduled by LV1 as soon as e.g. VUART data was sent to AV Manager or System Manager which run in LPAR1.
SLB Table
- Every thread has a SLB table.
- LV1 saves/restores ONLY the first 3 SLB entries during LPAR switch. The first 3 SLB entries are bolted. I only wanted to say to SONY guys: just fucking great done guys, really.
- UPDATE: The VUART problems were finally fixed in freebsd-head. Juhu, VUART works on freebsd now without hangs. Progressing with porting VUART drivers to FreeBSD.
Snippet of LV1 code which shows you how SLB entries are preserved:
ROM:00001F04 nop ROM:00001F08 nop ROM:00001F0C nop ROM:00001F10 ROM:00001F10 switch_LPAR: # CODE XREF: int_handler_1+EC�j ROM:00001F10 # sub_1BD0+58�j ROM:00001F10 lbz %r7, -0x6854(%r13) ROM:00001F14 li %r6, 0x18 ROM:00001F18 ROM:00001F18 loc_1F18: # CODE XREF: sub_1BD0+354�j ROM:00001F18 ldarx %r8, %r6, %r3 ROM:00001F1C andc %r8, %r8, %r7 ROM:00001F20 stdcx. %r8, %r6, %r3 ROM:00001F24 bc 6, eq, loc_1F18 ROM:00001F28 li %r3, -0x6E80 ROM:00001F2C dcbz %r3, %r13 ROM:00001F30 li %r8, 0 # r8 = 0 - SLB entry index ROM:00001F34 li %r9, 1 # r9 = 1 - SLB entry index ROM:00001F38 li %r10, 2 # r10 = 2 - SLB entry index ROM:00001F3C slbmfev %r3, %r8 ROM:00001F40 slbmfee %r4, %r8 ROM:00001F44 slbmfev %r5, %r9 ROM:00001F48 slbmfee %r6, %r9 # int ROM:00001F4C std %r3, -0x6F30(%r13) ROM:00001F50 mfsprg0 %r7 # int ROM:00001F54 std %r4, -0x6F28(%r13) ROM:00001F58 mfsprg1 %r8 # int ROM:00001F5C slbmfev %r3, %r10 ROM:00001F60 slbmfee %r4, %r10 ROM:00001F64 std %r5, -0x6F20(%r13) ROM:00001F68 mfsprg2 %r9 # int ROM:00001F6C std %r6, -0x6F18(%r13) ROM:00001F70 mfsprg3 %r10 # int ROM:00001F74 std %r3, -0x6F10(%r13) ROM:00001F78 std %r4, -0x6F08(%r13) ROM:00001F7C std %r7, -0x6940(%r13) ROM:00001F80 std %r8, -0x6938(%r13) ROM:00001F84 std %r9, -0x6930(%r13) ROM:00001F88 std %r10, -0x6928(%r13) ROM:00001F8C mfspr %r3, buscsr ROM:00001F90 mfspr %r4, iccr ROM:00001F94 mfspr %r5, pbu1 # int ROM:00001F98 std %r3, -0x69C0(%r13) ROM:00001F9C std %r4, -0x69D0(%r13) ROM:00001FA0 std %r5, -0x69C8(%r13) ROM:00001FA4 std %r14, -0x6E90(%r13) ROM:00001FA8 std %r15, -0x6E88(%r13) ROM:00001FAC std %r16, -0x6E80(%r13) ROM:00001FB0 std %r17, -0x6E78(%r13) ROM:00001FB4 std %r18, -0x6E70(%r13) ROM:00001FB8 std %r19, -0x6E68(%r13) ROM:00001FBC std %r20, -0x6E60(%r13) ROM:00001FC0 std %r21, -0x6E58(%r13) ROM:00001FC4 std %r22, -0x6E50(%r13) ROM:00001FC8 std %r23, -0x6E48(%r13) ROM:00001FCC std %r24, -0x6E40(%r13) ROM:00001FD0 std %r25, -0x6E38(%r13) ROM:00001FD4 std %r26, -0x6E30(%r13) ROM:00001FD8 std %r27, -0x6E28(%r13) ROM:00001FDC std %r28, -0x6E20(%r13) ROM:00001FE0 std %r29, -0x6E18(%r13) ROM:00001FE4 std %r30, -0x6E10(%r13) ROM:00001FE8 std %r31, -0x6E08(%r13) ROM:00001FEC
SLB table dump from FreeBSD:
ps3dumpslb:0: 0xcffffffff8000000 0x0000052dc7f77200----------------------- ps3dumpslb:1: 0x0000000008000000 0x0001000000000100 | ps3dumpslb:2: 0x00006c0078000000 0x000152eca1d00100 | ps3dumpslb:3: 0x0000000018000000 0x000100013bb00000 | ps3dumpslb:4: 0xc000000008000000 0x00010000ecc40000 | ps3dumpslb:5: 0xc000000018000000 0x0001000228740000 | ps3dumpslb:6: 0xc000000028000000 0x0001000364240000 | ps3dumpslb:7: 0xc000000038000000 0x000100049fd40000 | ps3dumpslb:8: 0xc000000048000000 0x00010005db840000 | ps3dumpslb:9: 0xc000000058000000 0x0001000717340000 | ps3dumpslb:10: 0xc000000068000000 0x0001000852e40000 | ps3dumpslb:11: 0xc000000078000000 0x000100098e940000 | ps3dumpslb:12: 0xc000000088000000 0x0001000aca440000 | ps3dumpslb:13: 0xc000000098000000 0x0001000c05f40000 | ps3dumpslb:14: 0xc0000000a8000000 0x0001000d41a40000 | ps3dumpslb:15: 0xc0000000b8000000 0x0001000e7d540000 | Too many SLB entries. ps3dumpslb:16: 0xc0000000c8000000 0x0001000fb9040000 | Ahhhhhhhhh, fuck, as soon ps3dumpslb:17: 0xc0000000d8000000 0x00010010f4b40000 | as LV1 switches to LPAR1 and ps3dumpslb:18: 0xc0000000e8000000 0x0001001230640000 | back to LPAR2, we are screwed !!! ps3dumpslb:19: 0xc0000000f8000000 0x000100136c140000 | ps3dumpslb:20: 0xc000000108000000 0x00010014a7c40000 | ps3dumpslb:21: 0xc000000118000000 0x00010015e3740000 | ps3dumpslb:22: 0xc000000128000000 0x000100171f240000 | ps3dumpslb:23: 0xc000000138000000 0x000100185ad40000 | ps3dumpslb:24: 0xc000000148000000 0x0001001996840000 | ps3dumpslb:25: 0xc000000158000000 0x0001001ad2340000 | ps3dumpslb:26: 0xc000000168000000 0x0001001c0de40000 | ps3dumpslb:27: 0xc000000178000000 0x0001001d49940000 | ps3dumpslb:28: 0xc000000188000000 0x0001001e85440000 | ps3dumpslb:29: 0xc000000198000000 0x0001001fc0f40000 | ps3dumpslb:30: 0xc0000001a8000000 0x00010020fca40000 | ps3dumpslb:31: 0xc0000001b8000000 0x0001002238540000 | ps3dumpslb:32: 0xc0000001c8000000 0x0001002374040000---------------------- ps3dumpslb:33: 0x0000000010000000 0x0000dd82ce799c80 ps3dumpslb:34: 0x0000000000000000 0x0000dcc36017ec80 ps3dumpslb:35: 0x00000000f0000000 0x0000e7fad7d13c80 ps3dumpslb:36: 0x0000000010000000 0x0000dd82ce799c80 ps3dumpslb:37: 0x00000000f0000000 0x0000ce15d890ac80 ps3dumpslb:38: 0x0000000010000000 0x0000c39dcf390c80 ps3dumpslb:39: 0x00000000f0000000 0x0000e7fad7d13c80 ps3dumpslb:40: 0x0000000010000000 0x0000dd82ce799c80 ps3dumpslb:41: 0x0000000000000000 0x0000dcc36017ec80 ps3dumpslb:42: 0x00000000f0000000 0x0000ce15d890ac80 ps3dumpslb:43: 0x0000000010000000 0x0000c39dcf390c80 ps3dumpslb:44: 0x00000000f0000000 0x0000e7fad7d13c80 ps3dumpslb:45: 0x0000000010000000 0x0000dd82ce799c80 ps3dumpslb:46: 0x0000000000000000 0x0000dcc36017ec80 ps3dumpslb:47: 0x00000000f0000000 0x000037cbb848ec80 ps3dumpslb:48: 0x0000000010000000 0x00002d53aef14c80 ps3dumpslb:49: 0x00000000f0000000 0x0000e7fad7d13c80 ps3dumpslb:50: 0x0000000010000000 0x0000dd82ce799c80 ps3dumpslb:51: 0x0000000000000000 0x0000dcc36017ec80 ps3dumpslb:52: 0x00000000f0000000 0x0000ce15d890ac80 ps3dumpslb:53: 0x0000000010000000 0x0000c39dcf390c80 ps3dumpslb:54: 0x00000000f0000000 0x0000e7fad7d13c80 ps3dumpslb:55: 0x0000000010000000 0x0000dd82ce799c80 ps3dumpslb:56: 0x0000000000000000 0x0000dcc36017ec80 ps3dumpslb:57: 0x00000000f0000000 0x000037cbb848ec80 ps3dumpslb:58: 0x0000000010000000 0x00002d53aef14c80 ps3dumpslb:59: 0x00000000f0000000 0x0000e7fad7d13c80 ps3dumpslb:60: 0x0000000010000000 0x0000dd82ce799c80 ps3dumpslb:61: 0x0000000000000000 0x0000dcc36017ec80 ps3dumpslb:62: 0x00000000f0000000 0x000037cbb848ec80 ps3dumpslb:63: 0x0000000010000000 0x00002d53aef14c80
SLB table dump from Linux:
ps3dumpslb:0: 0xc000000008000000 0x0000408f92c94500 <--------- bolted kernel segment ps3dumpslb:1: 0xd000000008000000 0x0000f09b89af5400 <--------- bolted kernel vmalloc segment ps3dumpslb:2: 0xc000000000000000 0x0000000000000000 ps3dumpslb:3: 0x0000000010000000 0x0000136eafb0bc80 ps3dumpslb:4: 0xd00007fff0000000 0x0000a70cf353a400 ps3dumpslb:5: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:6: 0x0000000000000000 0x00000957a0a78c80 ps3dumpslb:7: 0x00000000f0000000 0x0000148f1860dc80 ps3dumpslb:8: 0x0000000010000000 0x00000a170f093c80 ps3dumpslb:9: 0x0000000000000000 0x000012af414f0c80 ps3dumpslb:10: 0x00000000f0000000 0x00001de6b9085c80 ps3dumpslb:11: 0x0000000010000000 0x0000136eafb0bc80 ps3dumpslb:12: 0x0000000000000000 0x0000a44d91430c80 ps3dumpslb:13: 0x00000000f0000000 0x0000af8508fc5c80 ps3dumpslb:14: 0x0000000010000000 0x0000a50cffa4bc80 ps3dumpslb:15: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:16: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:17: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:18: 0x0000000010000000 0x0000a50cffa4bc80 ps3dumpslb:19: 0x00000000f0000000 0x0000af8508fc5c80 ps3dumpslb:20: 0x0000000008000000 0x00005dd45172ec80 ps3dumpslb:21: 0x00000000f8000000 0x0000690bc92c3c80 ps3dumpslb:22: 0x0000000018000000 0x00005e93bfd49c80 ps3dumpslb:23: 0xd00007fff8000000 0x0000a70cf353a400 ps3dumpslb:24: 0xd000080088000000 0x0000adc7d4c2d400 ps3dumpslb:25: 0xd00007fff0000000 0x0000a70cf353a400 ps3dumpslb:26: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:27: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:28: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:29: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:30: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:31: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:32: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:33: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:34: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:35: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:36: 0xd00007fff0000000 0x0000a70cf353a400 ps3dumpslb:37: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:38: 0x0000000020000000 0x0000c45d3d9abc80 ps3dumpslb:39: 0x0000000010000000 0x0000c39dcf390c80 ps3dumpslb:40: 0x00000000f0000000 0x0000ce15d890ac80 ps3dumpslb:41: 0xd00007fff0000000 0x0000a70cf353a400 ps3dumpslb:42: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:43: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:44: 0xd00007fff0000000 0x0000a70cf353a400 ps3dumpslb:45: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:46: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:47: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:48: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:49: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:50: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:51: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:52: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:53: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:54: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:55: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:56: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:57: 0xd00007fff0000000 0x0000a70cf353a400 ps3dumpslb:58: 0xd000080080000000 0x0000adc7d4c2d400 ps3dumpslb:59: 0x0000000020000000 0x0000c45d3d9abc80 ps3dumpslb:60: 0x0000000010000000 0x0000c39dcf390c80 ps3dumpslb:61: 0x00000000f0000000 0x0000ce15d890ac80 ps3dumpslb:62: 0x0000000000000000 0x000012af414f0c80 ps3dumpslb:63: 0x00000000f0000000 0x00001de6b9085c80

