From 28e2ccd35ac8bc20a6647f31ebcbfd7abb51185b Mon Sep 17 00:00:00 2001 From: buzzer-re <22428720+buzzer-re@users.noreply.github.com> Date: Sun, 10 May 2026 19:28:00 -0300 Subject: [PATCH] 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 --- .gitignore | 6 ++ Makefile | 2 + include/cpio.h | 19 ++++ include/firmware.h | 8 ++ include/offsets.h | 4 +- scripts/ubuntu/initramfs-tools | 14 +++ scripts/ubuntu/ps5-wifi-fw | 13 +++ source/cpio.c | 74 ++++++++++++++ source/firmware.c | 176 +++++++++++++++++++++++++++++++++ source/loader.c | 4 + source/offsets.c | 18 ++++ 11 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 include/cpio.h create mode 100644 include/firmware.h create mode 100644 scripts/ubuntu/initramfs-tools create mode 100644 scripts/ubuntu/ps5-wifi-fw create mode 100644 source/cpio.c create mode 100644 source/firmware.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0e4cabf --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin/ +shellcode_kernel/shellcode_kernel.h +shellcode_hypervisor/shellcode_hypervisor.h +*.elf +*.bin +*.o diff --git a/Makefile b/Makefile index 710943d..5f98fb5 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,8 @@ $(SC_HV_H): $(SC_K_H): $(MAKE) -C shellcode_kernel +source/firmware.o: scripts/ubuntu/initramfs-tools scripts/ubuntu/ps5-wifi-fw + $(OBJS): %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< diff --git a/include/cpio.h b/include/cpio.h new file mode 100644 index 0000000..f6b326a --- /dev/null +++ b/include/cpio.h @@ -0,0 +1,19 @@ +#ifndef CPIO_H +#define CPIO_H + +#include +#include + +#define CPIO_MODE_DIR 0040755 +#define CPIO_MODE_FILE 0100644 + +size_t cpio_newc_entry_size(const char *name, size_t data_size); +size_t cpio_newc_parent_dirs_size(const char *path); +uint8_t *cpio_newc_data(uint8_t *entry, const char *name); +uint8_t *cpio_newc_write_entry(uint8_t *out, const char *name, uint32_t mode, + const void *data, size_t data_size, + uint32_t ino); +uint8_t *cpio_newc_write_parent_dirs(uint8_t *out, const char *path, + uint32_t *ino); + +#endif diff --git a/include/firmware.h b/include/firmware.h new file mode 100644 index 0000000..4827aa9 --- /dev/null +++ b/include/firmware.h @@ -0,0 +1,8 @@ +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include + +int resolve_device_firmwares(void **initrd, size_t *initrd_size); + +#endif diff --git a/include/offsets.h b/include/offsets.h index 8ad8137..aebd773 100644 --- a/include/offsets.h +++ b/include/offsets.h @@ -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 \ No newline at end of file +#endif diff --git a/scripts/ubuntu/initramfs-tools b/scripts/ubuntu/initramfs-tools new file mode 100644 index 0000000..f62f02e --- /dev/null +++ b/scripts/ubuntu/initramfs-tools @@ -0,0 +1,14 @@ +if [ "${PS5_FW_INSTALL_DONE:-}" != y ] && + [ -f /scripts/functions ] && + [ -d /scripts/local-bottom ] && + [ -n "$rootmnt" ] && + [ -e "${rootmnt}/etc/os-release" ]; then + ID= + ID_LIKE= + . "${rootmnt}/etc/os-release" + case " ${ID} ${ID_LIKE} " in + *" ubuntu "*) + [ -e /scripts/ubuntu/ps5-wifi-fw ] && . /scripts/ubuntu/ps5-wifi-fw + ;; + esac +fi diff --git a/scripts/ubuntu/ps5-wifi-fw b/scripts/ubuntu/ps5-wifi-fw new file mode 100644 index 0000000..b5411a0 --- /dev/null +++ b/scripts/ubuntu/ps5-wifi-fw @@ -0,0 +1,13 @@ +if [ "${PS5_FW_INSTALL_DONE:-}" != y ]; then + FW_PATH="usr/lib/firmware/nxp/pcieuartiw620_combo_v1.bin" + FW_SRC="/${FW_PATH}" + FW_DST="${rootmnt}/${FW_PATH}" + if [ -n "$rootmnt" ] && [ -f "$FW_SRC" ]; then + mkdir -p "${FW_DST%/*}" + cp "$FW_SRC" "$FW_DST" + chmod 0644 "$FW_DST" + PS5_FW_INSTALL_DONE=y + export PS5_FW_INSTALL_DONE + [ -e /dev/kmsg ] && printf 'ps5-fw-install: installed %s\n' "$FW_DST" > /dev/kmsg || true + fi +fi diff --git a/source/cpio.c b/source/cpio.c new file mode 100644 index 0000000..aa66ffc --- /dev/null +++ b/source/cpio.c @@ -0,0 +1,74 @@ +#include "cpio.h" +#include +#include + +#define CPIO_NEWC_HEADER_LEN 110 + +static size_t cpio_newc_align4(size_t n) { return (n + 3) & ~3ULL; } + +size_t cpio_newc_entry_size(const char *name, size_t data_size) { + return cpio_newc_align4(CPIO_NEWC_HEADER_LEN + strlen(name) + 1) + + cpio_newc_align4(data_size); +} + +uint8_t *cpio_newc_data(uint8_t *entry, const char *name) { + return (uint8_t *)cpio_newc_align4((uintptr_t)entry + CPIO_NEWC_HEADER_LEN + + strlen(name) + 1); +} + +uint8_t *cpio_newc_write_entry(uint8_t *out, const char *name, uint32_t mode, + const void *data, size_t data_size, + uint32_t ino) { + size_t name_size = strlen(name) + 1; + + out = (uint8_t *)cpio_newc_align4((uintptr_t)out); + + sprintf((char *)out, + "070701%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X", + ino, mode, 0, 0, 1, 0, (uint32_t)data_size, 0, 0, 0, 0, + (uint32_t)name_size, 0); + out += CPIO_NEWC_HEADER_LEN; + + memcpy(out, name, name_size); + out += name_size; + out = (uint8_t *)cpio_newc_align4((uintptr_t)out); + + if (data_size != 0 && data != out) { + memcpy(out, data, data_size); + } + out += data_size; + out = (uint8_t *)cpio_newc_align4((uintptr_t)out); + + return out; +} + +size_t cpio_newc_parent_dirs_size(const char *path) { + char tmp[256]; + size_t total = 0; + + snprintf(tmp, sizeof(tmp), "%s", path); + for (char *p = strchr(tmp, '/'); p != NULL; p = strchr(p + 1, '/')) { + *p = '\0'; + if (tmp[0] != '\0') + total += cpio_newc_entry_size(tmp, 0); + *p = '/'; + } + + return total; +} + +uint8_t *cpio_newc_write_parent_dirs(uint8_t *out, const char *path, + uint32_t *ino) { + char tmp[256]; + + snprintf(tmp, sizeof(tmp), "%s", path); + for (char *p = strchr(tmp, '/'); p != NULL; p = strchr(p + 1, '/')) { + *p = '\0'; + if (tmp[0] != '\0') { + out = cpio_newc_write_entry(out, tmp, CPIO_MODE_DIR, NULL, 0, (*ino)++); + } + *p = '/'; + } + + return out; +} diff --git a/source/firmware.c b/source/firmware.c new file mode 100644 index 0000000..ff658b7 --- /dev/null +++ b/source/firmware.c @@ -0,0 +1,176 @@ +#include "firmware.h" +#include "cpio.h" +#include "utils.h" +#include +#include +#include + +#define FIRMWARE_CPIO_INO_BASE 0x500000 +#define INITRAMFS_COPY_SCRIPT_MAX_BLOBS 2 +#define PS5_WIFI_FW_CPIO_PATH "usr/lib/firmware/nxp/pcieuartiw620_combo_v1.bin" +#define PS5_WIFI_FW_DISPATCHER_PATH "conf/param.conf" +#define PS5_WIFI_FW_COPY_SCRIPT_UBUNTU_PATH "scripts/ubuntu/ps5-wifi-fw" + +#define INCBIN(name, path) \ + __asm__(".section .rodata\n" \ + ".balign 1\n" \ + ".globl " #name "\n" \ + #name ":\n" \ + ".incbin \"" path "\"\n" \ + ".globl " #name "_end\n" \ + #name "_end:\n" \ + ".previous\n"); \ + extern const uint8_t name[]; \ + extern const uint8_t name##_end[] + +static_assert(sizeof(PS5_WIFI_FW_CPIO_PATH) <= 256, + "PS5 WiFi firmware cpio path too long"); +static_assert(sizeof(PS5_WIFI_FW_DISPATCHER_PATH) <= 256, + "PS5 WiFi firmware dispatcher path too long"); +static_assert(sizeof(PS5_WIFI_FW_COPY_SCRIPT_UBUNTU_PATH) <= 256, + "PS5 WiFi firmware script path too long"); + +INCBIN(initramfs_script_ubuntu_initramfs_tools, + "scripts/ubuntu/initramfs-tools"); +INCBIN(initramfs_script_ubuntu_ps5_wifi_fw, "scripts/ubuntu/ps5-wifi-fw"); + +struct initramfs_blob { + const char *path; + const uint8_t *data; + size_t size; +}; + +struct initramfs_copy_script { + const char *distro; + struct initramfs_blob blobs[INITRAMFS_COPY_SCRIPT_MAX_BLOBS]; + size_t count; +}; + +static size_t incbin_size(const uint8_t *start, const uint8_t *end) { + return (size_t)(end - start); +} + +static struct initramfs_copy_script load_initramfs_copy_script(void) { + struct initramfs_copy_script script = { + .distro = "ubuntu", + .count = 2, + }; + + script.blobs[0] = (struct initramfs_blob){ + .path = PS5_WIFI_FW_COPY_SCRIPT_UBUNTU_PATH, + .data = initramfs_script_ubuntu_ps5_wifi_fw, + .size = incbin_size(initramfs_script_ubuntu_ps5_wifi_fw, + initramfs_script_ubuntu_ps5_wifi_fw_end), + }; + script.blobs[1] = (struct initramfs_blob){ + .path = PS5_WIFI_FW_DISPATCHER_PATH, + .data = initramfs_script_ubuntu_initramfs_tools, + .size = incbin_size(initramfs_script_ubuntu_initramfs_tools, + initramfs_script_ubuntu_initramfs_tools_end), + }; + + return script; +} + +static size_t initramfs_blob_size(const struct initramfs_blob *blob) { + return cpio_newc_parent_dirs_size(blob->path) + + cpio_newc_entry_size(blob->path, blob->size); +} + +static uint8_t *write_initramfs_blob(uint8_t *out, + const struct initramfs_blob *blob, + uint32_t *ino) { + out = cpio_newc_write_parent_dirs(out, blob->path, ino); + return cpio_newc_write_entry(out, blob->path, CPIO_MODE_FILE, blob->data, + blob->size, (*ino)++); +} + +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 size_t +ps5_wifi_firmware_overlay_size(const struct initramfs_copy_script *script) { + size_t total = cpio_newc_parent_dirs_size(PS5_WIFI_FW_CPIO_PATH) + + cpio_newc_entry_size(PS5_WIFI_FW_CPIO_PATH, + ps5_wifi_firmware_size()); + + for (size_t i = 0; i < script->count; i++) + total += initramfs_blob_size(&script->blobs[i]); + + return total + cpio_newc_entry_size("TRAILER!!!", 0); +} + +static void read_kernel_blob(uint64_t va, uint8_t *out, size_t size) { + for (size_t copied = 0; copied < size; copied += 0x4000) { + size_t chunk = size - copied; + if (chunk > 0x4000) + chunk = 0x4000; + kread(va + copied, out + copied, chunk); + } +} + +static size_t +write_ps5_wifi_firmware_overlay(void *overlay, + const struct initramfs_copy_script *script) { + uint8_t *out = (uint8_t *)overlay; + uint8_t *payload; + uint32_t ino = FIRMWARE_CPIO_INO_BASE; + size_t fw_size = ps5_wifi_firmware_size(); + + out = cpio_newc_write_parent_dirs(out, PS5_WIFI_FW_CPIO_PATH, &ino); + payload = cpio_newc_data(out, PS5_WIFI_FW_CPIO_PATH); + read_kernel_blob(ps5_wifi_firmware_va(), payload, fw_size); + out = cpio_newc_write_entry(out, PS5_WIFI_FW_CPIO_PATH, CPIO_MODE_FILE, + payload, fw_size, ino++); + + for (size_t i = 0; i < script->count; i++) + out = write_initramfs_blob(out, &script->blobs[i], &ino); + + out = cpio_newc_write_entry(out, "TRAILER!!!", CPIO_MODE_FILE, NULL, 0, + ino++); + + return (size_t)(out - (uint8_t *)overlay); +} + +static int resolve_ps5_wifi_firmware(void **initrd, size_t *initrd_size) { + size_t old_initrd_size = *initrd_size; + struct initramfs_copy_script script = load_initramfs_copy_script(); + size_t overlay_size; + void *extended_initrd; + + 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; + } + + overlay_size = ps5_wifi_firmware_overlay_size(&script); + extended_initrd = + mmap(NULL, ALIGN_UP(old_initrd_size + overlay_size, 0x1000), + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (extended_initrd == MAP_FAILED) { + notify("[-] Error could not allocate extended initrd.\n"); + return -1; + } + + overlay_size = write_ps5_wifi_firmware_overlay(extended_initrd, &script); + memcpy((uint8_t *)extended_initrd + overlay_size, *initrd, old_initrd_size); + munmap(*initrd, ALIGN_UP(old_initrd_size, 0x1000)); + + *initrd = extended_initrd; + *initrd_size = overlay_size + old_initrd_size; + + notify("Loaded PS5 WiFi firmware into initrd as /%s (%llu bytes, %s)\n", + PS5_WIFI_FW_CPIO_PATH, + (unsigned long long)ps5_wifi_firmware_size(), + script.distro); + return 0; +} + +int resolve_device_firmwares(void **initrd, size_t *initrd_size) { + return resolve_ps5_wifi_firmware(initrd, initrd_size); +} diff --git a/source/loader.c b/source/loader.c index ee141f7..87942cb 100644 --- a/source/loader.c +++ b/source/loader.c @@ -1,5 +1,6 @@ #include "loader.h" #include "config.h" +#include "firmware.h" #include "utils.h" #include #include @@ -227,6 +228,9 @@ int fetch_linux(struct linux_info *info) { return -1; } + if (resolve_device_firmwares(&initrd, &initrd_size) < 0) + return -1; + size_t vram_size; char buf_vram[16] = {}; int ret = find_and_read_file("vram.txt", buf_vram, sizeof(buf_vram) - 1); diff --git a/source/offsets.c b/source/offsets.c index bf93bfc..15063bf 100644 --- a/source/offsets.c +++ b/source/offsets.c @@ -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 = { @@ -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 = { @@ -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 = { @@ -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 = { @@ -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 = { @@ -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 = { @@ -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 = { @@ -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 = { @@ -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, };