mirror of
https://github.com/ps5-linux/ps5-linux-loader.git
synced 2026-05-11 17:22:01 +00:00
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
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
bin/
|
||||
shellcode_kernel/shellcode_kernel.h
|
||||
shellcode_hypervisor/shellcode_hypervisor.h
|
||||
*.elf
|
||||
*.bin
|
||||
*.o
|
||||
2
Makefile
2
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 $@ $<
|
||||
|
||||
|
||||
19
include/cpio.h
Normal file
19
include/cpio.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#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
|
||||
8
include/firmware.h
Normal file
8
include/firmware.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef FIRMWARE_H
|
||||
#define FIRMWARE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
int resolve_device_firmwares(void **initrd, size_t *initrd_size);
|
||||
|
||||
#endif
|
||||
@@ -35,6 +35,8 @@ typedef struct _offset_list {
|
||||
uint64_t KERNEL_UART_OVERRIDE;
|
||||
uint64_t KERNEL_DEBUG_PATCH;
|
||||
uint64_t KERNEL_CFI_CHECK;
|
||||
uint64_t PS5_WIFI_FW_OFFSET;
|
||||
uint64_t PS5_WIFI_FW_SIZE;
|
||||
} offset_list;
|
||||
|
||||
extern offset_list off_0300;
|
||||
@@ -47,4 +49,4 @@ extern offset_list off_0403;
|
||||
extern offset_list off_0450;
|
||||
extern offset_list off_0451;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
14
scripts/ubuntu/initramfs-tools
Normal file
14
scripts/ubuntu/initramfs-tools
Normal file
@@ -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
|
||||
13
scripts/ubuntu/ps5-wifi-fw
Normal file
13
scripts/ubuntu/ps5-wifi-fw
Normal file
@@ -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
|
||||
74
source/cpio.c
Normal file
74
source/cpio.c
Normal file
@@ -0,0 +1,74 @@
|
||||
#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;
|
||||
}
|
||||
176
source/firmware.c
Normal file
176
source/firmware.c
Normal file
@@ -0,0 +1,176 @@
|
||||
#include "firmware.h"
|
||||
#include "cpio.h"
|
||||
#include "utils.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.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)++);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "loader.h"
|
||||
#include "config.h"
|
||||
#include "firmware.h"
|
||||
#include "utils.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user