Compare commits

..

21 Commits

Author SHA1 Message Date
Mateico
01436f0a6b some tidy up 2026-05-13 09:37:44 +02: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
buzzer-re
6ac9bab944 Add direct "x86_64-linux-gnu-*" if running on ARM64 Linux machines 2026-04-29 23:53:05 -03:00
25 changed files with 570 additions and 317 deletions

6
.gitignore vendored Normal file
View File

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

114
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.xx or 4.xx firmwares** (and soon also on **firmware 6.02**) 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
- **Soon: 6.02** with M2 support
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
@@ -157,6 +140,12 @@ cd ps5-linux-loader
make
```
If you're on ARM64 Linux, then additionall install the x86-64 cross-compilation tools before:
```bash
sudo apt install gcc-x86-64-linux-gnu binutils-x86-64-linux-gnu
```
Find your PS5 IP at `Settings → Network → View Connection Status`.
```bash
@@ -167,11 +156,13 @@ If all is successful, the payload will automatically go into rest mode. Wait unt
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.
@@ -182,36 +173,51 @@ Then, there are certain settings and commands we recommend doing:
2. Possibly, you have to disable and reenable your Wired/WLAN connection to get internet connection.
3. Install Firefox:
3. Hold packages to prevent updating the kernel when doing `apt upgrade`:
```bash
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
```
4. Install Firefox:
```bash
sudo snap install firefox
sudo snap refresh mesa-2404 --channel=latest/edge
```
4. Clone our [ps5-linux-tools](https://github.com/ps5-linux/ps5-linux-tools):
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
```
## 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 format it on your PS5.
2. Boot Linux on your PS5 and 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
@@ -227,7 +233,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
@@ -235,7 +243,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
```
@@ -248,24 +255,27 @@ Always turn on fan when your turn on boost, as this is what the official 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?
- Q: Will higher >=6.50 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.

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,3 +1,6 @@
#ifndef LOADER_H
#define LOADER_H
#include "utils.h"
#include <stdint.h>
@@ -8,3 +11,5 @@ 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 +0,0 @@
#ifndef MAIN_H
#define MAIN_H
int main(void);
int setup_env(void);
int prepare_resume(void);
#endif

View File

@@ -35,6 +35,8 @@ typedef struct _offset_list {
uint64_t KERNEL_UART_OVERRIDE;
uint64_t KERNEL_DEBUG_PATCH;
uint64_t KERNEL_CFI_CHECK;
uint64_t PS5_WIFI_FW_OFFSET;
uint64_t PS5_WIFI_FW_SIZE;
} offset_list;
extern offset_list off_0300;
@@ -47,4 +49,4 @@ extern offset_list off_0403;
extern offset_list off_0450;
extern offset_list off_0451;
#endif
#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);
int update_sck_data_ptr (void* sc, uint64_t dest_text, uint64_t dest_data);
void hook_call_near(uint64_t hook, uint64_t dst);
void prepare_sck_args(uint64_t dest_data);
#endif

View File

@@ -42,6 +42,7 @@ struct linux_info {
size_t initrd_size;
size_t vram_size;
char cmdline[2048];
int kit_type;
uintptr_t linux_info; // PA of linux_info
};
@@ -55,6 +56,18 @@ 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);
}
@@ -156,4 +169,17 @@ void enter_rest_mode(void);
#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);
#endif

View File

@@ -3,9 +3,16 @@ ifndef PS5_PAYLOAD_SDK
endif
# 1. Variables
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
CFLAGS = -O2 -fno-stack-protector -ffreestanding -nostdlib -fcf-protection=none -I$(PS5_PAYLOAD_SDK)/target/include
OBJCOPY = objcopy
endif
CFLAGS = -O2 -fno-stack-protector -ffreestanding -nostdlib -fcf-protection=none -m64 -I$(PS5_PAYLOAD_SDK)/target/include
LDFLAGS = -T linker.ld
TARGET = shellcode_hypervisor.elf
TEXT_BIN = shellcode_hypervisor.bin
@@ -23,7 +30,7 @@ $(TARGET): $(OBJ)
$(CC) $(CFLAGS) -c $< -o $@
$(TEXT_BIN): $(TARGET)
objcopy -O binary -j .shell_code $(TARGET) $(TEXT_BIN)
$(OBJCOPY) -O binary -j .shell_code $(TARGET) $(TEXT_BIN)
clean:
rm -f $(OBJ) $(TARGET) $(TEXT_BIN) $(dump)

View File

