Simplify firmware setup, save at boot partition and let driver's installer copy when needed

This commit is contained in:
buzzer-re
2026-05-11 20:43:13 -03:00
parent 1be941e9bd
commit f017958451
8 changed files with 109 additions and 268 deletions

View File

@@ -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 $@ $<

View File

@@ -1,19 +0,0 @@
#ifndef CPIO_H
#define CPIO_H
#include <stddef.h>
#include <stdint.h>
#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

View File

@@ -1,8 +1,6 @@
#ifndef FIRMWARE_H
#define FIRMWARE_H
#include <stddef.h>
int resolve_device_firmwares(void **initrd, size_t *initrd_size);
int dump_device_firmwares(const char *boot_file_path);
#endif

View File

@@ -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

View File

@@ -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

View File

@@ -1,74 +0,0 @@
#include "cpio.h"
#include <stdio.h>
#include <string.h>
#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;
}

View File

@@ -1,89 +1,14 @@
#include "firmware.h"
#include "cpio.h"
#include "utils.h"
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#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);
}

View File

@@ -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;