24 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
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
25 changed files with 587 additions and 333 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

119
README.md
View File

@@ -1,6 +1,6 @@
# ps5-linux # 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) ![Alt Text](logo.webp)
@@ -8,14 +8,13 @@
*ps5-linux* is currently only supported on PS5 Phat on 3.xx and 4.xx firmwares. *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 - **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 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. Obviously you cannot downgrade.
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.
## Hardwares ## Hardwares
@@ -30,21 +29,34 @@ To run *ps5-linux*, you need some required and optional hardwares:
## Configure PS5 settings ## 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`. - 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` - Go to `Settings``HDMI``Enable HDMI Device Link`
- *Recommended*: Disable automatic updates: - *Recommended*: Disable automatic updates:
- Go to `Settings``System Software``System Software Update and Settings` - Go to `Settings``System Software``System Software Update and Settings`
- *Recommended*: Disable automatic error reporting: - *Recommended*: Disable automatic error reporting:
- Go to `Settings``System Software``Report System Software Errors Automatically` - 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 ## Installation
### 1. Get a Linux image ### 1. Get a Linux image
#### Linux/macOS: #### 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 ```bash
git clone https://github.com/ps5-linux/ps5-linux-image git clone https://github.com/ps5-linux/ps5-linux-image
cd 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): #### Windows (Balena Etcher):
Download Balena Etcher, select the .img file, select your USB drive, click Flash. Download [Balena Etcher](https://etcher.balena.io/), 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
```
### 3. Plug the USB drive into your PS5 ### 3. Plug the USB drive into your PS5
@@ -157,6 +140,12 @@ cd ps5-linux-loader
make 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`. Find your PS5 IP at `Settings → Network → View Connection Status`.
```bash ```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: 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 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. - 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. If none of this helps, please report the issue in our [Discord server](https://discord.gg/PeMGVB7BAm) and provide your EDID information.
## First Boot ## First Boot
Configure your system and memorize your login password. Configure your system and memorize your login password.
@@ -180,37 +171,53 @@ Then, there are certain settings and commands we recommend doing:
1. Disable screen saver, as it is currently buggy. 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 ```bash
snap install firefox 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
sudo snap refresh mesa-2404 --channel=latest/edge
``` ```
3. Clone our [ps5-linux-tools](https://github.com/ps5-linux/ps5-linux-tools): 4. Install Firefox:
```bash ```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 git clone https://github.com/ps5-linux/ps5-linux-tools
cd ps5-linux-tools
make
``` ```
## M.2 installation ## M.2 installation
You can use a M.2 SSD exclusively for Linux (which means you cannot use it for PS5 game storage). 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. 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`.
2. Run these commands to initialize your M.2: 3. Boot Linux on your PS5 and run these commands to initialize your M.2:
```bash ```bash
sudo apt install zlib1g-dev
cd ps5-linux-tools cd ps5-linux-tools
gcc -o m2_init m2_init.c -lz
sudo ./m2_init 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. 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. 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: 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 ```bash
cd ps5-linux-tools cd ps5-linux-tools
@@ -226,7 +233,9 @@ chmod +x ./m2_exec.sh
sudo ./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 ## Fan & boost control
@@ -234,7 +243,6 @@ We provide a simple tool that allows you to boost your CPU to 3500Mhz and GPU to
```bash ```bash
cd ps5-linux-tools cd ps5-linux-tools
gcc -o ps5_control ps5_control.c
sudo ./ps5_control --fan on sudo ./ps5_control --fan on
sudo ./ps5_control --boost on sudo ./ps5_control --boost on
``` ```
@@ -247,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. - 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? - 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. - 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? - Q: Can I continue using my PS5 if I install Linux?
- A: Yes, the internal SSD is not modified - A: Yes, the internal SSD is not modified
- Q: Can I use the PS5's NIC/WLAN module in Linux? - 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. - 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. - A: No.
- Q: Does the DualSense controller work? - Q: Does the DualSense controller work?
- A: Via a Bluetooth dongle. Built-in Bluetooth is not yet supported. - A: Via a Bluetooth dongle. Built-in Bluetooth is not yet supported.
- Q: What resolutions and refresh rates are 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 ## 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 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). - 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. - 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. 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,10 +1,15 @@
#ifndef LOADER_H
#define LOADER_H
#include "utils.h" #include "utils.h"
#include <stdint.h> #include <stdint.h>
static uint64_t alloc_page(void); uint64_t alloc_page(void);
static void install_page(uintptr_t pml4, vm_offset_t va, vm_paddr_t pa, void install_page(uintptr_t pml4, vm_offset_t va, vm_paddr_t pa,
int bits); int bits);
void pte_store(uintptr_t ptep, uint64_t pte); void pte_store(uintptr_t ptep, uint64_t pte);
static int read_file(const char *path, void *buf, size_t bufsize); int read_file(const char *path, void *buf, size_t bufsize);
static void trim_newline(char *s); void trim_newline(char *s);
int fetch_linux(struct linux_info *info); 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_UART_OVERRIDE;
uint64_t KERNEL_DEBUG_PATCH; uint64_t KERNEL_DEBUG_PATCH;
uint64_t KERNEL_CFI_CHECK; uint64_t KERNEL_CFI_CHECK;
uint64_t PS5_WIFI_FW_OFFSET;
uint64_t PS5_WIFI_FW_SIZE;
} offset_list; } offset_list;
extern offset_list off_0300; extern offset_list off_0300;
@@ -47,4 +49,4 @@ extern offset_list off_0403;
extern offset_list off_0450; extern offset_list off_0450;
extern offset_list off_0451; 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 initrd_size;
size_t vram_size; size_t vram_size;
char cmdline[2048]; char cmdline[2048];
int kit_type;
uintptr_t linux_info; // PA of linux_info 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 uint64_t vmcb_pa[16]; // Defined on hv_defeat.c
extern struct linux_info linux_i; // Declared on main.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) { static inline void kwrite(uint64_t ka, void *src, uint64_t len) {
kernel_copyin(src, ka, len); kernel_copyin(src, ka, len);
} }
@@ -156,4 +169,17 @@ void enter_rest_mode(void);
#define DEBUG_PRINT(fmt, ...) #define DEBUG_PRINT(fmt, ...)
#endif #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 #endif

View File

@@ -3,9 +3,16 @@ ifndef PS5_PAYLOAD_SDK
endif endif
# 1. Variables # 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 CC = gcc
LD = ld 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 LDFLAGS = -T linker.ld
TARGET = shellcode_hypervisor.elf TARGET = shellcode_hypervisor.elf
TEXT_BIN = shellcode_hypervisor.bin TEXT_BIN = shellcode_hypervisor.bin
@@ -23,7 +30,7 @@ $(TARGET): $(OBJ)
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
$(TEXT_BIN): $(TARGET) $(TEXT_BIN): $(TARGET)
objcopy -O binary -j .shell_code $(TARGET) $(TEXT_BIN) $(OBJCOPY) -O binary -j .shell_code $(TARGET) $(TEXT_BIN)
clean: clean:
rm -f $(OBJ) $(TARGET) $(TEXT_BIN) $(dump) rm -f $(OBJ) $(TARGET) $(TEXT_BIN) $(dump)

View File

@@ -12,6 +12,7 @@ struct linux_info {
size_t initrd_size; size_t initrd_size;
size_t vram_size; size_t vram_size;
char cmdline[2048]; char cmdline[2048];
int kit_type;
}; };
static struct linux_info info; 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, 0x0f0000000, 0x0f8000000, E820_TYPE_RESERVED);
append_e820_table(bp, 0x100000000, VRAM_BASE, E820_TYPE_RAM); 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, 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) { void boot_linux(void) {
@@ -124,7 +133,7 @@ void boot_linux(void) {
memcpy((void *)kernel_pa, (void *)(info.bzimage + setup_size), kernel_size); 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 (*startup_64)(uint64_t physaddr, struct boot_params *bp) =
(void *)(kernel_pa + 0x200); (void *)(kernel_pa + 0x200);
startup_64(kernel_pa, bp); startup_64(kernel_pa, bp);
@@ -159,7 +168,7 @@ void entry(void) {
} }
// Disable IOMMU. // 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)); 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_BASE_ADDR_0 0x24878
#define DCHUBBUB_WHITELIST_TOP_ADDR_0 0x2487c #define DCHUBBUB_WHITELIST_TOP_ADDR_0 0x2487c
#define AMDIOMMU_MMIO_BASE 0xfdd80000
#define AMDIOMMU_CTRL 0x18
#define MAXCPU 16 #define MAXCPU 16
void entry(void); 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) { __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 ebax, ebx, ecx, edx;
uint32_t cpu_id; uint32_t cpu_id;
@@ -17,12 +15,10 @@ __attribute__((section(".entry_point"), naked)) uint32_t main(void) {
cpu_id = (ebx >> 24) & 0xFF; cpu_id = (ebx >> 24) & 0xFF;
// We point to a location after the main linux boot code // Each CPU should have a different stack
// Each CPU should have a different location
uintptr_t new_rsp = uintptr_t new_rsp =
(uintptr_t)hv_base_rsp + ((uint64_t)(cpu_id)*hv_stack_size); (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" __asm__ volatile("movq %0, %%rsp \n\t"
"movq %%rsp, %%rbp \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> #include <stdint.h>
typedef struct { typedef struct {
uint64_t bzimage_pa; // Already relocated by Kernel shellcode uint64_t bzimage_pa;
uint64_t initrd_pa; // Already relocated by Kernel shellcode uint64_t initrd_pa;
uint64_t linux_info_pa; // Already relocated by Kernel shellcode uint64_t linux_info_pa;
} shellcode_hypervisor_args; } 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)); __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) { void atomic_add_32(volatile uint32_t *p, uint32_t v) {
__sync_fetch_and_add(p, 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) { int atomic_cmpset_32(volatile uint32_t *dst, uint32_t exp, uint32_t src) {
return __sync_bool_compare_and_swap(dst, exp, src); return __sync_bool_compare_and_swap(dst, exp, src);
} }

View File

@@ -3,9 +3,16 @@ ifndef PS5_PAYLOAD_SDK
endif 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 CC = gcc
LD = ld 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 LDFLAGS = -T linker.ld
TARGET = shellcode_kernel.elf TARGET = shellcode_kernel.elf
TEXT_BIN = shellcode_text.bin TEXT_BIN = shellcode_text.bin
@@ -24,7 +31,7 @@ $(TARGET): $(OBJ)
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
$(TEXT_BIN): $(TARGET) $(TEXT_BIN): $(TARGET)
objcopy -O binary -j .text $(TARGET) $(TEXT_BIN) $(OBJCOPY) -O binary -j .text $(TARGET) $(TEXT_BIN)
clean: clean:
rm -f $(OBJ) $(TARGET) $(TEXT_BIN) $(dump) rm -f $(OBJ) $(TARGET) $(TEXT_BIN) $(dump)

View File

@@ -9,11 +9,11 @@
#define TRANSMITTER_CONTROL_ENABLE 1 #define TRANSMITTER_CONTROL_ENABLE 1
#define TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS 11 #define TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS 11
int (*transmitter_control)(int cmd, void *control) = NULL; // Filled by main.c int (*transmitter_control)(int cmd, void *control) = NULL;
int (*mp3_initialize)(int vmid) = NULL; // Filled by main.c int (*mp3_initialize)(int vmid) = NULL;
int (*mp3_invoke)(int cmd_id, void *req, void *rsp) = NULL; // Filled by main.c 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 { typedef struct {
uint8_t lanenum; uint8_t lanenum;
@@ -46,6 +46,7 @@ struct linux_info {
size_t initrd_size; size_t initrd_size;
size_t vram_size; size_t vram_size;
char cmdline[2048]; char cmdline[2048];
int kit_type;
}; };
static struct linux_info info; static struct linux_info info;
@@ -94,39 +95,31 @@ static int mp3_enable_output(int be, int mode) {
static void patch_hv(void) { static void patch_hv(void) {
// Install identity map for HV uint64_t identity_cr3 = cave_hv_paging;
// HV Shellcode 1 it's updating CR3 uint64_t identity_pml4_0 = identity_cr3 + 0x1003ULL;
uint64_t identity_cr3 = cave_hv_paging; // P, RW, US=0 uint64_t l40_l3_addr = PAGE_PA(identity_pml4_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_pml40_l3[] = { uint64_t identity_pml40_l3[] = {
0x0000000000000083, // P, RW, US=0 - 0 GB to 1 GB 0x0000000000000083,
0x0000000040000083, // P, RW, US=0 - 1 GB to 2 GB 0x0000000040000083,
0x0000000080000083, // P, RW, US=0 - 3 GB to 3 GB 0x0000000080000083,
0x00000000C0000083, // P, RW, US=0 - 4 GB to 4 GB 0x00000000C0000083,
0x0000000100000083 // P, RW, US=0 - 5 GB to 6 GB --> Our paging structure 0x0000000100000083
}; };
uint64_t l3_size = sizeof(identity_pml40_l3) / sizeof(identity_pml40_l3[0]); 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; *(uint64_t *)PHYS_TO_DMAP(identity_cr3) = identity_pml4_0;
for (uint64_t i = 0; i < l3_size; i++) { for (uint64_t i = 0; i < l3_size; i++) {
*(uint64_t *)PHYS_TO_DMAP(l40_l3_addr + i * 8) = identity_pml40_l3[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, memcpy((void *)PHYS_TO_DMAP(cave_hv_code), shellcode_hypervisor,
shellcode_hypervisor_len); shellcode_hypervisor_len);
// Jump to shellcode final identity mapping
uint8_t shellcode_jmp[] = { uint8_t shellcode_jmp[] = {
0x48, 0xC7, 0xC0, 0x00, 0x6F, 0x80, 0x62, // mov rax, 0x62806f00 0x48, 0xC7, 0xC0, 0x00, 0x6F, 0x80, 0x62,
0xFF, 0xE0, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, // jmp rax 0xFF, 0xE0, 0xC3, 0xC3, 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; *(uint32_t *)(&shellcode_jmp[3]) = (uint32_t)args.hv_code_cave_pa;
// Just patch the VMEXIT handler directly, avoiding all checks // Just patch the VMEXIT handler directly, avoiding all checks
@@ -142,12 +135,9 @@ static void patch_hv(void) {
0xFF, 0xE0 // jmp rax 0xFF, 0xE0 // jmp rax
}; };
// Update CR3 PA (from config)
*(uint64_t *)(&shellcode_identity_and_jmp[2]) = cave_hv_paging; *(uint64_t *)(&shellcode_identity_and_jmp[2]) = cave_hv_paging;
// Update HV shellcode cave
*(uint64_t *)(&shellcode_identity_and_jmp[15]) = cave_hv_code; *(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, memcpy((void *)PHYS_TO_DMAP(args.hv_code_cave_pa), shellcode_identity_and_jmp,
sizeof(shellcode_identity_and_jmp)); sizeof(shellcode_identity_and_jmp));
} }

View File

@@ -3,19 +3,11 @@
#include "utils.h" #include "utils.h"
#include <stdint.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, __attribute__((section(".entry_point"))) uint32_t main(uint64_t add1,
uint64_t add2) { 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 *args_ptr =
(volatile shellcode_kernel_args (volatile shellcode_kernel_args
*)0x11AA11AA11AA11AA; // To be replaced with proper address in .kdata *)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 // "Hide" the pointer from the optimizer
__asm__ volatile("" : "+r"(args_ptr)); __asm__ volatile("" : "+r"(args_ptr));
// We don't have required information - Abort
if ((args_ptr->fun_printf & 0xFFFF) == 0) { if ((args_ptr->fun_printf & 0xFFFF) == 0) {
goto out; goto out;
} }
// Activate UART on Kernel
uint32_t *uart_va = (uint32_t *)(args_ptr->dmap_base + 0xC0115110ULL); uint32_t *uart_va = (uint32_t *)(args_ptr->dmap_base + 0xC0115110ULL);
*uart_va &= ~0x200; *uart_va &= ~0x200;
uint32_t *override_char_va = (uint32_t *)args_ptr->kernel_uart_override; 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; uint64_t unk;
int n_devices; int n_devices;
// Reconfigure IOMMU calling the HV
int ret = ((uint64_t(*)(uint64_t, uint64_t, uint64_t, uint64_t, int ret = ((uint64_t(*)(uint64_t, uint64_t, uint64_t, uint64_t,
int *))args_ptr->fun_hv_iommu_set_buffers)( int *))args_ptr->fun_hv_iommu_set_buffers)(
iommu_cb2_pa, iommu_cb3_pa, iommu_eb_pa, (uint64_t) &unk, &n_devices); 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; goto out;
} }
// Wait for completion
ret = ((uint64_t(*)(void))args_ptr->fun_hv_iommu_wait_completion)(); ret = ((uint64_t(*)(void))args_ptr->fun_hv_iommu_wait_completion)();
if (ret == 0) { 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, 'K');
putc_uart(args_ptr->dmap_base, '\n'); putc_uart(args_ptr->dmap_base, '\n');
// Allow R/W on HV and Kernel area
if (tmr_disable(args_ptr->dmap_base)) { if (tmr_disable(args_ptr->dmap_base)) {
putc_uart(args_ptr->dmap_base, 'T'); 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, 'K');
putc_uart(args_ptr->dmap_base, '\n'); putc_uart(args_ptr->dmap_base, '\n');
// Patch HV
patch_vmcb(args_ptr); patch_vmcb(args_ptr);
putc_uart(args_ptr->dmap_base, 'V'); putc_uart(args_ptr->dmap_base, 'V');
@@ -145,9 +131,6 @@ __attribute__((section(".entry_point"))) uint32_t main(uint64_t add1,
boot_linux(); boot_linux();
printf("Linux prepared OK\n"); 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", printf("Calling smp_rendezvous to exit all cores to HV with ptr: %016lx\n",
(uint64_t)vmmcall_dummy); (uint64_t)vmmcall_dummy);
printf("Good Bye VM :)\n"); printf("Good Bye VM :)\n");
@@ -186,36 +169,28 @@ __attribute__((noinline, optimize("O0"), naked)) void vmmcall_dummy(void) {
void halt(void) { __asm__ __volatile__("hlt"); } void halt(void) { __asm__ __volatile__("hlt"); }
// Submit a single 16-byte command and wait for completion
__attribute__((noinline, optimize("O0"))) void __attribute__((noinline, optimize("O0"))) void
iommu_submit_cmd(volatile shellcode_kernel_args *args_ptr, uint64_t *cmd) { 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 curr_tail = *(
(uint64_t *)args_ptr->iommu_mmio_va + (uint64_t *)args_ptr->iommu_mmio_va +
IOMMU_MMIO_CB_TAIL / IOMMU_MMIO_CB_TAIL / 8);
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;
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 *cmd_buffer =
(uint64_t *)args_ptr->iommu_cb2_va + curr_tail / 8; // Downscale the size of the ptr (uint64_t *)args_ptr->iommu_cb2_va + curr_tail / 8;
// Copy 0x10 bytes (CMD Size)
cmd_buffer[0] = cmd[0]; cmd_buffer[0] = cmd[0];
cmd_buffer[1] = cmd[1]; cmd_buffer[1] = cmd[1];
__asm__ volatile("" : : : "memory"); // Prevent reordering __asm__ volatile("" : : : "memory"); // Prevent reordering
*((uint64_t *)args_ptr->iommu_mmio_va + IOMMU_MMIO_CB_TAIL / 8) = *((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 next_tail;
// 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) != 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)) *((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 __attribute__((noinline, optimize("O0"))) void
iommu_write8_pa(volatile shellcode_kernel_args *args_ptr, uint64_t pa, uint64_t val) { iommu_write8_pa(volatile shellcode_kernel_args *args_ptr, uint64_t pa, uint64_t val) {
uint32_t cmd[4] = {0}; uint32_t cmd[4] = {0};
@@ -230,26 +205,22 @@ __attribute__((noinline, optimize("O0"))) void
patch_vmcb(volatile shellcode_kernel_args *args_ptr) { patch_vmcb(volatile shellcode_kernel_args *args_ptr) {
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
uint64_t pa = args_ptr->vmcb[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, iommu_write8_pa(args_ptr, pa + 0x00,
0x0000000000000000ULL); // Clear all intercepts (R/W) to 0x0000000000000000ULL);
// CR0-CR15 and DR0-DR15
iommu_write8_pa(args_ptr, pa + 0x08, iommu_write8_pa(args_ptr, pa + 0x08,
0x0004000000000000ULL); // Clear all intercepts of except. 0x0004000000000000ULL);
// vectors but CPUID
iommu_write8_pa(args_ptr, pa + 0x10, iommu_write8_pa(args_ptr, pa + 0x10,
0x000000000000000FULL); // Clear all except VMMCALL, VMLOAD, 0x000000000000000FULL);
// VMSAVE, VMRUN
iommu_write8_pa(args_ptr, pa + 0x58, iommu_write8_pa(args_ptr, pa + 0x58,
0x0000000000000001ULL); // Guest ASID ... 1 ? 0x0000000000000001ULL);
iommu_write8_pa(args_ptr, pa + 0x90, iommu_write8_pa(args_ptr, pa + 0x90,
0x0000000000000000ULL); // Disable NP_ENABLE 0x0000000000000000ULL);
} }
} }
__attribute__((noinline, optimize("O0"))) uint32_t tmr_read(uint64_t dmap, __attribute__((noinline, optimize("O0")))
uint32_t addr) { uint32_t tmr_read(uint64_t dmap, uint32_t addr) {
*(uint32_t *)(dmap + ECAM_B0D18F2 + TMR_INDEX_OFF) = addr; *(uint32_t *)(dmap + ECAM_B0D18F2 + TMR_INDEX_OFF) = addr;
return *(uint32_t *)(dmap + ECAM_B0D18F2 + TMR_DATA_OFF); 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; *(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) { __attribute__((noinline, optimize("O0"))) int tmr_disable(uint64_t dmap) {
for (int i = 0; i < 24; i++) { for (int i = 0; i < 24; i++) {
if (tmr_read(dmap, TMR_CONFIG(i)) != 0) { 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), smp_rendezvous = (void (*)(void (*)(void), void (*)(void),
void (*)(void), void *)) args.fun_smp_rendezvous; void (*)(void), void *)) args.fun_smp_rendezvous;
smp_no_rendevous_barrier = (void (*)(void)) args.fun_smp_no_rendevous_barrier; smp_no_rendevous_barrier = (void (*)(void)) args.fun_smp_no_rendevous_barrier;
transmitter_control = (int (*) (int, void*)) args.fun_transmitter_control; transmitter_control = (int (*) (int, void*)) args.fun_transmitter_control;
mp3_initialize = (int (*) (int)) args.fun_mp3_initialize; mp3_initialize = (int (*) (int)) args.fun_mp3_initialize;
mp3_invoke = (int (*) (int, void*, void*)) args.fun_mp3_invoke; 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 read_cr3(void) {
uint64_t cr3; uint64_t cr3;
__asm__ volatile("mov %%cr3, %0" __asm__ volatile("mov %%cr3, %0"
: "=r"(cr3) // Output: move CR3 into the variable 'cr3' : "=r"(cr3)
: // No inputs :
: // No clobbered registers :
); );
return 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,10 +217,9 @@ int stage4_force_vmcb_reload(void) {
int stage5_remove_xotext(void) { int stage5_remove_xotext(void) {
DEBUG_PRINT("\nHV-Defeat [stage5] xotext removal\n"); DEBUG_PRINT("\nHV-Defeat [stage5] xotext removal\n");
uint64_t start = uint64_t start = ktext;
ktext - 0xF0000; // Include first pages where fun stuff is located
uint64_t end = kdata; uint64_t end = kdata;
int n = 0; int n __attribute__((unused)) = 0;
for (uint64_t a = start; a < end; a += 0x1000) { for (uint64_t a = start; a < end; a += 0x1000) {
page_chain_set_rw(a); page_chain_set_rw(a);

View File

@@ -1,5 +1,6 @@
#include "loader.h" #include "loader.h"
#include "config.h" #include "config.h"
#include "firmware.h"
#include "utils.h" #include "utils.h"
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@@ -19,7 +20,7 @@
#define MINI_SYSCORE_PID 1 #define MINI_SYSCORE_PID 1
static uint64_t alloc_page(void) { uint64_t alloc_page(void) {
void *page = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, void *page = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0); MAP_SHARED | MAP_ANONYMOUS, -1, 0);
@@ -30,7 +31,7 @@ static uint64_t alloc_page(void) {
return va_to_pa_user((uintptr_t)page); return va_to_pa_user((uintptr_t)page);
} }
static void install_page(uintptr_t pml4, vm_offset_t va, vm_paddr_t pa, void install_page(uintptr_t pml4, vm_offset_t va, vm_paddr_t pa,
int bits) { int bits) {
uint64_t entry; uint64_t entry;
@@ -79,11 +80,52 @@ const char *file_paths[] = {
"/mnt/usb2/PS5/Linux/", "/mnt/usb3/PS5/Linux/", "/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) { long find_and_get_size_of_file(const char *filename, char *found_path) {
char full_path[256]; char full_path[256];
struct stat st; struct stat st;
filename = get_overridden_filename(filename);
int num_paths = sizeof(file_paths) / sizeof(file_paths[0]); int num_paths = sizeof(file_paths) / sizeof(file_paths[0]);
for (int i = 0; i < num_paths; i++) { for (int i = 0; i < num_paths; i++) {
@@ -100,7 +142,7 @@ long find_and_get_size_of_file(const char *filename, char *found_path) {
return -1; 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]; char full_path[256];
struct stat st; struct stat st;
@@ -119,7 +161,7 @@ static int find_and_read_file(const char *filename, void *buf, size_t bufsize) {
return -1; 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); int fd = open(path, O_RDONLY);
if (fd < 0) if (fd < 0)
return fd; return fd;
@@ -128,7 +170,7 @@ static int read_file(const char *path, void *buf, size_t bufsize) {
return r; return r;
} }
static void trim_newline(char *s) { void trim_newline(char *s) {
while (*s != '\0') { while (*s != '\0') {
if (*s == '\r' || *s == '\n') { if (*s == '\r' || *s == '\n') {
*s = '\0'; *s = '\0';
@@ -186,6 +228,10 @@ int fetch_linux(struct linux_info *info) {
return -1; return -1;
} }
if (dump_device_firmwares(initrd_path) < 0) {
notify("Something went wrong while dumping device firmwares - Continuing\n");
}
size_t vram_size; size_t vram_size;
char buf_vram[16] = {}; char buf_vram[16] = {};
int ret = find_and_read_file("vram.txt", buf_vram, sizeof(buf_vram) - 1); 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->initrd_size = initrd_size;
info->vram_size = vram_size; info->vram_size = vram_size;
strcpy(info->cmdline, cmdline); strcpy(info->cmdline, cmdline);
info->kit_type = (int) get_kit_type();
uint64_t page = alloc_page(); uint64_t page = alloc_page();
kwrite(pa_to_dmap(page), info, sizeof(struct linux_info)); 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 <unistd.h>
#include "utils.h"
#include "hv_defeat.h"
#include "prepare_resume.h"
#include "loader.h"
int main(void) { int main(void) {
@@ -36,143 +33,9 @@ int main(void) {
sleep(5); sleep(5);
enter_rest_mode(); enter_rest_mode();
while (1) { while (1) {
sleep(30); 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; return 0;
} }

View File

@@ -5,8 +5,8 @@ offset_list off_0300 = {
.HV_VCPU_ARRAY_OFF = 0x5D0, .HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320, .HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08, .HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000, .KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300, .KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33175E0, .IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4, .VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0, .VMSPACE_VM_PMAP = 0x1D0,
@@ -30,6 +30,8 @@ offset_list off_0300 = {
.KERNEL_UART_OVERRIDE = 0x1EB0258, .KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x0752460, .KERNEL_DEBUG_PATCH = 0x0752460,
.KERNEL_CFI_CHECK = 0x0441DD0, .KERNEL_CFI_CHECK = 0x0441DD0,
.PS5_WIFI_FW_OFFSET = 0x1274460,
.PS5_WIFI_FW_SIZE = 492304,
}; };
offset_list off_0310 = { offset_list off_0310 = {
@@ -37,8 +39,8 @@ offset_list off_0310 = {
.HV_VCPU_ARRAY_OFF = 0x5D0, .HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320, .HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08, .HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000, .KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300, .KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33175E0, .IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4, .VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0, .VMSPACE_VM_PMAP = 0x1D0,
@@ -62,6 +64,8 @@ offset_list off_0310 = {
.KERNEL_UART_OVERRIDE = 0x1EB0258, .KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x07524A0, .KERNEL_DEBUG_PATCH = 0x07524A0,
.KERNEL_CFI_CHECK = 0x0441E10, .KERNEL_CFI_CHECK = 0x0441E10,
.PS5_WIFI_FW_OFFSET = 0x1274490,
.PS5_WIFI_FW_SIZE = 492304,
}; };
offset_list off_0320 = { offset_list off_0320 = {
@@ -69,8 +73,8 @@ offset_list off_0320 = {
.HV_VCPU_ARRAY_OFF = 0x5D0, .HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320, .HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08, .HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000, .KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300, .KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33175E0, .IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4, .VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0, .VMSPACE_VM_PMAP = 0x1D0,
@@ -94,6 +98,8 @@ offset_list off_0320 = {
.KERNEL_UART_OVERRIDE = 0x1EB0258, .KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x7527F0, .KERNEL_DEBUG_PATCH = 0x7527F0,
.KERNEL_CFI_CHECK = 0x442160, .KERNEL_CFI_CHECK = 0x442160,
.PS5_WIFI_FW_OFFSET = 0x1274550,
.PS5_WIFI_FW_SIZE = 492304,
}; };
offset_list off_0321 = { offset_list off_0321 = {
@@ -101,8 +107,8 @@ offset_list off_0321 = {
.HV_VCPU_ARRAY_OFF = 0x5D0, .HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320, .HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08, .HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000, .KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300, .KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33175E0, .IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4, .VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0, .VMSPACE_VM_PMAP = 0x1D0,
@@ -126,6 +132,8 @@ offset_list off_0321 = {
.KERNEL_UART_OVERRIDE = 0x1EB0258, .KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x7527F0, .KERNEL_DEBUG_PATCH = 0x7527F0,
.KERNEL_CFI_CHECK = 0x442160, .KERNEL_CFI_CHECK = 0x442160,
.PS5_WIFI_FW_OFFSET = 0x1274550,
.PS5_WIFI_FW_SIZE = 492304,
}; };
offset_list off_0400 = { offset_list off_0400 = {
@@ -133,8 +141,8 @@ offset_list off_0400 = {
.HV_VCPU_ARRAY_OFF = 0x5D0, .HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320, .HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08, .HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000, .KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300, .KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33C7680, .IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4, .VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0, .VMSPACE_VM_PMAP = 0x1D0,
@@ -158,6 +166,8 @@ offset_list off_0400 = {
.KERNEL_UART_OVERRIDE = 0x1F522A8, .KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DA70, .KERNEL_DEBUG_PATCH = 0x77DA70,
.KERNEL_CFI_CHECK = 0x45A170, .KERNEL_CFI_CHECK = 0x45A170,
.PS5_WIFI_FW_OFFSET = 0x1392FB0,
.PS5_WIFI_FW_SIZE = 493000,
}; };
offset_list off_0402 = { offset_list off_0402 = {
@@ -165,8 +175,8 @@ offset_list off_0402 = {
.HV_VCPU_ARRAY_OFF = 0x5D0, .HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320, .HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08, .HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000, .KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300, .KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33C7680, .IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4, .VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0, .VMSPACE_VM_PMAP = 0x1D0,
@@ -190,6 +200,8 @@ offset_list off_0402 = {
.KERNEL_UART_OVERRIDE = 0x1F522A8, .KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DAC0, .KERNEL_DEBUG_PATCH = 0x77DAC0,
.KERNEL_CFI_CHECK = 0x45A170, .KERNEL_CFI_CHECK = 0x45A170,
.PS5_WIFI_FW_OFFSET = 0x1392FB0,
.PS5_WIFI_FW_SIZE = 493000,
}; };
offset_list off_0403 = { offset_list off_0403 = {
@@ -197,8 +209,8 @@ offset_list off_0403 = {
.HV_VCPU_ARRAY_OFF = 0x5D0, .HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320, .HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08, .HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000, .KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300, .KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33C7680, .IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4, .VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0, .VMSPACE_VM_PMAP = 0x1D0,
@@ -222,6 +234,8 @@ offset_list off_0403 = {
.KERNEL_UART_OVERRIDE = 0x1F522A8, .KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DB50, .KERNEL_DEBUG_PATCH = 0x77DB50,
.KERNEL_CFI_CHECK = 0x45A170, .KERNEL_CFI_CHECK = 0x45A170,
.PS5_WIFI_FW_OFFSET = 0x1392FB0,
.PS5_WIFI_FW_SIZE = 493000,
}; };
offset_list off_0450 = { offset_list off_0450 = {
@@ -229,8 +243,8 @@ offset_list off_0450 = {
.HV_VCPU_ARRAY_OFF = 0x5D0, .HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320, .HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08, .HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000, .KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300, .KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33C7680, .IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4, .VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0, .VMSPACE_VM_PMAP = 0x1D0,
@@ -254,6 +268,8 @@ offset_list off_0450 = {
.KERNEL_UART_OVERRIDE = 0x1F522A8, .KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DC80, .KERNEL_DEBUG_PATCH = 0x77DC80,
.KERNEL_CFI_CHECK = 0x45A1A0, .KERNEL_CFI_CHECK = 0x45A1A0,
.PS5_WIFI_FW_OFFSET = 0x1392FC0,
.PS5_WIFI_FW_SIZE = 493000,
}; };
offset_list off_0451 = { offset_list off_0451 = {
@@ -261,8 +277,8 @@ offset_list off_0451 = {
.HV_VCPU_ARRAY_OFF = 0x5D0, .HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320, .HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08, .HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000, .KERNEL_CODE_CAVE = 0x500,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300, .KERNEL_DATA_CAVE = 0xC01300,
.IOMMU_SOFTC = 0x33C7680, .IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4, .VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0, .VMSPACE_VM_PMAP = 0x1D0,
@@ -286,4 +302,6 @@ offset_list off_0451 = {
.KERNEL_UART_OVERRIDE = 0x1F522A8, .KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DC90, .KERNEL_DEBUG_PATCH = 0x77DC90,
.KERNEL_CFI_CHECK = 0x45A1A0, .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/param.h>
#include <sys/proc.h> #include <sys/proc.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h>
/* Global Variables */ /* Global Variables */
offset_list env_offset; offset_list env_offset;
@@ -16,6 +17,16 @@ uint64_t cr3;
uint32_t fw; uint32_t fw;
struct linux_info linux_i; 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) { int set_offsets(void) {
fw = kernel_get_fw_version() >> 16; fw = kernel_get_fw_version() >> 16;
if (fw == 0) if (fw == 0)
@@ -225,8 +236,8 @@ void notify(const char *fmt, ...) {
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
notify_internal(buffer); notify_internal((uint8_t *)buffer);
printf(buffer); printf("%s", buffer);
} }
void notify_internal(uint8_t *msg) { void notify_internal(uint8_t *msg) {
@@ -236,7 +247,7 @@ void notify_internal(uint8_t *msg) {
} req; } req;
bzero(&req, sizeof(req)); bzero(&req, sizeof(req));
uint64_t len = uint64_t len =
strlen(msg) < (sizeof(req.msg) - 1) ? strlen(msg) : (sizeof(req.msg) - 1); strlen((const char *)msg) < (sizeof(req.msg) - 1) ? strlen((const char *)msg) : (sizeof(req.msg) - 1);
memcpy(req.msg, msg, len); memcpy(req.msg, msg, len);
sceKernelSendNotificationRequest(0, &req, sizeof(req), 0); sceKernelSendNotificationRequest(0, &req, sizeof(req), 0);
} }
@@ -247,4 +258,32 @@ void enter_rest_mode(void) {
sceKernelNotifySystemSuspendStart(); sceKernelNotifySystemSuspendStart();
sceKernelSetEventFlag(event, 0x400); sceKernelSetEventFlag(event, 0x400);
sceKernelCloseEventFlag(&event); 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;
} }