@@ -12,6 +12,7 @@ struct linux_info {
size_t initrd_size;
size_t vram_size;
char cmdline[2048];
int kit_type;
};
static struct linux_info info;
@@ -88,8 +89,16 @@ 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);
}
}
void boot_linux(void) {
@@ -124,7 +133,7 @@ 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 +168,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

@@ -37,7 +37,16 @@
#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);
void boot_linux(void);
enum kit_type {
KIT_RETAIL,
KIT_TESTKIT,
KIT_DEVKIT
};

View File

@@ -6,8 +6,6 @@
__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;
@@ -17,12 +15,10 @@ __attribute__((section(".entry_point"), naked)) uint32_t main(void) {
cpu_id = (ebx >> 24) & 0xFF;
// We point to a location after the main linux boot code
// Each CPU should have a different location
// Each CPU should have a different stack
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"
:

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
uint64_t bzimage_pa;
uint64_t initrd_pa;
uint64_t linux_info_pa;
} shellcode_hypervisor_args;

View File

@@ -96,12 +96,10 @@ void wrmsr(uint32_t msr, uint64_t val) {
__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);
}

View File

@@ -3,9 +3,16 @@ ifndef PS5_PAYLOAD_SDK
endif
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
CFLAGS = -O2 -fno-stack-protector -ffreestanding -nostdlib -I$(PS5_PAYLOAD_SDK)/target/include
OBJCOPY = objcopy
endif
CFLAGS = -O2 -fno-stack-protector -ffreestanding -nostdlib -m64 -I$(PS5_PAYLOAD_SDK)/target/include
LDFLAGS = -T linker.ld
TARGET = shellcode_kernel.elf
TEXT_BIN = shellcode_text.bin
@@ -24,7 +31,7 @@ $(TARGET): $(OBJ)
$(CC) $(CFLAGS) -c $< -o $@
$(TEXT_BIN): $(TARGET)
objcopy -O binary -j .text $(TARGET) $(TEXT_BIN)
$(OBJCOPY) -O binary -j .text $(TARGET) $(TEXT_BIN)
clean:
rm -f $(OBJ) $(TARGET) $(TEXT_BIN) $(dump)

View File

@@ -9,11 +9,11 @@
#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 {
uint8_t lanenum;
@@ -46,6 +46,7 @@ struct linux_info {
size_t initrd_size;
size_t vram_size;
char cmdline[2048];
int kit_type;
};
static struct linux_info info;
@@ -94,39 +95,31 @@ static int mp3_enable_output(int be, int mode) {
static void patch_hv(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 l40_l3_addr = PAGE_PA(identity_pml4_0); // addr PML4[0]
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);
uint64_t identity_pml40_l3[] = {
0x0000000000000083, // P, RW, US=0 - 0 GB to 1 GB
0x0000000040000083, // P, RW, US=0 - 1 GB to 2 GB
0x0000000080000083, // P, RW, US=0 - 3 GB to 3 GB
0x00000000C0000083, // P, RW, US=0 - 4 GB to 4 GB
0x0000000100000083 // P, RW, US=0 - 5 GB to 6 GB --> Our paging structure
0x0000000000000083,
0x0000000040000083,
0x0000000080000083,
0x00000000C0000083,
0x0000000100000083
};
uint64_t l3_size = sizeof(identity_pml40_l3) / sizeof(identity_pml40_l3[0]);
// Create the map in memory
*(uint64_t *)PHYS_TO_DMAP(identity_cr3) = identity_pml4_0;
for (uint64_t i = 0; i < l3_size; i++) {
*(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);
// 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
0x48, 0xC7, 0xC0, 0x00, 0x6F, 0x80, 0x62,
0xFF, 0xE0, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
0xC3, 0xC3};
// Update code cave in hv 1:1 region
*(uint32_t *)(&shellcode_jmp[3]) = (uint32_t)args.hv_code_cave_pa;
// Just patch the VMEXIT handler directly, avoiding all checks
@@ -142,12 +135,9 @@ static void patch_hv(void) {
0xFF, 0xE0 // jmp rax
};
// Update CR3 PA (from config)
*(uint64_t *)(&shellcode_identity_and_jmp[2]) = cave_hv_paging;
// 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
memcpy((void *)PHYS_TO_DMAP(args.hv_code_cave_pa), shellcode_identity_and_jmp,
sizeof(shellcode_identity_and_jmp));
}

View File

