diff --git a/Makefile b/Makefile index 5f98fb5..710943d 100644 --- a/Makefile +++ b/Makefile @@ -24,8 +24,6 @@ $(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 deleted file mode 100644 index f6b326a..0000000 --- a/include/cpio.h +++ /dev/null @@ -1,19 +0,0 @@ -#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 index 4827aa9..fa79670 100644 --- a/include/firmware.h +++ b/include/firmware.h @@ -1,8 +1,6 @@ #ifndef FIRMWARE_H #define FIRMWARE_H -#include - -int resolve_device_firmwares(void **initrd, size_t *initrd_size); +int dump_device_firmwares(const char *boot_file_path); #endif diff --git a/scripts/ubuntu/initramfs-tools b/scripts/ubuntu/initramfs-tools deleted file mode 100644 index f62f02e..0000000 --- a/scripts/ubuntu/initramfs-tools +++ /dev/null @@ -1,14 +0,0 @@ -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 deleted file mode 100644 index b5411a0..0000000 --- a/scripts/ubuntu/ps5-wifi-fw +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index aa66ffc..0000000 --- a/source/cpio.c +++ /dev/null @@ -1,74 +0,0 @@ -#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 index 8fe633c..d77efec 100644 --- a/source/firmware.c +++ b/source/firmware.c @@ -1,89 +1,14 @@ #include "firmware.h" -#include "cpio.h" #include "utils.h" +#include +#include #include +#include #include -#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)++); -} +#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; @@ -93,87 +18,128 @@ 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()); +static int write_all(int fd, const void *buf, size_t len) { + size_t written = 0; - 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); + 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 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(); +static int mkdir_if_needed(const char *path) { + struct stat st; - 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++); + if (mkdir(path, 0777) == 0) + return 0; - for (size_t i = 0; i < script->count; i++) - out = write_initramfs_blob(out, &script->blobs[i], &ino); + if (errno != EEXIST) + return -1; - out = cpio_newc_write_entry(out, "TRAILER!!!", CPIO_MODE_FILE, NULL, 0, - ino++); + if (stat(path, &st) < 0) + return -1; - return (size_t)(out - (uint8_t *)overlay); + return S_ISDIR(st.st_mode) ? 0 : -1; } -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; +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; } - 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"); + 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; } - 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)); + if (create_firmware_dirs(boot_dir) < 0) { + notify("Could not create PS5 WiFi firmware directory under %s\n", boot_dir); + return -1; + } - *initrd = extended_initrd; - *initrd_size = overlay_size + old_initrd_size; + 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; + } - 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); + 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; } -// -// Wrapper to resolve future firmwares images using the kernel, at the moment only Marvell WiFi driver -// -int resolve_device_firmwares(void **initrd, size_t *initrd_size) { - return resolve_ps5_wifi_firmware(initrd, initrd_size); +int dump_device_firmwares(const char *boot_file_path) { + return dump_ps5_wifi_firmware(boot_file_path); } diff --git a/source/loader.c b/source/loader.c index b751618..394e357 100644 --- a/source/loader.c +++ b/source/loader.c @@ -228,9 +228,8 @@ int fetch_linux(struct linux_info *info) { return -1; } - if (resolve_device_firmwares(&initrd, &initrd_size) < 0) { - notify("Something went wrong while resolving device firmwares - Aborting\n"); - return -1; + if (dump_device_firmwares(initrd_path) < 0) { + notify("Something went wrong while dumping device firmwares - Continuing\n"); } size_t vram_size;