Probed unit: PS4 Fat (Aeolia CXD90025G)
Kernel: Linux 6.18.18-Strawberry-ThinLTO-LTS+ (CachyOS x86_64)
CPU: AMD Jaguar DG1002FGF84HT (8) @ 1.59 GHz — confirmed via fastfetch
Hostname: PlayStation4
Interface: /dev/icc via ioctl(0xC0284901)
| Module | Gist | Purpose |
|---|---|---|
ps4_icc_core.py |
https://gist.github.com/rmuxnet/34d828bc9832dd2582dc602c4f27ff20 | Low-level ioctl wrapper, constants, call_icc, call_clean |
ps4_icc_sysinfo.py |
https://gist.github.com/rmuxnet/e1d0fee9e06dbe7680effe395ac9ab72 | Major 0x02 — HW revision, EMC version, subsystem table, thermal slots |
ps4_icc_fan.py |
https://gist.github.com/rmuxnet/8648c7fa8e3ae0aef4e8114ae7661fbe | Major 0x0A / 0x0B — fan RMW threshold, live RPM, APU temp, trip points |
ps4_icc_led.py |
https://gist.github.com/rmuxnet/002a2ae105ffca290ada36a39d645f70 | Major 0x09 — full LED color table, payload builder, all effects |
ps4_icc_nvs.py |
https://gist.github.com/rmuxnet/fff41726596d7da0ba7196e099a8e8de | Major 0x01 — full 0x00–0xFF NVS directory sweep |
ps4_icc_i2c.py |
https://gist.github.com/rmuxnet/c2783c5a56a9955e08a5d572e466c017 | /dev/i2c-0 access — detect, read/write byte, I2CBus context manager |
ps4_icc_monitor.py |
https://gist.github.com/rmuxnet/105481ad0745806be4422a5ab5f3ab9b | CLI dispatcher tying all modules together |
All files must be in the same directory. ps4_icc_core.py is the only dependency the others import.
cat /proc/devices | grep icc # note the major number on the left
sudo mknod /dev/icc c <major> 0 # replace <major> with that number
sudo chmod 666 /dev/iccPermanent (survives reboots):
echo 'KERNEL=="icc", MODE="0666"' | sudo tee /etc/udev/rules.d/99-ps4-icc.rules
sudo udevadm control --reload-rules
sudo udevadm triggerFor /dev/i2c-0 (only needed for ps4_icc_i2c.py):
sudo modprobe i2c-dev
sudo chmod 666 /dev/i2c-0- Hardware Overview
- ICC Interface Specification
- Response Format and Status Codes
- Stale Buffer Hazard
- Temperature Encoding Reference
- Major Map Summary
- Major 0x01 — NVS Directory
- Major 0x02 — System Information
- Major 0x03 — Partial / No Extractable Data
- Major 0x04 — Power Management (DANGEROUS)
- Major 0x05 — Peripheral Power
- Major 0x06 — EAP-Gated Placeholder
- Major 0x07 / 0x08 — No Data / Power Button
- Major 0x09 — LED / Indicator
- Major 0x0A — Fan Control
- Major 0x0B — Device Power Info
- Major 0x10 — I2C Proxy
- Unimplemented Majors
- EMC UART Quick Reference
- EMC Firmware Patch — God Mode
- Confirmed Hardware Data
- Dead Ends
- Open Questions
- References
| PS4 Model | Southbridge | Part Number | EMC Core |
|---|---|---|---|
| PS4 Fat | Aeolia | CXD90025G | ARM Cortex-M3 r2p1, ~100 MHz |
| PS4 Slim | Belize | CXD90036G | ARM Cortex-M3 (protocol identical) |
| PS4 Pro / Late Slim | Baikal | CXD90042GG | ARM Cortex-M3 (protocol expected identical) |
All research conducted on PS4 Fat (Aeolia CXD90025G). Protocol confirmed identical on Belize.
CPU: AMD Jaguar (Liverpool / DG1002FGF84HT), 8-core @ 1.59 GHz, btver2
GPU: AMD GCN 1.1 (Liverpool integrated), ~800 MHz
Southbridge: Aeolia CXD90025G (probed unit)
EMC: ARM Cortex-M3 r2p1, ~100 MHz
Responsibilities: ICC, power sequencing, fan PID, UART debug,
syscon-internal NVS (distinct from sflash NVS areas)
EAP: Marvell PJ4C, ~500 MHz, FreeBSD 9
Responsibilities: network, HDD/optical in standby
Serial Flash: 256 MB Macronix MX25L25635FMI-10G
Southbridge RAM: 256 MB DDR3 (Fat / Slim), 1 GB DDR3 (Pro)
System RAM: 6.74 GiB
ICC = inter-chip communication between x86 APU and ARM EMC via shared memory,
managed by the ps4-apcie-icc.c kernel driver.
eb9e0130:b4081241:00000300
icc nvs device registered major 0x01
icc thermal info device registered major 0x02
icc fan info device registered major 0x0A
icc configuration info device registered major unknown
icc indicator device registered major 0x09
icc device_power info device registered major 0x0B
/dev/icc
_IOWR('I', 1, struct icc_cmd) = 0xC0284901
(3 << 30) | (40 << 16) | (0x49 << 8) | 1
0x00 u8 major
0x01 1B padding
0x02 u16 minor
0x04 4B padding
0x08 u64 data pointer
0x10 u16 data length
0x12 6B padding
0x18 u64 reply pointer
0x20 u16 reply length
0x22 6B padding
Python pack format: '<BxH4xQH6xQH6x' — 40 bytes confirmed.
Every ICC reply begins with a two-byte header:
byte[0] status code
byte[1] echo of major sent
byte[2+] payload (command-specific)
| Code | Name | Meaning |
|---|---|---|
0x00 |
OK | Success; payload from byte[2] |
0x01 |
UNK | Unknown minor for this major |
0x02 |
OK_DATA | Stub; real reply is 2 bytes only |
0x03 |
ALT_OK | Alternate success; same as OK on most majors |
0x04 |
NOT_IMPL | Not implemented in EMC firmware |
0x05 |
EAP_GATE | EAP-gated; major 0x06 only, not usable from Linux |
0x06 |
UNIQUE | Major 0x02/0x1B only; meaning TBD |
0x23 |
DEAD | Major 0x0A/0x04 only; zero data |
The ICC kernel driver does NOT zero the reply buffer between consecutive calls on the same file descriptor. Bytes beyond the real reply length retain data from the previous ioctl.
The most consequential example: 0x02/0x00 called on a shared fd after 0x02/0x01
consistently showed 00:d9:d1:58:81:82 (the Ethernet MAC) at bytes[4:10]. The
byte sequence reordered as 0x58D1D900 decoded to 2017-03-22 01:53:04 UTC — a
plausible timestamp that was documented as a live RTC field for some time. It is
not. The field is static and reads all-zeros under Linux.
Rule: Use call_clean() (fresh fd, first ioctl) for any command whose real reply
is short and contamination would be ambiguous: 0x02/0x00, 0x02/0x01, 0x02/0x07.
| Source | Format | Width | Example Raw | Decoded | Formula |
|---|---|---|---|---|---|
EMC UART tempr get |
8.8 fixed-point | u16 | 0x1E40 |
30.25 C | raw / 256.0 |
EMC UART etempr get |
8.8 fixed-point | u16 | 0x6000 |
96.0 C | raw / 256.0 |
| ICC 0x0A/0x08 reply[16:18] | 8.8 fixed-point LE | u16 | 0x3080 |
48.5 C | raw / 256.0 |
| ICC 0x0A/0x08 reply[8:12] | 16.16 fixed-point LE | u32 | 0x08200000 |
~2080 RPM | raw / 65536.0 |
| ICC 0x0B/0x01 reply[3] | Plain integer | u8 | 0x3E |
62 C | identity |
| ICC 0x0A/0x07 reply[5] | Plain integer | u8 | 0x50 |
80 C | identity |
| ICC 0x0B/0x05 reply[5,9] | Plain integer | u8 | 0x60 |
96 C | identity |
0xFFFFFFFF and 0x0FFFFFFF in the 16.16 RPM field both mean fan is idle. Report as 0.0.
UART MainSoc Alert 0x6000 / 256 = 96.0 C == ICC 0x0B/0x05 byte[5] = 96 CONFIRMED
UART MainSoc Critical 0x6100 / 256 = 97.0 C == ICC 0x0B/0x05 byte[9] = 97 CONFIRMED
| Major | Status Codes | Coverage | Notes |
|---|---|---|---|
| 0x00 | 04 |
None | Not implemented |
| 0x01 | 01, 02 |
Partial | NVS directory — metadata only, values RSA-gated |
| 0x02 | 00,01,02,03,06 |
Full | System info, HW rev, EMC version, thermal, capabilities |
| 0x03 | 00,01,03,04 |
Partial | OK replies on some minors; no extractable payload |
| 0x04 | not probed | DANGER | Power/reboot — shuts down in 3 seconds |
| 0x05 | 00,01,02 |
Partial | Peripheral power (BT/WLAN/USB) |
| 0x06 | 00,01,04,05 |
Dead end | EAP-gated; payload-invariant from Linux |
| 0x07 | 00,01,02 |
None | No data bytes on any minor |
| 0x08 | 00,01 |
Partial | Power button state |
| 0x09 | 00,01 |
Full | LED/indicator — fully mapped |
| 0x0A | 00,01,02,04,35 |
Full | Fan control — fully mapped |
| 0x0B | 00,01 |
Full | Device power info — fully mapped |
| 0x0C–0x0F | 04 |
None | Not implemented |
| 0x10 | 00,01 |
Full (via i2c-dev) | I2C proxy — use /dev/i2c-0, not raw ICC |
| 0x11 | 00,01,02 |
None | Stubs only |
| 0x12–0x14 | 04 |
None | Not implemented |
| 0x1F | 04 |
None | Not implemented |
Python: https://gist.github.com/rmuxnet/fff41726596d7da0ba7196e099a8e8de
The NVS accessed here is syscon's internal flash — distinct from the EMC-managed
sflash NVS areas (csarea, dsarea, osarea, pdarea). Syscon runs ICC over SPI
(SPI + interrupt line) and exposes its index through major 0x01.
Major: 0x01 Minor: 0x00 Payload: bytes([idx])
byte[0] 0x00 (OK) if populated, 0x02 (stub) if empty
byte[2] 0x01 if entry exists
byte[3] type code
byte[4] index echo
byte[5+] always 0x00 — value NOT returned (RSA-gated)
| Index | Type | Size | Known Content |
|---|---|---|---|
| 0x01 | 0x00 | 1B | unknown scalar |
| 0x02 | 0x06 | 6B | Wi-Fi MAC address |
| 0x03 | 0x00 | 1B | unknown scalar |
| 0x04 | 0x21 | 33B | Serial number (32 ASCII + null) |
| 0x05 | 0x01 | 1B | boolean flag |
| 0x06–0x08 | 0x00 | 1B | unknown scalars |
| 0x09 | 0x01 | 1B | boolean flag |
| 0x0A | 0x02 | 2B | unknown u16 |
| 0x0B | 0x01 | 1B | boolean flag |
| 0x10 | 0x01 | 1B | boolean flag |
| 0x11 | 0x00 | 1B | unknown scalar |
| 0x8C | 0x00 | 1B | factory calibration |
| 0x8D | 0x00 | 1B | factory calibration |
| Code | Meaning |
|---|---|
| 0x00 | u8 scalar |
| 0x01 | boolean flag |
| 0x02 | u16 little-endian |
| 0x06 | 6-byte blob (MAC / BD address format) |
| 0x21 | 33-byte null-terminated ASCII string |
Values are gated behind RSA authentication (uareq1 / uareq2 on EMC UART).
Exhaustive ICC probing confirmed no bypass: 256×256 payload sweep, all 256 minors,
7 format strategies, pre-auth sequences — zero value bytes returned under any condition.
| Entry | Recovery Path |
|---|---|
| Wi-Fi MAC | /sys/class/net/mlan0/address |
| Ethernet MAC | /sys/class/net/enp0s20f1/address |
| BD address | Stale buffer from 0x02/0x01 |
| Serial number | EMC UART dsarea after uareq1/uareq2 |
Python: https://gist.github.com/rmuxnet/e1d0fee9e06dbe7680effe395ac9ab72
| Minor | Status | Real Reply | Description |
|---|---|---|---|
| 0x00 | 00 |
4 bytes | Last Orbis boot timestamp (static under Linux) |
| 0x01 | 00 |
4 bytes | Hardware revision |
| 0x04 | 02 |
2 bytes | Stub |
| 0x05 | 00 |
10 bytes | Subsystem capability table |
| 0x06 | 00 |
16 bytes | EMC firmware version |
| 0x07 | 00 |
4 bytes | Thermal sensor slots |
| 0x09 | 00 |
6 bytes | Capability flag byte[5]=0x10 |
| 0x0B, 0x0C | 02 |
2 bytes | Stubs |
| 0x0F, 0x10 | 03 |
2 bytes | ALT_OK, no data |
| 0x1B | 06 |
2 bytes | Unique status, TBD |
Real reply: 4 bytes, all zeros under Linux. This field is written by the Orbis kernel at boot and is never updated by Linux. Not a live RTC counter.
The "Unix timestamp" 0x58D1D900 (2017-03-22) seen in early research was stale
buffer contamination: the Ethernet MAC 00:d9:d1:58:81:82 from a preceding
0x02/0x01 call on the same fd. Always call on a fresh fd as the first ioctl.
Real reply: 4 bytes. byte[3] = 0x02 on probed unit. Bytes[4:10] on a shared fd
contain the BD address 8c:9c:39:9f:93:f4 as stale buffer from EMC shared memory.
bytes[2:10] — version per subsystem in Orbis boot registration order:
[0] nvs v3 major 0x01
[1] thermal_info v2 major 0x02
[2] fan_info v2 major 0x0A
[3] config_info v1 major unknown
[4] indicator v1 major 0x09
[5] device_power v1 major 0x0B
[6] unknown_6 v5 unidentified — highest version of any subsystem
[7] unknown_7 v1 unidentified
byte[14] = 0x17 = 23 (major)
byte[15] = 0x12 = 18 (minor) -> v23.18
UART version returns 1.19.0 E r4336 — different numbering, same firmware.
Real reply: 4 bytes. byte[3] = 4 (slot count). All slots 0xFF (unpopulated).
APU temperature is read via 0x0B/0x01, not external sensor ICs.
Extremely stale-buffer sensitive — call on fresh fd as first ioctl.
Status codes seen: 0x00, 0x01, 0x03, 0x04. Some minors return OK but all
confirmed payload bytes are either 2-byte stubs or stale buffer artifacts. No
structured payload has been reliably extracted. Possibly corresponds to the
configuration info ICC device in the Orbis boot log.
DO NOT PROBE. Sending any valid command initiates shutdown or reboot in ~3 seconds.
The kernel driver calls WARN_ON(1). No cancel path once accepted by EMC.
| Minor | Payload | Function |
|---|---|---|
| 0x01 | 00 00 02 00 01 00 |
SHUTDOWN |
| 0x01 | 00 01 02 00 01 00 |
REBOOT |
| Minor | Function | Payload |
|---|---|---|
| 0x00 | BT/WLAN power | 0x02=OFF, 0x03=ON |
| 0x01 | BT/WLAN status | — |
| 0x10 | USB power | 0x00=OFF, 0x01=ON |
BT/WLAN are on a single power rail (Marvell 88W8897). Toggling while a network interface is active will drop the interface.
All minors 0x00–0x2F probed with all payload variants. Completely payload-invariant. Functional only during EAP (FreeBSD standby) sessions. Dead end from Linux.
0x07: Returns 0x00, 0x01, or 0x02 on various minors. No payload bytes
beyond the 2-byte header. Meaning unknown.
0x08 (Power Button): Returns 0x00 or 0x01. Short payload may encode power
button state but stale buffer contamination prevents reliable decoding.
Python: https://gist.github.com/rmuxnet/002a2ae105ffca290ada36a39d645f70
Major: 0x09 Minor: 0x20 Payload: 35 bytes Reply length: 0x30 bytes
Success: reply[0] == 0x00
0x10 Blue
0x11 White
0x02 Orange (3-operation timed sequence)
Red is not achievable. No red LED element exists on any PS4 hardware revision.
bytes [0-3] header: 03 01 00 00
bytes [4-10] Blue: 10 01 02 [BLUE_VAL] 02 01 00
bytes [11-17] White: 11 01 02 [WHITE_VAL] 02 01 00
bytes [18-19] Orange header: 02 03
bytes [20-34] Orange: 3 operations x 5 bytes each
byte[0] opcode: 0x01=FADE-FROM, 0x02=HOLD/FADE-TO
0x03, 0x04 accepted, visual TBD
byte[1] value: 0x00=off, 0xFF=full
byte[2] timing_hi
byte[3] timing_lo
byte[4] flags: 0xFF=loop forever, 0x00=one-shot, 0xBF=loop with hold
| Program | Bytes | Used For |
|---|---|---|
| OFF | 01 00 04 01 bf 02 00 05 01 ff 02 00 05 01 ff |
off, blue, white, white_blue |
| ON | 02 ff 02 01 00 02 ff 05 01 ff 02 ff 05 01 00 |
orange, violet_blue, pink, pink_blue |
| COMBINED | 01 00 04 01 bf 02 ff 05 01 ff 02 00 05 01 ff |
orange_blue, orange_white, orange_white_blue |
| PULSATE | 01 ff 04 01 00 02 ff 05 01 ff 02 ff 05 01 00 |
pulsate_orange |
| Effect | BLUE_VAL | WHITE_VAL | Orange |
|---|---|---|---|
| off | 0x00 | 0x00 | OFF |
| blue | 0xFF | 0x00 | OFF |
| white | 0x00 | 0xFF | OFF |
| white_blue | 0xFF | 0xFF | OFF |
| orange | 0x00 | 0x00 | ON |
| orange_blue | 0xFF | 0x00 | COMBINED |
| orange_white | 0x00 | 0xFF | COMBINED |
| orange_white_blue | 0xFF | 0xFF | COMBINED |
| violet_blue | 0x57 | 0x00 | ON |
| pink | 0x00 | 0x30 | ON |
| pink_blue | 0x20 | 0x00 | ON |
| pulsate_orange | 0x00 | 0x00 | PULSATE |
Orange channel at timing (0x00, 0x01) causes monitor dropout via voltage noise
on a shared power rail. Hardware electrical issue — not bus congestion (200 safe
ioctls/sec with no dropout confirmed). Minimum safe timing: (0x01, 0x01).
Python: https://gist.github.com/rmuxnet/8648c7fa8e3ae0aef4e8114ae7661fbe
| Minor | Function |
|---|---|
| 0x06 | Write fan threshold (RMW required) |
| 0x07 | Read fan config (52 bytes) |
| 0x08 | Live RPM + APU temp |
| 0x04 | Status 0x23 dead end |
Writing fewer than all 52 bytes, or zero-filling any byte, corrupts the EMC PID
configuration. The fan permanently reports 0 RPM until a hard power cycle (not
reboot). This was the root bug in the legacy ps4fancontrol tool: 6-byte allocation
with 52-byte length, zeroing 46 bytes including the PID flags.
Always use write_fan_threshold() from ps4_icc_fan.py. Never write raw config.
byte[5] temperature threshold in degrees C (plain u8)
byte[9] 0x08 PID integral gain flag -- MUST preserve
byte[12] 0x80 PID prop. gain flag -- MUST preserve
Stock config at 80 C (probed unit):
0x00: 00 00 00 00 00 50 00 00 00 08 00 00 80 00 00 00
0x10: ff ff ff 0f ff ff ff 0f ff ff ff 0f 00 00 00 00
0x20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x30: 00 00 00 00
byte[5] tachometer ADC raw (~232 at idle) — NOT a duty cycle %
bytes[8:12] RPM, u32 LE, 16.16 fixed-point (/ 65536.0)
0xFFFFFFFF or 0x0FFFFFFF = idle sentinel -> report 0.0
bytes[16:18] APU temp, u16 LE, 8.8 fixed-point (/ 256.0)
Tachometer updates ~1 Hz. RPM climbs slowly under load (PID integral windup — intentional). ~10 seconds to spin back down after threshold is raised.
SetVal=0x00005000 PGain=0x00000800 IGain=0x00000080 ILimit=0x0FFFFFFF
Valid threshold range: 45–85 C. Hardware default: 79 C (0x4F).
Python: https://gist.github.com/rmuxnet/8648c7fa8e3ae0aef4e8114ae7661fbe
Real reply: 20 bytes. Bytes beyond byte 19 are stale (LED payload from recent
0x09/0x20 commonly appears there).
reply[3] = APU temperature in degrees C, plain u8, no conversion.
Cross-validated against k10temp hwmon under stress-ng load.
payload [0x00] APU domain:
byte[5] = 96 C alert threshold
byte[7] = 0x02 domain count
byte[9] = 97 C EMC shutdown threshold
payload [0x01] domain 1 — untested (expected LPDDR4)
payload [0x02] GPU domain:
byte[9] = 72 C GPU shutdown threshold
READ-ONLY from ICC. EMC ignores write attempts. Values written by Orbis kernel at boot from NVRAM; fixed for the session.
Python: https://gist.github.com/rmuxnet/c2783c5a56a9955e08a5d572e466c017
The ICC driver implements I2C framing internally and registers a standard Linux adapter:
i2c i2c-0: adapter [icc] registered
i2c_dev: adapter [icc] registered as minor 0
Do not use raw ICC ioctls for I2C. All raw format attempts return zeros.
Use /dev/i2c-0 via i2c-dev instead:
i2cdetect -y 0
i2cget -y 0 0x60 0x9E
i2cset -y 0 0x60 0x9E 0x00Known I2C peripherals on the Aeolia bus (not yet enumerated via i2cdetect):
- Voltage Regulator Module (VRM) — likely near 0x60
- Power management IC
- Possibly HDMI repeater chip
i2cdetect -y 0 is the highest-priority remaining open investigation.
Return 0x04 (NOT_IMPL) on every minor — full 0–255 sweep confirmed:
0x00, 0x0C–0x0F, 0x12–0x14, 0x1F
115200 8N1
MMIO 0xd0340000 — DO NOT read via /dev/mem (machine check + instant poweroff)
Physical serial pads on motherboard
Auth: uareq1 (challenge) / uareq2 (RSA response) — required for all NVS reads.
| Command | Function | Auth |
|---|---|---|
tempr get |
SoC + exhaust temps, 8.8 fixed-point | No |
etempr get |
Trip point thresholds, 8.8 fixed-point | No |
fduty get |
Fan duty 0–1024 (÷1024×100=%) | No |
fservo get |
Full PID parameters | No |
fsstate get |
Live fan servo state (both loops) | No |
flimit get |
Fan duty limits (max/min per loop) | No |
devpm |
Device power states (wlan/hdd/usb/bd/hdmi/gbe) | No |
rtc |
RTC counter | No |
state |
System state | No |
powupcause |
Last power-up cause | No |
version |
EMC firmware version (1.19.0 E r4336) |
No |
scversion |
Syscon version (1.0.0 ET r1808) |
No |
getmacadr |
Wi-Fi MAC (direct NVS read) | Yes |
socuid |
SoC UID | Yes |
csarea |
Serial Flash NVS CS Area dump | Yes |
dsarea |
Serial Flash NVS DS Area (serial / socuid) | Yes |
osarea |
Serial Flash NVS OS Area | Yes |
pdarea |
Serial Flash NVS PD Area | Yes |
nvscsum |
NVS checksum | Yes |
stinfo |
Statistics from NVS | Yes |
uareq1 / uareq2 |
RSA auth handshake | — |
The EMC firmware enforces privilege levels via a permission byte in the command dispatch table. Patching grants unconditional access regardless of auth state.
Change all: 03 00 FD 00 -> 0F 00 FD 00
Change all: 07 00 FD 00 -> 0F 00 FD 00
Back up Serial Flash before patching. The EMC has no known external recovery mode if its firmware is corrupted. Wrong commands after unlocking can brick permanently.
Unit: PS4 Fat (CUH-1xxx)
Southbridge: Aeolia CXD90025G
Chip revision: eb9e0130:b4081241:00000300
EMC firmware (ICC): v23.18 (byte[14]=0x17, byte[15]=0x12)
EMC firmware (UART):1.19.0 E r4336
Syscon version: 1.0.0 ET r1808
HW revision: 0x02 (0x0B/0x01 reply byte[3])
Active flash bank: Second (UART: sb)
Kernel: Linux 6.18.18-Strawberry-ThinLTO-LTS+ (CachyOS x86_64)
Memory: 5.71 GiB / 6.74 GiB
Network: enp0s20f1 @ 192.168.1.116/24
Fan threshold: 80 C (byte[5]=0x50)
Hardware default: 79 C (0x4F)
PID flag [9]: 0x08 (intact)
PID flag [12]: 0x80 (intact)
APU alert: 96 C (0x0B/0x05 domain 0 byte[5])
APU shutdown: 97 C (0x0B/0x05 domain 0 byte[9])
GPU shutdown: 72 C (0x0B/0x05 domain 2 byte[9])
Wi-Fi MAC: e8:61:7e:f8:ea:b7 (/sys/class/net/mlan0/address)
Ethernet MAC: 00:d9:d1:58:81:82 (/sys/class/net/enp0s20f1/address)
BD address: 8c:9c:39:9f:93:f4 (stale buffer from 0x02/0x01)
Serial: not ICC-accessible (syscon NVS RSA-gated)
Last Orbis boot: 2017-03-22 01:53:04 UTC (frozen in EMC NVRAM)
Thermal slots: 4 configured, 0 populated (all 0xFF)
| Target | Reason | Consequence |
|---|---|---|
| Major 0x04 | Power/reboot management | System shuts down in 3 seconds |
| Major 0x06 all minors | EAP-gated, payload-invariant | No data |
| Major 0x07 all minors | No data bytes returned | No data |
| 0x0A/0x04 | Status 0x23, zero data | Dead end |
| NVS values via ICC | RSA auth gate — no bypass exists | Zero value bytes |
/dev/mem at 0xd0340000+ |
EMC MMIO | Machine check + instant poweroff |
| Raw ICC ioctls for I2C | Kernel handles framing | Returns zeros — use /dev/i2c-0 |
-
/dev/i2c-0bus enumeration —i2cdetect -y 0not yet run. VRM and other Aeolia-attached I2C devices may be fully accessible. Highest-priority remaining item. -
NVS serial number — not ICC-accessible without RSA auth. Recoverable via EMC UART
dsareaor hardware flash programmer. -
0x02/0x05 unknown subsystems (v5 and v1) — two unidentified subsystems in the capability table.
unknown_6at version 5 is notable. Hypotheses: majors in the 0x0C–0x1F NOT_IMPL range that are functional from Orbis but not Linux, or EAP-side only. -
LED opcodes 0x03 and 0x04 — accepted by EMC (returns OK_DATA) but visual behavior not yet observed.
-
0x0B/0x05 domain 1 (payload
[0x01]) — trip points for power domain 1 not yet queried. Likely LPDDR4 memory domain. -
Major 0x03 fresh-fd sweep — some minors return status 0x00 but real reply may be a 2-byte stub indistinguishable from stale buffer. Fresh-fd probing of all 0x03 minors not completed.
Kernel drivers:
drivers/ps4/ps4-apcie-icc.c ICC character device + ioctl
drivers/ps4/ps4-fan.c hwmon: temp1_input, temp1_crit (RMW), fan1_input
drivers/ps4/ps4-led.c LED class, 11 sysfs nodes, thermal trigger
Community:
psdevwiki.com/ps4/Southbridge EMC UART command reference
psdevwiki.com/ps4/Bootprocess Orbis boot log with ICC device names
psdevwiki.com/ps4/Serial_Flash NVS area layout
Projects:
ps4fancontrol (Ps3itaTeam) original fan tool — RMW bug documented here
ps4-kexec linux_boot.c Aeolia MMIO map
Belize ps4-linux led_config (Saya) LED payload format confirmed
Hardware:
Macronix MX25L25635FMI-10G Serial Flash datasheet
ARM Cortex-M3 r2p1 TRM EMC core reference
AMD btver2 (Jaguar) BKDG APU temp sensor details
Probed on PS4 Fat (Aeolia CXD90025G), Linux 6.18.18-Strawberry-ThinLTO-LTS+, CachyOS x86_64.
Protocol confirmed compatible with Belize (CXD90036G). Expected compatible with Baikal (CXD90042GG).