@@ -3,19 +3,11 @@
#include "utils.h"
#include <stdint.h>
#define MSR_EFER 0xC0000080
shellcode_kernel_args args = {0};
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
@@ -24,12 +16,10 @@ __attribute__((section(".entry_point"))) uint32_t main(uint64_t add1,
// "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;
@@ -45,7 +35,6 @@ __attribute__((section(".entry_point"))) uint32_t main(uint64_t add1,
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);
@@ -65,7 +54,6 @@ __attribute__((section(".entry_point"))) uint32_t main(uint64_t add1,
goto out;
}
// Wait for completion
ret = ((uint64_t(*)(void))args_ptr->fun_hv_iommu_wait_completion)();
if (ret == 0) {
@@ -83,7 +71,6 @@ __attribute__((section(".entry_point"))) uint32_t main(uint64_t add1,
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');
@@ -104,7 +91,6 @@ __attribute__((section(".entry_point"))) uint32_t main(uint64_t add1,
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');
@@ -145,9 +131,6 @@ __attribute__((section(".entry_point"))) uint32_t main(uint64_t add1,
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");
@@ -186,36 +169,28 @@ __attribute__((noinline, optimize("O0"), naked)) void vmmcall_dummy(void) {
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
IOMMU_MMIO_CB_TAIL / 8);
uint64_t next_tail = (curr_tail + IOMMU_CMD_ENTRY_SIZE) & IOMMU_CB_MASK;
// 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)
(uint64_t *)args_ptr->iommu_cb2_va + curr_tail / 8;
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
next_tail;
// 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};
@@ -230,26 +205,22 @@ __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
0x0000000000000000ULL);
iommu_write8_pa(args_ptr, pa + 0x08,
0x0004000000000000ULL); // Clear all intercepts of except.
// vectors but CPUID
0x0004000000000000ULL);
iommu_write8_pa(args_ptr, pa + 0x10,
0x000000000000000FULL); // Clear all except VMMCALL, VMLOAD,
// VMSAVE, VMRUN
0x000000000000000FULL);
iommu_write8_pa(args_ptr, pa + 0x58,
0x0000000000000001ULL); // Guest ASID ... 1 ?
0x0000000000000001ULL);
iommu_write8_pa(args_ptr, pa + 0x90,
0x0000000000000000ULL); // Disable NP_ENABLE
0x0000000000000000ULL);
}
}
__attribute__((noinline, optimize("O0"))) uint32_t tmr_read(uint64_t dmap,
uint32_t addr) {
__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);
}
@@ -260,8 +231,6 @@ tmr_write(uint64_t dmap, uint32_t addr, uint32_t val) {
*(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) {
@@ -282,7 +251,6 @@ void init_global_pointers(volatile shellcode_kernel_args *args_ptr) {
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;

View File

@@ -16,9 +16,9 @@ void memcpy(void *dest, void *src, uint64_t len) {
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
: "=r"(cr3)
:
:
);
return cr3;
}

145
source/firmware.c Normal file
View File

@@ -0,0 +1,145 @@
#include "firmware.h"
#include "utils.h"
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.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

@@ -217,8 +217,7 @@ int stage4_force_vmcb_reload(void) {
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 start = ktext;
uint64_t end = kdata;
int n __attribute__((unused)) = 0;

View File

@@ -1,5 +1,6 @@
#include "loader.h"
#include "config.h"
#include "firmware.h"
#include "utils.h"
#include <errno.h>
#include <fcntl.h>
@@ -79,11 +80,52 @@ 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);
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;
overrides_start[size] = 0; // make sure the last string is null-terminated
}
}
}
if (state == 1) // overrides not found, or unreadable, or currently looking for it
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++) {
@@ -186,6 +228,10 @@ int fetch_linux(struct linux_info *info) {
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);
@@ -220,6 +266,7 @@ int fetch_linux(struct linux_info *info) {
info->initrd_size = initrd_size;
info->vram_size = vram_size;
strcpy(info->cmdline, cmdline);
info->kit_type = (int) get_kit_type();
uint64_t page = alloc_page();
kwrite(pa_to_dmap(page), info, sizeof(struct linux_info));

View File

@@ -1,11 +1,8 @@
#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>
#include "utils.h"
#include "hv_defeat.h"
#include "prepare_resume.h"
#include "loader.h"
int main(void) {
@@ -36,143 +33,9 @@ int main(void) {
sleep(5);
enter_rest_mode();
while (1) {
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;
}

View File

@@ -5,8 +5,8 @@ offset_list off_0300 = {
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
@@ -30,6 +30,8 @@ offset_list off_0300 = {
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x0752460,
.KERNEL_CFI_CHECK = 0x0441DD0,
.PS5_WIFI_FW_OFFSET = 0x1274460,
.PS5_WIFI_FW_SIZE = 492304,
};
offset_list off_0310 = {
@@ -37,8 +39,8 @@ offset_list off_0310 = {
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
@@ -62,6 +64,8 @@ offset_list off_0310 = {
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x07524A0,
.KERNEL_CFI_CHECK = 0x0441E10,
.PS5_WIFI_FW_OFFSET = 0x1274490,
.PS5_WIFI_FW_SIZE = 492304,
};
offset_list off_0320 = {
@@ -69,8 +73,8 @@ offset_list off_0320 = {
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
@@ -94,6 +98,8 @@ offset_list off_0320 = {
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x7527F0,
.KERNEL_CFI_CHECK = 0x442160,
.PS5_WIFI_FW_OFFSET = 0x1274550,
.PS5_WIFI_FW_SIZE = 492304,
};
offset_list off_0321 = {
@@ -101,8 +107,8 @@ offset_list off_0321 = {
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
@@ -126,6 +132,8 @@ offset_list off_0321 = {
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x7527F0,
.KERNEL_CFI_CHECK = 0x442160,
.PS5_WIFI_FW_OFFSET = 0x1274550,
.PS5_WIFI_FW_SIZE = 492304,
};
offset_list off_0400 = {
@@ -133,8 +141,8 @@ offset_list off_0400 = {
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
@@ -158,6 +166,8 @@ offset_list off_0400 = {
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DA70,
.KERNEL_CFI_CHECK = 0x45A170,
.PS5_WIFI_FW_OFFSET = 0x1392FB0,
.PS5_WIFI_FW_SIZE = 493000,
};
offset_list off_0402 = {
@@ -165,8 +175,8 @@ offset_list off_0402 = {
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
@@ -190,6 +200,8 @@ offset_list off_0402 = {
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DAC0,
.KERNEL_CFI_CHECK = 0x45A170,
.PS5_WIFI_FW_OFFSET = 0x1392FB0,
.PS5_WIFI_FW_SIZE = 493000,
};
offset_list off_0403 = {
@@ -198,7 +210,7 @@ offset_list off_0403 = {
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
@@ -222,6 +234,8 @@ offset_list off_0403 = {
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DB50,
.KERNEL_CFI_CHECK = 0x45A170,
.PS5_WIFI_FW_OFFSET = 0x1392FB0,
.PS5_WIFI_FW_SIZE = 493000,
};
offset_list off_0450 = {
@@ -229,8 +243,8 @@ offset_list off_0450 = {
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
@@ -254,6 +268,8 @@ offset_list off_0450 = {
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DC80,
.KERNEL_CFI_CHECK = 0x45A1A0,
.PS5_WIFI_FW_OFFSET = 0x1392FC0,
.PS5_WIFI_FW_SIZE = 493000,
};
offset_list off_0451 = {
@@ -261,8 +277,8 @@ offset_list off_0451 = {
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
@@ -286,4 +302,6 @@ offset_list off_0451 = {
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DC90,
.KERNEL_CFI_CHECK = 0x45A1A0,
.PS5_WIFI_FW_OFFSET = 0x1393000,
.PS5_WIFI_FW_SIZE = 493000,
};

101
source/prepare_resume.c Normal file
View File

@@ -0,0 +1,101 @@
#include <stdio.h>
#include <stdint.h>
#include "prepare_resume.h"
#include "iommu.h"
#include "../shellcode_kernel/shellcode_kernel.h"
#include "offsets.h"
#include "utils.h"
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;
kwrite_large(dest_text, shellcode_kernel_text, shellcode_kernel_text_len);
prepare_sck_args(dest_data);
if(update_sck_data_ptr(shellcode_kernel_text, dest_text, dest_data))
return -1;
hook_call_near(ktext + env_offset.HOOK_ACPI_WAKEUP_MACHDEP, dest_text);
kwrite8(ktext + env_offset.KERNEL_DEBUG_PATCH, 0xC3);
kwrite8(ktext + env_offset.KERNEL_CFI_CHECK, 0xC3);
return 0;
}
int update_sck_data_ptr (void* sc, uint64_t dest_text, uint64_t dest_data) {
// Find the address 0x11AA11AA11AA11AA used as marker
int offset = -1;
for (int i = 0; i < 0x40; i++) {
if (*(uint64_t *)((uint64_t)sc + i) == 0x11AA11AA11AA11AA) {
offset = i;
break;
}
}
if (offset == -1) {
notify("Could not find offset of args_ptr address - Aborting\n");
return -1;
}
kwrite64(dest_text + offset, dest_data);
return 0;
}
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");
}
void prepare_sck_args(uint64_t dest_data) {
shellcode_kernel_args args;
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;
kernel_copyin(&args, dest_data, sizeof(args));
}

View File

@@ -6,6 +6,7 @@
#include <sys/param.h>
#include <sys/proc.h>
#include <unistd.h>
#include <sys/stat.h>
/* Global Variables */
offset_list env_offset;
@@ -16,6 +17,16 @@ 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() >> 16;
if (fw == 0)
@@ -247,4 +258,32 @@ void enter_rest_mode(void) {
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;
}