From c4856cd01773fbb97637fe9defe4a35f22a5fc7b Mon Sep 17 00:00:00 2001 From: ArabPixel Date: Fri, 19 Dec 2025 02:04:06 +0100 Subject: [PATCH 1/4] Add 12.50 / 12.52 support --- README.md | 3 +- freebsd-headers/ps4-offsets/1250.h | 14 + freebsd-headers/ps4-offsets/kernel.h | 4 + linux/Makefile | 90 +++- linux/fw1250/.keepgithub | 1 + linux/magic.h | 36 ++ linux/main-baikal.c | 3 + linux/main.c | 3 + linux/ps4-kexec-1250-baikal/LICENSE | 24 + linux/ps4-kexec-1250-baikal/Makefile | 37 ++ linux/ps4-kexec-1250-baikal/README.md | 121 +++++ linux/ps4-kexec-1250-baikal/acpi.c | 320 ++++++++++++ linux/ps4-kexec-1250-baikal/acpi.h | 22 + linux/ps4-kexec-1250-baikal/acpi_caps.h | 30 ++ linux/ps4-kexec-1250-baikal/crc32.c | 102 ++++ linux/ps4-kexec-1250-baikal/crc32.h | 7 + linux/ps4-kexec-1250-baikal/elf.h | 71 +++ linux/ps4-kexec-1250-baikal/firmware.c | 479 ++++++++++++++++++ linux/ps4-kexec-1250-baikal/firmware.h | 64 +++ linux/ps4-kexec-1250-baikal/kernel.c | 383 ++++++++++++++ linux/ps4-kexec-1250-baikal/kernel.h | 126 +++++ linux/ps4-kexec-1250-baikal/kexec.c | 229 +++++++++ linux/ps4-kexec-1250-baikal/kexec.h | 38 ++ linux/ps4-kexec-1250-baikal/kexec.ld | 25 + linux/ps4-kexec-1250-baikal/linux_boot.c | 453 +++++++++++++++++ linux/ps4-kexec-1250-baikal/linux_boot.h | 88 ++++ linux/ps4-kexec-1250-baikal/linux_thunk.S | 90 ++++ linux/ps4-kexec-1250-baikal/reboot.h | 71 +++ linux/ps4-kexec-1250-baikal/string.h | 96 ++++ linux/ps4-kexec-1250-baikal/types.h | 51 ++ linux/ps4-kexec-1250-baikal/uart.c | 64 +++ linux/ps4-kexec-1250-baikal/uart.h | 20 + linux/ps4-kexec-1250-baikal/x86.h | 195 +++++++ linux/ps4-kexec-1250-pro-baikal/LICENSE | 24 + linux/ps4-kexec-1250-pro-baikal/Makefile | 37 ++ linux/ps4-kexec-1250-pro-baikal/README.md | 121 +++++ linux/ps4-kexec-1250-pro-baikal/acpi.c | 320 ++++++++++++ linux/ps4-kexec-1250-pro-baikal/acpi.h | 22 + linux/ps4-kexec-1250-pro-baikal/acpi_caps.h | 30 ++ linux/ps4-kexec-1250-pro-baikal/crc32.c | 102 ++++ linux/ps4-kexec-1250-pro-baikal/crc32.h | 7 + linux/ps4-kexec-1250-pro-baikal/elf.h | 71 +++ linux/ps4-kexec-1250-pro-baikal/firmware.c | 479 ++++++++++++++++++ linux/ps4-kexec-1250-pro-baikal/firmware.h | 64 +++ linux/ps4-kexec-1250-pro-baikal/kernel.c | 383 ++++++++++++++ linux/ps4-kexec-1250-pro-baikal/kernel.h | 126 +++++ linux/ps4-kexec-1250-pro-baikal/kexec.c | 229 +++++++++ linux/ps4-kexec-1250-pro-baikal/kexec.h | 38 ++ linux/ps4-kexec-1250-pro-baikal/kexec.ld | 25 + linux/ps4-kexec-1250-pro-baikal/linux_boot.c | 453 +++++++++++++++++ linux/ps4-kexec-1250-pro-baikal/linux_boot.h | 88 ++++ linux/ps4-kexec-1250-pro-baikal/linux_thunk.S | 90 ++++ linux/ps4-kexec-1250-pro-baikal/reboot.h | 71 +++ linux/ps4-kexec-1250-pro-baikal/string.h | 96 ++++ linux/ps4-kexec-1250-pro-baikal/types.h | 51 ++ linux/ps4-kexec-1250-pro-baikal/uart.c | 64 +++ linux/ps4-kexec-1250-pro-baikal/uart.h | 20 + linux/ps4-kexec-1250-pro-baikal/x86.h | 195 +++++++ linux/ps4-kexec-1250-pro/LICENSE | 24 + linux/ps4-kexec-1250-pro/Makefile | 37 ++ linux/ps4-kexec-1250-pro/README.md | 121 +++++ linux/ps4-kexec-1250-pro/acpi.c | 296 +++++++++++ linux/ps4-kexec-1250-pro/acpi.h | 18 + linux/ps4-kexec-1250-pro/crc32.c | 102 ++++ linux/ps4-kexec-1250-pro/crc32.h | 7 + linux/ps4-kexec-1250-pro/elf.h | 71 +++ linux/ps4-kexec-1250-pro/firmware.c | 479 ++++++++++++++++++ linux/ps4-kexec-1250-pro/firmware.h | 64 +++ linux/ps4-kexec-1250-pro/kernel.c | 383 ++++++++++++++ linux/ps4-kexec-1250-pro/kernel.h | 126 +++++ linux/ps4-kexec-1250-pro/kexec.c | 229 +++++++++ linux/ps4-kexec-1250-pro/kexec.h | 38 ++ linux/ps4-kexec-1250-pro/kexec.ld | 25 + linux/ps4-kexec-1250-pro/linux_boot.c | 441 ++++++++++++++++ linux/ps4-kexec-1250-pro/linux_boot.h | 88 ++++ linux/ps4-kexec-1250-pro/linux_thunk.S | 90 ++++ linux/ps4-kexec-1250-pro/reboot.h | 71 +++ linux/ps4-kexec-1250-pro/string.h | 96 ++++ linux/ps4-kexec-1250-pro/types.h | 51 ++ linux/ps4-kexec-1250-pro/uart.c | 64 +++ linux/ps4-kexec-1250-pro/uart.h | 20 + linux/ps4-kexec-1250-pro/x86.h | 149 ++++++ linux/ps4-kexec-1250/LICENSE | 24 + linux/ps4-kexec-1250/Makefile | 37 ++ linux/ps4-kexec-1250/README.md | 121 +++++ linux/ps4-kexec-1250/acpi.c | 296 +++++++++++ linux/ps4-kexec-1250/acpi.h | 18 + linux/ps4-kexec-1250/crc32.c | 102 ++++ linux/ps4-kexec-1250/crc32.h | 7 + linux/ps4-kexec-1250/elf.h | 71 +++ linux/ps4-kexec-1250/firmware.c | 479 ++++++++++++++++++ linux/ps4-kexec-1250/firmware.h | 64 +++ linux/ps4-kexec-1250/kernel.c | 383 ++++++++++++++ linux/ps4-kexec-1250/kernel.h | 126 +++++ linux/ps4-kexec-1250/kexec.c | 229 +++++++++ linux/ps4-kexec-1250/kexec.h | 38 ++ linux/ps4-kexec-1250/kexec.ld | 25 + linux/ps4-kexec-1250/linux_boot.c | 441 ++++++++++++++++ linux/ps4-kexec-1250/linux_boot.h | 88 ++++ linux/ps4-kexec-1250/linux_thunk.S | 90 ++++ linux/ps4-kexec-1250/reboot.h | 71 +++ linux/ps4-kexec-1250/string.h | 96 ++++ linux/ps4-kexec-1250/types.h | 51 ++ linux/ps4-kexec-1250/uart.c | 64 +++ linux/ps4-kexec-1250/uart.h | 20 + linux/ps4-kexec-1250/x86.h | 149 ++++++ 106 files changed, 12732 insertions(+), 14 deletions(-) create mode 100644 freebsd-headers/ps4-offsets/1250.h create mode 100644 linux/fw1250/.keepgithub create mode 100644 linux/ps4-kexec-1250-baikal/LICENSE create mode 100644 linux/ps4-kexec-1250-baikal/Makefile create mode 100644 linux/ps4-kexec-1250-baikal/README.md create mode 100644 linux/ps4-kexec-1250-baikal/acpi.c create mode 100644 linux/ps4-kexec-1250-baikal/acpi.h create mode 100644 linux/ps4-kexec-1250-baikal/acpi_caps.h create mode 100644 linux/ps4-kexec-1250-baikal/crc32.c create mode 100644 linux/ps4-kexec-1250-baikal/crc32.h create mode 100644 linux/ps4-kexec-1250-baikal/elf.h create mode 100644 linux/ps4-kexec-1250-baikal/firmware.c create mode 100644 linux/ps4-kexec-1250-baikal/firmware.h create mode 100644 linux/ps4-kexec-1250-baikal/kernel.c create mode 100644 linux/ps4-kexec-1250-baikal/kernel.h create mode 100644 linux/ps4-kexec-1250-baikal/kexec.c create mode 100644 linux/ps4-kexec-1250-baikal/kexec.h create mode 100644 linux/ps4-kexec-1250-baikal/kexec.ld create mode 100644 linux/ps4-kexec-1250-baikal/linux_boot.c create mode 100644 linux/ps4-kexec-1250-baikal/linux_boot.h create mode 100644 linux/ps4-kexec-1250-baikal/linux_thunk.S create mode 100644 linux/ps4-kexec-1250-baikal/reboot.h create mode 100644 linux/ps4-kexec-1250-baikal/string.h create mode 100644 linux/ps4-kexec-1250-baikal/types.h create mode 100644 linux/ps4-kexec-1250-baikal/uart.c create mode 100644 linux/ps4-kexec-1250-baikal/uart.h create mode 100644 linux/ps4-kexec-1250-baikal/x86.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/LICENSE create mode 100644 linux/ps4-kexec-1250-pro-baikal/Makefile create mode 100644 linux/ps4-kexec-1250-pro-baikal/README.md create mode 100644 linux/ps4-kexec-1250-pro-baikal/acpi.c create mode 100644 linux/ps4-kexec-1250-pro-baikal/acpi.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/acpi_caps.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/crc32.c create mode 100644 linux/ps4-kexec-1250-pro-baikal/crc32.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/elf.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/firmware.c create mode 100644 linux/ps4-kexec-1250-pro-baikal/firmware.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/kernel.c create mode 100644 linux/ps4-kexec-1250-pro-baikal/kernel.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/kexec.c create mode 100644 linux/ps4-kexec-1250-pro-baikal/kexec.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/kexec.ld create mode 100644 linux/ps4-kexec-1250-pro-baikal/linux_boot.c create mode 100644 linux/ps4-kexec-1250-pro-baikal/linux_boot.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/linux_thunk.S create mode 100644 linux/ps4-kexec-1250-pro-baikal/reboot.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/string.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/types.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/uart.c create mode 100644 linux/ps4-kexec-1250-pro-baikal/uart.h create mode 100644 linux/ps4-kexec-1250-pro-baikal/x86.h create mode 100644 linux/ps4-kexec-1250-pro/LICENSE create mode 100644 linux/ps4-kexec-1250-pro/Makefile create mode 100644 linux/ps4-kexec-1250-pro/README.md create mode 100644 linux/ps4-kexec-1250-pro/acpi.c create mode 100644 linux/ps4-kexec-1250-pro/acpi.h create mode 100644 linux/ps4-kexec-1250-pro/crc32.c create mode 100644 linux/ps4-kexec-1250-pro/crc32.h create mode 100644 linux/ps4-kexec-1250-pro/elf.h create mode 100644 linux/ps4-kexec-1250-pro/firmware.c create mode 100644 linux/ps4-kexec-1250-pro/firmware.h create mode 100644 linux/ps4-kexec-1250-pro/kernel.c create mode 100644 linux/ps4-kexec-1250-pro/kernel.h create mode 100644 linux/ps4-kexec-1250-pro/kexec.c create mode 100644 linux/ps4-kexec-1250-pro/kexec.h create mode 100644 linux/ps4-kexec-1250-pro/kexec.ld create mode 100644 linux/ps4-kexec-1250-pro/linux_boot.c create mode 100644 linux/ps4-kexec-1250-pro/linux_boot.h create mode 100644 linux/ps4-kexec-1250-pro/linux_thunk.S create mode 100644 linux/ps4-kexec-1250-pro/reboot.h create mode 100644 linux/ps4-kexec-1250-pro/string.h create mode 100644 linux/ps4-kexec-1250-pro/types.h create mode 100644 linux/ps4-kexec-1250-pro/uart.c create mode 100644 linux/ps4-kexec-1250-pro/uart.h create mode 100644 linux/ps4-kexec-1250-pro/x86.h create mode 100644 linux/ps4-kexec-1250/LICENSE create mode 100644 linux/ps4-kexec-1250/Makefile create mode 100644 linux/ps4-kexec-1250/README.md create mode 100644 linux/ps4-kexec-1250/acpi.c create mode 100644 linux/ps4-kexec-1250/acpi.h create mode 100644 linux/ps4-kexec-1250/crc32.c create mode 100644 linux/ps4-kexec-1250/crc32.h create mode 100644 linux/ps4-kexec-1250/elf.h create mode 100644 linux/ps4-kexec-1250/firmware.c create mode 100644 linux/ps4-kexec-1250/firmware.h create mode 100644 linux/ps4-kexec-1250/kernel.c create mode 100644 linux/ps4-kexec-1250/kernel.h create mode 100644 linux/ps4-kexec-1250/kexec.c create mode 100644 linux/ps4-kexec-1250/kexec.h create mode 100644 linux/ps4-kexec-1250/kexec.ld create mode 100644 linux/ps4-kexec-1250/linux_boot.c create mode 100644 linux/ps4-kexec-1250/linux_boot.h create mode 100644 linux/ps4-kexec-1250/linux_thunk.S create mode 100644 linux/ps4-kexec-1250/reboot.h create mode 100644 linux/ps4-kexec-1250/string.h create mode 100644 linux/ps4-kexec-1250/types.h create mode 100644 linux/ps4-kexec-1250/uart.c create mode 100644 linux/ps4-kexec-1250/uart.h create mode 100644 linux/ps4-kexec-1250/x86.h diff --git a/README.md b/README.md index e544d0a..a5660c1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ![PS4](https://img.shields.io/badge/-PS4-003791?style=flat&logo=PlayStation) Linux Payloads for FW 5.05 - 12.02 +# ![PS4](https://img.shields.io/badge/-PS4-003791?style=flat&logo=PlayStation) Linux Payloads for FW 5.05 - 12.52 (Southbridge: Aeolia & Belize(2) & Baikal) **Linux-Payloads** kexec for PlayStation 4. @@ -23,6 +23,7 @@ you’ll find Linux payloads for your firmware, along with some extra payloads. * FW 11.02 ✅ * FW 11.50 / 11.52 ✅ * FW 12.00 / 12.02 ✅ +* FW 12.50 / 12.52 ✅ ## New diff --git a/freebsd-headers/ps4-offsets/1250.h b/freebsd-headers/ps4-offsets/1250.h new file mode 100644 index 0000000..8b74a46 --- /dev/null +++ b/freebsd-headers/ps4-offsets/1250.h @@ -0,0 +1,14 @@ +#pragma once +#define kernel_offset_xfast_syscall 0x1c0 +#define kernel_offset_allproc 0x1B28538 +#define kernel_offset_vmspace_acquire_ref 0x2F6F60 +#define kernel_offset_vmspace_free 0x2F6D90 +#define kernel_offset_printf 0x2E0420 +#define kernel_offset_kmem_alloc 0x465A20 +#define kernel_offset_kernel_map 0x22D1D50 +#define kernel_offset_sysent 0x1102B70 +#define kernel_offset_proc_rwmem 0x365FE0 +#define kernel_offset_copyin 0x2BD6B0 + +#define kernel_patch_kmem_alloc_1 0x465AEC +#define kernel_patch_kmem_alloc_2 0x465AF4 diff --git a/freebsd-headers/ps4-offsets/kernel.h b/freebsd-headers/ps4-offsets/kernel.h index 444f3ac..f3cdea3 100644 --- a/freebsd-headers/ps4-offsets/kernel.h +++ b/freebsd-headers/ps4-offsets/kernel.h @@ -34,6 +34,9 @@ #ifdef __12_00__ #include "1200.h" #else +#ifdef __12_50__ +#include "1250.h" +#else #error "unsupported firmware" #endif #endif @@ -47,3 +50,4 @@ #endif #endif #endif +#endif diff --git a/linux/Makefile b/linux/Makefile index a9d7329..169dd25 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -1,18 +1,19 @@ -all: fw505/payload-505-1gb.bin fw505/payload-505-2gb.bin fw505/payload-505-3gb.bin fw505/payload-505-4gb.bin fw505/payload-505-1gb-pro.bin fw505/payload-505-2gb-pro.bin fw505/payload-505-3gb-pro.bin fw505/payload-505-4gb-pro.bin fw672/payload-672-1gb.bin fw672/payload-672-2gb.bin fw672/payload-672-3gb.bin fw672/payload-672-4gb.bin fw672/payload-672-1gb-pro.bin fw672/payload-672-2gb-pro.bin fw672/payload-672-3gb-pro.bin fw672/payload-672-4gb-pro.bin fw700/payload-700-1gb.bin fw700/payload-700-2gb.bin fw700/payload-700-3gb.bin fw700/payload-700-4gb.bin fw700/payload-700-1gb-pro.bin fw700/payload-700-2gb-pro.bin fw700/payload-700-3gb-pro.bin fw700/payload-700-4gb-pro.bin fw900/payload-900-1gb.bin fw900/payload-900-2gb.bin fw900/payload-900-3gb.bin fw900/payload-900-4gb.bin fw900/payload-900-1gb-pro.bin fw900/payload-900-2gb-pro.bin fw900/payload-900-3gb-pro.bin fw900/payload-900-4gb-pro.bin fw903/payload-903-1gb.bin fw903/payload-903-2gb.bin fw903/payload-903-3gb.bin fw903/payload-903-4gb.bin fw903/payload-903-1gb-pro.bin fw903/payload-903-2gb-pro.bin fw903/payload-903-3gb-pro.bin fw903/payload-903-4gb-pro.bin fw960/payload-960-1gb.bin fw960/payload-960-2gb.bin fw960/payload-960-3gb.bin fw960/payload-960-4gb.bin fw960/payload-960-1gb-pro.bin fw960/payload-960-2gb-pro.bin fw960/payload-960-3gb-pro.bin fw960/payload-960-4gb-pro.bin fw1000/payload-1000-1gb.bin fw1000/payload-1000-2gb.bin fw1000/payload-1000-3gb.bin fw1000/payload-1000-4gb.bin fw1000/payload-1000-1gb-pro.bin fw1000/payload-1000-2gb-pro.bin fw1000/payload-1000-3gb-pro.bin fw1000/payload-1000-4gb-pro.bin fw1050/payload-1050-1gb.bin fw1050/payload-1050-2gb.bin fw1050/payload-1050-3gb.bin fw1050/payload-1050-4gb.bin fw1050/payload-1050-1gb-pro.bin fw1050/payload-1050-2gb-pro.bin fw1050/payload-1050-3gb-pro.bin fw1050/payload-1050-4gb-pro.bin fw1100/payload-1100-1gb.bin fw1100/payload-1100-2gb.bin fw1100/payload-1100-3gb.bin fw1100/payload-1100-4gb.bin fw1100/payload-1100-1gb-pro.bin fw1100/payload-1100-2gb-pro.bin fw1100/payload-1100-3gb-pro.bin fw1100/payload-1100-4gb-pro.bin fw1102/payload-1102-1gb.bin fw1102/payload-1102-2gb.bin fw1102/payload-1102-3gb.bin fw1102/payload-1102-4gb.bin fw1102/payload-1102-1gb-pro.bin fw1102/payload-1102-2gb-pro.bin fw1102/payload-1102-3gb-pro.bin fw1102/payload-1102-4gb-pro.bin fw1150/payload-1150-1gb.bin fw1150/payload-1150-2gb.bin fw1150/payload-1150-3gb.bin fw1150/payload-1150-4gb.bin fw1150/payload-1150-1gb-pro.bin fw1150/payload-1150-2gb-pro.bin fw1150/payload-1150-3gb-pro.bin fw1150/payload-1150-4gb-pro.bin fw1200/payload-1200-1gb.bin fw1200/payload-1200-2gb.bin fw1200/payload-1200-3gb.bin fw1200/payload-1200-4gb.bin fw1200/payload-1200-1gb-pro.bin fw1200/payload-1200-2gb-pro.bin fw1200/payload-1200-3gb-pro.bin fw1200/payload-1200-4gb-pro.bin fw505/payload-505-1gb-baikal.bin fw505/payload-505-2gb-baikal.bin fw505/payload-505-3gb-baikal.bin fw505/payload-505-4gb-baikal.bin fw505/payload-505-1gb-pro-baikal.bin fw505/payload-505-2gb-pro-baikal.bin fw505/payload-505-3gb-pro-baikal.bin fw505/payload-505-4gb-pro-baikal.bin fw672/payload-672-1gb-baikal.bin fw672/payload-672-2gb-baikal.bin fw672/payload-672-3gb-baikal.bin fw672/payload-672-4gb-baikal.bin fw672/payload-672-1gb-pro-baikal.bin fw672/payload-672-2gb-pro-baikal.bin fw672/payload-672-3gb-pro-baikal.bin fw672/payload-672-4gb-pro-baikal.bin fw700/payload-700-1gb-baikal.bin fw700/payload-700-2gb-baikal.bin fw700/payload-700-3gb-baikal.bin fw700/payload-700-4gb-baikal.bin fw700/payload-700-1gb-pro-baikal.bin fw700/payload-700-2gb-pro-baikal.bin fw700/payload-700-3gb-pro-baikal.bin fw700/payload-700-4gb-pro-baikal.bin fw900/payload-900-1gb-baikal.bin fw900/payload-900-2gb-baikal.bin fw900/payload-900-3gb-baikal.bin fw900/payload-900-4gb-baikal.bin fw900/payload-900-1gb-pro-baikal.bin fw900/payload-900-2gb-pro-baikal.bin fw900/payload-900-3gb-pro-baikal.bin fw900/payload-900-4gb-pro-baikal.bin fw903/payload-903-1gb-baikal.bin fw903/payload-903-2gb-baikal.bin fw903/payload-903-3gb-baikal.bin fw903/payload-903-4gb-baikal.bin fw903/payload-903-1gb-pro-baikal.bin fw903/payload-903-2gb-pro-baikal.bin fw903/payload-903-3gb-pro-baikal.bin fw903/payload-903-4gb-pro-baikal.bin fw960/payload-960-1gb-baikal.bin fw960/payload-960-2gb-baikal.bin fw960/payload-960-3gb-baikal.bin fw960/payload-960-4gb-baikal.bin fw960/payload-960-1gb-pro-baikal.bin fw960/payload-960-2gb-pro-baikal.bin fw960/payload-960-3gb-pro-baikal.bin fw960/payload-960-4gb-pro-baikal.bin fw1000/payload-1000-1gb-baikal.bin fw1000/payload-1000-2gb-baikal.bin fw1000/payload-1000-3gb-baikal.bin fw1000/payload-1000-4gb-baikal.bin fw1000/payload-1000-1gb-pro-baikal.bin fw1000/payload-1000-2gb-pro-baikal.bin fw1000/payload-1000-3gb-pro-baikal.bin fw1000/payload-1000-4gb-pro-baikal.bin fw1050/payload-1050-1gb-baikal.bin fw1050/payload-1050-2gb-baikal.bin fw1050/payload-1050-3gb-baikal.bin fw1050/payload-1050-4gb-baikal.bin fw1050/payload-1050-1gb-pro-baikal.bin fw1050/payload-1050-2gb-pro-baikal.bin fw1050/payload-1050-3gb-pro-baikal.bin fw1050/payload-1050-4gb-pro-baikal.bin fw1100/payload-1100-1gb-baikal.bin fw1100/payload-1100-2gb-baikal.bin fw1100/payload-1100-3gb-baikal.bin fw1100/payload-1100-4gb-baikal.bin fw1100/payload-1100-1gb-pro-baikal.bin fw1100/payload-1100-2gb-pro-baikal.bin fw1100/payload-1100-3gb-pro-baikal.bin fw1100/payload-1100-4gb-pro-baikal.bin fw1102/payload-1102-1gb-baikal.bin fw1102/payload-1102-2gb-baikal.bin fw1102/payload-1102-3gb-baikal.bin fw1102/payload-1102-4gb-baikal.bin fw1102/payload-1102-1gb-pro-baikal.bin fw1102/payload-1102-2gb-pro-baikal.bin fw1102/payload-1102-3gb-pro-baikal.bin fw1102/payload-1102-4gb-pro-baikal.bin fw1150/payload-1150-1gb-baikal.bin fw1150/payload-1150-2gb-baikal.bin fw1150/payload-1150-3gb-baikal.bin fw1150/payload-1150-4gb-baikal.bin fw1150/payload-1150-1gb-pro-baikal.bin fw1150/payload-1150-2gb-pro-baikal.bin fw1150/payload-1150-3gb-pro-baikal.bin fw1150/payload-1150-4gb-pro-baikal.bin fw1200/payload-1200-1gb-baikal.bin fw1200/payload-1200-2gb-baikal.bin fw1200/payload-1200-3gb-baikal.bin fw1200/payload-1200-4gb-baikal.bin fw1200/payload-1200-1gb-pro-baikal.bin fw1200/payload-1200-2gb-pro-baikal.bin fw1200/payload-1200-3gb-pro-baikal.bin fw1200/payload-1200-4gb-pro-baikal.bin +FIRMWARES = 505 672 700 900 903 960 1000 1050 1100 1102 1150 1200 1250 +SIZES = 1gb 2gb 3gb 4gb +# This uses a nested loop style logic to generate the file names automatically +PAYLOADS = $(foreach fw,$(FIRMWARES), \ + $(foreach sz,$(SIZES), \ + fw$(fw)/payload-$(fw)-$(sz).bin \ + fw$(fw)/payload-$(fw)-$(sz)-pro.bin \ + fw$(fw)/payload-$(fw)-$(sz)-baikal.bin \ + fw$(fw)/payload-$(fw)-$(sz)-pro-baikal.bin \ + ) \ + ) + +all: $(PAYLOADS) clean: - rm -f fw505/* - rm -f fw672/* - rm -f fw700/* - rm -f fw900/* - rm -f fw903/* - rm -f fw960/* - rm -f fw1000/* - rm -f fw1050/* - rm -f fw1100/* - rm -f fw1102/* - rm -f fw1150/* - rm -f fw1200/* ; + rm -f fw*/* cd ps4-kexec-505; make clean cd ps4-kexec-505-pro; make clean @@ -62,6 +63,10 @@ clean: cd ps4-kexec-1200-pro; make clean cd ps4-kexec-1200-baikal; make clean cd ps4-kexec-1200-pro-baikal; make clean + cd ps4-kexec-1250; make clean + cd ps4-kexec-1250-pro; make clean + cd ps4-kexec-1250-baikal; make clean + cd ps4-kexec-1250-pro-baikal; make clean cd ../lib/; make clean ../lib/lib.a: @@ -787,6 +792,65 @@ fw1200/payload-1200-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-120 fw1200/payload-1200-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1200-pro-baikal/kexec.bin gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1200/payload-1200-4gb-pro-baikal.elf -fPIE -ffreestanding +ps4-kexec-1250/kexec.bin: + cd ps4-kexec-1250; make + +fw1250/payload-1250-1gb.elf: ../lib/lib.a main.c ps4-kexec-1250/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 main.c -Wl,-gc-sections -o fw1250/payload-1250-1gb.elf -fPIE -ffreestanding + +fw1250/payload-1250-2gb.elf: ../lib/lib.a main.c ps4-kexec-1250/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1250/payload-1250-2gb.elf -fPIE -ffreestanding + +fw1250/payload-1250-3gb.elf: ../lib/lib.a main.c ps4-kexec-1250/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1250/payload-1250-3gb.elf -fPIE -ffreestanding + +fw1250/payload-1250-4gb.elf: ../lib/lib.a main.c ps4-kexec-1250/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1250/payload-1250-4gb.elf -fPIE -ffreestanding +ps4-kexec-1200-pro/kexec.bin: + cd ps4-kexec-1200-pro; make + +fw1250/payload-1250-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1250-pro/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 main.c -Wl,-gc-sections -o fw1250/payload-1250-1gb-pro.elf -fPIE -ffreestanding + +fw1250/payload-1250-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1250-pro/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1250/payload-1250-2gb-pro.elf -fPIE -ffreestanding + +fw1250/payload-1250-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1250-pro/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1250/payload-1250-3gb-pro.elf -fPIE -ffreestanding + +fw1250/payload-1250-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1250-pro/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1250/payload-1250-4gb-pro.elf -fPIE -ffreestanding + +ps4-kexec-1250-baikal/kexec.bin: + cd ps4-kexec-1250-baikal; make + +fw1250/payload-1250-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-1gb-baikal.elf -fPIE -ffreestanding + +fw1250/payload-1250-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-2gb-baikal.elf -fPIE -ffreestanding + +fw1250/payload-1250-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-3gb-baikal.elf -fPIE -ffreestanding + +fw1250/payload-1250-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-4gb-baikal.elf -fPIE -ffreestanding + +ps4-kexec-1250-pro-baikal/kexec.bin: + cd ps4-kexec-1250-pro-baikal; make + +fw1250/payload-1250-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-pro-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-1gb-pro-baikal.elf -fPIE -ffreestanding + +fw1250/payload-1250-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-pro-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-2gb-pro-baikal.elf -fPIE -ffreestanding + +fw1250/payload-1250-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-pro-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-3gb-pro-baikal.elf -fPIE -ffreestanding + +fw1250/payload-1250-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-pro-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-4gb-pro-baikal.elf -fPIE -ffreestanding + %.bin: %.elf objcopy $< --only-section .text --only-section .data --only-section .bss --only-section .rodata -O binary $@ file $@ | fgrep -q '$@: DOS executable (COM)' diff --git a/linux/fw1250/.keepgithub b/linux/fw1250/.keepgithub new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/linux/fw1250/.keepgithub @@ -0,0 +1 @@ + diff --git a/linux/magic.h b/linux/magic.h index 66b5d6d..c1148c2 100644 --- a/linux/magic.h +++ b/linux/magic.h @@ -450,4 +450,40 @@ #define kern_off_set_cu_power_gate 0x4ba3e0 #define kern_off_pstate_before_shutdown 0x3a2360 #define kern_off_set_nclk_mem_spd 0 + +#elif defined PS4_12_50 //ArabPixel +#define kern_off_printf 0x2E0420 +#define kern_off_snprintf 0x2E0720 +#define kern_off_copyin 0x2BD6B0 +#define kern_off_copyout 0x2BD5C0 +#define kern_off_copyinstr 0x2BDB60 +#define kern_off_kmem_alloc_contig 0x24D410 +#define kern_off_kmem_free 0x465BF4 +#define kern_off_pmap_extract 0x573D0 +#define kern_off_pmap_protect 0x58570 +#define kern_off_sched_pin 0x231640 +#define kern_off_sched_unpin 0x231660 +#define kern_off_smp_rendezvous 0x1AD520 +#define kern_off_smp_no_rendevous_barrier 0x1AD330 +#define kern_off_icc_query_nowait 0x447B10 +#define kern_off_kernel_map 0x22D1D50 +#define kern_off_sysent 0x1102B70 +#define kern_off_kernel_pmap_store 0x1b2c3a0 +#define kern_off_Starsha_UcodeInfo 0 +#define kern_off_gpu_devid_is_9924 0x4AC560 +#define kern_off_gc_get_fw_info 0x4BAF30 +#define kern_off_pml4pml4i 0x1B2C390 +#define kern_off_dmpml4i 0x1B2C394 +#define kern_off_dmpdpi 0x1B2C398 +#define kern_off_eap_hdd_key 0x26C4CF0 +#define kern_off_edid 0x275E148 +#define kern_off_wlanbt 0x478A30 +#define kern_off_kern_reboot 0x3A1DB0 +#define kern_off_set_gpu_freq 0x4B9A70 +#define kern_off_set_pstate 0x4BBE40 +#define kern_off_update_vddnp 0x4BA010 +#define kern_off_set_cu_power_gate 0x4BA420 +#define kern_off_pstate_before_shutdown 0x3A239F +#define kern_off_set_nclk_mem_spd 0 + #endif diff --git a/linux/main-baikal.c b/linux/main-baikal.c index acf81c1..204b8ea 100644 --- a/linux/main-baikal.c +++ b/linux/main-baikal.c @@ -44,6 +44,9 @@ asm("ps4kexec:\n.incbin \"ps4-kexec-1150-baikal/kexec.bin\"\nps4kexec_end:\n"); #elif defined(__12_00__) asm("ps4kexec:\n.incbin \"ps4-kexec-1200-baikal/kexec.bin\"\nps4kexec_end:\n"); #include "magic.h" +#elif defined(__12_50__) +asm("ps4kexec:\n.incbin \"ps4-kexec-1250-baikal/kexec.bin\"\nps4kexec_end:\n"); +#include "magic.h" #else #error "unsupported firmware" #endif diff --git a/linux/main.c b/linux/main.c index 363b75a..955c025 100644 --- a/linux/main.c +++ b/linux/main.c @@ -44,6 +44,9 @@ asm("ps4kexec:\n.incbin \"ps4-kexec-1150/kexec.bin\"\nps4kexec_end:\n"); #elif defined(__12_00__) asm("ps4kexec:\n.incbin \"ps4-kexec-1200/kexec.bin\"\nps4kexec_end:\n"); #include "magic.h" +#elif defined(__12_50__) +asm("ps4kexec:\n.incbin \"ps4-kexec-1250/kexec.bin\"\nps4kexec_end:\n"); +#include "magic.h" #else #error "unsupported firmware" #endif diff --git a/linux/ps4-kexec-1250-baikal/LICENSE b/linux/ps4-kexec-1250-baikal/LICENSE new file mode 100644 index 0000000..6c1cbba --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2015-2016 shuffle2 +Copyright (C) 2015-2016 Hector Martin "marcan" +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/ps4-kexec-1250-baikal/Makefile b/linux/ps4-kexec-1250-baikal/Makefile new file mode 100644 index 0000000..4101e52 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/Makefile @@ -0,0 +1,37 @@ +TOOLCHAIN_PREFIX ?= +CC = $(TOOLCHAIN_PREFIX)gcc +AR = $(TOOLCHAIN_PREFIX)ar +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy + +CFLAGS=$(CFLAG) -DPS4_12_50 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX +CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ + -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ + -fno-asynchronous-unwind-tables \ + -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ + -mcmodel=small -mno-red-zone + +SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ + acpi.c crc32.c + +OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) +DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld + +all: libkexec.a kexec.bin + +%.o: %.c *.h + $(CC) -c $(CFLAGS) -o $@ $< + +%.o: %.S + $(CC) -c $(CFLAGS) -o $@ $< + +libkexec.a: $(OBJS) + $(AR) -rc $@ $(OBJS) + +kexec.elf: libkexec.a kexec.ld + $(CC) $(CFLAGS) -o $@ libkexec.a + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +clean: + rm -f libkexec.a kexec.elf kexec.bin $(OBJS) diff --git a/linux/ps4-kexec-1250-baikal/README.md b/linux/ps4-kexec-1250-baikal/README.md new file mode 100644 index 0000000..6500878 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/README.md @@ -0,0 +1,121 @@ +# PS4 kexec implementation + +This repo implements a kexec()-style system call for the PS4 Orbis kernel +(FreeBSD derivative). This is designed to boot a Linux kernel directly from +FreeBSD. + +This is not an exploit. It is useless without some mechanism of injecting code +into the PS4 OS kernel. + +## Building + +To build a kexec.bin relocatable binary using the supplied Makefile, just type +`make`. This will also build a kexec.a archive. You can either use the binary +directly, or link the archive into your own project. + +If you link kexec.a with your own code, you need to supply the two symbols +`_start` and `_end` in your linker script, as `kernel_init()` will try to remap +all pages covered by that range as RWX (to make global variable accesses work). +Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this +feature, if you have already taken care of page permissions for the code. + +If you use a compiler toolchain that have a special prefix you can declare it +by passing TOOLCHAIN_PREFIX option to the Makefile like this: + + make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' + +## Usage + +The code is designed to be completely standalone. There is a single entry point: + + int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); + +Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel +symbols and install the sys_kexec system call. The syscall is registered +as number 153 by default (you can change this in kexec.h). The return value +is 0 on success, or negative on error. + +You may pass something other than NULL as `early_printf`. In that case, that +function will be used for debug output during early symbol resolution, before +printf is available. + +Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is +enabled by default, symtab also disappears in newer kernel, we have to +hardcode offsets for some symbols. Currently we use the `early_printf` +given by user to caculate the base address of kernel, then relocate all the +symbols from the kernel base. You could enable this feature like this: + + make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' + +If you do not want to call the syscall from userspace, you can pass the address +of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the +address of `sys_kexec`, so you can invoke it manually (see kexec.h for +its prototype and how the arguments are passed). + +If you are using the standalone kexec.bin blob, then the `kexec_init` function +is always located at offset 0, so simply call the base address of the blob. +Don't forget to pass two NULL arguments (or the appropriate pointers). + +The injected `sys_kexec` system call takes (userspace) pointers to the kernel +and initramfs blobs, their sizes, and a pointer to the (null-terminated) command +line string. From userspace, this looks like this: + + int kexec(void *kernel_image, size_t image_size, + void *initramfs, size_t initramfs_size, + const char *cmdline); + + // syscall() usage: + syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); + +`kexec()` will load the kernel and initramfs into memory, but will not directly +boot them. To boot the loaded kernel, shut down the system. This can be +accomplished by pressing the power button, but can also be done more quickly +and reliably from userspace with the following sequence of system calls (this +kills userspace quickly but still does a controlled filesystem unmount): + + int evf = syscall(540, "SceSysCoreReboot"); + syscall(546, evf, 0x4000, 0); + syscall(541, evf); + // should be syscall(37, 1, 30) but only tested via kill symbol + kill(1, 30); + +Note that this software should be loaded into kernel memory space. If you are +running kernel code from userland mappings, you should either switch to kernel +mappings or separately copy kexec.bin to a location in kernel address space. +While syscalls or exploit code may run properly from userland, the shutdown hook +will not, as it will be called from a different process context. + +## Features + +`kernel_init()` will automatically find the Orbis OS kernel and resolve all +necessary symbols to work. There are no static symbol dependencies. If +`DO_NOT_REMAP_RWX` is not defined (the default), it will also patch +`pmap_protect` to disable the W^X restriction. + +In addition to loading the user-supplied initramfs, `kexec` will locate the +Radeon firmware blobs inside Orbis OS, extract them, convert them to a format +suitable for Linux, and append them as an additional initramfs cpio image to +the existing initramfs. This avoids the need to distribute the Radeon firmware +blobs. The `radeon` module, when compiled into the kernel, will automatically +load this firmware on boot. Note however that most typical initramfs scripts +will wipe the initramfs contents while pivoting to the real system, so if you +compile `radeon` as a module you may not be able to access the firmware after +boot. To cover that case, add some code to your initramfs `/init` script to +copy the firmware to a tmpfs mounted on the real filesystem: + + # assuming real root FS is mounted on /mnt + + mkdir -p /mnt/lib/firmware/radeon + mount -t tmpfs none /mnt/lib/firmware/radeon + cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ + + # now switch_root to /mnt + +This avoids having to permanently store copies of the Radeon firmware, which +isn't really necessary for most use cases. + +There is significant debug logging available, which will appear on the system +UART. Most of the code relies on the kernel `printf` implementation, and +therefore you should patch out the UART output blanker to see it. The final +code that runs on the boot CPU before booting the kernel uses direct UART +writes and is not affected by the blanking feature of Orbis OS. diff --git a/linux/ps4-kexec-1250-baikal/acpi.c b/linux/ps4-kexec-1250-baikal/acpi.c new file mode 100644 index 0000000..bfccf08 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/acpi.c @@ -0,0 +1,320 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "types.h" +#include "kernel.h" +#include "acpi.h" +#include "acpi_caps.h" + +#ifdef TESTING +# include +# include +# include +# include +# include +# include +#else +# include "kernel.h" +# include "string.h" +# define printf kern.printf +#endif + +#define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) +#define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) + +#define PACKED __attribute__((packed)) + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +struct RSDP { + u64 sig; + u8 checksum; + u8 oemid[6]; + u8 rev; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 rsvd[3]; +} PACKED; + +struct SDTH { + u32 sig; + u32 length; + u8 rev; + u8 checksum; + u8 oem_id[6]; + u8 oem_tid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; +} PACKED; + +struct RSDT { + struct SDTH hdr; + u32 table_addr[]; +} PACKED; + +struct XSDT { + struct SDTH hdr; + u64 table_addr[]; +} PACKED; + +struct FADT { + struct SDTH hdr; + u32 facs; + u32 dsdt; + // more stuff... +} PACKED; + +struct ivhd_entry4 { + u8 type; + u16 devid; + u8 flags; +} PACKED; + +struct ivhd_header { + u8 type; + u8 flags; + u16 length; + u16 devid; + u16 cap_ptr; + u64 mmio_phys; + u16 pci_seg; + u16 info; + u32 efr_attr; +} PACKED; + +struct IVRS { + struct SDTH hdr; + u32 IVinfo; + u8 reserved[8]; + struct ivhd_header hd_hdr; + struct ivhd_entry4 hd_entries[3]; +} PACKED; + +struct MMIO { + u64 baseAddressECM; + u16 pciSegmentGroup; + u8 startPCIBus; + u8 endPCIBus; + u32 reserved; +} PACKED; + +// We have enough space to use the second half of the 64KB table area +// as scratch space for building the tables +#define BUFFER_OFF 0x8000 + +#define P2M(p) (((u64)(p)) - phys_base + map_base) +#define M2P(p) ((((void*)(p)) - map_base) + phys_base) +#define B2P(p) ((((void*)(p)) - buf_base) + phys_base) +#define P2B(p) ((((void*)(p)) - phys_base) + buf_base) + +#define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) +#define PADB(s) p += (s) +#define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) +#define ALLOC(t) (t)ALLOCB(sizeof(t)) +#define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) +#define COPYT(s) COPYB(((struct SDTH*)s)->length, s) +#define COPYTP(s) COPYT(P2M(s)) +#define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) +#define COPYP(t, s) COPY(t, P2M(s)) + +static void rsdp_checksum(struct RSDP *rsdp) { + rsdp->checksum = rsdp->ext_checksum = 0; + + u8 sum = 0; + for (int i = 0; i < 20; i++) + sum += ((u8*)rsdp)[i]; + rsdp->checksum = -sum; + sum = 0; + for (int i = 0; i < sizeof(*rsdp); i++) + sum += ((u8*)rsdp)[i]; + rsdp->ext_checksum = -sum; +} + +static void table_checksum(void *table) { + struct SDTH *hdr = table; + hdr->checksum = 0; + u8 sum = 0; + for (int i = 0; i < hdr->length; i++) + sum += ((u8*)table)[i]; + hdr->checksum = -sum; +} + +#define IVHD_FLAG_ISOC_EN_MASK 0x08 +#define IVHD_DEV_ALL 0x01 +#define IVHD_DEV_SELECT 0x02 +#define IVHD_DEV_SELECT_RANGE_START 0x03 +#define IVHD_DEV_RANGE_END 0x04 + +#define ACPI_DEVFLAG_SYSMGT1 0x10 +#define ACPI_DEVFLAG_SYSMGT2 0x20 + +static void *build_ivrs(struct IVRS *ivrs) { + memset(ivrs, 0, sizeof(*ivrs)); + + ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); + ivrs->hdr.length = sizeof(*ivrs); + ivrs->hdr.rev = 1; + memcpy(ivrs->hdr.oem_id, "F0F ", 6); + memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); + ivrs->hdr.oem_rev = 0x20161225; + memcpy(ivrs->hdr.creator_id, "KEXC", 4); + ivrs->hdr.creator_rev = 0x20161225; + ivrs->IVinfo = 0x00203040; //48882_IOMMU.pdf page 251 + + struct ivhd_header *hdr = &ivrs->hd_hdr; //48882_IOMMU.pdf page 254 + hdr->type = 0x10; + hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; + hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); + hdr->devid = PCI_DEVFN(0, 2); + hdr->cap_ptr = 0x40; // from config space + 0x34 + hdr->mmio_phys = 0xfc000000; //Base address of IOMMU control registers in MMIO space + hdr->pci_seg = 0; + hdr->info = 0; // msi msg num? (the pci cap should be written by software) + // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 + hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); //Feature Reporting Field, 48882_IOMMU.pdf page 255 + + struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; + // on fbsd, all aeolia devfns have active entries except memories (func 6) + // not sure if this is just because it wasn't in use when i dumped it? + // all entries are r/w + // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) + // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) + // Modes: + // 4 level: + // apcie + // 3 level: + // all others + + // the way to encode this info into the IVHD entries is fairly arbitrary... + entries[0].type = IVHD_DEV_SELECT; //DTE setting applies to the device specifed in DevID field. + entries[0].devid = PCI_DEVFN(20, 0); //vendorId: 104D, deviceId: 90D7; Sony Baikal ACPI + entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; + + entries[1].type = IVHD_DEV_SELECT_RANGE_START; + entries[1].devid = PCI_DEVFN(20, 1); + entries[1].flags = 0; //Identifies a device able to assert INIT interrupts (page 262) + entries[2].type = IVHD_DEV_RANGE_END; + entries[2].devid = PCI_DEVFN(20, 7); + entries[2].flags = 0; //Identifies a device able to assert INIT interrupts + + table_checksum(ivrs); + return ivrs + 1; +} + +void fix_acpi_tables(void *map_base, u64 phys_base) +{ + void *buf_base = map_base + 0x8000; + void *p = buf_base; + memset(buf_base, 0, 0x8000); + + printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); + + struct RSDP *rsdp = COPYP(struct RSDP, phys_base); + printf("RSDT at 0x%x\n", rsdp->rsdt_addr); + printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); + + struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); + struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); + rsdp->rsdt_addr = B2P(rsdt); + + PADB(0x30); // this gives us space for new tables + + struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); + rsdp->xsdt_addr = B2P(xsdt); + + PADB(0x60); + + struct FADT *fadt = NULL; + + int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; + int i; + for (i = 0; i < cnt; i++) { + struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); + printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); + switch (hdr->sig) { + case SIG32('F', 'A', 'C', 'P'): + { + fadt = (void*)hdr; + printf("FACS at 0x%x\n", fadt->facs); + printf("DSDT at 0x%x\n", fadt->dsdt); + // Sony puts the FACS before the FADT, unaligned, which is + // noncompliant, but let's keep it there + u8 *facs = COPYB(64, P2M(fadt->facs)); + fadt = (void*)(hdr = COPYT(hdr)); + fadt->facs = B2P(facs); + PADB(0x38); + break; + } + case SIG32('S', 'S', 'D', 'T'): + { + // Put the DSDT before the SSDT + if (fadt) { + PADB(0xf0); + u8 *dsdt = COPYTP(fadt->dsdt); + fadt->dsdt = B2P(dsdt); + PADB(0x174); + table_checksum(fadt); + } else { + printf("ERROR: no FADT yet?\n"); + } + hdr = COPYT(hdr); + break; + } + default: + hdr = COPYT(hdr); + } + table_checksum(hdr); + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); + } + + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); + i++; + p = build_ivrs(p); + + rsdt->hdr.length = sizeof(*rsdt) + 4 * i; + xsdt->hdr.length = sizeof(*xsdt) + 8 * i; + + rsdp_checksum(rsdp); + table_checksum(rsdt); + table_checksum(xsdt); + memcpy(map_base, buf_base, p - buf_base); +} + +u32 msi_mask(unsigned x) { + /* Don't shift by >= width of type */ + if (x >= 5) + return 0xffffffff; + return (1 << (1 << x)) - 1; +} +void disableMSI(u64 MSICapabilityRegAddr) { + PPCI_MSI_CAPABILITY pMSICapability = (PPCI_MSI_CAPABILITY)PA_TO_DM(MSICapabilityRegAddr); + if (pMSICapability->msiEnable == 1) + pMSICapability->msiEnable = 0; + pMSICapability->mask64 = msi_mask(pMSICapability->multipleMessageCapable); +} + +#ifdef TESTING + +int main(int argc, char **argv) +{ + int fd; + void *base; + + fd = open(argv[1], O_RDWR); + base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + fix_acpi_tables(base, 0xe0000); + return 0; +} + +#endif diff --git a/linux/ps4-kexec-1250-baikal/acpi.h b/linux/ps4-kexec-1250-baikal/acpi.h new file mode 100644 index 0000000..45f2834 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/acpi.h @@ -0,0 +1,22 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ACPI_H +#define ACPI_H + +#define PACKED __attribute__((packed)) + +void fix_acpi_tables(void *map_base, u64 phys_base); + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) + +void disableMSI(u64 MSICapabilityRegAddr); + +#endif diff --git a/linux/ps4-kexec-1250-baikal/acpi_caps.h b/linux/ps4-kexec-1250-baikal/acpi_caps.h new file mode 100644 index 0000000..070e9f6 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/acpi_caps.h @@ -0,0 +1,30 @@ +#ifndef ACPI_CAPS_H +#define ACPI_CAPS_H + +#define UCHAR u8 +#define USHORT u16 +#define ULONG u32 +#define PACKED __attribute__((packed)) +typedef struct PACKED _PCI_CAPABILITIES_HEADER { + UCHAR CapabilityID; + UCHAR Next; +} PCI_CAPABILITIES_HEADER, *PPCI_CAPABILITIES_HEADER; + +typedef struct PACKED _PCI_MSI_CAPABILITY { + PCI_CAPABILITIES_HEADER Header; + u16 msiEnable : 1, multipleMessageCapable : 3, multipleMessageEnable : 3, address64Capable : 1, reserved0 : 8; + u32 lowerAddress : 30, reserved1 : 2; + union { + struct { + u32 upperAddress; + u32 messageData64 : 16, reservedData64 : 16; + u32 mask64; + }; + struct { + u32 messageData32 : 16, reservedData32 : 16; + u32 mask32; + }; + }; +} PCI_MSI_CAPABILITY, *PPCI_MSI_CAPABILITY; + +#endif diff --git a/linux/ps4-kexec-1250-baikal/crc32.c b/linux/ps4-kexec-1250-baikal/crc32.c new file mode 100644 index 0000000..7836c18 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/crc32.c @@ -0,0 +1,102 @@ +/*- +* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or +* code or tables extracted from it, as desired without restriction. +* +* First, the polynomial itself and its table of feedback terms. The +* polynomial is +* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 +* +* Note that we take it "backwards" and put the highest-order term in +* the lowest-order bit. The X^32 term is "implied"; the LSB is the +* X^31 term, etc. The X^0 term (usually shown as "+1") results in +* the MSB being 1 +* +* Note that the usual hardware shift register implementation, which +* is what we're using (we're merely optimizing it by doing eight-bit +* chunks at a time) shifts bits into the lowest-order term. In our +* implementation, that means shifting towards the right. Why do we +* do it this way? Because the calculated CRC must be transmitted in +* order from highest-order term to lowest-order term. UARTs transmit +* characters in order from LSB to MSB. By storing the CRC this way +* we hand it to the UART in the order low-byte to high-byte; the UART +* sends each low-bit to hight-bit; and the result is transmission bit +* by bit from highest- to lowest-order term without requiring any bit +* shuffling on our part. Reception works similarly +* +* The feedback terms table consists of 256, 32-bit entries. Notes +* +* The table can be generated at runtime if desired; code to do so +* is shown later. It might not be obvious, but the feedback +* terms simply represent the results of eight shift/xor opera +* tions for all combinations of data and CRC register values +* +* The values must be right-shifted by eight bits by the "updcrc +* logic; the shift must be unsigned (bring in zeroes). On some +* hardware you could probably optimize the shift in assembler by +* using byte-swap instructions +* polynomial $edb88320 +* +* +* CRC32 code derived from work by Gary S. Brown. +*/ + +#include "crc32.h" + +static u32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 crc32(u32 crc, const void *buf, size_t size) +{ + const u8 *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} diff --git a/linux/ps4-kexec-1250-baikal/crc32.h b/linux/ps4-kexec-1250-baikal/crc32.h new file mode 100644 index 0000000..777278d --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/crc32.h @@ -0,0 +1,7 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +u32 crc32(u32 crc, const void *buf, size_t size); + +#endif diff --git a/linux/ps4-kexec-1250-baikal/elf.h b/linux/ps4-kexec-1250-baikal/elf.h new file mode 100644 index 0000000..63371c7 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/elf.h @@ -0,0 +1,71 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + u8 e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + u64 e_entry; + u64 e_phoff; + u64 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf64_Ehdr; + +typedef struct { + u32 p_type; + u32 p_flags; + u64 p_offset; + void *p_vaddr; + u64 p_paddr; + u64 p_filesz; + u64 p_memsz; + u64 p_align; +} Elf64_Phdr; + +#define PT_DYNAMIC 2 +#define PT_PHDR 6 + +#define DT_NULL 0 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_STRSZ 10 + +typedef struct { + s64 d_tag; + union { + u64 d_val; + void *d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + void *st_value; + u64 st_size; +} Elf64_Sym; + +#endif diff --git a/linux/ps4-kexec-1250-baikal/firmware.c b/linux/ps4-kexec-1250-baikal/firmware.c new file mode 100644 index 0000000..a831b1c --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/firmware.c @@ -0,0 +1,479 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "firmware.h" +#include "types.h" +#include "kernel.h" +#include "string.h" +#include "types.h" +#include "crc32.h" + +#define DIR 0040755 +#define FILE 0100644 + +struct firmware_header { + u32 size_bytes; + u32 header_size_bytes; + u16 header_version_major; + u16 header_version_minor; + u16 ip_version_major; + u16 ip_version_minor; + u32 ucode_version; + u32 ucode_size_bytes; + u32 ucode_array_offset_bytes; + u32 crc32; + union { + struct { + u32 ucode_feature_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } gfx1; + struct { + u32 ucode_feature_version; + u32 save_and_restore_offset; + u32 clear_state_descriptor_offset; + u32 avail_scratch_ram_locations; + u32 master_pkt_description_offset; + u8 end[]; + } rlc1; + struct { + u32 ucode_feature_version; + u32 ucode_change_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } sdma1; + u8 raw[0xe0]; + }; +}; + +static inline char hex(u8 c) +{ + if (c <= 9) + return '0' + c; + return 'a' + c - 10; +} + +static void hex8(u8 **p, u32 val) +{ + *(*p)++ = hex(val >> 28); + *(*p)++ = hex((val >> 24) & 0xf); + *(*p)++ = hex((val >> 20) & 0xf); + *(*p)++ = hex((val >> 16) & 0xf); + *(*p)++ = hex((val >> 12) & 0xf); + *(*p)++ = hex((val >> 8) & 0xf); + *(*p)++ = hex((val >> 4) & 0xf); + *(*p)++ = hex(val & 0xf); +} + +void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) +{ + size_t name_len = strlen(name); + + // Pad to 4 byte multiple + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; + + memcpy(*p, "070701", 6); + *p += 6; + hex8(p, 0); // c_ino + hex8(p, mode); // c_mode + hex8(p, 0); // c_uid + hex8(p, 0); // c_gid + hex8(p, 1); // c_nlink + hex8(p, 0); // c_mtime + hex8(p, size); // c_filesize + hex8(p, 0); // c_maj + hex8(p, 0); // c_min + hex8(p, 0); // c_rmaj + hex8(p, 0); // c_rmin + hex8(p, name_len + 1); // c_namesize + hex8(p, 0); // c_chksum + memcpy(*p, name, name_len); + *p += name_len; + *(*p)++ = 0; + + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; +} + +struct fw_header_t { + u64 size_words; + char *unk_ident; + u64 unk; + void *blob; + u64 unk2; +}; + +struct fw_info_t { + struct fw_header_t *rlc; + struct fw_header_t *sdma0; + struct fw_header_t *sdma1; + struct fw_header_t *ce; + struct fw_header_t *pfp; + struct fw_header_t *me; + struct fw_header_t *mec1; + struct fw_header_t *mec2; +}; + +struct fw_expected_sizes_t { + u64 rlc; + u64 sdma0; + u64 sdma1; + u64 ce; + u64 pfp; + u64 me; + u64 mec1; + u64 mec2; +}; +static const struct fw_expected_sizes_t liverpool_fw_sizes = { + LVP_FW_RLC_SIZE, + LVP_FW_SDMA_SIZE, + LVP_FW_SDMA1_SIZE, + LVP_FW_CE_SIZE, + LVP_FW_PFP_SIZE, + LVP_FW_ME_SIZE, + LVP_FW_MEC_SIZE, + LVP_FW_MEC2_SIZE +}; +static const struct fw_expected_sizes_t gladius_fw_sizes = { + GL_FW_RLC_SIZE, + GL_FW_SDMA_SIZE, + GL_FW_SDMA1_SIZE, + GL_FW_CE_SIZE, + GL_FW_PFP_SIZE, + GL_FW_ME_SIZE, + GL_FW_MEC_SIZE, + GL_FW_MEC2_SIZE +}; + +void copy_edid(u8 **p, int sz) +{ + int i; + u8 *edid = *p; + u8 *off_edid = kern.edid; + + memset(edid, 0, sz); + *p += sz; + + for(i = 0; i < sz; i++) + *(edid + i) = *(off_edid + i); + + *p += sz; +} + +void copy_eap_hdd_key(u8 **p) +{ + int i; + u8 *eap_key = *p; + u8 *off_eap_key = kern.eap_hdd_key; + + memset(eap_key, 0, 0x20); + *p += 0x20; + + for(i = 0; i < 0x20; i++) + { + if(i < 0x10) + *(eap_key + i) = *(off_eap_key + 0xF - i); + else + *(eap_key + i) = *(off_eap_key + 0x2F - i); + } + *p += 0x20; +} + +int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + kern.printf("Copying %s firmware\n", name); + if (expected_size != (hdr->size_words * 4)) { + kern.printf("copy_firmware: %s: expected size %d, got %d\n", + name, expected_size, hdr->size_words * 4); + return 0; + } + + struct firmware_header *fhdr = (struct firmware_header*)*p; + memset(fhdr, 0, sizeof(*fhdr)); + *p += sizeof(*fhdr); + + memcpy(*p, hdr->blob, expected_size); + + fhdr->size_bytes = expected_size + sizeof(*fhdr); + fhdr->header_size_bytes = offsetof(struct firmware_header, raw); + fhdr->header_version_major = 1; + fhdr->header_version_minor = 0; + fhdr->ucode_version = 0x10; + fhdr->ucode_size_bytes = expected_size; + fhdr->ucode_array_offset_bytes = sizeof(*fhdr); + + *p += expected_size; + + return 1; +} + +int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); + fhdr->gfx1.ucode_feature_version = 21; + fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); + fhdr->rlc1.ucode_feature_version = 1; + fhdr->rlc1.save_and_restore_offset = 0x90; + fhdr->rlc1.clear_state_descriptor_offset = 0x3d; + fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? + fhdr->rlc1.master_pkt_description_offset = 0; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 2; + fhdr->ip_version_minor = 1; + fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); + fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; + fhdr->sdma1.ucode_change_version = 0; + fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +static const u32 pfp_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc4200016, // ldw r8, [r0, #0x16] + 0xdc030000, // mov ctr, r0 + 0xcc000049, // stw r0, [r0, #0x49] + 0xcc200013, // stw r0, [r8, #0x13] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 ce_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc420000c, // ldw r8, [r0, #0xc] + 0xdc030000, // mov ctr, r0 + 0xcc00002f, // stw r0, [r0, #0x2f] + 0xcc200012, // stw r0, [r8, #0x12] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 mec_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc43c000c, // ldw r15, [r0, #0x9] + 0xdc030000, // mov ctr, r0 + 0xcc00002b, // stw r0, [r0, #0x2b] + 0xcc3c000d, // stw r0, [r15, #0xd] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +#define PACKET_TYPE_NOP 0x10 + +static void patch_fw(void *p, const u32 *handler, int handler_size) { + int size = ((struct firmware_header*)p)->ucode_size_bytes; + int code_size = (size & ~0xfff) / 4; + int nop_start = code_size - 0x10; + + u32 *fw = p + sizeof(struct firmware_header); + kern.printf("NOP handler at 0x%x\n", nop_start); + memcpy(&fw[nop_start], handler, handler_size); + + // patch the branch table entry + for (int off = code_size; off < size/4; off++) { + if ((fw[off] >> 16) == PACKET_TYPE_NOP) { + fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; + } + } +} + +struct fw_info_t *get_fw_info() { + if (kern.gc_get_fw_info) { + return kern.gc_get_fw_info(); + } else if (kern.Starsha_UcodeInfo) { + return kern.Starsha_UcodeInfo; + } else { + return NULL; + } +} + +const struct fw_expected_sizes_t *get_fw_expected_sizes() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return &gladius_fw_sizes; + } else { + return &liverpool_fw_sizes; + } +} + +const char * get_gpu_name() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return "gladius"; + } else { + return "liverpool"; + } +} + +ssize_t firmware_extract(void *dest) +{ + u8 *p = dest; + + // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. + struct fw_info_t *info = get_fw_info(); + if (!info) { + kern.printf("firmware_extract: Could not locate firmware table"); + return -1; + } + const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); + + //Eap hdd key + cpio_hdr(&p, "key", DIR, 0); + cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); + copy_eap_hdd_key(&p); + + cpio_hdr(&p, "lib", DIR, 0); + cpio_hdr(&p, "lib/firmware", DIR, 0); + + /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ + int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); + cpio_hdr(&p, "lib/firmware/edid", DIR, 0); + cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); + copy_edid(&p, edid_sz); + + char dir[7]; + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) + kern.snprintf(dir, sizeof(dir), "amdgpu"); + else + kern.snprintf(dir, sizeof(dir), "amdgpu"); + + char dir_path[64]; + kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); + cpio_hdr(&p, dir_path, DIR, 0); + + char pfp_path[64]; + kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", pfp_path); + cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); + u8 *pfp = p; + if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) + return -1; + patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); + + char me_path[64]; + kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", me_path); + cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); + if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) + return -1; + + char ce_path[64]; + kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", ce_path); + cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); + u8 *ce = p; + if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) + return -1; + patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); + + char mec_path[64]; + kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec_path); + cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); + u8 *mec1 = p; + if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) + return -1; + patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); + + char mec2_path[64]; + kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec2_path); + cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); + u8 *mec2 = p; + if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) + return -1; + patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); + + char rlc_path[64]; + kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", rlc_path); + cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); + if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) + return -1; + + char sdma_path[64]; + kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma_path); + cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); + if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + char sdma1_path[64]; + kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma1_path); + cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); + if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + size_t size = p - (u8*)dest; + if (size > FW_CPIO_SIZE) { + kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); + return -1; + } + + return size; +} diff --git a/linux/ps4-kexec-1250-baikal/firmware.h b/linux/ps4-kexec-1250-baikal/firmware.h new file mode 100644 index 0000000..4835983 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/firmware.h @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include "types.h" + +//sizes eap_hdd_key +#define EAP_HDD_KEY_SIZE 0x20 +#define EDID_SIZE 256 + +// sizes for liverpool +#define LVP_FW_CE_SIZE 8576 +#define LVP_FW_ME_SIZE 16768 +#define LVP_FW_MEC_SIZE 16768 +#define LVP_FW_MEC2_SIZE 16768 +#define LVP_FW_PFP_SIZE 16768 +#define LVP_FW_RLC_SIZE 6144 +#define LVP_FW_SDMA_SIZE 4200 +#define LVP_FW_SDMA1_SIZE 4200 +// sizes for gladius +#define GL_FW_CE_SIZE 8576 +#define GL_FW_ME_SIZE 16768 +#define GL_FW_MEC_SIZE 16768 +#define GL_FW_MEC2_SIZE 16768 +#define GL_FW_PFP_SIZE 16768 +#define GL_FW_RLC_SIZE 8192 +#define GL_FW_SDMA_SIZE 4200 +#define GL_FW_SDMA1_SIZE 4200 + +#define MAX(x ,y) (((x) > (y)) ? (x) : (y)) +#define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) + +#define FW_CE_SIZE MAX_FW_SIZE(CE) +#define FW_ME_SIZE MAX_FW_SIZE(ME) +#define FW_MEC_SIZE MAX_FW_SIZE(MEC) +#define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) +#define FW_PFP_SIZE MAX_FW_SIZE(PFP) +#define FW_RLC_SIZE MAX_FW_SIZE(RLC) +#define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) +#define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) + +// Conservative value (max 113 bytes plus name size plus alignment) +#define CPIO_HEADER_SIZE 256 + +#define FW_HEADER_SIZE 256 + +// Leave space for 16 files (currently 12) +#define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ + FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ + FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ + FW_HEADER_SIZE * 8) + +ssize_t firmware_extract(void *dest); + +#endif diff --git a/linux/ps4-kexec-1250-baikal/kernel.c b/linux/ps4-kexec-1250-baikal/kernel.c new file mode 100644 index 0000000..b6d31b4 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/kernel.c @@ -0,0 +1,383 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "string.h" +#include "elf.h" +#include "x86.h" +#include "../magic.h" + +struct ksym_t kern; +int (*early_printf)(const char *fmt, ...) = NULL; + +#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) + +#ifdef NO_SYMTAB + +#define RESOLVE_NOERR(name) do { \ + if (kern_off_ ## name == 0) { \ + kern.name = 0; \ + } else { \ + kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ + } \ +} while (0); + +#define RESOLVE(name) do { \ + if (kern_off_ ## name == 0) { \ + return 0; \ + } \ + RESOLVE_NOERR(name) \ +} while (0); + +#else + +#define KERNSIZE 0x2000000 + +static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; +static Elf64_Sym *symtab; +static char *strtab; +static size_t strtab_size; + +static Elf64_Ehdr *find_kern_ehdr(void) +{ + // Search for the kernel copy embedded in ubios, then follow it to see + // where it was relocated to + for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; + if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { + for (size_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; + if (phdr->p_type == PT_PHDR) { + return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); + } + } + } + } + return NULL; +} + +static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf64_Dyn *)phdr->p_vaddr; + } + } + return NULL; +} + +static int elf_parse_dyn(Elf64_Dyn *dyn) +{ + for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_SYMTAB: + symtab = (Elf64_Sym *)dp->d_un.d_ptr; + break; + case DT_STRTAB: + strtab = (char *)dp->d_un.d_ptr; + break; + case DT_STRSZ: + strtab_size = dp->d_un.d_val; + break; + } + } + return symtab && strtab && strtab_size; +} + +void *kernel_resolve(const char *name) +{ + for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { + if (!strcmp(name, &strtab[sym->st_name])) { + eprintf("kern.%s = %p\n", name, (void*)sym->st_value); + return (void *)sym->st_value; + } + } + eprintf("Failed to resolve symbol '%s'\n", name); + return NULL; +} + +#define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) +#define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; + +#endif + +static int resolve_symbols(void) +{ + RESOLVE(printf); + RESOLVE(snprintf); + early_printf = kern.printf; + RESOLVE(copyin); + RESOLVE(copyout); + RESOLVE(copyinstr); + RESOLVE(kernel_map); + RESOLVE(kernel_pmap_store); + RESOLVE(kmem_alloc_contig); + RESOLVE(kmem_free); + RESOLVE(pmap_extract); + RESOLVE(pmap_protect); + RESOLVE(sysent); + RESOLVE(sched_pin); + RESOLVE(sched_unpin); + RESOLVE(smp_rendezvous); + RESOLVE(smp_no_rendevous_barrier); + RESOLVE(icc_query_nowait); + RESOLVE_NOERR(Starsha_UcodeInfo); + RESOLVE_NOERR(gpu_devid_is_9924); + RESOLVE_NOERR(gc_get_fw_info); + RESOLVE_NOERR(eap_hdd_key); + RESOLVE_NOERR(edid); + RESOLVE(wlanbt); + RESOLVE(kern_reboot); + RESOLVE(set_gpu_freq); + RESOLVE(set_pstate); + RESOLVE(update_vddnp); + RESOLVE(set_cu_power_gate); + RESOLVE_NOERR(pstate_before_shutdown); + return 1; +} + +#define M_WAITOK 0x0002 +#define M_ZERO 0x0100 + +#define VM_MEMATTR_DEFAULT 0x06 + +void *kernel_alloc_contig(size_t size) +{ + // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... + vm_offset_t ret = 0; + while(!(ret = kern.kmem_alloc_contig( + *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, + ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); + + /*if (!ret) { + kern.printf("Failed to allocate %zud bytes\n", size); + return NULL; + }*/ + return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); +} + +void kernel_free_contig(void *addr, size_t size) +{ + if (!addr) + return; + kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); +} + +int kernel_hook_install(void *target, void *hook) +{ + uintptr_t t = (uintptr_t)target; // addr to redirect to + uintptr_t h = (uintptr_t)hook; // place to write the thunk + + if (!hook || !target) { + return 0; + } + + kern.printf("kernel_hook_install(%p, %p)\n", target, hook); + + if (!(t & (1L << 63))) { + kern.printf("\n===================== WARNING =====================\n"); + kern.printf("hook target function address: %p\n", target); + kern.printf("It looks like we're running from userland memory.\n"); + kern.printf("Please run this code from a kernel memory mapping.\n\n"); + return 0; + } + s64 displacement = t - (h + 5); + + kern.sched_pin(); + u64 wp = write_protect_disable(); + if (displacement < -0x80000000 || displacement > 0x7fffffff) { + kern.printf(" Using 64bit absolute jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[2]; + s32 zero; + void *target; + } jmp = { + .op = { 0xff, 0x25 }, + .zero = 0, + .target = target, + }; + ASSERT_STRSIZE(struct jmp_t, 14); + memcpy(hook, &jmp, sizeof(jmp)); + } else { + kern.printf(" Using 32bit relative jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[1]; + s32 imm; + } jmp = { + .op = { 0xe9 }, + .imm = displacement, + }; + ASSERT_STRSIZE(struct jmp_t, 5); + memcpy(hook, &jmp, sizeof(jmp)); + } + wbinvd(); + write_protect_restore(wp); + kern.sched_unpin(); + + return 1; +} + +void kernel_syscall_install(int num, void *call, int narg) +{ + struct sysent_t *sy = &kern.sysent[num]; + + kern.sched_pin(); + u64 wp = write_protect_disable(); + + memset(sy, 0, sizeof(*sy)); + sy->sy_narg = narg; + sy->sy_call = call; + sy->sy_thrcnt = 1; + + write_protect_restore(wp); + kern.sched_unpin(); +} + +void kernel_remap(void *start, void *end, int perm) +{ + u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); + u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); + + kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); + kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); +} + +static volatile int _global_test = 0; + +#ifndef DO_NOT_REMAP_RWX +extern u8 _start[], _end[]; + +static int patch_pmap_check(void) +{ + u8 *p; + + for (p = (u8*)kern.pmap_protect; + p < ((u8*)kern.pmap_protect + 0x500); p++) { + #ifdef PS4_6_72 + if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 + p[5] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + /*#ifdef PS4_5_05 + if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { + p[1] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + }*/ + #else + if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { + p[2] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + #endif + } + kern.printf("pmap_protect patch failed!\n"); + return 0; +} +#endif + +int kernel_init(void *_early_printf) +{ + int rv = -1; + + if (_early_printf) + early_printf = _early_printf; + + eprintf("kernel_init()\n"); + +#ifdef KASLR + // use `early_printf` to calculate kernel base + if (early_printf == NULL) + return 0; + + kern.kern_base = (u64)(early_printf - kern_off_printf); + if ((kern.kern_base & PAGE_MASK) != 0) { + eprintf("Kernel base is not aligned\n"); + return 0; + } else { + eprintf("Kernel base = %llx\n", kern.kern_base); + } + + u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); + u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); + +#else + kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 + + u64 DMPML4I = 0x1fc; + u64 DMPDPI = 0; +#endif + + kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); + eprintf("Direct map base = %llx\n", kern.dmap_base); + + // We may not be mapped writable yet, so to be able to write to globals + // we need WP disabled. + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + +#ifndef NO_SYMTAB + Elf64_Ehdr *ehdr = find_kern_ehdr(); + if (!ehdr) { + eprintf("Could not find kernel ELF header\n"); + goto err; + } + eprintf("ELF header at %p\n", ehdr); + + Elf64_Dyn *dyn = elf_get_dyn(ehdr); + if (!dyn) { + eprintf("Could not find kernel dynamic header\n"); + goto err; + } + eprintf("ELF dynamic section at %p\n", dyn); + + if (!elf_parse_dyn(dyn)) { + eprintf("Failed to parse ELF dynamic section\n"); + goto err; + } +#endif + + if (!resolve_symbols()) { + eprintf("Failed to resolve all symbols\n"); + goto err; + } + + // Pin ourselves as soon as possible. This is expected to be released by the caller. + kern.sched_pin(); + +#ifndef DO_NOT_REMAP_RWX + if (!patch_pmap_check()) + goto err; +#endif + +#ifndef DO_NOT_REMAP_RWX + // kernel_remap may need interrupts, but may not write to globals! + enable_interrupts(); + kernel_remap(_start, _end, 7); + disable_interrupts(); +#endif + + // Writing to globals is now safe. + + kern.printf("Testing global variable access (write protection)...\n"); + _global_test = 1; + kern.printf("OK.\n"); + + kern.printf("Kernel interface initialized\n"); + rv = 0; + +err: + write_protect_restore(wp); + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1250-baikal/kernel.h b/linux/ps4-kexec-1250-baikal/kernel.h new file mode 100644 index 0000000..1833f5b --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/kernel.h @@ -0,0 +1,126 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KERNEL_H +#define KERNEL_H + +#include "types.h" +#include "reboot.h" + +#define PAGE_SIZE 0x4000 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define PML4SHIFT 39 +#define PDPSHIFT 30 +#define PDRSHIFT 21 +#define PAGE_SHIFT 12 + +#define KVADDR(l4, l3, l2, l1) ( \ + ((unsigned long)-1 << 47) | \ + ((unsigned long)(l4) << PML4SHIFT) | \ + ((unsigned long)(l3) << PDPSHIFT) | \ + ((unsigned long)(l2) << PDRSHIFT) | \ + ((unsigned long)(l1) << PAGE_SHIFT)) + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) +#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX + +typedef u64 vm_paddr_t; +typedef u64 vm_offset_t; +typedef u64 vm_size_t; +typedef void * vm_map_t; +typedef char vm_memattr_t; +typedef void * pmap_t; + +typedef void (*smp_rendezvous_callback_t)(void *); + +struct sysent_t { + int sy_narg; + void *sy_call; + u16 sy_auevent; + void *sy_systrace_args_func; + int sy_entry; + int sy_return; + int sy_flags; + int sy_thrcnt; +}; + +struct ksym_t { + // two parameters related to kaslr (they are not symbols) + uintptr_t kern_base; + uintptr_t dmap_base; + + int (*printf)(const char *fmt, ...); + int (*snprintf)(const char *fmt, ...); + int (*copyin)(const void *uaddr, void *kaddr, size_t len); + int (*copyout)(const void *kaddr, void *uaddr, size_t len); + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); + + void **kernel_map; + void *kernel_pmap_store; + vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, + vm_paddr_t low, vm_paddr_t high, + unsigned long alignment, + unsigned long boundary, + vm_memattr_t memattr); + void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); + vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); + void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); + + struct sysent_t *sysent; + + void (*sched_pin)(void); + void (*sched_unpin)(void); + void (*smp_rendezvous)(smp_rendezvous_callback_t, + smp_rendezvous_callback_t, + smp_rendezvous_callback_t, void *); + // yes...it is misspelled :) + void (*smp_no_rendevous_barrier)(void *); + void *icc_query_nowait; + void *Starsha_UcodeInfo; + int (*gpu_devid_is_9924)(); + void *(*gc_get_fw_info)(); + void *eap_hdd_key; + void *edid; + void (*wlanbt)(unsigned int state); + int (*kern_reboot)(int magic); + void(*set_gpu_freq)(unsigned int num, unsigned int freq); + void(*set_pstate)(unsigned int val); + void(*update_vddnp)(unsigned int val); + void(*set_cu_power_gate)(unsigned int val); + void *pstate_before_shutdown; +}; + +extern struct ksym_t kern; + +static inline int curcpu(void) +{ + int cpuid; + // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw + asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); + return cpuid; +} + +// Assign a working printf function to this to debug the symbol resolver +extern int (*early_printf)(const char *fmt, ...); + +void *kernel_resolve(const char *name); + +void *kernel_alloc_contig(size_t size); +void kernel_free_contig(void *addr, size_t size); + +void kernel_remap(void *start, void *end, int perm); + +void kernel_syscall_install(int num, void *call, int narg); +int kernel_hook_install(void *target, void *hook); + +int kernel_init(void *early_printf); + +#endif diff --git a/linux/ps4-kexec-1250-baikal/kexec.c b/linux/ps4-kexec-1250-baikal/kexec.c new file mode 100644 index 0000000..d659229 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/kexec.c @@ -0,0 +1,229 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "linux_boot.h" +#include "x86.h" +#include "kexec.h" +#include "firmware.h" +#include "string.h" +#include "acpi.h" + +static int k_copyin(const void *uaddr, void *kaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(kaddr, uaddr, len); + return 0; +} + +static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + const char *ustr = (const char*)uaddr; + char *kstr = (char*)kaddr; + size_t ret; + if (!uaddr || !kaddr) + return EFAULT; + ret = strlcpy(kstr, ustr, len); + if (ret >= len) { + if (done) + *done = len; + return ENAMETOOLONG; + } else { + if (done) + *done = ret + 1; + } + return 0; +} + +static int k_copyout(const void *kaddr, void *uaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(uaddr, kaddr, len); + return 0; +} + +int sys_kexec(void *td, struct sys_kexec_args *uap) +{ + int err = 0; + size_t initramfs_size = uap->initramfs_size; + void *image = NULL; + void *initramfs = NULL; + size_t firmware_size = 0; + struct boot_params *bp = NULL; + size_t cmd_line_maxlen = 0; + char *cmd_line = NULL; + + int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; + int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; + + kern.printf("sys_kexec invoked\n"); + kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, + uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); + + // Look up our shutdown hook point + void *icc_query_nowait = kern.icc_query_nowait; + if (!icc_query_nowait) { + err = ENOENT; + goto cleanup; + } + + // Set gpu frequencies and pstate + // FAT&SLIM / PRO + kern.set_pstate(3); + + kern.set_gpu_freq(0, 800); //800 //800 + kern.set_gpu_freq(1, 673); //673 //853 + kern.set_gpu_freq(2, 609); //610 //711 + kern.set_gpu_freq(3, 800); //800 //800 + kern.set_gpu_freq(4, 800); //800 //911 + kern.set_gpu_freq(5, 711); //711 //800 + kern.set_gpu_freq(6, 711); //711 //984 + kern.set_gpu_freq(7, 673); //673 //673 + + kern.update_vddnp(0x12); + kern.set_cu_power_gate(0x12); + + // Copy in kernel image + image = kernel_alloc_contig(uap->image_size); + if (!image) { + kern.printf("Failed to allocate image\n"); + err = ENOMEM; + goto cleanup; + } + err = copyin(uap->image, image, uap->image_size); + if (err) { + kern.printf("Failed to copy in image\n"); + goto cleanup; + } + + // Copy in initramfs + initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); + if (!initramfs) { + kern.printf("Failed to allocate initramfs\n"); + err = ENOMEM; + goto cleanup; + } + + err = firmware_extract(((u8*)initramfs)); + if (err < 0) { + kern.printf("Failed to extract GPU firmware - continuing anyway\n"); + } else { + firmware_size = err; + } + + if (initramfs_size) { + err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); + if (err) { + kern.printf("Failed to copy in initramfs\n"); + goto cleanup; + } + } + initramfs_size += firmware_size; + + // Copy in cmdline + cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; + cmd_line = kernel_alloc_contig(cmd_line_maxlen); + if (!cmd_line) { + kern.printf("Failed to allocate cmdline\n"); + err = ENOMEM; + goto cleanup; + } + err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); + if (err) { + kern.printf("Failed to copy in cmdline\n"); + goto cleanup; + } + cmd_line[cmd_line_maxlen - 1] = 0; + + kern.printf("\nkexec parameters:\n"); + kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); + kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", + initramfs_size, uap->initramfs_size); + kern.printf(" Kernel command line: %s\n", cmd_line); + kern.printf(" Kernel image buffer: %p\n", image); + kern.printf(" Initramfs buffer: %p\n", initramfs); + + // Allocate our boot params + bp = kernel_alloc_contig(sizeof(*bp)); + if (!bp) { + kern.printf("Failed to allocate bp\n"); + err = ENOMEM; + goto cleanup; + } + + // Initialize bp + // TODO should probably do this from cpu_quiesce_gate, then bp doesn't + // need to be allocated here, just placed directly into low mem + set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); + + prepare_boot_params(bp, image); + + // Hook the final ICC shutdown function + if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { + kern.printf("Failed to install shutdown hook\n"); + err = EINVAL; + goto cleanup; + } + + kern.printf("******************************************************\n"); + kern.printf("kexec successfully armed. Please shut down the system.\n"); + kern.printf("******************************************************\n\n"); + +/* + kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); + if (kern.kern_reboot(RB_POWEROFF) == -1) + kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); +*/ + return 0; + +cleanup: + kernel_free_contig(cmd_line, cmd_line_maxlen); + kernel_free_contig(bp, sizeof(*bp)); + kernel_free_contig(image, uap->image_size); + kernel_free_contig(initramfs, uap->initramfs_size); + return err; + + copyout(NULL, NULL, 0); +} + +int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) +{ + int rv = 0; + + // potentially needed to write early_printf + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + + if (kernel_init(_early_printf) < 0) { + rv = -1; + goto cleanup; + } + + kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); + kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); + kern.printf("kexec_init() successful\n\n"); + + if (sys_kexec_ptr) + *sys_kexec_ptr = sys_kexec; + +cleanup: + write_protect_restore(wp); + if (kern.sched_unpin && wp & CR0_WP) { + // If we're returning to a state with WP enabled, assume the caller + // wants the thread unpinned. Else the caller is expected to + // call kern.sched_unpin() manually. + kern.sched_unpin(); + } + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1250-baikal/kexec.h b/linux/ps4-kexec-1250-baikal/kexec.h new file mode 100644 index 0000000..6c31637 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/kexec.h @@ -0,0 +1,38 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KEXEC_H +#define KEXEC_H + +#include "types.h" + +#define SYS_KEXEC 153 +#define SYS_KEXEC_NARGS 6 + +struct sys_kexec_args { + void *image; + size_t image_size; + void *initramfs; + size_t initramfs_size; + char *cmd_line; + int vram_gb; +}; + +typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); + +// Note: td is unused, you can pass NULL if you call this directly. +int sys_kexec(void *td, struct sys_kexec_args *uap); + +int kernel_init(void *early_printf); + +int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) + __attribute__ ((section (".init"))); + +#endif diff --git a/linux/ps4-kexec-1250-baikal/kexec.ld b/linux/ps4-kexec-1250-baikal/kexec.ld new file mode 100644 index 0000000..1bcf26c --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/kexec.ld @@ -0,0 +1,25 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +ENTRY(kexec_init) + +SECTIONS { + /* We don't do GOT relocation and rely on nothing ending up using the GOT + * (-fno-common helps here) */ + /DISCARD/ : { *(.comment) *(.got) } + _start = .; + .init : { *(.init) *(.init.*) } + .text : { *(.text) *(.text.*) } + .data : { *(.data) *(.data.*) } + .rodata : { *(.rodata) *(.rodata.*) } + .bss : { *(.bss) *(.bss.*) *(COMMON)} + .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ + _end = .; +} diff --git a/linux/ps4-kexec-1250-baikal/linux_boot.c b/linux/ps4-kexec-1250-baikal/linux_boot.c new file mode 100644 index 0000000..226dd4f --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/linux_boot.c @@ -0,0 +1,453 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "linux_boot.h" +#include "types.h" +#include "string.h" +#include "x86.h" +#include "kernel.h" +#include "uart.h" +#include "acpi.h" + +void uart_write_byte(u8 b); + +static u64 vram_base = 0x100000000; +// Current code assumes it's a power of two. +static u64 vram_size = 1024 * 1024 * 1024; +static int vram_gb = 2; + +#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) + +#define MSR_GS_BASE 0xc0000101 /* 64bit GS base */ + +struct desc_struct { + u16 limit0; + u16 base0; + u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; + u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, + uintptr_t new_cr3, uintptr_t gdt_ptr); +extern uint8_t *jmp_to_linux; +extern size_t jmp_to_linux_size; + +// FreeBSD DMAP addresses +struct linux_boot_info { + void *linux_image; + void *initramfs; + size_t initramfs_size; + struct boot_params *bp; + char *cmd_line; +}; +static struct linux_boot_info nix_info; + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v) +{ + nix_info.linux_image = linux_image; + nix_info.bp = bp; + nix_info.initramfs = initramfs; + nix_info.initramfs_size = initramfs_size; + nix_info.cmd_line = cmd_line; + vram_gb = v; +} + +static volatile int halted_cpus = 0; + +static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, + u32 type) +{ + uint8_t idx = bp->e820_entries; + bp->e820_map[idx].addr = addr; + bp->e820_map[idx].size = size; + bp->e820_map[idx].type = type; + bp->e820_entries++; +} + +void prepare_boot_params(struct boot_params *bp, u8 *linux_image) +{ + memset(bp, 0, sizeof(struct boot_params)); + struct boot_params *bp_src = (struct boot_params *)linux_image; + memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + + ((u8 *)&bp_src->hdr.jump)[1]); + + // These values are from fw 1.01 + bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); + bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + // This used to be VRAM, but we reclaim it as RAM + bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); + // Instead, carve out VRAM from the beginning of high memory + bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, + SMAP_TYPE_MEMORY); +} + +#define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_FB_OFFSET 0x2068 +#define HDP_NONSURFACE_BASE 0x2c04 +#define CONFIG_MEMSIZE 0x5428 + +static void configure_vram(void) +{ + u64 mmio_base = 0xe4800000; + u64 fb_base = 0x0f00000000; + u64 fb_top = fb_base + vram_gb * vram_size - 1; + + WR32(mmio_base + MC_VM_FB_LOCATION, 0); + WR32(mmio_base + HDP_NONSURFACE_BASE, 0); + + WR32(mmio_base + MC_VM_FB_LOCATION, + ((fb_top >> 24) << 16) | (fb_base >> 24)); + WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); + WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); + WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); +} + +#define IA32_MTRR_DEF_TYPE 0x2ff +#define MTRR_BASE(i) (0x200 + 2*i) +#define MTRR_MASK(i) (0x201 + 2*i) + +static void setup_mtrr(void) +{ + disable_interrupts(); + u64 cr0 = cr0_read(); + cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); + wbinvd(); + cr3_write(cr3_read()); // TLB flush + + wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled + + // Low memory (0GB-2GB) = WB + wrmsr(MTRR_BASE(0), 0x0000000006); + wrmsr(MTRR_MASK(0), 0xff80000800); + // High memory (4GB-8GB) = WB + wrmsr(MTRR_BASE(1), 0x0100000006); + wrmsr(MTRR_MASK(1), 0xff00000800); + // High memory (8GB-10GB) = WB + wrmsr(MTRR_BASE(2), 0x0200000006); + wrmsr(MTRR_MASK(2), 0xff80000800); + // VRAM (4GB-4GB+vram_size) = UC + wrmsr(MTRR_BASE(3), 0x0100000000); + wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); + + wbinvd(); + cr3_write(cr3_read()); // TLB flush + wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable + cr0_write(cr0); + enable_interrupts(); +} + +static void cleanup_interrupts(void) +{ + int i; + disable_interrupts(); + + // Reset APIC stuff (per-CPU) + *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; + for (i = 0x320; i < 0x380; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + for (i = 0x480; i < 0x500; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; + for (i = 0x500; i < 0x540; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; + + // Fix the LVT offset for thresholding + wrmsr(0x413, (1L<<24) | (1L<<52)); + wrmsr(0xc0000408, (1L<<24) | (1L<<52)); +} + +#define DEFAULT_STACK 0 + +#define DPL0 0x0 +#define DPL3 0x3 + +#define BPCIE_BAR2 0xc8800000 +#define BPCIE_HPET_BASE 0x109000 +#define BPCIE_HPET_SIZE 0x400 + +static void cpu_quiesce_gate(void *arg) +{ + + + // Ensure we can write anywhere + cr0_write(cr0_read() & ~CR0_WP); + + // Interrupt stuff local to each CPU + cleanup_interrupts(); + + // We want to set up MTRRs on all CPUs + setup_mtrr(); + + if (curcpu() != 0) { + // We're not on BSP. Try to halt. + __sync_fetch_and_add(&halted_cpus, 1); + cpu_stop(); + } + + uart_write_str("kexec: Waiting for secondary CPUs...\n"); + + // wait for all cpus to halt + while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); + + uart_write_str("kexec: Secondary CPUs quiesced\n"); + + //* Put ident mappings in current page tables + // Should not be needed, but maybe helps for debugging? + cr4_pge_disable(); + u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); + u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + // Clear (really) low mem. + // Linux reads from here to try and access EBDA... + // get_bios_ebda reads u16 from 0x40e + // reserve_ebda_region reads u16 from 0x413 + // Writing zeros causes linux to default to marking + // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. + // It doesn't match the ps4 e820 map, but that seems OK. + memset((void *)0, 0, 0x1000); + + // Create a new page table hierarchy out of the way of linux + // Accessed via freebsd direct map + pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt + // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| + memset(pml4_base, 0, 512 * sizeof(u64) * 2); + pdp_base = pml4_base + 512; + u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; + pml4_base[0] = pdpe; + // Maintain the freebsd direct map + pml4_base[DM_PML4_BASE] = pdpe; + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + uart_write_str("kexec: Setting up GDT...\n"); + + desc_ptr gdt_ptr; + struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); + gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; + gdt_ptr.address = DM_TO_ID(desc); + + // clear + memset(desc, 0, gdt_ptr.limit + 1); + // Most things are ignored in 64bit mode, and we will never be in + // 32bit/compat modes, so just setup another pure-64bit environment... + // Linux inits it's own GDT in secondary_startup_64 + // 0x10 + desc[2].limit0 = 0xffff; + desc[2].base0 = 0x0000; + desc[2].base1 = 0x0000; + desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; + desc[2].s = 1; + desc[2].dpl = 0; + desc[2].p = 1; + desc[2].limit = 0xf; + desc[2].avl = 0; + desc[2].l = 1; + desc[2].d = 0; + desc[2].g = 0; + desc[2].base2 = 0x00; + // 0x18 + desc[3].limit0 = 0xffff; + desc[3].base0 = 0x0000; + desc[3].base1 = 0x0000; + desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; + desc[3].s = 1; + desc[3].dpl = 0; + desc[3].p = 1; + desc[3].limit = 0xf; + desc[3].avl = 0; + desc[3].l = 0; + desc[3].d = 0; + desc[3].g = 0; + desc[3].base2 = 0x00; + // Task segment value + // 0x20 + desc[4].limit0 = 0x0000; + desc[4].base0 = 0x0000; + desc[4].base1 = 0x0000; + desc[4].type = SEG_TYPE_TSS; + desc[4].s = 1; + desc[4].dpl = 0; + desc[4].p = 1; + desc[4].limit = 0x0; + desc[4].avl = 0; + desc[4].l = 0; + desc[4].d = 0; + desc[4].g = 0; + desc[4].base2 = 0x00; + + uart_write_str("kexec: Relocating stub...\n"); + + // Relocate the stub and jump to it + // TODO should thunk_copy be DMAP here? + void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); + memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); + // XXX The +0x200 is for the iret stack in linux_thunk.S + uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; + + uart_write_str("kexec: Setting up boot params...\n"); + + // XXX we write into this bootargs and pass it to the kernel, but in + // jmp_to_linux we use the bootargs from the image as input. So they + // MUST MATCH! + struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; + *bp_lo = *nix_info.bp; + lowmem_pos += sizeof(struct boot_params); + + struct setup_header *shdr = &bp_lo->hdr; + shdr->cmd_line_ptr = lowmem_pos; + shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; + shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; + bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; + bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; + shdr->hardware_subarch = X86_SUBARCH_PS4; + // This needs to be nonzero for the initramfs to work + shdr->type_of_loader = 0xd0; // kexec + + strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, + nix_info.bp->hdr.cmdline_size); + lowmem_pos += strlen(nix_info.cmd_line) + 1; + + uart_write_str("kexec: Cleaning up hardware...\n"); + + // Disable IOMMU + *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; + + // Disable all MSIs on Baikal (bus=0, slot=20) + disableMSI(0xf80a00e0); //func = 0 Baikal ACPI + disableMSI(0xf80a10e0); //func = 1 Baikal Ethernet Controller + disableMSI(0xf80a20e0); //func = 2 Baikal SATA AHCI Controller + disableMSI(0xf80a30e0); //func = 3 Baikal SD/MMC Host Controller + disableMSI(0xf80a40e0); //func = 4 Baikal PCI Express Glue and Miscellaneous Devices + disableMSI(0xf80a50e0); //func = 5 Baikal DMA Controller + disableMSI(0xf80a60e0); //func = 6 Baikal Baikal Memory (DDR3/SPM) + disableMSI(0xf80a70e0); //func = 7 Baikal Baikal USB 3.0 xHCI Host Controller + + // Stop HPET timers + //*(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + 0x10) &= ~(1UL << 0); //General Configuration Register + /* + u64 NUM_TIM_CAP = *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE) & 0x1F00; + for (u64 N = 0; N <= NUM_TIM_CAP; N++) { + *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + (0x20*N) + 0x100) &= ~(1UL << 2); //Timer N Configuration and Capabilities Register + } + */ + uart_write_str("kexec: Reconfiguring VRAM...\n"); + + configure_vram(); + + uart_write_str("kexec: Resetting GPU...\n"); + + // Softreset GPU + *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks + *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC + *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 +// *(volatile u64 *)PA_TO_DM(0xe480d248) = 1; // Halt SDMA1 eeply + *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 + *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC + + *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply + +// *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x10003; // Softreset GFX/CP/RLC + *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x30005; // Softreset GFX/CP/RLC eeply + +// udelay(150); +// *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x10003; + *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x30005; //eeply +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) |= 0x00100140; // Softreset SDMA/GRBM +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~0x00100140; +// udelay(150); + + // Enable audio output + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; + *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; + +// // Set pin caps of pin 2 to vendor defined, to hide it +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// // Set pin caps of pin 3 to !HDMI +// *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; +// *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; + // Set pin configuration default + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; + + uart_write_str("kexec: About to relocate and jump to kernel\n"); + + ((jmp_to_linux_t)thunk_copy)( + DM_TO_ID(nix_info.linux_image), + DM_TO_ID(bp_lo), + DM_TO_ID(pml4_base), + (uintptr_t)&gdt_ptr + ); + // should never reach here + uart_write_str("kexec: unreachable (?)\n"); +} + +// Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) +int hook_icc_query_nowait(u8 *icc_msg) +{ + kern.printf("hook_icc_query_nowait called\n"); + + // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot + //In alternative we can re-enable it here, but sometimes that give problems.. + kern.wlanbt(0x2); + + fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); + + kern.printf("ACPI tables fixed\n"); + + // Transition to BSP and halt other cpus + // smp_no_rendevous_barrier is just nullsub, but it is treated specially by + // smp_rendezvous. This is the easiest way to do this, since we can't assume + // we're already running on BSP. Since smp_rendezvous normally waits on all + // cpus to finish the callbacks, we just never return... + kern.smp_rendezvous(kern.smp_no_rendevous_barrier, + cpu_quiesce_gate, + kern.smp_no_rendevous_barrier, NULL); + + // should never reach here + kern.printf("hook_icc_query_nowait: unreachable (?)\n"); + return 0; +} diff --git a/linux/ps4-kexec-1250-baikal/linux_boot.h b/linux/ps4-kexec-1250-baikal/linux_boot.h new file mode 100644 index 0000000..2232b35 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/linux_boot.h @@ -0,0 +1,88 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef LINUX_BOOT_H +#define LINUX_BOOT_H + +#include "types.h" + +#define SMAP_TYPE_MEMORY 1 +#define SMAP_TYPE_RESERVED 2 +#define SMAP_TYPE_ACPI_RECLAIM 3 +#define SMAP_TYPE_ACPI_NVS 4 +#define SMAP_TYPE_UNUSABLE 5 +#define SMAP_TYPE_PMEM 7 + +#define X86_SUBARCH_PS4 5 + +struct e820entry { + u64 addr; /* start of memory segment */ + u64 size; /* size of memory segment */ + u32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct setup_header { + u8 setup_sects; + u16 root_flags; + u32 syssize; + u16 ram_size; + u16 vid_mode; + u16 root_dev; + u16 boot_flag; + u16 jump; + u32 header; + u16 version; + u32 realmode_swtch; + u16 start_sys; + u16 kernel_version; + u8 type_of_loader; + u8 loadflags; + u16 setup_move_size; + u32 code32_start; + u32 ramdisk_image; + u32 ramdisk_size; + u32 bootsect_kludge; + u16 heap_end_ptr; + u8 ext_loader_ver; + u8 ext_loader_type; + u32 cmd_line_ptr; + u32 initrd_addr_max; + u32 kernel_alignment; + u8 relocatable_kernel; + u8 min_alignment; + u16 xloadflags; + u32 cmdline_size; + u32 hardware_subarch; + u64 hardware_subarch_data; + u32 payload_offset; + u32 payload_length; + u64 setup_data; + u64 pref_address; + u32 init_size; + u32 handover_offset; +} __attribute__((packed)); + +#define E820MAX 128 /* number of entries in E820MAP */ + +OSTRUCT(boot_params, 0x1000) +OFIELD(0x0c0, u32 ext_ramdisk_image); +OFIELD(0x0c4, u32 ext_ramdisk_size); +OFIELD(0x0c8, u32 ext_cmd_line_ptr); +OFIELD(0x1e8, u8 e820_entries); +OFIELD(0x1f1, struct setup_header hdr); +OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); +OSTRUCT_END + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v); +void prepare_boot_params(struct boot_params *bp, u8 *linux_image); +int hook_icc_query_nowait(u8 *icc_msg); + +#endif diff --git a/linux/ps4-kexec-1250-baikal/linux_thunk.S b/linux/ps4-kexec-1250-baikal/linux_thunk.S new file mode 100644 index 0000000..551964e --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/linux_thunk.S @@ -0,0 +1,90 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +.intel_syntax noprefix + +.equ setup_sects, 0x1f1 +.equ shdr_syssize, 0x1f4 +.equ pref_address, 0x258 + +.text + +#void jmp_to_linux( +# uintptr_t image_base, rdi +# uintptr_t bootargs, rsi +# uintptr_t new_cr3, rdx +# uintptr_t gdt_ptr rcx +#); +.globl jmp_to_linux +jmp_to_linux: + # switch to new gdt + data segments + cli + lgdt [rcx] + #xor eax, eax + mov eax, 0x18 + mov ds, eax + mov ss, eax + mov es, eax + mov fs, eax + mov gs, eax + + # switch to our own page tables (in low mem) + mov cr3, rdx + + # now we're on our own page tables, so we can obliterate the rest of memory + # TODO make sure we don't inadvertently overwrite (important) smap regions + # I think on ps4 we'll actually want to load to 0x700000 + # since we have tons of free room there. + # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We + # should choose a higher address as pref_address. + + # save args + mov r12, rdi + mov r13, rsi + + # memmove(pref_address, , (syssize * 0x10) / 8) + #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go + #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) + mov rdi, 0x6000000 # should be far from bzImage and initramfs + mov r14, rdi # r14 = pref_address + xor edx, edx + mov dl, [r12 + setup_sects] + inc rdx + shl rdx, 9 # rdx = offsetof(image_base, startup_32) + lea rsi, [r12 + rdx] # src = image_base + startup_32 + mov ecx, [r12 + shdr_syssize] + shl rcx, 4 + add rdi, rcx + add rsi, rcx + sub rdi, 8 + sub rsi, 8 + shr rcx, 3 + std + rep movsq + cld + + # make a tiny stack - we just need it for the lretq. + # what we jump to will not use this stack + lea rsp, [rip + jmp_to_linux_end + 0x200] + and rsp, -0x10 + #push 0 # retaddr + push 0x10 # cs = GDT[2] + add r14, 0x200 # pref_address + startup_64 + push r14 # rip + mov rsi, r13 # bootargs + lretq +jmp_to_linux_end: + +.data + +.globl jmp_to_linux_size +jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux + +.att_syntax prefix \ No newline at end of file diff --git a/linux/ps4-kexec-1250-baikal/reboot.h b/linux/ps4-kexec-1250-baikal/reboot.h new file mode 100644 index 0000000..20b91f8 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/reboot.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)reboot.h 8.3 (Berkeley) 12/13/94 + * $FreeBSD$ + */ + +#ifndef _SYS_REBOOT_H_ +#define _SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. These are passed to + * the boot program and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ +#define RB_SINGLE 0x002 /* reboot to single user only */ +#define RB_NOSYNC 0x004 /* dont sync before reboot */ +#define RB_HALT 0x008 /* don't reboot, just halt */ +#define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ +#define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ +#define RB_KDB 0x040 /* give control to kernel debugger */ +#define RB_RDONLY 0x080 /* mount root fs read-only */ +#define RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define RB_MINIROOT 0x200 /* Unused placeholder */ +#define RB_VERBOSE 0x800 /* print all potentially useful info */ +#define RB_SERIAL 0x1000 /* use serial port as console */ +#define RB_CDROM 0x2000 /* use cdrom as root */ +#define RB_POWEROFF 0x4000 /* turn the power off if possible */ +#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ +#define RB_MUTE 0x10000 /* start up with the console muted */ +#define RB_SELFTEST 0x20000 /* unused placeholder */ +#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ +#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ +#define RB_PAUSE 0x100000 /* pause after each output line during probe */ +#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ +#define RB_PROBE 0x10000000 /* Probe multiple consoles */ +#define RB_MULTIPLE 0x20000000 /* use multiple consoles */ + +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + +#endif diff --git a/linux/ps4-kexec-1250-baikal/string.h b/linux/ps4-kexec-1250-baikal/string.h new file mode 100644 index 0000000..70aa668 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/string.h @@ -0,0 +1,96 @@ +/* + * string.h -- standard C string-manipulation functions. + * + * Copyright (C) 2008 Segher Boessenkool + * Copyright (C) 2009 Haxx Enterprises + * Copyright (C) 2010-2016 Hector Martin "marcan" + * + * Portions taken from the Public Domain C Library (PDCLib). + * http://pdclib.rootdirectory.de/ + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +static inline int strcmp(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s1[i] == s2[i]; i++) + ; + + return s1[i] - s2[i]; +} + +static inline void *memset(void *b, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)b)[i] = c; + + return b; +} + +static inline void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} + +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) + return p1[i] - p2[i]; + + return 0; +} + +static inline size_t strlen(const char *s) +{ + size_t len; + + for (len = 0; s[len]; len++) + ; + + return len; +} + +static inline size_t strnlen(const char *s, size_t count) +{ + size_t len; + + for (len = 0; s[len] && len < count; len++) + ; + + return len; +} + +static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) +{ + size_t len, needed; + + len = needed = strnlen(src, maxlen - 1) + 1; + if (len >= maxlen) + len = maxlen - 1; + + memcpy(dest, src, len); + dest[len] = 0; + + return needed - 1; +} + +#endif diff --git a/linux/ps4-kexec-1250-baikal/types.h b/linux/ps4-kexec-1250-baikal/types.h new file mode 100644 index 0000000..28d6619 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/types.h @@ -0,0 +1,51 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef TYPES_H +#define TYPES_H + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#ifndef TESTING +typedef u8 uint8_t; +typedef u64 size_t; +typedef s64 ssize_t; +typedef u64 uintptr_t; +typedef s64 off_t; +#endif + +#define NULL ((void *)0) + +#define CAT_(x, y) x ## y +#define CAT(x, y) CAT_(x, y) + +#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] +#define OSTRUCT(name, size) struct name { union { OPAD(size); +#define OSTRUCT_END };}; +#define OFIELD(off, field) struct { OPAD(off); field; } + +#define ASSERT_STRSIZE(struc, size) \ + _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#define ENOENT 2 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define ENAMETOOLONG 63 + +#endif diff --git a/linux/ps4-kexec-1250-baikal/uart.c b/linux/ps4-kexec-1250-baikal/uart.c new file mode 100644 index 0000000..a401434 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/uart.c @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "uart.h" +#include "kernel.h" +#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) + +#define AEOLIA_UART_BASE 0xD0340000 +#define BAIKAL_UART_BASE 0xC890E000 + +#define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, BAIKAL_UART_BASE + (intf << 12) + (reg << 2)) + +#define UART_REG_DATA 0 +#define UART_REG_IER 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +# define LSR_TXRDY 0x20 +# define LSR_TEMT 0x40 +#define UART_REG_MSR 6 +#define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) +#define UART0_IER UART_REG(32, 0, UART_REG_IER) +#define UART0_IIR UART_REG(32, 0, UART_REG_IIR) +#define UART0_LCR UART_REG(32, 0, UART_REG_LCR) +#define UART0_MCR UART_REG(32, 0, UART_REG_MCR) +#define UART0_LSR UART_REG(32, 0, UART_REG_LSR) +#define UART0_MSR UART_REG(32, 0, UART_REG_MSR) + +void uart_write_byte(u8 b) +{ + int limit; + u64 barrier; + limit = 250000; + while (!(*UART0_LSR & LSR_TXRDY) && --limit) + ; + *UART0_DATA = b; + __sync_fetch_and_add(&barrier, 0); + limit = 250000; + while (!(*UART0_LSR & LSR_TEMT) && --limit) + ; +} + +void uart_write_char(char c) +{ + if (c == '\n') + uart_write_byte('\r'); + + uart_write_byte(c); +} + +void uart_write_str(const char *s) +{ + while (*s) { + uart_write_char(*s++); + } +} diff --git a/linux/ps4-kexec-1250-baikal/uart.h b/linux/ps4-kexec-1250-baikal/uart.h new file mode 100644 index 0000000..b803e29 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/uart.h @@ -0,0 +1,20 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef UART_H +#define UART_H + +#include "types.h" + +void uart_write_byte(u8 b); +void uart_write_char(char c); +void uart_write_str(const char *s); + +#endif diff --git a/linux/ps4-kexec-1250-baikal/x86.h b/linux/ps4-kexec-1250-baikal/x86.h new file mode 100644 index 0000000..2f6d8b5 --- /dev/null +++ b/linux/ps4-kexec-1250-baikal/x86.h @@ -0,0 +1,195 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef X86_H +#define X86_H + +#define FLAGS_IF (1 << 9) + +#define CR0_WP (1 << 16) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) + +#define PG_V (1 << 0) +#define PG_RW (1 << 1) +#define PG_U (1 << 2) +#define PG_PS (1 << 7) + +#define SEG_TYPE_DATA (0 << 3) +#define SEG_TYPE_READ_WRITE (1 << 1) +#define SEG_TYPE_CODE (1 << 3) +#define SEG_TYPE_EXEC_READ (1 << 1) +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) + +static inline u64 cr0_read(void) +{ + u64 reg; + asm volatile("mov %0, cr0;" : "=r" (reg)); + return reg; +} + +static inline void cr0_write(u64 val) +{ + asm volatile("mov cr0, %0;" :: "r" (val)); +} + +static inline u64 write_protect_disable() +{ + u64 cr0 = cr0_read(); + cr0_write(cr0 & ~CR0_WP); + return cr0; +} + +static inline void write_protect_restore(u64 cr0) +{ + // Use only WP bit of input + cr0_write(cr0_read() | (cr0 & CR0_WP)); +} + +static inline u64 cr3_read(void) +{ + u64 reg; + asm volatile("mov %0, cr3;" : "=r" (reg)); + return reg; +} + +static inline void cr3_write(u64 val) +{ + asm volatile("mov cr3, %0;" :: "r" (val)); +} + +typedef struct { + u16 limit; + u64 address; +} __attribute__((packed)) desc_ptr; + +static inline desc_ptr gdt_read(void) +{ + desc_ptr gdtr; + asm volatile("sgdt %0;" : "=m" (gdtr)); + return gdtr; +} + +static inline void gdt_write(desc_ptr* val) +{ + asm volatile("lgdt %0;" :: "m" (*val)); +} + +//IDT +typedef struct { + u16 limit; + u64 address; +} __attribute__((packed)) idt_ptr; + +static inline idt_ptr idt_read(void) +{ + idt_ptr idtr; + asm volatile("sidt %0;" : "=m" (idtr)); + return idtr; +} + +static inline void idt_write(idt_ptr* val) +{ + asm volatile("lidt %0;" :: "m" (*val)); +} + +static inline void cr4_pge_disable(void) +{ + u64 cr4_temp; + asm volatile( + "mov %0, cr4;" + "and %0, ~0x80;" + "mov cr4, %0;" + : "=r" (cr4_temp) + ); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +static inline void cpu_stop(void) +{ + for (;;) + asm volatile("cli; hlt;" : : : "memory"); +} + +static inline void outl(int port, unsigned int data) +{ + asm volatile("out %w1, %0" : : "a" (data), "d" (port)); +} + +static inline void wrmsr(u32 msr_id, u64 msr_value) +{ + asm volatile( + "wrmsr" + : + : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) + ); +} + +static inline u64 rdmsr(u64 msr_id) +{ + u32 low, high; + asm volatile ( + "rdmsr" + : "=a"(low), "=d"(high) + : "c"(msr_id) + ); + return ((u64)high << 32) | low; +} + +static inline u64 rdtsc (void) +{ + unsigned int tickl, tickh; + asm volatile( + "rdtsc" + :"=a"(tickl),"=d"(tickh) + ); + return ((u64)tickh << 32) | tickl; +} + +static inline void udelay(unsigned int usec) { + u64 later = rdtsc() + usec * 1594ULL; + while (((s64)(later - rdtsc())) > 0); +} + +static inline void disable_interrupts(void) +{ + asm volatile("cli"); +} + +static inline void enable_interrupts(void) +{ + asm volatile("sti"); +} + +static inline u64 read_flags(void) +{ + u64 flags; + asm volatile("pushf; pop %0;" : "=r" (flags)); + return flags; +} + +static inline u64 intr_disable(void) +{ + u64 flags = read_flags(); + disable_interrupts(); + return flags; +} + +static inline void intr_restore(u64 flags) +{ + // TODO should only IF be or'd in? + asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); +} + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/LICENSE b/linux/ps4-kexec-1250-pro-baikal/LICENSE new file mode 100644 index 0000000..6c1cbba --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2015-2016 shuffle2 +Copyright (C) 2015-2016 Hector Martin "marcan" +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/ps4-kexec-1250-pro-baikal/Makefile b/linux/ps4-kexec-1250-pro-baikal/Makefile new file mode 100644 index 0000000..4101e52 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/Makefile @@ -0,0 +1,37 @@ +TOOLCHAIN_PREFIX ?= +CC = $(TOOLCHAIN_PREFIX)gcc +AR = $(TOOLCHAIN_PREFIX)ar +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy + +CFLAGS=$(CFLAG) -DPS4_12_50 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX +CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ + -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ + -fno-asynchronous-unwind-tables \ + -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ + -mcmodel=small -mno-red-zone + +SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ + acpi.c crc32.c + +OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) +DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld + +all: libkexec.a kexec.bin + +%.o: %.c *.h + $(CC) -c $(CFLAGS) -o $@ $< + +%.o: %.S + $(CC) -c $(CFLAGS) -o $@ $< + +libkexec.a: $(OBJS) + $(AR) -rc $@ $(OBJS) + +kexec.elf: libkexec.a kexec.ld + $(CC) $(CFLAGS) -o $@ libkexec.a + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +clean: + rm -f libkexec.a kexec.elf kexec.bin $(OBJS) diff --git a/linux/ps4-kexec-1250-pro-baikal/README.md b/linux/ps4-kexec-1250-pro-baikal/README.md new file mode 100644 index 0000000..6500878 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/README.md @@ -0,0 +1,121 @@ +# PS4 kexec implementation + +This repo implements a kexec()-style system call for the PS4 Orbis kernel +(FreeBSD derivative). This is designed to boot a Linux kernel directly from +FreeBSD. + +This is not an exploit. It is useless without some mechanism of injecting code +into the PS4 OS kernel. + +## Building + +To build a kexec.bin relocatable binary using the supplied Makefile, just type +`make`. This will also build a kexec.a archive. You can either use the binary +directly, or link the archive into your own project. + +If you link kexec.a with your own code, you need to supply the two symbols +`_start` and `_end` in your linker script, as `kernel_init()` will try to remap +all pages covered by that range as RWX (to make global variable accesses work). +Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this +feature, if you have already taken care of page permissions for the code. + +If you use a compiler toolchain that have a special prefix you can declare it +by passing TOOLCHAIN_PREFIX option to the Makefile like this: + + make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' + +## Usage + +The code is designed to be completely standalone. There is a single entry point: + + int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); + +Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel +symbols and install the sys_kexec system call. The syscall is registered +as number 153 by default (you can change this in kexec.h). The return value +is 0 on success, or negative on error. + +You may pass something other than NULL as `early_printf`. In that case, that +function will be used for debug output during early symbol resolution, before +printf is available. + +Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is +enabled by default, symtab also disappears in newer kernel, we have to +hardcode offsets for some symbols. Currently we use the `early_printf` +given by user to caculate the base address of kernel, then relocate all the +symbols from the kernel base. You could enable this feature like this: + + make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' + +If you do not want to call the syscall from userspace, you can pass the address +of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the +address of `sys_kexec`, so you can invoke it manually (see kexec.h for +its prototype and how the arguments are passed). + +If you are using the standalone kexec.bin blob, then the `kexec_init` function +is always located at offset 0, so simply call the base address of the blob. +Don't forget to pass two NULL arguments (or the appropriate pointers). + +The injected `sys_kexec` system call takes (userspace) pointers to the kernel +and initramfs blobs, their sizes, and a pointer to the (null-terminated) command +line string. From userspace, this looks like this: + + int kexec(void *kernel_image, size_t image_size, + void *initramfs, size_t initramfs_size, + const char *cmdline); + + // syscall() usage: + syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); + +`kexec()` will load the kernel and initramfs into memory, but will not directly +boot them. To boot the loaded kernel, shut down the system. This can be +accomplished by pressing the power button, but can also be done more quickly +and reliably from userspace with the following sequence of system calls (this +kills userspace quickly but still does a controlled filesystem unmount): + + int evf = syscall(540, "SceSysCoreReboot"); + syscall(546, evf, 0x4000, 0); + syscall(541, evf); + // should be syscall(37, 1, 30) but only tested via kill symbol + kill(1, 30); + +Note that this software should be loaded into kernel memory space. If you are +running kernel code from userland mappings, you should either switch to kernel +mappings or separately copy kexec.bin to a location in kernel address space. +While syscalls or exploit code may run properly from userland, the shutdown hook +will not, as it will be called from a different process context. + +## Features + +`kernel_init()` will automatically find the Orbis OS kernel and resolve all +necessary symbols to work. There are no static symbol dependencies. If +`DO_NOT_REMAP_RWX` is not defined (the default), it will also patch +`pmap_protect` to disable the W^X restriction. + +In addition to loading the user-supplied initramfs, `kexec` will locate the +Radeon firmware blobs inside Orbis OS, extract them, convert them to a format +suitable for Linux, and append them as an additional initramfs cpio image to +the existing initramfs. This avoids the need to distribute the Radeon firmware +blobs. The `radeon` module, when compiled into the kernel, will automatically +load this firmware on boot. Note however that most typical initramfs scripts +will wipe the initramfs contents while pivoting to the real system, so if you +compile `radeon` as a module you may not be able to access the firmware after +boot. To cover that case, add some code to your initramfs `/init` script to +copy the firmware to a tmpfs mounted on the real filesystem: + + # assuming real root FS is mounted on /mnt + + mkdir -p /mnt/lib/firmware/radeon + mount -t tmpfs none /mnt/lib/firmware/radeon + cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ + + # now switch_root to /mnt + +This avoids having to permanently store copies of the Radeon firmware, which +isn't really necessary for most use cases. + +There is significant debug logging available, which will appear on the system +UART. Most of the code relies on the kernel `printf` implementation, and +therefore you should patch out the UART output blanker to see it. The final +code that runs on the boot CPU before booting the kernel uses direct UART +writes and is not affected by the blanking feature of Orbis OS. diff --git a/linux/ps4-kexec-1250-pro-baikal/acpi.c b/linux/ps4-kexec-1250-pro-baikal/acpi.c new file mode 100644 index 0000000..bfccf08 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/acpi.c @@ -0,0 +1,320 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "types.h" +#include "kernel.h" +#include "acpi.h" +#include "acpi_caps.h" + +#ifdef TESTING +# include +# include +# include +# include +# include +# include +#else +# include "kernel.h" +# include "string.h" +# define printf kern.printf +#endif + +#define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) +#define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) + +#define PACKED __attribute__((packed)) + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +struct RSDP { + u64 sig; + u8 checksum; + u8 oemid[6]; + u8 rev; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 rsvd[3]; +} PACKED; + +struct SDTH { + u32 sig; + u32 length; + u8 rev; + u8 checksum; + u8 oem_id[6]; + u8 oem_tid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; +} PACKED; + +struct RSDT { + struct SDTH hdr; + u32 table_addr[]; +} PACKED; + +struct XSDT { + struct SDTH hdr; + u64 table_addr[]; +} PACKED; + +struct FADT { + struct SDTH hdr; + u32 facs; + u32 dsdt; + // more stuff... +} PACKED; + +struct ivhd_entry4 { + u8 type; + u16 devid; + u8 flags; +} PACKED; + +struct ivhd_header { + u8 type; + u8 flags; + u16 length; + u16 devid; + u16 cap_ptr; + u64 mmio_phys; + u16 pci_seg; + u16 info; + u32 efr_attr; +} PACKED; + +struct IVRS { + struct SDTH hdr; + u32 IVinfo; + u8 reserved[8]; + struct ivhd_header hd_hdr; + struct ivhd_entry4 hd_entries[3]; +} PACKED; + +struct MMIO { + u64 baseAddressECM; + u16 pciSegmentGroup; + u8 startPCIBus; + u8 endPCIBus; + u32 reserved; +} PACKED; + +// We have enough space to use the second half of the 64KB table area +// as scratch space for building the tables +#define BUFFER_OFF 0x8000 + +#define P2M(p) (((u64)(p)) - phys_base + map_base) +#define M2P(p) ((((void*)(p)) - map_base) + phys_base) +#define B2P(p) ((((void*)(p)) - buf_base) + phys_base) +#define P2B(p) ((((void*)(p)) - phys_base) + buf_base) + +#define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) +#define PADB(s) p += (s) +#define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) +#define ALLOC(t) (t)ALLOCB(sizeof(t)) +#define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) +#define COPYT(s) COPYB(((struct SDTH*)s)->length, s) +#define COPYTP(s) COPYT(P2M(s)) +#define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) +#define COPYP(t, s) COPY(t, P2M(s)) + +static void rsdp_checksum(struct RSDP *rsdp) { + rsdp->checksum = rsdp->ext_checksum = 0; + + u8 sum = 0; + for (int i = 0; i < 20; i++) + sum += ((u8*)rsdp)[i]; + rsdp->checksum = -sum; + sum = 0; + for (int i = 0; i < sizeof(*rsdp); i++) + sum += ((u8*)rsdp)[i]; + rsdp->ext_checksum = -sum; +} + +static void table_checksum(void *table) { + struct SDTH *hdr = table; + hdr->checksum = 0; + u8 sum = 0; + for (int i = 0; i < hdr->length; i++) + sum += ((u8*)table)[i]; + hdr->checksum = -sum; +} + +#define IVHD_FLAG_ISOC_EN_MASK 0x08 +#define IVHD_DEV_ALL 0x01 +#define IVHD_DEV_SELECT 0x02 +#define IVHD_DEV_SELECT_RANGE_START 0x03 +#define IVHD_DEV_RANGE_END 0x04 + +#define ACPI_DEVFLAG_SYSMGT1 0x10 +#define ACPI_DEVFLAG_SYSMGT2 0x20 + +static void *build_ivrs(struct IVRS *ivrs) { + memset(ivrs, 0, sizeof(*ivrs)); + + ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); + ivrs->hdr.length = sizeof(*ivrs); + ivrs->hdr.rev = 1; + memcpy(ivrs->hdr.oem_id, "F0F ", 6); + memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); + ivrs->hdr.oem_rev = 0x20161225; + memcpy(ivrs->hdr.creator_id, "KEXC", 4); + ivrs->hdr.creator_rev = 0x20161225; + ivrs->IVinfo = 0x00203040; //48882_IOMMU.pdf page 251 + + struct ivhd_header *hdr = &ivrs->hd_hdr; //48882_IOMMU.pdf page 254 + hdr->type = 0x10; + hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; + hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); + hdr->devid = PCI_DEVFN(0, 2); + hdr->cap_ptr = 0x40; // from config space + 0x34 + hdr->mmio_phys = 0xfc000000; //Base address of IOMMU control registers in MMIO space + hdr->pci_seg = 0; + hdr->info = 0; // msi msg num? (the pci cap should be written by software) + // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 + hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); //Feature Reporting Field, 48882_IOMMU.pdf page 255 + + struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; + // on fbsd, all aeolia devfns have active entries except memories (func 6) + // not sure if this is just because it wasn't in use when i dumped it? + // all entries are r/w + // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) + // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) + // Modes: + // 4 level: + // apcie + // 3 level: + // all others + + // the way to encode this info into the IVHD entries is fairly arbitrary... + entries[0].type = IVHD_DEV_SELECT; //DTE setting applies to the device specifed in DevID field. + entries[0].devid = PCI_DEVFN(20, 0); //vendorId: 104D, deviceId: 90D7; Sony Baikal ACPI + entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; + + entries[1].type = IVHD_DEV_SELECT_RANGE_START; + entries[1].devid = PCI_DEVFN(20, 1); + entries[1].flags = 0; //Identifies a device able to assert INIT interrupts (page 262) + entries[2].type = IVHD_DEV_RANGE_END; + entries[2].devid = PCI_DEVFN(20, 7); + entries[2].flags = 0; //Identifies a device able to assert INIT interrupts + + table_checksum(ivrs); + return ivrs + 1; +} + +void fix_acpi_tables(void *map_base, u64 phys_base) +{ + void *buf_base = map_base + 0x8000; + void *p = buf_base; + memset(buf_base, 0, 0x8000); + + printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); + + struct RSDP *rsdp = COPYP(struct RSDP, phys_base); + printf("RSDT at 0x%x\n", rsdp->rsdt_addr); + printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); + + struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); + struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); + rsdp->rsdt_addr = B2P(rsdt); + + PADB(0x30); // this gives us space for new tables + + struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); + rsdp->xsdt_addr = B2P(xsdt); + + PADB(0x60); + + struct FADT *fadt = NULL; + + int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; + int i; + for (i = 0; i < cnt; i++) { + struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); + printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); + switch (hdr->sig) { + case SIG32('F', 'A', 'C', 'P'): + { + fadt = (void*)hdr; + printf("FACS at 0x%x\n", fadt->facs); + printf("DSDT at 0x%x\n", fadt->dsdt); + // Sony puts the FACS before the FADT, unaligned, which is + // noncompliant, but let's keep it there + u8 *facs = COPYB(64, P2M(fadt->facs)); + fadt = (void*)(hdr = COPYT(hdr)); + fadt->facs = B2P(facs); + PADB(0x38); + break; + } + case SIG32('S', 'S', 'D', 'T'): + { + // Put the DSDT before the SSDT + if (fadt) { + PADB(0xf0); + u8 *dsdt = COPYTP(fadt->dsdt); + fadt->dsdt = B2P(dsdt); + PADB(0x174); + table_checksum(fadt); + } else { + printf("ERROR: no FADT yet?\n"); + } + hdr = COPYT(hdr); + break; + } + default: + hdr = COPYT(hdr); + } + table_checksum(hdr); + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); + } + + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); + i++; + p = build_ivrs(p); + + rsdt->hdr.length = sizeof(*rsdt) + 4 * i; + xsdt->hdr.length = sizeof(*xsdt) + 8 * i; + + rsdp_checksum(rsdp); + table_checksum(rsdt); + table_checksum(xsdt); + memcpy(map_base, buf_base, p - buf_base); +} + +u32 msi_mask(unsigned x) { + /* Don't shift by >= width of type */ + if (x >= 5) + return 0xffffffff; + return (1 << (1 << x)) - 1; +} +void disableMSI(u64 MSICapabilityRegAddr) { + PPCI_MSI_CAPABILITY pMSICapability = (PPCI_MSI_CAPABILITY)PA_TO_DM(MSICapabilityRegAddr); + if (pMSICapability->msiEnable == 1) + pMSICapability->msiEnable = 0; + pMSICapability->mask64 = msi_mask(pMSICapability->multipleMessageCapable); +} + +#ifdef TESTING + +int main(int argc, char **argv) +{ + int fd; + void *base; + + fd = open(argv[1], O_RDWR); + base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + fix_acpi_tables(base, 0xe0000); + return 0; +} + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/acpi.h b/linux/ps4-kexec-1250-pro-baikal/acpi.h new file mode 100644 index 0000000..45f2834 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/acpi.h @@ -0,0 +1,22 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ACPI_H +#define ACPI_H + +#define PACKED __attribute__((packed)) + +void fix_acpi_tables(void *map_base, u64 phys_base); + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) + +void disableMSI(u64 MSICapabilityRegAddr); + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/acpi_caps.h b/linux/ps4-kexec-1250-pro-baikal/acpi_caps.h new file mode 100644 index 0000000..070e9f6 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/acpi_caps.h @@ -0,0 +1,30 @@ +#ifndef ACPI_CAPS_H +#define ACPI_CAPS_H + +#define UCHAR u8 +#define USHORT u16 +#define ULONG u32 +#define PACKED __attribute__((packed)) +typedef struct PACKED _PCI_CAPABILITIES_HEADER { + UCHAR CapabilityID; + UCHAR Next; +} PCI_CAPABILITIES_HEADER, *PPCI_CAPABILITIES_HEADER; + +typedef struct PACKED _PCI_MSI_CAPABILITY { + PCI_CAPABILITIES_HEADER Header; + u16 msiEnable : 1, multipleMessageCapable : 3, multipleMessageEnable : 3, address64Capable : 1, reserved0 : 8; + u32 lowerAddress : 30, reserved1 : 2; + union { + struct { + u32 upperAddress; + u32 messageData64 : 16, reservedData64 : 16; + u32 mask64; + }; + struct { + u32 messageData32 : 16, reservedData32 : 16; + u32 mask32; + }; + }; +} PCI_MSI_CAPABILITY, *PPCI_MSI_CAPABILITY; + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/crc32.c b/linux/ps4-kexec-1250-pro-baikal/crc32.c new file mode 100644 index 0000000..7836c18 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/crc32.c @@ -0,0 +1,102 @@ +/*- +* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or +* code or tables extracted from it, as desired without restriction. +* +* First, the polynomial itself and its table of feedback terms. The +* polynomial is +* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 +* +* Note that we take it "backwards" and put the highest-order term in +* the lowest-order bit. The X^32 term is "implied"; the LSB is the +* X^31 term, etc. The X^0 term (usually shown as "+1") results in +* the MSB being 1 +* +* Note that the usual hardware shift register implementation, which +* is what we're using (we're merely optimizing it by doing eight-bit +* chunks at a time) shifts bits into the lowest-order term. In our +* implementation, that means shifting towards the right. Why do we +* do it this way? Because the calculated CRC must be transmitted in +* order from highest-order term to lowest-order term. UARTs transmit +* characters in order from LSB to MSB. By storing the CRC this way +* we hand it to the UART in the order low-byte to high-byte; the UART +* sends each low-bit to hight-bit; and the result is transmission bit +* by bit from highest- to lowest-order term without requiring any bit +* shuffling on our part. Reception works similarly +* +* The feedback terms table consists of 256, 32-bit entries. Notes +* +* The table can be generated at runtime if desired; code to do so +* is shown later. It might not be obvious, but the feedback +* terms simply represent the results of eight shift/xor opera +* tions for all combinations of data and CRC register values +* +* The values must be right-shifted by eight bits by the "updcrc +* logic; the shift must be unsigned (bring in zeroes). On some +* hardware you could probably optimize the shift in assembler by +* using byte-swap instructions +* polynomial $edb88320 +* +* +* CRC32 code derived from work by Gary S. Brown. +*/ + +#include "crc32.h" + +static u32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 crc32(u32 crc, const void *buf, size_t size) +{ + const u8 *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} diff --git a/linux/ps4-kexec-1250-pro-baikal/crc32.h b/linux/ps4-kexec-1250-pro-baikal/crc32.h new file mode 100644 index 0000000..777278d --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/crc32.h @@ -0,0 +1,7 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +u32 crc32(u32 crc, const void *buf, size_t size); + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/elf.h b/linux/ps4-kexec-1250-pro-baikal/elf.h new file mode 100644 index 0000000..63371c7 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/elf.h @@ -0,0 +1,71 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + u8 e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + u64 e_entry; + u64 e_phoff; + u64 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf64_Ehdr; + +typedef struct { + u32 p_type; + u32 p_flags; + u64 p_offset; + void *p_vaddr; + u64 p_paddr; + u64 p_filesz; + u64 p_memsz; + u64 p_align; +} Elf64_Phdr; + +#define PT_DYNAMIC 2 +#define PT_PHDR 6 + +#define DT_NULL 0 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_STRSZ 10 + +typedef struct { + s64 d_tag; + union { + u64 d_val; + void *d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + void *st_value; + u64 st_size; +} Elf64_Sym; + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/firmware.c b/linux/ps4-kexec-1250-pro-baikal/firmware.c new file mode 100644 index 0000000..a831b1c --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/firmware.c @@ -0,0 +1,479 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "firmware.h" +#include "types.h" +#include "kernel.h" +#include "string.h" +#include "types.h" +#include "crc32.h" + +#define DIR 0040755 +#define FILE 0100644 + +struct firmware_header { + u32 size_bytes; + u32 header_size_bytes; + u16 header_version_major; + u16 header_version_minor; + u16 ip_version_major; + u16 ip_version_minor; + u32 ucode_version; + u32 ucode_size_bytes; + u32 ucode_array_offset_bytes; + u32 crc32; + union { + struct { + u32 ucode_feature_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } gfx1; + struct { + u32 ucode_feature_version; + u32 save_and_restore_offset; + u32 clear_state_descriptor_offset; + u32 avail_scratch_ram_locations; + u32 master_pkt_description_offset; + u8 end[]; + } rlc1; + struct { + u32 ucode_feature_version; + u32 ucode_change_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } sdma1; + u8 raw[0xe0]; + }; +}; + +static inline char hex(u8 c) +{ + if (c <= 9) + return '0' + c; + return 'a' + c - 10; +} + +static void hex8(u8 **p, u32 val) +{ + *(*p)++ = hex(val >> 28); + *(*p)++ = hex((val >> 24) & 0xf); + *(*p)++ = hex((val >> 20) & 0xf); + *(*p)++ = hex((val >> 16) & 0xf); + *(*p)++ = hex((val >> 12) & 0xf); + *(*p)++ = hex((val >> 8) & 0xf); + *(*p)++ = hex((val >> 4) & 0xf); + *(*p)++ = hex(val & 0xf); +} + +void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) +{ + size_t name_len = strlen(name); + + // Pad to 4 byte multiple + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; + + memcpy(*p, "070701", 6); + *p += 6; + hex8(p, 0); // c_ino + hex8(p, mode); // c_mode + hex8(p, 0); // c_uid + hex8(p, 0); // c_gid + hex8(p, 1); // c_nlink + hex8(p, 0); // c_mtime + hex8(p, size); // c_filesize + hex8(p, 0); // c_maj + hex8(p, 0); // c_min + hex8(p, 0); // c_rmaj + hex8(p, 0); // c_rmin + hex8(p, name_len + 1); // c_namesize + hex8(p, 0); // c_chksum + memcpy(*p, name, name_len); + *p += name_len; + *(*p)++ = 0; + + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; +} + +struct fw_header_t { + u64 size_words; + char *unk_ident; + u64 unk; + void *blob; + u64 unk2; +}; + +struct fw_info_t { + struct fw_header_t *rlc; + struct fw_header_t *sdma0; + struct fw_header_t *sdma1; + struct fw_header_t *ce; + struct fw_header_t *pfp; + struct fw_header_t *me; + struct fw_header_t *mec1; + struct fw_header_t *mec2; +}; + +struct fw_expected_sizes_t { + u64 rlc; + u64 sdma0; + u64 sdma1; + u64 ce; + u64 pfp; + u64 me; + u64 mec1; + u64 mec2; +}; +static const struct fw_expected_sizes_t liverpool_fw_sizes = { + LVP_FW_RLC_SIZE, + LVP_FW_SDMA_SIZE, + LVP_FW_SDMA1_SIZE, + LVP_FW_CE_SIZE, + LVP_FW_PFP_SIZE, + LVP_FW_ME_SIZE, + LVP_FW_MEC_SIZE, + LVP_FW_MEC2_SIZE +}; +static const struct fw_expected_sizes_t gladius_fw_sizes = { + GL_FW_RLC_SIZE, + GL_FW_SDMA_SIZE, + GL_FW_SDMA1_SIZE, + GL_FW_CE_SIZE, + GL_FW_PFP_SIZE, + GL_FW_ME_SIZE, + GL_FW_MEC_SIZE, + GL_FW_MEC2_SIZE +}; + +void copy_edid(u8 **p, int sz) +{ + int i; + u8 *edid = *p; + u8 *off_edid = kern.edid; + + memset(edid, 0, sz); + *p += sz; + + for(i = 0; i < sz; i++) + *(edid + i) = *(off_edid + i); + + *p += sz; +} + +void copy_eap_hdd_key(u8 **p) +{ + int i; + u8 *eap_key = *p; + u8 *off_eap_key = kern.eap_hdd_key; + + memset(eap_key, 0, 0x20); + *p += 0x20; + + for(i = 0; i < 0x20; i++) + { + if(i < 0x10) + *(eap_key + i) = *(off_eap_key + 0xF - i); + else + *(eap_key + i) = *(off_eap_key + 0x2F - i); + } + *p += 0x20; +} + +int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + kern.printf("Copying %s firmware\n", name); + if (expected_size != (hdr->size_words * 4)) { + kern.printf("copy_firmware: %s: expected size %d, got %d\n", + name, expected_size, hdr->size_words * 4); + return 0; + } + + struct firmware_header *fhdr = (struct firmware_header*)*p; + memset(fhdr, 0, sizeof(*fhdr)); + *p += sizeof(*fhdr); + + memcpy(*p, hdr->blob, expected_size); + + fhdr->size_bytes = expected_size + sizeof(*fhdr); + fhdr->header_size_bytes = offsetof(struct firmware_header, raw); + fhdr->header_version_major = 1; + fhdr->header_version_minor = 0; + fhdr->ucode_version = 0x10; + fhdr->ucode_size_bytes = expected_size; + fhdr->ucode_array_offset_bytes = sizeof(*fhdr); + + *p += expected_size; + + return 1; +} + +int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); + fhdr->gfx1.ucode_feature_version = 21; + fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); + fhdr->rlc1.ucode_feature_version = 1; + fhdr->rlc1.save_and_restore_offset = 0x90; + fhdr->rlc1.clear_state_descriptor_offset = 0x3d; + fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? + fhdr->rlc1.master_pkt_description_offset = 0; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 2; + fhdr->ip_version_minor = 1; + fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); + fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; + fhdr->sdma1.ucode_change_version = 0; + fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +static const u32 pfp_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc4200016, // ldw r8, [r0, #0x16] + 0xdc030000, // mov ctr, r0 + 0xcc000049, // stw r0, [r0, #0x49] + 0xcc200013, // stw r0, [r8, #0x13] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 ce_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc420000c, // ldw r8, [r0, #0xc] + 0xdc030000, // mov ctr, r0 + 0xcc00002f, // stw r0, [r0, #0x2f] + 0xcc200012, // stw r0, [r8, #0x12] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 mec_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc43c000c, // ldw r15, [r0, #0x9] + 0xdc030000, // mov ctr, r0 + 0xcc00002b, // stw r0, [r0, #0x2b] + 0xcc3c000d, // stw r0, [r15, #0xd] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +#define PACKET_TYPE_NOP 0x10 + +static void patch_fw(void *p, const u32 *handler, int handler_size) { + int size = ((struct firmware_header*)p)->ucode_size_bytes; + int code_size = (size & ~0xfff) / 4; + int nop_start = code_size - 0x10; + + u32 *fw = p + sizeof(struct firmware_header); + kern.printf("NOP handler at 0x%x\n", nop_start); + memcpy(&fw[nop_start], handler, handler_size); + + // patch the branch table entry + for (int off = code_size; off < size/4; off++) { + if ((fw[off] >> 16) == PACKET_TYPE_NOP) { + fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; + } + } +} + +struct fw_info_t *get_fw_info() { + if (kern.gc_get_fw_info) { + return kern.gc_get_fw_info(); + } else if (kern.Starsha_UcodeInfo) { + return kern.Starsha_UcodeInfo; + } else { + return NULL; + } +} + +const struct fw_expected_sizes_t *get_fw_expected_sizes() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return &gladius_fw_sizes; + } else { + return &liverpool_fw_sizes; + } +} + +const char * get_gpu_name() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return "gladius"; + } else { + return "liverpool"; + } +} + +ssize_t firmware_extract(void *dest) +{ + u8 *p = dest; + + // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. + struct fw_info_t *info = get_fw_info(); + if (!info) { + kern.printf("firmware_extract: Could not locate firmware table"); + return -1; + } + const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); + + //Eap hdd key + cpio_hdr(&p, "key", DIR, 0); + cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); + copy_eap_hdd_key(&p); + + cpio_hdr(&p, "lib", DIR, 0); + cpio_hdr(&p, "lib/firmware", DIR, 0); + + /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ + int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); + cpio_hdr(&p, "lib/firmware/edid", DIR, 0); + cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); + copy_edid(&p, edid_sz); + + char dir[7]; + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) + kern.snprintf(dir, sizeof(dir), "amdgpu"); + else + kern.snprintf(dir, sizeof(dir), "amdgpu"); + + char dir_path[64]; + kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); + cpio_hdr(&p, dir_path, DIR, 0); + + char pfp_path[64]; + kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", pfp_path); + cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); + u8 *pfp = p; + if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) + return -1; + patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); + + char me_path[64]; + kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", me_path); + cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); + if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) + return -1; + + char ce_path[64]; + kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", ce_path); + cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); + u8 *ce = p; + if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) + return -1; + patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); + + char mec_path[64]; + kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec_path); + cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); + u8 *mec1 = p; + if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) + return -1; + patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); + + char mec2_path[64]; + kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec2_path); + cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); + u8 *mec2 = p; + if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) + return -1; + patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); + + char rlc_path[64]; + kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", rlc_path); + cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); + if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) + return -1; + + char sdma_path[64]; + kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma_path); + cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); + if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + char sdma1_path[64]; + kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma1_path); + cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); + if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + size_t size = p - (u8*)dest; + if (size > FW_CPIO_SIZE) { + kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); + return -1; + } + + return size; +} diff --git a/linux/ps4-kexec-1250-pro-baikal/firmware.h b/linux/ps4-kexec-1250-pro-baikal/firmware.h new file mode 100644 index 0000000..4835983 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/firmware.h @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include "types.h" + +//sizes eap_hdd_key +#define EAP_HDD_KEY_SIZE 0x20 +#define EDID_SIZE 256 + +// sizes for liverpool +#define LVP_FW_CE_SIZE 8576 +#define LVP_FW_ME_SIZE 16768 +#define LVP_FW_MEC_SIZE 16768 +#define LVP_FW_MEC2_SIZE 16768 +#define LVP_FW_PFP_SIZE 16768 +#define LVP_FW_RLC_SIZE 6144 +#define LVP_FW_SDMA_SIZE 4200 +#define LVP_FW_SDMA1_SIZE 4200 +// sizes for gladius +#define GL_FW_CE_SIZE 8576 +#define GL_FW_ME_SIZE 16768 +#define GL_FW_MEC_SIZE 16768 +#define GL_FW_MEC2_SIZE 16768 +#define GL_FW_PFP_SIZE 16768 +#define GL_FW_RLC_SIZE 8192 +#define GL_FW_SDMA_SIZE 4200 +#define GL_FW_SDMA1_SIZE 4200 + +#define MAX(x ,y) (((x) > (y)) ? (x) : (y)) +#define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) + +#define FW_CE_SIZE MAX_FW_SIZE(CE) +#define FW_ME_SIZE MAX_FW_SIZE(ME) +#define FW_MEC_SIZE MAX_FW_SIZE(MEC) +#define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) +#define FW_PFP_SIZE MAX_FW_SIZE(PFP) +#define FW_RLC_SIZE MAX_FW_SIZE(RLC) +#define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) +#define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) + +// Conservative value (max 113 bytes plus name size plus alignment) +#define CPIO_HEADER_SIZE 256 + +#define FW_HEADER_SIZE 256 + +// Leave space for 16 files (currently 12) +#define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ + FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ + FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ + FW_HEADER_SIZE * 8) + +ssize_t firmware_extract(void *dest); + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/kernel.c b/linux/ps4-kexec-1250-pro-baikal/kernel.c new file mode 100644 index 0000000..b6d31b4 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/kernel.c @@ -0,0 +1,383 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "string.h" +#include "elf.h" +#include "x86.h" +#include "../magic.h" + +struct ksym_t kern; +int (*early_printf)(const char *fmt, ...) = NULL; + +#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) + +#ifdef NO_SYMTAB + +#define RESOLVE_NOERR(name) do { \ + if (kern_off_ ## name == 0) { \ + kern.name = 0; \ + } else { \ + kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ + } \ +} while (0); + +#define RESOLVE(name) do { \ + if (kern_off_ ## name == 0) { \ + return 0; \ + } \ + RESOLVE_NOERR(name) \ +} while (0); + +#else + +#define KERNSIZE 0x2000000 + +static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; +static Elf64_Sym *symtab; +static char *strtab; +static size_t strtab_size; + +static Elf64_Ehdr *find_kern_ehdr(void) +{ + // Search for the kernel copy embedded in ubios, then follow it to see + // where it was relocated to + for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; + if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { + for (size_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; + if (phdr->p_type == PT_PHDR) { + return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); + } + } + } + } + return NULL; +} + +static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf64_Dyn *)phdr->p_vaddr; + } + } + return NULL; +} + +static int elf_parse_dyn(Elf64_Dyn *dyn) +{ + for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_SYMTAB: + symtab = (Elf64_Sym *)dp->d_un.d_ptr; + break; + case DT_STRTAB: + strtab = (char *)dp->d_un.d_ptr; + break; + case DT_STRSZ: + strtab_size = dp->d_un.d_val; + break; + } + } + return symtab && strtab && strtab_size; +} + +void *kernel_resolve(const char *name) +{ + for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { + if (!strcmp(name, &strtab[sym->st_name])) { + eprintf("kern.%s = %p\n", name, (void*)sym->st_value); + return (void *)sym->st_value; + } + } + eprintf("Failed to resolve symbol '%s'\n", name); + return NULL; +} + +#define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) +#define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; + +#endif + +static int resolve_symbols(void) +{ + RESOLVE(printf); + RESOLVE(snprintf); + early_printf = kern.printf; + RESOLVE(copyin); + RESOLVE(copyout); + RESOLVE(copyinstr); + RESOLVE(kernel_map); + RESOLVE(kernel_pmap_store); + RESOLVE(kmem_alloc_contig); + RESOLVE(kmem_free); + RESOLVE(pmap_extract); + RESOLVE(pmap_protect); + RESOLVE(sysent); + RESOLVE(sched_pin); + RESOLVE(sched_unpin); + RESOLVE(smp_rendezvous); + RESOLVE(smp_no_rendevous_barrier); + RESOLVE(icc_query_nowait); + RESOLVE_NOERR(Starsha_UcodeInfo); + RESOLVE_NOERR(gpu_devid_is_9924); + RESOLVE_NOERR(gc_get_fw_info); + RESOLVE_NOERR(eap_hdd_key); + RESOLVE_NOERR(edid); + RESOLVE(wlanbt); + RESOLVE(kern_reboot); + RESOLVE(set_gpu_freq); + RESOLVE(set_pstate); + RESOLVE(update_vddnp); + RESOLVE(set_cu_power_gate); + RESOLVE_NOERR(pstate_before_shutdown); + return 1; +} + +#define M_WAITOK 0x0002 +#define M_ZERO 0x0100 + +#define VM_MEMATTR_DEFAULT 0x06 + +void *kernel_alloc_contig(size_t size) +{ + // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... + vm_offset_t ret = 0; + while(!(ret = kern.kmem_alloc_contig( + *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, + ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); + + /*if (!ret) { + kern.printf("Failed to allocate %zud bytes\n", size); + return NULL; + }*/ + return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); +} + +void kernel_free_contig(void *addr, size_t size) +{ + if (!addr) + return; + kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); +} + +int kernel_hook_install(void *target, void *hook) +{ + uintptr_t t = (uintptr_t)target; // addr to redirect to + uintptr_t h = (uintptr_t)hook; // place to write the thunk + + if (!hook || !target) { + return 0; + } + + kern.printf("kernel_hook_install(%p, %p)\n", target, hook); + + if (!(t & (1L << 63))) { + kern.printf("\n===================== WARNING =====================\n"); + kern.printf("hook target function address: %p\n", target); + kern.printf("It looks like we're running from userland memory.\n"); + kern.printf("Please run this code from a kernel memory mapping.\n\n"); + return 0; + } + s64 displacement = t - (h + 5); + + kern.sched_pin(); + u64 wp = write_protect_disable(); + if (displacement < -0x80000000 || displacement > 0x7fffffff) { + kern.printf(" Using 64bit absolute jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[2]; + s32 zero; + void *target; + } jmp = { + .op = { 0xff, 0x25 }, + .zero = 0, + .target = target, + }; + ASSERT_STRSIZE(struct jmp_t, 14); + memcpy(hook, &jmp, sizeof(jmp)); + } else { + kern.printf(" Using 32bit relative jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[1]; + s32 imm; + } jmp = { + .op = { 0xe9 }, + .imm = displacement, + }; + ASSERT_STRSIZE(struct jmp_t, 5); + memcpy(hook, &jmp, sizeof(jmp)); + } + wbinvd(); + write_protect_restore(wp); + kern.sched_unpin(); + + return 1; +} + +void kernel_syscall_install(int num, void *call, int narg) +{ + struct sysent_t *sy = &kern.sysent[num]; + + kern.sched_pin(); + u64 wp = write_protect_disable(); + + memset(sy, 0, sizeof(*sy)); + sy->sy_narg = narg; + sy->sy_call = call; + sy->sy_thrcnt = 1; + + write_protect_restore(wp); + kern.sched_unpin(); +} + +void kernel_remap(void *start, void *end, int perm) +{ + u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); + u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); + + kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); + kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); +} + +static volatile int _global_test = 0; + +#ifndef DO_NOT_REMAP_RWX +extern u8 _start[], _end[]; + +static int patch_pmap_check(void) +{ + u8 *p; + + for (p = (u8*)kern.pmap_protect; + p < ((u8*)kern.pmap_protect + 0x500); p++) { + #ifdef PS4_6_72 + if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 + p[5] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + /*#ifdef PS4_5_05 + if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { + p[1] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + }*/ + #else + if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { + p[2] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + #endif + } + kern.printf("pmap_protect patch failed!\n"); + return 0; +} +#endif + +int kernel_init(void *_early_printf) +{ + int rv = -1; + + if (_early_printf) + early_printf = _early_printf; + + eprintf("kernel_init()\n"); + +#ifdef KASLR + // use `early_printf` to calculate kernel base + if (early_printf == NULL) + return 0; + + kern.kern_base = (u64)(early_printf - kern_off_printf); + if ((kern.kern_base & PAGE_MASK) != 0) { + eprintf("Kernel base is not aligned\n"); + return 0; + } else { + eprintf("Kernel base = %llx\n", kern.kern_base); + } + + u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); + u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); + +#else + kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 + + u64 DMPML4I = 0x1fc; + u64 DMPDPI = 0; +#endif + + kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); + eprintf("Direct map base = %llx\n", kern.dmap_base); + + // We may not be mapped writable yet, so to be able to write to globals + // we need WP disabled. + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + +#ifndef NO_SYMTAB + Elf64_Ehdr *ehdr = find_kern_ehdr(); + if (!ehdr) { + eprintf("Could not find kernel ELF header\n"); + goto err; + } + eprintf("ELF header at %p\n", ehdr); + + Elf64_Dyn *dyn = elf_get_dyn(ehdr); + if (!dyn) { + eprintf("Could not find kernel dynamic header\n"); + goto err; + } + eprintf("ELF dynamic section at %p\n", dyn); + + if (!elf_parse_dyn(dyn)) { + eprintf("Failed to parse ELF dynamic section\n"); + goto err; + } +#endif + + if (!resolve_symbols()) { + eprintf("Failed to resolve all symbols\n"); + goto err; + } + + // Pin ourselves as soon as possible. This is expected to be released by the caller. + kern.sched_pin(); + +#ifndef DO_NOT_REMAP_RWX + if (!patch_pmap_check()) + goto err; +#endif + +#ifndef DO_NOT_REMAP_RWX + // kernel_remap may need interrupts, but may not write to globals! + enable_interrupts(); + kernel_remap(_start, _end, 7); + disable_interrupts(); +#endif + + // Writing to globals is now safe. + + kern.printf("Testing global variable access (write protection)...\n"); + _global_test = 1; + kern.printf("OK.\n"); + + kern.printf("Kernel interface initialized\n"); + rv = 0; + +err: + write_protect_restore(wp); + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1250-pro-baikal/kernel.h b/linux/ps4-kexec-1250-pro-baikal/kernel.h new file mode 100644 index 0000000..1833f5b --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/kernel.h @@ -0,0 +1,126 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KERNEL_H +#define KERNEL_H + +#include "types.h" +#include "reboot.h" + +#define PAGE_SIZE 0x4000 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define PML4SHIFT 39 +#define PDPSHIFT 30 +#define PDRSHIFT 21 +#define PAGE_SHIFT 12 + +#define KVADDR(l4, l3, l2, l1) ( \ + ((unsigned long)-1 << 47) | \ + ((unsigned long)(l4) << PML4SHIFT) | \ + ((unsigned long)(l3) << PDPSHIFT) | \ + ((unsigned long)(l2) << PDRSHIFT) | \ + ((unsigned long)(l1) << PAGE_SHIFT)) + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) +#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX + +typedef u64 vm_paddr_t; +typedef u64 vm_offset_t; +typedef u64 vm_size_t; +typedef void * vm_map_t; +typedef char vm_memattr_t; +typedef void * pmap_t; + +typedef void (*smp_rendezvous_callback_t)(void *); + +struct sysent_t { + int sy_narg; + void *sy_call; + u16 sy_auevent; + void *sy_systrace_args_func; + int sy_entry; + int sy_return; + int sy_flags; + int sy_thrcnt; +}; + +struct ksym_t { + // two parameters related to kaslr (they are not symbols) + uintptr_t kern_base; + uintptr_t dmap_base; + + int (*printf)(const char *fmt, ...); + int (*snprintf)(const char *fmt, ...); + int (*copyin)(const void *uaddr, void *kaddr, size_t len); + int (*copyout)(const void *kaddr, void *uaddr, size_t len); + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); + + void **kernel_map; + void *kernel_pmap_store; + vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, + vm_paddr_t low, vm_paddr_t high, + unsigned long alignment, + unsigned long boundary, + vm_memattr_t memattr); + void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); + vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); + void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); + + struct sysent_t *sysent; + + void (*sched_pin)(void); + void (*sched_unpin)(void); + void (*smp_rendezvous)(smp_rendezvous_callback_t, + smp_rendezvous_callback_t, + smp_rendezvous_callback_t, void *); + // yes...it is misspelled :) + void (*smp_no_rendevous_barrier)(void *); + void *icc_query_nowait; + void *Starsha_UcodeInfo; + int (*gpu_devid_is_9924)(); + void *(*gc_get_fw_info)(); + void *eap_hdd_key; + void *edid; + void (*wlanbt)(unsigned int state); + int (*kern_reboot)(int magic); + void(*set_gpu_freq)(unsigned int num, unsigned int freq); + void(*set_pstate)(unsigned int val); + void(*update_vddnp)(unsigned int val); + void(*set_cu_power_gate)(unsigned int val); + void *pstate_before_shutdown; +}; + +extern struct ksym_t kern; + +static inline int curcpu(void) +{ + int cpuid; + // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw + asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); + return cpuid; +} + +// Assign a working printf function to this to debug the symbol resolver +extern int (*early_printf)(const char *fmt, ...); + +void *kernel_resolve(const char *name); + +void *kernel_alloc_contig(size_t size); +void kernel_free_contig(void *addr, size_t size); + +void kernel_remap(void *start, void *end, int perm); + +void kernel_syscall_install(int num, void *call, int narg); +int kernel_hook_install(void *target, void *hook); + +int kernel_init(void *early_printf); + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/kexec.c b/linux/ps4-kexec-1250-pro-baikal/kexec.c new file mode 100644 index 0000000..fa55dfe --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/kexec.c @@ -0,0 +1,229 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "linux_boot.h" +#include "x86.h" +#include "kexec.h" +#include "firmware.h" +#include "string.h" +#include "acpi.h" + +static int k_copyin(const void *uaddr, void *kaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(kaddr, uaddr, len); + return 0; +} + +static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + const char *ustr = (const char*)uaddr; + char *kstr = (char*)kaddr; + size_t ret; + if (!uaddr || !kaddr) + return EFAULT; + ret = strlcpy(kstr, ustr, len); + if (ret >= len) { + if (done) + *done = len; + return ENAMETOOLONG; + } else { + if (done) + *done = ret + 1; + } + return 0; +} + +static int k_copyout(const void *kaddr, void *uaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(uaddr, kaddr, len); + return 0; +} + +int sys_kexec(void *td, struct sys_kexec_args *uap) +{ + int err = 0; + size_t initramfs_size = uap->initramfs_size; + void *image = NULL; + void *initramfs = NULL; + size_t firmware_size = 0; + struct boot_params *bp = NULL; + size_t cmd_line_maxlen = 0; + char *cmd_line = NULL; + + int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; + int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; + + kern.printf("sys_kexec invoked\n"); + kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, + uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); + + // Look up our shutdown hook point + void *icc_query_nowait = kern.icc_query_nowait; + if (!icc_query_nowait) { + err = ENOENT; + goto cleanup; + } + + // Set gpu frequencies and pstate + // FAT&SLIM / PRO + kern.set_pstate(3); + + kern.set_gpu_freq(0, 800); //800 //800 + kern.set_gpu_freq(1, 853); //673 //853 + kern.set_gpu_freq(2, 711); //610 //711 + kern.set_gpu_freq(3, 800); //800 //800 + kern.set_gpu_freq(4, 911); //800 //911 + kern.set_gpu_freq(5, 800); //711 //800 + kern.set_gpu_freq(6, 984); //711 //984 + kern.set_gpu_freq(7, 673); //673 //673 + + kern.update_vddnp(0x12); + kern.set_cu_power_gate(0x24); + + // Copy in kernel image + image = kernel_alloc_contig(uap->image_size); + if (!image) { + kern.printf("Failed to allocate image\n"); + err = ENOMEM; + goto cleanup; + } + err = copyin(uap->image, image, uap->image_size); + if (err) { + kern.printf("Failed to copy in image\n"); + goto cleanup; + } + + // Copy in initramfs + initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); + if (!initramfs) { + kern.printf("Failed to allocate initramfs\n"); + err = ENOMEM; + goto cleanup; + } + + err = firmware_extract(((u8*)initramfs)); + if (err < 0) { + kern.printf("Failed to extract GPU firmware - continuing anyway\n"); + } else { + firmware_size = err; + } + + if (initramfs_size) { + err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); + if (err) { + kern.printf("Failed to copy in initramfs\n"); + goto cleanup; + } + } + initramfs_size += firmware_size; + + // Copy in cmdline + cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; + cmd_line = kernel_alloc_contig(cmd_line_maxlen); + if (!cmd_line) { + kern.printf("Failed to allocate cmdline\n"); + err = ENOMEM; + goto cleanup; + } + err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); + if (err) { + kern.printf("Failed to copy in cmdline\n"); + goto cleanup; + } + cmd_line[cmd_line_maxlen - 1] = 0; + + kern.printf("\nkexec parameters:\n"); + kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); + kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", + initramfs_size, uap->initramfs_size); + kern.printf(" Kernel command line: %s\n", cmd_line); + kern.printf(" Kernel image buffer: %p\n", image); + kern.printf(" Initramfs buffer: %p\n", initramfs); + + // Allocate our boot params + bp = kernel_alloc_contig(sizeof(*bp)); + if (!bp) { + kern.printf("Failed to allocate bp\n"); + err = ENOMEM; + goto cleanup; + } + + // Initialize bp + // TODO should probably do this from cpu_quiesce_gate, then bp doesn't + // need to be allocated here, just placed directly into low mem + set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); + + prepare_boot_params(bp, image); + + // Hook the final ICC shutdown function + if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { + kern.printf("Failed to install shutdown hook\n"); + err = EINVAL; + goto cleanup; + } + + kern.printf("******************************************************\n"); + kern.printf("kexec successfully armed. Please shut down the system.\n"); + kern.printf("******************************************************\n\n"); + +/* + kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); + if (kern.kern_reboot(RB_POWEROFF) == -1) + kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); +*/ + return 0; + +cleanup: + kernel_free_contig(cmd_line, cmd_line_maxlen); + kernel_free_contig(bp, sizeof(*bp)); + kernel_free_contig(image, uap->image_size); + kernel_free_contig(initramfs, uap->initramfs_size); + return err; + + copyout(NULL, NULL, 0); +} + +int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) +{ + int rv = 0; + + // potentially needed to write early_printf + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + + if (kernel_init(_early_printf) < 0) { + rv = -1; + goto cleanup; + } + + kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); + kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); + kern.printf("kexec_init() successful\n\n"); + + if (sys_kexec_ptr) + *sys_kexec_ptr = sys_kexec; + +cleanup: + write_protect_restore(wp); + if (kern.sched_unpin && wp & CR0_WP) { + // If we're returning to a state with WP enabled, assume the caller + // wants the thread unpinned. Else the caller is expected to + // call kern.sched_unpin() manually. + kern.sched_unpin(); + } + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1250-pro-baikal/kexec.h b/linux/ps4-kexec-1250-pro-baikal/kexec.h new file mode 100644 index 0000000..6c31637 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/kexec.h @@ -0,0 +1,38 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KEXEC_H +#define KEXEC_H + +#include "types.h" + +#define SYS_KEXEC 153 +#define SYS_KEXEC_NARGS 6 + +struct sys_kexec_args { + void *image; + size_t image_size; + void *initramfs; + size_t initramfs_size; + char *cmd_line; + int vram_gb; +}; + +typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); + +// Note: td is unused, you can pass NULL if you call this directly. +int sys_kexec(void *td, struct sys_kexec_args *uap); + +int kernel_init(void *early_printf); + +int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) + __attribute__ ((section (".init"))); + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/kexec.ld b/linux/ps4-kexec-1250-pro-baikal/kexec.ld new file mode 100644 index 0000000..1bcf26c --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/kexec.ld @@ -0,0 +1,25 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +ENTRY(kexec_init) + +SECTIONS { + /* We don't do GOT relocation and rely on nothing ending up using the GOT + * (-fno-common helps here) */ + /DISCARD/ : { *(.comment) *(.got) } + _start = .; + .init : { *(.init) *(.init.*) } + .text : { *(.text) *(.text.*) } + .data : { *(.data) *(.data.*) } + .rodata : { *(.rodata) *(.rodata.*) } + .bss : { *(.bss) *(.bss.*) *(COMMON)} + .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ + _end = .; +} diff --git a/linux/ps4-kexec-1250-pro-baikal/linux_boot.c b/linux/ps4-kexec-1250-pro-baikal/linux_boot.c new file mode 100644 index 0000000..226dd4f --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/linux_boot.c @@ -0,0 +1,453 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "linux_boot.h" +#include "types.h" +#include "string.h" +#include "x86.h" +#include "kernel.h" +#include "uart.h" +#include "acpi.h" + +void uart_write_byte(u8 b); + +static u64 vram_base = 0x100000000; +// Current code assumes it's a power of two. +static u64 vram_size = 1024 * 1024 * 1024; +static int vram_gb = 2; + +#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) + +#define MSR_GS_BASE 0xc0000101 /* 64bit GS base */ + +struct desc_struct { + u16 limit0; + u16 base0; + u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; + u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, + uintptr_t new_cr3, uintptr_t gdt_ptr); +extern uint8_t *jmp_to_linux; +extern size_t jmp_to_linux_size; + +// FreeBSD DMAP addresses +struct linux_boot_info { + void *linux_image; + void *initramfs; + size_t initramfs_size; + struct boot_params *bp; + char *cmd_line; +}; +static struct linux_boot_info nix_info; + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v) +{ + nix_info.linux_image = linux_image; + nix_info.bp = bp; + nix_info.initramfs = initramfs; + nix_info.initramfs_size = initramfs_size; + nix_info.cmd_line = cmd_line; + vram_gb = v; +} + +static volatile int halted_cpus = 0; + +static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, + u32 type) +{ + uint8_t idx = bp->e820_entries; + bp->e820_map[idx].addr = addr; + bp->e820_map[idx].size = size; + bp->e820_map[idx].type = type; + bp->e820_entries++; +} + +void prepare_boot_params(struct boot_params *bp, u8 *linux_image) +{ + memset(bp, 0, sizeof(struct boot_params)); + struct boot_params *bp_src = (struct boot_params *)linux_image; + memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + + ((u8 *)&bp_src->hdr.jump)[1]); + + // These values are from fw 1.01 + bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); + bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + // This used to be VRAM, but we reclaim it as RAM + bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); + // Instead, carve out VRAM from the beginning of high memory + bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, + SMAP_TYPE_MEMORY); +} + +#define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_FB_OFFSET 0x2068 +#define HDP_NONSURFACE_BASE 0x2c04 +#define CONFIG_MEMSIZE 0x5428 + +static void configure_vram(void) +{ + u64 mmio_base = 0xe4800000; + u64 fb_base = 0x0f00000000; + u64 fb_top = fb_base + vram_gb * vram_size - 1; + + WR32(mmio_base + MC_VM_FB_LOCATION, 0); + WR32(mmio_base + HDP_NONSURFACE_BASE, 0); + + WR32(mmio_base + MC_VM_FB_LOCATION, + ((fb_top >> 24) << 16) | (fb_base >> 24)); + WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); + WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); + WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); +} + +#define IA32_MTRR_DEF_TYPE 0x2ff +#define MTRR_BASE(i) (0x200 + 2*i) +#define MTRR_MASK(i) (0x201 + 2*i) + +static void setup_mtrr(void) +{ + disable_interrupts(); + u64 cr0 = cr0_read(); + cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); + wbinvd(); + cr3_write(cr3_read()); // TLB flush + + wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled + + // Low memory (0GB-2GB) = WB + wrmsr(MTRR_BASE(0), 0x0000000006); + wrmsr(MTRR_MASK(0), 0xff80000800); + // High memory (4GB-8GB) = WB + wrmsr(MTRR_BASE(1), 0x0100000006); + wrmsr(MTRR_MASK(1), 0xff00000800); + // High memory (8GB-10GB) = WB + wrmsr(MTRR_BASE(2), 0x0200000006); + wrmsr(MTRR_MASK(2), 0xff80000800); + // VRAM (4GB-4GB+vram_size) = UC + wrmsr(MTRR_BASE(3), 0x0100000000); + wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); + + wbinvd(); + cr3_write(cr3_read()); // TLB flush + wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable + cr0_write(cr0); + enable_interrupts(); +} + +static void cleanup_interrupts(void) +{ + int i; + disable_interrupts(); + + // Reset APIC stuff (per-CPU) + *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; + for (i = 0x320; i < 0x380; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + for (i = 0x480; i < 0x500; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; + for (i = 0x500; i < 0x540; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; + + // Fix the LVT offset for thresholding + wrmsr(0x413, (1L<<24) | (1L<<52)); + wrmsr(0xc0000408, (1L<<24) | (1L<<52)); +} + +#define DEFAULT_STACK 0 + +#define DPL0 0x0 +#define DPL3 0x3 + +#define BPCIE_BAR2 0xc8800000 +#define BPCIE_HPET_BASE 0x109000 +#define BPCIE_HPET_SIZE 0x400 + +static void cpu_quiesce_gate(void *arg) +{ + + + // Ensure we can write anywhere + cr0_write(cr0_read() & ~CR0_WP); + + // Interrupt stuff local to each CPU + cleanup_interrupts(); + + // We want to set up MTRRs on all CPUs + setup_mtrr(); + + if (curcpu() != 0) { + // We're not on BSP. Try to halt. + __sync_fetch_and_add(&halted_cpus, 1); + cpu_stop(); + } + + uart_write_str("kexec: Waiting for secondary CPUs...\n"); + + // wait for all cpus to halt + while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); + + uart_write_str("kexec: Secondary CPUs quiesced\n"); + + //* Put ident mappings in current page tables + // Should not be needed, but maybe helps for debugging? + cr4_pge_disable(); + u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); + u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + // Clear (really) low mem. + // Linux reads from here to try and access EBDA... + // get_bios_ebda reads u16 from 0x40e + // reserve_ebda_region reads u16 from 0x413 + // Writing zeros causes linux to default to marking + // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. + // It doesn't match the ps4 e820 map, but that seems OK. + memset((void *)0, 0, 0x1000); + + // Create a new page table hierarchy out of the way of linux + // Accessed via freebsd direct map + pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt + // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| + memset(pml4_base, 0, 512 * sizeof(u64) * 2); + pdp_base = pml4_base + 512; + u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; + pml4_base[0] = pdpe; + // Maintain the freebsd direct map + pml4_base[DM_PML4_BASE] = pdpe; + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + uart_write_str("kexec: Setting up GDT...\n"); + + desc_ptr gdt_ptr; + struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); + gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; + gdt_ptr.address = DM_TO_ID(desc); + + // clear + memset(desc, 0, gdt_ptr.limit + 1); + // Most things are ignored in 64bit mode, and we will never be in + // 32bit/compat modes, so just setup another pure-64bit environment... + // Linux inits it's own GDT in secondary_startup_64 + // 0x10 + desc[2].limit0 = 0xffff; + desc[2].base0 = 0x0000; + desc[2].base1 = 0x0000; + desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; + desc[2].s = 1; + desc[2].dpl = 0; + desc[2].p = 1; + desc[2].limit = 0xf; + desc[2].avl = 0; + desc[2].l = 1; + desc[2].d = 0; + desc[2].g = 0; + desc[2].base2 = 0x00; + // 0x18 + desc[3].limit0 = 0xffff; + desc[3].base0 = 0x0000; + desc[3].base1 = 0x0000; + desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; + desc[3].s = 1; + desc[3].dpl = 0; + desc[3].p = 1; + desc[3].limit = 0xf; + desc[3].avl = 0; + desc[3].l = 0; + desc[3].d = 0; + desc[3].g = 0; + desc[3].base2 = 0x00; + // Task segment value + // 0x20 + desc[4].limit0 = 0x0000; + desc[4].base0 = 0x0000; + desc[4].base1 = 0x0000; + desc[4].type = SEG_TYPE_TSS; + desc[4].s = 1; + desc[4].dpl = 0; + desc[4].p = 1; + desc[4].limit = 0x0; + desc[4].avl = 0; + desc[4].l = 0; + desc[4].d = 0; + desc[4].g = 0; + desc[4].base2 = 0x00; + + uart_write_str("kexec: Relocating stub...\n"); + + // Relocate the stub and jump to it + // TODO should thunk_copy be DMAP here? + void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); + memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); + // XXX The +0x200 is for the iret stack in linux_thunk.S + uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; + + uart_write_str("kexec: Setting up boot params...\n"); + + // XXX we write into this bootargs and pass it to the kernel, but in + // jmp_to_linux we use the bootargs from the image as input. So they + // MUST MATCH! + struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; + *bp_lo = *nix_info.bp; + lowmem_pos += sizeof(struct boot_params); + + struct setup_header *shdr = &bp_lo->hdr; + shdr->cmd_line_ptr = lowmem_pos; + shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; + shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; + bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; + bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; + shdr->hardware_subarch = X86_SUBARCH_PS4; + // This needs to be nonzero for the initramfs to work + shdr->type_of_loader = 0xd0; // kexec + + strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, + nix_info.bp->hdr.cmdline_size); + lowmem_pos += strlen(nix_info.cmd_line) + 1; + + uart_write_str("kexec: Cleaning up hardware...\n"); + + // Disable IOMMU + *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; + + // Disable all MSIs on Baikal (bus=0, slot=20) + disableMSI(0xf80a00e0); //func = 0 Baikal ACPI + disableMSI(0xf80a10e0); //func = 1 Baikal Ethernet Controller + disableMSI(0xf80a20e0); //func = 2 Baikal SATA AHCI Controller + disableMSI(0xf80a30e0); //func = 3 Baikal SD/MMC Host Controller + disableMSI(0xf80a40e0); //func = 4 Baikal PCI Express Glue and Miscellaneous Devices + disableMSI(0xf80a50e0); //func = 5 Baikal DMA Controller + disableMSI(0xf80a60e0); //func = 6 Baikal Baikal Memory (DDR3/SPM) + disableMSI(0xf80a70e0); //func = 7 Baikal Baikal USB 3.0 xHCI Host Controller + + // Stop HPET timers + //*(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + 0x10) &= ~(1UL << 0); //General Configuration Register + /* + u64 NUM_TIM_CAP = *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE) & 0x1F00; + for (u64 N = 0; N <= NUM_TIM_CAP; N++) { + *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + (0x20*N) + 0x100) &= ~(1UL << 2); //Timer N Configuration and Capabilities Register + } + */ + uart_write_str("kexec: Reconfiguring VRAM...\n"); + + configure_vram(); + + uart_write_str("kexec: Resetting GPU...\n"); + + // Softreset GPU + *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks + *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC + *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 +// *(volatile u64 *)PA_TO_DM(0xe480d248) = 1; // Halt SDMA1 eeply + *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 + *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC + + *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply + +// *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x10003; // Softreset GFX/CP/RLC + *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x30005; // Softreset GFX/CP/RLC eeply + +// udelay(150); +// *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x10003; + *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x30005; //eeply +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) |= 0x00100140; // Softreset SDMA/GRBM +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~0x00100140; +// udelay(150); + + // Enable audio output + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; + *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; + +// // Set pin caps of pin 2 to vendor defined, to hide it +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// // Set pin caps of pin 3 to !HDMI +// *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; +// *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; + // Set pin configuration default + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; + + uart_write_str("kexec: About to relocate and jump to kernel\n"); + + ((jmp_to_linux_t)thunk_copy)( + DM_TO_ID(nix_info.linux_image), + DM_TO_ID(bp_lo), + DM_TO_ID(pml4_base), + (uintptr_t)&gdt_ptr + ); + // should never reach here + uart_write_str("kexec: unreachable (?)\n"); +} + +// Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) +int hook_icc_query_nowait(u8 *icc_msg) +{ + kern.printf("hook_icc_query_nowait called\n"); + + // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot + //In alternative we can re-enable it here, but sometimes that give problems.. + kern.wlanbt(0x2); + + fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); + + kern.printf("ACPI tables fixed\n"); + + // Transition to BSP and halt other cpus + // smp_no_rendevous_barrier is just nullsub, but it is treated specially by + // smp_rendezvous. This is the easiest way to do this, since we can't assume + // we're already running on BSP. Since smp_rendezvous normally waits on all + // cpus to finish the callbacks, we just never return... + kern.smp_rendezvous(kern.smp_no_rendevous_barrier, + cpu_quiesce_gate, + kern.smp_no_rendevous_barrier, NULL); + + // should never reach here + kern.printf("hook_icc_query_nowait: unreachable (?)\n"); + return 0; +} diff --git a/linux/ps4-kexec-1250-pro-baikal/linux_boot.h b/linux/ps4-kexec-1250-pro-baikal/linux_boot.h new file mode 100644 index 0000000..2232b35 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/linux_boot.h @@ -0,0 +1,88 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef LINUX_BOOT_H +#define LINUX_BOOT_H + +#include "types.h" + +#define SMAP_TYPE_MEMORY 1 +#define SMAP_TYPE_RESERVED 2 +#define SMAP_TYPE_ACPI_RECLAIM 3 +#define SMAP_TYPE_ACPI_NVS 4 +#define SMAP_TYPE_UNUSABLE 5 +#define SMAP_TYPE_PMEM 7 + +#define X86_SUBARCH_PS4 5 + +struct e820entry { + u64 addr; /* start of memory segment */ + u64 size; /* size of memory segment */ + u32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct setup_header { + u8 setup_sects; + u16 root_flags; + u32 syssize; + u16 ram_size; + u16 vid_mode; + u16 root_dev; + u16 boot_flag; + u16 jump; + u32 header; + u16 version; + u32 realmode_swtch; + u16 start_sys; + u16 kernel_version; + u8 type_of_loader; + u8 loadflags; + u16 setup_move_size; + u32 code32_start; + u32 ramdisk_image; + u32 ramdisk_size; + u32 bootsect_kludge; + u16 heap_end_ptr; + u8 ext_loader_ver; + u8 ext_loader_type; + u32 cmd_line_ptr; + u32 initrd_addr_max; + u32 kernel_alignment; + u8 relocatable_kernel; + u8 min_alignment; + u16 xloadflags; + u32 cmdline_size; + u32 hardware_subarch; + u64 hardware_subarch_data; + u32 payload_offset; + u32 payload_length; + u64 setup_data; + u64 pref_address; + u32 init_size; + u32 handover_offset; +} __attribute__((packed)); + +#define E820MAX 128 /* number of entries in E820MAP */ + +OSTRUCT(boot_params, 0x1000) +OFIELD(0x0c0, u32 ext_ramdisk_image); +OFIELD(0x0c4, u32 ext_ramdisk_size); +OFIELD(0x0c8, u32 ext_cmd_line_ptr); +OFIELD(0x1e8, u8 e820_entries); +OFIELD(0x1f1, struct setup_header hdr); +OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); +OSTRUCT_END + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v); +void prepare_boot_params(struct boot_params *bp, u8 *linux_image); +int hook_icc_query_nowait(u8 *icc_msg); + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/linux_thunk.S b/linux/ps4-kexec-1250-pro-baikal/linux_thunk.S new file mode 100644 index 0000000..551964e --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/linux_thunk.S @@ -0,0 +1,90 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +.intel_syntax noprefix + +.equ setup_sects, 0x1f1 +.equ shdr_syssize, 0x1f4 +.equ pref_address, 0x258 + +.text + +#void jmp_to_linux( +# uintptr_t image_base, rdi +# uintptr_t bootargs, rsi +# uintptr_t new_cr3, rdx +# uintptr_t gdt_ptr rcx +#); +.globl jmp_to_linux +jmp_to_linux: + # switch to new gdt + data segments + cli + lgdt [rcx] + #xor eax, eax + mov eax, 0x18 + mov ds, eax + mov ss, eax + mov es, eax + mov fs, eax + mov gs, eax + + # switch to our own page tables (in low mem) + mov cr3, rdx + + # now we're on our own page tables, so we can obliterate the rest of memory + # TODO make sure we don't inadvertently overwrite (important) smap regions + # I think on ps4 we'll actually want to load to 0x700000 + # since we have tons of free room there. + # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We + # should choose a higher address as pref_address. + + # save args + mov r12, rdi + mov r13, rsi + + # memmove(pref_address, , (syssize * 0x10) / 8) + #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go + #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) + mov rdi, 0x6000000 # should be far from bzImage and initramfs + mov r14, rdi # r14 = pref_address + xor edx, edx + mov dl, [r12 + setup_sects] + inc rdx + shl rdx, 9 # rdx = offsetof(image_base, startup_32) + lea rsi, [r12 + rdx] # src = image_base + startup_32 + mov ecx, [r12 + shdr_syssize] + shl rcx, 4 + add rdi, rcx + add rsi, rcx + sub rdi, 8 + sub rsi, 8 + shr rcx, 3 + std + rep movsq + cld + + # make a tiny stack - we just need it for the lretq. + # what we jump to will not use this stack + lea rsp, [rip + jmp_to_linux_end + 0x200] + and rsp, -0x10 + #push 0 # retaddr + push 0x10 # cs = GDT[2] + add r14, 0x200 # pref_address + startup_64 + push r14 # rip + mov rsi, r13 # bootargs + lretq +jmp_to_linux_end: + +.data + +.globl jmp_to_linux_size +jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux + +.att_syntax prefix \ No newline at end of file diff --git a/linux/ps4-kexec-1250-pro-baikal/reboot.h b/linux/ps4-kexec-1250-pro-baikal/reboot.h new file mode 100644 index 0000000..20b91f8 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/reboot.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)reboot.h 8.3 (Berkeley) 12/13/94 + * $FreeBSD$ + */ + +#ifndef _SYS_REBOOT_H_ +#define _SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. These are passed to + * the boot program and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ +#define RB_SINGLE 0x002 /* reboot to single user only */ +#define RB_NOSYNC 0x004 /* dont sync before reboot */ +#define RB_HALT 0x008 /* don't reboot, just halt */ +#define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ +#define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ +#define RB_KDB 0x040 /* give control to kernel debugger */ +#define RB_RDONLY 0x080 /* mount root fs read-only */ +#define RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define RB_MINIROOT 0x200 /* Unused placeholder */ +#define RB_VERBOSE 0x800 /* print all potentially useful info */ +#define RB_SERIAL 0x1000 /* use serial port as console */ +#define RB_CDROM 0x2000 /* use cdrom as root */ +#define RB_POWEROFF 0x4000 /* turn the power off if possible */ +#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ +#define RB_MUTE 0x10000 /* start up with the console muted */ +#define RB_SELFTEST 0x20000 /* unused placeholder */ +#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ +#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ +#define RB_PAUSE 0x100000 /* pause after each output line during probe */ +#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ +#define RB_PROBE 0x10000000 /* Probe multiple consoles */ +#define RB_MULTIPLE 0x20000000 /* use multiple consoles */ + +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/string.h b/linux/ps4-kexec-1250-pro-baikal/string.h new file mode 100644 index 0000000..70aa668 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/string.h @@ -0,0 +1,96 @@ +/* + * string.h -- standard C string-manipulation functions. + * + * Copyright (C) 2008 Segher Boessenkool + * Copyright (C) 2009 Haxx Enterprises + * Copyright (C) 2010-2016 Hector Martin "marcan" + * + * Portions taken from the Public Domain C Library (PDCLib). + * http://pdclib.rootdirectory.de/ + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +static inline int strcmp(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s1[i] == s2[i]; i++) + ; + + return s1[i] - s2[i]; +} + +static inline void *memset(void *b, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)b)[i] = c; + + return b; +} + +static inline void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} + +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) + return p1[i] - p2[i]; + + return 0; +} + +static inline size_t strlen(const char *s) +{ + size_t len; + + for (len = 0; s[len]; len++) + ; + + return len; +} + +static inline size_t strnlen(const char *s, size_t count) +{ + size_t len; + + for (len = 0; s[len] && len < count; len++) + ; + + return len; +} + +static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) +{ + size_t len, needed; + + len = needed = strnlen(src, maxlen - 1) + 1; + if (len >= maxlen) + len = maxlen - 1; + + memcpy(dest, src, len); + dest[len] = 0; + + return needed - 1; +} + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/types.h b/linux/ps4-kexec-1250-pro-baikal/types.h new file mode 100644 index 0000000..28d6619 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/types.h @@ -0,0 +1,51 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef TYPES_H +#define TYPES_H + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#ifndef TESTING +typedef u8 uint8_t; +typedef u64 size_t; +typedef s64 ssize_t; +typedef u64 uintptr_t; +typedef s64 off_t; +#endif + +#define NULL ((void *)0) + +#define CAT_(x, y) x ## y +#define CAT(x, y) CAT_(x, y) + +#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] +#define OSTRUCT(name, size) struct name { union { OPAD(size); +#define OSTRUCT_END };}; +#define OFIELD(off, field) struct { OPAD(off); field; } + +#define ASSERT_STRSIZE(struc, size) \ + _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#define ENOENT 2 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define ENAMETOOLONG 63 + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/uart.c b/linux/ps4-kexec-1250-pro-baikal/uart.c new file mode 100644 index 0000000..a401434 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/uart.c @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "uart.h" +#include "kernel.h" +#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) + +#define AEOLIA_UART_BASE 0xD0340000 +#define BAIKAL_UART_BASE 0xC890E000 + +#define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, BAIKAL_UART_BASE + (intf << 12) + (reg << 2)) + +#define UART_REG_DATA 0 +#define UART_REG_IER 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +# define LSR_TXRDY 0x20 +# define LSR_TEMT 0x40 +#define UART_REG_MSR 6 +#define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) +#define UART0_IER UART_REG(32, 0, UART_REG_IER) +#define UART0_IIR UART_REG(32, 0, UART_REG_IIR) +#define UART0_LCR UART_REG(32, 0, UART_REG_LCR) +#define UART0_MCR UART_REG(32, 0, UART_REG_MCR) +#define UART0_LSR UART_REG(32, 0, UART_REG_LSR) +#define UART0_MSR UART_REG(32, 0, UART_REG_MSR) + +void uart_write_byte(u8 b) +{ + int limit; + u64 barrier; + limit = 250000; + while (!(*UART0_LSR & LSR_TXRDY) && --limit) + ; + *UART0_DATA = b; + __sync_fetch_and_add(&barrier, 0); + limit = 250000; + while (!(*UART0_LSR & LSR_TEMT) && --limit) + ; +} + +void uart_write_char(char c) +{ + if (c == '\n') + uart_write_byte('\r'); + + uart_write_byte(c); +} + +void uart_write_str(const char *s) +{ + while (*s) { + uart_write_char(*s++); + } +} diff --git a/linux/ps4-kexec-1250-pro-baikal/uart.h b/linux/ps4-kexec-1250-pro-baikal/uart.h new file mode 100644 index 0000000..b803e29 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/uart.h @@ -0,0 +1,20 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef UART_H +#define UART_H + +#include "types.h" + +void uart_write_byte(u8 b); +void uart_write_char(char c); +void uart_write_str(const char *s); + +#endif diff --git a/linux/ps4-kexec-1250-pro-baikal/x86.h b/linux/ps4-kexec-1250-pro-baikal/x86.h new file mode 100644 index 0000000..2f6d8b5 --- /dev/null +++ b/linux/ps4-kexec-1250-pro-baikal/x86.h @@ -0,0 +1,195 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef X86_H +#define X86_H + +#define FLAGS_IF (1 << 9) + +#define CR0_WP (1 << 16) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) + +#define PG_V (1 << 0) +#define PG_RW (1 << 1) +#define PG_U (1 << 2) +#define PG_PS (1 << 7) + +#define SEG_TYPE_DATA (0 << 3) +#define SEG_TYPE_READ_WRITE (1 << 1) +#define SEG_TYPE_CODE (1 << 3) +#define SEG_TYPE_EXEC_READ (1 << 1) +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) + +static inline u64 cr0_read(void) +{ + u64 reg; + asm volatile("mov %0, cr0;" : "=r" (reg)); + return reg; +} + +static inline void cr0_write(u64 val) +{ + asm volatile("mov cr0, %0;" :: "r" (val)); +} + +static inline u64 write_protect_disable() +{ + u64 cr0 = cr0_read(); + cr0_write(cr0 & ~CR0_WP); + return cr0; +} + +static inline void write_protect_restore(u64 cr0) +{ + // Use only WP bit of input + cr0_write(cr0_read() | (cr0 & CR0_WP)); +} + +static inline u64 cr3_read(void) +{ + u64 reg; + asm volatile("mov %0, cr3;" : "=r" (reg)); + return reg; +} + +static inline void cr3_write(u64 val) +{ + asm volatile("mov cr3, %0;" :: "r" (val)); +} + +typedef struct { + u16 limit; + u64 address; +} __attribute__((packed)) desc_ptr; + +static inline desc_ptr gdt_read(void) +{ + desc_ptr gdtr; + asm volatile("sgdt %0;" : "=m" (gdtr)); + return gdtr; +} + +static inline void gdt_write(desc_ptr* val) +{ + asm volatile("lgdt %0;" :: "m" (*val)); +} + +//IDT +typedef struct { + u16 limit; + u64 address; +} __attribute__((packed)) idt_ptr; + +static inline idt_ptr idt_read(void) +{ + idt_ptr idtr; + asm volatile("sidt %0;" : "=m" (idtr)); + return idtr; +} + +static inline void idt_write(idt_ptr* val) +{ + asm volatile("lidt %0;" :: "m" (*val)); +} + +static inline void cr4_pge_disable(void) +{ + u64 cr4_temp; + asm volatile( + "mov %0, cr4;" + "and %0, ~0x80;" + "mov cr4, %0;" + : "=r" (cr4_temp) + ); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +static inline void cpu_stop(void) +{ + for (;;) + asm volatile("cli; hlt;" : : : "memory"); +} + +static inline void outl(int port, unsigned int data) +{ + asm volatile("out %w1, %0" : : "a" (data), "d" (port)); +} + +static inline void wrmsr(u32 msr_id, u64 msr_value) +{ + asm volatile( + "wrmsr" + : + : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) + ); +} + +static inline u64 rdmsr(u64 msr_id) +{ + u32 low, high; + asm volatile ( + "rdmsr" + : "=a"(low), "=d"(high) + : "c"(msr_id) + ); + return ((u64)high << 32) | low; +} + +static inline u64 rdtsc (void) +{ + unsigned int tickl, tickh; + asm volatile( + "rdtsc" + :"=a"(tickl),"=d"(tickh) + ); + return ((u64)tickh << 32) | tickl; +} + +static inline void udelay(unsigned int usec) { + u64 later = rdtsc() + usec * 1594ULL; + while (((s64)(later - rdtsc())) > 0); +} + +static inline void disable_interrupts(void) +{ + asm volatile("cli"); +} + +static inline void enable_interrupts(void) +{ + asm volatile("sti"); +} + +static inline u64 read_flags(void) +{ + u64 flags; + asm volatile("pushf; pop %0;" : "=r" (flags)); + return flags; +} + +static inline u64 intr_disable(void) +{ + u64 flags = read_flags(); + disable_interrupts(); + return flags; +} + +static inline void intr_restore(u64 flags) +{ + // TODO should only IF be or'd in? + asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); +} + +#endif diff --git a/linux/ps4-kexec-1250-pro/LICENSE b/linux/ps4-kexec-1250-pro/LICENSE new file mode 100644 index 0000000..6c1cbba --- /dev/null +++ b/linux/ps4-kexec-1250-pro/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2015-2016 shuffle2 +Copyright (C) 2015-2016 Hector Martin "marcan" +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/ps4-kexec-1250-pro/Makefile b/linux/ps4-kexec-1250-pro/Makefile new file mode 100644 index 0000000..4101e52 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/Makefile @@ -0,0 +1,37 @@ +TOOLCHAIN_PREFIX ?= +CC = $(TOOLCHAIN_PREFIX)gcc +AR = $(TOOLCHAIN_PREFIX)ar +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy + +CFLAGS=$(CFLAG) -DPS4_12_50 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX +CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ + -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ + -fno-asynchronous-unwind-tables \ + -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ + -mcmodel=small -mno-red-zone + +SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ + acpi.c crc32.c + +OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) +DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld + +all: libkexec.a kexec.bin + +%.o: %.c *.h + $(CC) -c $(CFLAGS) -o $@ $< + +%.o: %.S + $(CC) -c $(CFLAGS) -o $@ $< + +libkexec.a: $(OBJS) + $(AR) -rc $@ $(OBJS) + +kexec.elf: libkexec.a kexec.ld + $(CC) $(CFLAGS) -o $@ libkexec.a + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +clean: + rm -f libkexec.a kexec.elf kexec.bin $(OBJS) diff --git a/linux/ps4-kexec-1250-pro/README.md b/linux/ps4-kexec-1250-pro/README.md new file mode 100644 index 0000000..6500878 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/README.md @@ -0,0 +1,121 @@ +# PS4 kexec implementation + +This repo implements a kexec()-style system call for the PS4 Orbis kernel +(FreeBSD derivative). This is designed to boot a Linux kernel directly from +FreeBSD. + +This is not an exploit. It is useless without some mechanism of injecting code +into the PS4 OS kernel. + +## Building + +To build a kexec.bin relocatable binary using the supplied Makefile, just type +`make`. This will also build a kexec.a archive. You can either use the binary +directly, or link the archive into your own project. + +If you link kexec.a with your own code, you need to supply the two symbols +`_start` and `_end` in your linker script, as `kernel_init()` will try to remap +all pages covered by that range as RWX (to make global variable accesses work). +Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this +feature, if you have already taken care of page permissions for the code. + +If you use a compiler toolchain that have a special prefix you can declare it +by passing TOOLCHAIN_PREFIX option to the Makefile like this: + + make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' + +## Usage + +The code is designed to be completely standalone. There is a single entry point: + + int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); + +Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel +symbols and install the sys_kexec system call. The syscall is registered +as number 153 by default (you can change this in kexec.h). The return value +is 0 on success, or negative on error. + +You may pass something other than NULL as `early_printf`. In that case, that +function will be used for debug output during early symbol resolution, before +printf is available. + +Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is +enabled by default, symtab also disappears in newer kernel, we have to +hardcode offsets for some symbols. Currently we use the `early_printf` +given by user to caculate the base address of kernel, then relocate all the +symbols from the kernel base. You could enable this feature like this: + + make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' + +If you do not want to call the syscall from userspace, you can pass the address +of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the +address of `sys_kexec`, so you can invoke it manually (see kexec.h for +its prototype and how the arguments are passed). + +If you are using the standalone kexec.bin blob, then the `kexec_init` function +is always located at offset 0, so simply call the base address of the blob. +Don't forget to pass two NULL arguments (or the appropriate pointers). + +The injected `sys_kexec` system call takes (userspace) pointers to the kernel +and initramfs blobs, their sizes, and a pointer to the (null-terminated) command +line string. From userspace, this looks like this: + + int kexec(void *kernel_image, size_t image_size, + void *initramfs, size_t initramfs_size, + const char *cmdline); + + // syscall() usage: + syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); + +`kexec()` will load the kernel and initramfs into memory, but will not directly +boot them. To boot the loaded kernel, shut down the system. This can be +accomplished by pressing the power button, but can also be done more quickly +and reliably from userspace with the following sequence of system calls (this +kills userspace quickly but still does a controlled filesystem unmount): + + int evf = syscall(540, "SceSysCoreReboot"); + syscall(546, evf, 0x4000, 0); + syscall(541, evf); + // should be syscall(37, 1, 30) but only tested via kill symbol + kill(1, 30); + +Note that this software should be loaded into kernel memory space. If you are +running kernel code from userland mappings, you should either switch to kernel +mappings or separately copy kexec.bin to a location in kernel address space. +While syscalls or exploit code may run properly from userland, the shutdown hook +will not, as it will be called from a different process context. + +## Features + +`kernel_init()` will automatically find the Orbis OS kernel and resolve all +necessary symbols to work. There are no static symbol dependencies. If +`DO_NOT_REMAP_RWX` is not defined (the default), it will also patch +`pmap_protect` to disable the W^X restriction. + +In addition to loading the user-supplied initramfs, `kexec` will locate the +Radeon firmware blobs inside Orbis OS, extract them, convert them to a format +suitable for Linux, and append them as an additional initramfs cpio image to +the existing initramfs. This avoids the need to distribute the Radeon firmware +blobs. The `radeon` module, when compiled into the kernel, will automatically +load this firmware on boot. Note however that most typical initramfs scripts +will wipe the initramfs contents while pivoting to the real system, so if you +compile `radeon` as a module you may not be able to access the firmware after +boot. To cover that case, add some code to your initramfs `/init` script to +copy the firmware to a tmpfs mounted on the real filesystem: + + # assuming real root FS is mounted on /mnt + + mkdir -p /mnt/lib/firmware/radeon + mount -t tmpfs none /mnt/lib/firmware/radeon + cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ + + # now switch_root to /mnt + +This avoids having to permanently store copies of the Radeon firmware, which +isn't really necessary for most use cases. + +There is significant debug logging available, which will appear on the system +UART. Most of the code relies on the kernel `printf` implementation, and +therefore you should patch out the UART output blanker to see it. The final +code that runs on the boot CPU before booting the kernel uses direct UART +writes and is not affected by the blanking feature of Orbis OS. diff --git a/linux/ps4-kexec-1250-pro/acpi.c b/linux/ps4-kexec-1250-pro/acpi.c new file mode 100644 index 0000000..f53f04e --- /dev/null +++ b/linux/ps4-kexec-1250-pro/acpi.c @@ -0,0 +1,296 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "types.h" + +#ifdef TESTING +# include +# include +# include +# include +# include +# include +#else +# include "kernel.h" +# include "string.h" +# define printf kern.printf +#endif + +#define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) +#define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) + +#define PACKED __attribute__((packed)) + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +struct RSDP { + u64 sig; + u8 checksum; + u8 oemid[6]; + u8 rev; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 rsvd[3]; +} PACKED; + +struct SDTH { + u32 sig; + u32 length; + u8 rev; + u8 checksum; + u8 oem_id[6]; + u8 oem_tid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; +} PACKED; + +struct RSDT { + struct SDTH hdr; + u32 table_addr[]; +} PACKED; + +struct XSDT { + struct SDTH hdr; + u64 table_addr[]; +} PACKED; + +struct FADT { + struct SDTH hdr; + u32 facs; + u32 dsdt; + // more stuff... +} PACKED; + +struct ivhd_entry4 { + u8 type; + u16 devid; + u8 flags; +} PACKED; + +struct ivhd_header { + u8 type; + u8 flags; + u16 length; + u16 devid; + u16 cap_ptr; + u64 mmio_phys; + u16 pci_seg; + u16 info; + u32 efr_attr; +} PACKED; + +struct IVRS { + struct SDTH hdr; + u32 IVinfo; + u8 reserved[8]; + struct ivhd_header hd_hdr; + struct ivhd_entry4 hd_entries[3]; +} PACKED; + +// We have enough space to use the second half of the 64KB table area +// as scratch space for building the tables +#define BUFFER_OFF 0x8000 + +#define P2M(p) (((u64)(p)) - phys_base + map_base) +#define M2P(p) ((((void*)(p)) - map_base) + phys_base) +#define B2P(p) ((((void*)(p)) - buf_base) + phys_base) + +#define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) +#define PADB(s) p += (s) +#define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) +#define ALLOC(t) (t)ALLOCB(sizeof(t)) +#define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) +#define COPYT(s) COPYB(((struct SDTH*)s)->length, s) +#define COPYTP(s) COPYT(P2M(s)) +#define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) +#define COPYP(t, s) COPY(t, P2M(s)) + +static void rsdp_checksum(struct RSDP *rsdp) { + rsdp->checksum = rsdp->ext_checksum = 0; + + u8 sum = 0; + for (int i = 0; i < 20; i++) + sum += ((u8*)rsdp)[i]; + rsdp->checksum = -sum; + sum = 0; + for (int i = 0; i < sizeof(*rsdp); i++) + sum += ((u8*)rsdp)[i]; + rsdp->ext_checksum = -sum; +} + +static void table_checksum(void *table) { + struct SDTH *hdr = table; + hdr->checksum = 0; + u8 sum = 0; + for (int i = 0; i < hdr->length; i++) + sum += ((u8*)table)[i]; + hdr->checksum = -sum; +} + +#define IVHD_FLAG_ISOC_EN_MASK 0x08 +#define IVHD_DEV_ALL 0x01 +#define IVHD_DEV_SELECT 0x02 +#define IVHD_DEV_SELECT_RANGE_START 0x03 +#define IVHD_DEV_RANGE_END 0x04 + +#define ACPI_DEVFLAG_SYSMGT1 0x10 +#define ACPI_DEVFLAG_SYSMGT2 0x20 + +static void *build_ivrs(struct IVRS *ivrs) { + memset(ivrs, 0, sizeof(*ivrs)); + + ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); + ivrs->hdr.length = sizeof(*ivrs); + ivrs->hdr.rev = 1; + memcpy(ivrs->hdr.oem_id, "F0F ", 6); + memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); + ivrs->hdr.oem_rev = 0x20161225; + memcpy(ivrs->hdr.creator_id, "KEXC", 4); + ivrs->hdr.creator_rev = 0x20161225; + ivrs->IVinfo = 0x00203040; + + struct ivhd_header *hdr = &ivrs->hd_hdr; + hdr->type = 0x10; + hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; + hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); + hdr->devid = PCI_DEVFN(0, 2); + hdr->cap_ptr = 0x40; // from config space + 0x34 + hdr->mmio_phys = 0xfc000000; + hdr->pci_seg = 0; + hdr->info = 0; // msi msg num? (the pci cap should be written by software) + // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 + hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); + + struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; + // on fbsd, all aeolia devfns have active entries except memories (func 6) + // not sure if this is just because it wasn't in use when i dumped it? + // all entries are r/w + // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) + // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) + // Modes: + // 4 level: + // apcie + // 3 level: + // all others + + // the way to encode this info into the IVHD entries is fairly arbitrary... + entries[0].type = IVHD_DEV_SELECT; + entries[0].devid = PCI_DEVFN(20, 0); + entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; + + entries[1].type = IVHD_DEV_SELECT_RANGE_START; + entries[1].devid = PCI_DEVFN(20, 1); + entries[1].flags = 0; + entries[2].type = IVHD_DEV_RANGE_END; + entries[2].devid = PCI_DEVFN(20, 7); + entries[2].flags = 0; + + table_checksum(ivrs); + return ivrs + 1; +} + +void fix_acpi_tables(void *map_base, u64 phys_base) +{ + void *buf_base = map_base + 0x8000; + void *p = buf_base; + memset(buf_base, 0, 0x8000); + + printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); + + struct RSDP *rsdp = COPYP(struct RSDP, phys_base); + printf("RSDT at 0x%x\n", rsdp->rsdt_addr); + printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); + + struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); + struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); + rsdp->rsdt_addr = B2P(rsdt); + + PADB(0x30); // this gives us space for new tables + + struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); + rsdp->xsdt_addr = B2P(xsdt); + + PADB(0x60); + + struct FADT *fadt = NULL; + + int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; + int i; + for (i = 0; i < cnt; i++) { + struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); + printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); + switch (hdr->sig) { + case SIG32('F', 'A', 'C', 'P'): + { + fadt = (void*)hdr; + printf("FACS at 0x%x\n", fadt->facs); + printf("DSDT at 0x%x\n", fadt->dsdt); + // Sony puts the FACS before the FADT, unaligned, which is + // noncompliant, but let's keep it there + u8 *facs = COPYB(64, P2M(fadt->facs)); + fadt = (void*)(hdr = COPYT(hdr)); + fadt->facs = B2P(facs); + PADB(0x38); + break; + } + case SIG32('S', 'S', 'D', 'T'): + { + // Put the DSDT before the SSDT + if (fadt) { + PADB(0xf0); + u8 *dsdt = COPYTP(fadt->dsdt); + fadt->dsdt = B2P(dsdt); + PADB(0x174); + table_checksum(fadt); + } else { + printf("ERROR: no FADT yet?\n"); + } + hdr = COPYT(hdr); + break; + } + default: + hdr = COPYT(hdr); + } + table_checksum(hdr); + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); + } + + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); + i++; + p = build_ivrs(p); + + rsdt->hdr.length = sizeof(*rsdt) + 4 * i; + xsdt->hdr.length = sizeof(*xsdt) + 8 * i; + + rsdp_checksum(rsdp); + table_checksum(rsdt); + table_checksum(xsdt); + memcpy(map_base, buf_base, p - buf_base); +} + + +#ifdef TESTING + +int main(int argc, char **argv) +{ + int fd; + void *base; + + fd = open(argv[1], O_RDWR); + base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + fix_acpi_tables(base, 0xe0000); + return 0; +} + +#endif diff --git a/linux/ps4-kexec-1250-pro/acpi.h b/linux/ps4-kexec-1250-pro/acpi.h new file mode 100644 index 0000000..5723982 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/acpi.h @@ -0,0 +1,18 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ACPI_H +#define ACPI_H + +#include "types.h" + +void fix_acpi_tables(void *map_base, u64 phys_base); + +#endif diff --git a/linux/ps4-kexec-1250-pro/crc32.c b/linux/ps4-kexec-1250-pro/crc32.c new file mode 100644 index 0000000..7836c18 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/crc32.c @@ -0,0 +1,102 @@ +/*- +* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or +* code or tables extracted from it, as desired without restriction. +* +* First, the polynomial itself and its table of feedback terms. The +* polynomial is +* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 +* +* Note that we take it "backwards" and put the highest-order term in +* the lowest-order bit. The X^32 term is "implied"; the LSB is the +* X^31 term, etc. The X^0 term (usually shown as "+1") results in +* the MSB being 1 +* +* Note that the usual hardware shift register implementation, which +* is what we're using (we're merely optimizing it by doing eight-bit +* chunks at a time) shifts bits into the lowest-order term. In our +* implementation, that means shifting towards the right. Why do we +* do it this way? Because the calculated CRC must be transmitted in +* order from highest-order term to lowest-order term. UARTs transmit +* characters in order from LSB to MSB. By storing the CRC this way +* we hand it to the UART in the order low-byte to high-byte; the UART +* sends each low-bit to hight-bit; and the result is transmission bit +* by bit from highest- to lowest-order term without requiring any bit +* shuffling on our part. Reception works similarly +* +* The feedback terms table consists of 256, 32-bit entries. Notes +* +* The table can be generated at runtime if desired; code to do so +* is shown later. It might not be obvious, but the feedback +* terms simply represent the results of eight shift/xor opera +* tions for all combinations of data and CRC register values +* +* The values must be right-shifted by eight bits by the "updcrc +* logic; the shift must be unsigned (bring in zeroes). On some +* hardware you could probably optimize the shift in assembler by +* using byte-swap instructions +* polynomial $edb88320 +* +* +* CRC32 code derived from work by Gary S. Brown. +*/ + +#include "crc32.h" + +static u32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 crc32(u32 crc, const void *buf, size_t size) +{ + const u8 *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} diff --git a/linux/ps4-kexec-1250-pro/crc32.h b/linux/ps4-kexec-1250-pro/crc32.h new file mode 100644 index 0000000..777278d --- /dev/null +++ b/linux/ps4-kexec-1250-pro/crc32.h @@ -0,0 +1,7 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +u32 crc32(u32 crc, const void *buf, size_t size); + +#endif diff --git a/linux/ps4-kexec-1250-pro/elf.h b/linux/ps4-kexec-1250-pro/elf.h new file mode 100644 index 0000000..63371c7 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/elf.h @@ -0,0 +1,71 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + u8 e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + u64 e_entry; + u64 e_phoff; + u64 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf64_Ehdr; + +typedef struct { + u32 p_type; + u32 p_flags; + u64 p_offset; + void *p_vaddr; + u64 p_paddr; + u64 p_filesz; + u64 p_memsz; + u64 p_align; +} Elf64_Phdr; + +#define PT_DYNAMIC 2 +#define PT_PHDR 6 + +#define DT_NULL 0 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_STRSZ 10 + +typedef struct { + s64 d_tag; + union { + u64 d_val; + void *d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + void *st_value; + u64 st_size; +} Elf64_Sym; + +#endif diff --git a/linux/ps4-kexec-1250-pro/firmware.c b/linux/ps4-kexec-1250-pro/firmware.c new file mode 100644 index 0000000..a831b1c --- /dev/null +++ b/linux/ps4-kexec-1250-pro/firmware.c @@ -0,0 +1,479 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "firmware.h" +#include "types.h" +#include "kernel.h" +#include "string.h" +#include "types.h" +#include "crc32.h" + +#define DIR 0040755 +#define FILE 0100644 + +struct firmware_header { + u32 size_bytes; + u32 header_size_bytes; + u16 header_version_major; + u16 header_version_minor; + u16 ip_version_major; + u16 ip_version_minor; + u32 ucode_version; + u32 ucode_size_bytes; + u32 ucode_array_offset_bytes; + u32 crc32; + union { + struct { + u32 ucode_feature_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } gfx1; + struct { + u32 ucode_feature_version; + u32 save_and_restore_offset; + u32 clear_state_descriptor_offset; + u32 avail_scratch_ram_locations; + u32 master_pkt_description_offset; + u8 end[]; + } rlc1; + struct { + u32 ucode_feature_version; + u32 ucode_change_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } sdma1; + u8 raw[0xe0]; + }; +}; + +static inline char hex(u8 c) +{ + if (c <= 9) + return '0' + c; + return 'a' + c - 10; +} + +static void hex8(u8 **p, u32 val) +{ + *(*p)++ = hex(val >> 28); + *(*p)++ = hex((val >> 24) & 0xf); + *(*p)++ = hex((val >> 20) & 0xf); + *(*p)++ = hex((val >> 16) & 0xf); + *(*p)++ = hex((val >> 12) & 0xf); + *(*p)++ = hex((val >> 8) & 0xf); + *(*p)++ = hex((val >> 4) & 0xf); + *(*p)++ = hex(val & 0xf); +} + +void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) +{ + size_t name_len = strlen(name); + + // Pad to 4 byte multiple + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; + + memcpy(*p, "070701", 6); + *p += 6; + hex8(p, 0); // c_ino + hex8(p, mode); // c_mode + hex8(p, 0); // c_uid + hex8(p, 0); // c_gid + hex8(p, 1); // c_nlink + hex8(p, 0); // c_mtime + hex8(p, size); // c_filesize + hex8(p, 0); // c_maj + hex8(p, 0); // c_min + hex8(p, 0); // c_rmaj + hex8(p, 0); // c_rmin + hex8(p, name_len + 1); // c_namesize + hex8(p, 0); // c_chksum + memcpy(*p, name, name_len); + *p += name_len; + *(*p)++ = 0; + + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; +} + +struct fw_header_t { + u64 size_words; + char *unk_ident; + u64 unk; + void *blob; + u64 unk2; +}; + +struct fw_info_t { + struct fw_header_t *rlc; + struct fw_header_t *sdma0; + struct fw_header_t *sdma1; + struct fw_header_t *ce; + struct fw_header_t *pfp; + struct fw_header_t *me; + struct fw_header_t *mec1; + struct fw_header_t *mec2; +}; + +struct fw_expected_sizes_t { + u64 rlc; + u64 sdma0; + u64 sdma1; + u64 ce; + u64 pfp; + u64 me; + u64 mec1; + u64 mec2; +}; +static const struct fw_expected_sizes_t liverpool_fw_sizes = { + LVP_FW_RLC_SIZE, + LVP_FW_SDMA_SIZE, + LVP_FW_SDMA1_SIZE, + LVP_FW_CE_SIZE, + LVP_FW_PFP_SIZE, + LVP_FW_ME_SIZE, + LVP_FW_MEC_SIZE, + LVP_FW_MEC2_SIZE +}; +static const struct fw_expected_sizes_t gladius_fw_sizes = { + GL_FW_RLC_SIZE, + GL_FW_SDMA_SIZE, + GL_FW_SDMA1_SIZE, + GL_FW_CE_SIZE, + GL_FW_PFP_SIZE, + GL_FW_ME_SIZE, + GL_FW_MEC_SIZE, + GL_FW_MEC2_SIZE +}; + +void copy_edid(u8 **p, int sz) +{ + int i; + u8 *edid = *p; + u8 *off_edid = kern.edid; + + memset(edid, 0, sz); + *p += sz; + + for(i = 0; i < sz; i++) + *(edid + i) = *(off_edid + i); + + *p += sz; +} + +void copy_eap_hdd_key(u8 **p) +{ + int i; + u8 *eap_key = *p; + u8 *off_eap_key = kern.eap_hdd_key; + + memset(eap_key, 0, 0x20); + *p += 0x20; + + for(i = 0; i < 0x20; i++) + { + if(i < 0x10) + *(eap_key + i) = *(off_eap_key + 0xF - i); + else + *(eap_key + i) = *(off_eap_key + 0x2F - i); + } + *p += 0x20; +} + +int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + kern.printf("Copying %s firmware\n", name); + if (expected_size != (hdr->size_words * 4)) { + kern.printf("copy_firmware: %s: expected size %d, got %d\n", + name, expected_size, hdr->size_words * 4); + return 0; + } + + struct firmware_header *fhdr = (struct firmware_header*)*p; + memset(fhdr, 0, sizeof(*fhdr)); + *p += sizeof(*fhdr); + + memcpy(*p, hdr->blob, expected_size); + + fhdr->size_bytes = expected_size + sizeof(*fhdr); + fhdr->header_size_bytes = offsetof(struct firmware_header, raw); + fhdr->header_version_major = 1; + fhdr->header_version_minor = 0; + fhdr->ucode_version = 0x10; + fhdr->ucode_size_bytes = expected_size; + fhdr->ucode_array_offset_bytes = sizeof(*fhdr); + + *p += expected_size; + + return 1; +} + +int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); + fhdr->gfx1.ucode_feature_version = 21; + fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); + fhdr->rlc1.ucode_feature_version = 1; + fhdr->rlc1.save_and_restore_offset = 0x90; + fhdr->rlc1.clear_state_descriptor_offset = 0x3d; + fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? + fhdr->rlc1.master_pkt_description_offset = 0; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 2; + fhdr->ip_version_minor = 1; + fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); + fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; + fhdr->sdma1.ucode_change_version = 0; + fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +static const u32 pfp_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc4200016, // ldw r8, [r0, #0x16] + 0xdc030000, // mov ctr, r0 + 0xcc000049, // stw r0, [r0, #0x49] + 0xcc200013, // stw r0, [r8, #0x13] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 ce_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc420000c, // ldw r8, [r0, #0xc] + 0xdc030000, // mov ctr, r0 + 0xcc00002f, // stw r0, [r0, #0x2f] + 0xcc200012, // stw r0, [r8, #0x12] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 mec_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc43c000c, // ldw r15, [r0, #0x9] + 0xdc030000, // mov ctr, r0 + 0xcc00002b, // stw r0, [r0, #0x2b] + 0xcc3c000d, // stw r0, [r15, #0xd] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +#define PACKET_TYPE_NOP 0x10 + +static void patch_fw(void *p, const u32 *handler, int handler_size) { + int size = ((struct firmware_header*)p)->ucode_size_bytes; + int code_size = (size & ~0xfff) / 4; + int nop_start = code_size - 0x10; + + u32 *fw = p + sizeof(struct firmware_header); + kern.printf("NOP handler at 0x%x\n", nop_start); + memcpy(&fw[nop_start], handler, handler_size); + + // patch the branch table entry + for (int off = code_size; off < size/4; off++) { + if ((fw[off] >> 16) == PACKET_TYPE_NOP) { + fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; + } + } +} + +struct fw_info_t *get_fw_info() { + if (kern.gc_get_fw_info) { + return kern.gc_get_fw_info(); + } else if (kern.Starsha_UcodeInfo) { + return kern.Starsha_UcodeInfo; + } else { + return NULL; + } +} + +const struct fw_expected_sizes_t *get_fw_expected_sizes() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return &gladius_fw_sizes; + } else { + return &liverpool_fw_sizes; + } +} + +const char * get_gpu_name() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return "gladius"; + } else { + return "liverpool"; + } +} + +ssize_t firmware_extract(void *dest) +{ + u8 *p = dest; + + // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. + struct fw_info_t *info = get_fw_info(); + if (!info) { + kern.printf("firmware_extract: Could not locate firmware table"); + return -1; + } + const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); + + //Eap hdd key + cpio_hdr(&p, "key", DIR, 0); + cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); + copy_eap_hdd_key(&p); + + cpio_hdr(&p, "lib", DIR, 0); + cpio_hdr(&p, "lib/firmware", DIR, 0); + + /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ + int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); + cpio_hdr(&p, "lib/firmware/edid", DIR, 0); + cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); + copy_edid(&p, edid_sz); + + char dir[7]; + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) + kern.snprintf(dir, sizeof(dir), "amdgpu"); + else + kern.snprintf(dir, sizeof(dir), "amdgpu"); + + char dir_path[64]; + kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); + cpio_hdr(&p, dir_path, DIR, 0); + + char pfp_path[64]; + kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", pfp_path); + cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); + u8 *pfp = p; + if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) + return -1; + patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); + + char me_path[64]; + kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", me_path); + cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); + if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) + return -1; + + char ce_path[64]; + kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", ce_path); + cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); + u8 *ce = p; + if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) + return -1; + patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); + + char mec_path[64]; + kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec_path); + cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); + u8 *mec1 = p; + if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) + return -1; + patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); + + char mec2_path[64]; + kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec2_path); + cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); + u8 *mec2 = p; + if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) + return -1; + patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); + + char rlc_path[64]; + kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", rlc_path); + cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); + if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) + return -1; + + char sdma_path[64]; + kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma_path); + cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); + if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + char sdma1_path[64]; + kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma1_path); + cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); + if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + size_t size = p - (u8*)dest; + if (size > FW_CPIO_SIZE) { + kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); + return -1; + } + + return size; +} diff --git a/linux/ps4-kexec-1250-pro/firmware.h b/linux/ps4-kexec-1250-pro/firmware.h new file mode 100644 index 0000000..4835983 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/firmware.h @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include "types.h" + +//sizes eap_hdd_key +#define EAP_HDD_KEY_SIZE 0x20 +#define EDID_SIZE 256 + +// sizes for liverpool +#define LVP_FW_CE_SIZE 8576 +#define LVP_FW_ME_SIZE 16768 +#define LVP_FW_MEC_SIZE 16768 +#define LVP_FW_MEC2_SIZE 16768 +#define LVP_FW_PFP_SIZE 16768 +#define LVP_FW_RLC_SIZE 6144 +#define LVP_FW_SDMA_SIZE 4200 +#define LVP_FW_SDMA1_SIZE 4200 +// sizes for gladius +#define GL_FW_CE_SIZE 8576 +#define GL_FW_ME_SIZE 16768 +#define GL_FW_MEC_SIZE 16768 +#define GL_FW_MEC2_SIZE 16768 +#define GL_FW_PFP_SIZE 16768 +#define GL_FW_RLC_SIZE 8192 +#define GL_FW_SDMA_SIZE 4200 +#define GL_FW_SDMA1_SIZE 4200 + +#define MAX(x ,y) (((x) > (y)) ? (x) : (y)) +#define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) + +#define FW_CE_SIZE MAX_FW_SIZE(CE) +#define FW_ME_SIZE MAX_FW_SIZE(ME) +#define FW_MEC_SIZE MAX_FW_SIZE(MEC) +#define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) +#define FW_PFP_SIZE MAX_FW_SIZE(PFP) +#define FW_RLC_SIZE MAX_FW_SIZE(RLC) +#define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) +#define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) + +// Conservative value (max 113 bytes plus name size plus alignment) +#define CPIO_HEADER_SIZE 256 + +#define FW_HEADER_SIZE 256 + +// Leave space for 16 files (currently 12) +#define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ + FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ + FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ + FW_HEADER_SIZE * 8) + +ssize_t firmware_extract(void *dest); + +#endif diff --git a/linux/ps4-kexec-1250-pro/kernel.c b/linux/ps4-kexec-1250-pro/kernel.c new file mode 100644 index 0000000..b6d31b4 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/kernel.c @@ -0,0 +1,383 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "string.h" +#include "elf.h" +#include "x86.h" +#include "../magic.h" + +struct ksym_t kern; +int (*early_printf)(const char *fmt, ...) = NULL; + +#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) + +#ifdef NO_SYMTAB + +#define RESOLVE_NOERR(name) do { \ + if (kern_off_ ## name == 0) { \ + kern.name = 0; \ + } else { \ + kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ + } \ +} while (0); + +#define RESOLVE(name) do { \ + if (kern_off_ ## name == 0) { \ + return 0; \ + } \ + RESOLVE_NOERR(name) \ +} while (0); + +#else + +#define KERNSIZE 0x2000000 + +static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; +static Elf64_Sym *symtab; +static char *strtab; +static size_t strtab_size; + +static Elf64_Ehdr *find_kern_ehdr(void) +{ + // Search for the kernel copy embedded in ubios, then follow it to see + // where it was relocated to + for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; + if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { + for (size_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; + if (phdr->p_type == PT_PHDR) { + return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); + } + } + } + } + return NULL; +} + +static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf64_Dyn *)phdr->p_vaddr; + } + } + return NULL; +} + +static int elf_parse_dyn(Elf64_Dyn *dyn) +{ + for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_SYMTAB: + symtab = (Elf64_Sym *)dp->d_un.d_ptr; + break; + case DT_STRTAB: + strtab = (char *)dp->d_un.d_ptr; + break; + case DT_STRSZ: + strtab_size = dp->d_un.d_val; + break; + } + } + return symtab && strtab && strtab_size; +} + +void *kernel_resolve(const char *name) +{ + for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { + if (!strcmp(name, &strtab[sym->st_name])) { + eprintf("kern.%s = %p\n", name, (void*)sym->st_value); + return (void *)sym->st_value; + } + } + eprintf("Failed to resolve symbol '%s'\n", name); + return NULL; +} + +#define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) +#define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; + +#endif + +static int resolve_symbols(void) +{ + RESOLVE(printf); + RESOLVE(snprintf); + early_printf = kern.printf; + RESOLVE(copyin); + RESOLVE(copyout); + RESOLVE(copyinstr); + RESOLVE(kernel_map); + RESOLVE(kernel_pmap_store); + RESOLVE(kmem_alloc_contig); + RESOLVE(kmem_free); + RESOLVE(pmap_extract); + RESOLVE(pmap_protect); + RESOLVE(sysent); + RESOLVE(sched_pin); + RESOLVE(sched_unpin); + RESOLVE(smp_rendezvous); + RESOLVE(smp_no_rendevous_barrier); + RESOLVE(icc_query_nowait); + RESOLVE_NOERR(Starsha_UcodeInfo); + RESOLVE_NOERR(gpu_devid_is_9924); + RESOLVE_NOERR(gc_get_fw_info); + RESOLVE_NOERR(eap_hdd_key); + RESOLVE_NOERR(edid); + RESOLVE(wlanbt); + RESOLVE(kern_reboot); + RESOLVE(set_gpu_freq); + RESOLVE(set_pstate); + RESOLVE(update_vddnp); + RESOLVE(set_cu_power_gate); + RESOLVE_NOERR(pstate_before_shutdown); + return 1; +} + +#define M_WAITOK 0x0002 +#define M_ZERO 0x0100 + +#define VM_MEMATTR_DEFAULT 0x06 + +void *kernel_alloc_contig(size_t size) +{ + // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... + vm_offset_t ret = 0; + while(!(ret = kern.kmem_alloc_contig( + *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, + ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); + + /*if (!ret) { + kern.printf("Failed to allocate %zud bytes\n", size); + return NULL; + }*/ + return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); +} + +void kernel_free_contig(void *addr, size_t size) +{ + if (!addr) + return; + kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); +} + +int kernel_hook_install(void *target, void *hook) +{ + uintptr_t t = (uintptr_t)target; // addr to redirect to + uintptr_t h = (uintptr_t)hook; // place to write the thunk + + if (!hook || !target) { + return 0; + } + + kern.printf("kernel_hook_install(%p, %p)\n", target, hook); + + if (!(t & (1L << 63))) { + kern.printf("\n===================== WARNING =====================\n"); + kern.printf("hook target function address: %p\n", target); + kern.printf("It looks like we're running from userland memory.\n"); + kern.printf("Please run this code from a kernel memory mapping.\n\n"); + return 0; + } + s64 displacement = t - (h + 5); + + kern.sched_pin(); + u64 wp = write_protect_disable(); + if (displacement < -0x80000000 || displacement > 0x7fffffff) { + kern.printf(" Using 64bit absolute jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[2]; + s32 zero; + void *target; + } jmp = { + .op = { 0xff, 0x25 }, + .zero = 0, + .target = target, + }; + ASSERT_STRSIZE(struct jmp_t, 14); + memcpy(hook, &jmp, sizeof(jmp)); + } else { + kern.printf(" Using 32bit relative jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[1]; + s32 imm; + } jmp = { + .op = { 0xe9 }, + .imm = displacement, + }; + ASSERT_STRSIZE(struct jmp_t, 5); + memcpy(hook, &jmp, sizeof(jmp)); + } + wbinvd(); + write_protect_restore(wp); + kern.sched_unpin(); + + return 1; +} + +void kernel_syscall_install(int num, void *call, int narg) +{ + struct sysent_t *sy = &kern.sysent[num]; + + kern.sched_pin(); + u64 wp = write_protect_disable(); + + memset(sy, 0, sizeof(*sy)); + sy->sy_narg = narg; + sy->sy_call = call; + sy->sy_thrcnt = 1; + + write_protect_restore(wp); + kern.sched_unpin(); +} + +void kernel_remap(void *start, void *end, int perm) +{ + u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); + u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); + + kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); + kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); +} + +static volatile int _global_test = 0; + +#ifndef DO_NOT_REMAP_RWX +extern u8 _start[], _end[]; + +static int patch_pmap_check(void) +{ + u8 *p; + + for (p = (u8*)kern.pmap_protect; + p < ((u8*)kern.pmap_protect + 0x500); p++) { + #ifdef PS4_6_72 + if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 + p[5] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + /*#ifdef PS4_5_05 + if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { + p[1] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + }*/ + #else + if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { + p[2] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + #endif + } + kern.printf("pmap_protect patch failed!\n"); + return 0; +} +#endif + +int kernel_init(void *_early_printf) +{ + int rv = -1; + + if (_early_printf) + early_printf = _early_printf; + + eprintf("kernel_init()\n"); + +#ifdef KASLR + // use `early_printf` to calculate kernel base + if (early_printf == NULL) + return 0; + + kern.kern_base = (u64)(early_printf - kern_off_printf); + if ((kern.kern_base & PAGE_MASK) != 0) { + eprintf("Kernel base is not aligned\n"); + return 0; + } else { + eprintf("Kernel base = %llx\n", kern.kern_base); + } + + u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); + u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); + +#else + kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 + + u64 DMPML4I = 0x1fc; + u64 DMPDPI = 0; +#endif + + kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); + eprintf("Direct map base = %llx\n", kern.dmap_base); + + // We may not be mapped writable yet, so to be able to write to globals + // we need WP disabled. + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + +#ifndef NO_SYMTAB + Elf64_Ehdr *ehdr = find_kern_ehdr(); + if (!ehdr) { + eprintf("Could not find kernel ELF header\n"); + goto err; + } + eprintf("ELF header at %p\n", ehdr); + + Elf64_Dyn *dyn = elf_get_dyn(ehdr); + if (!dyn) { + eprintf("Could not find kernel dynamic header\n"); + goto err; + } + eprintf("ELF dynamic section at %p\n", dyn); + + if (!elf_parse_dyn(dyn)) { + eprintf("Failed to parse ELF dynamic section\n"); + goto err; + } +#endif + + if (!resolve_symbols()) { + eprintf("Failed to resolve all symbols\n"); + goto err; + } + + // Pin ourselves as soon as possible. This is expected to be released by the caller. + kern.sched_pin(); + +#ifndef DO_NOT_REMAP_RWX + if (!patch_pmap_check()) + goto err; +#endif + +#ifndef DO_NOT_REMAP_RWX + // kernel_remap may need interrupts, but may not write to globals! + enable_interrupts(); + kernel_remap(_start, _end, 7); + disable_interrupts(); +#endif + + // Writing to globals is now safe. + + kern.printf("Testing global variable access (write protection)...\n"); + _global_test = 1; + kern.printf("OK.\n"); + + kern.printf("Kernel interface initialized\n"); + rv = 0; + +err: + write_protect_restore(wp); + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1250-pro/kernel.h b/linux/ps4-kexec-1250-pro/kernel.h new file mode 100644 index 0000000..1833f5b --- /dev/null +++ b/linux/ps4-kexec-1250-pro/kernel.h @@ -0,0 +1,126 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KERNEL_H +#define KERNEL_H + +#include "types.h" +#include "reboot.h" + +#define PAGE_SIZE 0x4000 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define PML4SHIFT 39 +#define PDPSHIFT 30 +#define PDRSHIFT 21 +#define PAGE_SHIFT 12 + +#define KVADDR(l4, l3, l2, l1) ( \ + ((unsigned long)-1 << 47) | \ + ((unsigned long)(l4) << PML4SHIFT) | \ + ((unsigned long)(l3) << PDPSHIFT) | \ + ((unsigned long)(l2) << PDRSHIFT) | \ + ((unsigned long)(l1) << PAGE_SHIFT)) + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) +#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX + +typedef u64 vm_paddr_t; +typedef u64 vm_offset_t; +typedef u64 vm_size_t; +typedef void * vm_map_t; +typedef char vm_memattr_t; +typedef void * pmap_t; + +typedef void (*smp_rendezvous_callback_t)(void *); + +struct sysent_t { + int sy_narg; + void *sy_call; + u16 sy_auevent; + void *sy_systrace_args_func; + int sy_entry; + int sy_return; + int sy_flags; + int sy_thrcnt; +}; + +struct ksym_t { + // two parameters related to kaslr (they are not symbols) + uintptr_t kern_base; + uintptr_t dmap_base; + + int (*printf)(const char *fmt, ...); + int (*snprintf)(const char *fmt, ...); + int (*copyin)(const void *uaddr, void *kaddr, size_t len); + int (*copyout)(const void *kaddr, void *uaddr, size_t len); + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); + + void **kernel_map; + void *kernel_pmap_store; + vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, + vm_paddr_t low, vm_paddr_t high, + unsigned long alignment, + unsigned long boundary, + vm_memattr_t memattr); + void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); + vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); + void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); + + struct sysent_t *sysent; + + void (*sched_pin)(void); + void (*sched_unpin)(void); + void (*smp_rendezvous)(smp_rendezvous_callback_t, + smp_rendezvous_callback_t, + smp_rendezvous_callback_t, void *); + // yes...it is misspelled :) + void (*smp_no_rendevous_barrier)(void *); + void *icc_query_nowait; + void *Starsha_UcodeInfo; + int (*gpu_devid_is_9924)(); + void *(*gc_get_fw_info)(); + void *eap_hdd_key; + void *edid; + void (*wlanbt)(unsigned int state); + int (*kern_reboot)(int magic); + void(*set_gpu_freq)(unsigned int num, unsigned int freq); + void(*set_pstate)(unsigned int val); + void(*update_vddnp)(unsigned int val); + void(*set_cu_power_gate)(unsigned int val); + void *pstate_before_shutdown; +}; + +extern struct ksym_t kern; + +static inline int curcpu(void) +{ + int cpuid; + // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw + asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); + return cpuid; +} + +// Assign a working printf function to this to debug the symbol resolver +extern int (*early_printf)(const char *fmt, ...); + +void *kernel_resolve(const char *name); + +void *kernel_alloc_contig(size_t size); +void kernel_free_contig(void *addr, size_t size); + +void kernel_remap(void *start, void *end, int perm); + +void kernel_syscall_install(int num, void *call, int narg); +int kernel_hook_install(void *target, void *hook); + +int kernel_init(void *early_printf); + +#endif diff --git a/linux/ps4-kexec-1250-pro/kexec.c b/linux/ps4-kexec-1250-pro/kexec.c new file mode 100644 index 0000000..fa55dfe --- /dev/null +++ b/linux/ps4-kexec-1250-pro/kexec.c @@ -0,0 +1,229 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "linux_boot.h" +#include "x86.h" +#include "kexec.h" +#include "firmware.h" +#include "string.h" +#include "acpi.h" + +static int k_copyin(const void *uaddr, void *kaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(kaddr, uaddr, len); + return 0; +} + +static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + const char *ustr = (const char*)uaddr; + char *kstr = (char*)kaddr; + size_t ret; + if (!uaddr || !kaddr) + return EFAULT; + ret = strlcpy(kstr, ustr, len); + if (ret >= len) { + if (done) + *done = len; + return ENAMETOOLONG; + } else { + if (done) + *done = ret + 1; + } + return 0; +} + +static int k_copyout(const void *kaddr, void *uaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(uaddr, kaddr, len); + return 0; +} + +int sys_kexec(void *td, struct sys_kexec_args *uap) +{ + int err = 0; + size_t initramfs_size = uap->initramfs_size; + void *image = NULL; + void *initramfs = NULL; + size_t firmware_size = 0; + struct boot_params *bp = NULL; + size_t cmd_line_maxlen = 0; + char *cmd_line = NULL; + + int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; + int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; + + kern.printf("sys_kexec invoked\n"); + kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, + uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); + + // Look up our shutdown hook point + void *icc_query_nowait = kern.icc_query_nowait; + if (!icc_query_nowait) { + err = ENOENT; + goto cleanup; + } + + // Set gpu frequencies and pstate + // FAT&SLIM / PRO + kern.set_pstate(3); + + kern.set_gpu_freq(0, 800); //800 //800 + kern.set_gpu_freq(1, 853); //673 //853 + kern.set_gpu_freq(2, 711); //610 //711 + kern.set_gpu_freq(3, 800); //800 //800 + kern.set_gpu_freq(4, 911); //800 //911 + kern.set_gpu_freq(5, 800); //711 //800 + kern.set_gpu_freq(6, 984); //711 //984 + kern.set_gpu_freq(7, 673); //673 //673 + + kern.update_vddnp(0x12); + kern.set_cu_power_gate(0x24); + + // Copy in kernel image + image = kernel_alloc_contig(uap->image_size); + if (!image) { + kern.printf("Failed to allocate image\n"); + err = ENOMEM; + goto cleanup; + } + err = copyin(uap->image, image, uap->image_size); + if (err) { + kern.printf("Failed to copy in image\n"); + goto cleanup; + } + + // Copy in initramfs + initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); + if (!initramfs) { + kern.printf("Failed to allocate initramfs\n"); + err = ENOMEM; + goto cleanup; + } + + err = firmware_extract(((u8*)initramfs)); + if (err < 0) { + kern.printf("Failed to extract GPU firmware - continuing anyway\n"); + } else { + firmware_size = err; + } + + if (initramfs_size) { + err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); + if (err) { + kern.printf("Failed to copy in initramfs\n"); + goto cleanup; + } + } + initramfs_size += firmware_size; + + // Copy in cmdline + cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; + cmd_line = kernel_alloc_contig(cmd_line_maxlen); + if (!cmd_line) { + kern.printf("Failed to allocate cmdline\n"); + err = ENOMEM; + goto cleanup; + } + err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); + if (err) { + kern.printf("Failed to copy in cmdline\n"); + goto cleanup; + } + cmd_line[cmd_line_maxlen - 1] = 0; + + kern.printf("\nkexec parameters:\n"); + kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); + kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", + initramfs_size, uap->initramfs_size); + kern.printf(" Kernel command line: %s\n", cmd_line); + kern.printf(" Kernel image buffer: %p\n", image); + kern.printf(" Initramfs buffer: %p\n", initramfs); + + // Allocate our boot params + bp = kernel_alloc_contig(sizeof(*bp)); + if (!bp) { + kern.printf("Failed to allocate bp\n"); + err = ENOMEM; + goto cleanup; + } + + // Initialize bp + // TODO should probably do this from cpu_quiesce_gate, then bp doesn't + // need to be allocated here, just placed directly into low mem + set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); + + prepare_boot_params(bp, image); + + // Hook the final ICC shutdown function + if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { + kern.printf("Failed to install shutdown hook\n"); + err = EINVAL; + goto cleanup; + } + + kern.printf("******************************************************\n"); + kern.printf("kexec successfully armed. Please shut down the system.\n"); + kern.printf("******************************************************\n\n"); + +/* + kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); + if (kern.kern_reboot(RB_POWEROFF) == -1) + kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); +*/ + return 0; + +cleanup: + kernel_free_contig(cmd_line, cmd_line_maxlen); + kernel_free_contig(bp, sizeof(*bp)); + kernel_free_contig(image, uap->image_size); + kernel_free_contig(initramfs, uap->initramfs_size); + return err; + + copyout(NULL, NULL, 0); +} + +int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) +{ + int rv = 0; + + // potentially needed to write early_printf + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + + if (kernel_init(_early_printf) < 0) { + rv = -1; + goto cleanup; + } + + kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); + kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); + kern.printf("kexec_init() successful\n\n"); + + if (sys_kexec_ptr) + *sys_kexec_ptr = sys_kexec; + +cleanup: + write_protect_restore(wp); + if (kern.sched_unpin && wp & CR0_WP) { + // If we're returning to a state with WP enabled, assume the caller + // wants the thread unpinned. Else the caller is expected to + // call kern.sched_unpin() manually. + kern.sched_unpin(); + } + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1250-pro/kexec.h b/linux/ps4-kexec-1250-pro/kexec.h new file mode 100644 index 0000000..6c31637 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/kexec.h @@ -0,0 +1,38 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KEXEC_H +#define KEXEC_H + +#include "types.h" + +#define SYS_KEXEC 153 +#define SYS_KEXEC_NARGS 6 + +struct sys_kexec_args { + void *image; + size_t image_size; + void *initramfs; + size_t initramfs_size; + char *cmd_line; + int vram_gb; +}; + +typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); + +// Note: td is unused, you can pass NULL if you call this directly. +int sys_kexec(void *td, struct sys_kexec_args *uap); + +int kernel_init(void *early_printf); + +int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) + __attribute__ ((section (".init"))); + +#endif diff --git a/linux/ps4-kexec-1250-pro/kexec.ld b/linux/ps4-kexec-1250-pro/kexec.ld new file mode 100644 index 0000000..1bcf26c --- /dev/null +++ b/linux/ps4-kexec-1250-pro/kexec.ld @@ -0,0 +1,25 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +ENTRY(kexec_init) + +SECTIONS { + /* We don't do GOT relocation and rely on nothing ending up using the GOT + * (-fno-common helps here) */ + /DISCARD/ : { *(.comment) *(.got) } + _start = .; + .init : { *(.init) *(.init.*) } + .text : { *(.text) *(.text.*) } + .data : { *(.data) *(.data.*) } + .rodata : { *(.rodata) *(.rodata.*) } + .bss : { *(.bss) *(.bss.*) *(COMMON)} + .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ + _end = .; +} diff --git a/linux/ps4-kexec-1250-pro/linux_boot.c b/linux/ps4-kexec-1250-pro/linux_boot.c new file mode 100644 index 0000000..197ef63 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/linux_boot.c @@ -0,0 +1,441 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "linux_boot.h" +#include "types.h" +#include "string.h" +#include "x86.h" +#include "kernel.h" +#include "uart.h" +#include "acpi.h" + +void uart_write_byte(u8 b); + +static u64 vram_base = 0x100000000; +// Current code assumes it's a power of two. +static u64 vram_size = 1024 * 1024 * 1024; +static int vram_gb = 2; + +#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) + +struct desc_ptr { + u16 limit; + u64 address; +} __attribute__((packed)); + +struct desc_struct { + u16 limit0; + u16 base0; + u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; + u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, + uintptr_t new_cr3, uintptr_t gdt_ptr); +extern uint8_t *jmp_to_linux; +extern size_t jmp_to_linux_size; + +// FreeBSD DMAP addresses +struct linux_boot_info { + void *linux_image; + void *initramfs; + size_t initramfs_size; + struct boot_params *bp; + char *cmd_line; +}; +static struct linux_boot_info nix_info; + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v) +{ + nix_info.linux_image = linux_image; + nix_info.bp = bp; + nix_info.initramfs = initramfs; + nix_info.initramfs_size = initramfs_size; + nix_info.cmd_line = cmd_line; + vram_gb = v; +} + +static volatile int halted_cpus = 0; + +static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, + u32 type) +{ + uint8_t idx = bp->e820_entries; + bp->e820_map[idx].addr = addr; + bp->e820_map[idx].size = size; + bp->e820_map[idx].type = type; + bp->e820_entries++; +} + +void prepare_boot_params(struct boot_params *bp, u8 *linux_image) +{ + memset(bp, 0, sizeof(struct boot_params)); + struct boot_params *bp_src = (struct boot_params *)linux_image; + memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + + ((u8 *)&bp_src->hdr.jump)[1]); + + // These values are from fw 1.01 + bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); + bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + // This used to be VRAM, but we reclaim it as RAM + bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); + // Instead, carve out VRAM from the beginning of high memory + bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, + SMAP_TYPE_MEMORY); +} + +#define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_FB_OFFSET 0x2068 +#define HDP_NONSURFACE_BASE 0x2c04 +#define CONFIG_MEMSIZE 0x5428 + +static void configure_vram(void) +{ + u64 mmio_base = 0xe4800000; + u64 fb_base = 0x0f00000000; + u64 fb_top = fb_base + vram_gb * vram_size - 1; + + WR32(mmio_base + MC_VM_FB_LOCATION, 0); + WR32(mmio_base + HDP_NONSURFACE_BASE, 0); + + WR32(mmio_base + MC_VM_FB_LOCATION, + ((fb_top >> 24) << 16) | (fb_base >> 24)); + WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); + WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); + WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); +} + +#define IA32_MTRR_DEF_TYPE 0x2ff +#define MTRR_BASE(i) (0x200 + 2*i) +#define MTRR_MASK(i) (0x201 + 2*i) + +static void setup_mtrr(void) +{ + disable_interrupts(); + u64 cr0 = cr0_read(); + cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); + wbinvd(); + cr3_write(cr3_read()); // TLB flush + + wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled + + // Low memory (0GB-2GB) = WB + wrmsr(MTRR_BASE(0), 0x0000000006); + wrmsr(MTRR_MASK(0), 0xff80000800); + // High memory (4GB-8GB) = WB + wrmsr(MTRR_BASE(1), 0x0100000006); + wrmsr(MTRR_MASK(1), 0xff00000800); + // High memory (8GB-10GB) = WB + wrmsr(MTRR_BASE(2), 0x0200000006); + wrmsr(MTRR_MASK(2), 0xff80000800); + // VRAM (4GB-4GB+vram_size) = UC + wrmsr(MTRR_BASE(3), 0x0100000000); + wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); + + wbinvd(); + cr3_write(cr3_read()); // TLB flush + wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable + cr0_write(cr0); + enable_interrupts(); +} + +static void cleanup_interrupts(void) +{ + int i; + disable_interrupts(); + + // Reset APIC stuff (per-CPU) + *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; + for (i = 0x320; i < 0x380; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + for (i = 0x480; i < 0x500; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; + for (i = 0x500; i < 0x540; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; + + // Fix the LVT offset for thresholding + wrmsr(0x413, (1L<<24) | (1L<<52)); + wrmsr(0xc0000408, (1L<<24) | (1L<<52)); +} + +static void cpu_quiesce_gate(void *arg) +{ + int i; + + // Ensure we can write anywhere + cr0_write(cr0_read() & ~CR0_WP); + + // Interrupt stuff local to each CPU + cleanup_interrupts(); + + // We want to set up MTRRs on all CPUs + setup_mtrr(); + + if (curcpu() != 0) { + // We're not on BSP. Try to halt. + __sync_fetch_and_add(&halted_cpus, 1); + cpu_stop(); + } + + uart_write_str("kexec: Waiting for secondary CPUs...\n"); + + // wait for all cpus to halt + while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); + + uart_write_str("kexec: Secondary CPUs quiesced\n"); + + //* Put ident mappings in current page tables + // Should not be needed, but maybe helps for debugging? + cr4_pge_disable(); + u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); + u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + // Clear (really) low mem. + // Linux reads from here to try and access EBDA... + // get_bios_ebda reads u16 from 0x40e + // reserve_ebda_region reads u16 from 0x413 + // Writing zeros causes linux to default to marking + // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. + // It doesn't match the ps4 e820 map, but that seems OK. + memset((void *)0, 0, 0x1000); + + // Create a new page table hierarchy out of the way of linux + // Accessed via freebsd direct map + pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt + // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| + memset(pml4_base, 0, 512 * sizeof(u64) * 2); + pdp_base = pml4_base + 512; + u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; + pml4_base[0] = pdpe; + // Maintain the freebsd direct map + pml4_base[DM_PML4_BASE] = pdpe; + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + uart_write_str("kexec: Setting up GDT...\n"); + + struct desc_ptr gdt_ptr; + struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); + gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; + gdt_ptr.address = DM_TO_ID(desc); + + // clear + memset(desc, 0, gdt_ptr.limit + 1); + // Most things are ignored in 64bit mode, and we will never be in + // 32bit/compat modes, so just setup another pure-64bit environment... + // Linux inits it's own GDT in secondary_startup_64 + // 0x10 + desc[2].limit0 = 0xffff; + desc[2].base0 = 0x0000; + desc[2].base1 = 0x0000; + desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; + desc[2].s = 1; + desc[2].dpl = 0; + desc[2].p = 1; + desc[2].limit = 0xf; + desc[2].avl = 0; + desc[2].l = 1; + desc[2].d = 0; + desc[2].g = 0; + desc[2].base2 = 0x00; + // 0x18 + desc[3].limit0 = 0xffff; + desc[3].base0 = 0x0000; + desc[3].base1 = 0x0000; + desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; + desc[3].s = 1; + desc[3].dpl = 0; + desc[3].p = 1; + desc[3].limit = 0xf; + desc[3].avl = 0; + desc[3].l = 0; + desc[3].d = 0; + desc[3].g = 0; + desc[3].base2 = 0x00; + // Task segment value + // 0x20 + desc[4].limit0 = 0x0000; + desc[4].base0 = 0x0000; + desc[4].base1 = 0x0000; + desc[4].type = SEG_TYPE_TSS; + desc[4].s = 1; + desc[4].dpl = 0; + desc[4].p = 1; + desc[4].limit = 0x0; + desc[4].avl = 0; + desc[4].l = 0; + desc[4].d = 0; + desc[4].g = 0; + desc[4].base2 = 0x00; + + uart_write_str("kexec: Relocating stub...\n"); + + // Relocate the stub and jump to it + // TODO should thunk_copy be DMAP here? + void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); + memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); + // XXX The +0x200 is for the iret stack in linux_thunk.S + uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; + + uart_write_str("kexec: Setting up boot params...\n"); + + // XXX we write into this bootargs and pass it to the kernel, but in + // jmp_to_linux we use the bootargs from the image as input. So they + // MUST MATCH! + struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; + *bp_lo = *nix_info.bp; + lowmem_pos += sizeof(struct boot_params); + + struct setup_header *shdr = &bp_lo->hdr; + shdr->cmd_line_ptr = lowmem_pos; + shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; + shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; + bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; + bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; + shdr->hardware_subarch = X86_SUBARCH_PS4; + // This needs to be nonzero for the initramfs to work + shdr->type_of_loader = 0xd0; // kexec + + strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, + nix_info.bp->hdr.cmdline_size); + lowmem_pos += strlen(nix_info.cmd_line) + 1; + + uart_write_str("kexec: Cleaning up hardware...\n"); + + // Disable IOMMU + *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; + + // Disable all MSIs on Aeolia + for (i = 0; i < 8; i++) + *(volatile u32 *)PA_TO_DM(0xd03c844c + i*4) = 0; + + // Stop HPET timers + *(volatile u64 *)PA_TO_DM(0xd0382010) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382100) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382120) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382140) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382160) = 0; + + uart_write_str("kexec: Reconfiguring VRAM...\n"); + + configure_vram(); + + uart_write_str("kexec: Resetting GPU...\n"); + + // Softreset GPU + *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks + *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC + *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 +// *(volatile u64 *)PA_TO_DM(0xe480d248) = 1; // Halt SDMA1 eeply + *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 + *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC + + *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply + +// *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x10003; // Softreset GFX/CP/RLC + *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x30005; // Softreset GFX/CP/RLC eeply + +// udelay(150); +// *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x10003; + *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x30005; //eeply +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) |= 0x00100140; // Softreset SDMA/GRBM +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~0x00100140; +// udelay(150); + + // Enable audio output + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; + *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; + +// // Set pin caps of pin 2 to vendor defined, to hide it +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// // Set pin caps of pin 3 to !HDMI +// *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; +// *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; + // Set pin configuration default + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; + + uart_write_str("kexec: About to relocate and jump to kernel\n"); + + ((jmp_to_linux_t)thunk_copy)( + DM_TO_ID(nix_info.linux_image), + DM_TO_ID(bp_lo), + DM_TO_ID(pml4_base), + (uintptr_t)&gdt_ptr + ); + + // should never reach here + uart_write_str("kexec: unreachable (?)\n"); +} + +// Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) +int hook_icc_query_nowait(u8 *icc_msg) +{ + kern.printf("hook_icc_query_nowait called\n"); + + // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot + //In alternative we can re-enable it here, but sometimes that give problems.. + kern.wlanbt(0x2); + + fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); + + kern.printf("ACPI tables fixed\n"); + + // Transition to BSP and halt other cpus + // smp_no_rendevous_barrier is just nullsub, but it is treated specially by + // smp_rendezvous. This is the easiest way to do this, since we can't assume + // we're already running on BSP. Since smp_rendezvous normally waits on all + // cpus to finish the callbacks, we just never return... + kern.smp_rendezvous(kern.smp_no_rendevous_barrier, + cpu_quiesce_gate, + kern.smp_no_rendevous_barrier, NULL); + + // should never reach here + kern.printf("hook_icc_query_nowait: unreachable (?)\n"); + return 0; +} diff --git a/linux/ps4-kexec-1250-pro/linux_boot.h b/linux/ps4-kexec-1250-pro/linux_boot.h new file mode 100644 index 0000000..2232b35 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/linux_boot.h @@ -0,0 +1,88 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef LINUX_BOOT_H +#define LINUX_BOOT_H + +#include "types.h" + +#define SMAP_TYPE_MEMORY 1 +#define SMAP_TYPE_RESERVED 2 +#define SMAP_TYPE_ACPI_RECLAIM 3 +#define SMAP_TYPE_ACPI_NVS 4 +#define SMAP_TYPE_UNUSABLE 5 +#define SMAP_TYPE_PMEM 7 + +#define X86_SUBARCH_PS4 5 + +struct e820entry { + u64 addr; /* start of memory segment */ + u64 size; /* size of memory segment */ + u32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct setup_header { + u8 setup_sects; + u16 root_flags; + u32 syssize; + u16 ram_size; + u16 vid_mode; + u16 root_dev; + u16 boot_flag; + u16 jump; + u32 header; + u16 version; + u32 realmode_swtch; + u16 start_sys; + u16 kernel_version; + u8 type_of_loader; + u8 loadflags; + u16 setup_move_size; + u32 code32_start; + u32 ramdisk_image; + u32 ramdisk_size; + u32 bootsect_kludge; + u16 heap_end_ptr; + u8 ext_loader_ver; + u8 ext_loader_type; + u32 cmd_line_ptr; + u32 initrd_addr_max; + u32 kernel_alignment; + u8 relocatable_kernel; + u8 min_alignment; + u16 xloadflags; + u32 cmdline_size; + u32 hardware_subarch; + u64 hardware_subarch_data; + u32 payload_offset; + u32 payload_length; + u64 setup_data; + u64 pref_address; + u32 init_size; + u32 handover_offset; +} __attribute__((packed)); + +#define E820MAX 128 /* number of entries in E820MAP */ + +OSTRUCT(boot_params, 0x1000) +OFIELD(0x0c0, u32 ext_ramdisk_image); +OFIELD(0x0c4, u32 ext_ramdisk_size); +OFIELD(0x0c8, u32 ext_cmd_line_ptr); +OFIELD(0x1e8, u8 e820_entries); +OFIELD(0x1f1, struct setup_header hdr); +OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); +OSTRUCT_END + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v); +void prepare_boot_params(struct boot_params *bp, u8 *linux_image); +int hook_icc_query_nowait(u8 *icc_msg); + +#endif diff --git a/linux/ps4-kexec-1250-pro/linux_thunk.S b/linux/ps4-kexec-1250-pro/linux_thunk.S new file mode 100644 index 0000000..f6d2dfb --- /dev/null +++ b/linux/ps4-kexec-1250-pro/linux_thunk.S @@ -0,0 +1,90 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +.intel_syntax noprefix + +.equ setup_sects, 0x1f1 +.equ shdr_syssize, 0x1f4 +.equ pref_address, 0x258 + +.text + +#void jmp_to_linux( +# uintptr_t image_base, rdi +# uintptr_t bootargs, rsi +# uintptr_t new_cr3, rdx +# uintptr_t gdt_ptr rcx +#); +.globl jmp_to_linux +jmp_to_linux: + # switch to new gdt + data segments + cli + lgdt [rcx] + #xor eax, eax + mov eax, 0x18 + mov ds, eax + mov ss, eax + mov es, eax + mov fs, eax + mov gs, eax + + # switch to our own page tables (in low mem) + mov cr3, rdx + + # now we're on our own page tables, so we can obliterate the rest of memory + # TODO make sure we don't inadvertently overwrite (important) smap regions + # I think on ps4 we'll actually want to load to 0x700000 + # since we have tons of free room there. + # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We + # should choose a higher address as pref_address. + + # save args + mov r12, rdi + mov r13, rsi + + # memmove(pref_address, , (syssize * 0x10) / 8) + #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go + #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) + mov rdi, 0x6000000 # should be far from bzImage and initramfs + mov r14, rdi # r14 = pref_address + xor edx, edx + mov dl, [r12 + setup_sects] + inc rdx + shl rdx, 9 # rdx = offsetof(image_base, startup_32) + lea rsi, [r12 + rdx] # src = image_base + startup_32 + mov ecx, [r12 + shdr_syssize] + shl rcx, 4 + add rdi, rcx + add rsi, rcx + sub rdi, 8 + sub rsi, 8 + shr rcx, 3 + std + rep movsq + cld + + # make a tiny stack - we just need it for the lretq. + # what we jump to will not use this stack + lea rsp, [rip + jmp_to_linux_end + 0x200] + and rsp, -0x10 + #push 0 # retaddr + push 0x10 # cs = GDT[2] + add r14, 0x200 # pref_address + startup_64 + push r14 # rip + mov rsi, r13 # bootargs + lretq +jmp_to_linux_end: + +.data + +.globl jmp_to_linux_size +jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux + +.att_syntax prefix diff --git a/linux/ps4-kexec-1250-pro/reboot.h b/linux/ps4-kexec-1250-pro/reboot.h new file mode 100644 index 0000000..20b91f8 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/reboot.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)reboot.h 8.3 (Berkeley) 12/13/94 + * $FreeBSD$ + */ + +#ifndef _SYS_REBOOT_H_ +#define _SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. These are passed to + * the boot program and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ +#define RB_SINGLE 0x002 /* reboot to single user only */ +#define RB_NOSYNC 0x004 /* dont sync before reboot */ +#define RB_HALT 0x008 /* don't reboot, just halt */ +#define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ +#define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ +#define RB_KDB 0x040 /* give control to kernel debugger */ +#define RB_RDONLY 0x080 /* mount root fs read-only */ +#define RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define RB_MINIROOT 0x200 /* Unused placeholder */ +#define RB_VERBOSE 0x800 /* print all potentially useful info */ +#define RB_SERIAL 0x1000 /* use serial port as console */ +#define RB_CDROM 0x2000 /* use cdrom as root */ +#define RB_POWEROFF 0x4000 /* turn the power off if possible */ +#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ +#define RB_MUTE 0x10000 /* start up with the console muted */ +#define RB_SELFTEST 0x20000 /* unused placeholder */ +#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ +#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ +#define RB_PAUSE 0x100000 /* pause after each output line during probe */ +#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ +#define RB_PROBE 0x10000000 /* Probe multiple consoles */ +#define RB_MULTIPLE 0x20000000 /* use multiple consoles */ + +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + +#endif diff --git a/linux/ps4-kexec-1250-pro/string.h b/linux/ps4-kexec-1250-pro/string.h new file mode 100644 index 0000000..70aa668 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/string.h @@ -0,0 +1,96 @@ +/* + * string.h -- standard C string-manipulation functions. + * + * Copyright (C) 2008 Segher Boessenkool + * Copyright (C) 2009 Haxx Enterprises + * Copyright (C) 2010-2016 Hector Martin "marcan" + * + * Portions taken from the Public Domain C Library (PDCLib). + * http://pdclib.rootdirectory.de/ + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +static inline int strcmp(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s1[i] == s2[i]; i++) + ; + + return s1[i] - s2[i]; +} + +static inline void *memset(void *b, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)b)[i] = c; + + return b; +} + +static inline void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} + +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) + return p1[i] - p2[i]; + + return 0; +} + +static inline size_t strlen(const char *s) +{ + size_t len; + + for (len = 0; s[len]; len++) + ; + + return len; +} + +static inline size_t strnlen(const char *s, size_t count) +{ + size_t len; + + for (len = 0; s[len] && len < count; len++) + ; + + return len; +} + +static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) +{ + size_t len, needed; + + len = needed = strnlen(src, maxlen - 1) + 1; + if (len >= maxlen) + len = maxlen - 1; + + memcpy(dest, src, len); + dest[len] = 0; + + return needed - 1; +} + +#endif diff --git a/linux/ps4-kexec-1250-pro/types.h b/linux/ps4-kexec-1250-pro/types.h new file mode 100644 index 0000000..28d6619 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/types.h @@ -0,0 +1,51 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef TYPES_H +#define TYPES_H + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#ifndef TESTING +typedef u8 uint8_t; +typedef u64 size_t; +typedef s64 ssize_t; +typedef u64 uintptr_t; +typedef s64 off_t; +#endif + +#define NULL ((void *)0) + +#define CAT_(x, y) x ## y +#define CAT(x, y) CAT_(x, y) + +#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] +#define OSTRUCT(name, size) struct name { union { OPAD(size); +#define OSTRUCT_END };}; +#define OFIELD(off, field) struct { OPAD(off); field; } + +#define ASSERT_STRSIZE(struc, size) \ + _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#define ENOENT 2 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define ENAMETOOLONG 63 + +#endif diff --git a/linux/ps4-kexec-1250-pro/uart.c b/linux/ps4-kexec-1250-pro/uart.c new file mode 100644 index 0000000..62ea949 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/uart.c @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "uart.h" +#include "kernel.h" +#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) + +#define AEOLIA_UART_BASE 0xD0340000 +#define BAIKAL_UART_BASE 0xC890E000 + +#define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, AEOLIA_UART_BASE + (intf << 12) + (reg << 2)) + +#define UART_REG_DATA 0 +#define UART_REG_IER 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +# define LSR_TXRDY 0x20 +# define LSR_TEMT 0x40 +#define UART_REG_MSR 6 +#define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) +#define UART0_IER UART_REG(32, 0, UART_REG_IER) +#define UART0_IIR UART_REG(32, 0, UART_REG_IIR) +#define UART0_LCR UART_REG(32, 0, UART_REG_LCR) +#define UART0_MCR UART_REG(32, 0, UART_REG_MCR) +#define UART0_LSR UART_REG(32, 0, UART_REG_LSR) +#define UART0_MSR UART_REG(32, 0, UART_REG_MSR) + +void uart_write_byte(u8 b) +{ + int limit; + u64 barrier; + limit = 250000; + while (!(*UART0_LSR & LSR_TXRDY) && --limit) + ; + *UART0_DATA = b; + __sync_fetch_and_add(&barrier, 0); + limit = 250000; + while (!(*UART0_LSR & LSR_TEMT) && --limit) + ; +} + +void uart_write_char(char c) +{ + if (c == '\n') + uart_write_byte('\r'); + + uart_write_byte(c); +} + +void uart_write_str(const char *s) +{ + while (*s) { + uart_write_char(*s++); + } +} diff --git a/linux/ps4-kexec-1250-pro/uart.h b/linux/ps4-kexec-1250-pro/uart.h new file mode 100644 index 0000000..b803e29 --- /dev/null +++ b/linux/ps4-kexec-1250-pro/uart.h @@ -0,0 +1,20 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef UART_H +#define UART_H + +#include "types.h" + +void uart_write_byte(u8 b); +void uart_write_char(char c); +void uart_write_str(const char *s); + +#endif diff --git a/linux/ps4-kexec-1250-pro/x86.h b/linux/ps4-kexec-1250-pro/x86.h new file mode 100644 index 0000000..61b22ff --- /dev/null +++ b/linux/ps4-kexec-1250-pro/x86.h @@ -0,0 +1,149 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef X86_H +#define X86_H + +#define FLAGS_IF (1 << 9) + +#define CR0_WP (1 << 16) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) + +#define PG_V (1 << 0) +#define PG_RW (1 << 1) +#define PG_U (1 << 2) +#define PG_PS (1 << 7) + +#define SEG_TYPE_DATA (0 << 3) +#define SEG_TYPE_READ_WRITE (1 << 1) +#define SEG_TYPE_CODE (1 << 3) +#define SEG_TYPE_EXEC_READ (1 << 1) +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) + +static inline u64 cr0_read(void) +{ + u64 reg; + asm volatile("mov %0, cr0;" : "=r" (reg)); + return reg; +} + +static inline void cr0_write(u64 val) +{ + asm volatile("mov cr0, %0;" :: "r" (val)); +} + +static inline u64 write_protect_disable() +{ + u64 cr0 = cr0_read(); + cr0_write(cr0 & ~CR0_WP); + return cr0; +} + +static inline void write_protect_restore(u64 cr0) +{ + // Use only WP bit of input + cr0_write(cr0_read() | (cr0 & CR0_WP)); +} + +static inline u64 cr3_read(void) +{ + u64 reg; + asm volatile("mov %0, cr3;" : "=r" (reg)); + return reg; +} + +static inline void cr3_write(u64 val) +{ + asm volatile("mov cr3, %0;" :: "r" (val)); +} + +static inline void cr4_pge_disable(void) +{ + u64 cr4_temp; + asm volatile( + "mov %0, cr4;" + "and %0, ~0x80;" + "mov cr4, %0;" + : "=r" (cr4_temp) + ); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +static inline void cpu_stop(void) +{ + for (;;) + asm volatile("cli; hlt;" : : : "memory"); +} + +static inline void outl(int port, unsigned int data) +{ + asm volatile("out %w1, %0" : : "a" (data), "d" (port)); +} + +static inline void wrmsr(u32 msr_id, u64 msr_value) +{ + asm volatile( + "wrmsr" + : + : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) + ); +} + +static inline u64 rdtsc (void) +{ + unsigned int tickl, tickh; + asm volatile( + "rdtsc" + :"=a"(tickl),"=d"(tickh) + ); + return ((u64)tickh << 32) | tickl; +} + +static inline void udelay(unsigned int usec) { + u64 later = rdtsc() + usec * 1594ULL; + while (((s64)(later - rdtsc())) > 0); +} + +static inline void disable_interrupts(void) +{ + asm volatile("cli"); +} + +static inline void enable_interrupts(void) +{ + asm volatile("sti"); +} + +static inline u64 read_flags(void) +{ + u64 flags; + asm volatile("pushf; pop %0;" : "=r" (flags)); + return flags; +} + +static inline u64 intr_disable(void) +{ + u64 flags = read_flags(); + disable_interrupts(); + return flags; +} + +static inline void intr_restore(u64 flags) +{ + // TODO should only IF be or'd in? + asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); +} + +#endif diff --git a/linux/ps4-kexec-1250/LICENSE b/linux/ps4-kexec-1250/LICENSE new file mode 100644 index 0000000..6c1cbba --- /dev/null +++ b/linux/ps4-kexec-1250/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2015-2016 shuffle2 +Copyright (C) 2015-2016 Hector Martin "marcan" +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/ps4-kexec-1250/Makefile b/linux/ps4-kexec-1250/Makefile new file mode 100644 index 0000000..4101e52 --- /dev/null +++ b/linux/ps4-kexec-1250/Makefile @@ -0,0 +1,37 @@ +TOOLCHAIN_PREFIX ?= +CC = $(TOOLCHAIN_PREFIX)gcc +AR = $(TOOLCHAIN_PREFIX)ar +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy + +CFLAGS=$(CFLAG) -DPS4_12_50 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX +CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ + -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ + -fno-asynchronous-unwind-tables \ + -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ + -mcmodel=small -mno-red-zone + +SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ + acpi.c crc32.c + +OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) +DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld + +all: libkexec.a kexec.bin + +%.o: %.c *.h + $(CC) -c $(CFLAGS) -o $@ $< + +%.o: %.S + $(CC) -c $(CFLAGS) -o $@ $< + +libkexec.a: $(OBJS) + $(AR) -rc $@ $(OBJS) + +kexec.elf: libkexec.a kexec.ld + $(CC) $(CFLAGS) -o $@ libkexec.a + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +clean: + rm -f libkexec.a kexec.elf kexec.bin $(OBJS) diff --git a/linux/ps4-kexec-1250/README.md b/linux/ps4-kexec-1250/README.md new file mode 100644 index 0000000..6500878 --- /dev/null +++ b/linux/ps4-kexec-1250/README.md @@ -0,0 +1,121 @@ +# PS4 kexec implementation + +This repo implements a kexec()-style system call for the PS4 Orbis kernel +(FreeBSD derivative). This is designed to boot a Linux kernel directly from +FreeBSD. + +This is not an exploit. It is useless without some mechanism of injecting code +into the PS4 OS kernel. + +## Building + +To build a kexec.bin relocatable binary using the supplied Makefile, just type +`make`. This will also build a kexec.a archive. You can either use the binary +directly, or link the archive into your own project. + +If you link kexec.a with your own code, you need to supply the two symbols +`_start` and `_end` in your linker script, as `kernel_init()` will try to remap +all pages covered by that range as RWX (to make global variable accesses work). +Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this +feature, if you have already taken care of page permissions for the code. + +If you use a compiler toolchain that have a special prefix you can declare it +by passing TOOLCHAIN_PREFIX option to the Makefile like this: + + make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' + +## Usage + +The code is designed to be completely standalone. There is a single entry point: + + int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); + +Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel +symbols and install the sys_kexec system call. The syscall is registered +as number 153 by default (you can change this in kexec.h). The return value +is 0 on success, or negative on error. + +You may pass something other than NULL as `early_printf`. In that case, that +function will be used for debug output during early symbol resolution, before +printf is available. + +Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is +enabled by default, symtab also disappears in newer kernel, we have to +hardcode offsets for some symbols. Currently we use the `early_printf` +given by user to caculate the base address of kernel, then relocate all the +symbols from the kernel base. You could enable this feature like this: + + make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' + +If you do not want to call the syscall from userspace, you can pass the address +of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the +address of `sys_kexec`, so you can invoke it manually (see kexec.h for +its prototype and how the arguments are passed). + +If you are using the standalone kexec.bin blob, then the `kexec_init` function +is always located at offset 0, so simply call the base address of the blob. +Don't forget to pass two NULL arguments (or the appropriate pointers). + +The injected `sys_kexec` system call takes (userspace) pointers to the kernel +and initramfs blobs, their sizes, and a pointer to the (null-terminated) command +line string. From userspace, this looks like this: + + int kexec(void *kernel_image, size_t image_size, + void *initramfs, size_t initramfs_size, + const char *cmdline); + + // syscall() usage: + syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); + +`kexec()` will load the kernel and initramfs into memory, but will not directly +boot them. To boot the loaded kernel, shut down the system. This can be +accomplished by pressing the power button, but can also be done more quickly +and reliably from userspace with the following sequence of system calls (this +kills userspace quickly but still does a controlled filesystem unmount): + + int evf = syscall(540, "SceSysCoreReboot"); + syscall(546, evf, 0x4000, 0); + syscall(541, evf); + // should be syscall(37, 1, 30) but only tested via kill symbol + kill(1, 30); + +Note that this software should be loaded into kernel memory space. If you are +running kernel code from userland mappings, you should either switch to kernel +mappings or separately copy kexec.bin to a location in kernel address space. +While syscalls or exploit code may run properly from userland, the shutdown hook +will not, as it will be called from a different process context. + +## Features + +`kernel_init()` will automatically find the Orbis OS kernel and resolve all +necessary symbols to work. There are no static symbol dependencies. If +`DO_NOT_REMAP_RWX` is not defined (the default), it will also patch +`pmap_protect` to disable the W^X restriction. + +In addition to loading the user-supplied initramfs, `kexec` will locate the +Radeon firmware blobs inside Orbis OS, extract them, convert them to a format +suitable for Linux, and append them as an additional initramfs cpio image to +the existing initramfs. This avoids the need to distribute the Radeon firmware +blobs. The `radeon` module, when compiled into the kernel, will automatically +load this firmware on boot. Note however that most typical initramfs scripts +will wipe the initramfs contents while pivoting to the real system, so if you +compile `radeon` as a module you may not be able to access the firmware after +boot. To cover that case, add some code to your initramfs `/init` script to +copy the firmware to a tmpfs mounted on the real filesystem: + + # assuming real root FS is mounted on /mnt + + mkdir -p /mnt/lib/firmware/radeon + mount -t tmpfs none /mnt/lib/firmware/radeon + cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ + + # now switch_root to /mnt + +This avoids having to permanently store copies of the Radeon firmware, which +isn't really necessary for most use cases. + +There is significant debug logging available, which will appear on the system +UART. Most of the code relies on the kernel `printf` implementation, and +therefore you should patch out the UART output blanker to see it. The final +code that runs on the boot CPU before booting the kernel uses direct UART +writes and is not affected by the blanking feature of Orbis OS. diff --git a/linux/ps4-kexec-1250/acpi.c b/linux/ps4-kexec-1250/acpi.c new file mode 100644 index 0000000..f53f04e --- /dev/null +++ b/linux/ps4-kexec-1250/acpi.c @@ -0,0 +1,296 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "types.h" + +#ifdef TESTING +# include +# include +# include +# include +# include +# include +#else +# include "kernel.h" +# include "string.h" +# define printf kern.printf +#endif + +#define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) +#define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) + +#define PACKED __attribute__((packed)) + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +struct RSDP { + u64 sig; + u8 checksum; + u8 oemid[6]; + u8 rev; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 rsvd[3]; +} PACKED; + +struct SDTH { + u32 sig; + u32 length; + u8 rev; + u8 checksum; + u8 oem_id[6]; + u8 oem_tid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; +} PACKED; + +struct RSDT { + struct SDTH hdr; + u32 table_addr[]; +} PACKED; + +struct XSDT { + struct SDTH hdr; + u64 table_addr[]; +} PACKED; + +struct FADT { + struct SDTH hdr; + u32 facs; + u32 dsdt; + // more stuff... +} PACKED; + +struct ivhd_entry4 { + u8 type; + u16 devid; + u8 flags; +} PACKED; + +struct ivhd_header { + u8 type; + u8 flags; + u16 length; + u16 devid; + u16 cap_ptr; + u64 mmio_phys; + u16 pci_seg; + u16 info; + u32 efr_attr; +} PACKED; + +struct IVRS { + struct SDTH hdr; + u32 IVinfo; + u8 reserved[8]; + struct ivhd_header hd_hdr; + struct ivhd_entry4 hd_entries[3]; +} PACKED; + +// We have enough space to use the second half of the 64KB table area +// as scratch space for building the tables +#define BUFFER_OFF 0x8000 + +#define P2M(p) (((u64)(p)) - phys_base + map_base) +#define M2P(p) ((((void*)(p)) - map_base) + phys_base) +#define B2P(p) ((((void*)(p)) - buf_base) + phys_base) + +#define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) +#define PADB(s) p += (s) +#define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) +#define ALLOC(t) (t)ALLOCB(sizeof(t)) +#define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) +#define COPYT(s) COPYB(((struct SDTH*)s)->length, s) +#define COPYTP(s) COPYT(P2M(s)) +#define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) +#define COPYP(t, s) COPY(t, P2M(s)) + +static void rsdp_checksum(struct RSDP *rsdp) { + rsdp->checksum = rsdp->ext_checksum = 0; + + u8 sum = 0; + for (int i = 0; i < 20; i++) + sum += ((u8*)rsdp)[i]; + rsdp->checksum = -sum; + sum = 0; + for (int i = 0; i < sizeof(*rsdp); i++) + sum += ((u8*)rsdp)[i]; + rsdp->ext_checksum = -sum; +} + +static void table_checksum(void *table) { + struct SDTH *hdr = table; + hdr->checksum = 0; + u8 sum = 0; + for (int i = 0; i < hdr->length; i++) + sum += ((u8*)table)[i]; + hdr->checksum = -sum; +} + +#define IVHD_FLAG_ISOC_EN_MASK 0x08 +#define IVHD_DEV_ALL 0x01 +#define IVHD_DEV_SELECT 0x02 +#define IVHD_DEV_SELECT_RANGE_START 0x03 +#define IVHD_DEV_RANGE_END 0x04 + +#define ACPI_DEVFLAG_SYSMGT1 0x10 +#define ACPI_DEVFLAG_SYSMGT2 0x20 + +static void *build_ivrs(struct IVRS *ivrs) { + memset(ivrs, 0, sizeof(*ivrs)); + + ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); + ivrs->hdr.length = sizeof(*ivrs); + ivrs->hdr.rev = 1; + memcpy(ivrs->hdr.oem_id, "F0F ", 6); + memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); + ivrs->hdr.oem_rev = 0x20161225; + memcpy(ivrs->hdr.creator_id, "KEXC", 4); + ivrs->hdr.creator_rev = 0x20161225; + ivrs->IVinfo = 0x00203040; + + struct ivhd_header *hdr = &ivrs->hd_hdr; + hdr->type = 0x10; + hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; + hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); + hdr->devid = PCI_DEVFN(0, 2); + hdr->cap_ptr = 0x40; // from config space + 0x34 + hdr->mmio_phys = 0xfc000000; + hdr->pci_seg = 0; + hdr->info = 0; // msi msg num? (the pci cap should be written by software) + // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 + hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); + + struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; + // on fbsd, all aeolia devfns have active entries except memories (func 6) + // not sure if this is just because it wasn't in use when i dumped it? + // all entries are r/w + // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) + // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) + // Modes: + // 4 level: + // apcie + // 3 level: + // all others + + // the way to encode this info into the IVHD entries is fairly arbitrary... + entries[0].type = IVHD_DEV_SELECT; + entries[0].devid = PCI_DEVFN(20, 0); + entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; + + entries[1].type = IVHD_DEV_SELECT_RANGE_START; + entries[1].devid = PCI_DEVFN(20, 1); + entries[1].flags = 0; + entries[2].type = IVHD_DEV_RANGE_END; + entries[2].devid = PCI_DEVFN(20, 7); + entries[2].flags = 0; + + table_checksum(ivrs); + return ivrs + 1; +} + +void fix_acpi_tables(void *map_base, u64 phys_base) +{ + void *buf_base = map_base + 0x8000; + void *p = buf_base; + memset(buf_base, 0, 0x8000); + + printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); + + struct RSDP *rsdp = COPYP(struct RSDP, phys_base); + printf("RSDT at 0x%x\n", rsdp->rsdt_addr); + printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); + + struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); + struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); + rsdp->rsdt_addr = B2P(rsdt); + + PADB(0x30); // this gives us space for new tables + + struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); + rsdp->xsdt_addr = B2P(xsdt); + + PADB(0x60); + + struct FADT *fadt = NULL; + + int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; + int i; + for (i = 0; i < cnt; i++) { + struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); + printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); + switch (hdr->sig) { + case SIG32('F', 'A', 'C', 'P'): + { + fadt = (void*)hdr; + printf("FACS at 0x%x\n", fadt->facs); + printf("DSDT at 0x%x\n", fadt->dsdt); + // Sony puts the FACS before the FADT, unaligned, which is + // noncompliant, but let's keep it there + u8 *facs = COPYB(64, P2M(fadt->facs)); + fadt = (void*)(hdr = COPYT(hdr)); + fadt->facs = B2P(facs); + PADB(0x38); + break; + } + case SIG32('S', 'S', 'D', 'T'): + { + // Put the DSDT before the SSDT + if (fadt) { + PADB(0xf0); + u8 *dsdt = COPYTP(fadt->dsdt); + fadt->dsdt = B2P(dsdt); + PADB(0x174); + table_checksum(fadt); + } else { + printf("ERROR: no FADT yet?\n"); + } + hdr = COPYT(hdr); + break; + } + default: + hdr = COPYT(hdr); + } + table_checksum(hdr); + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); + } + + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); + i++; + p = build_ivrs(p); + + rsdt->hdr.length = sizeof(*rsdt) + 4 * i; + xsdt->hdr.length = sizeof(*xsdt) + 8 * i; + + rsdp_checksum(rsdp); + table_checksum(rsdt); + table_checksum(xsdt); + memcpy(map_base, buf_base, p - buf_base); +} + + +#ifdef TESTING + +int main(int argc, char **argv) +{ + int fd; + void *base; + + fd = open(argv[1], O_RDWR); + base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + fix_acpi_tables(base, 0xe0000); + return 0; +} + +#endif diff --git a/linux/ps4-kexec-1250/acpi.h b/linux/ps4-kexec-1250/acpi.h new file mode 100644 index 0000000..5723982 --- /dev/null +++ b/linux/ps4-kexec-1250/acpi.h @@ -0,0 +1,18 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ACPI_H +#define ACPI_H + +#include "types.h" + +void fix_acpi_tables(void *map_base, u64 phys_base); + +#endif diff --git a/linux/ps4-kexec-1250/crc32.c b/linux/ps4-kexec-1250/crc32.c new file mode 100644 index 0000000..7836c18 --- /dev/null +++ b/linux/ps4-kexec-1250/crc32.c @@ -0,0 +1,102 @@ +/*- +* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or +* code or tables extracted from it, as desired without restriction. +* +* First, the polynomial itself and its table of feedback terms. The +* polynomial is +* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 +* +* Note that we take it "backwards" and put the highest-order term in +* the lowest-order bit. The X^32 term is "implied"; the LSB is the +* X^31 term, etc. The X^0 term (usually shown as "+1") results in +* the MSB being 1 +* +* Note that the usual hardware shift register implementation, which +* is what we're using (we're merely optimizing it by doing eight-bit +* chunks at a time) shifts bits into the lowest-order term. In our +* implementation, that means shifting towards the right. Why do we +* do it this way? Because the calculated CRC must be transmitted in +* order from highest-order term to lowest-order term. UARTs transmit +* characters in order from LSB to MSB. By storing the CRC this way +* we hand it to the UART in the order low-byte to high-byte; the UART +* sends each low-bit to hight-bit; and the result is transmission bit +* by bit from highest- to lowest-order term without requiring any bit +* shuffling on our part. Reception works similarly +* +* The feedback terms table consists of 256, 32-bit entries. Notes +* +* The table can be generated at runtime if desired; code to do so +* is shown later. It might not be obvious, but the feedback +* terms simply represent the results of eight shift/xor opera +* tions for all combinations of data and CRC register values +* +* The values must be right-shifted by eight bits by the "updcrc +* logic; the shift must be unsigned (bring in zeroes). On some +* hardware you could probably optimize the shift in assembler by +* using byte-swap instructions +* polynomial $edb88320 +* +* +* CRC32 code derived from work by Gary S. Brown. +*/ + +#include "crc32.h" + +static u32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 crc32(u32 crc, const void *buf, size_t size) +{ + const u8 *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} diff --git a/linux/ps4-kexec-1250/crc32.h b/linux/ps4-kexec-1250/crc32.h new file mode 100644 index 0000000..777278d --- /dev/null +++ b/linux/ps4-kexec-1250/crc32.h @@ -0,0 +1,7 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +u32 crc32(u32 crc, const void *buf, size_t size); + +#endif diff --git a/linux/ps4-kexec-1250/elf.h b/linux/ps4-kexec-1250/elf.h new file mode 100644 index 0000000..63371c7 --- /dev/null +++ b/linux/ps4-kexec-1250/elf.h @@ -0,0 +1,71 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + u8 e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + u64 e_entry; + u64 e_phoff; + u64 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf64_Ehdr; + +typedef struct { + u32 p_type; + u32 p_flags; + u64 p_offset; + void *p_vaddr; + u64 p_paddr; + u64 p_filesz; + u64 p_memsz; + u64 p_align; +} Elf64_Phdr; + +#define PT_DYNAMIC 2 +#define PT_PHDR 6 + +#define DT_NULL 0 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_STRSZ 10 + +typedef struct { + s64 d_tag; + union { + u64 d_val; + void *d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + void *st_value; + u64 st_size; +} Elf64_Sym; + +#endif diff --git a/linux/ps4-kexec-1250/firmware.c b/linux/ps4-kexec-1250/firmware.c new file mode 100644 index 0000000..a831b1c --- /dev/null +++ b/linux/ps4-kexec-1250/firmware.c @@ -0,0 +1,479 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "firmware.h" +#include "types.h" +#include "kernel.h" +#include "string.h" +#include "types.h" +#include "crc32.h" + +#define DIR 0040755 +#define FILE 0100644 + +struct firmware_header { + u32 size_bytes; + u32 header_size_bytes; + u16 header_version_major; + u16 header_version_minor; + u16 ip_version_major; + u16 ip_version_minor; + u32 ucode_version; + u32 ucode_size_bytes; + u32 ucode_array_offset_bytes; + u32 crc32; + union { + struct { + u32 ucode_feature_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } gfx1; + struct { + u32 ucode_feature_version; + u32 save_and_restore_offset; + u32 clear_state_descriptor_offset; + u32 avail_scratch_ram_locations; + u32 master_pkt_description_offset; + u8 end[]; + } rlc1; + struct { + u32 ucode_feature_version; + u32 ucode_change_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } sdma1; + u8 raw[0xe0]; + }; +}; + +static inline char hex(u8 c) +{ + if (c <= 9) + return '0' + c; + return 'a' + c - 10; +} + +static void hex8(u8 **p, u32 val) +{ + *(*p)++ = hex(val >> 28); + *(*p)++ = hex((val >> 24) & 0xf); + *(*p)++ = hex((val >> 20) & 0xf); + *(*p)++ = hex((val >> 16) & 0xf); + *(*p)++ = hex((val >> 12) & 0xf); + *(*p)++ = hex((val >> 8) & 0xf); + *(*p)++ = hex((val >> 4) & 0xf); + *(*p)++ = hex(val & 0xf); +} + +void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) +{ + size_t name_len = strlen(name); + + // Pad to 4 byte multiple + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; + + memcpy(*p, "070701", 6); + *p += 6; + hex8(p, 0); // c_ino + hex8(p, mode); // c_mode + hex8(p, 0); // c_uid + hex8(p, 0); // c_gid + hex8(p, 1); // c_nlink + hex8(p, 0); // c_mtime + hex8(p, size); // c_filesize + hex8(p, 0); // c_maj + hex8(p, 0); // c_min + hex8(p, 0); // c_rmaj + hex8(p, 0); // c_rmin + hex8(p, name_len + 1); // c_namesize + hex8(p, 0); // c_chksum + memcpy(*p, name, name_len); + *p += name_len; + *(*p)++ = 0; + + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; +} + +struct fw_header_t { + u64 size_words; + char *unk_ident; + u64 unk; + void *blob; + u64 unk2; +}; + +struct fw_info_t { + struct fw_header_t *rlc; + struct fw_header_t *sdma0; + struct fw_header_t *sdma1; + struct fw_header_t *ce; + struct fw_header_t *pfp; + struct fw_header_t *me; + struct fw_header_t *mec1; + struct fw_header_t *mec2; +}; + +struct fw_expected_sizes_t { + u64 rlc; + u64 sdma0; + u64 sdma1; + u64 ce; + u64 pfp; + u64 me; + u64 mec1; + u64 mec2; +}; +static const struct fw_expected_sizes_t liverpool_fw_sizes = { + LVP_FW_RLC_SIZE, + LVP_FW_SDMA_SIZE, + LVP_FW_SDMA1_SIZE, + LVP_FW_CE_SIZE, + LVP_FW_PFP_SIZE, + LVP_FW_ME_SIZE, + LVP_FW_MEC_SIZE, + LVP_FW_MEC2_SIZE +}; +static const struct fw_expected_sizes_t gladius_fw_sizes = { + GL_FW_RLC_SIZE, + GL_FW_SDMA_SIZE, + GL_FW_SDMA1_SIZE, + GL_FW_CE_SIZE, + GL_FW_PFP_SIZE, + GL_FW_ME_SIZE, + GL_FW_MEC_SIZE, + GL_FW_MEC2_SIZE +}; + +void copy_edid(u8 **p, int sz) +{ + int i; + u8 *edid = *p; + u8 *off_edid = kern.edid; + + memset(edid, 0, sz); + *p += sz; + + for(i = 0; i < sz; i++) + *(edid + i) = *(off_edid + i); + + *p += sz; +} + +void copy_eap_hdd_key(u8 **p) +{ + int i; + u8 *eap_key = *p; + u8 *off_eap_key = kern.eap_hdd_key; + + memset(eap_key, 0, 0x20); + *p += 0x20; + + for(i = 0; i < 0x20; i++) + { + if(i < 0x10) + *(eap_key + i) = *(off_eap_key + 0xF - i); + else + *(eap_key + i) = *(off_eap_key + 0x2F - i); + } + *p += 0x20; +} + +int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + kern.printf("Copying %s firmware\n", name); + if (expected_size != (hdr->size_words * 4)) { + kern.printf("copy_firmware: %s: expected size %d, got %d\n", + name, expected_size, hdr->size_words * 4); + return 0; + } + + struct firmware_header *fhdr = (struct firmware_header*)*p; + memset(fhdr, 0, sizeof(*fhdr)); + *p += sizeof(*fhdr); + + memcpy(*p, hdr->blob, expected_size); + + fhdr->size_bytes = expected_size + sizeof(*fhdr); + fhdr->header_size_bytes = offsetof(struct firmware_header, raw); + fhdr->header_version_major = 1; + fhdr->header_version_minor = 0; + fhdr->ucode_version = 0x10; + fhdr->ucode_size_bytes = expected_size; + fhdr->ucode_array_offset_bytes = sizeof(*fhdr); + + *p += expected_size; + + return 1; +} + +int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); + fhdr->gfx1.ucode_feature_version = 21; + fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); + fhdr->rlc1.ucode_feature_version = 1; + fhdr->rlc1.save_and_restore_offset = 0x90; + fhdr->rlc1.clear_state_descriptor_offset = 0x3d; + fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? + fhdr->rlc1.master_pkt_description_offset = 0; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 2; + fhdr->ip_version_minor = 1; + fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); + fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; + fhdr->sdma1.ucode_change_version = 0; + fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +static const u32 pfp_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc4200016, // ldw r8, [r0, #0x16] + 0xdc030000, // mov ctr, r0 + 0xcc000049, // stw r0, [r0, #0x49] + 0xcc200013, // stw r0, [r8, #0x13] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 ce_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc420000c, // ldw r8, [r0, #0xc] + 0xdc030000, // mov ctr, r0 + 0xcc00002f, // stw r0, [r0, #0x2f] + 0xcc200012, // stw r0, [r8, #0x12] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 mec_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc43c000c, // ldw r15, [r0, #0x9] + 0xdc030000, // mov ctr, r0 + 0xcc00002b, // stw r0, [r0, #0x2b] + 0xcc3c000d, // stw r0, [r15, #0xd] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +#define PACKET_TYPE_NOP 0x10 + +static void patch_fw(void *p, const u32 *handler, int handler_size) { + int size = ((struct firmware_header*)p)->ucode_size_bytes; + int code_size = (size & ~0xfff) / 4; + int nop_start = code_size - 0x10; + + u32 *fw = p + sizeof(struct firmware_header); + kern.printf("NOP handler at 0x%x\n", nop_start); + memcpy(&fw[nop_start], handler, handler_size); + + // patch the branch table entry + for (int off = code_size; off < size/4; off++) { + if ((fw[off] >> 16) == PACKET_TYPE_NOP) { + fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; + } + } +} + +struct fw_info_t *get_fw_info() { + if (kern.gc_get_fw_info) { + return kern.gc_get_fw_info(); + } else if (kern.Starsha_UcodeInfo) { + return kern.Starsha_UcodeInfo; + } else { + return NULL; + } +} + +const struct fw_expected_sizes_t *get_fw_expected_sizes() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return &gladius_fw_sizes; + } else { + return &liverpool_fw_sizes; + } +} + +const char * get_gpu_name() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return "gladius"; + } else { + return "liverpool"; + } +} + +ssize_t firmware_extract(void *dest) +{ + u8 *p = dest; + + // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. + struct fw_info_t *info = get_fw_info(); + if (!info) { + kern.printf("firmware_extract: Could not locate firmware table"); + return -1; + } + const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); + + //Eap hdd key + cpio_hdr(&p, "key", DIR, 0); + cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); + copy_eap_hdd_key(&p); + + cpio_hdr(&p, "lib", DIR, 0); + cpio_hdr(&p, "lib/firmware", DIR, 0); + + /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ + int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); + cpio_hdr(&p, "lib/firmware/edid", DIR, 0); + cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); + copy_edid(&p, edid_sz); + + char dir[7]; + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) + kern.snprintf(dir, sizeof(dir), "amdgpu"); + else + kern.snprintf(dir, sizeof(dir), "amdgpu"); + + char dir_path[64]; + kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); + cpio_hdr(&p, dir_path, DIR, 0); + + char pfp_path[64]; + kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", pfp_path); + cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); + u8 *pfp = p; + if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) + return -1; + patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); + + char me_path[64]; + kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", me_path); + cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); + if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) + return -1; + + char ce_path[64]; + kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", ce_path); + cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); + u8 *ce = p; + if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) + return -1; + patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); + + char mec_path[64]; + kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec_path); + cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); + u8 *mec1 = p; + if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) + return -1; + patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); + + char mec2_path[64]; + kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec2_path); + cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); + u8 *mec2 = p; + if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) + return -1; + patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); + + char rlc_path[64]; + kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", rlc_path); + cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); + if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) + return -1; + + char sdma_path[64]; + kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma_path); + cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); + if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + char sdma1_path[64]; + kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma1_path); + cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); + if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + size_t size = p - (u8*)dest; + if (size > FW_CPIO_SIZE) { + kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); + return -1; + } + + return size; +} diff --git a/linux/ps4-kexec-1250/firmware.h b/linux/ps4-kexec-1250/firmware.h new file mode 100644 index 0000000..4835983 --- /dev/null +++ b/linux/ps4-kexec-1250/firmware.h @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include "types.h" + +//sizes eap_hdd_key +#define EAP_HDD_KEY_SIZE 0x20 +#define EDID_SIZE 256 + +// sizes for liverpool +#define LVP_FW_CE_SIZE 8576 +#define LVP_FW_ME_SIZE 16768 +#define LVP_FW_MEC_SIZE 16768 +#define LVP_FW_MEC2_SIZE 16768 +#define LVP_FW_PFP_SIZE 16768 +#define LVP_FW_RLC_SIZE 6144 +#define LVP_FW_SDMA_SIZE 4200 +#define LVP_FW_SDMA1_SIZE 4200 +// sizes for gladius +#define GL_FW_CE_SIZE 8576 +#define GL_FW_ME_SIZE 16768 +#define GL_FW_MEC_SIZE 16768 +#define GL_FW_MEC2_SIZE 16768 +#define GL_FW_PFP_SIZE 16768 +#define GL_FW_RLC_SIZE 8192 +#define GL_FW_SDMA_SIZE 4200 +#define GL_FW_SDMA1_SIZE 4200 + +#define MAX(x ,y) (((x) > (y)) ? (x) : (y)) +#define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) + +#define FW_CE_SIZE MAX_FW_SIZE(CE) +#define FW_ME_SIZE MAX_FW_SIZE(ME) +#define FW_MEC_SIZE MAX_FW_SIZE(MEC) +#define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) +#define FW_PFP_SIZE MAX_FW_SIZE(PFP) +#define FW_RLC_SIZE MAX_FW_SIZE(RLC) +#define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) +#define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) + +// Conservative value (max 113 bytes plus name size plus alignment) +#define CPIO_HEADER_SIZE 256 + +#define FW_HEADER_SIZE 256 + +// Leave space for 16 files (currently 12) +#define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ + FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ + FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ + FW_HEADER_SIZE * 8) + +ssize_t firmware_extract(void *dest); + +#endif diff --git a/linux/ps4-kexec-1250/kernel.c b/linux/ps4-kexec-1250/kernel.c new file mode 100644 index 0000000..b6d31b4 --- /dev/null +++ b/linux/ps4-kexec-1250/kernel.c @@ -0,0 +1,383 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "string.h" +#include "elf.h" +#include "x86.h" +#include "../magic.h" + +struct ksym_t kern; +int (*early_printf)(const char *fmt, ...) = NULL; + +#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) + +#ifdef NO_SYMTAB + +#define RESOLVE_NOERR(name) do { \ + if (kern_off_ ## name == 0) { \ + kern.name = 0; \ + } else { \ + kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ + } \ +} while (0); + +#define RESOLVE(name) do { \ + if (kern_off_ ## name == 0) { \ + return 0; \ + } \ + RESOLVE_NOERR(name) \ +} while (0); + +#else + +#define KERNSIZE 0x2000000 + +static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; +static Elf64_Sym *symtab; +static char *strtab; +static size_t strtab_size; + +static Elf64_Ehdr *find_kern_ehdr(void) +{ + // Search for the kernel copy embedded in ubios, then follow it to see + // where it was relocated to + for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; + if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { + for (size_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; + if (phdr->p_type == PT_PHDR) { + return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); + } + } + } + } + return NULL; +} + +static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf64_Dyn *)phdr->p_vaddr; + } + } + return NULL; +} + +static int elf_parse_dyn(Elf64_Dyn *dyn) +{ + for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_SYMTAB: + symtab = (Elf64_Sym *)dp->d_un.d_ptr; + break; + case DT_STRTAB: + strtab = (char *)dp->d_un.d_ptr; + break; + case DT_STRSZ: + strtab_size = dp->d_un.d_val; + break; + } + } + return symtab && strtab && strtab_size; +} + +void *kernel_resolve(const char *name) +{ + for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { + if (!strcmp(name, &strtab[sym->st_name])) { + eprintf("kern.%s = %p\n", name, (void*)sym->st_value); + return (void *)sym->st_value; + } + } + eprintf("Failed to resolve symbol '%s'\n", name); + return NULL; +} + +#define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) +#define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; + +#endif + +static int resolve_symbols(void) +{ + RESOLVE(printf); + RESOLVE(snprintf); + early_printf = kern.printf; + RESOLVE(copyin); + RESOLVE(copyout); + RESOLVE(copyinstr); + RESOLVE(kernel_map); + RESOLVE(kernel_pmap_store); + RESOLVE(kmem_alloc_contig); + RESOLVE(kmem_free); + RESOLVE(pmap_extract); + RESOLVE(pmap_protect); + RESOLVE(sysent); + RESOLVE(sched_pin); + RESOLVE(sched_unpin); + RESOLVE(smp_rendezvous); + RESOLVE(smp_no_rendevous_barrier); + RESOLVE(icc_query_nowait); + RESOLVE_NOERR(Starsha_UcodeInfo); + RESOLVE_NOERR(gpu_devid_is_9924); + RESOLVE_NOERR(gc_get_fw_info); + RESOLVE_NOERR(eap_hdd_key); + RESOLVE_NOERR(edid); + RESOLVE(wlanbt); + RESOLVE(kern_reboot); + RESOLVE(set_gpu_freq); + RESOLVE(set_pstate); + RESOLVE(update_vddnp); + RESOLVE(set_cu_power_gate); + RESOLVE_NOERR(pstate_before_shutdown); + return 1; +} + +#define M_WAITOK 0x0002 +#define M_ZERO 0x0100 + +#define VM_MEMATTR_DEFAULT 0x06 + +void *kernel_alloc_contig(size_t size) +{ + // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... + vm_offset_t ret = 0; + while(!(ret = kern.kmem_alloc_contig( + *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, + ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); + + /*if (!ret) { + kern.printf("Failed to allocate %zud bytes\n", size); + return NULL; + }*/ + return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); +} + +void kernel_free_contig(void *addr, size_t size) +{ + if (!addr) + return; + kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); +} + +int kernel_hook_install(void *target, void *hook) +{ + uintptr_t t = (uintptr_t)target; // addr to redirect to + uintptr_t h = (uintptr_t)hook; // place to write the thunk + + if (!hook || !target) { + return 0; + } + + kern.printf("kernel_hook_install(%p, %p)\n", target, hook); + + if (!(t & (1L << 63))) { + kern.printf("\n===================== WARNING =====================\n"); + kern.printf("hook target function address: %p\n", target); + kern.printf("It looks like we're running from userland memory.\n"); + kern.printf("Please run this code from a kernel memory mapping.\n\n"); + return 0; + } + s64 displacement = t - (h + 5); + + kern.sched_pin(); + u64 wp = write_protect_disable(); + if (displacement < -0x80000000 || displacement > 0x7fffffff) { + kern.printf(" Using 64bit absolute jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[2]; + s32 zero; + void *target; + } jmp = { + .op = { 0xff, 0x25 }, + .zero = 0, + .target = target, + }; + ASSERT_STRSIZE(struct jmp_t, 14); + memcpy(hook, &jmp, sizeof(jmp)); + } else { + kern.printf(" Using 32bit relative jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[1]; + s32 imm; + } jmp = { + .op = { 0xe9 }, + .imm = displacement, + }; + ASSERT_STRSIZE(struct jmp_t, 5); + memcpy(hook, &jmp, sizeof(jmp)); + } + wbinvd(); + write_protect_restore(wp); + kern.sched_unpin(); + + return 1; +} + +void kernel_syscall_install(int num, void *call, int narg) +{ + struct sysent_t *sy = &kern.sysent[num]; + + kern.sched_pin(); + u64 wp = write_protect_disable(); + + memset(sy, 0, sizeof(*sy)); + sy->sy_narg = narg; + sy->sy_call = call; + sy->sy_thrcnt = 1; + + write_protect_restore(wp); + kern.sched_unpin(); +} + +void kernel_remap(void *start, void *end, int perm) +{ + u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); + u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); + + kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); + kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); +} + +static volatile int _global_test = 0; + +#ifndef DO_NOT_REMAP_RWX +extern u8 _start[], _end[]; + +static int patch_pmap_check(void) +{ + u8 *p; + + for (p = (u8*)kern.pmap_protect; + p < ((u8*)kern.pmap_protect + 0x500); p++) { + #ifdef PS4_6_72 + if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 + p[5] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + /*#ifdef PS4_5_05 + if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { + p[1] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + }*/ + #else + if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { + p[2] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + #endif + } + kern.printf("pmap_protect patch failed!\n"); + return 0; +} +#endif + +int kernel_init(void *_early_printf) +{ + int rv = -1; + + if (_early_printf) + early_printf = _early_printf; + + eprintf("kernel_init()\n"); + +#ifdef KASLR + // use `early_printf` to calculate kernel base + if (early_printf == NULL) + return 0; + + kern.kern_base = (u64)(early_printf - kern_off_printf); + if ((kern.kern_base & PAGE_MASK) != 0) { + eprintf("Kernel base is not aligned\n"); + return 0; + } else { + eprintf("Kernel base = %llx\n", kern.kern_base); + } + + u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); + u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); + +#else + kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 + + u64 DMPML4I = 0x1fc; + u64 DMPDPI = 0; +#endif + + kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); + eprintf("Direct map base = %llx\n", kern.dmap_base); + + // We may not be mapped writable yet, so to be able to write to globals + // we need WP disabled. + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + +#ifndef NO_SYMTAB + Elf64_Ehdr *ehdr = find_kern_ehdr(); + if (!ehdr) { + eprintf("Could not find kernel ELF header\n"); + goto err; + } + eprintf("ELF header at %p\n", ehdr); + + Elf64_Dyn *dyn = elf_get_dyn(ehdr); + if (!dyn) { + eprintf("Could not find kernel dynamic header\n"); + goto err; + } + eprintf("ELF dynamic section at %p\n", dyn); + + if (!elf_parse_dyn(dyn)) { + eprintf("Failed to parse ELF dynamic section\n"); + goto err; + } +#endif + + if (!resolve_symbols()) { + eprintf("Failed to resolve all symbols\n"); + goto err; + } + + // Pin ourselves as soon as possible. This is expected to be released by the caller. + kern.sched_pin(); + +#ifndef DO_NOT_REMAP_RWX + if (!patch_pmap_check()) + goto err; +#endif + +#ifndef DO_NOT_REMAP_RWX + // kernel_remap may need interrupts, but may not write to globals! + enable_interrupts(); + kernel_remap(_start, _end, 7); + disable_interrupts(); +#endif + + // Writing to globals is now safe. + + kern.printf("Testing global variable access (write protection)...\n"); + _global_test = 1; + kern.printf("OK.\n"); + + kern.printf("Kernel interface initialized\n"); + rv = 0; + +err: + write_protect_restore(wp); + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1250/kernel.h b/linux/ps4-kexec-1250/kernel.h new file mode 100644 index 0000000..1833f5b --- /dev/null +++ b/linux/ps4-kexec-1250/kernel.h @@ -0,0 +1,126 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KERNEL_H +#define KERNEL_H + +#include "types.h" +#include "reboot.h" + +#define PAGE_SIZE 0x4000 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define PML4SHIFT 39 +#define PDPSHIFT 30 +#define PDRSHIFT 21 +#define PAGE_SHIFT 12 + +#define KVADDR(l4, l3, l2, l1) ( \ + ((unsigned long)-1 << 47) | \ + ((unsigned long)(l4) << PML4SHIFT) | \ + ((unsigned long)(l3) << PDPSHIFT) | \ + ((unsigned long)(l2) << PDRSHIFT) | \ + ((unsigned long)(l1) << PAGE_SHIFT)) + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) +#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX + +typedef u64 vm_paddr_t; +typedef u64 vm_offset_t; +typedef u64 vm_size_t; +typedef void * vm_map_t; +typedef char vm_memattr_t; +typedef void * pmap_t; + +typedef void (*smp_rendezvous_callback_t)(void *); + +struct sysent_t { + int sy_narg; + void *sy_call; + u16 sy_auevent; + void *sy_systrace_args_func; + int sy_entry; + int sy_return; + int sy_flags; + int sy_thrcnt; +}; + +struct ksym_t { + // two parameters related to kaslr (they are not symbols) + uintptr_t kern_base; + uintptr_t dmap_base; + + int (*printf)(const char *fmt, ...); + int (*snprintf)(const char *fmt, ...); + int (*copyin)(const void *uaddr, void *kaddr, size_t len); + int (*copyout)(const void *kaddr, void *uaddr, size_t len); + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); + + void **kernel_map; + void *kernel_pmap_store; + vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, + vm_paddr_t low, vm_paddr_t high, + unsigned long alignment, + unsigned long boundary, + vm_memattr_t memattr); + void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); + vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); + void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); + + struct sysent_t *sysent; + + void (*sched_pin)(void); + void (*sched_unpin)(void); + void (*smp_rendezvous)(smp_rendezvous_callback_t, + smp_rendezvous_callback_t, + smp_rendezvous_callback_t, void *); + // yes...it is misspelled :) + void (*smp_no_rendevous_barrier)(void *); + void *icc_query_nowait; + void *Starsha_UcodeInfo; + int (*gpu_devid_is_9924)(); + void *(*gc_get_fw_info)(); + void *eap_hdd_key; + void *edid; + void (*wlanbt)(unsigned int state); + int (*kern_reboot)(int magic); + void(*set_gpu_freq)(unsigned int num, unsigned int freq); + void(*set_pstate)(unsigned int val); + void(*update_vddnp)(unsigned int val); + void(*set_cu_power_gate)(unsigned int val); + void *pstate_before_shutdown; +}; + +extern struct ksym_t kern; + +static inline int curcpu(void) +{ + int cpuid; + // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw + asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); + return cpuid; +} + +// Assign a working printf function to this to debug the symbol resolver +extern int (*early_printf)(const char *fmt, ...); + +void *kernel_resolve(const char *name); + +void *kernel_alloc_contig(size_t size); +void kernel_free_contig(void *addr, size_t size); + +void kernel_remap(void *start, void *end, int perm); + +void kernel_syscall_install(int num, void *call, int narg); +int kernel_hook_install(void *target, void *hook); + +int kernel_init(void *early_printf); + +#endif diff --git a/linux/ps4-kexec-1250/kexec.c b/linux/ps4-kexec-1250/kexec.c new file mode 100644 index 0000000..d659229 --- /dev/null +++ b/linux/ps4-kexec-1250/kexec.c @@ -0,0 +1,229 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "linux_boot.h" +#include "x86.h" +#include "kexec.h" +#include "firmware.h" +#include "string.h" +#include "acpi.h" + +static int k_copyin(const void *uaddr, void *kaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(kaddr, uaddr, len); + return 0; +} + +static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + const char *ustr = (const char*)uaddr; + char *kstr = (char*)kaddr; + size_t ret; + if (!uaddr || !kaddr) + return EFAULT; + ret = strlcpy(kstr, ustr, len); + if (ret >= len) { + if (done) + *done = len; + return ENAMETOOLONG; + } else { + if (done) + *done = ret + 1; + } + return 0; +} + +static int k_copyout(const void *kaddr, void *uaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(uaddr, kaddr, len); + return 0; +} + +int sys_kexec(void *td, struct sys_kexec_args *uap) +{ + int err = 0; + size_t initramfs_size = uap->initramfs_size; + void *image = NULL; + void *initramfs = NULL; + size_t firmware_size = 0; + struct boot_params *bp = NULL; + size_t cmd_line_maxlen = 0; + char *cmd_line = NULL; + + int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; + int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; + + kern.printf("sys_kexec invoked\n"); + kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, + uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); + + // Look up our shutdown hook point + void *icc_query_nowait = kern.icc_query_nowait; + if (!icc_query_nowait) { + err = ENOENT; + goto cleanup; + } + + // Set gpu frequencies and pstate + // FAT&SLIM / PRO + kern.set_pstate(3); + + kern.set_gpu_freq(0, 800); //800 //800 + kern.set_gpu_freq(1, 673); //673 //853 + kern.set_gpu_freq(2, 609); //610 //711 + kern.set_gpu_freq(3, 800); //800 //800 + kern.set_gpu_freq(4, 800); //800 //911 + kern.set_gpu_freq(5, 711); //711 //800 + kern.set_gpu_freq(6, 711); //711 //984 + kern.set_gpu_freq(7, 673); //673 //673 + + kern.update_vddnp(0x12); + kern.set_cu_power_gate(0x12); + + // Copy in kernel image + image = kernel_alloc_contig(uap->image_size); + if (!image) { + kern.printf("Failed to allocate image\n"); + err = ENOMEM; + goto cleanup; + } + err = copyin(uap->image, image, uap->image_size); + if (err) { + kern.printf("Failed to copy in image\n"); + goto cleanup; + } + + // Copy in initramfs + initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); + if (!initramfs) { + kern.printf("Failed to allocate initramfs\n"); + err = ENOMEM; + goto cleanup; + } + + err = firmware_extract(((u8*)initramfs)); + if (err < 0) { + kern.printf("Failed to extract GPU firmware - continuing anyway\n"); + } else { + firmware_size = err; + } + + if (initramfs_size) { + err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); + if (err) { + kern.printf("Failed to copy in initramfs\n"); + goto cleanup; + } + } + initramfs_size += firmware_size; + + // Copy in cmdline + cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; + cmd_line = kernel_alloc_contig(cmd_line_maxlen); + if (!cmd_line) { + kern.printf("Failed to allocate cmdline\n"); + err = ENOMEM; + goto cleanup; + } + err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); + if (err) { + kern.printf("Failed to copy in cmdline\n"); + goto cleanup; + } + cmd_line[cmd_line_maxlen - 1] = 0; + + kern.printf("\nkexec parameters:\n"); + kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); + kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", + initramfs_size, uap->initramfs_size); + kern.printf(" Kernel command line: %s\n", cmd_line); + kern.printf(" Kernel image buffer: %p\n", image); + kern.printf(" Initramfs buffer: %p\n", initramfs); + + // Allocate our boot params + bp = kernel_alloc_contig(sizeof(*bp)); + if (!bp) { + kern.printf("Failed to allocate bp\n"); + err = ENOMEM; + goto cleanup; + } + + // Initialize bp + // TODO should probably do this from cpu_quiesce_gate, then bp doesn't + // need to be allocated here, just placed directly into low mem + set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); + + prepare_boot_params(bp, image); + + // Hook the final ICC shutdown function + if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { + kern.printf("Failed to install shutdown hook\n"); + err = EINVAL; + goto cleanup; + } + + kern.printf("******************************************************\n"); + kern.printf("kexec successfully armed. Please shut down the system.\n"); + kern.printf("******************************************************\n\n"); + +/* + kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); + if (kern.kern_reboot(RB_POWEROFF) == -1) + kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); +*/ + return 0; + +cleanup: + kernel_free_contig(cmd_line, cmd_line_maxlen); + kernel_free_contig(bp, sizeof(*bp)); + kernel_free_contig(image, uap->image_size); + kernel_free_contig(initramfs, uap->initramfs_size); + return err; + + copyout(NULL, NULL, 0); +} + +int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) +{ + int rv = 0; + + // potentially needed to write early_printf + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + + if (kernel_init(_early_printf) < 0) { + rv = -1; + goto cleanup; + } + + kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); + kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); + kern.printf("kexec_init() successful\n\n"); + + if (sys_kexec_ptr) + *sys_kexec_ptr = sys_kexec; + +cleanup: + write_protect_restore(wp); + if (kern.sched_unpin && wp & CR0_WP) { + // If we're returning to a state with WP enabled, assume the caller + // wants the thread unpinned. Else the caller is expected to + // call kern.sched_unpin() manually. + kern.sched_unpin(); + } + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1250/kexec.h b/linux/ps4-kexec-1250/kexec.h new file mode 100644 index 0000000..6c31637 --- /dev/null +++ b/linux/ps4-kexec-1250/kexec.h @@ -0,0 +1,38 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KEXEC_H +#define KEXEC_H + +#include "types.h" + +#define SYS_KEXEC 153 +#define SYS_KEXEC_NARGS 6 + +struct sys_kexec_args { + void *image; + size_t image_size; + void *initramfs; + size_t initramfs_size; + char *cmd_line; + int vram_gb; +}; + +typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); + +// Note: td is unused, you can pass NULL if you call this directly. +int sys_kexec(void *td, struct sys_kexec_args *uap); + +int kernel_init(void *early_printf); + +int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) + __attribute__ ((section (".init"))); + +#endif diff --git a/linux/ps4-kexec-1250/kexec.ld b/linux/ps4-kexec-1250/kexec.ld new file mode 100644 index 0000000..1bcf26c --- /dev/null +++ b/linux/ps4-kexec-1250/kexec.ld @@ -0,0 +1,25 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +ENTRY(kexec_init) + +SECTIONS { + /* We don't do GOT relocation and rely on nothing ending up using the GOT + * (-fno-common helps here) */ + /DISCARD/ : { *(.comment) *(.got) } + _start = .; + .init : { *(.init) *(.init.*) } + .text : { *(.text) *(.text.*) } + .data : { *(.data) *(.data.*) } + .rodata : { *(.rodata) *(.rodata.*) } + .bss : { *(.bss) *(.bss.*) *(COMMON)} + .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ + _end = .; +} diff --git a/linux/ps4-kexec-1250/linux_boot.c b/linux/ps4-kexec-1250/linux_boot.c new file mode 100644 index 0000000..197ef63 --- /dev/null +++ b/linux/ps4-kexec-1250/linux_boot.c @@ -0,0 +1,441 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "linux_boot.h" +#include "types.h" +#include "string.h" +#include "x86.h" +#include "kernel.h" +#include "uart.h" +#include "acpi.h" + +void uart_write_byte(u8 b); + +static u64 vram_base = 0x100000000; +// Current code assumes it's a power of two. +static u64 vram_size = 1024 * 1024 * 1024; +static int vram_gb = 2; + +#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) + +struct desc_ptr { + u16 limit; + u64 address; +} __attribute__((packed)); + +struct desc_struct { + u16 limit0; + u16 base0; + u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; + u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, + uintptr_t new_cr3, uintptr_t gdt_ptr); +extern uint8_t *jmp_to_linux; +extern size_t jmp_to_linux_size; + +// FreeBSD DMAP addresses +struct linux_boot_info { + void *linux_image; + void *initramfs; + size_t initramfs_size; + struct boot_params *bp; + char *cmd_line; +}; +static struct linux_boot_info nix_info; + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v) +{ + nix_info.linux_image = linux_image; + nix_info.bp = bp; + nix_info.initramfs = initramfs; + nix_info.initramfs_size = initramfs_size; + nix_info.cmd_line = cmd_line; + vram_gb = v; +} + +static volatile int halted_cpus = 0; + +static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, + u32 type) +{ + uint8_t idx = bp->e820_entries; + bp->e820_map[idx].addr = addr; + bp->e820_map[idx].size = size; + bp->e820_map[idx].type = type; + bp->e820_entries++; +} + +void prepare_boot_params(struct boot_params *bp, u8 *linux_image) +{ + memset(bp, 0, sizeof(struct boot_params)); + struct boot_params *bp_src = (struct boot_params *)linux_image; + memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + + ((u8 *)&bp_src->hdr.jump)[1]); + + // These values are from fw 1.01 + bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); + bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + // This used to be VRAM, but we reclaim it as RAM + bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); + // Instead, carve out VRAM from the beginning of high memory + bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, + SMAP_TYPE_MEMORY); +} + +#define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_FB_OFFSET 0x2068 +#define HDP_NONSURFACE_BASE 0x2c04 +#define CONFIG_MEMSIZE 0x5428 + +static void configure_vram(void) +{ + u64 mmio_base = 0xe4800000; + u64 fb_base = 0x0f00000000; + u64 fb_top = fb_base + vram_gb * vram_size - 1; + + WR32(mmio_base + MC_VM_FB_LOCATION, 0); + WR32(mmio_base + HDP_NONSURFACE_BASE, 0); + + WR32(mmio_base + MC_VM_FB_LOCATION, + ((fb_top >> 24) << 16) | (fb_base >> 24)); + WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); + WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); + WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); +} + +#define IA32_MTRR_DEF_TYPE 0x2ff +#define MTRR_BASE(i) (0x200 + 2*i) +#define MTRR_MASK(i) (0x201 + 2*i) + +static void setup_mtrr(void) +{ + disable_interrupts(); + u64 cr0 = cr0_read(); + cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); + wbinvd(); + cr3_write(cr3_read()); // TLB flush + + wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled + + // Low memory (0GB-2GB) = WB + wrmsr(MTRR_BASE(0), 0x0000000006); + wrmsr(MTRR_MASK(0), 0xff80000800); + // High memory (4GB-8GB) = WB + wrmsr(MTRR_BASE(1), 0x0100000006); + wrmsr(MTRR_MASK(1), 0xff00000800); + // High memory (8GB-10GB) = WB + wrmsr(MTRR_BASE(2), 0x0200000006); + wrmsr(MTRR_MASK(2), 0xff80000800); + // VRAM (4GB-4GB+vram_size) = UC + wrmsr(MTRR_BASE(3), 0x0100000000); + wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); + + wbinvd(); + cr3_write(cr3_read()); // TLB flush + wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable + cr0_write(cr0); + enable_interrupts(); +} + +static void cleanup_interrupts(void) +{ + int i; + disable_interrupts(); + + // Reset APIC stuff (per-CPU) + *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; + for (i = 0x320; i < 0x380; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + for (i = 0x480; i < 0x500; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; + for (i = 0x500; i < 0x540; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; + + // Fix the LVT offset for thresholding + wrmsr(0x413, (1L<<24) | (1L<<52)); + wrmsr(0xc0000408, (1L<<24) | (1L<<52)); +} + +static void cpu_quiesce_gate(void *arg) +{ + int i; + + // Ensure we can write anywhere + cr0_write(cr0_read() & ~CR0_WP); + + // Interrupt stuff local to each CPU + cleanup_interrupts(); + + // We want to set up MTRRs on all CPUs + setup_mtrr(); + + if (curcpu() != 0) { + // We're not on BSP. Try to halt. + __sync_fetch_and_add(&halted_cpus, 1); + cpu_stop(); + } + + uart_write_str("kexec: Waiting for secondary CPUs...\n"); + + // wait for all cpus to halt + while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); + + uart_write_str("kexec: Secondary CPUs quiesced\n"); + + //* Put ident mappings in current page tables + // Should not be needed, but maybe helps for debugging? + cr4_pge_disable(); + u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); + u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + // Clear (really) low mem. + // Linux reads from here to try and access EBDA... + // get_bios_ebda reads u16 from 0x40e + // reserve_ebda_region reads u16 from 0x413 + // Writing zeros causes linux to default to marking + // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. + // It doesn't match the ps4 e820 map, but that seems OK. + memset((void *)0, 0, 0x1000); + + // Create a new page table hierarchy out of the way of linux + // Accessed via freebsd direct map + pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt + // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| + memset(pml4_base, 0, 512 * sizeof(u64) * 2); + pdp_base = pml4_base + 512; + u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; + pml4_base[0] = pdpe; + // Maintain the freebsd direct map + pml4_base[DM_PML4_BASE] = pdpe; + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + uart_write_str("kexec: Setting up GDT...\n"); + + struct desc_ptr gdt_ptr; + struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); + gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; + gdt_ptr.address = DM_TO_ID(desc); + + // clear + memset(desc, 0, gdt_ptr.limit + 1); + // Most things are ignored in 64bit mode, and we will never be in + // 32bit/compat modes, so just setup another pure-64bit environment... + // Linux inits it's own GDT in secondary_startup_64 + // 0x10 + desc[2].limit0 = 0xffff; + desc[2].base0 = 0x0000; + desc[2].base1 = 0x0000; + desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; + desc[2].s = 1; + desc[2].dpl = 0; + desc[2].p = 1; + desc[2].limit = 0xf; + desc[2].avl = 0; + desc[2].l = 1; + desc[2].d = 0; + desc[2].g = 0; + desc[2].base2 = 0x00; + // 0x18 + desc[3].limit0 = 0xffff; + desc[3].base0 = 0x0000; + desc[3].base1 = 0x0000; + desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; + desc[3].s = 1; + desc[3].dpl = 0; + desc[3].p = 1; + desc[3].limit = 0xf; + desc[3].avl = 0; + desc[3].l = 0; + desc[3].d = 0; + desc[3].g = 0; + desc[3].base2 = 0x00; + // Task segment value + // 0x20 + desc[4].limit0 = 0x0000; + desc[4].base0 = 0x0000; + desc[4].base1 = 0x0000; + desc[4].type = SEG_TYPE_TSS; + desc[4].s = 1; + desc[4].dpl = 0; + desc[4].p = 1; + desc[4].limit = 0x0; + desc[4].avl = 0; + desc[4].l = 0; + desc[4].d = 0; + desc[4].g = 0; + desc[4].base2 = 0x00; + + uart_write_str("kexec: Relocating stub...\n"); + + // Relocate the stub and jump to it + // TODO should thunk_copy be DMAP here? + void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); + memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); + // XXX The +0x200 is for the iret stack in linux_thunk.S + uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; + + uart_write_str("kexec: Setting up boot params...\n"); + + // XXX we write into this bootargs and pass it to the kernel, but in + // jmp_to_linux we use the bootargs from the image as input. So they + // MUST MATCH! + struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; + *bp_lo = *nix_info.bp; + lowmem_pos += sizeof(struct boot_params); + + struct setup_header *shdr = &bp_lo->hdr; + shdr->cmd_line_ptr = lowmem_pos; + shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; + shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; + bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; + bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; + shdr->hardware_subarch = X86_SUBARCH_PS4; + // This needs to be nonzero for the initramfs to work + shdr->type_of_loader = 0xd0; // kexec + + strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, + nix_info.bp->hdr.cmdline_size); + lowmem_pos += strlen(nix_info.cmd_line) + 1; + + uart_write_str("kexec: Cleaning up hardware...\n"); + + // Disable IOMMU + *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; + + // Disable all MSIs on Aeolia + for (i = 0; i < 8; i++) + *(volatile u32 *)PA_TO_DM(0xd03c844c + i*4) = 0; + + // Stop HPET timers + *(volatile u64 *)PA_TO_DM(0xd0382010) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382100) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382120) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382140) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382160) = 0; + + uart_write_str("kexec: Reconfiguring VRAM...\n"); + + configure_vram(); + + uart_write_str("kexec: Resetting GPU...\n"); + + // Softreset GPU + *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks + *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC + *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 +// *(volatile u64 *)PA_TO_DM(0xe480d248) = 1; // Halt SDMA1 eeply + *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 + *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC + + *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply + +// *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x10003; // Softreset GFX/CP/RLC + *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x30005; // Softreset GFX/CP/RLC eeply + +// udelay(150); +// *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x10003; + *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x30005; //eeply +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) |= 0x00100140; // Softreset SDMA/GRBM +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~0x00100140; +// udelay(150); + + // Enable audio output + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; + *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; + +// // Set pin caps of pin 2 to vendor defined, to hide it +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// // Set pin caps of pin 3 to !HDMI +// *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; +// *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; + // Set pin configuration default + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; + + uart_write_str("kexec: About to relocate and jump to kernel\n"); + + ((jmp_to_linux_t)thunk_copy)( + DM_TO_ID(nix_info.linux_image), + DM_TO_ID(bp_lo), + DM_TO_ID(pml4_base), + (uintptr_t)&gdt_ptr + ); + + // should never reach here + uart_write_str("kexec: unreachable (?)\n"); +} + +// Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) +int hook_icc_query_nowait(u8 *icc_msg) +{ + kern.printf("hook_icc_query_nowait called\n"); + + // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot + //In alternative we can re-enable it here, but sometimes that give problems.. + kern.wlanbt(0x2); + + fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); + + kern.printf("ACPI tables fixed\n"); + + // Transition to BSP and halt other cpus + // smp_no_rendevous_barrier is just nullsub, but it is treated specially by + // smp_rendezvous. This is the easiest way to do this, since we can't assume + // we're already running on BSP. Since smp_rendezvous normally waits on all + // cpus to finish the callbacks, we just never return... + kern.smp_rendezvous(kern.smp_no_rendevous_barrier, + cpu_quiesce_gate, + kern.smp_no_rendevous_barrier, NULL); + + // should never reach here + kern.printf("hook_icc_query_nowait: unreachable (?)\n"); + return 0; +} diff --git a/linux/ps4-kexec-1250/linux_boot.h b/linux/ps4-kexec-1250/linux_boot.h new file mode 100644 index 0000000..2232b35 --- /dev/null +++ b/linux/ps4-kexec-1250/linux_boot.h @@ -0,0 +1,88 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef LINUX_BOOT_H +#define LINUX_BOOT_H + +#include "types.h" + +#define SMAP_TYPE_MEMORY 1 +#define SMAP_TYPE_RESERVED 2 +#define SMAP_TYPE_ACPI_RECLAIM 3 +#define SMAP_TYPE_ACPI_NVS 4 +#define SMAP_TYPE_UNUSABLE 5 +#define SMAP_TYPE_PMEM 7 + +#define X86_SUBARCH_PS4 5 + +struct e820entry { + u64 addr; /* start of memory segment */ + u64 size; /* size of memory segment */ + u32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct setup_header { + u8 setup_sects; + u16 root_flags; + u32 syssize; + u16 ram_size; + u16 vid_mode; + u16 root_dev; + u16 boot_flag; + u16 jump; + u32 header; + u16 version; + u32 realmode_swtch; + u16 start_sys; + u16 kernel_version; + u8 type_of_loader; + u8 loadflags; + u16 setup_move_size; + u32 code32_start; + u32 ramdisk_image; + u32 ramdisk_size; + u32 bootsect_kludge; + u16 heap_end_ptr; + u8 ext_loader_ver; + u8 ext_loader_type; + u32 cmd_line_ptr; + u32 initrd_addr_max; + u32 kernel_alignment; + u8 relocatable_kernel; + u8 min_alignment; + u16 xloadflags; + u32 cmdline_size; + u32 hardware_subarch; + u64 hardware_subarch_data; + u32 payload_offset; + u32 payload_length; + u64 setup_data; + u64 pref_address; + u32 init_size; + u32 handover_offset; +} __attribute__((packed)); + +#define E820MAX 128 /* number of entries in E820MAP */ + +OSTRUCT(boot_params, 0x1000) +OFIELD(0x0c0, u32 ext_ramdisk_image); +OFIELD(0x0c4, u32 ext_ramdisk_size); +OFIELD(0x0c8, u32 ext_cmd_line_ptr); +OFIELD(0x1e8, u8 e820_entries); +OFIELD(0x1f1, struct setup_header hdr); +OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); +OSTRUCT_END + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v); +void prepare_boot_params(struct boot_params *bp, u8 *linux_image); +int hook_icc_query_nowait(u8 *icc_msg); + +#endif diff --git a/linux/ps4-kexec-1250/linux_thunk.S b/linux/ps4-kexec-1250/linux_thunk.S new file mode 100644 index 0000000..f6d2dfb --- /dev/null +++ b/linux/ps4-kexec-1250/linux_thunk.S @@ -0,0 +1,90 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +.intel_syntax noprefix + +.equ setup_sects, 0x1f1 +.equ shdr_syssize, 0x1f4 +.equ pref_address, 0x258 + +.text + +#void jmp_to_linux( +# uintptr_t image_base, rdi +# uintptr_t bootargs, rsi +# uintptr_t new_cr3, rdx +# uintptr_t gdt_ptr rcx +#); +.globl jmp_to_linux +jmp_to_linux: + # switch to new gdt + data segments + cli + lgdt [rcx] + #xor eax, eax + mov eax, 0x18 + mov ds, eax + mov ss, eax + mov es, eax + mov fs, eax + mov gs, eax + + # switch to our own page tables (in low mem) + mov cr3, rdx + + # now we're on our own page tables, so we can obliterate the rest of memory + # TODO make sure we don't inadvertently overwrite (important) smap regions + # I think on ps4 we'll actually want to load to 0x700000 + # since we have tons of free room there. + # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We + # should choose a higher address as pref_address. + + # save args + mov r12, rdi + mov r13, rsi + + # memmove(pref_address, , (syssize * 0x10) / 8) + #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go + #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) + mov rdi, 0x6000000 # should be far from bzImage and initramfs + mov r14, rdi # r14 = pref_address + xor edx, edx + mov dl, [r12 + setup_sects] + inc rdx + shl rdx, 9 # rdx = offsetof(image_base, startup_32) + lea rsi, [r12 + rdx] # src = image_base + startup_32 + mov ecx, [r12 + shdr_syssize] + shl rcx, 4 + add rdi, rcx + add rsi, rcx + sub rdi, 8 + sub rsi, 8 + shr rcx, 3 + std + rep movsq + cld + + # make a tiny stack - we just need it for the lretq. + # what we jump to will not use this stack + lea rsp, [rip + jmp_to_linux_end + 0x200] + and rsp, -0x10 + #push 0 # retaddr + push 0x10 # cs = GDT[2] + add r14, 0x200 # pref_address + startup_64 + push r14 # rip + mov rsi, r13 # bootargs + lretq +jmp_to_linux_end: + +.data + +.globl jmp_to_linux_size +jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux + +.att_syntax prefix diff --git a/linux/ps4-kexec-1250/reboot.h b/linux/ps4-kexec-1250/reboot.h new file mode 100644 index 0000000..20b91f8 --- /dev/null +++ b/linux/ps4-kexec-1250/reboot.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)reboot.h 8.3 (Berkeley) 12/13/94 + * $FreeBSD$ + */ + +#ifndef _SYS_REBOOT_H_ +#define _SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. These are passed to + * the boot program and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ +#define RB_SINGLE 0x002 /* reboot to single user only */ +#define RB_NOSYNC 0x004 /* dont sync before reboot */ +#define RB_HALT 0x008 /* don't reboot, just halt */ +#define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ +#define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ +#define RB_KDB 0x040 /* give control to kernel debugger */ +#define RB_RDONLY 0x080 /* mount root fs read-only */ +#define RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define RB_MINIROOT 0x200 /* Unused placeholder */ +#define RB_VERBOSE 0x800 /* print all potentially useful info */ +#define RB_SERIAL 0x1000 /* use serial port as console */ +#define RB_CDROM 0x2000 /* use cdrom as root */ +#define RB_POWEROFF 0x4000 /* turn the power off if possible */ +#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ +#define RB_MUTE 0x10000 /* start up with the console muted */ +#define RB_SELFTEST 0x20000 /* unused placeholder */ +#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ +#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ +#define RB_PAUSE 0x100000 /* pause after each output line during probe */ +#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ +#define RB_PROBE 0x10000000 /* Probe multiple consoles */ +#define RB_MULTIPLE 0x20000000 /* use multiple consoles */ + +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + +#endif diff --git a/linux/ps4-kexec-1250/string.h b/linux/ps4-kexec-1250/string.h new file mode 100644 index 0000000..70aa668 --- /dev/null +++ b/linux/ps4-kexec-1250/string.h @@ -0,0 +1,96 @@ +/* + * string.h -- standard C string-manipulation functions. + * + * Copyright (C) 2008 Segher Boessenkool + * Copyright (C) 2009 Haxx Enterprises + * Copyright (C) 2010-2016 Hector Martin "marcan" + * + * Portions taken from the Public Domain C Library (PDCLib). + * http://pdclib.rootdirectory.de/ + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +static inline int strcmp(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s1[i] == s2[i]; i++) + ; + + return s1[i] - s2[i]; +} + +static inline void *memset(void *b, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)b)[i] = c; + + return b; +} + +static inline void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} + +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) + return p1[i] - p2[i]; + + return 0; +} + +static inline size_t strlen(const char *s) +{ + size_t len; + + for (len = 0; s[len]; len++) + ; + + return len; +} + +static inline size_t strnlen(const char *s, size_t count) +{ + size_t len; + + for (len = 0; s[len] && len < count; len++) + ; + + return len; +} + +static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) +{ + size_t len, needed; + + len = needed = strnlen(src, maxlen - 1) + 1; + if (len >= maxlen) + len = maxlen - 1; + + memcpy(dest, src, len); + dest[len] = 0; + + return needed - 1; +} + +#endif diff --git a/linux/ps4-kexec-1250/types.h b/linux/ps4-kexec-1250/types.h new file mode 100644 index 0000000..28d6619 --- /dev/null +++ b/linux/ps4-kexec-1250/types.h @@ -0,0 +1,51 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef TYPES_H +#define TYPES_H + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#ifndef TESTING +typedef u8 uint8_t; +typedef u64 size_t; +typedef s64 ssize_t; +typedef u64 uintptr_t; +typedef s64 off_t; +#endif + +#define NULL ((void *)0) + +#define CAT_(x, y) x ## y +#define CAT(x, y) CAT_(x, y) + +#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] +#define OSTRUCT(name, size) struct name { union { OPAD(size); +#define OSTRUCT_END };}; +#define OFIELD(off, field) struct { OPAD(off); field; } + +#define ASSERT_STRSIZE(struc, size) \ + _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#define ENOENT 2 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define ENAMETOOLONG 63 + +#endif diff --git a/linux/ps4-kexec-1250/uart.c b/linux/ps4-kexec-1250/uart.c new file mode 100644 index 0000000..62ea949 --- /dev/null +++ b/linux/ps4-kexec-1250/uart.c @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "uart.h" +#include "kernel.h" +#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) + +#define AEOLIA_UART_BASE 0xD0340000 +#define BAIKAL_UART_BASE 0xC890E000 + +#define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, AEOLIA_UART_BASE + (intf << 12) + (reg << 2)) + +#define UART_REG_DATA 0 +#define UART_REG_IER 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +# define LSR_TXRDY 0x20 +# define LSR_TEMT 0x40 +#define UART_REG_MSR 6 +#define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) +#define UART0_IER UART_REG(32, 0, UART_REG_IER) +#define UART0_IIR UART_REG(32, 0, UART_REG_IIR) +#define UART0_LCR UART_REG(32, 0, UART_REG_LCR) +#define UART0_MCR UART_REG(32, 0, UART_REG_MCR) +#define UART0_LSR UART_REG(32, 0, UART_REG_LSR) +#define UART0_MSR UART_REG(32, 0, UART_REG_MSR) + +void uart_write_byte(u8 b) +{ + int limit; + u64 barrier; + limit = 250000; + while (!(*UART0_LSR & LSR_TXRDY) && --limit) + ; + *UART0_DATA = b; + __sync_fetch_and_add(&barrier, 0); + limit = 250000; + while (!(*UART0_LSR & LSR_TEMT) && --limit) + ; +} + +void uart_write_char(char c) +{ + if (c == '\n') + uart_write_byte('\r'); + + uart_write_byte(c); +} + +void uart_write_str(const char *s) +{ + while (*s) { + uart_write_char(*s++); + } +} diff --git a/linux/ps4-kexec-1250/uart.h b/linux/ps4-kexec-1250/uart.h new file mode 100644 index 0000000..b803e29 --- /dev/null +++ b/linux/ps4-kexec-1250/uart.h @@ -0,0 +1,20 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef UART_H +#define UART_H + +#include "types.h" + +void uart_write_byte(u8 b); +void uart_write_char(char c); +void uart_write_str(const char *s); + +#endif diff --git a/linux/ps4-kexec-1250/x86.h b/linux/ps4-kexec-1250/x86.h new file mode 100644 index 0000000..61b22ff --- /dev/null +++ b/linux/ps4-kexec-1250/x86.h @@ -0,0 +1,149 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef X86_H +#define X86_H + +#define FLAGS_IF (1 << 9) + +#define CR0_WP (1 << 16) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) + +#define PG_V (1 << 0) +#define PG_RW (1 << 1) +#define PG_U (1 << 2) +#define PG_PS (1 << 7) + +#define SEG_TYPE_DATA (0 << 3) +#define SEG_TYPE_READ_WRITE (1 << 1) +#define SEG_TYPE_CODE (1 << 3) +#define SEG_TYPE_EXEC_READ (1 << 1) +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) + +static inline u64 cr0_read(void) +{ + u64 reg; + asm volatile("mov %0, cr0;" : "=r" (reg)); + return reg; +} + +static inline void cr0_write(u64 val) +{ + asm volatile("mov cr0, %0;" :: "r" (val)); +} + +static inline u64 write_protect_disable() +{ + u64 cr0 = cr0_read(); + cr0_write(cr0 & ~CR0_WP); + return cr0; +} + +static inline void write_protect_restore(u64 cr0) +{ + // Use only WP bit of input + cr0_write(cr0_read() | (cr0 & CR0_WP)); +} + +static inline u64 cr3_read(void) +{ + u64 reg; + asm volatile("mov %0, cr3;" : "=r" (reg)); + return reg; +} + +static inline void cr3_write(u64 val) +{ + asm volatile("mov cr3, %0;" :: "r" (val)); +} + +static inline void cr4_pge_disable(void) +{ + u64 cr4_temp; + asm volatile( + "mov %0, cr4;" + "and %0, ~0x80;" + "mov cr4, %0;" + : "=r" (cr4_temp) + ); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +static inline void cpu_stop(void) +{ + for (;;) + asm volatile("cli; hlt;" : : : "memory"); +} + +static inline void outl(int port, unsigned int data) +{ + asm volatile("out %w1, %0" : : "a" (data), "d" (port)); +} + +static inline void wrmsr(u32 msr_id, u64 msr_value) +{ + asm volatile( + "wrmsr" + : + : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) + ); +} + +static inline u64 rdtsc (void) +{ + unsigned int tickl, tickh; + asm volatile( + "rdtsc" + :"=a"(tickl),"=d"(tickh) + ); + return ((u64)tickh << 32) | tickl; +} + +static inline void udelay(unsigned int usec) { + u64 later = rdtsc() + usec * 1594ULL; + while (((s64)(later - rdtsc())) > 0); +} + +static inline void disable_interrupts(void) +{ + asm volatile("cli"); +} + +static inline void enable_interrupts(void) +{ + asm volatile("sti"); +} + +static inline u64 read_flags(void) +{ + u64 flags; + asm volatile("pushf; pop %0;" : "=r" (flags)); + return flags; +} + +static inline u64 intr_disable(void) +{ + u64 flags = read_flags(); + disable_interrupts(); + return flags; +} + +static inline void intr_restore(u64 flags) +{ + // TODO should only IF be or'd in? + asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); +} + +#endif From e82e5130d5439bc76d65482197bd26974a107ec2 Mon Sep 17 00:00:00 2001 From: ArabPixel Date: Wed, 31 Dec 2025 20:19:50 +0100 Subject: [PATCH 2/4] 12.5x fixes --- linux/magic.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/magic.h b/linux/magic.h index c1148c2..2636eb4 100644 --- a/linux/magic.h +++ b/linux/magic.h @@ -458,7 +458,7 @@ #define kern_off_copyout 0x2BD5C0 #define kern_off_copyinstr 0x2BDB60 #define kern_off_kmem_alloc_contig 0x24D410 -#define kern_off_kmem_free 0x465BF4 +#define kern_off_kmem_free 0x465BF0 #define kern_off_pmap_extract 0x573D0 #define kern_off_pmap_protect 0x58570 #define kern_off_sched_pin 0x231640 @@ -483,7 +483,7 @@ #define kern_off_set_pstate 0x4BBE40 #define kern_off_update_vddnp 0x4BA010 #define kern_off_set_cu_power_gate 0x4BA420 -#define kern_off_pstate_before_shutdown 0x3A239F +#define kern_off_pstate_before_shutdown 0x3A23A0 #define kern_off_set_nclk_mem_spd 0 #endif From 3d7a5456b75c8e2e1556cfbe1327a21464ebe02e Mon Sep 17 00:00:00 2001 From: ArabPixel Date: Wed, 31 Dec 2025 21:16:09 +0100 Subject: [PATCH 3/4] 12.5x fixes and 13.0x support --- README.md | 8 +- freebsd-headers/ps4-offsets/1300.h | 14 + freebsd-headers/ps4-offsets/1302.h | 14 + freebsd-headers/ps4-offsets/kernel.h | 8 + linux/Makefile | 126 ++++- linux/fw1300/.keepgithub | 1 + linux/fw1302/.keepgithub | 1 + linux/magic.h | 70 +++ linux/main-baikal.c | 6 + linux/main.c | 6 + linux/ps4-kexec-1300-baikal/LICENSE | 24 + linux/ps4-kexec-1300-baikal/Makefile | 37 ++ linux/ps4-kexec-1300-baikal/README.md | 121 +++++ linux/ps4-kexec-1300-baikal/acpi.c | 320 ++++++++++++ linux/ps4-kexec-1300-baikal/acpi.h | 22 + linux/ps4-kexec-1300-baikal/acpi_caps.h | 30 ++ linux/ps4-kexec-1300-baikal/crc32.c | 102 ++++ linux/ps4-kexec-1300-baikal/crc32.h | 7 + linux/ps4-kexec-1300-baikal/elf.h | 71 +++ linux/ps4-kexec-1300-baikal/firmware.c | 479 ++++++++++++++++++ linux/ps4-kexec-1300-baikal/firmware.h | 64 +++ linux/ps4-kexec-1300-baikal/kernel.c | 383 ++++++++++++++ linux/ps4-kexec-1300-baikal/kernel.h | 126 +++++ linux/ps4-kexec-1300-baikal/kexec.c | 229 +++++++++ linux/ps4-kexec-1300-baikal/kexec.h | 38 ++ linux/ps4-kexec-1300-baikal/kexec.ld | 25 + linux/ps4-kexec-1300-baikal/linux_boot.c | 453 +++++++++++++++++ linux/ps4-kexec-1300-baikal/linux_boot.h | 88 ++++ linux/ps4-kexec-1300-baikal/linux_thunk.S | 90 ++++ linux/ps4-kexec-1300-baikal/reboot.h | 71 +++ linux/ps4-kexec-1300-baikal/string.h | 96 ++++ linux/ps4-kexec-1300-baikal/types.h | 51 ++ linux/ps4-kexec-1300-baikal/uart.c | 64 +++ linux/ps4-kexec-1300-baikal/uart.h | 20 + linux/ps4-kexec-1300-baikal/x86.h | 195 +++++++ linux/ps4-kexec-1300-pro-baikal/LICENSE | 24 + linux/ps4-kexec-1300-pro-baikal/Makefile | 37 ++ linux/ps4-kexec-1300-pro-baikal/README.md | 121 +++++ linux/ps4-kexec-1300-pro-baikal/acpi.c | 320 ++++++++++++ linux/ps4-kexec-1300-pro-baikal/acpi.h | 22 + linux/ps4-kexec-1300-pro-baikal/acpi_caps.h | 30 ++ linux/ps4-kexec-1300-pro-baikal/crc32.c | 102 ++++ linux/ps4-kexec-1300-pro-baikal/crc32.h | 7 + linux/ps4-kexec-1300-pro-baikal/elf.h | 71 +++ linux/ps4-kexec-1300-pro-baikal/firmware.c | 479 ++++++++++++++++++ linux/ps4-kexec-1300-pro-baikal/firmware.h | 64 +++ linux/ps4-kexec-1300-pro-baikal/kernel.c | 383 ++++++++++++++ linux/ps4-kexec-1300-pro-baikal/kernel.h | 126 +++++ linux/ps4-kexec-1300-pro-baikal/kexec.c | 229 +++++++++ linux/ps4-kexec-1300-pro-baikal/kexec.h | 38 ++ linux/ps4-kexec-1300-pro-baikal/kexec.ld | 25 + linux/ps4-kexec-1300-pro-baikal/linux_boot.c | 453 +++++++++++++++++ linux/ps4-kexec-1300-pro-baikal/linux_boot.h | 88 ++++ linux/ps4-kexec-1300-pro-baikal/linux_thunk.S | 90 ++++ linux/ps4-kexec-1300-pro-baikal/reboot.h | 71 +++ linux/ps4-kexec-1300-pro-baikal/string.h | 96 ++++ linux/ps4-kexec-1300-pro-baikal/types.h | 51 ++ linux/ps4-kexec-1300-pro-baikal/uart.c | 64 +++ linux/ps4-kexec-1300-pro-baikal/uart.h | 20 + linux/ps4-kexec-1300-pro-baikal/x86.h | 195 +++++++ linux/ps4-kexec-1300-pro/LICENSE | 24 + linux/ps4-kexec-1300-pro/Makefile | 37 ++ linux/ps4-kexec-1300-pro/README.md | 121 +++++ linux/ps4-kexec-1300-pro/acpi.c | 296 +++++++++++ linux/ps4-kexec-1300-pro/acpi.h | 18 + linux/ps4-kexec-1300-pro/crc32.c | 102 ++++ linux/ps4-kexec-1300-pro/crc32.h | 7 + linux/ps4-kexec-1300-pro/elf.h | 71 +++ linux/ps4-kexec-1300-pro/firmware.c | 479 ++++++++++++++++++ linux/ps4-kexec-1300-pro/firmware.h | 64 +++ linux/ps4-kexec-1300-pro/kernel.c | 383 ++++++++++++++ linux/ps4-kexec-1300-pro/kernel.h | 126 +++++ linux/ps4-kexec-1300-pro/kexec.c | 229 +++++++++ linux/ps4-kexec-1300-pro/kexec.h | 38 ++ linux/ps4-kexec-1300-pro/kexec.ld | 25 + linux/ps4-kexec-1300-pro/linux_boot.c | 441 ++++++++++++++++ linux/ps4-kexec-1300-pro/linux_boot.h | 88 ++++ linux/ps4-kexec-1300-pro/linux_thunk.S | 90 ++++ linux/ps4-kexec-1300-pro/reboot.h | 71 +++ linux/ps4-kexec-1300-pro/string.h | 96 ++++ linux/ps4-kexec-1300-pro/types.h | 51 ++ linux/ps4-kexec-1300-pro/uart.c | 64 +++ linux/ps4-kexec-1300-pro/uart.h | 20 + linux/ps4-kexec-1300-pro/x86.h | 149 ++++++ linux/ps4-kexec-1300/LICENSE | 24 + linux/ps4-kexec-1300/Makefile | 37 ++ linux/ps4-kexec-1300/README.md | 121 +++++ linux/ps4-kexec-1300/acpi.c | 296 +++++++++++ linux/ps4-kexec-1300/acpi.h | 18 + linux/ps4-kexec-1300/crc32.c | 102 ++++ linux/ps4-kexec-1300/crc32.h | 7 + linux/ps4-kexec-1300/elf.h | 71 +++ linux/ps4-kexec-1300/firmware.c | 479 ++++++++++++++++++ linux/ps4-kexec-1300/firmware.h | 64 +++ linux/ps4-kexec-1300/kernel.c | 383 ++++++++++++++ linux/ps4-kexec-1300/kernel.h | 126 +++++ linux/ps4-kexec-1300/kexec.c | 229 +++++++++ linux/ps4-kexec-1300/kexec.h | 38 ++ linux/ps4-kexec-1300/kexec.ld | 25 + linux/ps4-kexec-1300/linux_boot.c | 441 ++++++++++++++++ linux/ps4-kexec-1300/linux_boot.h | 88 ++++ linux/ps4-kexec-1300/linux_thunk.S | 90 ++++ linux/ps4-kexec-1300/reboot.h | 71 +++ linux/ps4-kexec-1300/string.h | 96 ++++ linux/ps4-kexec-1300/types.h | 51 ++ linux/ps4-kexec-1300/uart.c | 64 +++ linux/ps4-kexec-1300/uart.h | 20 + linux/ps4-kexec-1300/x86.h | 149 ++++++ linux/ps4-kexec-1302-baikal/LICENSE | 24 + linux/ps4-kexec-1302-baikal/Makefile | 37 ++ linux/ps4-kexec-1302-baikal/README.md | 121 +++++ linux/ps4-kexec-1302-baikal/acpi.c | 320 ++++++++++++ linux/ps4-kexec-1302-baikal/acpi.h | 22 + linux/ps4-kexec-1302-baikal/acpi_caps.h | 30 ++ linux/ps4-kexec-1302-baikal/crc32.c | 102 ++++ linux/ps4-kexec-1302-baikal/crc32.h | 7 + linux/ps4-kexec-1302-baikal/elf.h | 71 +++ linux/ps4-kexec-1302-baikal/firmware.c | 479 ++++++++++++++++++ linux/ps4-kexec-1302-baikal/firmware.h | 64 +++ linux/ps4-kexec-1302-baikal/kernel.c | 383 ++++++++++++++ linux/ps4-kexec-1302-baikal/kernel.h | 126 +++++ linux/ps4-kexec-1302-baikal/kexec.c | 229 +++++++++ linux/ps4-kexec-1302-baikal/kexec.h | 38 ++ linux/ps4-kexec-1302-baikal/kexec.ld | 25 + linux/ps4-kexec-1302-baikal/linux_boot.c | 453 +++++++++++++++++ linux/ps4-kexec-1302-baikal/linux_boot.h | 88 ++++ linux/ps4-kexec-1302-baikal/linux_thunk.S | 90 ++++ linux/ps4-kexec-1302-baikal/reboot.h | 71 +++ linux/ps4-kexec-1302-baikal/string.h | 96 ++++ linux/ps4-kexec-1302-baikal/types.h | 51 ++ linux/ps4-kexec-1302-baikal/uart.c | 64 +++ linux/ps4-kexec-1302-baikal/uart.h | 20 + linux/ps4-kexec-1302-baikal/x86.h | 195 +++++++ linux/ps4-kexec-1302-pro-baikal/LICENSE | 24 + linux/ps4-kexec-1302-pro-baikal/Makefile | 37 ++ linux/ps4-kexec-1302-pro-baikal/README.md | 121 +++++ linux/ps4-kexec-1302-pro-baikal/acpi.c | 320 ++++++++++++ linux/ps4-kexec-1302-pro-baikal/acpi.h | 22 + linux/ps4-kexec-1302-pro-baikal/acpi_caps.h | 30 ++ linux/ps4-kexec-1302-pro-baikal/crc32.c | 102 ++++ linux/ps4-kexec-1302-pro-baikal/crc32.h | 7 + linux/ps4-kexec-1302-pro-baikal/elf.h | 71 +++ linux/ps4-kexec-1302-pro-baikal/firmware.c | 479 ++++++++++++++++++ linux/ps4-kexec-1302-pro-baikal/firmware.h | 64 +++ linux/ps4-kexec-1302-pro-baikal/kernel.c | 383 ++++++++++++++ linux/ps4-kexec-1302-pro-baikal/kernel.h | 126 +++++ linux/ps4-kexec-1302-pro-baikal/kexec.c | 229 +++++++++ linux/ps4-kexec-1302-pro-baikal/kexec.h | 38 ++ linux/ps4-kexec-1302-pro-baikal/kexec.ld | 25 + linux/ps4-kexec-1302-pro-baikal/linux_boot.c | 453 +++++++++++++++++ linux/ps4-kexec-1302-pro-baikal/linux_boot.h | 88 ++++ linux/ps4-kexec-1302-pro-baikal/linux_thunk.S | 90 ++++ linux/ps4-kexec-1302-pro-baikal/reboot.h | 71 +++ linux/ps4-kexec-1302-pro-baikal/string.h | 96 ++++ linux/ps4-kexec-1302-pro-baikal/types.h | 51 ++ linux/ps4-kexec-1302-pro-baikal/uart.c | 64 +++ linux/ps4-kexec-1302-pro-baikal/uart.h | 20 + linux/ps4-kexec-1302-pro-baikal/x86.h | 195 +++++++ linux/ps4-kexec-1302-pro/LICENSE | 24 + linux/ps4-kexec-1302-pro/Makefile | 37 ++ linux/ps4-kexec-1302-pro/README.md | 121 +++++ linux/ps4-kexec-1302-pro/acpi.c | 296 +++++++++++ linux/ps4-kexec-1302-pro/acpi.h | 18 + linux/ps4-kexec-1302-pro/crc32.c | 102 ++++ linux/ps4-kexec-1302-pro/crc32.h | 7 + linux/ps4-kexec-1302-pro/elf.h | 71 +++ linux/ps4-kexec-1302-pro/firmware.c | 479 ++++++++++++++++++ linux/ps4-kexec-1302-pro/firmware.h | 64 +++ linux/ps4-kexec-1302-pro/kernel.c | 383 ++++++++++++++ linux/ps4-kexec-1302-pro/kernel.h | 126 +++++ linux/ps4-kexec-1302-pro/kexec.c | 229 +++++++++ linux/ps4-kexec-1302-pro/kexec.h | 38 ++ linux/ps4-kexec-1302-pro/kexec.ld | 25 + linux/ps4-kexec-1302-pro/linux_boot.c | 441 ++++++++++++++++ linux/ps4-kexec-1302-pro/linux_boot.h | 88 ++++ linux/ps4-kexec-1302-pro/linux_thunk.S | 90 ++++ linux/ps4-kexec-1302-pro/reboot.h | 71 +++ linux/ps4-kexec-1302-pro/string.h | 96 ++++ linux/ps4-kexec-1302-pro/types.h | 51 ++ linux/ps4-kexec-1302-pro/uart.c | 64 +++ linux/ps4-kexec-1302-pro/uart.h | 20 + linux/ps4-kexec-1302-pro/x86.h | 149 ++++++ linux/ps4-kexec-1302/LICENSE | 24 + linux/ps4-kexec-1302/Makefile | 37 ++ linux/ps4-kexec-1302/README.md | 121 +++++ linux/ps4-kexec-1302/acpi.c | 296 +++++++++++ linux/ps4-kexec-1302/acpi.h | 18 + linux/ps4-kexec-1302/crc32.c | 102 ++++ linux/ps4-kexec-1302/crc32.h | 7 + linux/ps4-kexec-1302/elf.h | 71 +++ linux/ps4-kexec-1302/firmware.c | 479 ++++++++++++++++++ linux/ps4-kexec-1302/firmware.h | 64 +++ linux/ps4-kexec-1302/kernel.c | 383 ++++++++++++++ linux/ps4-kexec-1302/kernel.h | 126 +++++ linux/ps4-kexec-1302/kexec.c | 229 +++++++++ linux/ps4-kexec-1302/kexec.h | 38 ++ linux/ps4-kexec-1302/kexec.ld | 25 + linux/ps4-kexec-1302/linux_boot.c | 441 ++++++++++++++++ linux/ps4-kexec-1302/linux_boot.h | 88 ++++ linux/ps4-kexec-1302/linux_thunk.S | 90 ++++ linux/ps4-kexec-1302/reboot.h | 71 +++ linux/ps4-kexec-1302/string.h | 96 ++++ linux/ps4-kexec-1302/types.h | 51 ++ linux/ps4-kexec-1302/uart.c | 64 +++ linux/ps4-kexec-1302/uart.h | 20 + linux/ps4-kexec-1302/x86.h | 149 ++++++ 206 files changed, 25432 insertions(+), 6 deletions(-) create mode 100644 freebsd-headers/ps4-offsets/1300.h create mode 100644 freebsd-headers/ps4-offsets/1302.h create mode 100644 linux/fw1300/.keepgithub create mode 100644 linux/fw1302/.keepgithub create mode 100644 linux/ps4-kexec-1300-baikal/LICENSE create mode 100644 linux/ps4-kexec-1300-baikal/Makefile create mode 100644 linux/ps4-kexec-1300-baikal/README.md create mode 100644 linux/ps4-kexec-1300-baikal/acpi.c create mode 100644 linux/ps4-kexec-1300-baikal/acpi.h create mode 100644 linux/ps4-kexec-1300-baikal/acpi_caps.h create mode 100644 linux/ps4-kexec-1300-baikal/crc32.c create mode 100644 linux/ps4-kexec-1300-baikal/crc32.h create mode 100644 linux/ps4-kexec-1300-baikal/elf.h create mode 100644 linux/ps4-kexec-1300-baikal/firmware.c create mode 100644 linux/ps4-kexec-1300-baikal/firmware.h create mode 100644 linux/ps4-kexec-1300-baikal/kernel.c create mode 100644 linux/ps4-kexec-1300-baikal/kernel.h create mode 100644 linux/ps4-kexec-1300-baikal/kexec.c create mode 100644 linux/ps4-kexec-1300-baikal/kexec.h create mode 100644 linux/ps4-kexec-1300-baikal/kexec.ld create mode 100644 linux/ps4-kexec-1300-baikal/linux_boot.c create mode 100644 linux/ps4-kexec-1300-baikal/linux_boot.h create mode 100644 linux/ps4-kexec-1300-baikal/linux_thunk.S create mode 100644 linux/ps4-kexec-1300-baikal/reboot.h create mode 100644 linux/ps4-kexec-1300-baikal/string.h create mode 100644 linux/ps4-kexec-1300-baikal/types.h create mode 100644 linux/ps4-kexec-1300-baikal/uart.c create mode 100644 linux/ps4-kexec-1300-baikal/uart.h create mode 100644 linux/ps4-kexec-1300-baikal/x86.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/LICENSE create mode 100644 linux/ps4-kexec-1300-pro-baikal/Makefile create mode 100644 linux/ps4-kexec-1300-pro-baikal/README.md create mode 100644 linux/ps4-kexec-1300-pro-baikal/acpi.c create mode 100644 linux/ps4-kexec-1300-pro-baikal/acpi.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/acpi_caps.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/crc32.c create mode 100644 linux/ps4-kexec-1300-pro-baikal/crc32.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/elf.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/firmware.c create mode 100644 linux/ps4-kexec-1300-pro-baikal/firmware.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/kernel.c create mode 100644 linux/ps4-kexec-1300-pro-baikal/kernel.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/kexec.c create mode 100644 linux/ps4-kexec-1300-pro-baikal/kexec.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/kexec.ld create mode 100644 linux/ps4-kexec-1300-pro-baikal/linux_boot.c create mode 100644 linux/ps4-kexec-1300-pro-baikal/linux_boot.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/linux_thunk.S create mode 100644 linux/ps4-kexec-1300-pro-baikal/reboot.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/string.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/types.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/uart.c create mode 100644 linux/ps4-kexec-1300-pro-baikal/uart.h create mode 100644 linux/ps4-kexec-1300-pro-baikal/x86.h create mode 100644 linux/ps4-kexec-1300-pro/LICENSE create mode 100644 linux/ps4-kexec-1300-pro/Makefile create mode 100644 linux/ps4-kexec-1300-pro/README.md create mode 100644 linux/ps4-kexec-1300-pro/acpi.c create mode 100644 linux/ps4-kexec-1300-pro/acpi.h create mode 100644 linux/ps4-kexec-1300-pro/crc32.c create mode 100644 linux/ps4-kexec-1300-pro/crc32.h create mode 100644 linux/ps4-kexec-1300-pro/elf.h create mode 100644 linux/ps4-kexec-1300-pro/firmware.c create mode 100644 linux/ps4-kexec-1300-pro/firmware.h create mode 100644 linux/ps4-kexec-1300-pro/kernel.c create mode 100644 linux/ps4-kexec-1300-pro/kernel.h create mode 100644 linux/ps4-kexec-1300-pro/kexec.c create mode 100644 linux/ps4-kexec-1300-pro/kexec.h create mode 100644 linux/ps4-kexec-1300-pro/kexec.ld create mode 100644 linux/ps4-kexec-1300-pro/linux_boot.c create mode 100644 linux/ps4-kexec-1300-pro/linux_boot.h create mode 100644 linux/ps4-kexec-1300-pro/linux_thunk.S create mode 100644 linux/ps4-kexec-1300-pro/reboot.h create mode 100644 linux/ps4-kexec-1300-pro/string.h create mode 100644 linux/ps4-kexec-1300-pro/types.h create mode 100644 linux/ps4-kexec-1300-pro/uart.c create mode 100644 linux/ps4-kexec-1300-pro/uart.h create mode 100644 linux/ps4-kexec-1300-pro/x86.h create mode 100644 linux/ps4-kexec-1300/LICENSE create mode 100644 linux/ps4-kexec-1300/Makefile create mode 100644 linux/ps4-kexec-1300/README.md create mode 100644 linux/ps4-kexec-1300/acpi.c create mode 100644 linux/ps4-kexec-1300/acpi.h create mode 100644 linux/ps4-kexec-1300/crc32.c create mode 100644 linux/ps4-kexec-1300/crc32.h create mode 100644 linux/ps4-kexec-1300/elf.h create mode 100644 linux/ps4-kexec-1300/firmware.c create mode 100644 linux/ps4-kexec-1300/firmware.h create mode 100644 linux/ps4-kexec-1300/kernel.c create mode 100644 linux/ps4-kexec-1300/kernel.h create mode 100644 linux/ps4-kexec-1300/kexec.c create mode 100644 linux/ps4-kexec-1300/kexec.h create mode 100644 linux/ps4-kexec-1300/kexec.ld create mode 100644 linux/ps4-kexec-1300/linux_boot.c create mode 100644 linux/ps4-kexec-1300/linux_boot.h create mode 100644 linux/ps4-kexec-1300/linux_thunk.S create mode 100644 linux/ps4-kexec-1300/reboot.h create mode 100644 linux/ps4-kexec-1300/string.h create mode 100644 linux/ps4-kexec-1300/types.h create mode 100644 linux/ps4-kexec-1300/uart.c create mode 100644 linux/ps4-kexec-1300/uart.h create mode 100644 linux/ps4-kexec-1300/x86.h create mode 100644 linux/ps4-kexec-1302-baikal/LICENSE create mode 100644 linux/ps4-kexec-1302-baikal/Makefile create mode 100644 linux/ps4-kexec-1302-baikal/README.md create mode 100644 linux/ps4-kexec-1302-baikal/acpi.c create mode 100644 linux/ps4-kexec-1302-baikal/acpi.h create mode 100644 linux/ps4-kexec-1302-baikal/acpi_caps.h create mode 100644 linux/ps4-kexec-1302-baikal/crc32.c create mode 100644 linux/ps4-kexec-1302-baikal/crc32.h create mode 100644 linux/ps4-kexec-1302-baikal/elf.h create mode 100644 linux/ps4-kexec-1302-baikal/firmware.c create mode 100644 linux/ps4-kexec-1302-baikal/firmware.h create mode 100644 linux/ps4-kexec-1302-baikal/kernel.c create mode 100644 linux/ps4-kexec-1302-baikal/kernel.h create mode 100644 linux/ps4-kexec-1302-baikal/kexec.c create mode 100644 linux/ps4-kexec-1302-baikal/kexec.h create mode 100644 linux/ps4-kexec-1302-baikal/kexec.ld create mode 100644 linux/ps4-kexec-1302-baikal/linux_boot.c create mode 100644 linux/ps4-kexec-1302-baikal/linux_boot.h create mode 100644 linux/ps4-kexec-1302-baikal/linux_thunk.S create mode 100644 linux/ps4-kexec-1302-baikal/reboot.h create mode 100644 linux/ps4-kexec-1302-baikal/string.h create mode 100644 linux/ps4-kexec-1302-baikal/types.h create mode 100644 linux/ps4-kexec-1302-baikal/uart.c create mode 100644 linux/ps4-kexec-1302-baikal/uart.h create mode 100644 linux/ps4-kexec-1302-baikal/x86.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/LICENSE create mode 100644 linux/ps4-kexec-1302-pro-baikal/Makefile create mode 100644 linux/ps4-kexec-1302-pro-baikal/README.md create mode 100644 linux/ps4-kexec-1302-pro-baikal/acpi.c create mode 100644 linux/ps4-kexec-1302-pro-baikal/acpi.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/acpi_caps.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/crc32.c create mode 100644 linux/ps4-kexec-1302-pro-baikal/crc32.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/elf.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/firmware.c create mode 100644 linux/ps4-kexec-1302-pro-baikal/firmware.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/kernel.c create mode 100644 linux/ps4-kexec-1302-pro-baikal/kernel.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/kexec.c create mode 100644 linux/ps4-kexec-1302-pro-baikal/kexec.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/kexec.ld create mode 100644 linux/ps4-kexec-1302-pro-baikal/linux_boot.c create mode 100644 linux/ps4-kexec-1302-pro-baikal/linux_boot.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/linux_thunk.S create mode 100644 linux/ps4-kexec-1302-pro-baikal/reboot.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/string.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/types.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/uart.c create mode 100644 linux/ps4-kexec-1302-pro-baikal/uart.h create mode 100644 linux/ps4-kexec-1302-pro-baikal/x86.h create mode 100644 linux/ps4-kexec-1302-pro/LICENSE create mode 100644 linux/ps4-kexec-1302-pro/Makefile create mode 100644 linux/ps4-kexec-1302-pro/README.md create mode 100644 linux/ps4-kexec-1302-pro/acpi.c create mode 100644 linux/ps4-kexec-1302-pro/acpi.h create mode 100644 linux/ps4-kexec-1302-pro/crc32.c create mode 100644 linux/ps4-kexec-1302-pro/crc32.h create mode 100644 linux/ps4-kexec-1302-pro/elf.h create mode 100644 linux/ps4-kexec-1302-pro/firmware.c create mode 100644 linux/ps4-kexec-1302-pro/firmware.h create mode 100644 linux/ps4-kexec-1302-pro/kernel.c create mode 100644 linux/ps4-kexec-1302-pro/kernel.h create mode 100644 linux/ps4-kexec-1302-pro/kexec.c create mode 100644 linux/ps4-kexec-1302-pro/kexec.h create mode 100644 linux/ps4-kexec-1302-pro/kexec.ld create mode 100644 linux/ps4-kexec-1302-pro/linux_boot.c create mode 100644 linux/ps4-kexec-1302-pro/linux_boot.h create mode 100644 linux/ps4-kexec-1302-pro/linux_thunk.S create mode 100644 linux/ps4-kexec-1302-pro/reboot.h create mode 100644 linux/ps4-kexec-1302-pro/string.h create mode 100644 linux/ps4-kexec-1302-pro/types.h create mode 100644 linux/ps4-kexec-1302-pro/uart.c create mode 100644 linux/ps4-kexec-1302-pro/uart.h create mode 100644 linux/ps4-kexec-1302-pro/x86.h create mode 100644 linux/ps4-kexec-1302/LICENSE create mode 100644 linux/ps4-kexec-1302/Makefile create mode 100644 linux/ps4-kexec-1302/README.md create mode 100644 linux/ps4-kexec-1302/acpi.c create mode 100644 linux/ps4-kexec-1302/acpi.h create mode 100644 linux/ps4-kexec-1302/crc32.c create mode 100644 linux/ps4-kexec-1302/crc32.h create mode 100644 linux/ps4-kexec-1302/elf.h create mode 100644 linux/ps4-kexec-1302/firmware.c create mode 100644 linux/ps4-kexec-1302/firmware.h create mode 100644 linux/ps4-kexec-1302/kernel.c create mode 100644 linux/ps4-kexec-1302/kernel.h create mode 100644 linux/ps4-kexec-1302/kexec.c create mode 100644 linux/ps4-kexec-1302/kexec.h create mode 100644 linux/ps4-kexec-1302/kexec.ld create mode 100644 linux/ps4-kexec-1302/linux_boot.c create mode 100644 linux/ps4-kexec-1302/linux_boot.h create mode 100644 linux/ps4-kexec-1302/linux_thunk.S create mode 100644 linux/ps4-kexec-1302/reboot.h create mode 100644 linux/ps4-kexec-1302/string.h create mode 100644 linux/ps4-kexec-1302/types.h create mode 100644 linux/ps4-kexec-1302/uart.c create mode 100644 linux/ps4-kexec-1302/uart.h create mode 100644 linux/ps4-kexec-1302/x86.h diff --git a/README.md b/README.md index a5660c1..684f678 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# ![PS4](https://img.shields.io/badge/-PS4-003791?style=flat&logo=PlayStation) Linux Payloads for FW 5.05 - 12.52 +# ![PS4](https://img.shields.io/badge/-PS4-003791?style=flat&logo=PlayStation) Linux Payloads for FW 5.05 - 13.02 (Southbridge: Aeolia & Belize(2) & Baikal) **Linux-Payloads** kexec for PlayStation 4. The host with precompiled Linux payloads only works with GoldHEN v2.4b18.5/v2.4b18.6 BinLoader. Just open the web browser and cache the host—it will also work offline too. -https://ps4boot.github.io [▶️ click](https://www.youtube.com/watch?v=T3rXMWw6nIM) +[PSFree-Enhanced](https://arabpixel.github.io/PSFree-Enhanced) [▶️ click](https://www.youtube.com/watch?v=T3rXMWw6nIM) you’ll find Linux payloads for your firmware, along with some extra payloads. The rest are already included in GoldHEN. @@ -24,6 +24,8 @@ you’ll find Linux payloads for your firmware, along with some extra payloads. * FW 11.50 / 11.52 ✅ * FW 12.00 / 12.02 ✅ * FW 12.50 / 12.52 ✅ +* FW 13.00 ✅ +* 13.02(?) ✅ ## New @@ -64,6 +66,6 @@ Baikal: ``console=uart8250,mmio32,0xC890E000`` * marcan, shuffle2, eeply, rancido, valeryy, ethylamine, Joonie86 (Linux) * sleirsgoevy (for the script and better exploit FW 672) * AlAzif / KiwiDog / Specter / Celesteblue / ChendoChap / zecoxao / SocraticBliss / ctn123 (Exploit and Fun Stuff for the Console) -* bestpig / EchoStretch / EinTim23 / tihmstar (Offsets) +* bestpig / EchoStretch / EinTim23 / tihmstar / ArabPixel (Offsets) * others ... diff --git a/freebsd-headers/ps4-offsets/1300.h b/freebsd-headers/ps4-offsets/1300.h new file mode 100644 index 0000000..7d233f0 --- /dev/null +++ b/freebsd-headers/ps4-offsets/1300.h @@ -0,0 +1,14 @@ +#pragma once +#define kernel_offset_xfast_syscall 0x1c0 +#define kernel_offset_allproc 0x1b28538 +#define kernel_offset_vmspace_acquire_ref 0x2F6F80 +#define kernel_offset_vmspace_free 0x2F6DB0 +#define kernel_offset_printf 0x2E0440 +#define kernel_offset_kmem_alloc 0x465A40 +#define kernel_offset_kernel_map 0x22D1D50 +#define kernel_offset_sysent 0x1102B70 +#define kernel_offset_proc_rwmem 0x366000 +#define kernel_offset_copyin 0x2BD6D0 + +#define kernel_patch_kmem_alloc_1 0x465B0C +#define kernel_patch_kmem_alloc_2 0x465B14 diff --git a/freebsd-headers/ps4-offsets/1302.h b/freebsd-headers/ps4-offsets/1302.h new file mode 100644 index 0000000..ae3b6a8 --- /dev/null +++ b/freebsd-headers/ps4-offsets/1302.h @@ -0,0 +1,14 @@ +#pragma once +#define kernel_offset_xfast_syscall 0x1c0 +#define kernel_offset_allproc 0x1b28538 +#define kernel_offset_vmspace_acquire_ref 0x2F6F90 +#define kernel_offset_vmspace_free 0x2F6DC0 +#define kernel_offset_printf 0x2E0450 +#define kernel_offset_kmem_alloc 0x465A50 +#define kernel_offset_kernel_map 0x22D1D50 +#define kernel_offset_sysent 0x1102B70 +#define kernel_offset_proc_rwmem 0x366010 +#define kernel_offset_copyin 0x2BD6E0 + +#define kernel_patch_kmem_alloc_1 0x465B1C +#define kernel_patch_kmem_alloc_2 0x465B24 diff --git a/freebsd-headers/ps4-offsets/kernel.h b/freebsd-headers/ps4-offsets/kernel.h index f3cdea3..ec3961e 100644 --- a/freebsd-headers/ps4-offsets/kernel.h +++ b/freebsd-headers/ps4-offsets/kernel.h @@ -37,6 +37,12 @@ #ifdef __12_50__ #include "1250.h" #else +#ifdef __13_00__ +#include "1300.h" +#else +#ifdef __13_02__ +#include "1302.h" +#else #error "unsupported firmware" #endif #endif @@ -51,3 +57,5 @@ #endif #endif #endif +#endif +#endif \ No newline at end of file diff --git a/linux/Makefile b/linux/Makefile index 169dd25..3b46808 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -1,4 +1,4 @@ -FIRMWARES = 505 672 700 900 903 960 1000 1050 1100 1102 1150 1200 1250 +FIRMWARES = 505 672 700 900 903 960 1000 1050 1100 1102 1150 1200 1250 1300 1302 SIZES = 1gb 2gb 3gb 4gb # This uses a nested loop style logic to generate the file names automatically PAYLOADS = $(foreach fw,$(FIRMWARES), \ @@ -67,6 +67,14 @@ clean: cd ps4-kexec-1250-pro; make clean cd ps4-kexec-1250-baikal; make clean cd ps4-kexec-1250-pro-baikal; make clean + cd ps4-kexec-1300; make clean + cd ps4-kexec-1300-pro; make clean + cd ps4-kexec-1300-baikal; make clean + cd ps4-kexec-1300-pro-baikal; make clean + cd ps4-kexec-1302; make clean + cd ps4-kexec-1302-pro; make clean + cd ps4-kexec-1302-baikal; make clean + cd ps4-kexec-1302-pro-baikal; make clean cd ../lib/; make clean ../lib/lib.a: @@ -806,8 +814,8 @@ fw1250/payload-1250-3gb.elf: ../lib/lib.a main.c ps4-kexec-1250/kexec.bin fw1250/payload-1250-4gb.elf: ../lib/lib.a main.c ps4-kexec-1250/kexec.bin gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1250/payload-1250-4gb.elf -fPIE -ffreestanding -ps4-kexec-1200-pro/kexec.bin: - cd ps4-kexec-1200-pro; make +ps4-kexec-1250-pro/kexec.bin: + cd ps4-kexec-1250-pro; make fw1250/payload-1250-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1250-pro/kexec.bin gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 main.c -Wl,-gc-sections -o fw1250/payload-1250-1gb-pro.elf -fPIE -ffreestanding @@ -851,6 +859,118 @@ fw1250/payload-1250-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-125 fw1250/payload-1250-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-pro-baikal/kexec.bin gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-4gb-pro-baikal.elf -fPIE -ffreestanding +fw1300/payload-1300-1gb.elf: ../lib/lib.a main.c ps4-kexec-1300/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 main.c -Wl,-gc-sections -o fw1300/payload-1300-1gb.elf -fPIE -ffreestanding + +fw1300/payload-1300-2gb.elf: ../lib/lib.a main.c ps4-kexec-1300/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1300/payload-1300-2gb.elf -fPIE -ffreestanding + +fw1300/payload-1300-3gb.elf: ../lib/lib.a main.c ps4-kexec-1300/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1300/payload-1300-3gb.elf -fPIE -ffreestanding + +fw1300/payload-1300-4gb.elf: ../lib/lib.a main.c ps4-kexec-1300/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1300/payload-1300-4gb.elf -fPIE -ffreestanding +ps4-kexec-1300-pro/kexec.bin: + cd ps4-kexec-1300-pro; make + +fw1300/payload-1300-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1300-pro/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 main.c -Wl,-gc-sections -o fw1300/payload-1300-1gb-pro.elf -fPIE -ffreestanding + +fw1300/payload-1300-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1300-pro/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1300/payload-1300-2gb-pro.elf -fPIE -ffreestanding + +fw1300/payload-1300-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1300-pro/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1300/payload-1300-3gb-pro.elf -fPIE -ffreestanding + +fw1300/payload-1300-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1300-pro/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1300/payload-1300-4gb-pro.elf -fPIE -ffreestanding + +ps4-kexec-1300-baikal/kexec.bin: + cd ps4-kexec-1300-baikal; make + +fw1300/payload-1300-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-1gb-baikal.elf -fPIE -ffreestanding + +fw1300/payload-1300-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-2gb-baikal.elf -fPIE -ffreestanding + +fw1300/payload-1300-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-3gb-baikal.elf -fPIE -ffreestanding + +fw1300/payload-1300-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-4gb-baikal.elf -fPIE -ffreestanding + +ps4-kexec-1300-pro-baikal/kexec.bin: + cd ps4-kexec-1300-pro-baikal; make + +fw1300/payload-1300-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-pro-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-1gb-pro-baikal.elf -fPIE -ffreestanding + +fw1300/payload-1300-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-pro-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-2gb-pro-baikal.elf -fPIE -ffreestanding + +fw1300/payload-1300-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-pro-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-3gb-pro-baikal.elf -fPIE -ffreestanding + +fw1300/payload-1300-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-pro-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-4gb-pro-baikal.elf -fPIE -ffreestanding + +fw1302/payload-1302-1gb.elf: ../lib/lib.a main.c ps4-kexec-1302/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 main.c -Wl,-gc-sections -o fw1302/payload-1302-1gb.elf -fPIE -ffreestanding + +fw1302/payload-1302-2gb.elf: ../lib/lib.a main.c ps4-kexec-1302/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1302/payload-1302-2gb.elf -fPIE -ffreestanding + +fw1302/payload-1302-3gb.elf: ../lib/lib.a main.c ps4-kexec-1302/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1302/payload-1302-3gb.elf -fPIE -ffreestanding + +fw1302/payload-1302-4gb.elf: ../lib/lib.a main.c ps4-kexec-1302/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1302/payload-1302-4gb.elf -fPIE -ffreestanding +ps4-kexec-1302-pro/kexec.bin: + cd ps4-kexec-1302-pro; make + +fw1302/payload-1302-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1302-pro/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 main.c -Wl,-gc-sections -o fw1302/payload-1302-1gb-pro.elf -fPIE -ffreestanding + +fw1302/payload-1302-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1302-pro/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1302/payload-1302-2gb-pro.elf -fPIE -ffreestanding + +fw1302/payload-1302-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1302-pro/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1302/payload-1302-3gb-pro.elf -fPIE -ffreestanding + +fw1302/payload-1302-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1302-pro/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1302/payload-1302-4gb-pro.elf -fPIE -ffreestanding + +ps4-kexec-1302-baikal/kexec.bin: + cd ps4-kexec-1302-baikal; make + +fw1302/payload-1302-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-1gb-baikal.elf -fPIE -ffreestanding + +fw1302/payload-1302-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-2gb-baikal.elf -fPIE -ffreestanding + +fw1302/payload-1302-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-3gb-baikal.elf -fPIE -ffreestanding + +fw1302/payload-1302-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-4gb-baikal.elf -fPIE -ffreestanding + +ps4-kexec-1302-pro-baikal/kexec.bin: + cd ps4-kexec-1302-pro-baikal; make + +fw1302/payload-1302-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-pro-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-1gb-pro-baikal.elf -fPIE -ffreestanding + +fw1302/payload-1302-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-pro-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-2gb-pro-baikal.elf -fPIE -ffreestanding + +fw1302/payload-1302-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-pro-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-3gb-pro-baikal.elf -fPIE -ffreestanding + +fw1302/payload-1302-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-pro-baikal/kexec.bin + gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-4gb-pro-baikal.elf -fPIE -ffreestanding + %.bin: %.elf objcopy $< --only-section .text --only-section .data --only-section .bss --only-section .rodata -O binary $@ file $@ | fgrep -q '$@: DOS executable (COM)' diff --git a/linux/fw1300/.keepgithub b/linux/fw1300/.keepgithub new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/linux/fw1300/.keepgithub @@ -0,0 +1 @@ + diff --git a/linux/fw1302/.keepgithub b/linux/fw1302/.keepgithub new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/linux/fw1302/.keepgithub @@ -0,0 +1 @@ + diff --git a/linux/magic.h b/linux/magic.h index 2636eb4..b9267f1 100644 --- a/linux/magic.h +++ b/linux/magic.h @@ -486,4 +486,74 @@ #define kern_off_pstate_before_shutdown 0x3A23A0 #define kern_off_set_nclk_mem_spd 0 +#elif defined PS4_13_00 //ArabPixel +#define kern_off_printf 0x2E0440 // Done +#define kern_off_snprintf 0x2E0740 // Done +#define kern_off_copyin 0x2BD6D0 // Done +#define kern_off_copyout 0x2BD5E0 // Done +#define kern_off_copyinstr 0x2BDB80 // Done +#define kern_off_kmem_alloc_contig 0x24D430 // Done +#define kern_off_kmem_free 0x465C10 // Done +#define kern_off_pmap_extract 0x573D0 // Done +#define kern_off_pmap_protect 0x58570 // Done +#define kern_off_sched_pin 0x231660 // Done +#define kern_off_sched_unpin 0x231680 // Done +#define kern_off_smp_rendezvous 0x1AD520 // Done +#define kern_off_smp_no_rendevous_barrier 0x1AD330 // Done +#define kern_off_icc_query_nowait 0x447B30 // Done +#define kern_off_kernel_map 0x22D1D50 // Done +#define kern_off_sysent 0x1102B70 // Done +#define kern_off_kernel_pmap_store 0x1B2C3A0 // Done +#define kern_off_Starsha_UcodeInfo 0x0 +#define kern_off_gpu_devid_is_9924 0x4AC5A0 // Done +#define kern_off_gc_get_fw_info 0x4BAF50 // Done +#define kern_off_pml4pml4i 0x1B2C390 // Done +#define kern_off_dmpml4i 0x1B2C394 // Done +#define kern_off_dmpdpi 0x1B2C398 // Done +#define kern_off_eap_hdd_key 0x26C4CF0 // Done +#define kern_off_edid 0x275E148 // Done +#define kern_off_wlanbt 0x478A50 // Done +#define kern_off_kern_reboot 0x3A1DD0 // Done +#define kern_off_set_gpu_freq 0x4B9A90 // Done +#define kern_off_set_pstate 0x4BBE60 // Done +#define kern_off_update_vddnp 0x4BA030 // Done +#define kern_off_set_cu_power_gate 0x4BA440 // Done +#define kern_off_pstate_before_shutdown 0x3A23C0 // Done +#define kern_off_set_nclk_mem_spd 0 + +#elif defined PS4_13_02 //ArabPixel +#define kern_off_printf 0x2E0450 // Done +#define kern_off_snprintf 0x2E0750 // Done +#define kern_off_copyin 0x2BD6E0 // Done +#define kern_off_copyout 0x2BD5F0 // Done +#define kern_off_copyinstr 0x2BDB90 // Done +#define kern_off_kmem_alloc_contig 0x24D440 // Done +#define kern_off_kmem_free 0x465C20 // Done +#define kern_off_pmap_extract 0x573D0 // Done +#define kern_off_pmap_protect 0x58570 // Done +#define kern_off_sched_pin 0x231670 // Done +#define kern_off_sched_unpin 0x231690 // Done +#define kern_off_smp_rendezvous 0x1AD530 // Done +#define kern_off_smp_no_rendevous_barrier 0x1AD340 // Done +#define kern_off_icc_query_nowait 0x447B40 // Done +#define kern_off_kernel_map 0x22D1D50 // Done +#define kern_off_sysent 0x1102B70 // Done +#define kern_off_kernel_pmap_store 0x1B2C3A0 // Done +#define kern_off_Starsha_UcodeInfo 0x0 +#define kern_off_gpu_devid_is_9924 0x4AC5A0 // Done +#define kern_off_gc_get_fw_info 0x4BAF60 // Done +#define kern_off_pml4pml4i 0x1B2C390 // Done +#define kern_off_dmpml4i 0x1B2C394 // Done +#define kern_off_dmpdpi 0x1B2C398 // Done +#define kern_off_eap_hdd_key 0x26C4CF0 // Done +#define kern_off_edid 0x275E148 // Done +#define kern_off_wlanbt 0x478A60 // Done +#define kern_off_kern_reboot 0x3A1DE0 // Done +#define kern_off_set_gpu_freq 0x4B9AA0 // Done +#define kern_off_set_pstate 0x4BBE70 // Done +#define kern_off_update_vddnp 0x4BA040 // Done +#define kern_off_set_cu_power_gate 0x4BA450 // Done +#define kern_off_pstate_before_shutdown 0x3A23D0 // Done +#define kern_off_set_nclk_mem_spd 0 + #endif diff --git a/linux/main-baikal.c b/linux/main-baikal.c index 204b8ea..cdf950a 100644 --- a/linux/main-baikal.c +++ b/linux/main-baikal.c @@ -47,6 +47,12 @@ asm("ps4kexec:\n.incbin \"ps4-kexec-1200-baikal/kexec.bin\"\nps4kexec_end:\n"); #elif defined(__12_50__) asm("ps4kexec:\n.incbin \"ps4-kexec-1250-baikal/kexec.bin\"\nps4kexec_end:\n"); #include "magic.h" +#elif defined(__13_00__) +asm("ps4kexec:\n.incbin \"ps4-kexec-1300-baikal/kexec.bin\"\nps4kexec_end:\n"); +#include "magic.h" +#elif defined(__13_02__) +asm("ps4kexec:\n.incbin \"ps4-kexec-1302-baikal/kexec.bin\"\nps4kexec_end:\n"); +#include "magic.h" #else #error "unsupported firmware" #endif diff --git a/linux/main.c b/linux/main.c index 955c025..556f91a 100644 --- a/linux/main.c +++ b/linux/main.c @@ -47,6 +47,12 @@ asm("ps4kexec:\n.incbin \"ps4-kexec-1200/kexec.bin\"\nps4kexec_end:\n"); #elif defined(__12_50__) asm("ps4kexec:\n.incbin \"ps4-kexec-1250/kexec.bin\"\nps4kexec_end:\n"); #include "magic.h" +#elif defined(__13_00__) +asm("ps4kexec:\n.incbin \"ps4-kexec-1300/kexec.bin\"\nps4kexec_end:\n"); +#include "magic.h" +#elif defined(__13_02__) +asm("ps4kexec:\n.incbin \"ps4-kexec-1302/kexec.bin\"\nps4kexec_end:\n"); +#include "magic.h" #else #error "unsupported firmware" #endif diff --git a/linux/ps4-kexec-1300-baikal/LICENSE b/linux/ps4-kexec-1300-baikal/LICENSE new file mode 100644 index 0000000..6c1cbba --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2015-2016 shuffle2 +Copyright (C) 2015-2016 Hector Martin "marcan" +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/ps4-kexec-1300-baikal/Makefile b/linux/ps4-kexec-1300-baikal/Makefile new file mode 100644 index 0000000..d06ee6e --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/Makefile @@ -0,0 +1,37 @@ +TOOLCHAIN_PREFIX ?= +CC = $(TOOLCHAIN_PREFIX)gcc +AR = $(TOOLCHAIN_PREFIX)ar +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy + +CFLAGS=$(CFLAG) -DPS4_13_00 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX +CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ + -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ + -fno-asynchronous-unwind-tables \ + -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ + -mcmodel=small -mno-red-zone + +SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ + acpi.c crc32.c + +OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) +DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld + +all: libkexec.a kexec.bin + +%.o: %.c *.h + $(CC) -c $(CFLAGS) -o $@ $< + +%.o: %.S + $(CC) -c $(CFLAGS) -o $@ $< + +libkexec.a: $(OBJS) + $(AR) -rc $@ $(OBJS) + +kexec.elf: libkexec.a kexec.ld + $(CC) $(CFLAGS) -o $@ libkexec.a + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +clean: + rm -f libkexec.a kexec.elf kexec.bin $(OBJS) diff --git a/linux/ps4-kexec-1300-baikal/README.md b/linux/ps4-kexec-1300-baikal/README.md new file mode 100644 index 0000000..6500878 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/README.md @@ -0,0 +1,121 @@ +# PS4 kexec implementation + +This repo implements a kexec()-style system call for the PS4 Orbis kernel +(FreeBSD derivative). This is designed to boot a Linux kernel directly from +FreeBSD. + +This is not an exploit. It is useless without some mechanism of injecting code +into the PS4 OS kernel. + +## Building + +To build a kexec.bin relocatable binary using the supplied Makefile, just type +`make`. This will also build a kexec.a archive. You can either use the binary +directly, or link the archive into your own project. + +If you link kexec.a with your own code, you need to supply the two symbols +`_start` and `_end` in your linker script, as `kernel_init()` will try to remap +all pages covered by that range as RWX (to make global variable accesses work). +Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this +feature, if you have already taken care of page permissions for the code. + +If you use a compiler toolchain that have a special prefix you can declare it +by passing TOOLCHAIN_PREFIX option to the Makefile like this: + + make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' + +## Usage + +The code is designed to be completely standalone. There is a single entry point: + + int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); + +Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel +symbols and install the sys_kexec system call. The syscall is registered +as number 153 by default (you can change this in kexec.h). The return value +is 0 on success, or negative on error. + +You may pass something other than NULL as `early_printf`. In that case, that +function will be used for debug output during early symbol resolution, before +printf is available. + +Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is +enabled by default, symtab also disappears in newer kernel, we have to +hardcode offsets for some symbols. Currently we use the `early_printf` +given by user to caculate the base address of kernel, then relocate all the +symbols from the kernel base. You could enable this feature like this: + + make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' + +If you do not want to call the syscall from userspace, you can pass the address +of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the +address of `sys_kexec`, so you can invoke it manually (see kexec.h for +its prototype and how the arguments are passed). + +If you are using the standalone kexec.bin blob, then the `kexec_init` function +is always located at offset 0, so simply call the base address of the blob. +Don't forget to pass two NULL arguments (or the appropriate pointers). + +The injected `sys_kexec` system call takes (userspace) pointers to the kernel +and initramfs blobs, their sizes, and a pointer to the (null-terminated) command +line string. From userspace, this looks like this: + + int kexec(void *kernel_image, size_t image_size, + void *initramfs, size_t initramfs_size, + const char *cmdline); + + // syscall() usage: + syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); + +`kexec()` will load the kernel and initramfs into memory, but will not directly +boot them. To boot the loaded kernel, shut down the system. This can be +accomplished by pressing the power button, but can also be done more quickly +and reliably from userspace with the following sequence of system calls (this +kills userspace quickly but still does a controlled filesystem unmount): + + int evf = syscall(540, "SceSysCoreReboot"); + syscall(546, evf, 0x4000, 0); + syscall(541, evf); + // should be syscall(37, 1, 30) but only tested via kill symbol + kill(1, 30); + +Note that this software should be loaded into kernel memory space. If you are +running kernel code from userland mappings, you should either switch to kernel +mappings or separately copy kexec.bin to a location in kernel address space. +While syscalls or exploit code may run properly from userland, the shutdown hook +will not, as it will be called from a different process context. + +## Features + +`kernel_init()` will automatically find the Orbis OS kernel and resolve all +necessary symbols to work. There are no static symbol dependencies. If +`DO_NOT_REMAP_RWX` is not defined (the default), it will also patch +`pmap_protect` to disable the W^X restriction. + +In addition to loading the user-supplied initramfs, `kexec` will locate the +Radeon firmware blobs inside Orbis OS, extract them, convert them to a format +suitable for Linux, and append them as an additional initramfs cpio image to +the existing initramfs. This avoids the need to distribute the Radeon firmware +blobs. The `radeon` module, when compiled into the kernel, will automatically +load this firmware on boot. Note however that most typical initramfs scripts +will wipe the initramfs contents while pivoting to the real system, so if you +compile `radeon` as a module you may not be able to access the firmware after +boot. To cover that case, add some code to your initramfs `/init` script to +copy the firmware to a tmpfs mounted on the real filesystem: + + # assuming real root FS is mounted on /mnt + + mkdir -p /mnt/lib/firmware/radeon + mount -t tmpfs none /mnt/lib/firmware/radeon + cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ + + # now switch_root to /mnt + +This avoids having to permanently store copies of the Radeon firmware, which +isn't really necessary for most use cases. + +There is significant debug logging available, which will appear on the system +UART. Most of the code relies on the kernel `printf` implementation, and +therefore you should patch out the UART output blanker to see it. The final +code that runs on the boot CPU before booting the kernel uses direct UART +writes and is not affected by the blanking feature of Orbis OS. diff --git a/linux/ps4-kexec-1300-baikal/acpi.c b/linux/ps4-kexec-1300-baikal/acpi.c new file mode 100644 index 0000000..bfccf08 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/acpi.c @@ -0,0 +1,320 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "types.h" +#include "kernel.h" +#include "acpi.h" +#include "acpi_caps.h" + +#ifdef TESTING +# include +# include +# include +# include +# include +# include +#else +# include "kernel.h" +# include "string.h" +# define printf kern.printf +#endif + +#define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) +#define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) + +#define PACKED __attribute__((packed)) + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +struct RSDP { + u64 sig; + u8 checksum; + u8 oemid[6]; + u8 rev; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 rsvd[3]; +} PACKED; + +struct SDTH { + u32 sig; + u32 length; + u8 rev; + u8 checksum; + u8 oem_id[6]; + u8 oem_tid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; +} PACKED; + +struct RSDT { + struct SDTH hdr; + u32 table_addr[]; +} PACKED; + +struct XSDT { + struct SDTH hdr; + u64 table_addr[]; +} PACKED; + +struct FADT { + struct SDTH hdr; + u32 facs; + u32 dsdt; + // more stuff... +} PACKED; + +struct ivhd_entry4 { + u8 type; + u16 devid; + u8 flags; +} PACKED; + +struct ivhd_header { + u8 type; + u8 flags; + u16 length; + u16 devid; + u16 cap_ptr; + u64 mmio_phys; + u16 pci_seg; + u16 info; + u32 efr_attr; +} PACKED; + +struct IVRS { + struct SDTH hdr; + u32 IVinfo; + u8 reserved[8]; + struct ivhd_header hd_hdr; + struct ivhd_entry4 hd_entries[3]; +} PACKED; + +struct MMIO { + u64 baseAddressECM; + u16 pciSegmentGroup; + u8 startPCIBus; + u8 endPCIBus; + u32 reserved; +} PACKED; + +// We have enough space to use the second half of the 64KB table area +// as scratch space for building the tables +#define BUFFER_OFF 0x8000 + +#define P2M(p) (((u64)(p)) - phys_base + map_base) +#define M2P(p) ((((void*)(p)) - map_base) + phys_base) +#define B2P(p) ((((void*)(p)) - buf_base) + phys_base) +#define P2B(p) ((((void*)(p)) - phys_base) + buf_base) + +#define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) +#define PADB(s) p += (s) +#define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) +#define ALLOC(t) (t)ALLOCB(sizeof(t)) +#define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) +#define COPYT(s) COPYB(((struct SDTH*)s)->length, s) +#define COPYTP(s) COPYT(P2M(s)) +#define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) +#define COPYP(t, s) COPY(t, P2M(s)) + +static void rsdp_checksum(struct RSDP *rsdp) { + rsdp->checksum = rsdp->ext_checksum = 0; + + u8 sum = 0; + for (int i = 0; i < 20; i++) + sum += ((u8*)rsdp)[i]; + rsdp->checksum = -sum; + sum = 0; + for (int i = 0; i < sizeof(*rsdp); i++) + sum += ((u8*)rsdp)[i]; + rsdp->ext_checksum = -sum; +} + +static void table_checksum(void *table) { + struct SDTH *hdr = table; + hdr->checksum = 0; + u8 sum = 0; + for (int i = 0; i < hdr->length; i++) + sum += ((u8*)table)[i]; + hdr->checksum = -sum; +} + +#define IVHD_FLAG_ISOC_EN_MASK 0x08 +#define IVHD_DEV_ALL 0x01 +#define IVHD_DEV_SELECT 0x02 +#define IVHD_DEV_SELECT_RANGE_START 0x03 +#define IVHD_DEV_RANGE_END 0x04 + +#define ACPI_DEVFLAG_SYSMGT1 0x10 +#define ACPI_DEVFLAG_SYSMGT2 0x20 + +static void *build_ivrs(struct IVRS *ivrs) { + memset(ivrs, 0, sizeof(*ivrs)); + + ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); + ivrs->hdr.length = sizeof(*ivrs); + ivrs->hdr.rev = 1; + memcpy(ivrs->hdr.oem_id, "F0F ", 6); + memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); + ivrs->hdr.oem_rev = 0x20161225; + memcpy(ivrs->hdr.creator_id, "KEXC", 4); + ivrs->hdr.creator_rev = 0x20161225; + ivrs->IVinfo = 0x00203040; //48882_IOMMU.pdf page 251 + + struct ivhd_header *hdr = &ivrs->hd_hdr; //48882_IOMMU.pdf page 254 + hdr->type = 0x10; + hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; + hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); + hdr->devid = PCI_DEVFN(0, 2); + hdr->cap_ptr = 0x40; // from config space + 0x34 + hdr->mmio_phys = 0xfc000000; //Base address of IOMMU control registers in MMIO space + hdr->pci_seg = 0; + hdr->info = 0; // msi msg num? (the pci cap should be written by software) + // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 + hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); //Feature Reporting Field, 48882_IOMMU.pdf page 255 + + struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; + // on fbsd, all aeolia devfns have active entries except memories (func 6) + // not sure if this is just because it wasn't in use when i dumped it? + // all entries are r/w + // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) + // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) + // Modes: + // 4 level: + // apcie + // 3 level: + // all others + + // the way to encode this info into the IVHD entries is fairly arbitrary... + entries[0].type = IVHD_DEV_SELECT; //DTE setting applies to the device specifed in DevID field. + entries[0].devid = PCI_DEVFN(20, 0); //vendorId: 104D, deviceId: 90D7; Sony Baikal ACPI + entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; + + entries[1].type = IVHD_DEV_SELECT_RANGE_START; + entries[1].devid = PCI_DEVFN(20, 1); + entries[1].flags = 0; //Identifies a device able to assert INIT interrupts (page 262) + entries[2].type = IVHD_DEV_RANGE_END; + entries[2].devid = PCI_DEVFN(20, 7); + entries[2].flags = 0; //Identifies a device able to assert INIT interrupts + + table_checksum(ivrs); + return ivrs + 1; +} + +void fix_acpi_tables(void *map_base, u64 phys_base) +{ + void *buf_base = map_base + 0x8000; + void *p = buf_base; + memset(buf_base, 0, 0x8000); + + printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); + + struct RSDP *rsdp = COPYP(struct RSDP, phys_base); + printf("RSDT at 0x%x\n", rsdp->rsdt_addr); + printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); + + struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); + struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); + rsdp->rsdt_addr = B2P(rsdt); + + PADB(0x30); // this gives us space for new tables + + struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); + rsdp->xsdt_addr = B2P(xsdt); + + PADB(0x60); + + struct FADT *fadt = NULL; + + int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; + int i; + for (i = 0; i < cnt; i++) { + struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); + printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); + switch (hdr->sig) { + case SIG32('F', 'A', 'C', 'P'): + { + fadt = (void*)hdr; + printf("FACS at 0x%x\n", fadt->facs); + printf("DSDT at 0x%x\n", fadt->dsdt); + // Sony puts the FACS before the FADT, unaligned, which is + // noncompliant, but let's keep it there + u8 *facs = COPYB(64, P2M(fadt->facs)); + fadt = (void*)(hdr = COPYT(hdr)); + fadt->facs = B2P(facs); + PADB(0x38); + break; + } + case SIG32('S', 'S', 'D', 'T'): + { + // Put the DSDT before the SSDT + if (fadt) { + PADB(0xf0); + u8 *dsdt = COPYTP(fadt->dsdt); + fadt->dsdt = B2P(dsdt); + PADB(0x174); + table_checksum(fadt); + } else { + printf("ERROR: no FADT yet?\n"); + } + hdr = COPYT(hdr); + break; + } + default: + hdr = COPYT(hdr); + } + table_checksum(hdr); + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); + } + + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); + i++; + p = build_ivrs(p); + + rsdt->hdr.length = sizeof(*rsdt) + 4 * i; + xsdt->hdr.length = sizeof(*xsdt) + 8 * i; + + rsdp_checksum(rsdp); + table_checksum(rsdt); + table_checksum(xsdt); + memcpy(map_base, buf_base, p - buf_base); +} + +u32 msi_mask(unsigned x) { + /* Don't shift by >= width of type */ + if (x >= 5) + return 0xffffffff; + return (1 << (1 << x)) - 1; +} +void disableMSI(u64 MSICapabilityRegAddr) { + PPCI_MSI_CAPABILITY pMSICapability = (PPCI_MSI_CAPABILITY)PA_TO_DM(MSICapabilityRegAddr); + if (pMSICapability->msiEnable == 1) + pMSICapability->msiEnable = 0; + pMSICapability->mask64 = msi_mask(pMSICapability->multipleMessageCapable); +} + +#ifdef TESTING + +int main(int argc, char **argv) +{ + int fd; + void *base; + + fd = open(argv[1], O_RDWR); + base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + fix_acpi_tables(base, 0xe0000); + return 0; +} + +#endif diff --git a/linux/ps4-kexec-1300-baikal/acpi.h b/linux/ps4-kexec-1300-baikal/acpi.h new file mode 100644 index 0000000..45f2834 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/acpi.h @@ -0,0 +1,22 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ACPI_H +#define ACPI_H + +#define PACKED __attribute__((packed)) + +void fix_acpi_tables(void *map_base, u64 phys_base); + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) + +void disableMSI(u64 MSICapabilityRegAddr); + +#endif diff --git a/linux/ps4-kexec-1300-baikal/acpi_caps.h b/linux/ps4-kexec-1300-baikal/acpi_caps.h new file mode 100644 index 0000000..070e9f6 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/acpi_caps.h @@ -0,0 +1,30 @@ +#ifndef ACPI_CAPS_H +#define ACPI_CAPS_H + +#define UCHAR u8 +#define USHORT u16 +#define ULONG u32 +#define PACKED __attribute__((packed)) +typedef struct PACKED _PCI_CAPABILITIES_HEADER { + UCHAR CapabilityID; + UCHAR Next; +} PCI_CAPABILITIES_HEADER, *PPCI_CAPABILITIES_HEADER; + +typedef struct PACKED _PCI_MSI_CAPABILITY { + PCI_CAPABILITIES_HEADER Header; + u16 msiEnable : 1, multipleMessageCapable : 3, multipleMessageEnable : 3, address64Capable : 1, reserved0 : 8; + u32 lowerAddress : 30, reserved1 : 2; + union { + struct { + u32 upperAddress; + u32 messageData64 : 16, reservedData64 : 16; + u32 mask64; + }; + struct { + u32 messageData32 : 16, reservedData32 : 16; + u32 mask32; + }; + }; +} PCI_MSI_CAPABILITY, *PPCI_MSI_CAPABILITY; + +#endif diff --git a/linux/ps4-kexec-1300-baikal/crc32.c b/linux/ps4-kexec-1300-baikal/crc32.c new file mode 100644 index 0000000..7836c18 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/crc32.c @@ -0,0 +1,102 @@ +/*- +* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or +* code or tables extracted from it, as desired without restriction. +* +* First, the polynomial itself and its table of feedback terms. The +* polynomial is +* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 +* +* Note that we take it "backwards" and put the highest-order term in +* the lowest-order bit. The X^32 term is "implied"; the LSB is the +* X^31 term, etc. The X^0 term (usually shown as "+1") results in +* the MSB being 1 +* +* Note that the usual hardware shift register implementation, which +* is what we're using (we're merely optimizing it by doing eight-bit +* chunks at a time) shifts bits into the lowest-order term. In our +* implementation, that means shifting towards the right. Why do we +* do it this way? Because the calculated CRC must be transmitted in +* order from highest-order term to lowest-order term. UARTs transmit +* characters in order from LSB to MSB. By storing the CRC this way +* we hand it to the UART in the order low-byte to high-byte; the UART +* sends each low-bit to hight-bit; and the result is transmission bit +* by bit from highest- to lowest-order term without requiring any bit +* shuffling on our part. Reception works similarly +* +* The feedback terms table consists of 256, 32-bit entries. Notes +* +* The table can be generated at runtime if desired; code to do so +* is shown later. It might not be obvious, but the feedback +* terms simply represent the results of eight shift/xor opera +* tions for all combinations of data and CRC register values +* +* The values must be right-shifted by eight bits by the "updcrc +* logic; the shift must be unsigned (bring in zeroes). On some +* hardware you could probably optimize the shift in assembler by +* using byte-swap instructions +* polynomial $edb88320 +* +* +* CRC32 code derived from work by Gary S. Brown. +*/ + +#include "crc32.h" + +static u32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 crc32(u32 crc, const void *buf, size_t size) +{ + const u8 *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} diff --git a/linux/ps4-kexec-1300-baikal/crc32.h b/linux/ps4-kexec-1300-baikal/crc32.h new file mode 100644 index 0000000..777278d --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/crc32.h @@ -0,0 +1,7 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +u32 crc32(u32 crc, const void *buf, size_t size); + +#endif diff --git a/linux/ps4-kexec-1300-baikal/elf.h b/linux/ps4-kexec-1300-baikal/elf.h new file mode 100644 index 0000000..63371c7 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/elf.h @@ -0,0 +1,71 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + u8 e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + u64 e_entry; + u64 e_phoff; + u64 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf64_Ehdr; + +typedef struct { + u32 p_type; + u32 p_flags; + u64 p_offset; + void *p_vaddr; + u64 p_paddr; + u64 p_filesz; + u64 p_memsz; + u64 p_align; +} Elf64_Phdr; + +#define PT_DYNAMIC 2 +#define PT_PHDR 6 + +#define DT_NULL 0 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_STRSZ 10 + +typedef struct { + s64 d_tag; + union { + u64 d_val; + void *d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + void *st_value; + u64 st_size; +} Elf64_Sym; + +#endif diff --git a/linux/ps4-kexec-1300-baikal/firmware.c b/linux/ps4-kexec-1300-baikal/firmware.c new file mode 100644 index 0000000..a831b1c --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/firmware.c @@ -0,0 +1,479 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "firmware.h" +#include "types.h" +#include "kernel.h" +#include "string.h" +#include "types.h" +#include "crc32.h" + +#define DIR 0040755 +#define FILE 0100644 + +struct firmware_header { + u32 size_bytes; + u32 header_size_bytes; + u16 header_version_major; + u16 header_version_minor; + u16 ip_version_major; + u16 ip_version_minor; + u32 ucode_version; + u32 ucode_size_bytes; + u32 ucode_array_offset_bytes; + u32 crc32; + union { + struct { + u32 ucode_feature_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } gfx1; + struct { + u32 ucode_feature_version; + u32 save_and_restore_offset; + u32 clear_state_descriptor_offset; + u32 avail_scratch_ram_locations; + u32 master_pkt_description_offset; + u8 end[]; + } rlc1; + struct { + u32 ucode_feature_version; + u32 ucode_change_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } sdma1; + u8 raw[0xe0]; + }; +}; + +static inline char hex(u8 c) +{ + if (c <= 9) + return '0' + c; + return 'a' + c - 10; +} + +static void hex8(u8 **p, u32 val) +{ + *(*p)++ = hex(val >> 28); + *(*p)++ = hex((val >> 24) & 0xf); + *(*p)++ = hex((val >> 20) & 0xf); + *(*p)++ = hex((val >> 16) & 0xf); + *(*p)++ = hex((val >> 12) & 0xf); + *(*p)++ = hex((val >> 8) & 0xf); + *(*p)++ = hex((val >> 4) & 0xf); + *(*p)++ = hex(val & 0xf); +} + +void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) +{ + size_t name_len = strlen(name); + + // Pad to 4 byte multiple + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; + + memcpy(*p, "070701", 6); + *p += 6; + hex8(p, 0); // c_ino + hex8(p, mode); // c_mode + hex8(p, 0); // c_uid + hex8(p, 0); // c_gid + hex8(p, 1); // c_nlink + hex8(p, 0); // c_mtime + hex8(p, size); // c_filesize + hex8(p, 0); // c_maj + hex8(p, 0); // c_min + hex8(p, 0); // c_rmaj + hex8(p, 0); // c_rmin + hex8(p, name_len + 1); // c_namesize + hex8(p, 0); // c_chksum + memcpy(*p, name, name_len); + *p += name_len; + *(*p)++ = 0; + + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; +} + +struct fw_header_t { + u64 size_words; + char *unk_ident; + u64 unk; + void *blob; + u64 unk2; +}; + +struct fw_info_t { + struct fw_header_t *rlc; + struct fw_header_t *sdma0; + struct fw_header_t *sdma1; + struct fw_header_t *ce; + struct fw_header_t *pfp; + struct fw_header_t *me; + struct fw_header_t *mec1; + struct fw_header_t *mec2; +}; + +struct fw_expected_sizes_t { + u64 rlc; + u64 sdma0; + u64 sdma1; + u64 ce; + u64 pfp; + u64 me; + u64 mec1; + u64 mec2; +}; +static const struct fw_expected_sizes_t liverpool_fw_sizes = { + LVP_FW_RLC_SIZE, + LVP_FW_SDMA_SIZE, + LVP_FW_SDMA1_SIZE, + LVP_FW_CE_SIZE, + LVP_FW_PFP_SIZE, + LVP_FW_ME_SIZE, + LVP_FW_MEC_SIZE, + LVP_FW_MEC2_SIZE +}; +static const struct fw_expected_sizes_t gladius_fw_sizes = { + GL_FW_RLC_SIZE, + GL_FW_SDMA_SIZE, + GL_FW_SDMA1_SIZE, + GL_FW_CE_SIZE, + GL_FW_PFP_SIZE, + GL_FW_ME_SIZE, + GL_FW_MEC_SIZE, + GL_FW_MEC2_SIZE +}; + +void copy_edid(u8 **p, int sz) +{ + int i; + u8 *edid = *p; + u8 *off_edid = kern.edid; + + memset(edid, 0, sz); + *p += sz; + + for(i = 0; i < sz; i++) + *(edid + i) = *(off_edid + i); + + *p += sz; +} + +void copy_eap_hdd_key(u8 **p) +{ + int i; + u8 *eap_key = *p; + u8 *off_eap_key = kern.eap_hdd_key; + + memset(eap_key, 0, 0x20); + *p += 0x20; + + for(i = 0; i < 0x20; i++) + { + if(i < 0x10) + *(eap_key + i) = *(off_eap_key + 0xF - i); + else + *(eap_key + i) = *(off_eap_key + 0x2F - i); + } + *p += 0x20; +} + +int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + kern.printf("Copying %s firmware\n", name); + if (expected_size != (hdr->size_words * 4)) { + kern.printf("copy_firmware: %s: expected size %d, got %d\n", + name, expected_size, hdr->size_words * 4); + return 0; + } + + struct firmware_header *fhdr = (struct firmware_header*)*p; + memset(fhdr, 0, sizeof(*fhdr)); + *p += sizeof(*fhdr); + + memcpy(*p, hdr->blob, expected_size); + + fhdr->size_bytes = expected_size + sizeof(*fhdr); + fhdr->header_size_bytes = offsetof(struct firmware_header, raw); + fhdr->header_version_major = 1; + fhdr->header_version_minor = 0; + fhdr->ucode_version = 0x10; + fhdr->ucode_size_bytes = expected_size; + fhdr->ucode_array_offset_bytes = sizeof(*fhdr); + + *p += expected_size; + + return 1; +} + +int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); + fhdr->gfx1.ucode_feature_version = 21; + fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); + fhdr->rlc1.ucode_feature_version = 1; + fhdr->rlc1.save_and_restore_offset = 0x90; + fhdr->rlc1.clear_state_descriptor_offset = 0x3d; + fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? + fhdr->rlc1.master_pkt_description_offset = 0; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 2; + fhdr->ip_version_minor = 1; + fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); + fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; + fhdr->sdma1.ucode_change_version = 0; + fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +static const u32 pfp_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc4200016, // ldw r8, [r0, #0x16] + 0xdc030000, // mov ctr, r0 + 0xcc000049, // stw r0, [r0, #0x49] + 0xcc200013, // stw r0, [r8, #0x13] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 ce_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc420000c, // ldw r8, [r0, #0xc] + 0xdc030000, // mov ctr, r0 + 0xcc00002f, // stw r0, [r0, #0x2f] + 0xcc200012, // stw r0, [r8, #0x12] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 mec_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc43c000c, // ldw r15, [r0, #0x9] + 0xdc030000, // mov ctr, r0 + 0xcc00002b, // stw r0, [r0, #0x2b] + 0xcc3c000d, // stw r0, [r15, #0xd] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +#define PACKET_TYPE_NOP 0x10 + +static void patch_fw(void *p, const u32 *handler, int handler_size) { + int size = ((struct firmware_header*)p)->ucode_size_bytes; + int code_size = (size & ~0xfff) / 4; + int nop_start = code_size - 0x10; + + u32 *fw = p + sizeof(struct firmware_header); + kern.printf("NOP handler at 0x%x\n", nop_start); + memcpy(&fw[nop_start], handler, handler_size); + + // patch the branch table entry + for (int off = code_size; off < size/4; off++) { + if ((fw[off] >> 16) == PACKET_TYPE_NOP) { + fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; + } + } +} + +struct fw_info_t *get_fw_info() { + if (kern.gc_get_fw_info) { + return kern.gc_get_fw_info(); + } else if (kern.Starsha_UcodeInfo) { + return kern.Starsha_UcodeInfo; + } else { + return NULL; + } +} + +const struct fw_expected_sizes_t *get_fw_expected_sizes() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return &gladius_fw_sizes; + } else { + return &liverpool_fw_sizes; + } +} + +const char * get_gpu_name() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return "gladius"; + } else { + return "liverpool"; + } +} + +ssize_t firmware_extract(void *dest) +{ + u8 *p = dest; + + // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. + struct fw_info_t *info = get_fw_info(); + if (!info) { + kern.printf("firmware_extract: Could not locate firmware table"); + return -1; + } + const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); + + //Eap hdd key + cpio_hdr(&p, "key", DIR, 0); + cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); + copy_eap_hdd_key(&p); + + cpio_hdr(&p, "lib", DIR, 0); + cpio_hdr(&p, "lib/firmware", DIR, 0); + + /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ + int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); + cpio_hdr(&p, "lib/firmware/edid", DIR, 0); + cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); + copy_edid(&p, edid_sz); + + char dir[7]; + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) + kern.snprintf(dir, sizeof(dir), "amdgpu"); + else + kern.snprintf(dir, sizeof(dir), "amdgpu"); + + char dir_path[64]; + kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); + cpio_hdr(&p, dir_path, DIR, 0); + + char pfp_path[64]; + kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", pfp_path); + cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); + u8 *pfp = p; + if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) + return -1; + patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); + + char me_path[64]; + kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", me_path); + cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); + if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) + return -1; + + char ce_path[64]; + kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", ce_path); + cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); + u8 *ce = p; + if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) + return -1; + patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); + + char mec_path[64]; + kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec_path); + cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); + u8 *mec1 = p; + if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) + return -1; + patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); + + char mec2_path[64]; + kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec2_path); + cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); + u8 *mec2 = p; + if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) + return -1; + patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); + + char rlc_path[64]; + kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", rlc_path); + cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); + if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) + return -1; + + char sdma_path[64]; + kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma_path); + cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); + if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + char sdma1_path[64]; + kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma1_path); + cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); + if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + size_t size = p - (u8*)dest; + if (size > FW_CPIO_SIZE) { + kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); + return -1; + } + + return size; +} diff --git a/linux/ps4-kexec-1300-baikal/firmware.h b/linux/ps4-kexec-1300-baikal/firmware.h new file mode 100644 index 0000000..4835983 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/firmware.h @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include "types.h" + +//sizes eap_hdd_key +#define EAP_HDD_KEY_SIZE 0x20 +#define EDID_SIZE 256 + +// sizes for liverpool +#define LVP_FW_CE_SIZE 8576 +#define LVP_FW_ME_SIZE 16768 +#define LVP_FW_MEC_SIZE 16768 +#define LVP_FW_MEC2_SIZE 16768 +#define LVP_FW_PFP_SIZE 16768 +#define LVP_FW_RLC_SIZE 6144 +#define LVP_FW_SDMA_SIZE 4200 +#define LVP_FW_SDMA1_SIZE 4200 +// sizes for gladius +#define GL_FW_CE_SIZE 8576 +#define GL_FW_ME_SIZE 16768 +#define GL_FW_MEC_SIZE 16768 +#define GL_FW_MEC2_SIZE 16768 +#define GL_FW_PFP_SIZE 16768 +#define GL_FW_RLC_SIZE 8192 +#define GL_FW_SDMA_SIZE 4200 +#define GL_FW_SDMA1_SIZE 4200 + +#define MAX(x ,y) (((x) > (y)) ? (x) : (y)) +#define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) + +#define FW_CE_SIZE MAX_FW_SIZE(CE) +#define FW_ME_SIZE MAX_FW_SIZE(ME) +#define FW_MEC_SIZE MAX_FW_SIZE(MEC) +#define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) +#define FW_PFP_SIZE MAX_FW_SIZE(PFP) +#define FW_RLC_SIZE MAX_FW_SIZE(RLC) +#define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) +#define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) + +// Conservative value (max 113 bytes plus name size plus alignment) +#define CPIO_HEADER_SIZE 256 + +#define FW_HEADER_SIZE 256 + +// Leave space for 16 files (currently 12) +#define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ + FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ + FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ + FW_HEADER_SIZE * 8) + +ssize_t firmware_extract(void *dest); + +#endif diff --git a/linux/ps4-kexec-1300-baikal/kernel.c b/linux/ps4-kexec-1300-baikal/kernel.c new file mode 100644 index 0000000..b6d31b4 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/kernel.c @@ -0,0 +1,383 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "string.h" +#include "elf.h" +#include "x86.h" +#include "../magic.h" + +struct ksym_t kern; +int (*early_printf)(const char *fmt, ...) = NULL; + +#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) + +#ifdef NO_SYMTAB + +#define RESOLVE_NOERR(name) do { \ + if (kern_off_ ## name == 0) { \ + kern.name = 0; \ + } else { \ + kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ + } \ +} while (0); + +#define RESOLVE(name) do { \ + if (kern_off_ ## name == 0) { \ + return 0; \ + } \ + RESOLVE_NOERR(name) \ +} while (0); + +#else + +#define KERNSIZE 0x2000000 + +static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; +static Elf64_Sym *symtab; +static char *strtab; +static size_t strtab_size; + +static Elf64_Ehdr *find_kern_ehdr(void) +{ + // Search for the kernel copy embedded in ubios, then follow it to see + // where it was relocated to + for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; + if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { + for (size_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; + if (phdr->p_type == PT_PHDR) { + return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); + } + } + } + } + return NULL; +} + +static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf64_Dyn *)phdr->p_vaddr; + } + } + return NULL; +} + +static int elf_parse_dyn(Elf64_Dyn *dyn) +{ + for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_SYMTAB: + symtab = (Elf64_Sym *)dp->d_un.d_ptr; + break; + case DT_STRTAB: + strtab = (char *)dp->d_un.d_ptr; + break; + case DT_STRSZ: + strtab_size = dp->d_un.d_val; + break; + } + } + return symtab && strtab && strtab_size; +} + +void *kernel_resolve(const char *name) +{ + for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { + if (!strcmp(name, &strtab[sym->st_name])) { + eprintf("kern.%s = %p\n", name, (void*)sym->st_value); + return (void *)sym->st_value; + } + } + eprintf("Failed to resolve symbol '%s'\n", name); + return NULL; +} + +#define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) +#define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; + +#endif + +static int resolve_symbols(void) +{ + RESOLVE(printf); + RESOLVE(snprintf); + early_printf = kern.printf; + RESOLVE(copyin); + RESOLVE(copyout); + RESOLVE(copyinstr); + RESOLVE(kernel_map); + RESOLVE(kernel_pmap_store); + RESOLVE(kmem_alloc_contig); + RESOLVE(kmem_free); + RESOLVE(pmap_extract); + RESOLVE(pmap_protect); + RESOLVE(sysent); + RESOLVE(sched_pin); + RESOLVE(sched_unpin); + RESOLVE(smp_rendezvous); + RESOLVE(smp_no_rendevous_barrier); + RESOLVE(icc_query_nowait); + RESOLVE_NOERR(Starsha_UcodeInfo); + RESOLVE_NOERR(gpu_devid_is_9924); + RESOLVE_NOERR(gc_get_fw_info); + RESOLVE_NOERR(eap_hdd_key); + RESOLVE_NOERR(edid); + RESOLVE(wlanbt); + RESOLVE(kern_reboot); + RESOLVE(set_gpu_freq); + RESOLVE(set_pstate); + RESOLVE(update_vddnp); + RESOLVE(set_cu_power_gate); + RESOLVE_NOERR(pstate_before_shutdown); + return 1; +} + +#define M_WAITOK 0x0002 +#define M_ZERO 0x0100 + +#define VM_MEMATTR_DEFAULT 0x06 + +void *kernel_alloc_contig(size_t size) +{ + // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... + vm_offset_t ret = 0; + while(!(ret = kern.kmem_alloc_contig( + *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, + ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); + + /*if (!ret) { + kern.printf("Failed to allocate %zud bytes\n", size); + return NULL; + }*/ + return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); +} + +void kernel_free_contig(void *addr, size_t size) +{ + if (!addr) + return; + kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); +} + +int kernel_hook_install(void *target, void *hook) +{ + uintptr_t t = (uintptr_t)target; // addr to redirect to + uintptr_t h = (uintptr_t)hook; // place to write the thunk + + if (!hook || !target) { + return 0; + } + + kern.printf("kernel_hook_install(%p, %p)\n", target, hook); + + if (!(t & (1L << 63))) { + kern.printf("\n===================== WARNING =====================\n"); + kern.printf("hook target function address: %p\n", target); + kern.printf("It looks like we're running from userland memory.\n"); + kern.printf("Please run this code from a kernel memory mapping.\n\n"); + return 0; + } + s64 displacement = t - (h + 5); + + kern.sched_pin(); + u64 wp = write_protect_disable(); + if (displacement < -0x80000000 || displacement > 0x7fffffff) { + kern.printf(" Using 64bit absolute jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[2]; + s32 zero; + void *target; + } jmp = { + .op = { 0xff, 0x25 }, + .zero = 0, + .target = target, + }; + ASSERT_STRSIZE(struct jmp_t, 14); + memcpy(hook, &jmp, sizeof(jmp)); + } else { + kern.printf(" Using 32bit relative jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[1]; + s32 imm; + } jmp = { + .op = { 0xe9 }, + .imm = displacement, + }; + ASSERT_STRSIZE(struct jmp_t, 5); + memcpy(hook, &jmp, sizeof(jmp)); + } + wbinvd(); + write_protect_restore(wp); + kern.sched_unpin(); + + return 1; +} + +void kernel_syscall_install(int num, void *call, int narg) +{ + struct sysent_t *sy = &kern.sysent[num]; + + kern.sched_pin(); + u64 wp = write_protect_disable(); + + memset(sy, 0, sizeof(*sy)); + sy->sy_narg = narg; + sy->sy_call = call; + sy->sy_thrcnt = 1; + + write_protect_restore(wp); + kern.sched_unpin(); +} + +void kernel_remap(void *start, void *end, int perm) +{ + u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); + u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); + + kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); + kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); +} + +static volatile int _global_test = 0; + +#ifndef DO_NOT_REMAP_RWX +extern u8 _start[], _end[]; + +static int patch_pmap_check(void) +{ + u8 *p; + + for (p = (u8*)kern.pmap_protect; + p < ((u8*)kern.pmap_protect + 0x500); p++) { + #ifdef PS4_6_72 + if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 + p[5] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + /*#ifdef PS4_5_05 + if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { + p[1] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + }*/ + #else + if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { + p[2] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + #endif + } + kern.printf("pmap_protect patch failed!\n"); + return 0; +} +#endif + +int kernel_init(void *_early_printf) +{ + int rv = -1; + + if (_early_printf) + early_printf = _early_printf; + + eprintf("kernel_init()\n"); + +#ifdef KASLR + // use `early_printf` to calculate kernel base + if (early_printf == NULL) + return 0; + + kern.kern_base = (u64)(early_printf - kern_off_printf); + if ((kern.kern_base & PAGE_MASK) != 0) { + eprintf("Kernel base is not aligned\n"); + return 0; + } else { + eprintf("Kernel base = %llx\n", kern.kern_base); + } + + u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); + u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); + +#else + kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 + + u64 DMPML4I = 0x1fc; + u64 DMPDPI = 0; +#endif + + kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); + eprintf("Direct map base = %llx\n", kern.dmap_base); + + // We may not be mapped writable yet, so to be able to write to globals + // we need WP disabled. + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + +#ifndef NO_SYMTAB + Elf64_Ehdr *ehdr = find_kern_ehdr(); + if (!ehdr) { + eprintf("Could not find kernel ELF header\n"); + goto err; + } + eprintf("ELF header at %p\n", ehdr); + + Elf64_Dyn *dyn = elf_get_dyn(ehdr); + if (!dyn) { + eprintf("Could not find kernel dynamic header\n"); + goto err; + } + eprintf("ELF dynamic section at %p\n", dyn); + + if (!elf_parse_dyn(dyn)) { + eprintf("Failed to parse ELF dynamic section\n"); + goto err; + } +#endif + + if (!resolve_symbols()) { + eprintf("Failed to resolve all symbols\n"); + goto err; + } + + // Pin ourselves as soon as possible. This is expected to be released by the caller. + kern.sched_pin(); + +#ifndef DO_NOT_REMAP_RWX + if (!patch_pmap_check()) + goto err; +#endif + +#ifndef DO_NOT_REMAP_RWX + // kernel_remap may need interrupts, but may not write to globals! + enable_interrupts(); + kernel_remap(_start, _end, 7); + disable_interrupts(); +#endif + + // Writing to globals is now safe. + + kern.printf("Testing global variable access (write protection)...\n"); + _global_test = 1; + kern.printf("OK.\n"); + + kern.printf("Kernel interface initialized\n"); + rv = 0; + +err: + write_protect_restore(wp); + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1300-baikal/kernel.h b/linux/ps4-kexec-1300-baikal/kernel.h new file mode 100644 index 0000000..1833f5b --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/kernel.h @@ -0,0 +1,126 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KERNEL_H +#define KERNEL_H + +#include "types.h" +#include "reboot.h" + +#define PAGE_SIZE 0x4000 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define PML4SHIFT 39 +#define PDPSHIFT 30 +#define PDRSHIFT 21 +#define PAGE_SHIFT 12 + +#define KVADDR(l4, l3, l2, l1) ( \ + ((unsigned long)-1 << 47) | \ + ((unsigned long)(l4) << PML4SHIFT) | \ + ((unsigned long)(l3) << PDPSHIFT) | \ + ((unsigned long)(l2) << PDRSHIFT) | \ + ((unsigned long)(l1) << PAGE_SHIFT)) + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) +#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX + +typedef u64 vm_paddr_t; +typedef u64 vm_offset_t; +typedef u64 vm_size_t; +typedef void * vm_map_t; +typedef char vm_memattr_t; +typedef void * pmap_t; + +typedef void (*smp_rendezvous_callback_t)(void *); + +struct sysent_t { + int sy_narg; + void *sy_call; + u16 sy_auevent; + void *sy_systrace_args_func; + int sy_entry; + int sy_return; + int sy_flags; + int sy_thrcnt; +}; + +struct ksym_t { + // two parameters related to kaslr (they are not symbols) + uintptr_t kern_base; + uintptr_t dmap_base; + + int (*printf)(const char *fmt, ...); + int (*snprintf)(const char *fmt, ...); + int (*copyin)(const void *uaddr, void *kaddr, size_t len); + int (*copyout)(const void *kaddr, void *uaddr, size_t len); + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); + + void **kernel_map; + void *kernel_pmap_store; + vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, + vm_paddr_t low, vm_paddr_t high, + unsigned long alignment, + unsigned long boundary, + vm_memattr_t memattr); + void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); + vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); + void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); + + struct sysent_t *sysent; + + void (*sched_pin)(void); + void (*sched_unpin)(void); + void (*smp_rendezvous)(smp_rendezvous_callback_t, + smp_rendezvous_callback_t, + smp_rendezvous_callback_t, void *); + // yes...it is misspelled :) + void (*smp_no_rendevous_barrier)(void *); + void *icc_query_nowait; + void *Starsha_UcodeInfo; + int (*gpu_devid_is_9924)(); + void *(*gc_get_fw_info)(); + void *eap_hdd_key; + void *edid; + void (*wlanbt)(unsigned int state); + int (*kern_reboot)(int magic); + void(*set_gpu_freq)(unsigned int num, unsigned int freq); + void(*set_pstate)(unsigned int val); + void(*update_vddnp)(unsigned int val); + void(*set_cu_power_gate)(unsigned int val); + void *pstate_before_shutdown; +}; + +extern struct ksym_t kern; + +static inline int curcpu(void) +{ + int cpuid; + // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw + asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); + return cpuid; +} + +// Assign a working printf function to this to debug the symbol resolver +extern int (*early_printf)(const char *fmt, ...); + +void *kernel_resolve(const char *name); + +void *kernel_alloc_contig(size_t size); +void kernel_free_contig(void *addr, size_t size); + +void kernel_remap(void *start, void *end, int perm); + +void kernel_syscall_install(int num, void *call, int narg); +int kernel_hook_install(void *target, void *hook); + +int kernel_init(void *early_printf); + +#endif diff --git a/linux/ps4-kexec-1300-baikal/kexec.c b/linux/ps4-kexec-1300-baikal/kexec.c new file mode 100644 index 0000000..d659229 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/kexec.c @@ -0,0 +1,229 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "linux_boot.h" +#include "x86.h" +#include "kexec.h" +#include "firmware.h" +#include "string.h" +#include "acpi.h" + +static int k_copyin(const void *uaddr, void *kaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(kaddr, uaddr, len); + return 0; +} + +static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + const char *ustr = (const char*)uaddr; + char *kstr = (char*)kaddr; + size_t ret; + if (!uaddr || !kaddr) + return EFAULT; + ret = strlcpy(kstr, ustr, len); + if (ret >= len) { + if (done) + *done = len; + return ENAMETOOLONG; + } else { + if (done) + *done = ret + 1; + } + return 0; +} + +static int k_copyout(const void *kaddr, void *uaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(uaddr, kaddr, len); + return 0; +} + +int sys_kexec(void *td, struct sys_kexec_args *uap) +{ + int err = 0; + size_t initramfs_size = uap->initramfs_size; + void *image = NULL; + void *initramfs = NULL; + size_t firmware_size = 0; + struct boot_params *bp = NULL; + size_t cmd_line_maxlen = 0; + char *cmd_line = NULL; + + int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; + int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; + + kern.printf("sys_kexec invoked\n"); + kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, + uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); + + // Look up our shutdown hook point + void *icc_query_nowait = kern.icc_query_nowait; + if (!icc_query_nowait) { + err = ENOENT; + goto cleanup; + } + + // Set gpu frequencies and pstate + // FAT&SLIM / PRO + kern.set_pstate(3); + + kern.set_gpu_freq(0, 800); //800 //800 + kern.set_gpu_freq(1, 673); //673 //853 + kern.set_gpu_freq(2, 609); //610 //711 + kern.set_gpu_freq(3, 800); //800 //800 + kern.set_gpu_freq(4, 800); //800 //911 + kern.set_gpu_freq(5, 711); //711 //800 + kern.set_gpu_freq(6, 711); //711 //984 + kern.set_gpu_freq(7, 673); //673 //673 + + kern.update_vddnp(0x12); + kern.set_cu_power_gate(0x12); + + // Copy in kernel image + image = kernel_alloc_contig(uap->image_size); + if (!image) { + kern.printf("Failed to allocate image\n"); + err = ENOMEM; + goto cleanup; + } + err = copyin(uap->image, image, uap->image_size); + if (err) { + kern.printf("Failed to copy in image\n"); + goto cleanup; + } + + // Copy in initramfs + initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); + if (!initramfs) { + kern.printf("Failed to allocate initramfs\n"); + err = ENOMEM; + goto cleanup; + } + + err = firmware_extract(((u8*)initramfs)); + if (err < 0) { + kern.printf("Failed to extract GPU firmware - continuing anyway\n"); + } else { + firmware_size = err; + } + + if (initramfs_size) { + err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); + if (err) { + kern.printf("Failed to copy in initramfs\n"); + goto cleanup; + } + } + initramfs_size += firmware_size; + + // Copy in cmdline + cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; + cmd_line = kernel_alloc_contig(cmd_line_maxlen); + if (!cmd_line) { + kern.printf("Failed to allocate cmdline\n"); + err = ENOMEM; + goto cleanup; + } + err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); + if (err) { + kern.printf("Failed to copy in cmdline\n"); + goto cleanup; + } + cmd_line[cmd_line_maxlen - 1] = 0; + + kern.printf("\nkexec parameters:\n"); + kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); + kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", + initramfs_size, uap->initramfs_size); + kern.printf(" Kernel command line: %s\n", cmd_line); + kern.printf(" Kernel image buffer: %p\n", image); + kern.printf(" Initramfs buffer: %p\n", initramfs); + + // Allocate our boot params + bp = kernel_alloc_contig(sizeof(*bp)); + if (!bp) { + kern.printf("Failed to allocate bp\n"); + err = ENOMEM; + goto cleanup; + } + + // Initialize bp + // TODO should probably do this from cpu_quiesce_gate, then bp doesn't + // need to be allocated here, just placed directly into low mem + set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); + + prepare_boot_params(bp, image); + + // Hook the final ICC shutdown function + if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { + kern.printf("Failed to install shutdown hook\n"); + err = EINVAL; + goto cleanup; + } + + kern.printf("******************************************************\n"); + kern.printf("kexec successfully armed. Please shut down the system.\n"); + kern.printf("******************************************************\n\n"); + +/* + kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); + if (kern.kern_reboot(RB_POWEROFF) == -1) + kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); +*/ + return 0; + +cleanup: + kernel_free_contig(cmd_line, cmd_line_maxlen); + kernel_free_contig(bp, sizeof(*bp)); + kernel_free_contig(image, uap->image_size); + kernel_free_contig(initramfs, uap->initramfs_size); + return err; + + copyout(NULL, NULL, 0); +} + +int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) +{ + int rv = 0; + + // potentially needed to write early_printf + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + + if (kernel_init(_early_printf) < 0) { + rv = -1; + goto cleanup; + } + + kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); + kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); + kern.printf("kexec_init() successful\n\n"); + + if (sys_kexec_ptr) + *sys_kexec_ptr = sys_kexec; + +cleanup: + write_protect_restore(wp); + if (kern.sched_unpin && wp & CR0_WP) { + // If we're returning to a state with WP enabled, assume the caller + // wants the thread unpinned. Else the caller is expected to + // call kern.sched_unpin() manually. + kern.sched_unpin(); + } + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1300-baikal/kexec.h b/linux/ps4-kexec-1300-baikal/kexec.h new file mode 100644 index 0000000..6c31637 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/kexec.h @@ -0,0 +1,38 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KEXEC_H +#define KEXEC_H + +#include "types.h" + +#define SYS_KEXEC 153 +#define SYS_KEXEC_NARGS 6 + +struct sys_kexec_args { + void *image; + size_t image_size; + void *initramfs; + size_t initramfs_size; + char *cmd_line; + int vram_gb; +}; + +typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); + +// Note: td is unused, you can pass NULL if you call this directly. +int sys_kexec(void *td, struct sys_kexec_args *uap); + +int kernel_init(void *early_printf); + +int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) + __attribute__ ((section (".init"))); + +#endif diff --git a/linux/ps4-kexec-1300-baikal/kexec.ld b/linux/ps4-kexec-1300-baikal/kexec.ld new file mode 100644 index 0000000..1bcf26c --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/kexec.ld @@ -0,0 +1,25 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +ENTRY(kexec_init) + +SECTIONS { + /* We don't do GOT relocation and rely on nothing ending up using the GOT + * (-fno-common helps here) */ + /DISCARD/ : { *(.comment) *(.got) } + _start = .; + .init : { *(.init) *(.init.*) } + .text : { *(.text) *(.text.*) } + .data : { *(.data) *(.data.*) } + .rodata : { *(.rodata) *(.rodata.*) } + .bss : { *(.bss) *(.bss.*) *(COMMON)} + .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ + _end = .; +} diff --git a/linux/ps4-kexec-1300-baikal/linux_boot.c b/linux/ps4-kexec-1300-baikal/linux_boot.c new file mode 100644 index 0000000..226dd4f --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/linux_boot.c @@ -0,0 +1,453 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "linux_boot.h" +#include "types.h" +#include "string.h" +#include "x86.h" +#include "kernel.h" +#include "uart.h" +#include "acpi.h" + +void uart_write_byte(u8 b); + +static u64 vram_base = 0x100000000; +// Current code assumes it's a power of two. +static u64 vram_size = 1024 * 1024 * 1024; +static int vram_gb = 2; + +#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) + +#define MSR_GS_BASE 0xc0000101 /* 64bit GS base */ + +struct desc_struct { + u16 limit0; + u16 base0; + u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; + u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, + uintptr_t new_cr3, uintptr_t gdt_ptr); +extern uint8_t *jmp_to_linux; +extern size_t jmp_to_linux_size; + +// FreeBSD DMAP addresses +struct linux_boot_info { + void *linux_image; + void *initramfs; + size_t initramfs_size; + struct boot_params *bp; + char *cmd_line; +}; +static struct linux_boot_info nix_info; + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v) +{ + nix_info.linux_image = linux_image; + nix_info.bp = bp; + nix_info.initramfs = initramfs; + nix_info.initramfs_size = initramfs_size; + nix_info.cmd_line = cmd_line; + vram_gb = v; +} + +static volatile int halted_cpus = 0; + +static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, + u32 type) +{ + uint8_t idx = bp->e820_entries; + bp->e820_map[idx].addr = addr; + bp->e820_map[idx].size = size; + bp->e820_map[idx].type = type; + bp->e820_entries++; +} + +void prepare_boot_params(struct boot_params *bp, u8 *linux_image) +{ + memset(bp, 0, sizeof(struct boot_params)); + struct boot_params *bp_src = (struct boot_params *)linux_image; + memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + + ((u8 *)&bp_src->hdr.jump)[1]); + + // These values are from fw 1.01 + bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); + bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + // This used to be VRAM, but we reclaim it as RAM + bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); + // Instead, carve out VRAM from the beginning of high memory + bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, + SMAP_TYPE_MEMORY); +} + +#define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_FB_OFFSET 0x2068 +#define HDP_NONSURFACE_BASE 0x2c04 +#define CONFIG_MEMSIZE 0x5428 + +static void configure_vram(void) +{ + u64 mmio_base = 0xe4800000; + u64 fb_base = 0x0f00000000; + u64 fb_top = fb_base + vram_gb * vram_size - 1; + + WR32(mmio_base + MC_VM_FB_LOCATION, 0); + WR32(mmio_base + HDP_NONSURFACE_BASE, 0); + + WR32(mmio_base + MC_VM_FB_LOCATION, + ((fb_top >> 24) << 16) | (fb_base >> 24)); + WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); + WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); + WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); +} + +#define IA32_MTRR_DEF_TYPE 0x2ff +#define MTRR_BASE(i) (0x200 + 2*i) +#define MTRR_MASK(i) (0x201 + 2*i) + +static void setup_mtrr(void) +{ + disable_interrupts(); + u64 cr0 = cr0_read(); + cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); + wbinvd(); + cr3_write(cr3_read()); // TLB flush + + wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled + + // Low memory (0GB-2GB) = WB + wrmsr(MTRR_BASE(0), 0x0000000006); + wrmsr(MTRR_MASK(0), 0xff80000800); + // High memory (4GB-8GB) = WB + wrmsr(MTRR_BASE(1), 0x0100000006); + wrmsr(MTRR_MASK(1), 0xff00000800); + // High memory (8GB-10GB) = WB + wrmsr(MTRR_BASE(2), 0x0200000006); + wrmsr(MTRR_MASK(2), 0xff80000800); + // VRAM (4GB-4GB+vram_size) = UC + wrmsr(MTRR_BASE(3), 0x0100000000); + wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); + + wbinvd(); + cr3_write(cr3_read()); // TLB flush + wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable + cr0_write(cr0); + enable_interrupts(); +} + +static void cleanup_interrupts(void) +{ + int i; + disable_interrupts(); + + // Reset APIC stuff (per-CPU) + *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; + for (i = 0x320; i < 0x380; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + for (i = 0x480; i < 0x500; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; + for (i = 0x500; i < 0x540; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; + + // Fix the LVT offset for thresholding + wrmsr(0x413, (1L<<24) | (1L<<52)); + wrmsr(0xc0000408, (1L<<24) | (1L<<52)); +} + +#define DEFAULT_STACK 0 + +#define DPL0 0x0 +#define DPL3 0x3 + +#define BPCIE_BAR2 0xc8800000 +#define BPCIE_HPET_BASE 0x109000 +#define BPCIE_HPET_SIZE 0x400 + +static void cpu_quiesce_gate(void *arg) +{ + + + // Ensure we can write anywhere + cr0_write(cr0_read() & ~CR0_WP); + + // Interrupt stuff local to each CPU + cleanup_interrupts(); + + // We want to set up MTRRs on all CPUs + setup_mtrr(); + + if (curcpu() != 0) { + // We're not on BSP. Try to halt. + __sync_fetch_and_add(&halted_cpus, 1); + cpu_stop(); + } + + uart_write_str("kexec: Waiting for secondary CPUs...\n"); + + // wait for all cpus to halt + while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); + + uart_write_str("kexec: Secondary CPUs quiesced\n"); + + //* Put ident mappings in current page tables + // Should not be needed, but maybe helps for debugging? + cr4_pge_disable(); + u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); + u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + // Clear (really) low mem. + // Linux reads from here to try and access EBDA... + // get_bios_ebda reads u16 from 0x40e + // reserve_ebda_region reads u16 from 0x413 + // Writing zeros causes linux to default to marking + // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. + // It doesn't match the ps4 e820 map, but that seems OK. + memset((void *)0, 0, 0x1000); + + // Create a new page table hierarchy out of the way of linux + // Accessed via freebsd direct map + pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt + // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| + memset(pml4_base, 0, 512 * sizeof(u64) * 2); + pdp_base = pml4_base + 512; + u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; + pml4_base[0] = pdpe; + // Maintain the freebsd direct map + pml4_base[DM_PML4_BASE] = pdpe; + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + uart_write_str("kexec: Setting up GDT...\n"); + + desc_ptr gdt_ptr; + struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); + gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; + gdt_ptr.address = DM_TO_ID(desc); + + // clear + memset(desc, 0, gdt_ptr.limit + 1); + // Most things are ignored in 64bit mode, and we will never be in + // 32bit/compat modes, so just setup another pure-64bit environment... + // Linux inits it's own GDT in secondary_startup_64 + // 0x10 + desc[2].limit0 = 0xffff; + desc[2].base0 = 0x0000; + desc[2].base1 = 0x0000; + desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; + desc[2].s = 1; + desc[2].dpl = 0; + desc[2].p = 1; + desc[2].limit = 0xf; + desc[2].avl = 0; + desc[2].l = 1; + desc[2].d = 0; + desc[2].g = 0; + desc[2].base2 = 0x00; + // 0x18 + desc[3].limit0 = 0xffff; + desc[3].base0 = 0x0000; + desc[3].base1 = 0x0000; + desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; + desc[3].s = 1; + desc[3].dpl = 0; + desc[3].p = 1; + desc[3].limit = 0xf; + desc[3].avl = 0; + desc[3].l = 0; + desc[3].d = 0; + desc[3].g = 0; + desc[3].base2 = 0x00; + // Task segment value + // 0x20 + desc[4].limit0 = 0x0000; + desc[4].base0 = 0x0000; + desc[4].base1 = 0x0000; + desc[4].type = SEG_TYPE_TSS; + desc[4].s = 1; + desc[4].dpl = 0; + desc[4].p = 1; + desc[4].limit = 0x0; + desc[4].avl = 0; + desc[4].l = 0; + desc[4].d = 0; + desc[4].g = 0; + desc[4].base2 = 0x00; + + uart_write_str("kexec: Relocating stub...\n"); + + // Relocate the stub and jump to it + // TODO should thunk_copy be DMAP here? + void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); + memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); + // XXX The +0x200 is for the iret stack in linux_thunk.S + uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; + + uart_write_str("kexec: Setting up boot params...\n"); + + // XXX we write into this bootargs and pass it to the kernel, but in + // jmp_to_linux we use the bootargs from the image as input. So they + // MUST MATCH! + struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; + *bp_lo = *nix_info.bp; + lowmem_pos += sizeof(struct boot_params); + + struct setup_header *shdr = &bp_lo->hdr; + shdr->cmd_line_ptr = lowmem_pos; + shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; + shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; + bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; + bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; + shdr->hardware_subarch = X86_SUBARCH_PS4; + // This needs to be nonzero for the initramfs to work + shdr->type_of_loader = 0xd0; // kexec + + strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, + nix_info.bp->hdr.cmdline_size); + lowmem_pos += strlen(nix_info.cmd_line) + 1; + + uart_write_str("kexec: Cleaning up hardware...\n"); + + // Disable IOMMU + *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; + + // Disable all MSIs on Baikal (bus=0, slot=20) + disableMSI(0xf80a00e0); //func = 0 Baikal ACPI + disableMSI(0xf80a10e0); //func = 1 Baikal Ethernet Controller + disableMSI(0xf80a20e0); //func = 2 Baikal SATA AHCI Controller + disableMSI(0xf80a30e0); //func = 3 Baikal SD/MMC Host Controller + disableMSI(0xf80a40e0); //func = 4 Baikal PCI Express Glue and Miscellaneous Devices + disableMSI(0xf80a50e0); //func = 5 Baikal DMA Controller + disableMSI(0xf80a60e0); //func = 6 Baikal Baikal Memory (DDR3/SPM) + disableMSI(0xf80a70e0); //func = 7 Baikal Baikal USB 3.0 xHCI Host Controller + + // Stop HPET timers + //*(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + 0x10) &= ~(1UL << 0); //General Configuration Register + /* + u64 NUM_TIM_CAP = *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE) & 0x1F00; + for (u64 N = 0; N <= NUM_TIM_CAP; N++) { + *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + (0x20*N) + 0x100) &= ~(1UL << 2); //Timer N Configuration and Capabilities Register + } + */ + uart_write_str("kexec: Reconfiguring VRAM...\n"); + + configure_vram(); + + uart_write_str("kexec: Resetting GPU...\n"); + + // Softreset GPU + *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks + *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC + *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 +// *(volatile u64 *)PA_TO_DM(0xe480d248) = 1; // Halt SDMA1 eeply + *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 + *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC + + *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply + +// *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x10003; // Softreset GFX/CP/RLC + *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x30005; // Softreset GFX/CP/RLC eeply + +// udelay(150); +// *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x10003; + *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x30005; //eeply +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) |= 0x00100140; // Softreset SDMA/GRBM +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~0x00100140; +// udelay(150); + + // Enable audio output + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; + *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; + +// // Set pin caps of pin 2 to vendor defined, to hide it +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// // Set pin caps of pin 3 to !HDMI +// *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; +// *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; + // Set pin configuration default + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; + + uart_write_str("kexec: About to relocate and jump to kernel\n"); + + ((jmp_to_linux_t)thunk_copy)( + DM_TO_ID(nix_info.linux_image), + DM_TO_ID(bp_lo), + DM_TO_ID(pml4_base), + (uintptr_t)&gdt_ptr + ); + // should never reach here + uart_write_str("kexec: unreachable (?)\n"); +} + +// Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) +int hook_icc_query_nowait(u8 *icc_msg) +{ + kern.printf("hook_icc_query_nowait called\n"); + + // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot + //In alternative we can re-enable it here, but sometimes that give problems.. + kern.wlanbt(0x2); + + fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); + + kern.printf("ACPI tables fixed\n"); + + // Transition to BSP and halt other cpus + // smp_no_rendevous_barrier is just nullsub, but it is treated specially by + // smp_rendezvous. This is the easiest way to do this, since we can't assume + // we're already running on BSP. Since smp_rendezvous normally waits on all + // cpus to finish the callbacks, we just never return... + kern.smp_rendezvous(kern.smp_no_rendevous_barrier, + cpu_quiesce_gate, + kern.smp_no_rendevous_barrier, NULL); + + // should never reach here + kern.printf("hook_icc_query_nowait: unreachable (?)\n"); + return 0; +} diff --git a/linux/ps4-kexec-1300-baikal/linux_boot.h b/linux/ps4-kexec-1300-baikal/linux_boot.h new file mode 100644 index 0000000..2232b35 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/linux_boot.h @@ -0,0 +1,88 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef LINUX_BOOT_H +#define LINUX_BOOT_H + +#include "types.h" + +#define SMAP_TYPE_MEMORY 1 +#define SMAP_TYPE_RESERVED 2 +#define SMAP_TYPE_ACPI_RECLAIM 3 +#define SMAP_TYPE_ACPI_NVS 4 +#define SMAP_TYPE_UNUSABLE 5 +#define SMAP_TYPE_PMEM 7 + +#define X86_SUBARCH_PS4 5 + +struct e820entry { + u64 addr; /* start of memory segment */ + u64 size; /* size of memory segment */ + u32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct setup_header { + u8 setup_sects; + u16 root_flags; + u32 syssize; + u16 ram_size; + u16 vid_mode; + u16 root_dev; + u16 boot_flag; + u16 jump; + u32 header; + u16 version; + u32 realmode_swtch; + u16 start_sys; + u16 kernel_version; + u8 type_of_loader; + u8 loadflags; + u16 setup_move_size; + u32 code32_start; + u32 ramdisk_image; + u32 ramdisk_size; + u32 bootsect_kludge; + u16 heap_end_ptr; + u8 ext_loader_ver; + u8 ext_loader_type; + u32 cmd_line_ptr; + u32 initrd_addr_max; + u32 kernel_alignment; + u8 relocatable_kernel; + u8 min_alignment; + u16 xloadflags; + u32 cmdline_size; + u32 hardware_subarch; + u64 hardware_subarch_data; + u32 payload_offset; + u32 payload_length; + u64 setup_data; + u64 pref_address; + u32 init_size; + u32 handover_offset; +} __attribute__((packed)); + +#define E820MAX 128 /* number of entries in E820MAP */ + +OSTRUCT(boot_params, 0x1000) +OFIELD(0x0c0, u32 ext_ramdisk_image); +OFIELD(0x0c4, u32 ext_ramdisk_size); +OFIELD(0x0c8, u32 ext_cmd_line_ptr); +OFIELD(0x1e8, u8 e820_entries); +OFIELD(0x1f1, struct setup_header hdr); +OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); +OSTRUCT_END + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v); +void prepare_boot_params(struct boot_params *bp, u8 *linux_image); +int hook_icc_query_nowait(u8 *icc_msg); + +#endif diff --git a/linux/ps4-kexec-1300-baikal/linux_thunk.S b/linux/ps4-kexec-1300-baikal/linux_thunk.S new file mode 100644 index 0000000..551964e --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/linux_thunk.S @@ -0,0 +1,90 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +.intel_syntax noprefix + +.equ setup_sects, 0x1f1 +.equ shdr_syssize, 0x1f4 +.equ pref_address, 0x258 + +.text + +#void jmp_to_linux( +# uintptr_t image_base, rdi +# uintptr_t bootargs, rsi +# uintptr_t new_cr3, rdx +# uintptr_t gdt_ptr rcx +#); +.globl jmp_to_linux +jmp_to_linux: + # switch to new gdt + data segments + cli + lgdt [rcx] + #xor eax, eax + mov eax, 0x18 + mov ds, eax + mov ss, eax + mov es, eax + mov fs, eax + mov gs, eax + + # switch to our own page tables (in low mem) + mov cr3, rdx + + # now we're on our own page tables, so we can obliterate the rest of memory + # TODO make sure we don't inadvertently overwrite (important) smap regions + # I think on ps4 we'll actually want to load to 0x700000 + # since we have tons of free room there. + # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We + # should choose a higher address as pref_address. + + # save args + mov r12, rdi + mov r13, rsi + + # memmove(pref_address, , (syssize * 0x10) / 8) + #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go + #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) + mov rdi, 0x6000000 # should be far from bzImage and initramfs + mov r14, rdi # r14 = pref_address + xor edx, edx + mov dl, [r12 + setup_sects] + inc rdx + shl rdx, 9 # rdx = offsetof(image_base, startup_32) + lea rsi, [r12 + rdx] # src = image_base + startup_32 + mov ecx, [r12 + shdr_syssize] + shl rcx, 4 + add rdi, rcx + add rsi, rcx + sub rdi, 8 + sub rsi, 8 + shr rcx, 3 + std + rep movsq + cld + + # make a tiny stack - we just need it for the lretq. + # what we jump to will not use this stack + lea rsp, [rip + jmp_to_linux_end + 0x200] + and rsp, -0x10 + #push 0 # retaddr + push 0x10 # cs = GDT[2] + add r14, 0x200 # pref_address + startup_64 + push r14 # rip + mov rsi, r13 # bootargs + lretq +jmp_to_linux_end: + +.data + +.globl jmp_to_linux_size +jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux + +.att_syntax prefix \ No newline at end of file diff --git a/linux/ps4-kexec-1300-baikal/reboot.h b/linux/ps4-kexec-1300-baikal/reboot.h new file mode 100644 index 0000000..20b91f8 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/reboot.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)reboot.h 8.3 (Berkeley) 12/13/94 + * $FreeBSD$ + */ + +#ifndef _SYS_REBOOT_H_ +#define _SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. These are passed to + * the boot program and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ +#define RB_SINGLE 0x002 /* reboot to single user only */ +#define RB_NOSYNC 0x004 /* dont sync before reboot */ +#define RB_HALT 0x008 /* don't reboot, just halt */ +#define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ +#define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ +#define RB_KDB 0x040 /* give control to kernel debugger */ +#define RB_RDONLY 0x080 /* mount root fs read-only */ +#define RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define RB_MINIROOT 0x200 /* Unused placeholder */ +#define RB_VERBOSE 0x800 /* print all potentially useful info */ +#define RB_SERIAL 0x1000 /* use serial port as console */ +#define RB_CDROM 0x2000 /* use cdrom as root */ +#define RB_POWEROFF 0x4000 /* turn the power off if possible */ +#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ +#define RB_MUTE 0x10000 /* start up with the console muted */ +#define RB_SELFTEST 0x20000 /* unused placeholder */ +#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ +#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ +#define RB_PAUSE 0x100000 /* pause after each output line during probe */ +#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ +#define RB_PROBE 0x10000000 /* Probe multiple consoles */ +#define RB_MULTIPLE 0x20000000 /* use multiple consoles */ + +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + +#endif diff --git a/linux/ps4-kexec-1300-baikal/string.h b/linux/ps4-kexec-1300-baikal/string.h new file mode 100644 index 0000000..70aa668 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/string.h @@ -0,0 +1,96 @@ +/* + * string.h -- standard C string-manipulation functions. + * + * Copyright (C) 2008 Segher Boessenkool + * Copyright (C) 2009 Haxx Enterprises + * Copyright (C) 2010-2016 Hector Martin "marcan" + * + * Portions taken from the Public Domain C Library (PDCLib). + * http://pdclib.rootdirectory.de/ + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +static inline int strcmp(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s1[i] == s2[i]; i++) + ; + + return s1[i] - s2[i]; +} + +static inline void *memset(void *b, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)b)[i] = c; + + return b; +} + +static inline void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} + +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) + return p1[i] - p2[i]; + + return 0; +} + +static inline size_t strlen(const char *s) +{ + size_t len; + + for (len = 0; s[len]; len++) + ; + + return len; +} + +static inline size_t strnlen(const char *s, size_t count) +{ + size_t len; + + for (len = 0; s[len] && len < count; len++) + ; + + return len; +} + +static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) +{ + size_t len, needed; + + len = needed = strnlen(src, maxlen - 1) + 1; + if (len >= maxlen) + len = maxlen - 1; + + memcpy(dest, src, len); + dest[len] = 0; + + return needed - 1; +} + +#endif diff --git a/linux/ps4-kexec-1300-baikal/types.h b/linux/ps4-kexec-1300-baikal/types.h new file mode 100644 index 0000000..28d6619 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/types.h @@ -0,0 +1,51 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef TYPES_H +#define TYPES_H + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#ifndef TESTING +typedef u8 uint8_t; +typedef u64 size_t; +typedef s64 ssize_t; +typedef u64 uintptr_t; +typedef s64 off_t; +#endif + +#define NULL ((void *)0) + +#define CAT_(x, y) x ## y +#define CAT(x, y) CAT_(x, y) + +#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] +#define OSTRUCT(name, size) struct name { union { OPAD(size); +#define OSTRUCT_END };}; +#define OFIELD(off, field) struct { OPAD(off); field; } + +#define ASSERT_STRSIZE(struc, size) \ + _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#define ENOENT 2 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define ENAMETOOLONG 63 + +#endif diff --git a/linux/ps4-kexec-1300-baikal/uart.c b/linux/ps4-kexec-1300-baikal/uart.c new file mode 100644 index 0000000..a401434 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/uart.c @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "uart.h" +#include "kernel.h" +#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) + +#define AEOLIA_UART_BASE 0xD0340000 +#define BAIKAL_UART_BASE 0xC890E000 + +#define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, BAIKAL_UART_BASE + (intf << 12) + (reg << 2)) + +#define UART_REG_DATA 0 +#define UART_REG_IER 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +# define LSR_TXRDY 0x20 +# define LSR_TEMT 0x40 +#define UART_REG_MSR 6 +#define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) +#define UART0_IER UART_REG(32, 0, UART_REG_IER) +#define UART0_IIR UART_REG(32, 0, UART_REG_IIR) +#define UART0_LCR UART_REG(32, 0, UART_REG_LCR) +#define UART0_MCR UART_REG(32, 0, UART_REG_MCR) +#define UART0_LSR UART_REG(32, 0, UART_REG_LSR) +#define UART0_MSR UART_REG(32, 0, UART_REG_MSR) + +void uart_write_byte(u8 b) +{ + int limit; + u64 barrier; + limit = 250000; + while (!(*UART0_LSR & LSR_TXRDY) && --limit) + ; + *UART0_DATA = b; + __sync_fetch_and_add(&barrier, 0); + limit = 250000; + while (!(*UART0_LSR & LSR_TEMT) && --limit) + ; +} + +void uart_write_char(char c) +{ + if (c == '\n') + uart_write_byte('\r'); + + uart_write_byte(c); +} + +void uart_write_str(const char *s) +{ + while (*s) { + uart_write_char(*s++); + } +} diff --git a/linux/ps4-kexec-1300-baikal/uart.h b/linux/ps4-kexec-1300-baikal/uart.h new file mode 100644 index 0000000..b803e29 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/uart.h @@ -0,0 +1,20 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef UART_H +#define UART_H + +#include "types.h" + +void uart_write_byte(u8 b); +void uart_write_char(char c); +void uart_write_str(const char *s); + +#endif diff --git a/linux/ps4-kexec-1300-baikal/x86.h b/linux/ps4-kexec-1300-baikal/x86.h new file mode 100644 index 0000000..2f6d8b5 --- /dev/null +++ b/linux/ps4-kexec-1300-baikal/x86.h @@ -0,0 +1,195 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef X86_H +#define X86_H + +#define FLAGS_IF (1 << 9) + +#define CR0_WP (1 << 16) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) + +#define PG_V (1 << 0) +#define PG_RW (1 << 1) +#define PG_U (1 << 2) +#define PG_PS (1 << 7) + +#define SEG_TYPE_DATA (0 << 3) +#define SEG_TYPE_READ_WRITE (1 << 1) +#define SEG_TYPE_CODE (1 << 3) +#define SEG_TYPE_EXEC_READ (1 << 1) +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) + +static inline u64 cr0_read(void) +{ + u64 reg; + asm volatile("mov %0, cr0;" : "=r" (reg)); + return reg; +} + +static inline void cr0_write(u64 val) +{ + asm volatile("mov cr0, %0;" :: "r" (val)); +} + +static inline u64 write_protect_disable() +{ + u64 cr0 = cr0_read(); + cr0_write(cr0 & ~CR0_WP); + return cr0; +} + +static inline void write_protect_restore(u64 cr0) +{ + // Use only WP bit of input + cr0_write(cr0_read() | (cr0 & CR0_WP)); +} + +static inline u64 cr3_read(void) +{ + u64 reg; + asm volatile("mov %0, cr3;" : "=r" (reg)); + return reg; +} + +static inline void cr3_write(u64 val) +{ + asm volatile("mov cr3, %0;" :: "r" (val)); +} + +typedef struct { + u16 limit; + u64 address; +} __attribute__((packed)) desc_ptr; + +static inline desc_ptr gdt_read(void) +{ + desc_ptr gdtr; + asm volatile("sgdt %0;" : "=m" (gdtr)); + return gdtr; +} + +static inline void gdt_write(desc_ptr* val) +{ + asm volatile("lgdt %0;" :: "m" (*val)); +} + +//IDT +typedef struct { + u16 limit; + u64 address; +} __attribute__((packed)) idt_ptr; + +static inline idt_ptr idt_read(void) +{ + idt_ptr idtr; + asm volatile("sidt %0;" : "=m" (idtr)); + return idtr; +} + +static inline void idt_write(idt_ptr* val) +{ + asm volatile("lidt %0;" :: "m" (*val)); +} + +static inline void cr4_pge_disable(void) +{ + u64 cr4_temp; + asm volatile( + "mov %0, cr4;" + "and %0, ~0x80;" + "mov cr4, %0;" + : "=r" (cr4_temp) + ); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +static inline void cpu_stop(void) +{ + for (;;) + asm volatile("cli; hlt;" : : : "memory"); +} + +static inline void outl(int port, unsigned int data) +{ + asm volatile("out %w1, %0" : : "a" (data), "d" (port)); +} + +static inline void wrmsr(u32 msr_id, u64 msr_value) +{ + asm volatile( + "wrmsr" + : + : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) + ); +} + +static inline u64 rdmsr(u64 msr_id) +{ + u32 low, high; + asm volatile ( + "rdmsr" + : "=a"(low), "=d"(high) + : "c"(msr_id) + ); + return ((u64)high << 32) | low; +} + +static inline u64 rdtsc (void) +{ + unsigned int tickl, tickh; + asm volatile( + "rdtsc" + :"=a"(tickl),"=d"(tickh) + ); + return ((u64)tickh << 32) | tickl; +} + +static inline void udelay(unsigned int usec) { + u64 later = rdtsc() + usec * 1594ULL; + while (((s64)(later - rdtsc())) > 0); +} + +static inline void disable_interrupts(void) +{ + asm volatile("cli"); +} + +static inline void enable_interrupts(void) +{ + asm volatile("sti"); +} + +static inline u64 read_flags(void) +{ + u64 flags; + asm volatile("pushf; pop %0;" : "=r" (flags)); + return flags; +} + +static inline u64 intr_disable(void) +{ + u64 flags = read_flags(); + disable_interrupts(); + return flags; +} + +static inline void intr_restore(u64 flags) +{ + // TODO should only IF be or'd in? + asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); +} + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/LICENSE b/linux/ps4-kexec-1300-pro-baikal/LICENSE new file mode 100644 index 0000000..6c1cbba --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2015-2016 shuffle2 +Copyright (C) 2015-2016 Hector Martin "marcan" +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/ps4-kexec-1300-pro-baikal/Makefile b/linux/ps4-kexec-1300-pro-baikal/Makefile new file mode 100644 index 0000000..d06ee6e --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/Makefile @@ -0,0 +1,37 @@ +TOOLCHAIN_PREFIX ?= +CC = $(TOOLCHAIN_PREFIX)gcc +AR = $(TOOLCHAIN_PREFIX)ar +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy + +CFLAGS=$(CFLAG) -DPS4_13_00 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX +CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ + -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ + -fno-asynchronous-unwind-tables \ + -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ + -mcmodel=small -mno-red-zone + +SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ + acpi.c crc32.c + +OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) +DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld + +all: libkexec.a kexec.bin + +%.o: %.c *.h + $(CC) -c $(CFLAGS) -o $@ $< + +%.o: %.S + $(CC) -c $(CFLAGS) -o $@ $< + +libkexec.a: $(OBJS) + $(AR) -rc $@ $(OBJS) + +kexec.elf: libkexec.a kexec.ld + $(CC) $(CFLAGS) -o $@ libkexec.a + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +clean: + rm -f libkexec.a kexec.elf kexec.bin $(OBJS) diff --git a/linux/ps4-kexec-1300-pro-baikal/README.md b/linux/ps4-kexec-1300-pro-baikal/README.md new file mode 100644 index 0000000..6500878 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/README.md @@ -0,0 +1,121 @@ +# PS4 kexec implementation + +This repo implements a kexec()-style system call for the PS4 Orbis kernel +(FreeBSD derivative). This is designed to boot a Linux kernel directly from +FreeBSD. + +This is not an exploit. It is useless without some mechanism of injecting code +into the PS4 OS kernel. + +## Building + +To build a kexec.bin relocatable binary using the supplied Makefile, just type +`make`. This will also build a kexec.a archive. You can either use the binary +directly, or link the archive into your own project. + +If you link kexec.a with your own code, you need to supply the two symbols +`_start` and `_end` in your linker script, as `kernel_init()` will try to remap +all pages covered by that range as RWX (to make global variable accesses work). +Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this +feature, if you have already taken care of page permissions for the code. + +If you use a compiler toolchain that have a special prefix you can declare it +by passing TOOLCHAIN_PREFIX option to the Makefile like this: + + make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' + +## Usage + +The code is designed to be completely standalone. There is a single entry point: + + int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); + +Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel +symbols and install the sys_kexec system call. The syscall is registered +as number 153 by default (you can change this in kexec.h). The return value +is 0 on success, or negative on error. + +You may pass something other than NULL as `early_printf`. In that case, that +function will be used for debug output during early symbol resolution, before +printf is available. + +Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is +enabled by default, symtab also disappears in newer kernel, we have to +hardcode offsets for some symbols. Currently we use the `early_printf` +given by user to caculate the base address of kernel, then relocate all the +symbols from the kernel base. You could enable this feature like this: + + make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' + +If you do not want to call the syscall from userspace, you can pass the address +of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the +address of `sys_kexec`, so you can invoke it manually (see kexec.h for +its prototype and how the arguments are passed). + +If you are using the standalone kexec.bin blob, then the `kexec_init` function +is always located at offset 0, so simply call the base address of the blob. +Don't forget to pass two NULL arguments (or the appropriate pointers). + +The injected `sys_kexec` system call takes (userspace) pointers to the kernel +and initramfs blobs, their sizes, and a pointer to the (null-terminated) command +line string. From userspace, this looks like this: + + int kexec(void *kernel_image, size_t image_size, + void *initramfs, size_t initramfs_size, + const char *cmdline); + + // syscall() usage: + syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); + +`kexec()` will load the kernel and initramfs into memory, but will not directly +boot them. To boot the loaded kernel, shut down the system. This can be +accomplished by pressing the power button, but can also be done more quickly +and reliably from userspace with the following sequence of system calls (this +kills userspace quickly but still does a controlled filesystem unmount): + + int evf = syscall(540, "SceSysCoreReboot"); + syscall(546, evf, 0x4000, 0); + syscall(541, evf); + // should be syscall(37, 1, 30) but only tested via kill symbol + kill(1, 30); + +Note that this software should be loaded into kernel memory space. If you are +running kernel code from userland mappings, you should either switch to kernel +mappings or separately copy kexec.bin to a location in kernel address space. +While syscalls or exploit code may run properly from userland, the shutdown hook +will not, as it will be called from a different process context. + +## Features + +`kernel_init()` will automatically find the Orbis OS kernel and resolve all +necessary symbols to work. There are no static symbol dependencies. If +`DO_NOT_REMAP_RWX` is not defined (the default), it will also patch +`pmap_protect` to disable the W^X restriction. + +In addition to loading the user-supplied initramfs, `kexec` will locate the +Radeon firmware blobs inside Orbis OS, extract them, convert them to a format +suitable for Linux, and append them as an additional initramfs cpio image to +the existing initramfs. This avoids the need to distribute the Radeon firmware +blobs. The `radeon` module, when compiled into the kernel, will automatically +load this firmware on boot. Note however that most typical initramfs scripts +will wipe the initramfs contents while pivoting to the real system, so if you +compile `radeon` as a module you may not be able to access the firmware after +boot. To cover that case, add some code to your initramfs `/init` script to +copy the firmware to a tmpfs mounted on the real filesystem: + + # assuming real root FS is mounted on /mnt + + mkdir -p /mnt/lib/firmware/radeon + mount -t tmpfs none /mnt/lib/firmware/radeon + cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ + + # now switch_root to /mnt + +This avoids having to permanently store copies of the Radeon firmware, which +isn't really necessary for most use cases. + +There is significant debug logging available, which will appear on the system +UART. Most of the code relies on the kernel `printf` implementation, and +therefore you should patch out the UART output blanker to see it. The final +code that runs on the boot CPU before booting the kernel uses direct UART +writes and is not affected by the blanking feature of Orbis OS. diff --git a/linux/ps4-kexec-1300-pro-baikal/acpi.c b/linux/ps4-kexec-1300-pro-baikal/acpi.c new file mode 100644 index 0000000..bfccf08 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/acpi.c @@ -0,0 +1,320 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "types.h" +#include "kernel.h" +#include "acpi.h" +#include "acpi_caps.h" + +#ifdef TESTING +# include +# include +# include +# include +# include +# include +#else +# include "kernel.h" +# include "string.h" +# define printf kern.printf +#endif + +#define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) +#define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) + +#define PACKED __attribute__((packed)) + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +struct RSDP { + u64 sig; + u8 checksum; + u8 oemid[6]; + u8 rev; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 rsvd[3]; +} PACKED; + +struct SDTH { + u32 sig; + u32 length; + u8 rev; + u8 checksum; + u8 oem_id[6]; + u8 oem_tid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; +} PACKED; + +struct RSDT { + struct SDTH hdr; + u32 table_addr[]; +} PACKED; + +struct XSDT { + struct SDTH hdr; + u64 table_addr[]; +} PACKED; + +struct FADT { + struct SDTH hdr; + u32 facs; + u32 dsdt; + // more stuff... +} PACKED; + +struct ivhd_entry4 { + u8 type; + u16 devid; + u8 flags; +} PACKED; + +struct ivhd_header { + u8 type; + u8 flags; + u16 length; + u16 devid; + u16 cap_ptr; + u64 mmio_phys; + u16 pci_seg; + u16 info; + u32 efr_attr; +} PACKED; + +struct IVRS { + struct SDTH hdr; + u32 IVinfo; + u8 reserved[8]; + struct ivhd_header hd_hdr; + struct ivhd_entry4 hd_entries[3]; +} PACKED; + +struct MMIO { + u64 baseAddressECM; + u16 pciSegmentGroup; + u8 startPCIBus; + u8 endPCIBus; + u32 reserved; +} PACKED; + +// We have enough space to use the second half of the 64KB table area +// as scratch space for building the tables +#define BUFFER_OFF 0x8000 + +#define P2M(p) (((u64)(p)) - phys_base + map_base) +#define M2P(p) ((((void*)(p)) - map_base) + phys_base) +#define B2P(p) ((((void*)(p)) - buf_base) + phys_base) +#define P2B(p) ((((void*)(p)) - phys_base) + buf_base) + +#define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) +#define PADB(s) p += (s) +#define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) +#define ALLOC(t) (t)ALLOCB(sizeof(t)) +#define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) +#define COPYT(s) COPYB(((struct SDTH*)s)->length, s) +#define COPYTP(s) COPYT(P2M(s)) +#define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) +#define COPYP(t, s) COPY(t, P2M(s)) + +static void rsdp_checksum(struct RSDP *rsdp) { + rsdp->checksum = rsdp->ext_checksum = 0; + + u8 sum = 0; + for (int i = 0; i < 20; i++) + sum += ((u8*)rsdp)[i]; + rsdp->checksum = -sum; + sum = 0; + for (int i = 0; i < sizeof(*rsdp); i++) + sum += ((u8*)rsdp)[i]; + rsdp->ext_checksum = -sum; +} + +static void table_checksum(void *table) { + struct SDTH *hdr = table; + hdr->checksum = 0; + u8 sum = 0; + for (int i = 0; i < hdr->length; i++) + sum += ((u8*)table)[i]; + hdr->checksum = -sum; +} + +#define IVHD_FLAG_ISOC_EN_MASK 0x08 +#define IVHD_DEV_ALL 0x01 +#define IVHD_DEV_SELECT 0x02 +#define IVHD_DEV_SELECT_RANGE_START 0x03 +#define IVHD_DEV_RANGE_END 0x04 + +#define ACPI_DEVFLAG_SYSMGT1 0x10 +#define ACPI_DEVFLAG_SYSMGT2 0x20 + +static void *build_ivrs(struct IVRS *ivrs) { + memset(ivrs, 0, sizeof(*ivrs)); + + ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); + ivrs->hdr.length = sizeof(*ivrs); + ivrs->hdr.rev = 1; + memcpy(ivrs->hdr.oem_id, "F0F ", 6); + memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); + ivrs->hdr.oem_rev = 0x20161225; + memcpy(ivrs->hdr.creator_id, "KEXC", 4); + ivrs->hdr.creator_rev = 0x20161225; + ivrs->IVinfo = 0x00203040; //48882_IOMMU.pdf page 251 + + struct ivhd_header *hdr = &ivrs->hd_hdr; //48882_IOMMU.pdf page 254 + hdr->type = 0x10; + hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; + hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); + hdr->devid = PCI_DEVFN(0, 2); + hdr->cap_ptr = 0x40; // from config space + 0x34 + hdr->mmio_phys = 0xfc000000; //Base address of IOMMU control registers in MMIO space + hdr->pci_seg = 0; + hdr->info = 0; // msi msg num? (the pci cap should be written by software) + // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 + hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); //Feature Reporting Field, 48882_IOMMU.pdf page 255 + + struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; + // on fbsd, all aeolia devfns have active entries except memories (func 6) + // not sure if this is just because it wasn't in use when i dumped it? + // all entries are r/w + // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) + // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) + // Modes: + // 4 level: + // apcie + // 3 level: + // all others + + // the way to encode this info into the IVHD entries is fairly arbitrary... + entries[0].type = IVHD_DEV_SELECT; //DTE setting applies to the device specifed in DevID field. + entries[0].devid = PCI_DEVFN(20, 0); //vendorId: 104D, deviceId: 90D7; Sony Baikal ACPI + entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; + + entries[1].type = IVHD_DEV_SELECT_RANGE_START; + entries[1].devid = PCI_DEVFN(20, 1); + entries[1].flags = 0; //Identifies a device able to assert INIT interrupts (page 262) + entries[2].type = IVHD_DEV_RANGE_END; + entries[2].devid = PCI_DEVFN(20, 7); + entries[2].flags = 0; //Identifies a device able to assert INIT interrupts + + table_checksum(ivrs); + return ivrs + 1; +} + +void fix_acpi_tables(void *map_base, u64 phys_base) +{ + void *buf_base = map_base + 0x8000; + void *p = buf_base; + memset(buf_base, 0, 0x8000); + + printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); + + struct RSDP *rsdp = COPYP(struct RSDP, phys_base); + printf("RSDT at 0x%x\n", rsdp->rsdt_addr); + printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); + + struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); + struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); + rsdp->rsdt_addr = B2P(rsdt); + + PADB(0x30); // this gives us space for new tables + + struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); + rsdp->xsdt_addr = B2P(xsdt); + + PADB(0x60); + + struct FADT *fadt = NULL; + + int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; + int i; + for (i = 0; i < cnt; i++) { + struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); + printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); + switch (hdr->sig) { + case SIG32('F', 'A', 'C', 'P'): + { + fadt = (void*)hdr; + printf("FACS at 0x%x\n", fadt->facs); + printf("DSDT at 0x%x\n", fadt->dsdt); + // Sony puts the FACS before the FADT, unaligned, which is + // noncompliant, but let's keep it there + u8 *facs = COPYB(64, P2M(fadt->facs)); + fadt = (void*)(hdr = COPYT(hdr)); + fadt->facs = B2P(facs); + PADB(0x38); + break; + } + case SIG32('S', 'S', 'D', 'T'): + { + // Put the DSDT before the SSDT + if (fadt) { + PADB(0xf0); + u8 *dsdt = COPYTP(fadt->dsdt); + fadt->dsdt = B2P(dsdt); + PADB(0x174); + table_checksum(fadt); + } else { + printf("ERROR: no FADT yet?\n"); + } + hdr = COPYT(hdr); + break; + } + default: + hdr = COPYT(hdr); + } + table_checksum(hdr); + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); + } + + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); + i++; + p = build_ivrs(p); + + rsdt->hdr.length = sizeof(*rsdt) + 4 * i; + xsdt->hdr.length = sizeof(*xsdt) + 8 * i; + + rsdp_checksum(rsdp); + table_checksum(rsdt); + table_checksum(xsdt); + memcpy(map_base, buf_base, p - buf_base); +} + +u32 msi_mask(unsigned x) { + /* Don't shift by >= width of type */ + if (x >= 5) + return 0xffffffff; + return (1 << (1 << x)) - 1; +} +void disableMSI(u64 MSICapabilityRegAddr) { + PPCI_MSI_CAPABILITY pMSICapability = (PPCI_MSI_CAPABILITY)PA_TO_DM(MSICapabilityRegAddr); + if (pMSICapability->msiEnable == 1) + pMSICapability->msiEnable = 0; + pMSICapability->mask64 = msi_mask(pMSICapability->multipleMessageCapable); +} + +#ifdef TESTING + +int main(int argc, char **argv) +{ + int fd; + void *base; + + fd = open(argv[1], O_RDWR); + base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + fix_acpi_tables(base, 0xe0000); + return 0; +} + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/acpi.h b/linux/ps4-kexec-1300-pro-baikal/acpi.h new file mode 100644 index 0000000..45f2834 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/acpi.h @@ -0,0 +1,22 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ACPI_H +#define ACPI_H + +#define PACKED __attribute__((packed)) + +void fix_acpi_tables(void *map_base, u64 phys_base); + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) + +void disableMSI(u64 MSICapabilityRegAddr); + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/acpi_caps.h b/linux/ps4-kexec-1300-pro-baikal/acpi_caps.h new file mode 100644 index 0000000..070e9f6 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/acpi_caps.h @@ -0,0 +1,30 @@ +#ifndef ACPI_CAPS_H +#define ACPI_CAPS_H + +#define UCHAR u8 +#define USHORT u16 +#define ULONG u32 +#define PACKED __attribute__((packed)) +typedef struct PACKED _PCI_CAPABILITIES_HEADER { + UCHAR CapabilityID; + UCHAR Next; +} PCI_CAPABILITIES_HEADER, *PPCI_CAPABILITIES_HEADER; + +typedef struct PACKED _PCI_MSI_CAPABILITY { + PCI_CAPABILITIES_HEADER Header; + u16 msiEnable : 1, multipleMessageCapable : 3, multipleMessageEnable : 3, address64Capable : 1, reserved0 : 8; + u32 lowerAddress : 30, reserved1 : 2; + union { + struct { + u32 upperAddress; + u32 messageData64 : 16, reservedData64 : 16; + u32 mask64; + }; + struct { + u32 messageData32 : 16, reservedData32 : 16; + u32 mask32; + }; + }; +} PCI_MSI_CAPABILITY, *PPCI_MSI_CAPABILITY; + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/crc32.c b/linux/ps4-kexec-1300-pro-baikal/crc32.c new file mode 100644 index 0000000..7836c18 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/crc32.c @@ -0,0 +1,102 @@ +/*- +* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or +* code or tables extracted from it, as desired without restriction. +* +* First, the polynomial itself and its table of feedback terms. The +* polynomial is +* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 +* +* Note that we take it "backwards" and put the highest-order term in +* the lowest-order bit. The X^32 term is "implied"; the LSB is the +* X^31 term, etc. The X^0 term (usually shown as "+1") results in +* the MSB being 1 +* +* Note that the usual hardware shift register implementation, which +* is what we're using (we're merely optimizing it by doing eight-bit +* chunks at a time) shifts bits into the lowest-order term. In our +* implementation, that means shifting towards the right. Why do we +* do it this way? Because the calculated CRC must be transmitted in +* order from highest-order term to lowest-order term. UARTs transmit +* characters in order from LSB to MSB. By storing the CRC this way +* we hand it to the UART in the order low-byte to high-byte; the UART +* sends each low-bit to hight-bit; and the result is transmission bit +* by bit from highest- to lowest-order term without requiring any bit +* shuffling on our part. Reception works similarly +* +* The feedback terms table consists of 256, 32-bit entries. Notes +* +* The table can be generated at runtime if desired; code to do so +* is shown later. It might not be obvious, but the feedback +* terms simply represent the results of eight shift/xor opera +* tions for all combinations of data and CRC register values +* +* The values must be right-shifted by eight bits by the "updcrc +* logic; the shift must be unsigned (bring in zeroes). On some +* hardware you could probably optimize the shift in assembler by +* using byte-swap instructions +* polynomial $edb88320 +* +* +* CRC32 code derived from work by Gary S. Brown. +*/ + +#include "crc32.h" + +static u32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 crc32(u32 crc, const void *buf, size_t size) +{ + const u8 *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} diff --git a/linux/ps4-kexec-1300-pro-baikal/crc32.h b/linux/ps4-kexec-1300-pro-baikal/crc32.h new file mode 100644 index 0000000..777278d --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/crc32.h @@ -0,0 +1,7 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +u32 crc32(u32 crc, const void *buf, size_t size); + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/elf.h b/linux/ps4-kexec-1300-pro-baikal/elf.h new file mode 100644 index 0000000..63371c7 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/elf.h @@ -0,0 +1,71 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + u8 e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + u64 e_entry; + u64 e_phoff; + u64 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf64_Ehdr; + +typedef struct { + u32 p_type; + u32 p_flags; + u64 p_offset; + void *p_vaddr; + u64 p_paddr; + u64 p_filesz; + u64 p_memsz; + u64 p_align; +} Elf64_Phdr; + +#define PT_DYNAMIC 2 +#define PT_PHDR 6 + +#define DT_NULL 0 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_STRSZ 10 + +typedef struct { + s64 d_tag; + union { + u64 d_val; + void *d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + void *st_value; + u64 st_size; +} Elf64_Sym; + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/firmware.c b/linux/ps4-kexec-1300-pro-baikal/firmware.c new file mode 100644 index 0000000..a831b1c --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/firmware.c @@ -0,0 +1,479 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "firmware.h" +#include "types.h" +#include "kernel.h" +#include "string.h" +#include "types.h" +#include "crc32.h" + +#define DIR 0040755 +#define FILE 0100644 + +struct firmware_header { + u32 size_bytes; + u32 header_size_bytes; + u16 header_version_major; + u16 header_version_minor; + u16 ip_version_major; + u16 ip_version_minor; + u32 ucode_version; + u32 ucode_size_bytes; + u32 ucode_array_offset_bytes; + u32 crc32; + union { + struct { + u32 ucode_feature_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } gfx1; + struct { + u32 ucode_feature_version; + u32 save_and_restore_offset; + u32 clear_state_descriptor_offset; + u32 avail_scratch_ram_locations; + u32 master_pkt_description_offset; + u8 end[]; + } rlc1; + struct { + u32 ucode_feature_version; + u32 ucode_change_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } sdma1; + u8 raw[0xe0]; + }; +}; + +static inline char hex(u8 c) +{ + if (c <= 9) + return '0' + c; + return 'a' + c - 10; +} + +static void hex8(u8 **p, u32 val) +{ + *(*p)++ = hex(val >> 28); + *(*p)++ = hex((val >> 24) & 0xf); + *(*p)++ = hex((val >> 20) & 0xf); + *(*p)++ = hex((val >> 16) & 0xf); + *(*p)++ = hex((val >> 12) & 0xf); + *(*p)++ = hex((val >> 8) & 0xf); + *(*p)++ = hex((val >> 4) & 0xf); + *(*p)++ = hex(val & 0xf); +} + +void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) +{ + size_t name_len = strlen(name); + + // Pad to 4 byte multiple + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; + + memcpy(*p, "070701", 6); + *p += 6; + hex8(p, 0); // c_ino + hex8(p, mode); // c_mode + hex8(p, 0); // c_uid + hex8(p, 0); // c_gid + hex8(p, 1); // c_nlink + hex8(p, 0); // c_mtime + hex8(p, size); // c_filesize + hex8(p, 0); // c_maj + hex8(p, 0); // c_min + hex8(p, 0); // c_rmaj + hex8(p, 0); // c_rmin + hex8(p, name_len + 1); // c_namesize + hex8(p, 0); // c_chksum + memcpy(*p, name, name_len); + *p += name_len; + *(*p)++ = 0; + + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; +} + +struct fw_header_t { + u64 size_words; + char *unk_ident; + u64 unk; + void *blob; + u64 unk2; +}; + +struct fw_info_t { + struct fw_header_t *rlc; + struct fw_header_t *sdma0; + struct fw_header_t *sdma1; + struct fw_header_t *ce; + struct fw_header_t *pfp; + struct fw_header_t *me; + struct fw_header_t *mec1; + struct fw_header_t *mec2; +}; + +struct fw_expected_sizes_t { + u64 rlc; + u64 sdma0; + u64 sdma1; + u64 ce; + u64 pfp; + u64 me; + u64 mec1; + u64 mec2; +}; +static const struct fw_expected_sizes_t liverpool_fw_sizes = { + LVP_FW_RLC_SIZE, + LVP_FW_SDMA_SIZE, + LVP_FW_SDMA1_SIZE, + LVP_FW_CE_SIZE, + LVP_FW_PFP_SIZE, + LVP_FW_ME_SIZE, + LVP_FW_MEC_SIZE, + LVP_FW_MEC2_SIZE +}; +static const struct fw_expected_sizes_t gladius_fw_sizes = { + GL_FW_RLC_SIZE, + GL_FW_SDMA_SIZE, + GL_FW_SDMA1_SIZE, + GL_FW_CE_SIZE, + GL_FW_PFP_SIZE, + GL_FW_ME_SIZE, + GL_FW_MEC_SIZE, + GL_FW_MEC2_SIZE +}; + +void copy_edid(u8 **p, int sz) +{ + int i; + u8 *edid = *p; + u8 *off_edid = kern.edid; + + memset(edid, 0, sz); + *p += sz; + + for(i = 0; i < sz; i++) + *(edid + i) = *(off_edid + i); + + *p += sz; +} + +void copy_eap_hdd_key(u8 **p) +{ + int i; + u8 *eap_key = *p; + u8 *off_eap_key = kern.eap_hdd_key; + + memset(eap_key, 0, 0x20); + *p += 0x20; + + for(i = 0; i < 0x20; i++) + { + if(i < 0x10) + *(eap_key + i) = *(off_eap_key + 0xF - i); + else + *(eap_key + i) = *(off_eap_key + 0x2F - i); + } + *p += 0x20; +} + +int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + kern.printf("Copying %s firmware\n", name); + if (expected_size != (hdr->size_words * 4)) { + kern.printf("copy_firmware: %s: expected size %d, got %d\n", + name, expected_size, hdr->size_words * 4); + return 0; + } + + struct firmware_header *fhdr = (struct firmware_header*)*p; + memset(fhdr, 0, sizeof(*fhdr)); + *p += sizeof(*fhdr); + + memcpy(*p, hdr->blob, expected_size); + + fhdr->size_bytes = expected_size + sizeof(*fhdr); + fhdr->header_size_bytes = offsetof(struct firmware_header, raw); + fhdr->header_version_major = 1; + fhdr->header_version_minor = 0; + fhdr->ucode_version = 0x10; + fhdr->ucode_size_bytes = expected_size; + fhdr->ucode_array_offset_bytes = sizeof(*fhdr); + + *p += expected_size; + + return 1; +} + +int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); + fhdr->gfx1.ucode_feature_version = 21; + fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); + fhdr->rlc1.ucode_feature_version = 1; + fhdr->rlc1.save_and_restore_offset = 0x90; + fhdr->rlc1.clear_state_descriptor_offset = 0x3d; + fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? + fhdr->rlc1.master_pkt_description_offset = 0; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 2; + fhdr->ip_version_minor = 1; + fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); + fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; + fhdr->sdma1.ucode_change_version = 0; + fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +static const u32 pfp_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc4200016, // ldw r8, [r0, #0x16] + 0xdc030000, // mov ctr, r0 + 0xcc000049, // stw r0, [r0, #0x49] + 0xcc200013, // stw r0, [r8, #0x13] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 ce_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc420000c, // ldw r8, [r0, #0xc] + 0xdc030000, // mov ctr, r0 + 0xcc00002f, // stw r0, [r0, #0x2f] + 0xcc200012, // stw r0, [r8, #0x12] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 mec_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc43c000c, // ldw r15, [r0, #0x9] + 0xdc030000, // mov ctr, r0 + 0xcc00002b, // stw r0, [r0, #0x2b] + 0xcc3c000d, // stw r0, [r15, #0xd] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +#define PACKET_TYPE_NOP 0x10 + +static void patch_fw(void *p, const u32 *handler, int handler_size) { + int size = ((struct firmware_header*)p)->ucode_size_bytes; + int code_size = (size & ~0xfff) / 4; + int nop_start = code_size - 0x10; + + u32 *fw = p + sizeof(struct firmware_header); + kern.printf("NOP handler at 0x%x\n", nop_start); + memcpy(&fw[nop_start], handler, handler_size); + + // patch the branch table entry + for (int off = code_size; off < size/4; off++) { + if ((fw[off] >> 16) == PACKET_TYPE_NOP) { + fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; + } + } +} + +struct fw_info_t *get_fw_info() { + if (kern.gc_get_fw_info) { + return kern.gc_get_fw_info(); + } else if (kern.Starsha_UcodeInfo) { + return kern.Starsha_UcodeInfo; + } else { + return NULL; + } +} + +const struct fw_expected_sizes_t *get_fw_expected_sizes() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return &gladius_fw_sizes; + } else { + return &liverpool_fw_sizes; + } +} + +const char * get_gpu_name() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return "gladius"; + } else { + return "liverpool"; + } +} + +ssize_t firmware_extract(void *dest) +{ + u8 *p = dest; + + // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. + struct fw_info_t *info = get_fw_info(); + if (!info) { + kern.printf("firmware_extract: Could not locate firmware table"); + return -1; + } + const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); + + //Eap hdd key + cpio_hdr(&p, "key", DIR, 0); + cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); + copy_eap_hdd_key(&p); + + cpio_hdr(&p, "lib", DIR, 0); + cpio_hdr(&p, "lib/firmware", DIR, 0); + + /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ + int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); + cpio_hdr(&p, "lib/firmware/edid", DIR, 0); + cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); + copy_edid(&p, edid_sz); + + char dir[7]; + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) + kern.snprintf(dir, sizeof(dir), "amdgpu"); + else + kern.snprintf(dir, sizeof(dir), "amdgpu"); + + char dir_path[64]; + kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); + cpio_hdr(&p, dir_path, DIR, 0); + + char pfp_path[64]; + kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", pfp_path); + cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); + u8 *pfp = p; + if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) + return -1; + patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); + + char me_path[64]; + kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", me_path); + cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); + if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) + return -1; + + char ce_path[64]; + kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", ce_path); + cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); + u8 *ce = p; + if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) + return -1; + patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); + + char mec_path[64]; + kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec_path); + cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); + u8 *mec1 = p; + if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) + return -1; + patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); + + char mec2_path[64]; + kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec2_path); + cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); + u8 *mec2 = p; + if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) + return -1; + patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); + + char rlc_path[64]; + kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", rlc_path); + cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); + if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) + return -1; + + char sdma_path[64]; + kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma_path); + cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); + if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + char sdma1_path[64]; + kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma1_path); + cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); + if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + size_t size = p - (u8*)dest; + if (size > FW_CPIO_SIZE) { + kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); + return -1; + } + + return size; +} diff --git a/linux/ps4-kexec-1300-pro-baikal/firmware.h b/linux/ps4-kexec-1300-pro-baikal/firmware.h new file mode 100644 index 0000000..4835983 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/firmware.h @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include "types.h" + +//sizes eap_hdd_key +#define EAP_HDD_KEY_SIZE 0x20 +#define EDID_SIZE 256 + +// sizes for liverpool +#define LVP_FW_CE_SIZE 8576 +#define LVP_FW_ME_SIZE 16768 +#define LVP_FW_MEC_SIZE 16768 +#define LVP_FW_MEC2_SIZE 16768 +#define LVP_FW_PFP_SIZE 16768 +#define LVP_FW_RLC_SIZE 6144 +#define LVP_FW_SDMA_SIZE 4200 +#define LVP_FW_SDMA1_SIZE 4200 +// sizes for gladius +#define GL_FW_CE_SIZE 8576 +#define GL_FW_ME_SIZE 16768 +#define GL_FW_MEC_SIZE 16768 +#define GL_FW_MEC2_SIZE 16768 +#define GL_FW_PFP_SIZE 16768 +#define GL_FW_RLC_SIZE 8192 +#define GL_FW_SDMA_SIZE 4200 +#define GL_FW_SDMA1_SIZE 4200 + +#define MAX(x ,y) (((x) > (y)) ? (x) : (y)) +#define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) + +#define FW_CE_SIZE MAX_FW_SIZE(CE) +#define FW_ME_SIZE MAX_FW_SIZE(ME) +#define FW_MEC_SIZE MAX_FW_SIZE(MEC) +#define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) +#define FW_PFP_SIZE MAX_FW_SIZE(PFP) +#define FW_RLC_SIZE MAX_FW_SIZE(RLC) +#define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) +#define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) + +// Conservative value (max 113 bytes plus name size plus alignment) +#define CPIO_HEADER_SIZE 256 + +#define FW_HEADER_SIZE 256 + +// Leave space for 16 files (currently 12) +#define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ + FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ + FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ + FW_HEADER_SIZE * 8) + +ssize_t firmware_extract(void *dest); + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/kernel.c b/linux/ps4-kexec-1300-pro-baikal/kernel.c new file mode 100644 index 0000000..b6d31b4 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/kernel.c @@ -0,0 +1,383 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "string.h" +#include "elf.h" +#include "x86.h" +#include "../magic.h" + +struct ksym_t kern; +int (*early_printf)(const char *fmt, ...) = NULL; + +#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) + +#ifdef NO_SYMTAB + +#define RESOLVE_NOERR(name) do { \ + if (kern_off_ ## name == 0) { \ + kern.name = 0; \ + } else { \ + kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ + } \ +} while (0); + +#define RESOLVE(name) do { \ + if (kern_off_ ## name == 0) { \ + return 0; \ + } \ + RESOLVE_NOERR(name) \ +} while (0); + +#else + +#define KERNSIZE 0x2000000 + +static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; +static Elf64_Sym *symtab; +static char *strtab; +static size_t strtab_size; + +static Elf64_Ehdr *find_kern_ehdr(void) +{ + // Search for the kernel copy embedded in ubios, then follow it to see + // where it was relocated to + for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; + if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { + for (size_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; + if (phdr->p_type == PT_PHDR) { + return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); + } + } + } + } + return NULL; +} + +static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf64_Dyn *)phdr->p_vaddr; + } + } + return NULL; +} + +static int elf_parse_dyn(Elf64_Dyn *dyn) +{ + for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_SYMTAB: + symtab = (Elf64_Sym *)dp->d_un.d_ptr; + break; + case DT_STRTAB: + strtab = (char *)dp->d_un.d_ptr; + break; + case DT_STRSZ: + strtab_size = dp->d_un.d_val; + break; + } + } + return symtab && strtab && strtab_size; +} + +void *kernel_resolve(const char *name) +{ + for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { + if (!strcmp(name, &strtab[sym->st_name])) { + eprintf("kern.%s = %p\n", name, (void*)sym->st_value); + return (void *)sym->st_value; + } + } + eprintf("Failed to resolve symbol '%s'\n", name); + return NULL; +} + +#define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) +#define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; + +#endif + +static int resolve_symbols(void) +{ + RESOLVE(printf); + RESOLVE(snprintf); + early_printf = kern.printf; + RESOLVE(copyin); + RESOLVE(copyout); + RESOLVE(copyinstr); + RESOLVE(kernel_map); + RESOLVE(kernel_pmap_store); + RESOLVE(kmem_alloc_contig); + RESOLVE(kmem_free); + RESOLVE(pmap_extract); + RESOLVE(pmap_protect); + RESOLVE(sysent); + RESOLVE(sched_pin); + RESOLVE(sched_unpin); + RESOLVE(smp_rendezvous); + RESOLVE(smp_no_rendevous_barrier); + RESOLVE(icc_query_nowait); + RESOLVE_NOERR(Starsha_UcodeInfo); + RESOLVE_NOERR(gpu_devid_is_9924); + RESOLVE_NOERR(gc_get_fw_info); + RESOLVE_NOERR(eap_hdd_key); + RESOLVE_NOERR(edid); + RESOLVE(wlanbt); + RESOLVE(kern_reboot); + RESOLVE(set_gpu_freq); + RESOLVE(set_pstate); + RESOLVE(update_vddnp); + RESOLVE(set_cu_power_gate); + RESOLVE_NOERR(pstate_before_shutdown); + return 1; +} + +#define M_WAITOK 0x0002 +#define M_ZERO 0x0100 + +#define VM_MEMATTR_DEFAULT 0x06 + +void *kernel_alloc_contig(size_t size) +{ + // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... + vm_offset_t ret = 0; + while(!(ret = kern.kmem_alloc_contig( + *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, + ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); + + /*if (!ret) { + kern.printf("Failed to allocate %zud bytes\n", size); + return NULL; + }*/ + return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); +} + +void kernel_free_contig(void *addr, size_t size) +{ + if (!addr) + return; + kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); +} + +int kernel_hook_install(void *target, void *hook) +{ + uintptr_t t = (uintptr_t)target; // addr to redirect to + uintptr_t h = (uintptr_t)hook; // place to write the thunk + + if (!hook || !target) { + return 0; + } + + kern.printf("kernel_hook_install(%p, %p)\n", target, hook); + + if (!(t & (1L << 63))) { + kern.printf("\n===================== WARNING =====================\n"); + kern.printf("hook target function address: %p\n", target); + kern.printf("It looks like we're running from userland memory.\n"); + kern.printf("Please run this code from a kernel memory mapping.\n\n"); + return 0; + } + s64 displacement = t - (h + 5); + + kern.sched_pin(); + u64 wp = write_protect_disable(); + if (displacement < -0x80000000 || displacement > 0x7fffffff) { + kern.printf(" Using 64bit absolute jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[2]; + s32 zero; + void *target; + } jmp = { + .op = { 0xff, 0x25 }, + .zero = 0, + .target = target, + }; + ASSERT_STRSIZE(struct jmp_t, 14); + memcpy(hook, &jmp, sizeof(jmp)); + } else { + kern.printf(" Using 32bit relative jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[1]; + s32 imm; + } jmp = { + .op = { 0xe9 }, + .imm = displacement, + }; + ASSERT_STRSIZE(struct jmp_t, 5); + memcpy(hook, &jmp, sizeof(jmp)); + } + wbinvd(); + write_protect_restore(wp); + kern.sched_unpin(); + + return 1; +} + +void kernel_syscall_install(int num, void *call, int narg) +{ + struct sysent_t *sy = &kern.sysent[num]; + + kern.sched_pin(); + u64 wp = write_protect_disable(); + + memset(sy, 0, sizeof(*sy)); + sy->sy_narg = narg; + sy->sy_call = call; + sy->sy_thrcnt = 1; + + write_protect_restore(wp); + kern.sched_unpin(); +} + +void kernel_remap(void *start, void *end, int perm) +{ + u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); + u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); + + kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); + kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); +} + +static volatile int _global_test = 0; + +#ifndef DO_NOT_REMAP_RWX +extern u8 _start[], _end[]; + +static int patch_pmap_check(void) +{ + u8 *p; + + for (p = (u8*)kern.pmap_protect; + p < ((u8*)kern.pmap_protect + 0x500); p++) { + #ifdef PS4_6_72 + if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 + p[5] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + /*#ifdef PS4_5_05 + if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { + p[1] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + }*/ + #else + if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { + p[2] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + #endif + } + kern.printf("pmap_protect patch failed!\n"); + return 0; +} +#endif + +int kernel_init(void *_early_printf) +{ + int rv = -1; + + if (_early_printf) + early_printf = _early_printf; + + eprintf("kernel_init()\n"); + +#ifdef KASLR + // use `early_printf` to calculate kernel base + if (early_printf == NULL) + return 0; + + kern.kern_base = (u64)(early_printf - kern_off_printf); + if ((kern.kern_base & PAGE_MASK) != 0) { + eprintf("Kernel base is not aligned\n"); + return 0; + } else { + eprintf("Kernel base = %llx\n", kern.kern_base); + } + + u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); + u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); + +#else + kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 + + u64 DMPML4I = 0x1fc; + u64 DMPDPI = 0; +#endif + + kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); + eprintf("Direct map base = %llx\n", kern.dmap_base); + + // We may not be mapped writable yet, so to be able to write to globals + // we need WP disabled. + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + +#ifndef NO_SYMTAB + Elf64_Ehdr *ehdr = find_kern_ehdr(); + if (!ehdr) { + eprintf("Could not find kernel ELF header\n"); + goto err; + } + eprintf("ELF header at %p\n", ehdr); + + Elf64_Dyn *dyn = elf_get_dyn(ehdr); + if (!dyn) { + eprintf("Could not find kernel dynamic header\n"); + goto err; + } + eprintf("ELF dynamic section at %p\n", dyn); + + if (!elf_parse_dyn(dyn)) { + eprintf("Failed to parse ELF dynamic section\n"); + goto err; + } +#endif + + if (!resolve_symbols()) { + eprintf("Failed to resolve all symbols\n"); + goto err; + } + + // Pin ourselves as soon as possible. This is expected to be released by the caller. + kern.sched_pin(); + +#ifndef DO_NOT_REMAP_RWX + if (!patch_pmap_check()) + goto err; +#endif + +#ifndef DO_NOT_REMAP_RWX + // kernel_remap may need interrupts, but may not write to globals! + enable_interrupts(); + kernel_remap(_start, _end, 7); + disable_interrupts(); +#endif + + // Writing to globals is now safe. + + kern.printf("Testing global variable access (write protection)...\n"); + _global_test = 1; + kern.printf("OK.\n"); + + kern.printf("Kernel interface initialized\n"); + rv = 0; + +err: + write_protect_restore(wp); + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1300-pro-baikal/kernel.h b/linux/ps4-kexec-1300-pro-baikal/kernel.h new file mode 100644 index 0000000..1833f5b --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/kernel.h @@ -0,0 +1,126 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KERNEL_H +#define KERNEL_H + +#include "types.h" +#include "reboot.h" + +#define PAGE_SIZE 0x4000 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define PML4SHIFT 39 +#define PDPSHIFT 30 +#define PDRSHIFT 21 +#define PAGE_SHIFT 12 + +#define KVADDR(l4, l3, l2, l1) ( \ + ((unsigned long)-1 << 47) | \ + ((unsigned long)(l4) << PML4SHIFT) | \ + ((unsigned long)(l3) << PDPSHIFT) | \ + ((unsigned long)(l2) << PDRSHIFT) | \ + ((unsigned long)(l1) << PAGE_SHIFT)) + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) +#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX + +typedef u64 vm_paddr_t; +typedef u64 vm_offset_t; +typedef u64 vm_size_t; +typedef void * vm_map_t; +typedef char vm_memattr_t; +typedef void * pmap_t; + +typedef void (*smp_rendezvous_callback_t)(void *); + +struct sysent_t { + int sy_narg; + void *sy_call; + u16 sy_auevent; + void *sy_systrace_args_func; + int sy_entry; + int sy_return; + int sy_flags; + int sy_thrcnt; +}; + +struct ksym_t { + // two parameters related to kaslr (they are not symbols) + uintptr_t kern_base; + uintptr_t dmap_base; + + int (*printf)(const char *fmt, ...); + int (*snprintf)(const char *fmt, ...); + int (*copyin)(const void *uaddr, void *kaddr, size_t len); + int (*copyout)(const void *kaddr, void *uaddr, size_t len); + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); + + void **kernel_map; + void *kernel_pmap_store; + vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, + vm_paddr_t low, vm_paddr_t high, + unsigned long alignment, + unsigned long boundary, + vm_memattr_t memattr); + void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); + vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); + void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); + + struct sysent_t *sysent; + + void (*sched_pin)(void); + void (*sched_unpin)(void); + void (*smp_rendezvous)(smp_rendezvous_callback_t, + smp_rendezvous_callback_t, + smp_rendezvous_callback_t, void *); + // yes...it is misspelled :) + void (*smp_no_rendevous_barrier)(void *); + void *icc_query_nowait; + void *Starsha_UcodeInfo; + int (*gpu_devid_is_9924)(); + void *(*gc_get_fw_info)(); + void *eap_hdd_key; + void *edid; + void (*wlanbt)(unsigned int state); + int (*kern_reboot)(int magic); + void(*set_gpu_freq)(unsigned int num, unsigned int freq); + void(*set_pstate)(unsigned int val); + void(*update_vddnp)(unsigned int val); + void(*set_cu_power_gate)(unsigned int val); + void *pstate_before_shutdown; +}; + +extern struct ksym_t kern; + +static inline int curcpu(void) +{ + int cpuid; + // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw + asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); + return cpuid; +} + +// Assign a working printf function to this to debug the symbol resolver +extern int (*early_printf)(const char *fmt, ...); + +void *kernel_resolve(const char *name); + +void *kernel_alloc_contig(size_t size); +void kernel_free_contig(void *addr, size_t size); + +void kernel_remap(void *start, void *end, int perm); + +void kernel_syscall_install(int num, void *call, int narg); +int kernel_hook_install(void *target, void *hook); + +int kernel_init(void *early_printf); + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/kexec.c b/linux/ps4-kexec-1300-pro-baikal/kexec.c new file mode 100644 index 0000000..fa55dfe --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/kexec.c @@ -0,0 +1,229 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "linux_boot.h" +#include "x86.h" +#include "kexec.h" +#include "firmware.h" +#include "string.h" +#include "acpi.h" + +static int k_copyin(const void *uaddr, void *kaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(kaddr, uaddr, len); + return 0; +} + +static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + const char *ustr = (const char*)uaddr; + char *kstr = (char*)kaddr; + size_t ret; + if (!uaddr || !kaddr) + return EFAULT; + ret = strlcpy(kstr, ustr, len); + if (ret >= len) { + if (done) + *done = len; + return ENAMETOOLONG; + } else { + if (done) + *done = ret + 1; + } + return 0; +} + +static int k_copyout(const void *kaddr, void *uaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(uaddr, kaddr, len); + return 0; +} + +int sys_kexec(void *td, struct sys_kexec_args *uap) +{ + int err = 0; + size_t initramfs_size = uap->initramfs_size; + void *image = NULL; + void *initramfs = NULL; + size_t firmware_size = 0; + struct boot_params *bp = NULL; + size_t cmd_line_maxlen = 0; + char *cmd_line = NULL; + + int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; + int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; + + kern.printf("sys_kexec invoked\n"); + kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, + uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); + + // Look up our shutdown hook point + void *icc_query_nowait = kern.icc_query_nowait; + if (!icc_query_nowait) { + err = ENOENT; + goto cleanup; + } + + // Set gpu frequencies and pstate + // FAT&SLIM / PRO + kern.set_pstate(3); + + kern.set_gpu_freq(0, 800); //800 //800 + kern.set_gpu_freq(1, 853); //673 //853 + kern.set_gpu_freq(2, 711); //610 //711 + kern.set_gpu_freq(3, 800); //800 //800 + kern.set_gpu_freq(4, 911); //800 //911 + kern.set_gpu_freq(5, 800); //711 //800 + kern.set_gpu_freq(6, 984); //711 //984 + kern.set_gpu_freq(7, 673); //673 //673 + + kern.update_vddnp(0x12); + kern.set_cu_power_gate(0x24); + + // Copy in kernel image + image = kernel_alloc_contig(uap->image_size); + if (!image) { + kern.printf("Failed to allocate image\n"); + err = ENOMEM; + goto cleanup; + } + err = copyin(uap->image, image, uap->image_size); + if (err) { + kern.printf("Failed to copy in image\n"); + goto cleanup; + } + + // Copy in initramfs + initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); + if (!initramfs) { + kern.printf("Failed to allocate initramfs\n"); + err = ENOMEM; + goto cleanup; + } + + err = firmware_extract(((u8*)initramfs)); + if (err < 0) { + kern.printf("Failed to extract GPU firmware - continuing anyway\n"); + } else { + firmware_size = err; + } + + if (initramfs_size) { + err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); + if (err) { + kern.printf("Failed to copy in initramfs\n"); + goto cleanup; + } + } + initramfs_size += firmware_size; + + // Copy in cmdline + cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; + cmd_line = kernel_alloc_contig(cmd_line_maxlen); + if (!cmd_line) { + kern.printf("Failed to allocate cmdline\n"); + err = ENOMEM; + goto cleanup; + } + err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); + if (err) { + kern.printf("Failed to copy in cmdline\n"); + goto cleanup; + } + cmd_line[cmd_line_maxlen - 1] = 0; + + kern.printf("\nkexec parameters:\n"); + kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); + kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", + initramfs_size, uap->initramfs_size); + kern.printf(" Kernel command line: %s\n", cmd_line); + kern.printf(" Kernel image buffer: %p\n", image); + kern.printf(" Initramfs buffer: %p\n", initramfs); + + // Allocate our boot params + bp = kernel_alloc_contig(sizeof(*bp)); + if (!bp) { + kern.printf("Failed to allocate bp\n"); + err = ENOMEM; + goto cleanup; + } + + // Initialize bp + // TODO should probably do this from cpu_quiesce_gate, then bp doesn't + // need to be allocated here, just placed directly into low mem + set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); + + prepare_boot_params(bp, image); + + // Hook the final ICC shutdown function + if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { + kern.printf("Failed to install shutdown hook\n"); + err = EINVAL; + goto cleanup; + } + + kern.printf("******************************************************\n"); + kern.printf("kexec successfully armed. Please shut down the system.\n"); + kern.printf("******************************************************\n\n"); + +/* + kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); + if (kern.kern_reboot(RB_POWEROFF) == -1) + kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); +*/ + return 0; + +cleanup: + kernel_free_contig(cmd_line, cmd_line_maxlen); + kernel_free_contig(bp, sizeof(*bp)); + kernel_free_contig(image, uap->image_size); + kernel_free_contig(initramfs, uap->initramfs_size); + return err; + + copyout(NULL, NULL, 0); +} + +int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) +{ + int rv = 0; + + // potentially needed to write early_printf + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + + if (kernel_init(_early_printf) < 0) { + rv = -1; + goto cleanup; + } + + kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); + kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); + kern.printf("kexec_init() successful\n\n"); + + if (sys_kexec_ptr) + *sys_kexec_ptr = sys_kexec; + +cleanup: + write_protect_restore(wp); + if (kern.sched_unpin && wp & CR0_WP) { + // If we're returning to a state with WP enabled, assume the caller + // wants the thread unpinned. Else the caller is expected to + // call kern.sched_unpin() manually. + kern.sched_unpin(); + } + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1300-pro-baikal/kexec.h b/linux/ps4-kexec-1300-pro-baikal/kexec.h new file mode 100644 index 0000000..6c31637 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/kexec.h @@ -0,0 +1,38 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KEXEC_H +#define KEXEC_H + +#include "types.h" + +#define SYS_KEXEC 153 +#define SYS_KEXEC_NARGS 6 + +struct sys_kexec_args { + void *image; + size_t image_size; + void *initramfs; + size_t initramfs_size; + char *cmd_line; + int vram_gb; +}; + +typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); + +// Note: td is unused, you can pass NULL if you call this directly. +int sys_kexec(void *td, struct sys_kexec_args *uap); + +int kernel_init(void *early_printf); + +int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) + __attribute__ ((section (".init"))); + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/kexec.ld b/linux/ps4-kexec-1300-pro-baikal/kexec.ld new file mode 100644 index 0000000..1bcf26c --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/kexec.ld @@ -0,0 +1,25 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +ENTRY(kexec_init) + +SECTIONS { + /* We don't do GOT relocation and rely on nothing ending up using the GOT + * (-fno-common helps here) */ + /DISCARD/ : { *(.comment) *(.got) } + _start = .; + .init : { *(.init) *(.init.*) } + .text : { *(.text) *(.text.*) } + .data : { *(.data) *(.data.*) } + .rodata : { *(.rodata) *(.rodata.*) } + .bss : { *(.bss) *(.bss.*) *(COMMON)} + .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ + _end = .; +} diff --git a/linux/ps4-kexec-1300-pro-baikal/linux_boot.c b/linux/ps4-kexec-1300-pro-baikal/linux_boot.c new file mode 100644 index 0000000..226dd4f --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/linux_boot.c @@ -0,0 +1,453 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "linux_boot.h" +#include "types.h" +#include "string.h" +#include "x86.h" +#include "kernel.h" +#include "uart.h" +#include "acpi.h" + +void uart_write_byte(u8 b); + +static u64 vram_base = 0x100000000; +// Current code assumes it's a power of two. +static u64 vram_size = 1024 * 1024 * 1024; +static int vram_gb = 2; + +#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) + +#define MSR_GS_BASE 0xc0000101 /* 64bit GS base */ + +struct desc_struct { + u16 limit0; + u16 base0; + u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; + u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, + uintptr_t new_cr3, uintptr_t gdt_ptr); +extern uint8_t *jmp_to_linux; +extern size_t jmp_to_linux_size; + +// FreeBSD DMAP addresses +struct linux_boot_info { + void *linux_image; + void *initramfs; + size_t initramfs_size; + struct boot_params *bp; + char *cmd_line; +}; +static struct linux_boot_info nix_info; + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v) +{ + nix_info.linux_image = linux_image; + nix_info.bp = bp; + nix_info.initramfs = initramfs; + nix_info.initramfs_size = initramfs_size; + nix_info.cmd_line = cmd_line; + vram_gb = v; +} + +static volatile int halted_cpus = 0; + +static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, + u32 type) +{ + uint8_t idx = bp->e820_entries; + bp->e820_map[idx].addr = addr; + bp->e820_map[idx].size = size; + bp->e820_map[idx].type = type; + bp->e820_entries++; +} + +void prepare_boot_params(struct boot_params *bp, u8 *linux_image) +{ + memset(bp, 0, sizeof(struct boot_params)); + struct boot_params *bp_src = (struct boot_params *)linux_image; + memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + + ((u8 *)&bp_src->hdr.jump)[1]); + + // These values are from fw 1.01 + bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); + bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + // This used to be VRAM, but we reclaim it as RAM + bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); + // Instead, carve out VRAM from the beginning of high memory + bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, + SMAP_TYPE_MEMORY); +} + +#define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_FB_OFFSET 0x2068 +#define HDP_NONSURFACE_BASE 0x2c04 +#define CONFIG_MEMSIZE 0x5428 + +static void configure_vram(void) +{ + u64 mmio_base = 0xe4800000; + u64 fb_base = 0x0f00000000; + u64 fb_top = fb_base + vram_gb * vram_size - 1; + + WR32(mmio_base + MC_VM_FB_LOCATION, 0); + WR32(mmio_base + HDP_NONSURFACE_BASE, 0); + + WR32(mmio_base + MC_VM_FB_LOCATION, + ((fb_top >> 24) << 16) | (fb_base >> 24)); + WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); + WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); + WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); +} + +#define IA32_MTRR_DEF_TYPE 0x2ff +#define MTRR_BASE(i) (0x200 + 2*i) +#define MTRR_MASK(i) (0x201 + 2*i) + +static void setup_mtrr(void) +{ + disable_interrupts(); + u64 cr0 = cr0_read(); + cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); + wbinvd(); + cr3_write(cr3_read()); // TLB flush + + wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled + + // Low memory (0GB-2GB) = WB + wrmsr(MTRR_BASE(0), 0x0000000006); + wrmsr(MTRR_MASK(0), 0xff80000800); + // High memory (4GB-8GB) = WB + wrmsr(MTRR_BASE(1), 0x0100000006); + wrmsr(MTRR_MASK(1), 0xff00000800); + // High memory (8GB-10GB) = WB + wrmsr(MTRR_BASE(2), 0x0200000006); + wrmsr(MTRR_MASK(2), 0xff80000800); + // VRAM (4GB-4GB+vram_size) = UC + wrmsr(MTRR_BASE(3), 0x0100000000); + wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); + + wbinvd(); + cr3_write(cr3_read()); // TLB flush + wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable + cr0_write(cr0); + enable_interrupts(); +} + +static void cleanup_interrupts(void) +{ + int i; + disable_interrupts(); + + // Reset APIC stuff (per-CPU) + *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; + for (i = 0x320; i < 0x380; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + for (i = 0x480; i < 0x500; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; + for (i = 0x500; i < 0x540; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; + + // Fix the LVT offset for thresholding + wrmsr(0x413, (1L<<24) | (1L<<52)); + wrmsr(0xc0000408, (1L<<24) | (1L<<52)); +} + +#define DEFAULT_STACK 0 + +#define DPL0 0x0 +#define DPL3 0x3 + +#define BPCIE_BAR2 0xc8800000 +#define BPCIE_HPET_BASE 0x109000 +#define BPCIE_HPET_SIZE 0x400 + +static void cpu_quiesce_gate(void *arg) +{ + + + // Ensure we can write anywhere + cr0_write(cr0_read() & ~CR0_WP); + + // Interrupt stuff local to each CPU + cleanup_interrupts(); + + // We want to set up MTRRs on all CPUs + setup_mtrr(); + + if (curcpu() != 0) { + // We're not on BSP. Try to halt. + __sync_fetch_and_add(&halted_cpus, 1); + cpu_stop(); + } + + uart_write_str("kexec: Waiting for secondary CPUs...\n"); + + // wait for all cpus to halt + while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); + + uart_write_str("kexec: Secondary CPUs quiesced\n"); + + //* Put ident mappings in current page tables + // Should not be needed, but maybe helps for debugging? + cr4_pge_disable(); + u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); + u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + // Clear (really) low mem. + // Linux reads from here to try and access EBDA... + // get_bios_ebda reads u16 from 0x40e + // reserve_ebda_region reads u16 from 0x413 + // Writing zeros causes linux to default to marking + // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. + // It doesn't match the ps4 e820 map, but that seems OK. + memset((void *)0, 0, 0x1000); + + // Create a new page table hierarchy out of the way of linux + // Accessed via freebsd direct map + pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt + // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| + memset(pml4_base, 0, 512 * sizeof(u64) * 2); + pdp_base = pml4_base + 512; + u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; + pml4_base[0] = pdpe; + // Maintain the freebsd direct map + pml4_base[DM_PML4_BASE] = pdpe; + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + uart_write_str("kexec: Setting up GDT...\n"); + + desc_ptr gdt_ptr; + struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); + gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; + gdt_ptr.address = DM_TO_ID(desc); + + // clear + memset(desc, 0, gdt_ptr.limit + 1); + // Most things are ignored in 64bit mode, and we will never be in + // 32bit/compat modes, so just setup another pure-64bit environment... + // Linux inits it's own GDT in secondary_startup_64 + // 0x10 + desc[2].limit0 = 0xffff; + desc[2].base0 = 0x0000; + desc[2].base1 = 0x0000; + desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; + desc[2].s = 1; + desc[2].dpl = 0; + desc[2].p = 1; + desc[2].limit = 0xf; + desc[2].avl = 0; + desc[2].l = 1; + desc[2].d = 0; + desc[2].g = 0; + desc[2].base2 = 0x00; + // 0x18 + desc[3].limit0 = 0xffff; + desc[3].base0 = 0x0000; + desc[3].base1 = 0x0000; + desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; + desc[3].s = 1; + desc[3].dpl = 0; + desc[3].p = 1; + desc[3].limit = 0xf; + desc[3].avl = 0; + desc[3].l = 0; + desc[3].d = 0; + desc[3].g = 0; + desc[3].base2 = 0x00; + // Task segment value + // 0x20 + desc[4].limit0 = 0x0000; + desc[4].base0 = 0x0000; + desc[4].base1 = 0x0000; + desc[4].type = SEG_TYPE_TSS; + desc[4].s = 1; + desc[4].dpl = 0; + desc[4].p = 1; + desc[4].limit = 0x0; + desc[4].avl = 0; + desc[4].l = 0; + desc[4].d = 0; + desc[4].g = 0; + desc[4].base2 = 0x00; + + uart_write_str("kexec: Relocating stub...\n"); + + // Relocate the stub and jump to it + // TODO should thunk_copy be DMAP here? + void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); + memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); + // XXX The +0x200 is for the iret stack in linux_thunk.S + uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; + + uart_write_str("kexec: Setting up boot params...\n"); + + // XXX we write into this bootargs and pass it to the kernel, but in + // jmp_to_linux we use the bootargs from the image as input. So they + // MUST MATCH! + struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; + *bp_lo = *nix_info.bp; + lowmem_pos += sizeof(struct boot_params); + + struct setup_header *shdr = &bp_lo->hdr; + shdr->cmd_line_ptr = lowmem_pos; + shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; + shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; + bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; + bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; + shdr->hardware_subarch = X86_SUBARCH_PS4; + // This needs to be nonzero for the initramfs to work + shdr->type_of_loader = 0xd0; // kexec + + strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, + nix_info.bp->hdr.cmdline_size); + lowmem_pos += strlen(nix_info.cmd_line) + 1; + + uart_write_str("kexec: Cleaning up hardware...\n"); + + // Disable IOMMU + *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; + + // Disable all MSIs on Baikal (bus=0, slot=20) + disableMSI(0xf80a00e0); //func = 0 Baikal ACPI + disableMSI(0xf80a10e0); //func = 1 Baikal Ethernet Controller + disableMSI(0xf80a20e0); //func = 2 Baikal SATA AHCI Controller + disableMSI(0xf80a30e0); //func = 3 Baikal SD/MMC Host Controller + disableMSI(0xf80a40e0); //func = 4 Baikal PCI Express Glue and Miscellaneous Devices + disableMSI(0xf80a50e0); //func = 5 Baikal DMA Controller + disableMSI(0xf80a60e0); //func = 6 Baikal Baikal Memory (DDR3/SPM) + disableMSI(0xf80a70e0); //func = 7 Baikal Baikal USB 3.0 xHCI Host Controller + + // Stop HPET timers + //*(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + 0x10) &= ~(1UL << 0); //General Configuration Register + /* + u64 NUM_TIM_CAP = *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE) & 0x1F00; + for (u64 N = 0; N <= NUM_TIM_CAP; N++) { + *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + (0x20*N) + 0x100) &= ~(1UL << 2); //Timer N Configuration and Capabilities Register + } + */ + uart_write_str("kexec: Reconfiguring VRAM...\n"); + + configure_vram(); + + uart_write_str("kexec: Resetting GPU...\n"); + + // Softreset GPU + *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks + *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC + *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 +// *(volatile u64 *)PA_TO_DM(0xe480d248) = 1; // Halt SDMA1 eeply + *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 + *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC + + *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply + +// *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x10003; // Softreset GFX/CP/RLC + *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x30005; // Softreset GFX/CP/RLC eeply + +// udelay(150); +// *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x10003; + *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x30005; //eeply +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) |= 0x00100140; // Softreset SDMA/GRBM +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~0x00100140; +// udelay(150); + + // Enable audio output + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; + *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; + +// // Set pin caps of pin 2 to vendor defined, to hide it +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// // Set pin caps of pin 3 to !HDMI +// *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; +// *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; + // Set pin configuration default + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; + + uart_write_str("kexec: About to relocate and jump to kernel\n"); + + ((jmp_to_linux_t)thunk_copy)( + DM_TO_ID(nix_info.linux_image), + DM_TO_ID(bp_lo), + DM_TO_ID(pml4_base), + (uintptr_t)&gdt_ptr + ); + // should never reach here + uart_write_str("kexec: unreachable (?)\n"); +} + +// Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) +int hook_icc_query_nowait(u8 *icc_msg) +{ + kern.printf("hook_icc_query_nowait called\n"); + + // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot + //In alternative we can re-enable it here, but sometimes that give problems.. + kern.wlanbt(0x2); + + fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); + + kern.printf("ACPI tables fixed\n"); + + // Transition to BSP and halt other cpus + // smp_no_rendevous_barrier is just nullsub, but it is treated specially by + // smp_rendezvous. This is the easiest way to do this, since we can't assume + // we're already running on BSP. Since smp_rendezvous normally waits on all + // cpus to finish the callbacks, we just never return... + kern.smp_rendezvous(kern.smp_no_rendevous_barrier, + cpu_quiesce_gate, + kern.smp_no_rendevous_barrier, NULL); + + // should never reach here + kern.printf("hook_icc_query_nowait: unreachable (?)\n"); + return 0; +} diff --git a/linux/ps4-kexec-1300-pro-baikal/linux_boot.h b/linux/ps4-kexec-1300-pro-baikal/linux_boot.h new file mode 100644 index 0000000..2232b35 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/linux_boot.h @@ -0,0 +1,88 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef LINUX_BOOT_H +#define LINUX_BOOT_H + +#include "types.h" + +#define SMAP_TYPE_MEMORY 1 +#define SMAP_TYPE_RESERVED 2 +#define SMAP_TYPE_ACPI_RECLAIM 3 +#define SMAP_TYPE_ACPI_NVS 4 +#define SMAP_TYPE_UNUSABLE 5 +#define SMAP_TYPE_PMEM 7 + +#define X86_SUBARCH_PS4 5 + +struct e820entry { + u64 addr; /* start of memory segment */ + u64 size; /* size of memory segment */ + u32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct setup_header { + u8 setup_sects; + u16 root_flags; + u32 syssize; + u16 ram_size; + u16 vid_mode; + u16 root_dev; + u16 boot_flag; + u16 jump; + u32 header; + u16 version; + u32 realmode_swtch; + u16 start_sys; + u16 kernel_version; + u8 type_of_loader; + u8 loadflags; + u16 setup_move_size; + u32 code32_start; + u32 ramdisk_image; + u32 ramdisk_size; + u32 bootsect_kludge; + u16 heap_end_ptr; + u8 ext_loader_ver; + u8 ext_loader_type; + u32 cmd_line_ptr; + u32 initrd_addr_max; + u32 kernel_alignment; + u8 relocatable_kernel; + u8 min_alignment; + u16 xloadflags; + u32 cmdline_size; + u32 hardware_subarch; + u64 hardware_subarch_data; + u32 payload_offset; + u32 payload_length; + u64 setup_data; + u64 pref_address; + u32 init_size; + u32 handover_offset; +} __attribute__((packed)); + +#define E820MAX 128 /* number of entries in E820MAP */ + +OSTRUCT(boot_params, 0x1000) +OFIELD(0x0c0, u32 ext_ramdisk_image); +OFIELD(0x0c4, u32 ext_ramdisk_size); +OFIELD(0x0c8, u32 ext_cmd_line_ptr); +OFIELD(0x1e8, u8 e820_entries); +OFIELD(0x1f1, struct setup_header hdr); +OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); +OSTRUCT_END + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v); +void prepare_boot_params(struct boot_params *bp, u8 *linux_image); +int hook_icc_query_nowait(u8 *icc_msg); + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/linux_thunk.S b/linux/ps4-kexec-1300-pro-baikal/linux_thunk.S new file mode 100644 index 0000000..551964e --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/linux_thunk.S @@ -0,0 +1,90 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +.intel_syntax noprefix + +.equ setup_sects, 0x1f1 +.equ shdr_syssize, 0x1f4 +.equ pref_address, 0x258 + +.text + +#void jmp_to_linux( +# uintptr_t image_base, rdi +# uintptr_t bootargs, rsi +# uintptr_t new_cr3, rdx +# uintptr_t gdt_ptr rcx +#); +.globl jmp_to_linux +jmp_to_linux: + # switch to new gdt + data segments + cli + lgdt [rcx] + #xor eax, eax + mov eax, 0x18 + mov ds, eax + mov ss, eax + mov es, eax + mov fs, eax + mov gs, eax + + # switch to our own page tables (in low mem) + mov cr3, rdx + + # now we're on our own page tables, so we can obliterate the rest of memory + # TODO make sure we don't inadvertently overwrite (important) smap regions + # I think on ps4 we'll actually want to load to 0x700000 + # since we have tons of free room there. + # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We + # should choose a higher address as pref_address. + + # save args + mov r12, rdi + mov r13, rsi + + # memmove(pref_address, , (syssize * 0x10) / 8) + #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go + #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) + mov rdi, 0x6000000 # should be far from bzImage and initramfs + mov r14, rdi # r14 = pref_address + xor edx, edx + mov dl, [r12 + setup_sects] + inc rdx + shl rdx, 9 # rdx = offsetof(image_base, startup_32) + lea rsi, [r12 + rdx] # src = image_base + startup_32 + mov ecx, [r12 + shdr_syssize] + shl rcx, 4 + add rdi, rcx + add rsi, rcx + sub rdi, 8 + sub rsi, 8 + shr rcx, 3 + std + rep movsq + cld + + # make a tiny stack - we just need it for the lretq. + # what we jump to will not use this stack + lea rsp, [rip + jmp_to_linux_end + 0x200] + and rsp, -0x10 + #push 0 # retaddr + push 0x10 # cs = GDT[2] + add r14, 0x200 # pref_address + startup_64 + push r14 # rip + mov rsi, r13 # bootargs + lretq +jmp_to_linux_end: + +.data + +.globl jmp_to_linux_size +jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux + +.att_syntax prefix \ No newline at end of file diff --git a/linux/ps4-kexec-1300-pro-baikal/reboot.h b/linux/ps4-kexec-1300-pro-baikal/reboot.h new file mode 100644 index 0000000..20b91f8 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/reboot.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)reboot.h 8.3 (Berkeley) 12/13/94 + * $FreeBSD$ + */ + +#ifndef _SYS_REBOOT_H_ +#define _SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. These are passed to + * the boot program and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ +#define RB_SINGLE 0x002 /* reboot to single user only */ +#define RB_NOSYNC 0x004 /* dont sync before reboot */ +#define RB_HALT 0x008 /* don't reboot, just halt */ +#define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ +#define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ +#define RB_KDB 0x040 /* give control to kernel debugger */ +#define RB_RDONLY 0x080 /* mount root fs read-only */ +#define RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define RB_MINIROOT 0x200 /* Unused placeholder */ +#define RB_VERBOSE 0x800 /* print all potentially useful info */ +#define RB_SERIAL 0x1000 /* use serial port as console */ +#define RB_CDROM 0x2000 /* use cdrom as root */ +#define RB_POWEROFF 0x4000 /* turn the power off if possible */ +#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ +#define RB_MUTE 0x10000 /* start up with the console muted */ +#define RB_SELFTEST 0x20000 /* unused placeholder */ +#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ +#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ +#define RB_PAUSE 0x100000 /* pause after each output line during probe */ +#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ +#define RB_PROBE 0x10000000 /* Probe multiple consoles */ +#define RB_MULTIPLE 0x20000000 /* use multiple consoles */ + +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/string.h b/linux/ps4-kexec-1300-pro-baikal/string.h new file mode 100644 index 0000000..70aa668 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/string.h @@ -0,0 +1,96 @@ +/* + * string.h -- standard C string-manipulation functions. + * + * Copyright (C) 2008 Segher Boessenkool + * Copyright (C) 2009 Haxx Enterprises + * Copyright (C) 2010-2016 Hector Martin "marcan" + * + * Portions taken from the Public Domain C Library (PDCLib). + * http://pdclib.rootdirectory.de/ + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +static inline int strcmp(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s1[i] == s2[i]; i++) + ; + + return s1[i] - s2[i]; +} + +static inline void *memset(void *b, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)b)[i] = c; + + return b; +} + +static inline void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} + +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) + return p1[i] - p2[i]; + + return 0; +} + +static inline size_t strlen(const char *s) +{ + size_t len; + + for (len = 0; s[len]; len++) + ; + + return len; +} + +static inline size_t strnlen(const char *s, size_t count) +{ + size_t len; + + for (len = 0; s[len] && len < count; len++) + ; + + return len; +} + +static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) +{ + size_t len, needed; + + len = needed = strnlen(src, maxlen - 1) + 1; + if (len >= maxlen) + len = maxlen - 1; + + memcpy(dest, src, len); + dest[len] = 0; + + return needed - 1; +} + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/types.h b/linux/ps4-kexec-1300-pro-baikal/types.h new file mode 100644 index 0000000..28d6619 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/types.h @@ -0,0 +1,51 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef TYPES_H +#define TYPES_H + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#ifndef TESTING +typedef u8 uint8_t; +typedef u64 size_t; +typedef s64 ssize_t; +typedef u64 uintptr_t; +typedef s64 off_t; +#endif + +#define NULL ((void *)0) + +#define CAT_(x, y) x ## y +#define CAT(x, y) CAT_(x, y) + +#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] +#define OSTRUCT(name, size) struct name { union { OPAD(size); +#define OSTRUCT_END };}; +#define OFIELD(off, field) struct { OPAD(off); field; } + +#define ASSERT_STRSIZE(struc, size) \ + _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#define ENOENT 2 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define ENAMETOOLONG 63 + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/uart.c b/linux/ps4-kexec-1300-pro-baikal/uart.c new file mode 100644 index 0000000..a401434 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/uart.c @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "uart.h" +#include "kernel.h" +#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) + +#define AEOLIA_UART_BASE 0xD0340000 +#define BAIKAL_UART_BASE 0xC890E000 + +#define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, BAIKAL_UART_BASE + (intf << 12) + (reg << 2)) + +#define UART_REG_DATA 0 +#define UART_REG_IER 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +# define LSR_TXRDY 0x20 +# define LSR_TEMT 0x40 +#define UART_REG_MSR 6 +#define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) +#define UART0_IER UART_REG(32, 0, UART_REG_IER) +#define UART0_IIR UART_REG(32, 0, UART_REG_IIR) +#define UART0_LCR UART_REG(32, 0, UART_REG_LCR) +#define UART0_MCR UART_REG(32, 0, UART_REG_MCR) +#define UART0_LSR UART_REG(32, 0, UART_REG_LSR) +#define UART0_MSR UART_REG(32, 0, UART_REG_MSR) + +void uart_write_byte(u8 b) +{ + int limit; + u64 barrier; + limit = 250000; + while (!(*UART0_LSR & LSR_TXRDY) && --limit) + ; + *UART0_DATA = b; + __sync_fetch_and_add(&barrier, 0); + limit = 250000; + while (!(*UART0_LSR & LSR_TEMT) && --limit) + ; +} + +void uart_write_char(char c) +{ + if (c == '\n') + uart_write_byte('\r'); + + uart_write_byte(c); +} + +void uart_write_str(const char *s) +{ + while (*s) { + uart_write_char(*s++); + } +} diff --git a/linux/ps4-kexec-1300-pro-baikal/uart.h b/linux/ps4-kexec-1300-pro-baikal/uart.h new file mode 100644 index 0000000..b803e29 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/uart.h @@ -0,0 +1,20 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef UART_H +#define UART_H + +#include "types.h" + +void uart_write_byte(u8 b); +void uart_write_char(char c); +void uart_write_str(const char *s); + +#endif diff --git a/linux/ps4-kexec-1300-pro-baikal/x86.h b/linux/ps4-kexec-1300-pro-baikal/x86.h new file mode 100644 index 0000000..2f6d8b5 --- /dev/null +++ b/linux/ps4-kexec-1300-pro-baikal/x86.h @@ -0,0 +1,195 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef X86_H +#define X86_H + +#define FLAGS_IF (1 << 9) + +#define CR0_WP (1 << 16) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) + +#define PG_V (1 << 0) +#define PG_RW (1 << 1) +#define PG_U (1 << 2) +#define PG_PS (1 << 7) + +#define SEG_TYPE_DATA (0 << 3) +#define SEG_TYPE_READ_WRITE (1 << 1) +#define SEG_TYPE_CODE (1 << 3) +#define SEG_TYPE_EXEC_READ (1 << 1) +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) + +static inline u64 cr0_read(void) +{ + u64 reg; + asm volatile("mov %0, cr0;" : "=r" (reg)); + return reg; +} + +static inline void cr0_write(u64 val) +{ + asm volatile("mov cr0, %0;" :: "r" (val)); +} + +static inline u64 write_protect_disable() +{ + u64 cr0 = cr0_read(); + cr0_write(cr0 & ~CR0_WP); + return cr0; +} + +static inline void write_protect_restore(u64 cr0) +{ + // Use only WP bit of input + cr0_write(cr0_read() | (cr0 & CR0_WP)); +} + +static inline u64 cr3_read(void) +{ + u64 reg; + asm volatile("mov %0, cr3;" : "=r" (reg)); + return reg; +} + +static inline void cr3_write(u64 val) +{ + asm volatile("mov cr3, %0;" :: "r" (val)); +} + +typedef struct { + u16 limit; + u64 address; +} __attribute__((packed)) desc_ptr; + +static inline desc_ptr gdt_read(void) +{ + desc_ptr gdtr; + asm volatile("sgdt %0;" : "=m" (gdtr)); + return gdtr; +} + +static inline void gdt_write(desc_ptr* val) +{ + asm volatile("lgdt %0;" :: "m" (*val)); +} + +//IDT +typedef struct { + u16 limit; + u64 address; +} __attribute__((packed)) idt_ptr; + +static inline idt_ptr idt_read(void) +{ + idt_ptr idtr; + asm volatile("sidt %0;" : "=m" (idtr)); + return idtr; +} + +static inline void idt_write(idt_ptr* val) +{ + asm volatile("lidt %0;" :: "m" (*val)); +} + +static inline void cr4_pge_disable(void) +{ + u64 cr4_temp; + asm volatile( + "mov %0, cr4;" + "and %0, ~0x80;" + "mov cr4, %0;" + : "=r" (cr4_temp) + ); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +static inline void cpu_stop(void) +{ + for (;;) + asm volatile("cli; hlt;" : : : "memory"); +} + +static inline void outl(int port, unsigned int data) +{ + asm volatile("out %w1, %0" : : "a" (data), "d" (port)); +} + +static inline void wrmsr(u32 msr_id, u64 msr_value) +{ + asm volatile( + "wrmsr" + : + : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) + ); +} + +static inline u64 rdmsr(u64 msr_id) +{ + u32 low, high; + asm volatile ( + "rdmsr" + : "=a"(low), "=d"(high) + : "c"(msr_id) + ); + return ((u64)high << 32) | low; +} + +static inline u64 rdtsc (void) +{ + unsigned int tickl, tickh; + asm volatile( + "rdtsc" + :"=a"(tickl),"=d"(tickh) + ); + return ((u64)tickh << 32) | tickl; +} + +static inline void udelay(unsigned int usec) { + u64 later = rdtsc() + usec * 1594ULL; + while (((s64)(later - rdtsc())) > 0); +} + +static inline void disable_interrupts(void) +{ + asm volatile("cli"); +} + +static inline void enable_interrupts(void) +{ + asm volatile("sti"); +} + +static inline u64 read_flags(void) +{ + u64 flags; + asm volatile("pushf; pop %0;" : "=r" (flags)); + return flags; +} + +static inline u64 intr_disable(void) +{ + u64 flags = read_flags(); + disable_interrupts(); + return flags; +} + +static inline void intr_restore(u64 flags) +{ + // TODO should only IF be or'd in? + asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); +} + +#endif diff --git a/linux/ps4-kexec-1300-pro/LICENSE b/linux/ps4-kexec-1300-pro/LICENSE new file mode 100644 index 0000000..6c1cbba --- /dev/null +++ b/linux/ps4-kexec-1300-pro/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2015-2016 shuffle2 +Copyright (C) 2015-2016 Hector Martin "marcan" +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/ps4-kexec-1300-pro/Makefile b/linux/ps4-kexec-1300-pro/Makefile new file mode 100644 index 0000000..d06ee6e --- /dev/null +++ b/linux/ps4-kexec-1300-pro/Makefile @@ -0,0 +1,37 @@ +TOOLCHAIN_PREFIX ?= +CC = $(TOOLCHAIN_PREFIX)gcc +AR = $(TOOLCHAIN_PREFIX)ar +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy + +CFLAGS=$(CFLAG) -DPS4_13_00 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX +CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ + -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ + -fno-asynchronous-unwind-tables \ + -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ + -mcmodel=small -mno-red-zone + +SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ + acpi.c crc32.c + +OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) +DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld + +all: libkexec.a kexec.bin + +%.o: %.c *.h + $(CC) -c $(CFLAGS) -o $@ $< + +%.o: %.S + $(CC) -c $(CFLAGS) -o $@ $< + +libkexec.a: $(OBJS) + $(AR) -rc $@ $(OBJS) + +kexec.elf: libkexec.a kexec.ld + $(CC) $(CFLAGS) -o $@ libkexec.a + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +clean: + rm -f libkexec.a kexec.elf kexec.bin $(OBJS) diff --git a/linux/ps4-kexec-1300-pro/README.md b/linux/ps4-kexec-1300-pro/README.md new file mode 100644 index 0000000..6500878 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/README.md @@ -0,0 +1,121 @@ +# PS4 kexec implementation + +This repo implements a kexec()-style system call for the PS4 Orbis kernel +(FreeBSD derivative). This is designed to boot a Linux kernel directly from +FreeBSD. + +This is not an exploit. It is useless without some mechanism of injecting code +into the PS4 OS kernel. + +## Building + +To build a kexec.bin relocatable binary using the supplied Makefile, just type +`make`. This will also build a kexec.a archive. You can either use the binary +directly, or link the archive into your own project. + +If you link kexec.a with your own code, you need to supply the two symbols +`_start` and `_end` in your linker script, as `kernel_init()` will try to remap +all pages covered by that range as RWX (to make global variable accesses work). +Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this +feature, if you have already taken care of page permissions for the code. + +If you use a compiler toolchain that have a special prefix you can declare it +by passing TOOLCHAIN_PREFIX option to the Makefile like this: + + make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' + +## Usage + +The code is designed to be completely standalone. There is a single entry point: + + int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); + +Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel +symbols and install the sys_kexec system call. The syscall is registered +as number 153 by default (you can change this in kexec.h). The return value +is 0 on success, or negative on error. + +You may pass something other than NULL as `early_printf`. In that case, that +function will be used for debug output during early symbol resolution, before +printf is available. + +Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is +enabled by default, symtab also disappears in newer kernel, we have to +hardcode offsets for some symbols. Currently we use the `early_printf` +given by user to caculate the base address of kernel, then relocate all the +symbols from the kernel base. You could enable this feature like this: + + make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' + +If you do not want to call the syscall from userspace, you can pass the address +of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the +address of `sys_kexec`, so you can invoke it manually (see kexec.h for +its prototype and how the arguments are passed). + +If you are using the standalone kexec.bin blob, then the `kexec_init` function +is always located at offset 0, so simply call the base address of the blob. +Don't forget to pass two NULL arguments (or the appropriate pointers). + +The injected `sys_kexec` system call takes (userspace) pointers to the kernel +and initramfs blobs, their sizes, and a pointer to the (null-terminated) command +line string. From userspace, this looks like this: + + int kexec(void *kernel_image, size_t image_size, + void *initramfs, size_t initramfs_size, + const char *cmdline); + + // syscall() usage: + syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); + +`kexec()` will load the kernel and initramfs into memory, but will not directly +boot them. To boot the loaded kernel, shut down the system. This can be +accomplished by pressing the power button, but can also be done more quickly +and reliably from userspace with the following sequence of system calls (this +kills userspace quickly but still does a controlled filesystem unmount): + + int evf = syscall(540, "SceSysCoreReboot"); + syscall(546, evf, 0x4000, 0); + syscall(541, evf); + // should be syscall(37, 1, 30) but only tested via kill symbol + kill(1, 30); + +Note that this software should be loaded into kernel memory space. If you are +running kernel code from userland mappings, you should either switch to kernel +mappings or separately copy kexec.bin to a location in kernel address space. +While syscalls or exploit code may run properly from userland, the shutdown hook +will not, as it will be called from a different process context. + +## Features + +`kernel_init()` will automatically find the Orbis OS kernel and resolve all +necessary symbols to work. There are no static symbol dependencies. If +`DO_NOT_REMAP_RWX` is not defined (the default), it will also patch +`pmap_protect` to disable the W^X restriction. + +In addition to loading the user-supplied initramfs, `kexec` will locate the +Radeon firmware blobs inside Orbis OS, extract them, convert them to a format +suitable for Linux, and append them as an additional initramfs cpio image to +the existing initramfs. This avoids the need to distribute the Radeon firmware +blobs. The `radeon` module, when compiled into the kernel, will automatically +load this firmware on boot. Note however that most typical initramfs scripts +will wipe the initramfs contents while pivoting to the real system, so if you +compile `radeon` as a module you may not be able to access the firmware after +boot. To cover that case, add some code to your initramfs `/init` script to +copy the firmware to a tmpfs mounted on the real filesystem: + + # assuming real root FS is mounted on /mnt + + mkdir -p /mnt/lib/firmware/radeon + mount -t tmpfs none /mnt/lib/firmware/radeon + cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ + + # now switch_root to /mnt + +This avoids having to permanently store copies of the Radeon firmware, which +isn't really necessary for most use cases. + +There is significant debug logging available, which will appear on the system +UART. Most of the code relies on the kernel `printf` implementation, and +therefore you should patch out the UART output blanker to see it. The final +code that runs on the boot CPU before booting the kernel uses direct UART +writes and is not affected by the blanking feature of Orbis OS. diff --git a/linux/ps4-kexec-1300-pro/acpi.c b/linux/ps4-kexec-1300-pro/acpi.c new file mode 100644 index 0000000..f53f04e --- /dev/null +++ b/linux/ps4-kexec-1300-pro/acpi.c @@ -0,0 +1,296 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "types.h" + +#ifdef TESTING +# include +# include +# include +# include +# include +# include +#else +# include "kernel.h" +# include "string.h" +# define printf kern.printf +#endif + +#define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) +#define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) + +#define PACKED __attribute__((packed)) + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +struct RSDP { + u64 sig; + u8 checksum; + u8 oemid[6]; + u8 rev; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 rsvd[3]; +} PACKED; + +struct SDTH { + u32 sig; + u32 length; + u8 rev; + u8 checksum; + u8 oem_id[6]; + u8 oem_tid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; +} PACKED; + +struct RSDT { + struct SDTH hdr; + u32 table_addr[]; +} PACKED; + +struct XSDT { + struct SDTH hdr; + u64 table_addr[]; +} PACKED; + +struct FADT { + struct SDTH hdr; + u32 facs; + u32 dsdt; + // more stuff... +} PACKED; + +struct ivhd_entry4 { + u8 type; + u16 devid; + u8 flags; +} PACKED; + +struct ivhd_header { + u8 type; + u8 flags; + u16 length; + u16 devid; + u16 cap_ptr; + u64 mmio_phys; + u16 pci_seg; + u16 info; + u32 efr_attr; +} PACKED; + +struct IVRS { + struct SDTH hdr; + u32 IVinfo; + u8 reserved[8]; + struct ivhd_header hd_hdr; + struct ivhd_entry4 hd_entries[3]; +} PACKED; + +// We have enough space to use the second half of the 64KB table area +// as scratch space for building the tables +#define BUFFER_OFF 0x8000 + +#define P2M(p) (((u64)(p)) - phys_base + map_base) +#define M2P(p) ((((void*)(p)) - map_base) + phys_base) +#define B2P(p) ((((void*)(p)) - buf_base) + phys_base) + +#define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) +#define PADB(s) p += (s) +#define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) +#define ALLOC(t) (t)ALLOCB(sizeof(t)) +#define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) +#define COPYT(s) COPYB(((struct SDTH*)s)->length, s) +#define COPYTP(s) COPYT(P2M(s)) +#define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) +#define COPYP(t, s) COPY(t, P2M(s)) + +static void rsdp_checksum(struct RSDP *rsdp) { + rsdp->checksum = rsdp->ext_checksum = 0; + + u8 sum = 0; + for (int i = 0; i < 20; i++) + sum += ((u8*)rsdp)[i]; + rsdp->checksum = -sum; + sum = 0; + for (int i = 0; i < sizeof(*rsdp); i++) + sum += ((u8*)rsdp)[i]; + rsdp->ext_checksum = -sum; +} + +static void table_checksum(void *table) { + struct SDTH *hdr = table; + hdr->checksum = 0; + u8 sum = 0; + for (int i = 0; i < hdr->length; i++) + sum += ((u8*)table)[i]; + hdr->checksum = -sum; +} + +#define IVHD_FLAG_ISOC_EN_MASK 0x08 +#define IVHD_DEV_ALL 0x01 +#define IVHD_DEV_SELECT 0x02 +#define IVHD_DEV_SELECT_RANGE_START 0x03 +#define IVHD_DEV_RANGE_END 0x04 + +#define ACPI_DEVFLAG_SYSMGT1 0x10 +#define ACPI_DEVFLAG_SYSMGT2 0x20 + +static void *build_ivrs(struct IVRS *ivrs) { + memset(ivrs, 0, sizeof(*ivrs)); + + ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); + ivrs->hdr.length = sizeof(*ivrs); + ivrs->hdr.rev = 1; + memcpy(ivrs->hdr.oem_id, "F0F ", 6); + memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); + ivrs->hdr.oem_rev = 0x20161225; + memcpy(ivrs->hdr.creator_id, "KEXC", 4); + ivrs->hdr.creator_rev = 0x20161225; + ivrs->IVinfo = 0x00203040; + + struct ivhd_header *hdr = &ivrs->hd_hdr; + hdr->type = 0x10; + hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; + hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); + hdr->devid = PCI_DEVFN(0, 2); + hdr->cap_ptr = 0x40; // from config space + 0x34 + hdr->mmio_phys = 0xfc000000; + hdr->pci_seg = 0; + hdr->info = 0; // msi msg num? (the pci cap should be written by software) + // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 + hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); + + struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; + // on fbsd, all aeolia devfns have active entries except memories (func 6) + // not sure if this is just because it wasn't in use when i dumped it? + // all entries are r/w + // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) + // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) + // Modes: + // 4 level: + // apcie + // 3 level: + // all others + + // the way to encode this info into the IVHD entries is fairly arbitrary... + entries[0].type = IVHD_DEV_SELECT; + entries[0].devid = PCI_DEVFN(20, 0); + entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; + + entries[1].type = IVHD_DEV_SELECT_RANGE_START; + entries[1].devid = PCI_DEVFN(20, 1); + entries[1].flags = 0; + entries[2].type = IVHD_DEV_RANGE_END; + entries[2].devid = PCI_DEVFN(20, 7); + entries[2].flags = 0; + + table_checksum(ivrs); + return ivrs + 1; +} + +void fix_acpi_tables(void *map_base, u64 phys_base) +{ + void *buf_base = map_base + 0x8000; + void *p = buf_base; + memset(buf_base, 0, 0x8000); + + printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); + + struct RSDP *rsdp = COPYP(struct RSDP, phys_base); + printf("RSDT at 0x%x\n", rsdp->rsdt_addr); + printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); + + struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); + struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); + rsdp->rsdt_addr = B2P(rsdt); + + PADB(0x30); // this gives us space for new tables + + struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); + rsdp->xsdt_addr = B2P(xsdt); + + PADB(0x60); + + struct FADT *fadt = NULL; + + int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; + int i; + for (i = 0; i < cnt; i++) { + struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); + printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); + switch (hdr->sig) { + case SIG32('F', 'A', 'C', 'P'): + { + fadt = (void*)hdr; + printf("FACS at 0x%x\n", fadt->facs); + printf("DSDT at 0x%x\n", fadt->dsdt); + // Sony puts the FACS before the FADT, unaligned, which is + // noncompliant, but let's keep it there + u8 *facs = COPYB(64, P2M(fadt->facs)); + fadt = (void*)(hdr = COPYT(hdr)); + fadt->facs = B2P(facs); + PADB(0x38); + break; + } + case SIG32('S', 'S', 'D', 'T'): + { + // Put the DSDT before the SSDT + if (fadt) { + PADB(0xf0); + u8 *dsdt = COPYTP(fadt->dsdt); + fadt->dsdt = B2P(dsdt); + PADB(0x174); + table_checksum(fadt); + } else { + printf("ERROR: no FADT yet?\n"); + } + hdr = COPYT(hdr); + break; + } + default: + hdr = COPYT(hdr); + } + table_checksum(hdr); + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); + } + + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); + i++; + p = build_ivrs(p); + + rsdt->hdr.length = sizeof(*rsdt) + 4 * i; + xsdt->hdr.length = sizeof(*xsdt) + 8 * i; + + rsdp_checksum(rsdp); + table_checksum(rsdt); + table_checksum(xsdt); + memcpy(map_base, buf_base, p - buf_base); +} + + +#ifdef TESTING + +int main(int argc, char **argv) +{ + int fd; + void *base; + + fd = open(argv[1], O_RDWR); + base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + fix_acpi_tables(base, 0xe0000); + return 0; +} + +#endif diff --git a/linux/ps4-kexec-1300-pro/acpi.h b/linux/ps4-kexec-1300-pro/acpi.h new file mode 100644 index 0000000..5723982 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/acpi.h @@ -0,0 +1,18 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ACPI_H +#define ACPI_H + +#include "types.h" + +void fix_acpi_tables(void *map_base, u64 phys_base); + +#endif diff --git a/linux/ps4-kexec-1300-pro/crc32.c b/linux/ps4-kexec-1300-pro/crc32.c new file mode 100644 index 0000000..7836c18 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/crc32.c @@ -0,0 +1,102 @@ +/*- +* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or +* code or tables extracted from it, as desired without restriction. +* +* First, the polynomial itself and its table of feedback terms. The +* polynomial is +* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 +* +* Note that we take it "backwards" and put the highest-order term in +* the lowest-order bit. The X^32 term is "implied"; the LSB is the +* X^31 term, etc. The X^0 term (usually shown as "+1") results in +* the MSB being 1 +* +* Note that the usual hardware shift register implementation, which +* is what we're using (we're merely optimizing it by doing eight-bit +* chunks at a time) shifts bits into the lowest-order term. In our +* implementation, that means shifting towards the right. Why do we +* do it this way? Because the calculated CRC must be transmitted in +* order from highest-order term to lowest-order term. UARTs transmit +* characters in order from LSB to MSB. By storing the CRC this way +* we hand it to the UART in the order low-byte to high-byte; the UART +* sends each low-bit to hight-bit; and the result is transmission bit +* by bit from highest- to lowest-order term without requiring any bit +* shuffling on our part. Reception works similarly +* +* The feedback terms table consists of 256, 32-bit entries. Notes +* +* The table can be generated at runtime if desired; code to do so +* is shown later. It might not be obvious, but the feedback +* terms simply represent the results of eight shift/xor opera +* tions for all combinations of data and CRC register values +* +* The values must be right-shifted by eight bits by the "updcrc +* logic; the shift must be unsigned (bring in zeroes). On some +* hardware you could probably optimize the shift in assembler by +* using byte-swap instructions +* polynomial $edb88320 +* +* +* CRC32 code derived from work by Gary S. Brown. +*/ + +#include "crc32.h" + +static u32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 crc32(u32 crc, const void *buf, size_t size) +{ + const u8 *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} diff --git a/linux/ps4-kexec-1300-pro/crc32.h b/linux/ps4-kexec-1300-pro/crc32.h new file mode 100644 index 0000000..777278d --- /dev/null +++ b/linux/ps4-kexec-1300-pro/crc32.h @@ -0,0 +1,7 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +u32 crc32(u32 crc, const void *buf, size_t size); + +#endif diff --git a/linux/ps4-kexec-1300-pro/elf.h b/linux/ps4-kexec-1300-pro/elf.h new file mode 100644 index 0000000..63371c7 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/elf.h @@ -0,0 +1,71 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + u8 e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + u64 e_entry; + u64 e_phoff; + u64 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf64_Ehdr; + +typedef struct { + u32 p_type; + u32 p_flags; + u64 p_offset; + void *p_vaddr; + u64 p_paddr; + u64 p_filesz; + u64 p_memsz; + u64 p_align; +} Elf64_Phdr; + +#define PT_DYNAMIC 2 +#define PT_PHDR 6 + +#define DT_NULL 0 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_STRSZ 10 + +typedef struct { + s64 d_tag; + union { + u64 d_val; + void *d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + void *st_value; + u64 st_size; +} Elf64_Sym; + +#endif diff --git a/linux/ps4-kexec-1300-pro/firmware.c b/linux/ps4-kexec-1300-pro/firmware.c new file mode 100644 index 0000000..a831b1c --- /dev/null +++ b/linux/ps4-kexec-1300-pro/firmware.c @@ -0,0 +1,479 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "firmware.h" +#include "types.h" +#include "kernel.h" +#include "string.h" +#include "types.h" +#include "crc32.h" + +#define DIR 0040755 +#define FILE 0100644 + +struct firmware_header { + u32 size_bytes; + u32 header_size_bytes; + u16 header_version_major; + u16 header_version_minor; + u16 ip_version_major; + u16 ip_version_minor; + u32 ucode_version; + u32 ucode_size_bytes; + u32 ucode_array_offset_bytes; + u32 crc32; + union { + struct { + u32 ucode_feature_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } gfx1; + struct { + u32 ucode_feature_version; + u32 save_and_restore_offset; + u32 clear_state_descriptor_offset; + u32 avail_scratch_ram_locations; + u32 master_pkt_description_offset; + u8 end[]; + } rlc1; + struct { + u32 ucode_feature_version; + u32 ucode_change_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } sdma1; + u8 raw[0xe0]; + }; +}; + +static inline char hex(u8 c) +{ + if (c <= 9) + return '0' + c; + return 'a' + c - 10; +} + +static void hex8(u8 **p, u32 val) +{ + *(*p)++ = hex(val >> 28); + *(*p)++ = hex((val >> 24) & 0xf); + *(*p)++ = hex((val >> 20) & 0xf); + *(*p)++ = hex((val >> 16) & 0xf); + *(*p)++ = hex((val >> 12) & 0xf); + *(*p)++ = hex((val >> 8) & 0xf); + *(*p)++ = hex((val >> 4) & 0xf); + *(*p)++ = hex(val & 0xf); +} + +void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) +{ + size_t name_len = strlen(name); + + // Pad to 4 byte multiple + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; + + memcpy(*p, "070701", 6); + *p += 6; + hex8(p, 0); // c_ino + hex8(p, mode); // c_mode + hex8(p, 0); // c_uid + hex8(p, 0); // c_gid + hex8(p, 1); // c_nlink + hex8(p, 0); // c_mtime + hex8(p, size); // c_filesize + hex8(p, 0); // c_maj + hex8(p, 0); // c_min + hex8(p, 0); // c_rmaj + hex8(p, 0); // c_rmin + hex8(p, name_len + 1); // c_namesize + hex8(p, 0); // c_chksum + memcpy(*p, name, name_len); + *p += name_len; + *(*p)++ = 0; + + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; +} + +struct fw_header_t { + u64 size_words; + char *unk_ident; + u64 unk; + void *blob; + u64 unk2; +}; + +struct fw_info_t { + struct fw_header_t *rlc; + struct fw_header_t *sdma0; + struct fw_header_t *sdma1; + struct fw_header_t *ce; + struct fw_header_t *pfp; + struct fw_header_t *me; + struct fw_header_t *mec1; + struct fw_header_t *mec2; +}; + +struct fw_expected_sizes_t { + u64 rlc; + u64 sdma0; + u64 sdma1; + u64 ce; + u64 pfp; + u64 me; + u64 mec1; + u64 mec2; +}; +static const struct fw_expected_sizes_t liverpool_fw_sizes = { + LVP_FW_RLC_SIZE, + LVP_FW_SDMA_SIZE, + LVP_FW_SDMA1_SIZE, + LVP_FW_CE_SIZE, + LVP_FW_PFP_SIZE, + LVP_FW_ME_SIZE, + LVP_FW_MEC_SIZE, + LVP_FW_MEC2_SIZE +}; +static const struct fw_expected_sizes_t gladius_fw_sizes = { + GL_FW_RLC_SIZE, + GL_FW_SDMA_SIZE, + GL_FW_SDMA1_SIZE, + GL_FW_CE_SIZE, + GL_FW_PFP_SIZE, + GL_FW_ME_SIZE, + GL_FW_MEC_SIZE, + GL_FW_MEC2_SIZE +}; + +void copy_edid(u8 **p, int sz) +{ + int i; + u8 *edid = *p; + u8 *off_edid = kern.edid; + + memset(edid, 0, sz); + *p += sz; + + for(i = 0; i < sz; i++) + *(edid + i) = *(off_edid + i); + + *p += sz; +} + +void copy_eap_hdd_key(u8 **p) +{ + int i; + u8 *eap_key = *p; + u8 *off_eap_key = kern.eap_hdd_key; + + memset(eap_key, 0, 0x20); + *p += 0x20; + + for(i = 0; i < 0x20; i++) + { + if(i < 0x10) + *(eap_key + i) = *(off_eap_key + 0xF - i); + else + *(eap_key + i) = *(off_eap_key + 0x2F - i); + } + *p += 0x20; +} + +int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + kern.printf("Copying %s firmware\n", name); + if (expected_size != (hdr->size_words * 4)) { + kern.printf("copy_firmware: %s: expected size %d, got %d\n", + name, expected_size, hdr->size_words * 4); + return 0; + } + + struct firmware_header *fhdr = (struct firmware_header*)*p; + memset(fhdr, 0, sizeof(*fhdr)); + *p += sizeof(*fhdr); + + memcpy(*p, hdr->blob, expected_size); + + fhdr->size_bytes = expected_size + sizeof(*fhdr); + fhdr->header_size_bytes = offsetof(struct firmware_header, raw); + fhdr->header_version_major = 1; + fhdr->header_version_minor = 0; + fhdr->ucode_version = 0x10; + fhdr->ucode_size_bytes = expected_size; + fhdr->ucode_array_offset_bytes = sizeof(*fhdr); + + *p += expected_size; + + return 1; +} + +int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); + fhdr->gfx1.ucode_feature_version = 21; + fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); + fhdr->rlc1.ucode_feature_version = 1; + fhdr->rlc1.save_and_restore_offset = 0x90; + fhdr->rlc1.clear_state_descriptor_offset = 0x3d; + fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? + fhdr->rlc1.master_pkt_description_offset = 0; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 2; + fhdr->ip_version_minor = 1; + fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); + fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; + fhdr->sdma1.ucode_change_version = 0; + fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +static const u32 pfp_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc4200016, // ldw r8, [r0, #0x16] + 0xdc030000, // mov ctr, r0 + 0xcc000049, // stw r0, [r0, #0x49] + 0xcc200013, // stw r0, [r8, #0x13] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 ce_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc420000c, // ldw r8, [r0, #0xc] + 0xdc030000, // mov ctr, r0 + 0xcc00002f, // stw r0, [r0, #0x2f] + 0xcc200012, // stw r0, [r8, #0x12] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 mec_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc43c000c, // ldw r15, [r0, #0x9] + 0xdc030000, // mov ctr, r0 + 0xcc00002b, // stw r0, [r0, #0x2b] + 0xcc3c000d, // stw r0, [r15, #0xd] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +#define PACKET_TYPE_NOP 0x10 + +static void patch_fw(void *p, const u32 *handler, int handler_size) { + int size = ((struct firmware_header*)p)->ucode_size_bytes; + int code_size = (size & ~0xfff) / 4; + int nop_start = code_size - 0x10; + + u32 *fw = p + sizeof(struct firmware_header); + kern.printf("NOP handler at 0x%x\n", nop_start); + memcpy(&fw[nop_start], handler, handler_size); + + // patch the branch table entry + for (int off = code_size; off < size/4; off++) { + if ((fw[off] >> 16) == PACKET_TYPE_NOP) { + fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; + } + } +} + +struct fw_info_t *get_fw_info() { + if (kern.gc_get_fw_info) { + return kern.gc_get_fw_info(); + } else if (kern.Starsha_UcodeInfo) { + return kern.Starsha_UcodeInfo; + } else { + return NULL; + } +} + +const struct fw_expected_sizes_t *get_fw_expected_sizes() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return &gladius_fw_sizes; + } else { + return &liverpool_fw_sizes; + } +} + +const char * get_gpu_name() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return "gladius"; + } else { + return "liverpool"; + } +} + +ssize_t firmware_extract(void *dest) +{ + u8 *p = dest; + + // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. + struct fw_info_t *info = get_fw_info(); + if (!info) { + kern.printf("firmware_extract: Could not locate firmware table"); + return -1; + } + const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); + + //Eap hdd key + cpio_hdr(&p, "key", DIR, 0); + cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); + copy_eap_hdd_key(&p); + + cpio_hdr(&p, "lib", DIR, 0); + cpio_hdr(&p, "lib/firmware", DIR, 0); + + /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ + int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); + cpio_hdr(&p, "lib/firmware/edid", DIR, 0); + cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); + copy_edid(&p, edid_sz); + + char dir[7]; + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) + kern.snprintf(dir, sizeof(dir), "amdgpu"); + else + kern.snprintf(dir, sizeof(dir), "amdgpu"); + + char dir_path[64]; + kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); + cpio_hdr(&p, dir_path, DIR, 0); + + char pfp_path[64]; + kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", pfp_path); + cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); + u8 *pfp = p; + if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) + return -1; + patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); + + char me_path[64]; + kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", me_path); + cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); + if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) + return -1; + + char ce_path[64]; + kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", ce_path); + cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); + u8 *ce = p; + if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) + return -1; + patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); + + char mec_path[64]; + kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec_path); + cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); + u8 *mec1 = p; + if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) + return -1; + patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); + + char mec2_path[64]; + kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec2_path); + cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); + u8 *mec2 = p; + if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) + return -1; + patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); + + char rlc_path[64]; + kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", rlc_path); + cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); + if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) + return -1; + + char sdma_path[64]; + kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma_path); + cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); + if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + char sdma1_path[64]; + kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma1_path); + cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); + if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + size_t size = p - (u8*)dest; + if (size > FW_CPIO_SIZE) { + kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); + return -1; + } + + return size; +} diff --git a/linux/ps4-kexec-1300-pro/firmware.h b/linux/ps4-kexec-1300-pro/firmware.h new file mode 100644 index 0000000..4835983 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/firmware.h @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include "types.h" + +//sizes eap_hdd_key +#define EAP_HDD_KEY_SIZE 0x20 +#define EDID_SIZE 256 + +// sizes for liverpool +#define LVP_FW_CE_SIZE 8576 +#define LVP_FW_ME_SIZE 16768 +#define LVP_FW_MEC_SIZE 16768 +#define LVP_FW_MEC2_SIZE 16768 +#define LVP_FW_PFP_SIZE 16768 +#define LVP_FW_RLC_SIZE 6144 +#define LVP_FW_SDMA_SIZE 4200 +#define LVP_FW_SDMA1_SIZE 4200 +// sizes for gladius +#define GL_FW_CE_SIZE 8576 +#define GL_FW_ME_SIZE 16768 +#define GL_FW_MEC_SIZE 16768 +#define GL_FW_MEC2_SIZE 16768 +#define GL_FW_PFP_SIZE 16768 +#define GL_FW_RLC_SIZE 8192 +#define GL_FW_SDMA_SIZE 4200 +#define GL_FW_SDMA1_SIZE 4200 + +#define MAX(x ,y) (((x) > (y)) ? (x) : (y)) +#define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) + +#define FW_CE_SIZE MAX_FW_SIZE(CE) +#define FW_ME_SIZE MAX_FW_SIZE(ME) +#define FW_MEC_SIZE MAX_FW_SIZE(MEC) +#define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) +#define FW_PFP_SIZE MAX_FW_SIZE(PFP) +#define FW_RLC_SIZE MAX_FW_SIZE(RLC) +#define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) +#define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) + +// Conservative value (max 113 bytes plus name size plus alignment) +#define CPIO_HEADER_SIZE 256 + +#define FW_HEADER_SIZE 256 + +// Leave space for 16 files (currently 12) +#define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ + FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ + FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ + FW_HEADER_SIZE * 8) + +ssize_t firmware_extract(void *dest); + +#endif diff --git a/linux/ps4-kexec-1300-pro/kernel.c b/linux/ps4-kexec-1300-pro/kernel.c new file mode 100644 index 0000000..b6d31b4 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/kernel.c @@ -0,0 +1,383 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "string.h" +#include "elf.h" +#include "x86.h" +#include "../magic.h" + +struct ksym_t kern; +int (*early_printf)(const char *fmt, ...) = NULL; + +#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) + +#ifdef NO_SYMTAB + +#define RESOLVE_NOERR(name) do { \ + if (kern_off_ ## name == 0) { \ + kern.name = 0; \ + } else { \ + kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ + } \ +} while (0); + +#define RESOLVE(name) do { \ + if (kern_off_ ## name == 0) { \ + return 0; \ + } \ + RESOLVE_NOERR(name) \ +} while (0); + +#else + +#define KERNSIZE 0x2000000 + +static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; +static Elf64_Sym *symtab; +static char *strtab; +static size_t strtab_size; + +static Elf64_Ehdr *find_kern_ehdr(void) +{ + // Search for the kernel copy embedded in ubios, then follow it to see + // where it was relocated to + for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; + if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { + for (size_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; + if (phdr->p_type == PT_PHDR) { + return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); + } + } + } + } + return NULL; +} + +static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf64_Dyn *)phdr->p_vaddr; + } + } + return NULL; +} + +static int elf_parse_dyn(Elf64_Dyn *dyn) +{ + for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_SYMTAB: + symtab = (Elf64_Sym *)dp->d_un.d_ptr; + break; + case DT_STRTAB: + strtab = (char *)dp->d_un.d_ptr; + break; + case DT_STRSZ: + strtab_size = dp->d_un.d_val; + break; + } + } + return symtab && strtab && strtab_size; +} + +void *kernel_resolve(const char *name) +{ + for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { + if (!strcmp(name, &strtab[sym->st_name])) { + eprintf("kern.%s = %p\n", name, (void*)sym->st_value); + return (void *)sym->st_value; + } + } + eprintf("Failed to resolve symbol '%s'\n", name); + return NULL; +} + +#define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) +#define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; + +#endif + +static int resolve_symbols(void) +{ + RESOLVE(printf); + RESOLVE(snprintf); + early_printf = kern.printf; + RESOLVE(copyin); + RESOLVE(copyout); + RESOLVE(copyinstr); + RESOLVE(kernel_map); + RESOLVE(kernel_pmap_store); + RESOLVE(kmem_alloc_contig); + RESOLVE(kmem_free); + RESOLVE(pmap_extract); + RESOLVE(pmap_protect); + RESOLVE(sysent); + RESOLVE(sched_pin); + RESOLVE(sched_unpin); + RESOLVE(smp_rendezvous); + RESOLVE(smp_no_rendevous_barrier); + RESOLVE(icc_query_nowait); + RESOLVE_NOERR(Starsha_UcodeInfo); + RESOLVE_NOERR(gpu_devid_is_9924); + RESOLVE_NOERR(gc_get_fw_info); + RESOLVE_NOERR(eap_hdd_key); + RESOLVE_NOERR(edid); + RESOLVE(wlanbt); + RESOLVE(kern_reboot); + RESOLVE(set_gpu_freq); + RESOLVE(set_pstate); + RESOLVE(update_vddnp); + RESOLVE(set_cu_power_gate); + RESOLVE_NOERR(pstate_before_shutdown); + return 1; +} + +#define M_WAITOK 0x0002 +#define M_ZERO 0x0100 + +#define VM_MEMATTR_DEFAULT 0x06 + +void *kernel_alloc_contig(size_t size) +{ + // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... + vm_offset_t ret = 0; + while(!(ret = kern.kmem_alloc_contig( + *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, + ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); + + /*if (!ret) { + kern.printf("Failed to allocate %zud bytes\n", size); + return NULL; + }*/ + return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); +} + +void kernel_free_contig(void *addr, size_t size) +{ + if (!addr) + return; + kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); +} + +int kernel_hook_install(void *target, void *hook) +{ + uintptr_t t = (uintptr_t)target; // addr to redirect to + uintptr_t h = (uintptr_t)hook; // place to write the thunk + + if (!hook || !target) { + return 0; + } + + kern.printf("kernel_hook_install(%p, %p)\n", target, hook); + + if (!(t & (1L << 63))) { + kern.printf("\n===================== WARNING =====================\n"); + kern.printf("hook target function address: %p\n", target); + kern.printf("It looks like we're running from userland memory.\n"); + kern.printf("Please run this code from a kernel memory mapping.\n\n"); + return 0; + } + s64 displacement = t - (h + 5); + + kern.sched_pin(); + u64 wp = write_protect_disable(); + if (displacement < -0x80000000 || displacement > 0x7fffffff) { + kern.printf(" Using 64bit absolute jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[2]; + s32 zero; + void *target; + } jmp = { + .op = { 0xff, 0x25 }, + .zero = 0, + .target = target, + }; + ASSERT_STRSIZE(struct jmp_t, 14); + memcpy(hook, &jmp, sizeof(jmp)); + } else { + kern.printf(" Using 32bit relative jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[1]; + s32 imm; + } jmp = { + .op = { 0xe9 }, + .imm = displacement, + }; + ASSERT_STRSIZE(struct jmp_t, 5); + memcpy(hook, &jmp, sizeof(jmp)); + } + wbinvd(); + write_protect_restore(wp); + kern.sched_unpin(); + + return 1; +} + +void kernel_syscall_install(int num, void *call, int narg) +{ + struct sysent_t *sy = &kern.sysent[num]; + + kern.sched_pin(); + u64 wp = write_protect_disable(); + + memset(sy, 0, sizeof(*sy)); + sy->sy_narg = narg; + sy->sy_call = call; + sy->sy_thrcnt = 1; + + write_protect_restore(wp); + kern.sched_unpin(); +} + +void kernel_remap(void *start, void *end, int perm) +{ + u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); + u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); + + kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); + kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); +} + +static volatile int _global_test = 0; + +#ifndef DO_NOT_REMAP_RWX +extern u8 _start[], _end[]; + +static int patch_pmap_check(void) +{ + u8 *p; + + for (p = (u8*)kern.pmap_protect; + p < ((u8*)kern.pmap_protect + 0x500); p++) { + #ifdef PS4_6_72 + if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 + p[5] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + /*#ifdef PS4_5_05 + if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { + p[1] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + }*/ + #else + if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { + p[2] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + #endif + } + kern.printf("pmap_protect patch failed!\n"); + return 0; +} +#endif + +int kernel_init(void *_early_printf) +{ + int rv = -1; + + if (_early_printf) + early_printf = _early_printf; + + eprintf("kernel_init()\n"); + +#ifdef KASLR + // use `early_printf` to calculate kernel base + if (early_printf == NULL) + return 0; + + kern.kern_base = (u64)(early_printf - kern_off_printf); + if ((kern.kern_base & PAGE_MASK) != 0) { + eprintf("Kernel base is not aligned\n"); + return 0; + } else { + eprintf("Kernel base = %llx\n", kern.kern_base); + } + + u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); + u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); + +#else + kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 + + u64 DMPML4I = 0x1fc; + u64 DMPDPI = 0; +#endif + + kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); + eprintf("Direct map base = %llx\n", kern.dmap_base); + + // We may not be mapped writable yet, so to be able to write to globals + // we need WP disabled. + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + +#ifndef NO_SYMTAB + Elf64_Ehdr *ehdr = find_kern_ehdr(); + if (!ehdr) { + eprintf("Could not find kernel ELF header\n"); + goto err; + } + eprintf("ELF header at %p\n", ehdr); + + Elf64_Dyn *dyn = elf_get_dyn(ehdr); + if (!dyn) { + eprintf("Could not find kernel dynamic header\n"); + goto err; + } + eprintf("ELF dynamic section at %p\n", dyn); + + if (!elf_parse_dyn(dyn)) { + eprintf("Failed to parse ELF dynamic section\n"); + goto err; + } +#endif + + if (!resolve_symbols()) { + eprintf("Failed to resolve all symbols\n"); + goto err; + } + + // Pin ourselves as soon as possible. This is expected to be released by the caller. + kern.sched_pin(); + +#ifndef DO_NOT_REMAP_RWX + if (!patch_pmap_check()) + goto err; +#endif + +#ifndef DO_NOT_REMAP_RWX + // kernel_remap may need interrupts, but may not write to globals! + enable_interrupts(); + kernel_remap(_start, _end, 7); + disable_interrupts(); +#endif + + // Writing to globals is now safe. + + kern.printf("Testing global variable access (write protection)...\n"); + _global_test = 1; + kern.printf("OK.\n"); + + kern.printf("Kernel interface initialized\n"); + rv = 0; + +err: + write_protect_restore(wp); + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1300-pro/kernel.h b/linux/ps4-kexec-1300-pro/kernel.h new file mode 100644 index 0000000..1833f5b --- /dev/null +++ b/linux/ps4-kexec-1300-pro/kernel.h @@ -0,0 +1,126 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KERNEL_H +#define KERNEL_H + +#include "types.h" +#include "reboot.h" + +#define PAGE_SIZE 0x4000 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define PML4SHIFT 39 +#define PDPSHIFT 30 +#define PDRSHIFT 21 +#define PAGE_SHIFT 12 + +#define KVADDR(l4, l3, l2, l1) ( \ + ((unsigned long)-1 << 47) | \ + ((unsigned long)(l4) << PML4SHIFT) | \ + ((unsigned long)(l3) << PDPSHIFT) | \ + ((unsigned long)(l2) << PDRSHIFT) | \ + ((unsigned long)(l1) << PAGE_SHIFT)) + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) +#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX + +typedef u64 vm_paddr_t; +typedef u64 vm_offset_t; +typedef u64 vm_size_t; +typedef void * vm_map_t; +typedef char vm_memattr_t; +typedef void * pmap_t; + +typedef void (*smp_rendezvous_callback_t)(void *); + +struct sysent_t { + int sy_narg; + void *sy_call; + u16 sy_auevent; + void *sy_systrace_args_func; + int sy_entry; + int sy_return; + int sy_flags; + int sy_thrcnt; +}; + +struct ksym_t { + // two parameters related to kaslr (they are not symbols) + uintptr_t kern_base; + uintptr_t dmap_base; + + int (*printf)(const char *fmt, ...); + int (*snprintf)(const char *fmt, ...); + int (*copyin)(const void *uaddr, void *kaddr, size_t len); + int (*copyout)(const void *kaddr, void *uaddr, size_t len); + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); + + void **kernel_map; + void *kernel_pmap_store; + vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, + vm_paddr_t low, vm_paddr_t high, + unsigned long alignment, + unsigned long boundary, + vm_memattr_t memattr); + void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); + vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); + void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); + + struct sysent_t *sysent; + + void (*sched_pin)(void); + void (*sched_unpin)(void); + void (*smp_rendezvous)(smp_rendezvous_callback_t, + smp_rendezvous_callback_t, + smp_rendezvous_callback_t, void *); + // yes...it is misspelled :) + void (*smp_no_rendevous_barrier)(void *); + void *icc_query_nowait; + void *Starsha_UcodeInfo; + int (*gpu_devid_is_9924)(); + void *(*gc_get_fw_info)(); + void *eap_hdd_key; + void *edid; + void (*wlanbt)(unsigned int state); + int (*kern_reboot)(int magic); + void(*set_gpu_freq)(unsigned int num, unsigned int freq); + void(*set_pstate)(unsigned int val); + void(*update_vddnp)(unsigned int val); + void(*set_cu_power_gate)(unsigned int val); + void *pstate_before_shutdown; +}; + +extern struct ksym_t kern; + +static inline int curcpu(void) +{ + int cpuid; + // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw + asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); + return cpuid; +} + +// Assign a working printf function to this to debug the symbol resolver +extern int (*early_printf)(const char *fmt, ...); + +void *kernel_resolve(const char *name); + +void *kernel_alloc_contig(size_t size); +void kernel_free_contig(void *addr, size_t size); + +void kernel_remap(void *start, void *end, int perm); + +void kernel_syscall_install(int num, void *call, int narg); +int kernel_hook_install(void *target, void *hook); + +int kernel_init(void *early_printf); + +#endif diff --git a/linux/ps4-kexec-1300-pro/kexec.c b/linux/ps4-kexec-1300-pro/kexec.c new file mode 100644 index 0000000..fa55dfe --- /dev/null +++ b/linux/ps4-kexec-1300-pro/kexec.c @@ -0,0 +1,229 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "linux_boot.h" +#include "x86.h" +#include "kexec.h" +#include "firmware.h" +#include "string.h" +#include "acpi.h" + +static int k_copyin(const void *uaddr, void *kaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(kaddr, uaddr, len); + return 0; +} + +static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + const char *ustr = (const char*)uaddr; + char *kstr = (char*)kaddr; + size_t ret; + if (!uaddr || !kaddr) + return EFAULT; + ret = strlcpy(kstr, ustr, len); + if (ret >= len) { + if (done) + *done = len; + return ENAMETOOLONG; + } else { + if (done) + *done = ret + 1; + } + return 0; +} + +static int k_copyout(const void *kaddr, void *uaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(uaddr, kaddr, len); + return 0; +} + +int sys_kexec(void *td, struct sys_kexec_args *uap) +{ + int err = 0; + size_t initramfs_size = uap->initramfs_size; + void *image = NULL; + void *initramfs = NULL; + size_t firmware_size = 0; + struct boot_params *bp = NULL; + size_t cmd_line_maxlen = 0; + char *cmd_line = NULL; + + int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; + int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; + + kern.printf("sys_kexec invoked\n"); + kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, + uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); + + // Look up our shutdown hook point + void *icc_query_nowait = kern.icc_query_nowait; + if (!icc_query_nowait) { + err = ENOENT; + goto cleanup; + } + + // Set gpu frequencies and pstate + // FAT&SLIM / PRO + kern.set_pstate(3); + + kern.set_gpu_freq(0, 800); //800 //800 + kern.set_gpu_freq(1, 853); //673 //853 + kern.set_gpu_freq(2, 711); //610 //711 + kern.set_gpu_freq(3, 800); //800 //800 + kern.set_gpu_freq(4, 911); //800 //911 + kern.set_gpu_freq(5, 800); //711 //800 + kern.set_gpu_freq(6, 984); //711 //984 + kern.set_gpu_freq(7, 673); //673 //673 + + kern.update_vddnp(0x12); + kern.set_cu_power_gate(0x24); + + // Copy in kernel image + image = kernel_alloc_contig(uap->image_size); + if (!image) { + kern.printf("Failed to allocate image\n"); + err = ENOMEM; + goto cleanup; + } + err = copyin(uap->image, image, uap->image_size); + if (err) { + kern.printf("Failed to copy in image\n"); + goto cleanup; + } + + // Copy in initramfs + initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); + if (!initramfs) { + kern.printf("Failed to allocate initramfs\n"); + err = ENOMEM; + goto cleanup; + } + + err = firmware_extract(((u8*)initramfs)); + if (err < 0) { + kern.printf("Failed to extract GPU firmware - continuing anyway\n"); + } else { + firmware_size = err; + } + + if (initramfs_size) { + err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); + if (err) { + kern.printf("Failed to copy in initramfs\n"); + goto cleanup; + } + } + initramfs_size += firmware_size; + + // Copy in cmdline + cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; + cmd_line = kernel_alloc_contig(cmd_line_maxlen); + if (!cmd_line) { + kern.printf("Failed to allocate cmdline\n"); + err = ENOMEM; + goto cleanup; + } + err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); + if (err) { + kern.printf("Failed to copy in cmdline\n"); + goto cleanup; + } + cmd_line[cmd_line_maxlen - 1] = 0; + + kern.printf("\nkexec parameters:\n"); + kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); + kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", + initramfs_size, uap->initramfs_size); + kern.printf(" Kernel command line: %s\n", cmd_line); + kern.printf(" Kernel image buffer: %p\n", image); + kern.printf(" Initramfs buffer: %p\n", initramfs); + + // Allocate our boot params + bp = kernel_alloc_contig(sizeof(*bp)); + if (!bp) { + kern.printf("Failed to allocate bp\n"); + err = ENOMEM; + goto cleanup; + } + + // Initialize bp + // TODO should probably do this from cpu_quiesce_gate, then bp doesn't + // need to be allocated here, just placed directly into low mem + set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); + + prepare_boot_params(bp, image); + + // Hook the final ICC shutdown function + if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { + kern.printf("Failed to install shutdown hook\n"); + err = EINVAL; + goto cleanup; + } + + kern.printf("******************************************************\n"); + kern.printf("kexec successfully armed. Please shut down the system.\n"); + kern.printf("******************************************************\n\n"); + +/* + kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); + if (kern.kern_reboot(RB_POWEROFF) == -1) + kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); +*/ + return 0; + +cleanup: + kernel_free_contig(cmd_line, cmd_line_maxlen); + kernel_free_contig(bp, sizeof(*bp)); + kernel_free_contig(image, uap->image_size); + kernel_free_contig(initramfs, uap->initramfs_size); + return err; + + copyout(NULL, NULL, 0); +} + +int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) +{ + int rv = 0; + + // potentially needed to write early_printf + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + + if (kernel_init(_early_printf) < 0) { + rv = -1; + goto cleanup; + } + + kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); + kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); + kern.printf("kexec_init() successful\n\n"); + + if (sys_kexec_ptr) + *sys_kexec_ptr = sys_kexec; + +cleanup: + write_protect_restore(wp); + if (kern.sched_unpin && wp & CR0_WP) { + // If we're returning to a state with WP enabled, assume the caller + // wants the thread unpinned. Else the caller is expected to + // call kern.sched_unpin() manually. + kern.sched_unpin(); + } + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1300-pro/kexec.h b/linux/ps4-kexec-1300-pro/kexec.h new file mode 100644 index 0000000..6c31637 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/kexec.h @@ -0,0 +1,38 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KEXEC_H +#define KEXEC_H + +#include "types.h" + +#define SYS_KEXEC 153 +#define SYS_KEXEC_NARGS 6 + +struct sys_kexec_args { + void *image; + size_t image_size; + void *initramfs; + size_t initramfs_size; + char *cmd_line; + int vram_gb; +}; + +typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); + +// Note: td is unused, you can pass NULL if you call this directly. +int sys_kexec(void *td, struct sys_kexec_args *uap); + +int kernel_init(void *early_printf); + +int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) + __attribute__ ((section (".init"))); + +#endif diff --git a/linux/ps4-kexec-1300-pro/kexec.ld b/linux/ps4-kexec-1300-pro/kexec.ld new file mode 100644 index 0000000..1bcf26c --- /dev/null +++ b/linux/ps4-kexec-1300-pro/kexec.ld @@ -0,0 +1,25 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +ENTRY(kexec_init) + +SECTIONS { + /* We don't do GOT relocation and rely on nothing ending up using the GOT + * (-fno-common helps here) */ + /DISCARD/ : { *(.comment) *(.got) } + _start = .; + .init : { *(.init) *(.init.*) } + .text : { *(.text) *(.text.*) } + .data : { *(.data) *(.data.*) } + .rodata : { *(.rodata) *(.rodata.*) } + .bss : { *(.bss) *(.bss.*) *(COMMON)} + .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ + _end = .; +} diff --git a/linux/ps4-kexec-1300-pro/linux_boot.c b/linux/ps4-kexec-1300-pro/linux_boot.c new file mode 100644 index 0000000..197ef63 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/linux_boot.c @@ -0,0 +1,441 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "linux_boot.h" +#include "types.h" +#include "string.h" +#include "x86.h" +#include "kernel.h" +#include "uart.h" +#include "acpi.h" + +void uart_write_byte(u8 b); + +static u64 vram_base = 0x100000000; +// Current code assumes it's a power of two. +static u64 vram_size = 1024 * 1024 * 1024; +static int vram_gb = 2; + +#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) + +struct desc_ptr { + u16 limit; + u64 address; +} __attribute__((packed)); + +struct desc_struct { + u16 limit0; + u16 base0; + u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; + u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, + uintptr_t new_cr3, uintptr_t gdt_ptr); +extern uint8_t *jmp_to_linux; +extern size_t jmp_to_linux_size; + +// FreeBSD DMAP addresses +struct linux_boot_info { + void *linux_image; + void *initramfs; + size_t initramfs_size; + struct boot_params *bp; + char *cmd_line; +}; +static struct linux_boot_info nix_info; + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v) +{ + nix_info.linux_image = linux_image; + nix_info.bp = bp; + nix_info.initramfs = initramfs; + nix_info.initramfs_size = initramfs_size; + nix_info.cmd_line = cmd_line; + vram_gb = v; +} + +static volatile int halted_cpus = 0; + +static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, + u32 type) +{ + uint8_t idx = bp->e820_entries; + bp->e820_map[idx].addr = addr; + bp->e820_map[idx].size = size; + bp->e820_map[idx].type = type; + bp->e820_entries++; +} + +void prepare_boot_params(struct boot_params *bp, u8 *linux_image) +{ + memset(bp, 0, sizeof(struct boot_params)); + struct boot_params *bp_src = (struct boot_params *)linux_image; + memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + + ((u8 *)&bp_src->hdr.jump)[1]); + + // These values are from fw 1.01 + bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); + bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + // This used to be VRAM, but we reclaim it as RAM + bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); + // Instead, carve out VRAM from the beginning of high memory + bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, + SMAP_TYPE_MEMORY); +} + +#define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_FB_OFFSET 0x2068 +#define HDP_NONSURFACE_BASE 0x2c04 +#define CONFIG_MEMSIZE 0x5428 + +static void configure_vram(void) +{ + u64 mmio_base = 0xe4800000; + u64 fb_base = 0x0f00000000; + u64 fb_top = fb_base + vram_gb * vram_size - 1; + + WR32(mmio_base + MC_VM_FB_LOCATION, 0); + WR32(mmio_base + HDP_NONSURFACE_BASE, 0); + + WR32(mmio_base + MC_VM_FB_LOCATION, + ((fb_top >> 24) << 16) | (fb_base >> 24)); + WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); + WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); + WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); +} + +#define IA32_MTRR_DEF_TYPE 0x2ff +#define MTRR_BASE(i) (0x200 + 2*i) +#define MTRR_MASK(i) (0x201 + 2*i) + +static void setup_mtrr(void) +{ + disable_interrupts(); + u64 cr0 = cr0_read(); + cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); + wbinvd(); + cr3_write(cr3_read()); // TLB flush + + wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled + + // Low memory (0GB-2GB) = WB + wrmsr(MTRR_BASE(0), 0x0000000006); + wrmsr(MTRR_MASK(0), 0xff80000800); + // High memory (4GB-8GB) = WB + wrmsr(MTRR_BASE(1), 0x0100000006); + wrmsr(MTRR_MASK(1), 0xff00000800); + // High memory (8GB-10GB) = WB + wrmsr(MTRR_BASE(2), 0x0200000006); + wrmsr(MTRR_MASK(2), 0xff80000800); + // VRAM (4GB-4GB+vram_size) = UC + wrmsr(MTRR_BASE(3), 0x0100000000); + wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); + + wbinvd(); + cr3_write(cr3_read()); // TLB flush + wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable + cr0_write(cr0); + enable_interrupts(); +} + +static void cleanup_interrupts(void) +{ + int i; + disable_interrupts(); + + // Reset APIC stuff (per-CPU) + *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; + for (i = 0x320; i < 0x380; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + for (i = 0x480; i < 0x500; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; + for (i = 0x500; i < 0x540; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; + + // Fix the LVT offset for thresholding + wrmsr(0x413, (1L<<24) | (1L<<52)); + wrmsr(0xc0000408, (1L<<24) | (1L<<52)); +} + +static void cpu_quiesce_gate(void *arg) +{ + int i; + + // Ensure we can write anywhere + cr0_write(cr0_read() & ~CR0_WP); + + // Interrupt stuff local to each CPU + cleanup_interrupts(); + + // We want to set up MTRRs on all CPUs + setup_mtrr(); + + if (curcpu() != 0) { + // We're not on BSP. Try to halt. + __sync_fetch_and_add(&halted_cpus, 1); + cpu_stop(); + } + + uart_write_str("kexec: Waiting for secondary CPUs...\n"); + + // wait for all cpus to halt + while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); + + uart_write_str("kexec: Secondary CPUs quiesced\n"); + + //* Put ident mappings in current page tables + // Should not be needed, but maybe helps for debugging? + cr4_pge_disable(); + u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); + u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + // Clear (really) low mem. + // Linux reads from here to try and access EBDA... + // get_bios_ebda reads u16 from 0x40e + // reserve_ebda_region reads u16 from 0x413 + // Writing zeros causes linux to default to marking + // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. + // It doesn't match the ps4 e820 map, but that seems OK. + memset((void *)0, 0, 0x1000); + + // Create a new page table hierarchy out of the way of linux + // Accessed via freebsd direct map + pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt + // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| + memset(pml4_base, 0, 512 * sizeof(u64) * 2); + pdp_base = pml4_base + 512; + u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; + pml4_base[0] = pdpe; + // Maintain the freebsd direct map + pml4_base[DM_PML4_BASE] = pdpe; + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + uart_write_str("kexec: Setting up GDT...\n"); + + struct desc_ptr gdt_ptr; + struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); + gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; + gdt_ptr.address = DM_TO_ID(desc); + + // clear + memset(desc, 0, gdt_ptr.limit + 1); + // Most things are ignored in 64bit mode, and we will never be in + // 32bit/compat modes, so just setup another pure-64bit environment... + // Linux inits it's own GDT in secondary_startup_64 + // 0x10 + desc[2].limit0 = 0xffff; + desc[2].base0 = 0x0000; + desc[2].base1 = 0x0000; + desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; + desc[2].s = 1; + desc[2].dpl = 0; + desc[2].p = 1; + desc[2].limit = 0xf; + desc[2].avl = 0; + desc[2].l = 1; + desc[2].d = 0; + desc[2].g = 0; + desc[2].base2 = 0x00; + // 0x18 + desc[3].limit0 = 0xffff; + desc[3].base0 = 0x0000; + desc[3].base1 = 0x0000; + desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; + desc[3].s = 1; + desc[3].dpl = 0; + desc[3].p = 1; + desc[3].limit = 0xf; + desc[3].avl = 0; + desc[3].l = 0; + desc[3].d = 0; + desc[3].g = 0; + desc[3].base2 = 0x00; + // Task segment value + // 0x20 + desc[4].limit0 = 0x0000; + desc[4].base0 = 0x0000; + desc[4].base1 = 0x0000; + desc[4].type = SEG_TYPE_TSS; + desc[4].s = 1; + desc[4].dpl = 0; + desc[4].p = 1; + desc[4].limit = 0x0; + desc[4].avl = 0; + desc[4].l = 0; + desc[4].d = 0; + desc[4].g = 0; + desc[4].base2 = 0x00; + + uart_write_str("kexec: Relocating stub...\n"); + + // Relocate the stub and jump to it + // TODO should thunk_copy be DMAP here? + void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); + memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); + // XXX The +0x200 is for the iret stack in linux_thunk.S + uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; + + uart_write_str("kexec: Setting up boot params...\n"); + + // XXX we write into this bootargs and pass it to the kernel, but in + // jmp_to_linux we use the bootargs from the image as input. So they + // MUST MATCH! + struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; + *bp_lo = *nix_info.bp; + lowmem_pos += sizeof(struct boot_params); + + struct setup_header *shdr = &bp_lo->hdr; + shdr->cmd_line_ptr = lowmem_pos; + shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; + shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; + bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; + bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; + shdr->hardware_subarch = X86_SUBARCH_PS4; + // This needs to be nonzero for the initramfs to work + shdr->type_of_loader = 0xd0; // kexec + + strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, + nix_info.bp->hdr.cmdline_size); + lowmem_pos += strlen(nix_info.cmd_line) + 1; + + uart_write_str("kexec: Cleaning up hardware...\n"); + + // Disable IOMMU + *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; + + // Disable all MSIs on Aeolia + for (i = 0; i < 8; i++) + *(volatile u32 *)PA_TO_DM(0xd03c844c + i*4) = 0; + + // Stop HPET timers + *(volatile u64 *)PA_TO_DM(0xd0382010) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382100) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382120) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382140) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382160) = 0; + + uart_write_str("kexec: Reconfiguring VRAM...\n"); + + configure_vram(); + + uart_write_str("kexec: Resetting GPU...\n"); + + // Softreset GPU + *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks + *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC + *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 +// *(volatile u64 *)PA_TO_DM(0xe480d248) = 1; // Halt SDMA1 eeply + *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 + *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC + + *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply + +// *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x10003; // Softreset GFX/CP/RLC + *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x30005; // Softreset GFX/CP/RLC eeply + +// udelay(150); +// *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x10003; + *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x30005; //eeply +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) |= 0x00100140; // Softreset SDMA/GRBM +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~0x00100140; +// udelay(150); + + // Enable audio output + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; + *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; + +// // Set pin caps of pin 2 to vendor defined, to hide it +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// // Set pin caps of pin 3 to !HDMI +// *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; +// *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; + // Set pin configuration default + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; + + uart_write_str("kexec: About to relocate and jump to kernel\n"); + + ((jmp_to_linux_t)thunk_copy)( + DM_TO_ID(nix_info.linux_image), + DM_TO_ID(bp_lo), + DM_TO_ID(pml4_base), + (uintptr_t)&gdt_ptr + ); + + // should never reach here + uart_write_str("kexec: unreachable (?)\n"); +} + +// Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) +int hook_icc_query_nowait(u8 *icc_msg) +{ + kern.printf("hook_icc_query_nowait called\n"); + + // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot + //In alternative we can re-enable it here, but sometimes that give problems.. + kern.wlanbt(0x2); + + fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); + + kern.printf("ACPI tables fixed\n"); + + // Transition to BSP and halt other cpus + // smp_no_rendevous_barrier is just nullsub, but it is treated specially by + // smp_rendezvous. This is the easiest way to do this, since we can't assume + // we're already running on BSP. Since smp_rendezvous normally waits on all + // cpus to finish the callbacks, we just never return... + kern.smp_rendezvous(kern.smp_no_rendevous_barrier, + cpu_quiesce_gate, + kern.smp_no_rendevous_barrier, NULL); + + // should never reach here + kern.printf("hook_icc_query_nowait: unreachable (?)\n"); + return 0; +} diff --git a/linux/ps4-kexec-1300-pro/linux_boot.h b/linux/ps4-kexec-1300-pro/linux_boot.h new file mode 100644 index 0000000..2232b35 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/linux_boot.h @@ -0,0 +1,88 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef LINUX_BOOT_H +#define LINUX_BOOT_H + +#include "types.h" + +#define SMAP_TYPE_MEMORY 1 +#define SMAP_TYPE_RESERVED 2 +#define SMAP_TYPE_ACPI_RECLAIM 3 +#define SMAP_TYPE_ACPI_NVS 4 +#define SMAP_TYPE_UNUSABLE 5 +#define SMAP_TYPE_PMEM 7 + +#define X86_SUBARCH_PS4 5 + +struct e820entry { + u64 addr; /* start of memory segment */ + u64 size; /* size of memory segment */ + u32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct setup_header { + u8 setup_sects; + u16 root_flags; + u32 syssize; + u16 ram_size; + u16 vid_mode; + u16 root_dev; + u16 boot_flag; + u16 jump; + u32 header; + u16 version; + u32 realmode_swtch; + u16 start_sys; + u16 kernel_version; + u8 type_of_loader; + u8 loadflags; + u16 setup_move_size; + u32 code32_start; + u32 ramdisk_image; + u32 ramdisk_size; + u32 bootsect_kludge; + u16 heap_end_ptr; + u8 ext_loader_ver; + u8 ext_loader_type; + u32 cmd_line_ptr; + u32 initrd_addr_max; + u32 kernel_alignment; + u8 relocatable_kernel; + u8 min_alignment; + u16 xloadflags; + u32 cmdline_size; + u32 hardware_subarch; + u64 hardware_subarch_data; + u32 payload_offset; + u32 payload_length; + u64 setup_data; + u64 pref_address; + u32 init_size; + u32 handover_offset; +} __attribute__((packed)); + +#define E820MAX 128 /* number of entries in E820MAP */ + +OSTRUCT(boot_params, 0x1000) +OFIELD(0x0c0, u32 ext_ramdisk_image); +OFIELD(0x0c4, u32 ext_ramdisk_size); +OFIELD(0x0c8, u32 ext_cmd_line_ptr); +OFIELD(0x1e8, u8 e820_entries); +OFIELD(0x1f1, struct setup_header hdr); +OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); +OSTRUCT_END + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v); +void prepare_boot_params(struct boot_params *bp, u8 *linux_image); +int hook_icc_query_nowait(u8 *icc_msg); + +#endif diff --git a/linux/ps4-kexec-1300-pro/linux_thunk.S b/linux/ps4-kexec-1300-pro/linux_thunk.S new file mode 100644 index 0000000..f6d2dfb --- /dev/null +++ b/linux/ps4-kexec-1300-pro/linux_thunk.S @@ -0,0 +1,90 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +.intel_syntax noprefix + +.equ setup_sects, 0x1f1 +.equ shdr_syssize, 0x1f4 +.equ pref_address, 0x258 + +.text + +#void jmp_to_linux( +# uintptr_t image_base, rdi +# uintptr_t bootargs, rsi +# uintptr_t new_cr3, rdx +# uintptr_t gdt_ptr rcx +#); +.globl jmp_to_linux +jmp_to_linux: + # switch to new gdt + data segments + cli + lgdt [rcx] + #xor eax, eax + mov eax, 0x18 + mov ds, eax + mov ss, eax + mov es, eax + mov fs, eax + mov gs, eax + + # switch to our own page tables (in low mem) + mov cr3, rdx + + # now we're on our own page tables, so we can obliterate the rest of memory + # TODO make sure we don't inadvertently overwrite (important) smap regions + # I think on ps4 we'll actually want to load to 0x700000 + # since we have tons of free room there. + # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We + # should choose a higher address as pref_address. + + # save args + mov r12, rdi + mov r13, rsi + + # memmove(pref_address, , (syssize * 0x10) / 8) + #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go + #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) + mov rdi, 0x6000000 # should be far from bzImage and initramfs + mov r14, rdi # r14 = pref_address + xor edx, edx + mov dl, [r12 + setup_sects] + inc rdx + shl rdx, 9 # rdx = offsetof(image_base, startup_32) + lea rsi, [r12 + rdx] # src = image_base + startup_32 + mov ecx, [r12 + shdr_syssize] + shl rcx, 4 + add rdi, rcx + add rsi, rcx + sub rdi, 8 + sub rsi, 8 + shr rcx, 3 + std + rep movsq + cld + + # make a tiny stack - we just need it for the lretq. + # what we jump to will not use this stack + lea rsp, [rip + jmp_to_linux_end + 0x200] + and rsp, -0x10 + #push 0 # retaddr + push 0x10 # cs = GDT[2] + add r14, 0x200 # pref_address + startup_64 + push r14 # rip + mov rsi, r13 # bootargs + lretq +jmp_to_linux_end: + +.data + +.globl jmp_to_linux_size +jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux + +.att_syntax prefix diff --git a/linux/ps4-kexec-1300-pro/reboot.h b/linux/ps4-kexec-1300-pro/reboot.h new file mode 100644 index 0000000..20b91f8 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/reboot.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)reboot.h 8.3 (Berkeley) 12/13/94 + * $FreeBSD$ + */ + +#ifndef _SYS_REBOOT_H_ +#define _SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. These are passed to + * the boot program and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ +#define RB_SINGLE 0x002 /* reboot to single user only */ +#define RB_NOSYNC 0x004 /* dont sync before reboot */ +#define RB_HALT 0x008 /* don't reboot, just halt */ +#define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ +#define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ +#define RB_KDB 0x040 /* give control to kernel debugger */ +#define RB_RDONLY 0x080 /* mount root fs read-only */ +#define RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define RB_MINIROOT 0x200 /* Unused placeholder */ +#define RB_VERBOSE 0x800 /* print all potentially useful info */ +#define RB_SERIAL 0x1000 /* use serial port as console */ +#define RB_CDROM 0x2000 /* use cdrom as root */ +#define RB_POWEROFF 0x4000 /* turn the power off if possible */ +#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ +#define RB_MUTE 0x10000 /* start up with the console muted */ +#define RB_SELFTEST 0x20000 /* unused placeholder */ +#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ +#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ +#define RB_PAUSE 0x100000 /* pause after each output line during probe */ +#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ +#define RB_PROBE 0x10000000 /* Probe multiple consoles */ +#define RB_MULTIPLE 0x20000000 /* use multiple consoles */ + +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + +#endif diff --git a/linux/ps4-kexec-1300-pro/string.h b/linux/ps4-kexec-1300-pro/string.h new file mode 100644 index 0000000..70aa668 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/string.h @@ -0,0 +1,96 @@ +/* + * string.h -- standard C string-manipulation functions. + * + * Copyright (C) 2008 Segher Boessenkool + * Copyright (C) 2009 Haxx Enterprises + * Copyright (C) 2010-2016 Hector Martin "marcan" + * + * Portions taken from the Public Domain C Library (PDCLib). + * http://pdclib.rootdirectory.de/ + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +static inline int strcmp(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s1[i] == s2[i]; i++) + ; + + return s1[i] - s2[i]; +} + +static inline void *memset(void *b, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)b)[i] = c; + + return b; +} + +static inline void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} + +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) + return p1[i] - p2[i]; + + return 0; +} + +static inline size_t strlen(const char *s) +{ + size_t len; + + for (len = 0; s[len]; len++) + ; + + return len; +} + +static inline size_t strnlen(const char *s, size_t count) +{ + size_t len; + + for (len = 0; s[len] && len < count; len++) + ; + + return len; +} + +static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) +{ + size_t len, needed; + + len = needed = strnlen(src, maxlen - 1) + 1; + if (len >= maxlen) + len = maxlen - 1; + + memcpy(dest, src, len); + dest[len] = 0; + + return needed - 1; +} + +#endif diff --git a/linux/ps4-kexec-1300-pro/types.h b/linux/ps4-kexec-1300-pro/types.h new file mode 100644 index 0000000..28d6619 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/types.h @@ -0,0 +1,51 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef TYPES_H +#define TYPES_H + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#ifndef TESTING +typedef u8 uint8_t; +typedef u64 size_t; +typedef s64 ssize_t; +typedef u64 uintptr_t; +typedef s64 off_t; +#endif + +#define NULL ((void *)0) + +#define CAT_(x, y) x ## y +#define CAT(x, y) CAT_(x, y) + +#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] +#define OSTRUCT(name, size) struct name { union { OPAD(size); +#define OSTRUCT_END };}; +#define OFIELD(off, field) struct { OPAD(off); field; } + +#define ASSERT_STRSIZE(struc, size) \ + _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#define ENOENT 2 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define ENAMETOOLONG 63 + +#endif diff --git a/linux/ps4-kexec-1300-pro/uart.c b/linux/ps4-kexec-1300-pro/uart.c new file mode 100644 index 0000000..62ea949 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/uart.c @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "uart.h" +#include "kernel.h" +#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) + +#define AEOLIA_UART_BASE 0xD0340000 +#define BAIKAL_UART_BASE 0xC890E000 + +#define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, AEOLIA_UART_BASE + (intf << 12) + (reg << 2)) + +#define UART_REG_DATA 0 +#define UART_REG_IER 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +# define LSR_TXRDY 0x20 +# define LSR_TEMT 0x40 +#define UART_REG_MSR 6 +#define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) +#define UART0_IER UART_REG(32, 0, UART_REG_IER) +#define UART0_IIR UART_REG(32, 0, UART_REG_IIR) +#define UART0_LCR UART_REG(32, 0, UART_REG_LCR) +#define UART0_MCR UART_REG(32, 0, UART_REG_MCR) +#define UART0_LSR UART_REG(32, 0, UART_REG_LSR) +#define UART0_MSR UART_REG(32, 0, UART_REG_MSR) + +void uart_write_byte(u8 b) +{ + int limit; + u64 barrier; + limit = 250000; + while (!(*UART0_LSR & LSR_TXRDY) && --limit) + ; + *UART0_DATA = b; + __sync_fetch_and_add(&barrier, 0); + limit = 250000; + while (!(*UART0_LSR & LSR_TEMT) && --limit) + ; +} + +void uart_write_char(char c) +{ + if (c == '\n') + uart_write_byte('\r'); + + uart_write_byte(c); +} + +void uart_write_str(const char *s) +{ + while (*s) { + uart_write_char(*s++); + } +} diff --git a/linux/ps4-kexec-1300-pro/uart.h b/linux/ps4-kexec-1300-pro/uart.h new file mode 100644 index 0000000..b803e29 --- /dev/null +++ b/linux/ps4-kexec-1300-pro/uart.h @@ -0,0 +1,20 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef UART_H +#define UART_H + +#include "types.h" + +void uart_write_byte(u8 b); +void uart_write_char(char c); +void uart_write_str(const char *s); + +#endif diff --git a/linux/ps4-kexec-1300-pro/x86.h b/linux/ps4-kexec-1300-pro/x86.h new file mode 100644 index 0000000..61b22ff --- /dev/null +++ b/linux/ps4-kexec-1300-pro/x86.h @@ -0,0 +1,149 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef X86_H +#define X86_H + +#define FLAGS_IF (1 << 9) + +#define CR0_WP (1 << 16) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) + +#define PG_V (1 << 0) +#define PG_RW (1 << 1) +#define PG_U (1 << 2) +#define PG_PS (1 << 7) + +#define SEG_TYPE_DATA (0 << 3) +#define SEG_TYPE_READ_WRITE (1 << 1) +#define SEG_TYPE_CODE (1 << 3) +#define SEG_TYPE_EXEC_READ (1 << 1) +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) + +static inline u64 cr0_read(void) +{ + u64 reg; + asm volatile("mov %0, cr0;" : "=r" (reg)); + return reg; +} + +static inline void cr0_write(u64 val) +{ + asm volatile("mov cr0, %0;" :: "r" (val)); +} + +static inline u64 write_protect_disable() +{ + u64 cr0 = cr0_read(); + cr0_write(cr0 & ~CR0_WP); + return cr0; +} + +static inline void write_protect_restore(u64 cr0) +{ + // Use only WP bit of input + cr0_write(cr0_read() | (cr0 & CR0_WP)); +} + +static inline u64 cr3_read(void) +{ + u64 reg; + asm volatile("mov %0, cr3;" : "=r" (reg)); + return reg; +} + +static inline void cr3_write(u64 val) +{ + asm volatile("mov cr3, %0;" :: "r" (val)); +} + +static inline void cr4_pge_disable(void) +{ + u64 cr4_temp; + asm volatile( + "mov %0, cr4;" + "and %0, ~0x80;" + "mov cr4, %0;" + : "=r" (cr4_temp) + ); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +static inline void cpu_stop(void) +{ + for (;;) + asm volatile("cli; hlt;" : : : "memory"); +} + +static inline void outl(int port, unsigned int data) +{ + asm volatile("out %w1, %0" : : "a" (data), "d" (port)); +} + +static inline void wrmsr(u32 msr_id, u64 msr_value) +{ + asm volatile( + "wrmsr" + : + : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) + ); +} + +static inline u64 rdtsc (void) +{ + unsigned int tickl, tickh; + asm volatile( + "rdtsc" + :"=a"(tickl),"=d"(tickh) + ); + return ((u64)tickh << 32) | tickl; +} + +static inline void udelay(unsigned int usec) { + u64 later = rdtsc() + usec * 1594ULL; + while (((s64)(later - rdtsc())) > 0); +} + +static inline void disable_interrupts(void) +{ + asm volatile("cli"); +} + +static inline void enable_interrupts(void) +{ + asm volatile("sti"); +} + +static inline u64 read_flags(void) +{ + u64 flags; + asm volatile("pushf; pop %0;" : "=r" (flags)); + return flags; +} + +static inline u64 intr_disable(void) +{ + u64 flags = read_flags(); + disable_interrupts(); + return flags; +} + +static inline void intr_restore(u64 flags) +{ + // TODO should only IF be or'd in? + asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); +} + +#endif diff --git a/linux/ps4-kexec-1300/LICENSE b/linux/ps4-kexec-1300/LICENSE new file mode 100644 index 0000000..6c1cbba --- /dev/null +++ b/linux/ps4-kexec-1300/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2015-2016 shuffle2 +Copyright (C) 2015-2016 Hector Martin "marcan" +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/ps4-kexec-1300/Makefile b/linux/ps4-kexec-1300/Makefile new file mode 100644 index 0000000..d06ee6e --- /dev/null +++ b/linux/ps4-kexec-1300/Makefile @@ -0,0 +1,37 @@ +TOOLCHAIN_PREFIX ?= +CC = $(TOOLCHAIN_PREFIX)gcc +AR = $(TOOLCHAIN_PREFIX)ar +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy + +CFLAGS=$(CFLAG) -DPS4_13_00 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX +CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ + -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ + -fno-asynchronous-unwind-tables \ + -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ + -mcmodel=small -mno-red-zone + +SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ + acpi.c crc32.c + +OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) +DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld + +all: libkexec.a kexec.bin + +%.o: %.c *.h + $(CC) -c $(CFLAGS) -o $@ $< + +%.o: %.S + $(CC) -c $(CFLAGS) -o $@ $< + +libkexec.a: $(OBJS) + $(AR) -rc $@ $(OBJS) + +kexec.elf: libkexec.a kexec.ld + $(CC) $(CFLAGS) -o $@ libkexec.a + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +clean: + rm -f libkexec.a kexec.elf kexec.bin $(OBJS) diff --git a/linux/ps4-kexec-1300/README.md b/linux/ps4-kexec-1300/README.md new file mode 100644 index 0000000..6500878 --- /dev/null +++ b/linux/ps4-kexec-1300/README.md @@ -0,0 +1,121 @@ +# PS4 kexec implementation + +This repo implements a kexec()-style system call for the PS4 Orbis kernel +(FreeBSD derivative). This is designed to boot a Linux kernel directly from +FreeBSD. + +This is not an exploit. It is useless without some mechanism of injecting code +into the PS4 OS kernel. + +## Building + +To build a kexec.bin relocatable binary using the supplied Makefile, just type +`make`. This will also build a kexec.a archive. You can either use the binary +directly, or link the archive into your own project. + +If you link kexec.a with your own code, you need to supply the two symbols +`_start` and `_end` in your linker script, as `kernel_init()` will try to remap +all pages covered by that range as RWX (to make global variable accesses work). +Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this +feature, if you have already taken care of page permissions for the code. + +If you use a compiler toolchain that have a special prefix you can declare it +by passing TOOLCHAIN_PREFIX option to the Makefile like this: + + make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' + +## Usage + +The code is designed to be completely standalone. There is a single entry point: + + int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); + +Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel +symbols and install the sys_kexec system call. The syscall is registered +as number 153 by default (you can change this in kexec.h). The return value +is 0 on success, or negative on error. + +You may pass something other than NULL as `early_printf`. In that case, that +function will be used for debug output during early symbol resolution, before +printf is available. + +Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is +enabled by default, symtab also disappears in newer kernel, we have to +hardcode offsets for some symbols. Currently we use the `early_printf` +given by user to caculate the base address of kernel, then relocate all the +symbols from the kernel base. You could enable this feature like this: + + make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' + +If you do not want to call the syscall from userspace, you can pass the address +of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the +address of `sys_kexec`, so you can invoke it manually (see kexec.h for +its prototype and how the arguments are passed). + +If you are using the standalone kexec.bin blob, then the `kexec_init` function +is always located at offset 0, so simply call the base address of the blob. +Don't forget to pass two NULL arguments (or the appropriate pointers). + +The injected `sys_kexec` system call takes (userspace) pointers to the kernel +and initramfs blobs, their sizes, and a pointer to the (null-terminated) command +line string. From userspace, this looks like this: + + int kexec(void *kernel_image, size_t image_size, + void *initramfs, size_t initramfs_size, + const char *cmdline); + + // syscall() usage: + syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); + +`kexec()` will load the kernel and initramfs into memory, but will not directly +boot them. To boot the loaded kernel, shut down the system. This can be +accomplished by pressing the power button, but can also be done more quickly +and reliably from userspace with the following sequence of system calls (this +kills userspace quickly but still does a controlled filesystem unmount): + + int evf = syscall(540, "SceSysCoreReboot"); + syscall(546, evf, 0x4000, 0); + syscall(541, evf); + // should be syscall(37, 1, 30) but only tested via kill symbol + kill(1, 30); + +Note that this software should be loaded into kernel memory space. If you are +running kernel code from userland mappings, you should either switch to kernel +mappings or separately copy kexec.bin to a location in kernel address space. +While syscalls or exploit code may run properly from userland, the shutdown hook +will not, as it will be called from a different process context. + +## Features + +`kernel_init()` will automatically find the Orbis OS kernel and resolve all +necessary symbols to work. There are no static symbol dependencies. If +`DO_NOT_REMAP_RWX` is not defined (the default), it will also patch +`pmap_protect` to disable the W^X restriction. + +In addition to loading the user-supplied initramfs, `kexec` will locate the +Radeon firmware blobs inside Orbis OS, extract them, convert them to a format +suitable for Linux, and append them as an additional initramfs cpio image to +the existing initramfs. This avoids the need to distribute the Radeon firmware +blobs. The `radeon` module, when compiled into the kernel, will automatically +load this firmware on boot. Note however that most typical initramfs scripts +will wipe the initramfs contents while pivoting to the real system, so if you +compile `radeon` as a module you may not be able to access the firmware after +boot. To cover that case, add some code to your initramfs `/init` script to +copy the firmware to a tmpfs mounted on the real filesystem: + + # assuming real root FS is mounted on /mnt + + mkdir -p /mnt/lib/firmware/radeon + mount -t tmpfs none /mnt/lib/firmware/radeon + cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ + + # now switch_root to /mnt + +This avoids having to permanently store copies of the Radeon firmware, which +isn't really necessary for most use cases. + +There is significant debug logging available, which will appear on the system +UART. Most of the code relies on the kernel `printf` implementation, and +therefore you should patch out the UART output blanker to see it. The final +code that runs on the boot CPU before booting the kernel uses direct UART +writes and is not affected by the blanking feature of Orbis OS. diff --git a/linux/ps4-kexec-1300/acpi.c b/linux/ps4-kexec-1300/acpi.c new file mode 100644 index 0000000..f53f04e --- /dev/null +++ b/linux/ps4-kexec-1300/acpi.c @@ -0,0 +1,296 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "types.h" + +#ifdef TESTING +# include +# include +# include +# include +# include +# include +#else +# include "kernel.h" +# include "string.h" +# define printf kern.printf +#endif + +#define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) +#define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) + +#define PACKED __attribute__((packed)) + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +struct RSDP { + u64 sig; + u8 checksum; + u8 oemid[6]; + u8 rev; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 rsvd[3]; +} PACKED; + +struct SDTH { + u32 sig; + u32 length; + u8 rev; + u8 checksum; + u8 oem_id[6]; + u8 oem_tid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; +} PACKED; + +struct RSDT { + struct SDTH hdr; + u32 table_addr[]; +} PACKED; + +struct XSDT { + struct SDTH hdr; + u64 table_addr[]; +} PACKED; + +struct FADT { + struct SDTH hdr; + u32 facs; + u32 dsdt; + // more stuff... +} PACKED; + +struct ivhd_entry4 { + u8 type; + u16 devid; + u8 flags; +} PACKED; + +struct ivhd_header { + u8 type; + u8 flags; + u16 length; + u16 devid; + u16 cap_ptr; + u64 mmio_phys; + u16 pci_seg; + u16 info; + u32 efr_attr; +} PACKED; + +struct IVRS { + struct SDTH hdr; + u32 IVinfo; + u8 reserved[8]; + struct ivhd_header hd_hdr; + struct ivhd_entry4 hd_entries[3]; +} PACKED; + +// We have enough space to use the second half of the 64KB table area +// as scratch space for building the tables +#define BUFFER_OFF 0x8000 + +#define P2M(p) (((u64)(p)) - phys_base + map_base) +#define M2P(p) ((((void*)(p)) - map_base) + phys_base) +#define B2P(p) ((((void*)(p)) - buf_base) + phys_base) + +#define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) +#define PADB(s) p += (s) +#define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) +#define ALLOC(t) (t)ALLOCB(sizeof(t)) +#define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) +#define COPYT(s) COPYB(((struct SDTH*)s)->length, s) +#define COPYTP(s) COPYT(P2M(s)) +#define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) +#define COPYP(t, s) COPY(t, P2M(s)) + +static void rsdp_checksum(struct RSDP *rsdp) { + rsdp->checksum = rsdp->ext_checksum = 0; + + u8 sum = 0; + for (int i = 0; i < 20; i++) + sum += ((u8*)rsdp)[i]; + rsdp->checksum = -sum; + sum = 0; + for (int i = 0; i < sizeof(*rsdp); i++) + sum += ((u8*)rsdp)[i]; + rsdp->ext_checksum = -sum; +} + +static void table_checksum(void *table) { + struct SDTH *hdr = table; + hdr->checksum = 0; + u8 sum = 0; + for (int i = 0; i < hdr->length; i++) + sum += ((u8*)table)[i]; + hdr->checksum = -sum; +} + +#define IVHD_FLAG_ISOC_EN_MASK 0x08 +#define IVHD_DEV_ALL 0x01 +#define IVHD_DEV_SELECT 0x02 +#define IVHD_DEV_SELECT_RANGE_START 0x03 +#define IVHD_DEV_RANGE_END 0x04 + +#define ACPI_DEVFLAG_SYSMGT1 0x10 +#define ACPI_DEVFLAG_SYSMGT2 0x20 + +static void *build_ivrs(struct IVRS *ivrs) { + memset(ivrs, 0, sizeof(*ivrs)); + + ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); + ivrs->hdr.length = sizeof(*ivrs); + ivrs->hdr.rev = 1; + memcpy(ivrs->hdr.oem_id, "F0F ", 6); + memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); + ivrs->hdr.oem_rev = 0x20161225; + memcpy(ivrs->hdr.creator_id, "KEXC", 4); + ivrs->hdr.creator_rev = 0x20161225; + ivrs->IVinfo = 0x00203040; + + struct ivhd_header *hdr = &ivrs->hd_hdr; + hdr->type = 0x10; + hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; + hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); + hdr->devid = PCI_DEVFN(0, 2); + hdr->cap_ptr = 0x40; // from config space + 0x34 + hdr->mmio_phys = 0xfc000000; + hdr->pci_seg = 0; + hdr->info = 0; // msi msg num? (the pci cap should be written by software) + // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 + hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); + + struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; + // on fbsd, all aeolia devfns have active entries except memories (func 6) + // not sure if this is just because it wasn't in use when i dumped it? + // all entries are r/w + // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) + // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) + // Modes: + // 4 level: + // apcie + // 3 level: + // all others + + // the way to encode this info into the IVHD entries is fairly arbitrary... + entries[0].type = IVHD_DEV_SELECT; + entries[0].devid = PCI_DEVFN(20, 0); + entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; + + entries[1].type = IVHD_DEV_SELECT_RANGE_START; + entries[1].devid = PCI_DEVFN(20, 1); + entries[1].flags = 0; + entries[2].type = IVHD_DEV_RANGE_END; + entries[2].devid = PCI_DEVFN(20, 7); + entries[2].flags = 0; + + table_checksum(ivrs); + return ivrs + 1; +} + +void fix_acpi_tables(void *map_base, u64 phys_base) +{ + void *buf_base = map_base + 0x8000; + void *p = buf_base; + memset(buf_base, 0, 0x8000); + + printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); + + struct RSDP *rsdp = COPYP(struct RSDP, phys_base); + printf("RSDT at 0x%x\n", rsdp->rsdt_addr); + printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); + + struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); + struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); + rsdp->rsdt_addr = B2P(rsdt); + + PADB(0x30); // this gives us space for new tables + + struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); + rsdp->xsdt_addr = B2P(xsdt); + + PADB(0x60); + + struct FADT *fadt = NULL; + + int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; + int i; + for (i = 0; i < cnt; i++) { + struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); + printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); + switch (hdr->sig) { + case SIG32('F', 'A', 'C', 'P'): + { + fadt = (void*)hdr; + printf("FACS at 0x%x\n", fadt->facs); + printf("DSDT at 0x%x\n", fadt->dsdt); + // Sony puts the FACS before the FADT, unaligned, which is + // noncompliant, but let's keep it there + u8 *facs = COPYB(64, P2M(fadt->facs)); + fadt = (void*)(hdr = COPYT(hdr)); + fadt->facs = B2P(facs); + PADB(0x38); + break; + } + case SIG32('S', 'S', 'D', 'T'): + { + // Put the DSDT before the SSDT + if (fadt) { + PADB(0xf0); + u8 *dsdt = COPYTP(fadt->dsdt); + fadt->dsdt = B2P(dsdt); + PADB(0x174); + table_checksum(fadt); + } else { + printf("ERROR: no FADT yet?\n"); + } + hdr = COPYT(hdr); + break; + } + default: + hdr = COPYT(hdr); + } + table_checksum(hdr); + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); + } + + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); + i++; + p = build_ivrs(p); + + rsdt->hdr.length = sizeof(*rsdt) + 4 * i; + xsdt->hdr.length = sizeof(*xsdt) + 8 * i; + + rsdp_checksum(rsdp); + table_checksum(rsdt); + table_checksum(xsdt); + memcpy(map_base, buf_base, p - buf_base); +} + + +#ifdef TESTING + +int main(int argc, char **argv) +{ + int fd; + void *base; + + fd = open(argv[1], O_RDWR); + base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + fix_acpi_tables(base, 0xe0000); + return 0; +} + +#endif diff --git a/linux/ps4-kexec-1300/acpi.h b/linux/ps4-kexec-1300/acpi.h new file mode 100644 index 0000000..5723982 --- /dev/null +++ b/linux/ps4-kexec-1300/acpi.h @@ -0,0 +1,18 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ACPI_H +#define ACPI_H + +#include "types.h" + +void fix_acpi_tables(void *map_base, u64 phys_base); + +#endif diff --git a/linux/ps4-kexec-1300/crc32.c b/linux/ps4-kexec-1300/crc32.c new file mode 100644 index 0000000..7836c18 --- /dev/null +++ b/linux/ps4-kexec-1300/crc32.c @@ -0,0 +1,102 @@ +/*- +* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or +* code or tables extracted from it, as desired without restriction. +* +* First, the polynomial itself and its table of feedback terms. The +* polynomial is +* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 +* +* Note that we take it "backwards" and put the highest-order term in +* the lowest-order bit. The X^32 term is "implied"; the LSB is the +* X^31 term, etc. The X^0 term (usually shown as "+1") results in +* the MSB being 1 +* +* Note that the usual hardware shift register implementation, which +* is what we're using (we're merely optimizing it by doing eight-bit +* chunks at a time) shifts bits into the lowest-order term. In our +* implementation, that means shifting towards the right. Why do we +* do it this way? Because the calculated CRC must be transmitted in +* order from highest-order term to lowest-order term. UARTs transmit +* characters in order from LSB to MSB. By storing the CRC this way +* we hand it to the UART in the order low-byte to high-byte; the UART +* sends each low-bit to hight-bit; and the result is transmission bit +* by bit from highest- to lowest-order term without requiring any bit +* shuffling on our part. Reception works similarly +* +* The feedback terms table consists of 256, 32-bit entries. Notes +* +* The table can be generated at runtime if desired; code to do so +* is shown later. It might not be obvious, but the feedback +* terms simply represent the results of eight shift/xor opera +* tions for all combinations of data and CRC register values +* +* The values must be right-shifted by eight bits by the "updcrc +* logic; the shift must be unsigned (bring in zeroes). On some +* hardware you could probably optimize the shift in assembler by +* using byte-swap instructions +* polynomial $edb88320 +* +* +* CRC32 code derived from work by Gary S. Brown. +*/ + +#include "crc32.h" + +static u32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 crc32(u32 crc, const void *buf, size_t size) +{ + const u8 *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} diff --git a/linux/ps4-kexec-1300/crc32.h b/linux/ps4-kexec-1300/crc32.h new file mode 100644 index 0000000..777278d --- /dev/null +++ b/linux/ps4-kexec-1300/crc32.h @@ -0,0 +1,7 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +u32 crc32(u32 crc, const void *buf, size_t size); + +#endif diff --git a/linux/ps4-kexec-1300/elf.h b/linux/ps4-kexec-1300/elf.h new file mode 100644 index 0000000..63371c7 --- /dev/null +++ b/linux/ps4-kexec-1300/elf.h @@ -0,0 +1,71 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + u8 e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + u64 e_entry; + u64 e_phoff; + u64 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf64_Ehdr; + +typedef struct { + u32 p_type; + u32 p_flags; + u64 p_offset; + void *p_vaddr; + u64 p_paddr; + u64 p_filesz; + u64 p_memsz; + u64 p_align; +} Elf64_Phdr; + +#define PT_DYNAMIC 2 +#define PT_PHDR 6 + +#define DT_NULL 0 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_STRSZ 10 + +typedef struct { + s64 d_tag; + union { + u64 d_val; + void *d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + void *st_value; + u64 st_size; +} Elf64_Sym; + +#endif diff --git a/linux/ps4-kexec-1300/firmware.c b/linux/ps4-kexec-1300/firmware.c new file mode 100644 index 0000000..a831b1c --- /dev/null +++ b/linux/ps4-kexec-1300/firmware.c @@ -0,0 +1,479 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "firmware.h" +#include "types.h" +#include "kernel.h" +#include "string.h" +#include "types.h" +#include "crc32.h" + +#define DIR 0040755 +#define FILE 0100644 + +struct firmware_header { + u32 size_bytes; + u32 header_size_bytes; + u16 header_version_major; + u16 header_version_minor; + u16 ip_version_major; + u16 ip_version_minor; + u32 ucode_version; + u32 ucode_size_bytes; + u32 ucode_array_offset_bytes; + u32 crc32; + union { + struct { + u32 ucode_feature_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } gfx1; + struct { + u32 ucode_feature_version; + u32 save_and_restore_offset; + u32 clear_state_descriptor_offset; + u32 avail_scratch_ram_locations; + u32 master_pkt_description_offset; + u8 end[]; + } rlc1; + struct { + u32 ucode_feature_version; + u32 ucode_change_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } sdma1; + u8 raw[0xe0]; + }; +}; + +static inline char hex(u8 c) +{ + if (c <= 9) + return '0' + c; + return 'a' + c - 10; +} + +static void hex8(u8 **p, u32 val) +{ + *(*p)++ = hex(val >> 28); + *(*p)++ = hex((val >> 24) & 0xf); + *(*p)++ = hex((val >> 20) & 0xf); + *(*p)++ = hex((val >> 16) & 0xf); + *(*p)++ = hex((val >> 12) & 0xf); + *(*p)++ = hex((val >> 8) & 0xf); + *(*p)++ = hex((val >> 4) & 0xf); + *(*p)++ = hex(val & 0xf); +} + +void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) +{ + size_t name_len = strlen(name); + + // Pad to 4 byte multiple + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; + + memcpy(*p, "070701", 6); + *p += 6; + hex8(p, 0); // c_ino + hex8(p, mode); // c_mode + hex8(p, 0); // c_uid + hex8(p, 0); // c_gid + hex8(p, 1); // c_nlink + hex8(p, 0); // c_mtime + hex8(p, size); // c_filesize + hex8(p, 0); // c_maj + hex8(p, 0); // c_min + hex8(p, 0); // c_rmaj + hex8(p, 0); // c_rmin + hex8(p, name_len + 1); // c_namesize + hex8(p, 0); // c_chksum + memcpy(*p, name, name_len); + *p += name_len; + *(*p)++ = 0; + + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; +} + +struct fw_header_t { + u64 size_words; + char *unk_ident; + u64 unk; + void *blob; + u64 unk2; +}; + +struct fw_info_t { + struct fw_header_t *rlc; + struct fw_header_t *sdma0; + struct fw_header_t *sdma1; + struct fw_header_t *ce; + struct fw_header_t *pfp; + struct fw_header_t *me; + struct fw_header_t *mec1; + struct fw_header_t *mec2; +}; + +struct fw_expected_sizes_t { + u64 rlc; + u64 sdma0; + u64 sdma1; + u64 ce; + u64 pfp; + u64 me; + u64 mec1; + u64 mec2; +}; +static const struct fw_expected_sizes_t liverpool_fw_sizes = { + LVP_FW_RLC_SIZE, + LVP_FW_SDMA_SIZE, + LVP_FW_SDMA1_SIZE, + LVP_FW_CE_SIZE, + LVP_FW_PFP_SIZE, + LVP_FW_ME_SIZE, + LVP_FW_MEC_SIZE, + LVP_FW_MEC2_SIZE +}; +static const struct fw_expected_sizes_t gladius_fw_sizes = { + GL_FW_RLC_SIZE, + GL_FW_SDMA_SIZE, + GL_FW_SDMA1_SIZE, + GL_FW_CE_SIZE, + GL_FW_PFP_SIZE, + GL_FW_ME_SIZE, + GL_FW_MEC_SIZE, + GL_FW_MEC2_SIZE +}; + +void copy_edid(u8 **p, int sz) +{ + int i; + u8 *edid = *p; + u8 *off_edid = kern.edid; + + memset(edid, 0, sz); + *p += sz; + + for(i = 0; i < sz; i++) + *(edid + i) = *(off_edid + i); + + *p += sz; +} + +void copy_eap_hdd_key(u8 **p) +{ + int i; + u8 *eap_key = *p; + u8 *off_eap_key = kern.eap_hdd_key; + + memset(eap_key, 0, 0x20); + *p += 0x20; + + for(i = 0; i < 0x20; i++) + { + if(i < 0x10) + *(eap_key + i) = *(off_eap_key + 0xF - i); + else + *(eap_key + i) = *(off_eap_key + 0x2F - i); + } + *p += 0x20; +} + +int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + kern.printf("Copying %s firmware\n", name); + if (expected_size != (hdr->size_words * 4)) { + kern.printf("copy_firmware: %s: expected size %d, got %d\n", + name, expected_size, hdr->size_words * 4); + return 0; + } + + struct firmware_header *fhdr = (struct firmware_header*)*p; + memset(fhdr, 0, sizeof(*fhdr)); + *p += sizeof(*fhdr); + + memcpy(*p, hdr->blob, expected_size); + + fhdr->size_bytes = expected_size + sizeof(*fhdr); + fhdr->header_size_bytes = offsetof(struct firmware_header, raw); + fhdr->header_version_major = 1; + fhdr->header_version_minor = 0; + fhdr->ucode_version = 0x10; + fhdr->ucode_size_bytes = expected_size; + fhdr->ucode_array_offset_bytes = sizeof(*fhdr); + + *p += expected_size; + + return 1; +} + +int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); + fhdr->gfx1.ucode_feature_version = 21; + fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); + fhdr->rlc1.ucode_feature_version = 1; + fhdr->rlc1.save_and_restore_offset = 0x90; + fhdr->rlc1.clear_state_descriptor_offset = 0x3d; + fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? + fhdr->rlc1.master_pkt_description_offset = 0; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 2; + fhdr->ip_version_minor = 1; + fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); + fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; + fhdr->sdma1.ucode_change_version = 0; + fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +static const u32 pfp_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc4200016, // ldw r8, [r0, #0x16] + 0xdc030000, // mov ctr, r0 + 0xcc000049, // stw r0, [r0, #0x49] + 0xcc200013, // stw r0, [r8, #0x13] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 ce_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc420000c, // ldw r8, [r0, #0xc] + 0xdc030000, // mov ctr, r0 + 0xcc00002f, // stw r0, [r0, #0x2f] + 0xcc200012, // stw r0, [r8, #0x12] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 mec_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc43c000c, // ldw r15, [r0, #0x9] + 0xdc030000, // mov ctr, r0 + 0xcc00002b, // stw r0, [r0, #0x2b] + 0xcc3c000d, // stw r0, [r15, #0xd] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +#define PACKET_TYPE_NOP 0x10 + +static void patch_fw(void *p, const u32 *handler, int handler_size) { + int size = ((struct firmware_header*)p)->ucode_size_bytes; + int code_size = (size & ~0xfff) / 4; + int nop_start = code_size - 0x10; + + u32 *fw = p + sizeof(struct firmware_header); + kern.printf("NOP handler at 0x%x\n", nop_start); + memcpy(&fw[nop_start], handler, handler_size); + + // patch the branch table entry + for (int off = code_size; off < size/4; off++) { + if ((fw[off] >> 16) == PACKET_TYPE_NOP) { + fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; + } + } +} + +struct fw_info_t *get_fw_info() { + if (kern.gc_get_fw_info) { + return kern.gc_get_fw_info(); + } else if (kern.Starsha_UcodeInfo) { + return kern.Starsha_UcodeInfo; + } else { + return NULL; + } +} + +const struct fw_expected_sizes_t *get_fw_expected_sizes() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return &gladius_fw_sizes; + } else { + return &liverpool_fw_sizes; + } +} + +const char * get_gpu_name() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return "gladius"; + } else { + return "liverpool"; + } +} + +ssize_t firmware_extract(void *dest) +{ + u8 *p = dest; + + // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. + struct fw_info_t *info = get_fw_info(); + if (!info) { + kern.printf("firmware_extract: Could not locate firmware table"); + return -1; + } + const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); + + //Eap hdd key + cpio_hdr(&p, "key", DIR, 0); + cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); + copy_eap_hdd_key(&p); + + cpio_hdr(&p, "lib", DIR, 0); + cpio_hdr(&p, "lib/firmware", DIR, 0); + + /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ + int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); + cpio_hdr(&p, "lib/firmware/edid", DIR, 0); + cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); + copy_edid(&p, edid_sz); + + char dir[7]; + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) + kern.snprintf(dir, sizeof(dir), "amdgpu"); + else + kern.snprintf(dir, sizeof(dir), "amdgpu"); + + char dir_path[64]; + kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); + cpio_hdr(&p, dir_path, DIR, 0); + + char pfp_path[64]; + kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", pfp_path); + cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); + u8 *pfp = p; + if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) + return -1; + patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); + + char me_path[64]; + kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", me_path); + cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); + if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) + return -1; + + char ce_path[64]; + kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", ce_path); + cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); + u8 *ce = p; + if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) + return -1; + patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); + + char mec_path[64]; + kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec_path); + cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); + u8 *mec1 = p; + if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) + return -1; + patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); + + char mec2_path[64]; + kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec2_path); + cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); + u8 *mec2 = p; + if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) + return -1; + patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); + + char rlc_path[64]; + kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", rlc_path); + cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); + if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) + return -1; + + char sdma_path[64]; + kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma_path); + cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); + if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + char sdma1_path[64]; + kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma1_path); + cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); + if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + size_t size = p - (u8*)dest; + if (size > FW_CPIO_SIZE) { + kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); + return -1; + } + + return size; +} diff --git a/linux/ps4-kexec-1300/firmware.h b/linux/ps4-kexec-1300/firmware.h new file mode 100644 index 0000000..4835983 --- /dev/null +++ b/linux/ps4-kexec-1300/firmware.h @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include "types.h" + +//sizes eap_hdd_key +#define EAP_HDD_KEY_SIZE 0x20 +#define EDID_SIZE 256 + +// sizes for liverpool +#define LVP_FW_CE_SIZE 8576 +#define LVP_FW_ME_SIZE 16768 +#define LVP_FW_MEC_SIZE 16768 +#define LVP_FW_MEC2_SIZE 16768 +#define LVP_FW_PFP_SIZE 16768 +#define LVP_FW_RLC_SIZE 6144 +#define LVP_FW_SDMA_SIZE 4200 +#define LVP_FW_SDMA1_SIZE 4200 +// sizes for gladius +#define GL_FW_CE_SIZE 8576 +#define GL_FW_ME_SIZE 16768 +#define GL_FW_MEC_SIZE 16768 +#define GL_FW_MEC2_SIZE 16768 +#define GL_FW_PFP_SIZE 16768 +#define GL_FW_RLC_SIZE 8192 +#define GL_FW_SDMA_SIZE 4200 +#define GL_FW_SDMA1_SIZE 4200 + +#define MAX(x ,y) (((x) > (y)) ? (x) : (y)) +#define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) + +#define FW_CE_SIZE MAX_FW_SIZE(CE) +#define FW_ME_SIZE MAX_FW_SIZE(ME) +#define FW_MEC_SIZE MAX_FW_SIZE(MEC) +#define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) +#define FW_PFP_SIZE MAX_FW_SIZE(PFP) +#define FW_RLC_SIZE MAX_FW_SIZE(RLC) +#define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) +#define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) + +// Conservative value (max 113 bytes plus name size plus alignment) +#define CPIO_HEADER_SIZE 256 + +#define FW_HEADER_SIZE 256 + +// Leave space for 16 files (currently 12) +#define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ + FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ + FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ + FW_HEADER_SIZE * 8) + +ssize_t firmware_extract(void *dest); + +#endif diff --git a/linux/ps4-kexec-1300/kernel.c b/linux/ps4-kexec-1300/kernel.c new file mode 100644 index 0000000..b6d31b4 --- /dev/null +++ b/linux/ps4-kexec-1300/kernel.c @@ -0,0 +1,383 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "string.h" +#include "elf.h" +#include "x86.h" +#include "../magic.h" + +struct ksym_t kern; +int (*early_printf)(const char *fmt, ...) = NULL; + +#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) + +#ifdef NO_SYMTAB + +#define RESOLVE_NOERR(name) do { \ + if (kern_off_ ## name == 0) { \ + kern.name = 0; \ + } else { \ + kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ + } \ +} while (0); + +#define RESOLVE(name) do { \ + if (kern_off_ ## name == 0) { \ + return 0; \ + } \ + RESOLVE_NOERR(name) \ +} while (0); + +#else + +#define KERNSIZE 0x2000000 + +static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; +static Elf64_Sym *symtab; +static char *strtab; +static size_t strtab_size; + +static Elf64_Ehdr *find_kern_ehdr(void) +{ + // Search for the kernel copy embedded in ubios, then follow it to see + // where it was relocated to + for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; + if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { + for (size_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; + if (phdr->p_type == PT_PHDR) { + return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); + } + } + } + } + return NULL; +} + +static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf64_Dyn *)phdr->p_vaddr; + } + } + return NULL; +} + +static int elf_parse_dyn(Elf64_Dyn *dyn) +{ + for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_SYMTAB: + symtab = (Elf64_Sym *)dp->d_un.d_ptr; + break; + case DT_STRTAB: + strtab = (char *)dp->d_un.d_ptr; + break; + case DT_STRSZ: + strtab_size = dp->d_un.d_val; + break; + } + } + return symtab && strtab && strtab_size; +} + +void *kernel_resolve(const char *name) +{ + for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { + if (!strcmp(name, &strtab[sym->st_name])) { + eprintf("kern.%s = %p\n", name, (void*)sym->st_value); + return (void *)sym->st_value; + } + } + eprintf("Failed to resolve symbol '%s'\n", name); + return NULL; +} + +#define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) +#define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; + +#endif + +static int resolve_symbols(void) +{ + RESOLVE(printf); + RESOLVE(snprintf); + early_printf = kern.printf; + RESOLVE(copyin); + RESOLVE(copyout); + RESOLVE(copyinstr); + RESOLVE(kernel_map); + RESOLVE(kernel_pmap_store); + RESOLVE(kmem_alloc_contig); + RESOLVE(kmem_free); + RESOLVE(pmap_extract); + RESOLVE(pmap_protect); + RESOLVE(sysent); + RESOLVE(sched_pin); + RESOLVE(sched_unpin); + RESOLVE(smp_rendezvous); + RESOLVE(smp_no_rendevous_barrier); + RESOLVE(icc_query_nowait); + RESOLVE_NOERR(Starsha_UcodeInfo); + RESOLVE_NOERR(gpu_devid_is_9924); + RESOLVE_NOERR(gc_get_fw_info); + RESOLVE_NOERR(eap_hdd_key); + RESOLVE_NOERR(edid); + RESOLVE(wlanbt); + RESOLVE(kern_reboot); + RESOLVE(set_gpu_freq); + RESOLVE(set_pstate); + RESOLVE(update_vddnp); + RESOLVE(set_cu_power_gate); + RESOLVE_NOERR(pstate_before_shutdown); + return 1; +} + +#define M_WAITOK 0x0002 +#define M_ZERO 0x0100 + +#define VM_MEMATTR_DEFAULT 0x06 + +void *kernel_alloc_contig(size_t size) +{ + // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... + vm_offset_t ret = 0; + while(!(ret = kern.kmem_alloc_contig( + *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, + ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); + + /*if (!ret) { + kern.printf("Failed to allocate %zud bytes\n", size); + return NULL; + }*/ + return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); +} + +void kernel_free_contig(void *addr, size_t size) +{ + if (!addr) + return; + kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); +} + +int kernel_hook_install(void *target, void *hook) +{ + uintptr_t t = (uintptr_t)target; // addr to redirect to + uintptr_t h = (uintptr_t)hook; // place to write the thunk + + if (!hook || !target) { + return 0; + } + + kern.printf("kernel_hook_install(%p, %p)\n", target, hook); + + if (!(t & (1L << 63))) { + kern.printf("\n===================== WARNING =====================\n"); + kern.printf("hook target function address: %p\n", target); + kern.printf("It looks like we're running from userland memory.\n"); + kern.printf("Please run this code from a kernel memory mapping.\n\n"); + return 0; + } + s64 displacement = t - (h + 5); + + kern.sched_pin(); + u64 wp = write_protect_disable(); + if (displacement < -0x80000000 || displacement > 0x7fffffff) { + kern.printf(" Using 64bit absolute jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[2]; + s32 zero; + void *target; + } jmp = { + .op = { 0xff, 0x25 }, + .zero = 0, + .target = target, + }; + ASSERT_STRSIZE(struct jmp_t, 14); + memcpy(hook, &jmp, sizeof(jmp)); + } else { + kern.printf(" Using 32bit relative jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[1]; + s32 imm; + } jmp = { + .op = { 0xe9 }, + .imm = displacement, + }; + ASSERT_STRSIZE(struct jmp_t, 5); + memcpy(hook, &jmp, sizeof(jmp)); + } + wbinvd(); + write_protect_restore(wp); + kern.sched_unpin(); + + return 1; +} + +void kernel_syscall_install(int num, void *call, int narg) +{ + struct sysent_t *sy = &kern.sysent[num]; + + kern.sched_pin(); + u64 wp = write_protect_disable(); + + memset(sy, 0, sizeof(*sy)); + sy->sy_narg = narg; + sy->sy_call = call; + sy->sy_thrcnt = 1; + + write_protect_restore(wp); + kern.sched_unpin(); +} + +void kernel_remap(void *start, void *end, int perm) +{ + u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); + u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); + + kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); + kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); +} + +static volatile int _global_test = 0; + +#ifndef DO_NOT_REMAP_RWX +extern u8 _start[], _end[]; + +static int patch_pmap_check(void) +{ + u8 *p; + + for (p = (u8*)kern.pmap_protect; + p < ((u8*)kern.pmap_protect + 0x500); p++) { + #ifdef PS4_6_72 + if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 + p[5] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + /*#ifdef PS4_5_05 + if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { + p[1] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + }*/ + #else + if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { + p[2] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + #endif + } + kern.printf("pmap_protect patch failed!\n"); + return 0; +} +#endif + +int kernel_init(void *_early_printf) +{ + int rv = -1; + + if (_early_printf) + early_printf = _early_printf; + + eprintf("kernel_init()\n"); + +#ifdef KASLR + // use `early_printf` to calculate kernel base + if (early_printf == NULL) + return 0; + + kern.kern_base = (u64)(early_printf - kern_off_printf); + if ((kern.kern_base & PAGE_MASK) != 0) { + eprintf("Kernel base is not aligned\n"); + return 0; + } else { + eprintf("Kernel base = %llx\n", kern.kern_base); + } + + u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); + u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); + +#else + kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 + + u64 DMPML4I = 0x1fc; + u64 DMPDPI = 0; +#endif + + kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); + eprintf("Direct map base = %llx\n", kern.dmap_base); + + // We may not be mapped writable yet, so to be able to write to globals + // we need WP disabled. + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + +#ifndef NO_SYMTAB + Elf64_Ehdr *ehdr = find_kern_ehdr(); + if (!ehdr) { + eprintf("Could not find kernel ELF header\n"); + goto err; + } + eprintf("ELF header at %p\n", ehdr); + + Elf64_Dyn *dyn = elf_get_dyn(ehdr); + if (!dyn) { + eprintf("Could not find kernel dynamic header\n"); + goto err; + } + eprintf("ELF dynamic section at %p\n", dyn); + + if (!elf_parse_dyn(dyn)) { + eprintf("Failed to parse ELF dynamic section\n"); + goto err; + } +#endif + + if (!resolve_symbols()) { + eprintf("Failed to resolve all symbols\n"); + goto err; + } + + // Pin ourselves as soon as possible. This is expected to be released by the caller. + kern.sched_pin(); + +#ifndef DO_NOT_REMAP_RWX + if (!patch_pmap_check()) + goto err; +#endif + +#ifndef DO_NOT_REMAP_RWX + // kernel_remap may need interrupts, but may not write to globals! + enable_interrupts(); + kernel_remap(_start, _end, 7); + disable_interrupts(); +#endif + + // Writing to globals is now safe. + + kern.printf("Testing global variable access (write protection)...\n"); + _global_test = 1; + kern.printf("OK.\n"); + + kern.printf("Kernel interface initialized\n"); + rv = 0; + +err: + write_protect_restore(wp); + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1300/kernel.h b/linux/ps4-kexec-1300/kernel.h new file mode 100644 index 0000000..1833f5b --- /dev/null +++ b/linux/ps4-kexec-1300/kernel.h @@ -0,0 +1,126 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KERNEL_H +#define KERNEL_H + +#include "types.h" +#include "reboot.h" + +#define PAGE_SIZE 0x4000 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define PML4SHIFT 39 +#define PDPSHIFT 30 +#define PDRSHIFT 21 +#define PAGE_SHIFT 12 + +#define KVADDR(l4, l3, l2, l1) ( \ + ((unsigned long)-1 << 47) | \ + ((unsigned long)(l4) << PML4SHIFT) | \ + ((unsigned long)(l3) << PDPSHIFT) | \ + ((unsigned long)(l2) << PDRSHIFT) | \ + ((unsigned long)(l1) << PAGE_SHIFT)) + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) +#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX + +typedef u64 vm_paddr_t; +typedef u64 vm_offset_t; +typedef u64 vm_size_t; +typedef void * vm_map_t; +typedef char vm_memattr_t; +typedef void * pmap_t; + +typedef void (*smp_rendezvous_callback_t)(void *); + +struct sysent_t { + int sy_narg; + void *sy_call; + u16 sy_auevent; + void *sy_systrace_args_func; + int sy_entry; + int sy_return; + int sy_flags; + int sy_thrcnt; +}; + +struct ksym_t { + // two parameters related to kaslr (they are not symbols) + uintptr_t kern_base; + uintptr_t dmap_base; + + int (*printf)(const char *fmt, ...); + int (*snprintf)(const char *fmt, ...); + int (*copyin)(const void *uaddr, void *kaddr, size_t len); + int (*copyout)(const void *kaddr, void *uaddr, size_t len); + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); + + void **kernel_map; + void *kernel_pmap_store; + vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, + vm_paddr_t low, vm_paddr_t high, + unsigned long alignment, + unsigned long boundary, + vm_memattr_t memattr); + void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); + vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); + void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); + + struct sysent_t *sysent; + + void (*sched_pin)(void); + void (*sched_unpin)(void); + void (*smp_rendezvous)(smp_rendezvous_callback_t, + smp_rendezvous_callback_t, + smp_rendezvous_callback_t, void *); + // yes...it is misspelled :) + void (*smp_no_rendevous_barrier)(void *); + void *icc_query_nowait; + void *Starsha_UcodeInfo; + int (*gpu_devid_is_9924)(); + void *(*gc_get_fw_info)(); + void *eap_hdd_key; + void *edid; + void (*wlanbt)(unsigned int state); + int (*kern_reboot)(int magic); + void(*set_gpu_freq)(unsigned int num, unsigned int freq); + void(*set_pstate)(unsigned int val); + void(*update_vddnp)(unsigned int val); + void(*set_cu_power_gate)(unsigned int val); + void *pstate_before_shutdown; +}; + +extern struct ksym_t kern; + +static inline int curcpu(void) +{ + int cpuid; + // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw + asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); + return cpuid; +} + +// Assign a working printf function to this to debug the symbol resolver +extern int (*early_printf)(const char *fmt, ...); + +void *kernel_resolve(const char *name); + +void *kernel_alloc_contig(size_t size); +void kernel_free_contig(void *addr, size_t size); + +void kernel_remap(void *start, void *end, int perm); + +void kernel_syscall_install(int num, void *call, int narg); +int kernel_hook_install(void *target, void *hook); + +int kernel_init(void *early_printf); + +#endif diff --git a/linux/ps4-kexec-1300/kexec.c b/linux/ps4-kexec-1300/kexec.c new file mode 100644 index 0000000..d659229 --- /dev/null +++ b/linux/ps4-kexec-1300/kexec.c @@ -0,0 +1,229 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "linux_boot.h" +#include "x86.h" +#include "kexec.h" +#include "firmware.h" +#include "string.h" +#include "acpi.h" + +static int k_copyin(const void *uaddr, void *kaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(kaddr, uaddr, len); + return 0; +} + +static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + const char *ustr = (const char*)uaddr; + char *kstr = (char*)kaddr; + size_t ret; + if (!uaddr || !kaddr) + return EFAULT; + ret = strlcpy(kstr, ustr, len); + if (ret >= len) { + if (done) + *done = len; + return ENAMETOOLONG; + } else { + if (done) + *done = ret + 1; + } + return 0; +} + +static int k_copyout(const void *kaddr, void *uaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(uaddr, kaddr, len); + return 0; +} + +int sys_kexec(void *td, struct sys_kexec_args *uap) +{ + int err = 0; + size_t initramfs_size = uap->initramfs_size; + void *image = NULL; + void *initramfs = NULL; + size_t firmware_size = 0; + struct boot_params *bp = NULL; + size_t cmd_line_maxlen = 0; + char *cmd_line = NULL; + + int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; + int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; + + kern.printf("sys_kexec invoked\n"); + kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, + uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); + + // Look up our shutdown hook point + void *icc_query_nowait = kern.icc_query_nowait; + if (!icc_query_nowait) { + err = ENOENT; + goto cleanup; + } + + // Set gpu frequencies and pstate + // FAT&SLIM / PRO + kern.set_pstate(3); + + kern.set_gpu_freq(0, 800); //800 //800 + kern.set_gpu_freq(1, 673); //673 //853 + kern.set_gpu_freq(2, 609); //610 //711 + kern.set_gpu_freq(3, 800); //800 //800 + kern.set_gpu_freq(4, 800); //800 //911 + kern.set_gpu_freq(5, 711); //711 //800 + kern.set_gpu_freq(6, 711); //711 //984 + kern.set_gpu_freq(7, 673); //673 //673 + + kern.update_vddnp(0x12); + kern.set_cu_power_gate(0x12); + + // Copy in kernel image + image = kernel_alloc_contig(uap->image_size); + if (!image) { + kern.printf("Failed to allocate image\n"); + err = ENOMEM; + goto cleanup; + } + err = copyin(uap->image, image, uap->image_size); + if (err) { + kern.printf("Failed to copy in image\n"); + goto cleanup; + } + + // Copy in initramfs + initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); + if (!initramfs) { + kern.printf("Failed to allocate initramfs\n"); + err = ENOMEM; + goto cleanup; + } + + err = firmware_extract(((u8*)initramfs)); + if (err < 0) { + kern.printf("Failed to extract GPU firmware - continuing anyway\n"); + } else { + firmware_size = err; + } + + if (initramfs_size) { + err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); + if (err) { + kern.printf("Failed to copy in initramfs\n"); + goto cleanup; + } + } + initramfs_size += firmware_size; + + // Copy in cmdline + cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; + cmd_line = kernel_alloc_contig(cmd_line_maxlen); + if (!cmd_line) { + kern.printf("Failed to allocate cmdline\n"); + err = ENOMEM; + goto cleanup; + } + err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); + if (err) { + kern.printf("Failed to copy in cmdline\n"); + goto cleanup; + } + cmd_line[cmd_line_maxlen - 1] = 0; + + kern.printf("\nkexec parameters:\n"); + kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); + kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", + initramfs_size, uap->initramfs_size); + kern.printf(" Kernel command line: %s\n", cmd_line); + kern.printf(" Kernel image buffer: %p\n", image); + kern.printf(" Initramfs buffer: %p\n", initramfs); + + // Allocate our boot params + bp = kernel_alloc_contig(sizeof(*bp)); + if (!bp) { + kern.printf("Failed to allocate bp\n"); + err = ENOMEM; + goto cleanup; + } + + // Initialize bp + // TODO should probably do this from cpu_quiesce_gate, then bp doesn't + // need to be allocated here, just placed directly into low mem + set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); + + prepare_boot_params(bp, image); + + // Hook the final ICC shutdown function + if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { + kern.printf("Failed to install shutdown hook\n"); + err = EINVAL; + goto cleanup; + } + + kern.printf("******************************************************\n"); + kern.printf("kexec successfully armed. Please shut down the system.\n"); + kern.printf("******************************************************\n\n"); + +/* + kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); + if (kern.kern_reboot(RB_POWEROFF) == -1) + kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); +*/ + return 0; + +cleanup: + kernel_free_contig(cmd_line, cmd_line_maxlen); + kernel_free_contig(bp, sizeof(*bp)); + kernel_free_contig(image, uap->image_size); + kernel_free_contig(initramfs, uap->initramfs_size); + return err; + + copyout(NULL, NULL, 0); +} + +int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) +{ + int rv = 0; + + // potentially needed to write early_printf + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + + if (kernel_init(_early_printf) < 0) { + rv = -1; + goto cleanup; + } + + kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); + kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); + kern.printf("kexec_init() successful\n\n"); + + if (sys_kexec_ptr) + *sys_kexec_ptr = sys_kexec; + +cleanup: + write_protect_restore(wp); + if (kern.sched_unpin && wp & CR0_WP) { + // If we're returning to a state with WP enabled, assume the caller + // wants the thread unpinned. Else the caller is expected to + // call kern.sched_unpin() manually. + kern.sched_unpin(); + } + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1300/kexec.h b/linux/ps4-kexec-1300/kexec.h new file mode 100644 index 0000000..6c31637 --- /dev/null +++ b/linux/ps4-kexec-1300/kexec.h @@ -0,0 +1,38 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KEXEC_H +#define KEXEC_H + +#include "types.h" + +#define SYS_KEXEC 153 +#define SYS_KEXEC_NARGS 6 + +struct sys_kexec_args { + void *image; + size_t image_size; + void *initramfs; + size_t initramfs_size; + char *cmd_line; + int vram_gb; +}; + +typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); + +// Note: td is unused, you can pass NULL if you call this directly. +int sys_kexec(void *td, struct sys_kexec_args *uap); + +int kernel_init(void *early_printf); + +int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) + __attribute__ ((section (".init"))); + +#endif diff --git a/linux/ps4-kexec-1300/kexec.ld b/linux/ps4-kexec-1300/kexec.ld new file mode 100644 index 0000000..1bcf26c --- /dev/null +++ b/linux/ps4-kexec-1300/kexec.ld @@ -0,0 +1,25 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +ENTRY(kexec_init) + +SECTIONS { + /* We don't do GOT relocation and rely on nothing ending up using the GOT + * (-fno-common helps here) */ + /DISCARD/ : { *(.comment) *(.got) } + _start = .; + .init : { *(.init) *(.init.*) } + .text : { *(.text) *(.text.*) } + .data : { *(.data) *(.data.*) } + .rodata : { *(.rodata) *(.rodata.*) } + .bss : { *(.bss) *(.bss.*) *(COMMON)} + .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ + _end = .; +} diff --git a/linux/ps4-kexec-1300/linux_boot.c b/linux/ps4-kexec-1300/linux_boot.c new file mode 100644 index 0000000..197ef63 --- /dev/null +++ b/linux/ps4-kexec-1300/linux_boot.c @@ -0,0 +1,441 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "linux_boot.h" +#include "types.h" +#include "string.h" +#include "x86.h" +#include "kernel.h" +#include "uart.h" +#include "acpi.h" + +void uart_write_byte(u8 b); + +static u64 vram_base = 0x100000000; +// Current code assumes it's a power of two. +static u64 vram_size = 1024 * 1024 * 1024; +static int vram_gb = 2; + +#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) + +struct desc_ptr { + u16 limit; + u64 address; +} __attribute__((packed)); + +struct desc_struct { + u16 limit0; + u16 base0; + u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; + u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, + uintptr_t new_cr3, uintptr_t gdt_ptr); +extern uint8_t *jmp_to_linux; +extern size_t jmp_to_linux_size; + +// FreeBSD DMAP addresses +struct linux_boot_info { + void *linux_image; + void *initramfs; + size_t initramfs_size; + struct boot_params *bp; + char *cmd_line; +}; +static struct linux_boot_info nix_info; + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v) +{ + nix_info.linux_image = linux_image; + nix_info.bp = bp; + nix_info.initramfs = initramfs; + nix_info.initramfs_size = initramfs_size; + nix_info.cmd_line = cmd_line; + vram_gb = v; +} + +static volatile int halted_cpus = 0; + +static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, + u32 type) +{ + uint8_t idx = bp->e820_entries; + bp->e820_map[idx].addr = addr; + bp->e820_map[idx].size = size; + bp->e820_map[idx].type = type; + bp->e820_entries++; +} + +void prepare_boot_params(struct boot_params *bp, u8 *linux_image) +{ + memset(bp, 0, sizeof(struct boot_params)); + struct boot_params *bp_src = (struct boot_params *)linux_image; + memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + + ((u8 *)&bp_src->hdr.jump)[1]); + + // These values are from fw 1.01 + bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); + bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + // This used to be VRAM, but we reclaim it as RAM + bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); + // Instead, carve out VRAM from the beginning of high memory + bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, + SMAP_TYPE_MEMORY); +} + +#define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_FB_OFFSET 0x2068 +#define HDP_NONSURFACE_BASE 0x2c04 +#define CONFIG_MEMSIZE 0x5428 + +static void configure_vram(void) +{ + u64 mmio_base = 0xe4800000; + u64 fb_base = 0x0f00000000; + u64 fb_top = fb_base + vram_gb * vram_size - 1; + + WR32(mmio_base + MC_VM_FB_LOCATION, 0); + WR32(mmio_base + HDP_NONSURFACE_BASE, 0); + + WR32(mmio_base + MC_VM_FB_LOCATION, + ((fb_top >> 24) << 16) | (fb_base >> 24)); + WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); + WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); + WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); +} + +#define IA32_MTRR_DEF_TYPE 0x2ff +#define MTRR_BASE(i) (0x200 + 2*i) +#define MTRR_MASK(i) (0x201 + 2*i) + +static void setup_mtrr(void) +{ + disable_interrupts(); + u64 cr0 = cr0_read(); + cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); + wbinvd(); + cr3_write(cr3_read()); // TLB flush + + wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled + + // Low memory (0GB-2GB) = WB + wrmsr(MTRR_BASE(0), 0x0000000006); + wrmsr(MTRR_MASK(0), 0xff80000800); + // High memory (4GB-8GB) = WB + wrmsr(MTRR_BASE(1), 0x0100000006); + wrmsr(MTRR_MASK(1), 0xff00000800); + // High memory (8GB-10GB) = WB + wrmsr(MTRR_BASE(2), 0x0200000006); + wrmsr(MTRR_MASK(2), 0xff80000800); + // VRAM (4GB-4GB+vram_size) = UC + wrmsr(MTRR_BASE(3), 0x0100000000); + wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); + + wbinvd(); + cr3_write(cr3_read()); // TLB flush + wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable + cr0_write(cr0); + enable_interrupts(); +} + +static void cleanup_interrupts(void) +{ + int i; + disable_interrupts(); + + // Reset APIC stuff (per-CPU) + *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; + for (i = 0x320; i < 0x380; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + for (i = 0x480; i < 0x500; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; + for (i = 0x500; i < 0x540; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; + + // Fix the LVT offset for thresholding + wrmsr(0x413, (1L<<24) | (1L<<52)); + wrmsr(0xc0000408, (1L<<24) | (1L<<52)); +} + +static void cpu_quiesce_gate(void *arg) +{ + int i; + + // Ensure we can write anywhere + cr0_write(cr0_read() & ~CR0_WP); + + // Interrupt stuff local to each CPU + cleanup_interrupts(); + + // We want to set up MTRRs on all CPUs + setup_mtrr(); + + if (curcpu() != 0) { + // We're not on BSP. Try to halt. + __sync_fetch_and_add(&halted_cpus, 1); + cpu_stop(); + } + + uart_write_str("kexec: Waiting for secondary CPUs...\n"); + + // wait for all cpus to halt + while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); + + uart_write_str("kexec: Secondary CPUs quiesced\n"); + + //* Put ident mappings in current page tables + // Should not be needed, but maybe helps for debugging? + cr4_pge_disable(); + u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); + u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + // Clear (really) low mem. + // Linux reads from here to try and access EBDA... + // get_bios_ebda reads u16 from 0x40e + // reserve_ebda_region reads u16 from 0x413 + // Writing zeros causes linux to default to marking + // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. + // It doesn't match the ps4 e820 map, but that seems OK. + memset((void *)0, 0, 0x1000); + + // Create a new page table hierarchy out of the way of linux + // Accessed via freebsd direct map + pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt + // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| + memset(pml4_base, 0, 512 * sizeof(u64) * 2); + pdp_base = pml4_base + 512; + u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; + pml4_base[0] = pdpe; + // Maintain the freebsd direct map + pml4_base[DM_PML4_BASE] = pdpe; + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + uart_write_str("kexec: Setting up GDT...\n"); + + struct desc_ptr gdt_ptr; + struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); + gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; + gdt_ptr.address = DM_TO_ID(desc); + + // clear + memset(desc, 0, gdt_ptr.limit + 1); + // Most things are ignored in 64bit mode, and we will never be in + // 32bit/compat modes, so just setup another pure-64bit environment... + // Linux inits it's own GDT in secondary_startup_64 + // 0x10 + desc[2].limit0 = 0xffff; + desc[2].base0 = 0x0000; + desc[2].base1 = 0x0000; + desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; + desc[2].s = 1; + desc[2].dpl = 0; + desc[2].p = 1; + desc[2].limit = 0xf; + desc[2].avl = 0; + desc[2].l = 1; + desc[2].d = 0; + desc[2].g = 0; + desc[2].base2 = 0x00; + // 0x18 + desc[3].limit0 = 0xffff; + desc[3].base0 = 0x0000; + desc[3].base1 = 0x0000; + desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; + desc[3].s = 1; + desc[3].dpl = 0; + desc[3].p = 1; + desc[3].limit = 0xf; + desc[3].avl = 0; + desc[3].l = 0; + desc[3].d = 0; + desc[3].g = 0; + desc[3].base2 = 0x00; + // Task segment value + // 0x20 + desc[4].limit0 = 0x0000; + desc[4].base0 = 0x0000; + desc[4].base1 = 0x0000; + desc[4].type = SEG_TYPE_TSS; + desc[4].s = 1; + desc[4].dpl = 0; + desc[4].p = 1; + desc[4].limit = 0x0; + desc[4].avl = 0; + desc[4].l = 0; + desc[4].d = 0; + desc[4].g = 0; + desc[4].base2 = 0x00; + + uart_write_str("kexec: Relocating stub...\n"); + + // Relocate the stub and jump to it + // TODO should thunk_copy be DMAP here? + void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); + memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); + // XXX The +0x200 is for the iret stack in linux_thunk.S + uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; + + uart_write_str("kexec: Setting up boot params...\n"); + + // XXX we write into this bootargs and pass it to the kernel, but in + // jmp_to_linux we use the bootargs from the image as input. So they + // MUST MATCH! + struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; + *bp_lo = *nix_info.bp; + lowmem_pos += sizeof(struct boot_params); + + struct setup_header *shdr = &bp_lo->hdr; + shdr->cmd_line_ptr = lowmem_pos; + shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; + shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; + bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; + bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; + shdr->hardware_subarch = X86_SUBARCH_PS4; + // This needs to be nonzero for the initramfs to work + shdr->type_of_loader = 0xd0; // kexec + + strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, + nix_info.bp->hdr.cmdline_size); + lowmem_pos += strlen(nix_info.cmd_line) + 1; + + uart_write_str("kexec: Cleaning up hardware...\n"); + + // Disable IOMMU + *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; + + // Disable all MSIs on Aeolia + for (i = 0; i < 8; i++) + *(volatile u32 *)PA_TO_DM(0xd03c844c + i*4) = 0; + + // Stop HPET timers + *(volatile u64 *)PA_TO_DM(0xd0382010) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382100) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382120) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382140) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382160) = 0; + + uart_write_str("kexec: Reconfiguring VRAM...\n"); + + configure_vram(); + + uart_write_str("kexec: Resetting GPU...\n"); + + // Softreset GPU + *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks + *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC + *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 +// *(volatile u64 *)PA_TO_DM(0xe480d248) = 1; // Halt SDMA1 eeply + *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 + *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC + + *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply + +// *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x10003; // Softreset GFX/CP/RLC + *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x30005; // Softreset GFX/CP/RLC eeply + +// udelay(150); +// *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x10003; + *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x30005; //eeply +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) |= 0x00100140; // Softreset SDMA/GRBM +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~0x00100140; +// udelay(150); + + // Enable audio output + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; + *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; + +// // Set pin caps of pin 2 to vendor defined, to hide it +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// // Set pin caps of pin 3 to !HDMI +// *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; +// *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; + // Set pin configuration default + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; + + uart_write_str("kexec: About to relocate and jump to kernel\n"); + + ((jmp_to_linux_t)thunk_copy)( + DM_TO_ID(nix_info.linux_image), + DM_TO_ID(bp_lo), + DM_TO_ID(pml4_base), + (uintptr_t)&gdt_ptr + ); + + // should never reach here + uart_write_str("kexec: unreachable (?)\n"); +} + +// Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) +int hook_icc_query_nowait(u8 *icc_msg) +{ + kern.printf("hook_icc_query_nowait called\n"); + + // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot + //In alternative we can re-enable it here, but sometimes that give problems.. + kern.wlanbt(0x2); + + fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); + + kern.printf("ACPI tables fixed\n"); + + // Transition to BSP and halt other cpus + // smp_no_rendevous_barrier is just nullsub, but it is treated specially by + // smp_rendezvous. This is the easiest way to do this, since we can't assume + // we're already running on BSP. Since smp_rendezvous normally waits on all + // cpus to finish the callbacks, we just never return... + kern.smp_rendezvous(kern.smp_no_rendevous_barrier, + cpu_quiesce_gate, + kern.smp_no_rendevous_barrier, NULL); + + // should never reach here + kern.printf("hook_icc_query_nowait: unreachable (?)\n"); + return 0; +} diff --git a/linux/ps4-kexec-1300/linux_boot.h b/linux/ps4-kexec-1300/linux_boot.h new file mode 100644 index 0000000..2232b35 --- /dev/null +++ b/linux/ps4-kexec-1300/linux_boot.h @@ -0,0 +1,88 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef LINUX_BOOT_H +#define LINUX_BOOT_H + +#include "types.h" + +#define SMAP_TYPE_MEMORY 1 +#define SMAP_TYPE_RESERVED 2 +#define SMAP_TYPE_ACPI_RECLAIM 3 +#define SMAP_TYPE_ACPI_NVS 4 +#define SMAP_TYPE_UNUSABLE 5 +#define SMAP_TYPE_PMEM 7 + +#define X86_SUBARCH_PS4 5 + +struct e820entry { + u64 addr; /* start of memory segment */ + u64 size; /* size of memory segment */ + u32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct setup_header { + u8 setup_sects; + u16 root_flags; + u32 syssize; + u16 ram_size; + u16 vid_mode; + u16 root_dev; + u16 boot_flag; + u16 jump; + u32 header; + u16 version; + u32 realmode_swtch; + u16 start_sys; + u16 kernel_version; + u8 type_of_loader; + u8 loadflags; + u16 setup_move_size; + u32 code32_start; + u32 ramdisk_image; + u32 ramdisk_size; + u32 bootsect_kludge; + u16 heap_end_ptr; + u8 ext_loader_ver; + u8 ext_loader_type; + u32 cmd_line_ptr; + u32 initrd_addr_max; + u32 kernel_alignment; + u8 relocatable_kernel; + u8 min_alignment; + u16 xloadflags; + u32 cmdline_size; + u32 hardware_subarch; + u64 hardware_subarch_data; + u32 payload_offset; + u32 payload_length; + u64 setup_data; + u64 pref_address; + u32 init_size; + u32 handover_offset; +} __attribute__((packed)); + +#define E820MAX 128 /* number of entries in E820MAP */ + +OSTRUCT(boot_params, 0x1000) +OFIELD(0x0c0, u32 ext_ramdisk_image); +OFIELD(0x0c4, u32 ext_ramdisk_size); +OFIELD(0x0c8, u32 ext_cmd_line_ptr); +OFIELD(0x1e8, u8 e820_entries); +OFIELD(0x1f1, struct setup_header hdr); +OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); +OSTRUCT_END + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v); +void prepare_boot_params(struct boot_params *bp, u8 *linux_image); +int hook_icc_query_nowait(u8 *icc_msg); + +#endif diff --git a/linux/ps4-kexec-1300/linux_thunk.S b/linux/ps4-kexec-1300/linux_thunk.S new file mode 100644 index 0000000..f6d2dfb --- /dev/null +++ b/linux/ps4-kexec-1300/linux_thunk.S @@ -0,0 +1,90 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +.intel_syntax noprefix + +.equ setup_sects, 0x1f1 +.equ shdr_syssize, 0x1f4 +.equ pref_address, 0x258 + +.text + +#void jmp_to_linux( +# uintptr_t image_base, rdi +# uintptr_t bootargs, rsi +# uintptr_t new_cr3, rdx +# uintptr_t gdt_ptr rcx +#); +.globl jmp_to_linux +jmp_to_linux: + # switch to new gdt + data segments + cli + lgdt [rcx] + #xor eax, eax + mov eax, 0x18 + mov ds, eax + mov ss, eax + mov es, eax + mov fs, eax + mov gs, eax + + # switch to our own page tables (in low mem) + mov cr3, rdx + + # now we're on our own page tables, so we can obliterate the rest of memory + # TODO make sure we don't inadvertently overwrite (important) smap regions + # I think on ps4 we'll actually want to load to 0x700000 + # since we have tons of free room there. + # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We + # should choose a higher address as pref_address. + + # save args + mov r12, rdi + mov r13, rsi + + # memmove(pref_address, , (syssize * 0x10) / 8) + #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go + #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) + mov rdi, 0x6000000 # should be far from bzImage and initramfs + mov r14, rdi # r14 = pref_address + xor edx, edx + mov dl, [r12 + setup_sects] + inc rdx + shl rdx, 9 # rdx = offsetof(image_base, startup_32) + lea rsi, [r12 + rdx] # src = image_base + startup_32 + mov ecx, [r12 + shdr_syssize] + shl rcx, 4 + add rdi, rcx + add rsi, rcx + sub rdi, 8 + sub rsi, 8 + shr rcx, 3 + std + rep movsq + cld + + # make a tiny stack - we just need it for the lretq. + # what we jump to will not use this stack + lea rsp, [rip + jmp_to_linux_end + 0x200] + and rsp, -0x10 + #push 0 # retaddr + push 0x10 # cs = GDT[2] + add r14, 0x200 # pref_address + startup_64 + push r14 # rip + mov rsi, r13 # bootargs + lretq +jmp_to_linux_end: + +.data + +.globl jmp_to_linux_size +jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux + +.att_syntax prefix diff --git a/linux/ps4-kexec-1300/reboot.h b/linux/ps4-kexec-1300/reboot.h new file mode 100644 index 0000000..20b91f8 --- /dev/null +++ b/linux/ps4-kexec-1300/reboot.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)reboot.h 8.3 (Berkeley) 12/13/94 + * $FreeBSD$ + */ + +#ifndef _SYS_REBOOT_H_ +#define _SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. These are passed to + * the boot program and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ +#define RB_SINGLE 0x002 /* reboot to single user only */ +#define RB_NOSYNC 0x004 /* dont sync before reboot */ +#define RB_HALT 0x008 /* don't reboot, just halt */ +#define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ +#define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ +#define RB_KDB 0x040 /* give control to kernel debugger */ +#define RB_RDONLY 0x080 /* mount root fs read-only */ +#define RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define RB_MINIROOT 0x200 /* Unused placeholder */ +#define RB_VERBOSE 0x800 /* print all potentially useful info */ +#define RB_SERIAL 0x1000 /* use serial port as console */ +#define RB_CDROM 0x2000 /* use cdrom as root */ +#define RB_POWEROFF 0x4000 /* turn the power off if possible */ +#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ +#define RB_MUTE 0x10000 /* start up with the console muted */ +#define RB_SELFTEST 0x20000 /* unused placeholder */ +#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ +#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ +#define RB_PAUSE 0x100000 /* pause after each output line during probe */ +#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ +#define RB_PROBE 0x10000000 /* Probe multiple consoles */ +#define RB_MULTIPLE 0x20000000 /* use multiple consoles */ + +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + +#endif diff --git a/linux/ps4-kexec-1300/string.h b/linux/ps4-kexec-1300/string.h new file mode 100644 index 0000000..70aa668 --- /dev/null +++ b/linux/ps4-kexec-1300/string.h @@ -0,0 +1,96 @@ +/* + * string.h -- standard C string-manipulation functions. + * + * Copyright (C) 2008 Segher Boessenkool + * Copyright (C) 2009 Haxx Enterprises + * Copyright (C) 2010-2016 Hector Martin "marcan" + * + * Portions taken from the Public Domain C Library (PDCLib). + * http://pdclib.rootdirectory.de/ + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +static inline int strcmp(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s1[i] == s2[i]; i++) + ; + + return s1[i] - s2[i]; +} + +static inline void *memset(void *b, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)b)[i] = c; + + return b; +} + +static inline void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} + +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) + return p1[i] - p2[i]; + + return 0; +} + +static inline size_t strlen(const char *s) +{ + size_t len; + + for (len = 0; s[len]; len++) + ; + + return len; +} + +static inline size_t strnlen(const char *s, size_t count) +{ + size_t len; + + for (len = 0; s[len] && len < count; len++) + ; + + return len; +} + +static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) +{ + size_t len, needed; + + len = needed = strnlen(src, maxlen - 1) + 1; + if (len >= maxlen) + len = maxlen - 1; + + memcpy(dest, src, len); + dest[len] = 0; + + return needed - 1; +} + +#endif diff --git a/linux/ps4-kexec-1300/types.h b/linux/ps4-kexec-1300/types.h new file mode 100644 index 0000000..28d6619 --- /dev/null +++ b/linux/ps4-kexec-1300/types.h @@ -0,0 +1,51 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef TYPES_H +#define TYPES_H + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#ifndef TESTING +typedef u8 uint8_t; +typedef u64 size_t; +typedef s64 ssize_t; +typedef u64 uintptr_t; +typedef s64 off_t; +#endif + +#define NULL ((void *)0) + +#define CAT_(x, y) x ## y +#define CAT(x, y) CAT_(x, y) + +#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] +#define OSTRUCT(name, size) struct name { union { OPAD(size); +#define OSTRUCT_END };}; +#define OFIELD(off, field) struct { OPAD(off); field; } + +#define ASSERT_STRSIZE(struc, size) \ + _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#define ENOENT 2 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define ENAMETOOLONG 63 + +#endif diff --git a/linux/ps4-kexec-1300/uart.c b/linux/ps4-kexec-1300/uart.c new file mode 100644 index 0000000..62ea949 --- /dev/null +++ b/linux/ps4-kexec-1300/uart.c @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "uart.h" +#include "kernel.h" +#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) + +#define AEOLIA_UART_BASE 0xD0340000 +#define BAIKAL_UART_BASE 0xC890E000 + +#define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, AEOLIA_UART_BASE + (intf << 12) + (reg << 2)) + +#define UART_REG_DATA 0 +#define UART_REG_IER 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +# define LSR_TXRDY 0x20 +# define LSR_TEMT 0x40 +#define UART_REG_MSR 6 +#define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) +#define UART0_IER UART_REG(32, 0, UART_REG_IER) +#define UART0_IIR UART_REG(32, 0, UART_REG_IIR) +#define UART0_LCR UART_REG(32, 0, UART_REG_LCR) +#define UART0_MCR UART_REG(32, 0, UART_REG_MCR) +#define UART0_LSR UART_REG(32, 0, UART_REG_LSR) +#define UART0_MSR UART_REG(32, 0, UART_REG_MSR) + +void uart_write_byte(u8 b) +{ + int limit; + u64 barrier; + limit = 250000; + while (!(*UART0_LSR & LSR_TXRDY) && --limit) + ; + *UART0_DATA = b; + __sync_fetch_and_add(&barrier, 0); + limit = 250000; + while (!(*UART0_LSR & LSR_TEMT) && --limit) + ; +} + +void uart_write_char(char c) +{ + if (c == '\n') + uart_write_byte('\r'); + + uart_write_byte(c); +} + +void uart_write_str(const char *s) +{ + while (*s) { + uart_write_char(*s++); + } +} diff --git a/linux/ps4-kexec-1300/uart.h b/linux/ps4-kexec-1300/uart.h new file mode 100644 index 0000000..b803e29 --- /dev/null +++ b/linux/ps4-kexec-1300/uart.h @@ -0,0 +1,20 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef UART_H +#define UART_H + +#include "types.h" + +void uart_write_byte(u8 b); +void uart_write_char(char c); +void uart_write_str(const char *s); + +#endif diff --git a/linux/ps4-kexec-1300/x86.h b/linux/ps4-kexec-1300/x86.h new file mode 100644 index 0000000..61b22ff --- /dev/null +++ b/linux/ps4-kexec-1300/x86.h @@ -0,0 +1,149 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef X86_H +#define X86_H + +#define FLAGS_IF (1 << 9) + +#define CR0_WP (1 << 16) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) + +#define PG_V (1 << 0) +#define PG_RW (1 << 1) +#define PG_U (1 << 2) +#define PG_PS (1 << 7) + +#define SEG_TYPE_DATA (0 << 3) +#define SEG_TYPE_READ_WRITE (1 << 1) +#define SEG_TYPE_CODE (1 << 3) +#define SEG_TYPE_EXEC_READ (1 << 1) +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) + +static inline u64 cr0_read(void) +{ + u64 reg; + asm volatile("mov %0, cr0;" : "=r" (reg)); + return reg; +} + +static inline void cr0_write(u64 val) +{ + asm volatile("mov cr0, %0;" :: "r" (val)); +} + +static inline u64 write_protect_disable() +{ + u64 cr0 = cr0_read(); + cr0_write(cr0 & ~CR0_WP); + return cr0; +} + +static inline void write_protect_restore(u64 cr0) +{ + // Use only WP bit of input + cr0_write(cr0_read() | (cr0 & CR0_WP)); +} + +static inline u64 cr3_read(void) +{ + u64 reg; + asm volatile("mov %0, cr3;" : "=r" (reg)); + return reg; +} + +static inline void cr3_write(u64 val) +{ + asm volatile("mov cr3, %0;" :: "r" (val)); +} + +static inline void cr4_pge_disable(void) +{ + u64 cr4_temp; + asm volatile( + "mov %0, cr4;" + "and %0, ~0x80;" + "mov cr4, %0;" + : "=r" (cr4_temp) + ); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +static inline void cpu_stop(void) +{ + for (;;) + asm volatile("cli; hlt;" : : : "memory"); +} + +static inline void outl(int port, unsigned int data) +{ + asm volatile("out %w1, %0" : : "a" (data), "d" (port)); +} + +static inline void wrmsr(u32 msr_id, u64 msr_value) +{ + asm volatile( + "wrmsr" + : + : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) + ); +} + +static inline u64 rdtsc (void) +{ + unsigned int tickl, tickh; + asm volatile( + "rdtsc" + :"=a"(tickl),"=d"(tickh) + ); + return ((u64)tickh << 32) | tickl; +} + +static inline void udelay(unsigned int usec) { + u64 later = rdtsc() + usec * 1594ULL; + while (((s64)(later - rdtsc())) > 0); +} + +static inline void disable_interrupts(void) +{ + asm volatile("cli"); +} + +static inline void enable_interrupts(void) +{ + asm volatile("sti"); +} + +static inline u64 read_flags(void) +{ + u64 flags; + asm volatile("pushf; pop %0;" : "=r" (flags)); + return flags; +} + +static inline u64 intr_disable(void) +{ + u64 flags = read_flags(); + disable_interrupts(); + return flags; +} + +static inline void intr_restore(u64 flags) +{ + // TODO should only IF be or'd in? + asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); +} + +#endif diff --git a/linux/ps4-kexec-1302-baikal/LICENSE b/linux/ps4-kexec-1302-baikal/LICENSE new file mode 100644 index 0000000..6c1cbba --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2015-2016 shuffle2 +Copyright (C) 2015-2016 Hector Martin "marcan" +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/ps4-kexec-1302-baikal/Makefile b/linux/ps4-kexec-1302-baikal/Makefile new file mode 100644 index 0000000..41fe2d6 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/Makefile @@ -0,0 +1,37 @@ +TOOLCHAIN_PREFIX ?= +CC = $(TOOLCHAIN_PREFIX)gcc +AR = $(TOOLCHAIN_PREFIX)ar +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy + +CFLAGS=$(CFLAG) -DPS4_13_02 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX +CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ + -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ + -fno-asynchronous-unwind-tables \ + -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ + -mcmodel=small -mno-red-zone + +SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ + acpi.c crc32.c + +OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) +DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld + +all: libkexec.a kexec.bin + +%.o: %.c *.h + $(CC) -c $(CFLAGS) -o $@ $< + +%.o: %.S + $(CC) -c $(CFLAGS) -o $@ $< + +libkexec.a: $(OBJS) + $(AR) -rc $@ $(OBJS) + +kexec.elf: libkexec.a kexec.ld + $(CC) $(CFLAGS) -o $@ libkexec.a + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +clean: + rm -f libkexec.a kexec.elf kexec.bin $(OBJS) diff --git a/linux/ps4-kexec-1302-baikal/README.md b/linux/ps4-kexec-1302-baikal/README.md new file mode 100644 index 0000000..6500878 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/README.md @@ -0,0 +1,121 @@ +# PS4 kexec implementation + +This repo implements a kexec()-style system call for the PS4 Orbis kernel +(FreeBSD derivative). This is designed to boot a Linux kernel directly from +FreeBSD. + +This is not an exploit. It is useless without some mechanism of injecting code +into the PS4 OS kernel. + +## Building + +To build a kexec.bin relocatable binary using the supplied Makefile, just type +`make`. This will also build a kexec.a archive. You can either use the binary +directly, or link the archive into your own project. + +If you link kexec.a with your own code, you need to supply the two symbols +`_start` and `_end` in your linker script, as `kernel_init()` will try to remap +all pages covered by that range as RWX (to make global variable accesses work). +Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this +feature, if you have already taken care of page permissions for the code. + +If you use a compiler toolchain that have a special prefix you can declare it +by passing TOOLCHAIN_PREFIX option to the Makefile like this: + + make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' + +## Usage + +The code is designed to be completely standalone. There is a single entry point: + + int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); + +Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel +symbols and install the sys_kexec system call. The syscall is registered +as number 153 by default (you can change this in kexec.h). The return value +is 0 on success, or negative on error. + +You may pass something other than NULL as `early_printf`. In that case, that +function will be used for debug output during early symbol resolution, before +printf is available. + +Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is +enabled by default, symtab also disappears in newer kernel, we have to +hardcode offsets for some symbols. Currently we use the `early_printf` +given by user to caculate the base address of kernel, then relocate all the +symbols from the kernel base. You could enable this feature like this: + + make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' + +If you do not want to call the syscall from userspace, you can pass the address +of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the +address of `sys_kexec`, so you can invoke it manually (see kexec.h for +its prototype and how the arguments are passed). + +If you are using the standalone kexec.bin blob, then the `kexec_init` function +is always located at offset 0, so simply call the base address of the blob. +Don't forget to pass two NULL arguments (or the appropriate pointers). + +The injected `sys_kexec` system call takes (userspace) pointers to the kernel +and initramfs blobs, their sizes, and a pointer to the (null-terminated) command +line string. From userspace, this looks like this: + + int kexec(void *kernel_image, size_t image_size, + void *initramfs, size_t initramfs_size, + const char *cmdline); + + // syscall() usage: + syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); + +`kexec()` will load the kernel and initramfs into memory, but will not directly +boot them. To boot the loaded kernel, shut down the system. This can be +accomplished by pressing the power button, but can also be done more quickly +and reliably from userspace with the following sequence of system calls (this +kills userspace quickly but still does a controlled filesystem unmount): + + int evf = syscall(540, "SceSysCoreReboot"); + syscall(546, evf, 0x4000, 0); + syscall(541, evf); + // should be syscall(37, 1, 30) but only tested via kill symbol + kill(1, 30); + +Note that this software should be loaded into kernel memory space. If you are +running kernel code from userland mappings, you should either switch to kernel +mappings or separately copy kexec.bin to a location in kernel address space. +While syscalls or exploit code may run properly from userland, the shutdown hook +will not, as it will be called from a different process context. + +## Features + +`kernel_init()` will automatically find the Orbis OS kernel and resolve all +necessary symbols to work. There are no static symbol dependencies. If +`DO_NOT_REMAP_RWX` is not defined (the default), it will also patch +`pmap_protect` to disable the W^X restriction. + +In addition to loading the user-supplied initramfs, `kexec` will locate the +Radeon firmware blobs inside Orbis OS, extract them, convert them to a format +suitable for Linux, and append them as an additional initramfs cpio image to +the existing initramfs. This avoids the need to distribute the Radeon firmware +blobs. The `radeon` module, when compiled into the kernel, will automatically +load this firmware on boot. Note however that most typical initramfs scripts +will wipe the initramfs contents while pivoting to the real system, so if you +compile `radeon` as a module you may not be able to access the firmware after +boot. To cover that case, add some code to your initramfs `/init` script to +copy the firmware to a tmpfs mounted on the real filesystem: + + # assuming real root FS is mounted on /mnt + + mkdir -p /mnt/lib/firmware/radeon + mount -t tmpfs none /mnt/lib/firmware/radeon + cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ + + # now switch_root to /mnt + +This avoids having to permanently store copies of the Radeon firmware, which +isn't really necessary for most use cases. + +There is significant debug logging available, which will appear on the system +UART. Most of the code relies on the kernel `printf` implementation, and +therefore you should patch out the UART output blanker to see it. The final +code that runs on the boot CPU before booting the kernel uses direct UART +writes and is not affected by the blanking feature of Orbis OS. diff --git a/linux/ps4-kexec-1302-baikal/acpi.c b/linux/ps4-kexec-1302-baikal/acpi.c new file mode 100644 index 0000000..bfccf08 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/acpi.c @@ -0,0 +1,320 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "types.h" +#include "kernel.h" +#include "acpi.h" +#include "acpi_caps.h" + +#ifdef TESTING +# include +# include +# include +# include +# include +# include +#else +# include "kernel.h" +# include "string.h" +# define printf kern.printf +#endif + +#define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) +#define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) + +#define PACKED __attribute__((packed)) + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +struct RSDP { + u64 sig; + u8 checksum; + u8 oemid[6]; + u8 rev; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 rsvd[3]; +} PACKED; + +struct SDTH { + u32 sig; + u32 length; + u8 rev; + u8 checksum; + u8 oem_id[6]; + u8 oem_tid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; +} PACKED; + +struct RSDT { + struct SDTH hdr; + u32 table_addr[]; +} PACKED; + +struct XSDT { + struct SDTH hdr; + u64 table_addr[]; +} PACKED; + +struct FADT { + struct SDTH hdr; + u32 facs; + u32 dsdt; + // more stuff... +} PACKED; + +struct ivhd_entry4 { + u8 type; + u16 devid; + u8 flags; +} PACKED; + +struct ivhd_header { + u8 type; + u8 flags; + u16 length; + u16 devid; + u16 cap_ptr; + u64 mmio_phys; + u16 pci_seg; + u16 info; + u32 efr_attr; +} PACKED; + +struct IVRS { + struct SDTH hdr; + u32 IVinfo; + u8 reserved[8]; + struct ivhd_header hd_hdr; + struct ivhd_entry4 hd_entries[3]; +} PACKED; + +struct MMIO { + u64 baseAddressECM; + u16 pciSegmentGroup; + u8 startPCIBus; + u8 endPCIBus; + u32 reserved; +} PACKED; + +// We have enough space to use the second half of the 64KB table area +// as scratch space for building the tables +#define BUFFER_OFF 0x8000 + +#define P2M(p) (((u64)(p)) - phys_base + map_base) +#define M2P(p) ((((void*)(p)) - map_base) + phys_base) +#define B2P(p) ((((void*)(p)) - buf_base) + phys_base) +#define P2B(p) ((((void*)(p)) - phys_base) + buf_base) + +#define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) +#define PADB(s) p += (s) +#define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) +#define ALLOC(t) (t)ALLOCB(sizeof(t)) +#define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) +#define COPYT(s) COPYB(((struct SDTH*)s)->length, s) +#define COPYTP(s) COPYT(P2M(s)) +#define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) +#define COPYP(t, s) COPY(t, P2M(s)) + +static void rsdp_checksum(struct RSDP *rsdp) { + rsdp->checksum = rsdp->ext_checksum = 0; + + u8 sum = 0; + for (int i = 0; i < 20; i++) + sum += ((u8*)rsdp)[i]; + rsdp->checksum = -sum; + sum = 0; + for (int i = 0; i < sizeof(*rsdp); i++) + sum += ((u8*)rsdp)[i]; + rsdp->ext_checksum = -sum; +} + +static void table_checksum(void *table) { + struct SDTH *hdr = table; + hdr->checksum = 0; + u8 sum = 0; + for (int i = 0; i < hdr->length; i++) + sum += ((u8*)table)[i]; + hdr->checksum = -sum; +} + +#define IVHD_FLAG_ISOC_EN_MASK 0x08 +#define IVHD_DEV_ALL 0x01 +#define IVHD_DEV_SELECT 0x02 +#define IVHD_DEV_SELECT_RANGE_START 0x03 +#define IVHD_DEV_RANGE_END 0x04 + +#define ACPI_DEVFLAG_SYSMGT1 0x10 +#define ACPI_DEVFLAG_SYSMGT2 0x20 + +static void *build_ivrs(struct IVRS *ivrs) { + memset(ivrs, 0, sizeof(*ivrs)); + + ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); + ivrs->hdr.length = sizeof(*ivrs); + ivrs->hdr.rev = 1; + memcpy(ivrs->hdr.oem_id, "F0F ", 6); + memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); + ivrs->hdr.oem_rev = 0x20161225; + memcpy(ivrs->hdr.creator_id, "KEXC", 4); + ivrs->hdr.creator_rev = 0x20161225; + ivrs->IVinfo = 0x00203040; //48882_IOMMU.pdf page 251 + + struct ivhd_header *hdr = &ivrs->hd_hdr; //48882_IOMMU.pdf page 254 + hdr->type = 0x10; + hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; + hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); + hdr->devid = PCI_DEVFN(0, 2); + hdr->cap_ptr = 0x40; // from config space + 0x34 + hdr->mmio_phys = 0xfc000000; //Base address of IOMMU control registers in MMIO space + hdr->pci_seg = 0; + hdr->info = 0; // msi msg num? (the pci cap should be written by software) + // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 + hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); //Feature Reporting Field, 48882_IOMMU.pdf page 255 + + struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; + // on fbsd, all aeolia devfns have active entries except memories (func 6) + // not sure if this is just because it wasn't in use when i dumped it? + // all entries are r/w + // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) + // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) + // Modes: + // 4 level: + // apcie + // 3 level: + // all others + + // the way to encode this info into the IVHD entries is fairly arbitrary... + entries[0].type = IVHD_DEV_SELECT; //DTE setting applies to the device specifed in DevID field. + entries[0].devid = PCI_DEVFN(20, 0); //vendorId: 104D, deviceId: 90D7; Sony Baikal ACPI + entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; + + entries[1].type = IVHD_DEV_SELECT_RANGE_START; + entries[1].devid = PCI_DEVFN(20, 1); + entries[1].flags = 0; //Identifies a device able to assert INIT interrupts (page 262) + entries[2].type = IVHD_DEV_RANGE_END; + entries[2].devid = PCI_DEVFN(20, 7); + entries[2].flags = 0; //Identifies a device able to assert INIT interrupts + + table_checksum(ivrs); + return ivrs + 1; +} + +void fix_acpi_tables(void *map_base, u64 phys_base) +{ + void *buf_base = map_base + 0x8000; + void *p = buf_base; + memset(buf_base, 0, 0x8000); + + printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); + + struct RSDP *rsdp = COPYP(struct RSDP, phys_base); + printf("RSDT at 0x%x\n", rsdp->rsdt_addr); + printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); + + struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); + struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); + rsdp->rsdt_addr = B2P(rsdt); + + PADB(0x30); // this gives us space for new tables + + struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); + rsdp->xsdt_addr = B2P(xsdt); + + PADB(0x60); + + struct FADT *fadt = NULL; + + int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; + int i; + for (i = 0; i < cnt; i++) { + struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); + printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); + switch (hdr->sig) { + case SIG32('F', 'A', 'C', 'P'): + { + fadt = (void*)hdr; + printf("FACS at 0x%x\n", fadt->facs); + printf("DSDT at 0x%x\n", fadt->dsdt); + // Sony puts the FACS before the FADT, unaligned, which is + // noncompliant, but let's keep it there + u8 *facs = COPYB(64, P2M(fadt->facs)); + fadt = (void*)(hdr = COPYT(hdr)); + fadt->facs = B2P(facs); + PADB(0x38); + break; + } + case SIG32('S', 'S', 'D', 'T'): + { + // Put the DSDT before the SSDT + if (fadt) { + PADB(0xf0); + u8 *dsdt = COPYTP(fadt->dsdt); + fadt->dsdt = B2P(dsdt); + PADB(0x174); + table_checksum(fadt); + } else { + printf("ERROR: no FADT yet?\n"); + } + hdr = COPYT(hdr); + break; + } + default: + hdr = COPYT(hdr); + } + table_checksum(hdr); + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); + } + + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); + i++; + p = build_ivrs(p); + + rsdt->hdr.length = sizeof(*rsdt) + 4 * i; + xsdt->hdr.length = sizeof(*xsdt) + 8 * i; + + rsdp_checksum(rsdp); + table_checksum(rsdt); + table_checksum(xsdt); + memcpy(map_base, buf_base, p - buf_base); +} + +u32 msi_mask(unsigned x) { + /* Don't shift by >= width of type */ + if (x >= 5) + return 0xffffffff; + return (1 << (1 << x)) - 1; +} +void disableMSI(u64 MSICapabilityRegAddr) { + PPCI_MSI_CAPABILITY pMSICapability = (PPCI_MSI_CAPABILITY)PA_TO_DM(MSICapabilityRegAddr); + if (pMSICapability->msiEnable == 1) + pMSICapability->msiEnable = 0; + pMSICapability->mask64 = msi_mask(pMSICapability->multipleMessageCapable); +} + +#ifdef TESTING + +int main(int argc, char **argv) +{ + int fd; + void *base; + + fd = open(argv[1], O_RDWR); + base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + fix_acpi_tables(base, 0xe0000); + return 0; +} + +#endif diff --git a/linux/ps4-kexec-1302-baikal/acpi.h b/linux/ps4-kexec-1302-baikal/acpi.h new file mode 100644 index 0000000..45f2834 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/acpi.h @@ -0,0 +1,22 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ACPI_H +#define ACPI_H + +#define PACKED __attribute__((packed)) + +void fix_acpi_tables(void *map_base, u64 phys_base); + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) + +void disableMSI(u64 MSICapabilityRegAddr); + +#endif diff --git a/linux/ps4-kexec-1302-baikal/acpi_caps.h b/linux/ps4-kexec-1302-baikal/acpi_caps.h new file mode 100644 index 0000000..070e9f6 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/acpi_caps.h @@ -0,0 +1,30 @@ +#ifndef ACPI_CAPS_H +#define ACPI_CAPS_H + +#define UCHAR u8 +#define USHORT u16 +#define ULONG u32 +#define PACKED __attribute__((packed)) +typedef struct PACKED _PCI_CAPABILITIES_HEADER { + UCHAR CapabilityID; + UCHAR Next; +} PCI_CAPABILITIES_HEADER, *PPCI_CAPABILITIES_HEADER; + +typedef struct PACKED _PCI_MSI_CAPABILITY { + PCI_CAPABILITIES_HEADER Header; + u16 msiEnable : 1, multipleMessageCapable : 3, multipleMessageEnable : 3, address64Capable : 1, reserved0 : 8; + u32 lowerAddress : 30, reserved1 : 2; + union { + struct { + u32 upperAddress; + u32 messageData64 : 16, reservedData64 : 16; + u32 mask64; + }; + struct { + u32 messageData32 : 16, reservedData32 : 16; + u32 mask32; + }; + }; +} PCI_MSI_CAPABILITY, *PPCI_MSI_CAPABILITY; + +#endif diff --git a/linux/ps4-kexec-1302-baikal/crc32.c b/linux/ps4-kexec-1302-baikal/crc32.c new file mode 100644 index 0000000..7836c18 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/crc32.c @@ -0,0 +1,102 @@ +/*- +* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or +* code or tables extracted from it, as desired without restriction. +* +* First, the polynomial itself and its table of feedback terms. The +* polynomial is +* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 +* +* Note that we take it "backwards" and put the highest-order term in +* the lowest-order bit. The X^32 term is "implied"; the LSB is the +* X^31 term, etc. The X^0 term (usually shown as "+1") results in +* the MSB being 1 +* +* Note that the usual hardware shift register implementation, which +* is what we're using (we're merely optimizing it by doing eight-bit +* chunks at a time) shifts bits into the lowest-order term. In our +* implementation, that means shifting towards the right. Why do we +* do it this way? Because the calculated CRC must be transmitted in +* order from highest-order term to lowest-order term. UARTs transmit +* characters in order from LSB to MSB. By storing the CRC this way +* we hand it to the UART in the order low-byte to high-byte; the UART +* sends each low-bit to hight-bit; and the result is transmission bit +* by bit from highest- to lowest-order term without requiring any bit +* shuffling on our part. Reception works similarly +* +* The feedback terms table consists of 256, 32-bit entries. Notes +* +* The table can be generated at runtime if desired; code to do so +* is shown later. It might not be obvious, but the feedback +* terms simply represent the results of eight shift/xor opera +* tions for all combinations of data and CRC register values +* +* The values must be right-shifted by eight bits by the "updcrc +* logic; the shift must be unsigned (bring in zeroes). On some +* hardware you could probably optimize the shift in assembler by +* using byte-swap instructions +* polynomial $edb88320 +* +* +* CRC32 code derived from work by Gary S. Brown. +*/ + +#include "crc32.h" + +static u32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 crc32(u32 crc, const void *buf, size_t size) +{ + const u8 *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} diff --git a/linux/ps4-kexec-1302-baikal/crc32.h b/linux/ps4-kexec-1302-baikal/crc32.h new file mode 100644 index 0000000..777278d --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/crc32.h @@ -0,0 +1,7 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +u32 crc32(u32 crc, const void *buf, size_t size); + +#endif diff --git a/linux/ps4-kexec-1302-baikal/elf.h b/linux/ps4-kexec-1302-baikal/elf.h new file mode 100644 index 0000000..63371c7 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/elf.h @@ -0,0 +1,71 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + u8 e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + u64 e_entry; + u64 e_phoff; + u64 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf64_Ehdr; + +typedef struct { + u32 p_type; + u32 p_flags; + u64 p_offset; + void *p_vaddr; + u64 p_paddr; + u64 p_filesz; + u64 p_memsz; + u64 p_align; +} Elf64_Phdr; + +#define PT_DYNAMIC 2 +#define PT_PHDR 6 + +#define DT_NULL 0 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_STRSZ 10 + +typedef struct { + s64 d_tag; + union { + u64 d_val; + void *d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + void *st_value; + u64 st_size; +} Elf64_Sym; + +#endif diff --git a/linux/ps4-kexec-1302-baikal/firmware.c b/linux/ps4-kexec-1302-baikal/firmware.c new file mode 100644 index 0000000..a831b1c --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/firmware.c @@ -0,0 +1,479 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "firmware.h" +#include "types.h" +#include "kernel.h" +#include "string.h" +#include "types.h" +#include "crc32.h" + +#define DIR 0040755 +#define FILE 0100644 + +struct firmware_header { + u32 size_bytes; + u32 header_size_bytes; + u16 header_version_major; + u16 header_version_minor; + u16 ip_version_major; + u16 ip_version_minor; + u32 ucode_version; + u32 ucode_size_bytes; + u32 ucode_array_offset_bytes; + u32 crc32; + union { + struct { + u32 ucode_feature_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } gfx1; + struct { + u32 ucode_feature_version; + u32 save_and_restore_offset; + u32 clear_state_descriptor_offset; + u32 avail_scratch_ram_locations; + u32 master_pkt_description_offset; + u8 end[]; + } rlc1; + struct { + u32 ucode_feature_version; + u32 ucode_change_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } sdma1; + u8 raw[0xe0]; + }; +}; + +static inline char hex(u8 c) +{ + if (c <= 9) + return '0' + c; + return 'a' + c - 10; +} + +static void hex8(u8 **p, u32 val) +{ + *(*p)++ = hex(val >> 28); + *(*p)++ = hex((val >> 24) & 0xf); + *(*p)++ = hex((val >> 20) & 0xf); + *(*p)++ = hex((val >> 16) & 0xf); + *(*p)++ = hex((val >> 12) & 0xf); + *(*p)++ = hex((val >> 8) & 0xf); + *(*p)++ = hex((val >> 4) & 0xf); + *(*p)++ = hex(val & 0xf); +} + +void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) +{ + size_t name_len = strlen(name); + + // Pad to 4 byte multiple + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; + + memcpy(*p, "070701", 6); + *p += 6; + hex8(p, 0); // c_ino + hex8(p, mode); // c_mode + hex8(p, 0); // c_uid + hex8(p, 0); // c_gid + hex8(p, 1); // c_nlink + hex8(p, 0); // c_mtime + hex8(p, size); // c_filesize + hex8(p, 0); // c_maj + hex8(p, 0); // c_min + hex8(p, 0); // c_rmaj + hex8(p, 0); // c_rmin + hex8(p, name_len + 1); // c_namesize + hex8(p, 0); // c_chksum + memcpy(*p, name, name_len); + *p += name_len; + *(*p)++ = 0; + + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; +} + +struct fw_header_t { + u64 size_words; + char *unk_ident; + u64 unk; + void *blob; + u64 unk2; +}; + +struct fw_info_t { + struct fw_header_t *rlc; + struct fw_header_t *sdma0; + struct fw_header_t *sdma1; + struct fw_header_t *ce; + struct fw_header_t *pfp; + struct fw_header_t *me; + struct fw_header_t *mec1; + struct fw_header_t *mec2; +}; + +struct fw_expected_sizes_t { + u64 rlc; + u64 sdma0; + u64 sdma1; + u64 ce; + u64 pfp; + u64 me; + u64 mec1; + u64 mec2; +}; +static const struct fw_expected_sizes_t liverpool_fw_sizes = { + LVP_FW_RLC_SIZE, + LVP_FW_SDMA_SIZE, + LVP_FW_SDMA1_SIZE, + LVP_FW_CE_SIZE, + LVP_FW_PFP_SIZE, + LVP_FW_ME_SIZE, + LVP_FW_MEC_SIZE, + LVP_FW_MEC2_SIZE +}; +static const struct fw_expected_sizes_t gladius_fw_sizes = { + GL_FW_RLC_SIZE, + GL_FW_SDMA_SIZE, + GL_FW_SDMA1_SIZE, + GL_FW_CE_SIZE, + GL_FW_PFP_SIZE, + GL_FW_ME_SIZE, + GL_FW_MEC_SIZE, + GL_FW_MEC2_SIZE +}; + +void copy_edid(u8 **p, int sz) +{ + int i; + u8 *edid = *p; + u8 *off_edid = kern.edid; + + memset(edid, 0, sz); + *p += sz; + + for(i = 0; i < sz; i++) + *(edid + i) = *(off_edid + i); + + *p += sz; +} + +void copy_eap_hdd_key(u8 **p) +{ + int i; + u8 *eap_key = *p; + u8 *off_eap_key = kern.eap_hdd_key; + + memset(eap_key, 0, 0x20); + *p += 0x20; + + for(i = 0; i < 0x20; i++) + { + if(i < 0x10) + *(eap_key + i) = *(off_eap_key + 0xF - i); + else + *(eap_key + i) = *(off_eap_key + 0x2F - i); + } + *p += 0x20; +} + +int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + kern.printf("Copying %s firmware\n", name); + if (expected_size != (hdr->size_words * 4)) { + kern.printf("copy_firmware: %s: expected size %d, got %d\n", + name, expected_size, hdr->size_words * 4); + return 0; + } + + struct firmware_header *fhdr = (struct firmware_header*)*p; + memset(fhdr, 0, sizeof(*fhdr)); + *p += sizeof(*fhdr); + + memcpy(*p, hdr->blob, expected_size); + + fhdr->size_bytes = expected_size + sizeof(*fhdr); + fhdr->header_size_bytes = offsetof(struct firmware_header, raw); + fhdr->header_version_major = 1; + fhdr->header_version_minor = 0; + fhdr->ucode_version = 0x10; + fhdr->ucode_size_bytes = expected_size; + fhdr->ucode_array_offset_bytes = sizeof(*fhdr); + + *p += expected_size; + + return 1; +} + +int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); + fhdr->gfx1.ucode_feature_version = 21; + fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); + fhdr->rlc1.ucode_feature_version = 1; + fhdr->rlc1.save_and_restore_offset = 0x90; + fhdr->rlc1.clear_state_descriptor_offset = 0x3d; + fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? + fhdr->rlc1.master_pkt_description_offset = 0; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 2; + fhdr->ip_version_minor = 1; + fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); + fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; + fhdr->sdma1.ucode_change_version = 0; + fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +static const u32 pfp_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc4200016, // ldw r8, [r0, #0x16] + 0xdc030000, // mov ctr, r0 + 0xcc000049, // stw r0, [r0, #0x49] + 0xcc200013, // stw r0, [r8, #0x13] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 ce_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc420000c, // ldw r8, [r0, #0xc] + 0xdc030000, // mov ctr, r0 + 0xcc00002f, // stw r0, [r0, #0x2f] + 0xcc200012, // stw r0, [r8, #0x12] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 mec_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc43c000c, // ldw r15, [r0, #0x9] + 0xdc030000, // mov ctr, r0 + 0xcc00002b, // stw r0, [r0, #0x2b] + 0xcc3c000d, // stw r0, [r15, #0xd] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +#define PACKET_TYPE_NOP 0x10 + +static void patch_fw(void *p, const u32 *handler, int handler_size) { + int size = ((struct firmware_header*)p)->ucode_size_bytes; + int code_size = (size & ~0xfff) / 4; + int nop_start = code_size - 0x10; + + u32 *fw = p + sizeof(struct firmware_header); + kern.printf("NOP handler at 0x%x\n", nop_start); + memcpy(&fw[nop_start], handler, handler_size); + + // patch the branch table entry + for (int off = code_size; off < size/4; off++) { + if ((fw[off] >> 16) == PACKET_TYPE_NOP) { + fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; + } + } +} + +struct fw_info_t *get_fw_info() { + if (kern.gc_get_fw_info) { + return kern.gc_get_fw_info(); + } else if (kern.Starsha_UcodeInfo) { + return kern.Starsha_UcodeInfo; + } else { + return NULL; + } +} + +const struct fw_expected_sizes_t *get_fw_expected_sizes() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return &gladius_fw_sizes; + } else { + return &liverpool_fw_sizes; + } +} + +const char * get_gpu_name() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return "gladius"; + } else { + return "liverpool"; + } +} + +ssize_t firmware_extract(void *dest) +{ + u8 *p = dest; + + // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. + struct fw_info_t *info = get_fw_info(); + if (!info) { + kern.printf("firmware_extract: Could not locate firmware table"); + return -1; + } + const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); + + //Eap hdd key + cpio_hdr(&p, "key", DIR, 0); + cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); + copy_eap_hdd_key(&p); + + cpio_hdr(&p, "lib", DIR, 0); + cpio_hdr(&p, "lib/firmware", DIR, 0); + + /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ + int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); + cpio_hdr(&p, "lib/firmware/edid", DIR, 0); + cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); + copy_edid(&p, edid_sz); + + char dir[7]; + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) + kern.snprintf(dir, sizeof(dir), "amdgpu"); + else + kern.snprintf(dir, sizeof(dir), "amdgpu"); + + char dir_path[64]; + kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); + cpio_hdr(&p, dir_path, DIR, 0); + + char pfp_path[64]; + kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", pfp_path); + cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); + u8 *pfp = p; + if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) + return -1; + patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); + + char me_path[64]; + kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", me_path); + cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); + if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) + return -1; + + char ce_path[64]; + kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", ce_path); + cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); + u8 *ce = p; + if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) + return -1; + patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); + + char mec_path[64]; + kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec_path); + cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); + u8 *mec1 = p; + if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) + return -1; + patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); + + char mec2_path[64]; + kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec2_path); + cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); + u8 *mec2 = p; + if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) + return -1; + patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); + + char rlc_path[64]; + kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", rlc_path); + cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); + if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) + return -1; + + char sdma_path[64]; + kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma_path); + cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); + if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + char sdma1_path[64]; + kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma1_path); + cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); + if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + size_t size = p - (u8*)dest; + if (size > FW_CPIO_SIZE) { + kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); + return -1; + } + + return size; +} diff --git a/linux/ps4-kexec-1302-baikal/firmware.h b/linux/ps4-kexec-1302-baikal/firmware.h new file mode 100644 index 0000000..4835983 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/firmware.h @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include "types.h" + +//sizes eap_hdd_key +#define EAP_HDD_KEY_SIZE 0x20 +#define EDID_SIZE 256 + +// sizes for liverpool +#define LVP_FW_CE_SIZE 8576 +#define LVP_FW_ME_SIZE 16768 +#define LVP_FW_MEC_SIZE 16768 +#define LVP_FW_MEC2_SIZE 16768 +#define LVP_FW_PFP_SIZE 16768 +#define LVP_FW_RLC_SIZE 6144 +#define LVP_FW_SDMA_SIZE 4200 +#define LVP_FW_SDMA1_SIZE 4200 +// sizes for gladius +#define GL_FW_CE_SIZE 8576 +#define GL_FW_ME_SIZE 16768 +#define GL_FW_MEC_SIZE 16768 +#define GL_FW_MEC2_SIZE 16768 +#define GL_FW_PFP_SIZE 16768 +#define GL_FW_RLC_SIZE 8192 +#define GL_FW_SDMA_SIZE 4200 +#define GL_FW_SDMA1_SIZE 4200 + +#define MAX(x ,y) (((x) > (y)) ? (x) : (y)) +#define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) + +#define FW_CE_SIZE MAX_FW_SIZE(CE) +#define FW_ME_SIZE MAX_FW_SIZE(ME) +#define FW_MEC_SIZE MAX_FW_SIZE(MEC) +#define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) +#define FW_PFP_SIZE MAX_FW_SIZE(PFP) +#define FW_RLC_SIZE MAX_FW_SIZE(RLC) +#define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) +#define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) + +// Conservative value (max 113 bytes plus name size plus alignment) +#define CPIO_HEADER_SIZE 256 + +#define FW_HEADER_SIZE 256 + +// Leave space for 16 files (currently 12) +#define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ + FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ + FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ + FW_HEADER_SIZE * 8) + +ssize_t firmware_extract(void *dest); + +#endif diff --git a/linux/ps4-kexec-1302-baikal/kernel.c b/linux/ps4-kexec-1302-baikal/kernel.c new file mode 100644 index 0000000..b6d31b4 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/kernel.c @@ -0,0 +1,383 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "string.h" +#include "elf.h" +#include "x86.h" +#include "../magic.h" + +struct ksym_t kern; +int (*early_printf)(const char *fmt, ...) = NULL; + +#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) + +#ifdef NO_SYMTAB + +#define RESOLVE_NOERR(name) do { \ + if (kern_off_ ## name == 0) { \ + kern.name = 0; \ + } else { \ + kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ + } \ +} while (0); + +#define RESOLVE(name) do { \ + if (kern_off_ ## name == 0) { \ + return 0; \ + } \ + RESOLVE_NOERR(name) \ +} while (0); + +#else + +#define KERNSIZE 0x2000000 + +static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; +static Elf64_Sym *symtab; +static char *strtab; +static size_t strtab_size; + +static Elf64_Ehdr *find_kern_ehdr(void) +{ + // Search for the kernel copy embedded in ubios, then follow it to see + // where it was relocated to + for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; + if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { + for (size_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; + if (phdr->p_type == PT_PHDR) { + return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); + } + } + } + } + return NULL; +} + +static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf64_Dyn *)phdr->p_vaddr; + } + } + return NULL; +} + +static int elf_parse_dyn(Elf64_Dyn *dyn) +{ + for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_SYMTAB: + symtab = (Elf64_Sym *)dp->d_un.d_ptr; + break; + case DT_STRTAB: + strtab = (char *)dp->d_un.d_ptr; + break; + case DT_STRSZ: + strtab_size = dp->d_un.d_val; + break; + } + } + return symtab && strtab && strtab_size; +} + +void *kernel_resolve(const char *name) +{ + for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { + if (!strcmp(name, &strtab[sym->st_name])) { + eprintf("kern.%s = %p\n", name, (void*)sym->st_value); + return (void *)sym->st_value; + } + } + eprintf("Failed to resolve symbol '%s'\n", name); + return NULL; +} + +#define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) +#define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; + +#endif + +static int resolve_symbols(void) +{ + RESOLVE(printf); + RESOLVE(snprintf); + early_printf = kern.printf; + RESOLVE(copyin); + RESOLVE(copyout); + RESOLVE(copyinstr); + RESOLVE(kernel_map); + RESOLVE(kernel_pmap_store); + RESOLVE(kmem_alloc_contig); + RESOLVE(kmem_free); + RESOLVE(pmap_extract); + RESOLVE(pmap_protect); + RESOLVE(sysent); + RESOLVE(sched_pin); + RESOLVE(sched_unpin); + RESOLVE(smp_rendezvous); + RESOLVE(smp_no_rendevous_barrier); + RESOLVE(icc_query_nowait); + RESOLVE_NOERR(Starsha_UcodeInfo); + RESOLVE_NOERR(gpu_devid_is_9924); + RESOLVE_NOERR(gc_get_fw_info); + RESOLVE_NOERR(eap_hdd_key); + RESOLVE_NOERR(edid); + RESOLVE(wlanbt); + RESOLVE(kern_reboot); + RESOLVE(set_gpu_freq); + RESOLVE(set_pstate); + RESOLVE(update_vddnp); + RESOLVE(set_cu_power_gate); + RESOLVE_NOERR(pstate_before_shutdown); + return 1; +} + +#define M_WAITOK 0x0002 +#define M_ZERO 0x0100 + +#define VM_MEMATTR_DEFAULT 0x06 + +void *kernel_alloc_contig(size_t size) +{ + // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... + vm_offset_t ret = 0; + while(!(ret = kern.kmem_alloc_contig( + *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, + ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); + + /*if (!ret) { + kern.printf("Failed to allocate %zud bytes\n", size); + return NULL; + }*/ + return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); +} + +void kernel_free_contig(void *addr, size_t size) +{ + if (!addr) + return; + kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); +} + +int kernel_hook_install(void *target, void *hook) +{ + uintptr_t t = (uintptr_t)target; // addr to redirect to + uintptr_t h = (uintptr_t)hook; // place to write the thunk + + if (!hook || !target) { + return 0; + } + + kern.printf("kernel_hook_install(%p, %p)\n", target, hook); + + if (!(t & (1L << 63))) { + kern.printf("\n===================== WARNING =====================\n"); + kern.printf("hook target function address: %p\n", target); + kern.printf("It looks like we're running from userland memory.\n"); + kern.printf("Please run this code from a kernel memory mapping.\n\n"); + return 0; + } + s64 displacement = t - (h + 5); + + kern.sched_pin(); + u64 wp = write_protect_disable(); + if (displacement < -0x80000000 || displacement > 0x7fffffff) { + kern.printf(" Using 64bit absolute jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[2]; + s32 zero; + void *target; + } jmp = { + .op = { 0xff, 0x25 }, + .zero = 0, + .target = target, + }; + ASSERT_STRSIZE(struct jmp_t, 14); + memcpy(hook, &jmp, sizeof(jmp)); + } else { + kern.printf(" Using 32bit relative jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[1]; + s32 imm; + } jmp = { + .op = { 0xe9 }, + .imm = displacement, + }; + ASSERT_STRSIZE(struct jmp_t, 5); + memcpy(hook, &jmp, sizeof(jmp)); + } + wbinvd(); + write_protect_restore(wp); + kern.sched_unpin(); + + return 1; +} + +void kernel_syscall_install(int num, void *call, int narg) +{ + struct sysent_t *sy = &kern.sysent[num]; + + kern.sched_pin(); + u64 wp = write_protect_disable(); + + memset(sy, 0, sizeof(*sy)); + sy->sy_narg = narg; + sy->sy_call = call; + sy->sy_thrcnt = 1; + + write_protect_restore(wp); + kern.sched_unpin(); +} + +void kernel_remap(void *start, void *end, int perm) +{ + u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); + u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); + + kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); + kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); +} + +static volatile int _global_test = 0; + +#ifndef DO_NOT_REMAP_RWX +extern u8 _start[], _end[]; + +static int patch_pmap_check(void) +{ + u8 *p; + + for (p = (u8*)kern.pmap_protect; + p < ((u8*)kern.pmap_protect + 0x500); p++) { + #ifdef PS4_6_72 + if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 + p[5] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + /*#ifdef PS4_5_05 + if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { + p[1] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + }*/ + #else + if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { + p[2] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + #endif + } + kern.printf("pmap_protect patch failed!\n"); + return 0; +} +#endif + +int kernel_init(void *_early_printf) +{ + int rv = -1; + + if (_early_printf) + early_printf = _early_printf; + + eprintf("kernel_init()\n"); + +#ifdef KASLR + // use `early_printf` to calculate kernel base + if (early_printf == NULL) + return 0; + + kern.kern_base = (u64)(early_printf - kern_off_printf); + if ((kern.kern_base & PAGE_MASK) != 0) { + eprintf("Kernel base is not aligned\n"); + return 0; + } else { + eprintf("Kernel base = %llx\n", kern.kern_base); + } + + u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); + u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); + +#else + kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 + + u64 DMPML4I = 0x1fc; + u64 DMPDPI = 0; +#endif + + kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); + eprintf("Direct map base = %llx\n", kern.dmap_base); + + // We may not be mapped writable yet, so to be able to write to globals + // we need WP disabled. + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + +#ifndef NO_SYMTAB + Elf64_Ehdr *ehdr = find_kern_ehdr(); + if (!ehdr) { + eprintf("Could not find kernel ELF header\n"); + goto err; + } + eprintf("ELF header at %p\n", ehdr); + + Elf64_Dyn *dyn = elf_get_dyn(ehdr); + if (!dyn) { + eprintf("Could not find kernel dynamic header\n"); + goto err; + } + eprintf("ELF dynamic section at %p\n", dyn); + + if (!elf_parse_dyn(dyn)) { + eprintf("Failed to parse ELF dynamic section\n"); + goto err; + } +#endif + + if (!resolve_symbols()) { + eprintf("Failed to resolve all symbols\n"); + goto err; + } + + // Pin ourselves as soon as possible. This is expected to be released by the caller. + kern.sched_pin(); + +#ifndef DO_NOT_REMAP_RWX + if (!patch_pmap_check()) + goto err; +#endif + +#ifndef DO_NOT_REMAP_RWX + // kernel_remap may need interrupts, but may not write to globals! + enable_interrupts(); + kernel_remap(_start, _end, 7); + disable_interrupts(); +#endif + + // Writing to globals is now safe. + + kern.printf("Testing global variable access (write protection)...\n"); + _global_test = 1; + kern.printf("OK.\n"); + + kern.printf("Kernel interface initialized\n"); + rv = 0; + +err: + write_protect_restore(wp); + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1302-baikal/kernel.h b/linux/ps4-kexec-1302-baikal/kernel.h new file mode 100644 index 0000000..1833f5b --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/kernel.h @@ -0,0 +1,126 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KERNEL_H +#define KERNEL_H + +#include "types.h" +#include "reboot.h" + +#define PAGE_SIZE 0x4000 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define PML4SHIFT 39 +#define PDPSHIFT 30 +#define PDRSHIFT 21 +#define PAGE_SHIFT 12 + +#define KVADDR(l4, l3, l2, l1) ( \ + ((unsigned long)-1 << 47) | \ + ((unsigned long)(l4) << PML4SHIFT) | \ + ((unsigned long)(l3) << PDPSHIFT) | \ + ((unsigned long)(l2) << PDRSHIFT) | \ + ((unsigned long)(l1) << PAGE_SHIFT)) + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) +#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX + +typedef u64 vm_paddr_t; +typedef u64 vm_offset_t; +typedef u64 vm_size_t; +typedef void * vm_map_t; +typedef char vm_memattr_t; +typedef void * pmap_t; + +typedef void (*smp_rendezvous_callback_t)(void *); + +struct sysent_t { + int sy_narg; + void *sy_call; + u16 sy_auevent; + void *sy_systrace_args_func; + int sy_entry; + int sy_return; + int sy_flags; + int sy_thrcnt; +}; + +struct ksym_t { + // two parameters related to kaslr (they are not symbols) + uintptr_t kern_base; + uintptr_t dmap_base; + + int (*printf)(const char *fmt, ...); + int (*snprintf)(const char *fmt, ...); + int (*copyin)(const void *uaddr, void *kaddr, size_t len); + int (*copyout)(const void *kaddr, void *uaddr, size_t len); + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); + + void **kernel_map; + void *kernel_pmap_store; + vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, + vm_paddr_t low, vm_paddr_t high, + unsigned long alignment, + unsigned long boundary, + vm_memattr_t memattr); + void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); + vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); + void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); + + struct sysent_t *sysent; + + void (*sched_pin)(void); + void (*sched_unpin)(void); + void (*smp_rendezvous)(smp_rendezvous_callback_t, + smp_rendezvous_callback_t, + smp_rendezvous_callback_t, void *); + // yes...it is misspelled :) + void (*smp_no_rendevous_barrier)(void *); + void *icc_query_nowait; + void *Starsha_UcodeInfo; + int (*gpu_devid_is_9924)(); + void *(*gc_get_fw_info)(); + void *eap_hdd_key; + void *edid; + void (*wlanbt)(unsigned int state); + int (*kern_reboot)(int magic); + void(*set_gpu_freq)(unsigned int num, unsigned int freq); + void(*set_pstate)(unsigned int val); + void(*update_vddnp)(unsigned int val); + void(*set_cu_power_gate)(unsigned int val); + void *pstate_before_shutdown; +}; + +extern struct ksym_t kern; + +static inline int curcpu(void) +{ + int cpuid; + // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw + asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); + return cpuid; +} + +// Assign a working printf function to this to debug the symbol resolver +extern int (*early_printf)(const char *fmt, ...); + +void *kernel_resolve(const char *name); + +void *kernel_alloc_contig(size_t size); +void kernel_free_contig(void *addr, size_t size); + +void kernel_remap(void *start, void *end, int perm); + +void kernel_syscall_install(int num, void *call, int narg); +int kernel_hook_install(void *target, void *hook); + +int kernel_init(void *early_printf); + +#endif diff --git a/linux/ps4-kexec-1302-baikal/kexec.c b/linux/ps4-kexec-1302-baikal/kexec.c new file mode 100644 index 0000000..d659229 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/kexec.c @@ -0,0 +1,229 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "linux_boot.h" +#include "x86.h" +#include "kexec.h" +#include "firmware.h" +#include "string.h" +#include "acpi.h" + +static int k_copyin(const void *uaddr, void *kaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(kaddr, uaddr, len); + return 0; +} + +static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + const char *ustr = (const char*)uaddr; + char *kstr = (char*)kaddr; + size_t ret; + if (!uaddr || !kaddr) + return EFAULT; + ret = strlcpy(kstr, ustr, len); + if (ret >= len) { + if (done) + *done = len; + return ENAMETOOLONG; + } else { + if (done) + *done = ret + 1; + } + return 0; +} + +static int k_copyout(const void *kaddr, void *uaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(uaddr, kaddr, len); + return 0; +} + +int sys_kexec(void *td, struct sys_kexec_args *uap) +{ + int err = 0; + size_t initramfs_size = uap->initramfs_size; + void *image = NULL; + void *initramfs = NULL; + size_t firmware_size = 0; + struct boot_params *bp = NULL; + size_t cmd_line_maxlen = 0; + char *cmd_line = NULL; + + int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; + int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; + + kern.printf("sys_kexec invoked\n"); + kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, + uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); + + // Look up our shutdown hook point + void *icc_query_nowait = kern.icc_query_nowait; + if (!icc_query_nowait) { + err = ENOENT; + goto cleanup; + } + + // Set gpu frequencies and pstate + // FAT&SLIM / PRO + kern.set_pstate(3); + + kern.set_gpu_freq(0, 800); //800 //800 + kern.set_gpu_freq(1, 673); //673 //853 + kern.set_gpu_freq(2, 609); //610 //711 + kern.set_gpu_freq(3, 800); //800 //800 + kern.set_gpu_freq(4, 800); //800 //911 + kern.set_gpu_freq(5, 711); //711 //800 + kern.set_gpu_freq(6, 711); //711 //984 + kern.set_gpu_freq(7, 673); //673 //673 + + kern.update_vddnp(0x12); + kern.set_cu_power_gate(0x12); + + // Copy in kernel image + image = kernel_alloc_contig(uap->image_size); + if (!image) { + kern.printf("Failed to allocate image\n"); + err = ENOMEM; + goto cleanup; + } + err = copyin(uap->image, image, uap->image_size); + if (err) { + kern.printf("Failed to copy in image\n"); + goto cleanup; + } + + // Copy in initramfs + initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); + if (!initramfs) { + kern.printf("Failed to allocate initramfs\n"); + err = ENOMEM; + goto cleanup; + } + + err = firmware_extract(((u8*)initramfs)); + if (err < 0) { + kern.printf("Failed to extract GPU firmware - continuing anyway\n"); + } else { + firmware_size = err; + } + + if (initramfs_size) { + err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); + if (err) { + kern.printf("Failed to copy in initramfs\n"); + goto cleanup; + } + } + initramfs_size += firmware_size; + + // Copy in cmdline + cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; + cmd_line = kernel_alloc_contig(cmd_line_maxlen); + if (!cmd_line) { + kern.printf("Failed to allocate cmdline\n"); + err = ENOMEM; + goto cleanup; + } + err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); + if (err) { + kern.printf("Failed to copy in cmdline\n"); + goto cleanup; + } + cmd_line[cmd_line_maxlen - 1] = 0; + + kern.printf("\nkexec parameters:\n"); + kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); + kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", + initramfs_size, uap->initramfs_size); + kern.printf(" Kernel command line: %s\n", cmd_line); + kern.printf(" Kernel image buffer: %p\n", image); + kern.printf(" Initramfs buffer: %p\n", initramfs); + + // Allocate our boot params + bp = kernel_alloc_contig(sizeof(*bp)); + if (!bp) { + kern.printf("Failed to allocate bp\n"); + err = ENOMEM; + goto cleanup; + } + + // Initialize bp + // TODO should probably do this from cpu_quiesce_gate, then bp doesn't + // need to be allocated here, just placed directly into low mem + set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); + + prepare_boot_params(bp, image); + + // Hook the final ICC shutdown function + if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { + kern.printf("Failed to install shutdown hook\n"); + err = EINVAL; + goto cleanup; + } + + kern.printf("******************************************************\n"); + kern.printf("kexec successfully armed. Please shut down the system.\n"); + kern.printf("******************************************************\n\n"); + +/* + kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); + if (kern.kern_reboot(RB_POWEROFF) == -1) + kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); +*/ + return 0; + +cleanup: + kernel_free_contig(cmd_line, cmd_line_maxlen); + kernel_free_contig(bp, sizeof(*bp)); + kernel_free_contig(image, uap->image_size); + kernel_free_contig(initramfs, uap->initramfs_size); + return err; + + copyout(NULL, NULL, 0); +} + +int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) +{ + int rv = 0; + + // potentially needed to write early_printf + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + + if (kernel_init(_early_printf) < 0) { + rv = -1; + goto cleanup; + } + + kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); + kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); + kern.printf("kexec_init() successful\n\n"); + + if (sys_kexec_ptr) + *sys_kexec_ptr = sys_kexec; + +cleanup: + write_protect_restore(wp); + if (kern.sched_unpin && wp & CR0_WP) { + // If we're returning to a state with WP enabled, assume the caller + // wants the thread unpinned. Else the caller is expected to + // call kern.sched_unpin() manually. + kern.sched_unpin(); + } + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1302-baikal/kexec.h b/linux/ps4-kexec-1302-baikal/kexec.h new file mode 100644 index 0000000..6c31637 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/kexec.h @@ -0,0 +1,38 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KEXEC_H +#define KEXEC_H + +#include "types.h" + +#define SYS_KEXEC 153 +#define SYS_KEXEC_NARGS 6 + +struct sys_kexec_args { + void *image; + size_t image_size; + void *initramfs; + size_t initramfs_size; + char *cmd_line; + int vram_gb; +}; + +typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); + +// Note: td is unused, you can pass NULL if you call this directly. +int sys_kexec(void *td, struct sys_kexec_args *uap); + +int kernel_init(void *early_printf); + +int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) + __attribute__ ((section (".init"))); + +#endif diff --git a/linux/ps4-kexec-1302-baikal/kexec.ld b/linux/ps4-kexec-1302-baikal/kexec.ld new file mode 100644 index 0000000..1bcf26c --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/kexec.ld @@ -0,0 +1,25 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +ENTRY(kexec_init) + +SECTIONS { + /* We don't do GOT relocation and rely on nothing ending up using the GOT + * (-fno-common helps here) */ + /DISCARD/ : { *(.comment) *(.got) } + _start = .; + .init : { *(.init) *(.init.*) } + .text : { *(.text) *(.text.*) } + .data : { *(.data) *(.data.*) } + .rodata : { *(.rodata) *(.rodata.*) } + .bss : { *(.bss) *(.bss.*) *(COMMON)} + .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ + _end = .; +} diff --git a/linux/ps4-kexec-1302-baikal/linux_boot.c b/linux/ps4-kexec-1302-baikal/linux_boot.c new file mode 100644 index 0000000..226dd4f --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/linux_boot.c @@ -0,0 +1,453 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "linux_boot.h" +#include "types.h" +#include "string.h" +#include "x86.h" +#include "kernel.h" +#include "uart.h" +#include "acpi.h" + +void uart_write_byte(u8 b); + +static u64 vram_base = 0x100000000; +// Current code assumes it's a power of two. +static u64 vram_size = 1024 * 1024 * 1024; +static int vram_gb = 2; + +#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) + +#define MSR_GS_BASE 0xc0000101 /* 64bit GS base */ + +struct desc_struct { + u16 limit0; + u16 base0; + u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; + u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, + uintptr_t new_cr3, uintptr_t gdt_ptr); +extern uint8_t *jmp_to_linux; +extern size_t jmp_to_linux_size; + +// FreeBSD DMAP addresses +struct linux_boot_info { + void *linux_image; + void *initramfs; + size_t initramfs_size; + struct boot_params *bp; + char *cmd_line; +}; +static struct linux_boot_info nix_info; + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v) +{ + nix_info.linux_image = linux_image; + nix_info.bp = bp; + nix_info.initramfs = initramfs; + nix_info.initramfs_size = initramfs_size; + nix_info.cmd_line = cmd_line; + vram_gb = v; +} + +static volatile int halted_cpus = 0; + +static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, + u32 type) +{ + uint8_t idx = bp->e820_entries; + bp->e820_map[idx].addr = addr; + bp->e820_map[idx].size = size; + bp->e820_map[idx].type = type; + bp->e820_entries++; +} + +void prepare_boot_params(struct boot_params *bp, u8 *linux_image) +{ + memset(bp, 0, sizeof(struct boot_params)); + struct boot_params *bp_src = (struct boot_params *)linux_image; + memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + + ((u8 *)&bp_src->hdr.jump)[1]); + + // These values are from fw 1.01 + bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); + bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + // This used to be VRAM, but we reclaim it as RAM + bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); + // Instead, carve out VRAM from the beginning of high memory + bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, + SMAP_TYPE_MEMORY); +} + +#define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_FB_OFFSET 0x2068 +#define HDP_NONSURFACE_BASE 0x2c04 +#define CONFIG_MEMSIZE 0x5428 + +static void configure_vram(void) +{ + u64 mmio_base = 0xe4800000; + u64 fb_base = 0x0f00000000; + u64 fb_top = fb_base + vram_gb * vram_size - 1; + + WR32(mmio_base + MC_VM_FB_LOCATION, 0); + WR32(mmio_base + HDP_NONSURFACE_BASE, 0); + + WR32(mmio_base + MC_VM_FB_LOCATION, + ((fb_top >> 24) << 16) | (fb_base >> 24)); + WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); + WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); + WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); +} + +#define IA32_MTRR_DEF_TYPE 0x2ff +#define MTRR_BASE(i) (0x200 + 2*i) +#define MTRR_MASK(i) (0x201 + 2*i) + +static void setup_mtrr(void) +{ + disable_interrupts(); + u64 cr0 = cr0_read(); + cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); + wbinvd(); + cr3_write(cr3_read()); // TLB flush + + wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled + + // Low memory (0GB-2GB) = WB + wrmsr(MTRR_BASE(0), 0x0000000006); + wrmsr(MTRR_MASK(0), 0xff80000800); + // High memory (4GB-8GB) = WB + wrmsr(MTRR_BASE(1), 0x0100000006); + wrmsr(MTRR_MASK(1), 0xff00000800); + // High memory (8GB-10GB) = WB + wrmsr(MTRR_BASE(2), 0x0200000006); + wrmsr(MTRR_MASK(2), 0xff80000800); + // VRAM (4GB-4GB+vram_size) = UC + wrmsr(MTRR_BASE(3), 0x0100000000); + wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); + + wbinvd(); + cr3_write(cr3_read()); // TLB flush + wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable + cr0_write(cr0); + enable_interrupts(); +} + +static void cleanup_interrupts(void) +{ + int i; + disable_interrupts(); + + // Reset APIC stuff (per-CPU) + *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; + for (i = 0x320; i < 0x380; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + for (i = 0x480; i < 0x500; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; + for (i = 0x500; i < 0x540; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; + + // Fix the LVT offset for thresholding + wrmsr(0x413, (1L<<24) | (1L<<52)); + wrmsr(0xc0000408, (1L<<24) | (1L<<52)); +} + +#define DEFAULT_STACK 0 + +#define DPL0 0x0 +#define DPL3 0x3 + +#define BPCIE_BAR2 0xc8800000 +#define BPCIE_HPET_BASE 0x109000 +#define BPCIE_HPET_SIZE 0x400 + +static void cpu_quiesce_gate(void *arg) +{ + + + // Ensure we can write anywhere + cr0_write(cr0_read() & ~CR0_WP); + + // Interrupt stuff local to each CPU + cleanup_interrupts(); + + // We want to set up MTRRs on all CPUs + setup_mtrr(); + + if (curcpu() != 0) { + // We're not on BSP. Try to halt. + __sync_fetch_and_add(&halted_cpus, 1); + cpu_stop(); + } + + uart_write_str("kexec: Waiting for secondary CPUs...\n"); + + // wait for all cpus to halt + while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); + + uart_write_str("kexec: Secondary CPUs quiesced\n"); + + //* Put ident mappings in current page tables + // Should not be needed, but maybe helps for debugging? + cr4_pge_disable(); + u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); + u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + // Clear (really) low mem. + // Linux reads from here to try and access EBDA... + // get_bios_ebda reads u16 from 0x40e + // reserve_ebda_region reads u16 from 0x413 + // Writing zeros causes linux to default to marking + // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. + // It doesn't match the ps4 e820 map, but that seems OK. + memset((void *)0, 0, 0x1000); + + // Create a new page table hierarchy out of the way of linux + // Accessed via freebsd direct map + pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt + // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| + memset(pml4_base, 0, 512 * sizeof(u64) * 2); + pdp_base = pml4_base + 512; + u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; + pml4_base[0] = pdpe; + // Maintain the freebsd direct map + pml4_base[DM_PML4_BASE] = pdpe; + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + uart_write_str("kexec: Setting up GDT...\n"); + + desc_ptr gdt_ptr; + struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); + gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; + gdt_ptr.address = DM_TO_ID(desc); + + // clear + memset(desc, 0, gdt_ptr.limit + 1); + // Most things are ignored in 64bit mode, and we will never be in + // 32bit/compat modes, so just setup another pure-64bit environment... + // Linux inits it's own GDT in secondary_startup_64 + // 0x10 + desc[2].limit0 = 0xffff; + desc[2].base0 = 0x0000; + desc[2].base1 = 0x0000; + desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; + desc[2].s = 1; + desc[2].dpl = 0; + desc[2].p = 1; + desc[2].limit = 0xf; + desc[2].avl = 0; + desc[2].l = 1; + desc[2].d = 0; + desc[2].g = 0; + desc[2].base2 = 0x00; + // 0x18 + desc[3].limit0 = 0xffff; + desc[3].base0 = 0x0000; + desc[3].base1 = 0x0000; + desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; + desc[3].s = 1; + desc[3].dpl = 0; + desc[3].p = 1; + desc[3].limit = 0xf; + desc[3].avl = 0; + desc[3].l = 0; + desc[3].d = 0; + desc[3].g = 0; + desc[3].base2 = 0x00; + // Task segment value + // 0x20 + desc[4].limit0 = 0x0000; + desc[4].base0 = 0x0000; + desc[4].base1 = 0x0000; + desc[4].type = SEG_TYPE_TSS; + desc[4].s = 1; + desc[4].dpl = 0; + desc[4].p = 1; + desc[4].limit = 0x0; + desc[4].avl = 0; + desc[4].l = 0; + desc[4].d = 0; + desc[4].g = 0; + desc[4].base2 = 0x00; + + uart_write_str("kexec: Relocating stub...\n"); + + // Relocate the stub and jump to it + // TODO should thunk_copy be DMAP here? + void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); + memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); + // XXX The +0x200 is for the iret stack in linux_thunk.S + uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; + + uart_write_str("kexec: Setting up boot params...\n"); + + // XXX we write into this bootargs and pass it to the kernel, but in + // jmp_to_linux we use the bootargs from the image as input. So they + // MUST MATCH! + struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; + *bp_lo = *nix_info.bp; + lowmem_pos += sizeof(struct boot_params); + + struct setup_header *shdr = &bp_lo->hdr; + shdr->cmd_line_ptr = lowmem_pos; + shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; + shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; + bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; + bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; + shdr->hardware_subarch = X86_SUBARCH_PS4; + // This needs to be nonzero for the initramfs to work + shdr->type_of_loader = 0xd0; // kexec + + strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, + nix_info.bp->hdr.cmdline_size); + lowmem_pos += strlen(nix_info.cmd_line) + 1; + + uart_write_str("kexec: Cleaning up hardware...\n"); + + // Disable IOMMU + *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; + + // Disable all MSIs on Baikal (bus=0, slot=20) + disableMSI(0xf80a00e0); //func = 0 Baikal ACPI + disableMSI(0xf80a10e0); //func = 1 Baikal Ethernet Controller + disableMSI(0xf80a20e0); //func = 2 Baikal SATA AHCI Controller + disableMSI(0xf80a30e0); //func = 3 Baikal SD/MMC Host Controller + disableMSI(0xf80a40e0); //func = 4 Baikal PCI Express Glue and Miscellaneous Devices + disableMSI(0xf80a50e0); //func = 5 Baikal DMA Controller + disableMSI(0xf80a60e0); //func = 6 Baikal Baikal Memory (DDR3/SPM) + disableMSI(0xf80a70e0); //func = 7 Baikal Baikal USB 3.0 xHCI Host Controller + + // Stop HPET timers + //*(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + 0x10) &= ~(1UL << 0); //General Configuration Register + /* + u64 NUM_TIM_CAP = *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE) & 0x1F00; + for (u64 N = 0; N <= NUM_TIM_CAP; N++) { + *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + (0x20*N) + 0x100) &= ~(1UL << 2); //Timer N Configuration and Capabilities Register + } + */ + uart_write_str("kexec: Reconfiguring VRAM...\n"); + + configure_vram(); + + uart_write_str("kexec: Resetting GPU...\n"); + + // Softreset GPU + *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks + *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC + *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 +// *(volatile u64 *)PA_TO_DM(0xe480d248) = 1; // Halt SDMA1 eeply + *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 + *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC + + *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply + +// *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x10003; // Softreset GFX/CP/RLC + *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x30005; // Softreset GFX/CP/RLC eeply + +// udelay(150); +// *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x10003; + *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x30005; //eeply +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) |= 0x00100140; // Softreset SDMA/GRBM +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~0x00100140; +// udelay(150); + + // Enable audio output + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; + *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; + +// // Set pin caps of pin 2 to vendor defined, to hide it +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// // Set pin caps of pin 3 to !HDMI +// *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; +// *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; + // Set pin configuration default + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; + + uart_write_str("kexec: About to relocate and jump to kernel\n"); + + ((jmp_to_linux_t)thunk_copy)( + DM_TO_ID(nix_info.linux_image), + DM_TO_ID(bp_lo), + DM_TO_ID(pml4_base), + (uintptr_t)&gdt_ptr + ); + // should never reach here + uart_write_str("kexec: unreachable (?)\n"); +} + +// Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) +int hook_icc_query_nowait(u8 *icc_msg) +{ + kern.printf("hook_icc_query_nowait called\n"); + + // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot + //In alternative we can re-enable it here, but sometimes that give problems.. + kern.wlanbt(0x2); + + fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); + + kern.printf("ACPI tables fixed\n"); + + // Transition to BSP and halt other cpus + // smp_no_rendevous_barrier is just nullsub, but it is treated specially by + // smp_rendezvous. This is the easiest way to do this, since we can't assume + // we're already running on BSP. Since smp_rendezvous normally waits on all + // cpus to finish the callbacks, we just never return... + kern.smp_rendezvous(kern.smp_no_rendevous_barrier, + cpu_quiesce_gate, + kern.smp_no_rendevous_barrier, NULL); + + // should never reach here + kern.printf("hook_icc_query_nowait: unreachable (?)\n"); + return 0; +} diff --git a/linux/ps4-kexec-1302-baikal/linux_boot.h b/linux/ps4-kexec-1302-baikal/linux_boot.h new file mode 100644 index 0000000..2232b35 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/linux_boot.h @@ -0,0 +1,88 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef LINUX_BOOT_H +#define LINUX_BOOT_H + +#include "types.h" + +#define SMAP_TYPE_MEMORY 1 +#define SMAP_TYPE_RESERVED 2 +#define SMAP_TYPE_ACPI_RECLAIM 3 +#define SMAP_TYPE_ACPI_NVS 4 +#define SMAP_TYPE_UNUSABLE 5 +#define SMAP_TYPE_PMEM 7 + +#define X86_SUBARCH_PS4 5 + +struct e820entry { + u64 addr; /* start of memory segment */ + u64 size; /* size of memory segment */ + u32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct setup_header { + u8 setup_sects; + u16 root_flags; + u32 syssize; + u16 ram_size; + u16 vid_mode; + u16 root_dev; + u16 boot_flag; + u16 jump; + u32 header; + u16 version; + u32 realmode_swtch; + u16 start_sys; + u16 kernel_version; + u8 type_of_loader; + u8 loadflags; + u16 setup_move_size; + u32 code32_start; + u32 ramdisk_image; + u32 ramdisk_size; + u32 bootsect_kludge; + u16 heap_end_ptr; + u8 ext_loader_ver; + u8 ext_loader_type; + u32 cmd_line_ptr; + u32 initrd_addr_max; + u32 kernel_alignment; + u8 relocatable_kernel; + u8 min_alignment; + u16 xloadflags; + u32 cmdline_size; + u32 hardware_subarch; + u64 hardware_subarch_data; + u32 payload_offset; + u32 payload_length; + u64 setup_data; + u64 pref_address; + u32 init_size; + u32 handover_offset; +} __attribute__((packed)); + +#define E820MAX 128 /* number of entries in E820MAP */ + +OSTRUCT(boot_params, 0x1000) +OFIELD(0x0c0, u32 ext_ramdisk_image); +OFIELD(0x0c4, u32 ext_ramdisk_size); +OFIELD(0x0c8, u32 ext_cmd_line_ptr); +OFIELD(0x1e8, u8 e820_entries); +OFIELD(0x1f1, struct setup_header hdr); +OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); +OSTRUCT_END + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v); +void prepare_boot_params(struct boot_params *bp, u8 *linux_image); +int hook_icc_query_nowait(u8 *icc_msg); + +#endif diff --git a/linux/ps4-kexec-1302-baikal/linux_thunk.S b/linux/ps4-kexec-1302-baikal/linux_thunk.S new file mode 100644 index 0000000..551964e --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/linux_thunk.S @@ -0,0 +1,90 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +.intel_syntax noprefix + +.equ setup_sects, 0x1f1 +.equ shdr_syssize, 0x1f4 +.equ pref_address, 0x258 + +.text + +#void jmp_to_linux( +# uintptr_t image_base, rdi +# uintptr_t bootargs, rsi +# uintptr_t new_cr3, rdx +# uintptr_t gdt_ptr rcx +#); +.globl jmp_to_linux +jmp_to_linux: + # switch to new gdt + data segments + cli + lgdt [rcx] + #xor eax, eax + mov eax, 0x18 + mov ds, eax + mov ss, eax + mov es, eax + mov fs, eax + mov gs, eax + + # switch to our own page tables (in low mem) + mov cr3, rdx + + # now we're on our own page tables, so we can obliterate the rest of memory + # TODO make sure we don't inadvertently overwrite (important) smap regions + # I think on ps4 we'll actually want to load to 0x700000 + # since we have tons of free room there. + # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We + # should choose a higher address as pref_address. + + # save args + mov r12, rdi + mov r13, rsi + + # memmove(pref_address, , (syssize * 0x10) / 8) + #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go + #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) + mov rdi, 0x6000000 # should be far from bzImage and initramfs + mov r14, rdi # r14 = pref_address + xor edx, edx + mov dl, [r12 + setup_sects] + inc rdx + shl rdx, 9 # rdx = offsetof(image_base, startup_32) + lea rsi, [r12 + rdx] # src = image_base + startup_32 + mov ecx, [r12 + shdr_syssize] + shl rcx, 4 + add rdi, rcx + add rsi, rcx + sub rdi, 8 + sub rsi, 8 + shr rcx, 3 + std + rep movsq + cld + + # make a tiny stack - we just need it for the lretq. + # what we jump to will not use this stack + lea rsp, [rip + jmp_to_linux_end + 0x200] + and rsp, -0x10 + #push 0 # retaddr + push 0x10 # cs = GDT[2] + add r14, 0x200 # pref_address + startup_64 + push r14 # rip + mov rsi, r13 # bootargs + lretq +jmp_to_linux_end: + +.data + +.globl jmp_to_linux_size +jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux + +.att_syntax prefix \ No newline at end of file diff --git a/linux/ps4-kexec-1302-baikal/reboot.h b/linux/ps4-kexec-1302-baikal/reboot.h new file mode 100644 index 0000000..20b91f8 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/reboot.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)reboot.h 8.3 (Berkeley) 12/13/94 + * $FreeBSD$ + */ + +#ifndef _SYS_REBOOT_H_ +#define _SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. These are passed to + * the boot program and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ +#define RB_SINGLE 0x002 /* reboot to single user only */ +#define RB_NOSYNC 0x004 /* dont sync before reboot */ +#define RB_HALT 0x008 /* don't reboot, just halt */ +#define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ +#define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ +#define RB_KDB 0x040 /* give control to kernel debugger */ +#define RB_RDONLY 0x080 /* mount root fs read-only */ +#define RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define RB_MINIROOT 0x200 /* Unused placeholder */ +#define RB_VERBOSE 0x800 /* print all potentially useful info */ +#define RB_SERIAL 0x1000 /* use serial port as console */ +#define RB_CDROM 0x2000 /* use cdrom as root */ +#define RB_POWEROFF 0x4000 /* turn the power off if possible */ +#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ +#define RB_MUTE 0x10000 /* start up with the console muted */ +#define RB_SELFTEST 0x20000 /* unused placeholder */ +#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ +#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ +#define RB_PAUSE 0x100000 /* pause after each output line during probe */ +#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ +#define RB_PROBE 0x10000000 /* Probe multiple consoles */ +#define RB_MULTIPLE 0x20000000 /* use multiple consoles */ + +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + +#endif diff --git a/linux/ps4-kexec-1302-baikal/string.h b/linux/ps4-kexec-1302-baikal/string.h new file mode 100644 index 0000000..70aa668 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/string.h @@ -0,0 +1,96 @@ +/* + * string.h -- standard C string-manipulation functions. + * + * Copyright (C) 2008 Segher Boessenkool + * Copyright (C) 2009 Haxx Enterprises + * Copyright (C) 2010-2016 Hector Martin "marcan" + * + * Portions taken from the Public Domain C Library (PDCLib). + * http://pdclib.rootdirectory.de/ + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +static inline int strcmp(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s1[i] == s2[i]; i++) + ; + + return s1[i] - s2[i]; +} + +static inline void *memset(void *b, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)b)[i] = c; + + return b; +} + +static inline void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} + +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) + return p1[i] - p2[i]; + + return 0; +} + +static inline size_t strlen(const char *s) +{ + size_t len; + + for (len = 0; s[len]; len++) + ; + + return len; +} + +static inline size_t strnlen(const char *s, size_t count) +{ + size_t len; + + for (len = 0; s[len] && len < count; len++) + ; + + return len; +} + +static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) +{ + size_t len, needed; + + len = needed = strnlen(src, maxlen - 1) + 1; + if (len >= maxlen) + len = maxlen - 1; + + memcpy(dest, src, len); + dest[len] = 0; + + return needed - 1; +} + +#endif diff --git a/linux/ps4-kexec-1302-baikal/types.h b/linux/ps4-kexec-1302-baikal/types.h new file mode 100644 index 0000000..28d6619 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/types.h @@ -0,0 +1,51 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef TYPES_H +#define TYPES_H + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#ifndef TESTING +typedef u8 uint8_t; +typedef u64 size_t; +typedef s64 ssize_t; +typedef u64 uintptr_t; +typedef s64 off_t; +#endif + +#define NULL ((void *)0) + +#define CAT_(x, y) x ## y +#define CAT(x, y) CAT_(x, y) + +#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] +#define OSTRUCT(name, size) struct name { union { OPAD(size); +#define OSTRUCT_END };}; +#define OFIELD(off, field) struct { OPAD(off); field; } + +#define ASSERT_STRSIZE(struc, size) \ + _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#define ENOENT 2 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define ENAMETOOLONG 63 + +#endif diff --git a/linux/ps4-kexec-1302-baikal/uart.c b/linux/ps4-kexec-1302-baikal/uart.c new file mode 100644 index 0000000..a401434 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/uart.c @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "uart.h" +#include "kernel.h" +#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) + +#define AEOLIA_UART_BASE 0xD0340000 +#define BAIKAL_UART_BASE 0xC890E000 + +#define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, BAIKAL_UART_BASE + (intf << 12) + (reg << 2)) + +#define UART_REG_DATA 0 +#define UART_REG_IER 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +# define LSR_TXRDY 0x20 +# define LSR_TEMT 0x40 +#define UART_REG_MSR 6 +#define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) +#define UART0_IER UART_REG(32, 0, UART_REG_IER) +#define UART0_IIR UART_REG(32, 0, UART_REG_IIR) +#define UART0_LCR UART_REG(32, 0, UART_REG_LCR) +#define UART0_MCR UART_REG(32, 0, UART_REG_MCR) +#define UART0_LSR UART_REG(32, 0, UART_REG_LSR) +#define UART0_MSR UART_REG(32, 0, UART_REG_MSR) + +void uart_write_byte(u8 b) +{ + int limit; + u64 barrier; + limit = 250000; + while (!(*UART0_LSR & LSR_TXRDY) && --limit) + ; + *UART0_DATA = b; + __sync_fetch_and_add(&barrier, 0); + limit = 250000; + while (!(*UART0_LSR & LSR_TEMT) && --limit) + ; +} + +void uart_write_char(char c) +{ + if (c == '\n') + uart_write_byte('\r'); + + uart_write_byte(c); +} + +void uart_write_str(const char *s) +{ + while (*s) { + uart_write_char(*s++); + } +} diff --git a/linux/ps4-kexec-1302-baikal/uart.h b/linux/ps4-kexec-1302-baikal/uart.h new file mode 100644 index 0000000..b803e29 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/uart.h @@ -0,0 +1,20 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef UART_H +#define UART_H + +#include "types.h" + +void uart_write_byte(u8 b); +void uart_write_char(char c); +void uart_write_str(const char *s); + +#endif diff --git a/linux/ps4-kexec-1302-baikal/x86.h b/linux/ps4-kexec-1302-baikal/x86.h new file mode 100644 index 0000000..2f6d8b5 --- /dev/null +++ b/linux/ps4-kexec-1302-baikal/x86.h @@ -0,0 +1,195 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef X86_H +#define X86_H + +#define FLAGS_IF (1 << 9) + +#define CR0_WP (1 << 16) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) + +#define PG_V (1 << 0) +#define PG_RW (1 << 1) +#define PG_U (1 << 2) +#define PG_PS (1 << 7) + +#define SEG_TYPE_DATA (0 << 3) +#define SEG_TYPE_READ_WRITE (1 << 1) +#define SEG_TYPE_CODE (1 << 3) +#define SEG_TYPE_EXEC_READ (1 << 1) +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) + +static inline u64 cr0_read(void) +{ + u64 reg; + asm volatile("mov %0, cr0;" : "=r" (reg)); + return reg; +} + +static inline void cr0_write(u64 val) +{ + asm volatile("mov cr0, %0;" :: "r" (val)); +} + +static inline u64 write_protect_disable() +{ + u64 cr0 = cr0_read(); + cr0_write(cr0 & ~CR0_WP); + return cr0; +} + +static inline void write_protect_restore(u64 cr0) +{ + // Use only WP bit of input + cr0_write(cr0_read() | (cr0 & CR0_WP)); +} + +static inline u64 cr3_read(void) +{ + u64 reg; + asm volatile("mov %0, cr3;" : "=r" (reg)); + return reg; +} + +static inline void cr3_write(u64 val) +{ + asm volatile("mov cr3, %0;" :: "r" (val)); +} + +typedef struct { + u16 limit; + u64 address; +} __attribute__((packed)) desc_ptr; + +static inline desc_ptr gdt_read(void) +{ + desc_ptr gdtr; + asm volatile("sgdt %0;" : "=m" (gdtr)); + return gdtr; +} + +static inline void gdt_write(desc_ptr* val) +{ + asm volatile("lgdt %0;" :: "m" (*val)); +} + +//IDT +typedef struct { + u16 limit; + u64 address; +} __attribute__((packed)) idt_ptr; + +static inline idt_ptr idt_read(void) +{ + idt_ptr idtr; + asm volatile("sidt %0;" : "=m" (idtr)); + return idtr; +} + +static inline void idt_write(idt_ptr* val) +{ + asm volatile("lidt %0;" :: "m" (*val)); +} + +static inline void cr4_pge_disable(void) +{ + u64 cr4_temp; + asm volatile( + "mov %0, cr4;" + "and %0, ~0x80;" + "mov cr4, %0;" + : "=r" (cr4_temp) + ); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +static inline void cpu_stop(void) +{ + for (;;) + asm volatile("cli; hlt;" : : : "memory"); +} + +static inline void outl(int port, unsigned int data) +{ + asm volatile("out %w1, %0" : : "a" (data), "d" (port)); +} + +static inline void wrmsr(u32 msr_id, u64 msr_value) +{ + asm volatile( + "wrmsr" + : + : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) + ); +} + +static inline u64 rdmsr(u64 msr_id) +{ + u32 low, high; + asm volatile ( + "rdmsr" + : "=a"(low), "=d"(high) + : "c"(msr_id) + ); + return ((u64)high << 32) | low; +} + +static inline u64 rdtsc (void) +{ + unsigned int tickl, tickh; + asm volatile( + "rdtsc" + :"=a"(tickl),"=d"(tickh) + ); + return ((u64)tickh << 32) | tickl; +} + +static inline void udelay(unsigned int usec) { + u64 later = rdtsc() + usec * 1594ULL; + while (((s64)(later - rdtsc())) > 0); +} + +static inline void disable_interrupts(void) +{ + asm volatile("cli"); +} + +static inline void enable_interrupts(void) +{ + asm volatile("sti"); +} + +static inline u64 read_flags(void) +{ + u64 flags; + asm volatile("pushf; pop %0;" : "=r" (flags)); + return flags; +} + +static inline u64 intr_disable(void) +{ + u64 flags = read_flags(); + disable_interrupts(); + return flags; +} + +static inline void intr_restore(u64 flags) +{ + // TODO should only IF be or'd in? + asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); +} + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/LICENSE b/linux/ps4-kexec-1302-pro-baikal/LICENSE new file mode 100644 index 0000000..6c1cbba --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2015-2016 shuffle2 +Copyright (C) 2015-2016 Hector Martin "marcan" +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/ps4-kexec-1302-pro-baikal/Makefile b/linux/ps4-kexec-1302-pro-baikal/Makefile new file mode 100644 index 0000000..41fe2d6 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/Makefile @@ -0,0 +1,37 @@ +TOOLCHAIN_PREFIX ?= +CC = $(TOOLCHAIN_PREFIX)gcc +AR = $(TOOLCHAIN_PREFIX)ar +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy + +CFLAGS=$(CFLAG) -DPS4_13_02 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX +CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ + -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ + -fno-asynchronous-unwind-tables \ + -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ + -mcmodel=small -mno-red-zone + +SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ + acpi.c crc32.c + +OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) +DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld + +all: libkexec.a kexec.bin + +%.o: %.c *.h + $(CC) -c $(CFLAGS) -o $@ $< + +%.o: %.S + $(CC) -c $(CFLAGS) -o $@ $< + +libkexec.a: $(OBJS) + $(AR) -rc $@ $(OBJS) + +kexec.elf: libkexec.a kexec.ld + $(CC) $(CFLAGS) -o $@ libkexec.a + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +clean: + rm -f libkexec.a kexec.elf kexec.bin $(OBJS) diff --git a/linux/ps4-kexec-1302-pro-baikal/README.md b/linux/ps4-kexec-1302-pro-baikal/README.md new file mode 100644 index 0000000..6500878 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/README.md @@ -0,0 +1,121 @@ +# PS4 kexec implementation + +This repo implements a kexec()-style system call for the PS4 Orbis kernel +(FreeBSD derivative). This is designed to boot a Linux kernel directly from +FreeBSD. + +This is not an exploit. It is useless without some mechanism of injecting code +into the PS4 OS kernel. + +## Building + +To build a kexec.bin relocatable binary using the supplied Makefile, just type +`make`. This will also build a kexec.a archive. You can either use the binary +directly, or link the archive into your own project. + +If you link kexec.a with your own code, you need to supply the two symbols +`_start` and `_end` in your linker script, as `kernel_init()` will try to remap +all pages covered by that range as RWX (to make global variable accesses work). +Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this +feature, if you have already taken care of page permissions for the code. + +If you use a compiler toolchain that have a special prefix you can declare it +by passing TOOLCHAIN_PREFIX option to the Makefile like this: + + make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' + +## Usage + +The code is designed to be completely standalone. There is a single entry point: + + int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); + +Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel +symbols and install the sys_kexec system call. The syscall is registered +as number 153 by default (you can change this in kexec.h). The return value +is 0 on success, or negative on error. + +You may pass something other than NULL as `early_printf`. In that case, that +function will be used for debug output during early symbol resolution, before +printf is available. + +Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is +enabled by default, symtab also disappears in newer kernel, we have to +hardcode offsets for some symbols. Currently we use the `early_printf` +given by user to caculate the base address of kernel, then relocate all the +symbols from the kernel base. You could enable this feature like this: + + make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' + +If you do not want to call the syscall from userspace, you can pass the address +of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the +address of `sys_kexec`, so you can invoke it manually (see kexec.h for +its prototype and how the arguments are passed). + +If you are using the standalone kexec.bin blob, then the `kexec_init` function +is always located at offset 0, so simply call the base address of the blob. +Don't forget to pass two NULL arguments (or the appropriate pointers). + +The injected `sys_kexec` system call takes (userspace) pointers to the kernel +and initramfs blobs, their sizes, and a pointer to the (null-terminated) command +line string. From userspace, this looks like this: + + int kexec(void *kernel_image, size_t image_size, + void *initramfs, size_t initramfs_size, + const char *cmdline); + + // syscall() usage: + syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); + +`kexec()` will load the kernel and initramfs into memory, but will not directly +boot them. To boot the loaded kernel, shut down the system. This can be +accomplished by pressing the power button, but can also be done more quickly +and reliably from userspace with the following sequence of system calls (this +kills userspace quickly but still does a controlled filesystem unmount): + + int evf = syscall(540, "SceSysCoreReboot"); + syscall(546, evf, 0x4000, 0); + syscall(541, evf); + // should be syscall(37, 1, 30) but only tested via kill symbol + kill(1, 30); + +Note that this software should be loaded into kernel memory space. If you are +running kernel code from userland mappings, you should either switch to kernel +mappings or separately copy kexec.bin to a location in kernel address space. +While syscalls or exploit code may run properly from userland, the shutdown hook +will not, as it will be called from a different process context. + +## Features + +`kernel_init()` will automatically find the Orbis OS kernel and resolve all +necessary symbols to work. There are no static symbol dependencies. If +`DO_NOT_REMAP_RWX` is not defined (the default), it will also patch +`pmap_protect` to disable the W^X restriction. + +In addition to loading the user-supplied initramfs, `kexec` will locate the +Radeon firmware blobs inside Orbis OS, extract them, convert them to a format +suitable for Linux, and append them as an additional initramfs cpio image to +the existing initramfs. This avoids the need to distribute the Radeon firmware +blobs. The `radeon` module, when compiled into the kernel, will automatically +load this firmware on boot. Note however that most typical initramfs scripts +will wipe the initramfs contents while pivoting to the real system, so if you +compile `radeon` as a module you may not be able to access the firmware after +boot. To cover that case, add some code to your initramfs `/init` script to +copy the firmware to a tmpfs mounted on the real filesystem: + + # assuming real root FS is mounted on /mnt + + mkdir -p /mnt/lib/firmware/radeon + mount -t tmpfs none /mnt/lib/firmware/radeon + cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ + + # now switch_root to /mnt + +This avoids having to permanently store copies of the Radeon firmware, which +isn't really necessary for most use cases. + +There is significant debug logging available, which will appear on the system +UART. Most of the code relies on the kernel `printf` implementation, and +therefore you should patch out the UART output blanker to see it. The final +code that runs on the boot CPU before booting the kernel uses direct UART +writes and is not affected by the blanking feature of Orbis OS. diff --git a/linux/ps4-kexec-1302-pro-baikal/acpi.c b/linux/ps4-kexec-1302-pro-baikal/acpi.c new file mode 100644 index 0000000..bfccf08 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/acpi.c @@ -0,0 +1,320 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "types.h" +#include "kernel.h" +#include "acpi.h" +#include "acpi_caps.h" + +#ifdef TESTING +# include +# include +# include +# include +# include +# include +#else +# include "kernel.h" +# include "string.h" +# define printf kern.printf +#endif + +#define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) +#define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) + +#define PACKED __attribute__((packed)) + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +struct RSDP { + u64 sig; + u8 checksum; + u8 oemid[6]; + u8 rev; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 rsvd[3]; +} PACKED; + +struct SDTH { + u32 sig; + u32 length; + u8 rev; + u8 checksum; + u8 oem_id[6]; + u8 oem_tid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; +} PACKED; + +struct RSDT { + struct SDTH hdr; + u32 table_addr[]; +} PACKED; + +struct XSDT { + struct SDTH hdr; + u64 table_addr[]; +} PACKED; + +struct FADT { + struct SDTH hdr; + u32 facs; + u32 dsdt; + // more stuff... +} PACKED; + +struct ivhd_entry4 { + u8 type; + u16 devid; + u8 flags; +} PACKED; + +struct ivhd_header { + u8 type; + u8 flags; + u16 length; + u16 devid; + u16 cap_ptr; + u64 mmio_phys; + u16 pci_seg; + u16 info; + u32 efr_attr; +} PACKED; + +struct IVRS { + struct SDTH hdr; + u32 IVinfo; + u8 reserved[8]; + struct ivhd_header hd_hdr; + struct ivhd_entry4 hd_entries[3]; +} PACKED; + +struct MMIO { + u64 baseAddressECM; + u16 pciSegmentGroup; + u8 startPCIBus; + u8 endPCIBus; + u32 reserved; +} PACKED; + +// We have enough space to use the second half of the 64KB table area +// as scratch space for building the tables +#define BUFFER_OFF 0x8000 + +#define P2M(p) (((u64)(p)) - phys_base + map_base) +#define M2P(p) ((((void*)(p)) - map_base) + phys_base) +#define B2P(p) ((((void*)(p)) - buf_base) + phys_base) +#define P2B(p) ((((void*)(p)) - phys_base) + buf_base) + +#define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) +#define PADB(s) p += (s) +#define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) +#define ALLOC(t) (t)ALLOCB(sizeof(t)) +#define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) +#define COPYT(s) COPYB(((struct SDTH*)s)->length, s) +#define COPYTP(s) COPYT(P2M(s)) +#define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) +#define COPYP(t, s) COPY(t, P2M(s)) + +static void rsdp_checksum(struct RSDP *rsdp) { + rsdp->checksum = rsdp->ext_checksum = 0; + + u8 sum = 0; + for (int i = 0; i < 20; i++) + sum += ((u8*)rsdp)[i]; + rsdp->checksum = -sum; + sum = 0; + for (int i = 0; i < sizeof(*rsdp); i++) + sum += ((u8*)rsdp)[i]; + rsdp->ext_checksum = -sum; +} + +static void table_checksum(void *table) { + struct SDTH *hdr = table; + hdr->checksum = 0; + u8 sum = 0; + for (int i = 0; i < hdr->length; i++) + sum += ((u8*)table)[i]; + hdr->checksum = -sum; +} + +#define IVHD_FLAG_ISOC_EN_MASK 0x08 +#define IVHD_DEV_ALL 0x01 +#define IVHD_DEV_SELECT 0x02 +#define IVHD_DEV_SELECT_RANGE_START 0x03 +#define IVHD_DEV_RANGE_END 0x04 + +#define ACPI_DEVFLAG_SYSMGT1 0x10 +#define ACPI_DEVFLAG_SYSMGT2 0x20 + +static void *build_ivrs(struct IVRS *ivrs) { + memset(ivrs, 0, sizeof(*ivrs)); + + ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); + ivrs->hdr.length = sizeof(*ivrs); + ivrs->hdr.rev = 1; + memcpy(ivrs->hdr.oem_id, "F0F ", 6); + memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); + ivrs->hdr.oem_rev = 0x20161225; + memcpy(ivrs->hdr.creator_id, "KEXC", 4); + ivrs->hdr.creator_rev = 0x20161225; + ivrs->IVinfo = 0x00203040; //48882_IOMMU.pdf page 251 + + struct ivhd_header *hdr = &ivrs->hd_hdr; //48882_IOMMU.pdf page 254 + hdr->type = 0x10; + hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; + hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); + hdr->devid = PCI_DEVFN(0, 2); + hdr->cap_ptr = 0x40; // from config space + 0x34 + hdr->mmio_phys = 0xfc000000; //Base address of IOMMU control registers in MMIO space + hdr->pci_seg = 0; + hdr->info = 0; // msi msg num? (the pci cap should be written by software) + // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 + hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); //Feature Reporting Field, 48882_IOMMU.pdf page 255 + + struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; + // on fbsd, all aeolia devfns have active entries except memories (func 6) + // not sure if this is just because it wasn't in use when i dumped it? + // all entries are r/w + // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) + // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) + // Modes: + // 4 level: + // apcie + // 3 level: + // all others + + // the way to encode this info into the IVHD entries is fairly arbitrary... + entries[0].type = IVHD_DEV_SELECT; //DTE setting applies to the device specifed in DevID field. + entries[0].devid = PCI_DEVFN(20, 0); //vendorId: 104D, deviceId: 90D7; Sony Baikal ACPI + entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; + + entries[1].type = IVHD_DEV_SELECT_RANGE_START; + entries[1].devid = PCI_DEVFN(20, 1); + entries[1].flags = 0; //Identifies a device able to assert INIT interrupts (page 262) + entries[2].type = IVHD_DEV_RANGE_END; + entries[2].devid = PCI_DEVFN(20, 7); + entries[2].flags = 0; //Identifies a device able to assert INIT interrupts + + table_checksum(ivrs); + return ivrs + 1; +} + +void fix_acpi_tables(void *map_base, u64 phys_base) +{ + void *buf_base = map_base + 0x8000; + void *p = buf_base; + memset(buf_base, 0, 0x8000); + + printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); + + struct RSDP *rsdp = COPYP(struct RSDP, phys_base); + printf("RSDT at 0x%x\n", rsdp->rsdt_addr); + printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); + + struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); + struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); + rsdp->rsdt_addr = B2P(rsdt); + + PADB(0x30); // this gives us space for new tables + + struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); + rsdp->xsdt_addr = B2P(xsdt); + + PADB(0x60); + + struct FADT *fadt = NULL; + + int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; + int i; + for (i = 0; i < cnt; i++) { + struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); + printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); + switch (hdr->sig) { + case SIG32('F', 'A', 'C', 'P'): + { + fadt = (void*)hdr; + printf("FACS at 0x%x\n", fadt->facs); + printf("DSDT at 0x%x\n", fadt->dsdt); + // Sony puts the FACS before the FADT, unaligned, which is + // noncompliant, but let's keep it there + u8 *facs = COPYB(64, P2M(fadt->facs)); + fadt = (void*)(hdr = COPYT(hdr)); + fadt->facs = B2P(facs); + PADB(0x38); + break; + } + case SIG32('S', 'S', 'D', 'T'): + { + // Put the DSDT before the SSDT + if (fadt) { + PADB(0xf0); + u8 *dsdt = COPYTP(fadt->dsdt); + fadt->dsdt = B2P(dsdt); + PADB(0x174); + table_checksum(fadt); + } else { + printf("ERROR: no FADT yet?\n"); + } + hdr = COPYT(hdr); + break; + } + default: + hdr = COPYT(hdr); + } + table_checksum(hdr); + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); + } + + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); + i++; + p = build_ivrs(p); + + rsdt->hdr.length = sizeof(*rsdt) + 4 * i; + xsdt->hdr.length = sizeof(*xsdt) + 8 * i; + + rsdp_checksum(rsdp); + table_checksum(rsdt); + table_checksum(xsdt); + memcpy(map_base, buf_base, p - buf_base); +} + +u32 msi_mask(unsigned x) { + /* Don't shift by >= width of type */ + if (x >= 5) + return 0xffffffff; + return (1 << (1 << x)) - 1; +} +void disableMSI(u64 MSICapabilityRegAddr) { + PPCI_MSI_CAPABILITY pMSICapability = (PPCI_MSI_CAPABILITY)PA_TO_DM(MSICapabilityRegAddr); + if (pMSICapability->msiEnable == 1) + pMSICapability->msiEnable = 0; + pMSICapability->mask64 = msi_mask(pMSICapability->multipleMessageCapable); +} + +#ifdef TESTING + +int main(int argc, char **argv) +{ + int fd; + void *base; + + fd = open(argv[1], O_RDWR); + base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + fix_acpi_tables(base, 0xe0000); + return 0; +} + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/acpi.h b/linux/ps4-kexec-1302-pro-baikal/acpi.h new file mode 100644 index 0000000..45f2834 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/acpi.h @@ -0,0 +1,22 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ACPI_H +#define ACPI_H + +#define PACKED __attribute__((packed)) + +void fix_acpi_tables(void *map_base, u64 phys_base); + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) + +void disableMSI(u64 MSICapabilityRegAddr); + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/acpi_caps.h b/linux/ps4-kexec-1302-pro-baikal/acpi_caps.h new file mode 100644 index 0000000..070e9f6 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/acpi_caps.h @@ -0,0 +1,30 @@ +#ifndef ACPI_CAPS_H +#define ACPI_CAPS_H + +#define UCHAR u8 +#define USHORT u16 +#define ULONG u32 +#define PACKED __attribute__((packed)) +typedef struct PACKED _PCI_CAPABILITIES_HEADER { + UCHAR CapabilityID; + UCHAR Next; +} PCI_CAPABILITIES_HEADER, *PPCI_CAPABILITIES_HEADER; + +typedef struct PACKED _PCI_MSI_CAPABILITY { + PCI_CAPABILITIES_HEADER Header; + u16 msiEnable : 1, multipleMessageCapable : 3, multipleMessageEnable : 3, address64Capable : 1, reserved0 : 8; + u32 lowerAddress : 30, reserved1 : 2; + union { + struct { + u32 upperAddress; + u32 messageData64 : 16, reservedData64 : 16; + u32 mask64; + }; + struct { + u32 messageData32 : 16, reservedData32 : 16; + u32 mask32; + }; + }; +} PCI_MSI_CAPABILITY, *PPCI_MSI_CAPABILITY; + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/crc32.c b/linux/ps4-kexec-1302-pro-baikal/crc32.c new file mode 100644 index 0000000..7836c18 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/crc32.c @@ -0,0 +1,102 @@ +/*- +* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or +* code or tables extracted from it, as desired without restriction. +* +* First, the polynomial itself and its table of feedback terms. The +* polynomial is +* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 +* +* Note that we take it "backwards" and put the highest-order term in +* the lowest-order bit. The X^32 term is "implied"; the LSB is the +* X^31 term, etc. The X^0 term (usually shown as "+1") results in +* the MSB being 1 +* +* Note that the usual hardware shift register implementation, which +* is what we're using (we're merely optimizing it by doing eight-bit +* chunks at a time) shifts bits into the lowest-order term. In our +* implementation, that means shifting towards the right. Why do we +* do it this way? Because the calculated CRC must be transmitted in +* order from highest-order term to lowest-order term. UARTs transmit +* characters in order from LSB to MSB. By storing the CRC this way +* we hand it to the UART in the order low-byte to high-byte; the UART +* sends each low-bit to hight-bit; and the result is transmission bit +* by bit from highest- to lowest-order term without requiring any bit +* shuffling on our part. Reception works similarly +* +* The feedback terms table consists of 256, 32-bit entries. Notes +* +* The table can be generated at runtime if desired; code to do so +* is shown later. It might not be obvious, but the feedback +* terms simply represent the results of eight shift/xor opera +* tions for all combinations of data and CRC register values +* +* The values must be right-shifted by eight bits by the "updcrc +* logic; the shift must be unsigned (bring in zeroes). On some +* hardware you could probably optimize the shift in assembler by +* using byte-swap instructions +* polynomial $edb88320 +* +* +* CRC32 code derived from work by Gary S. Brown. +*/ + +#include "crc32.h" + +static u32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 crc32(u32 crc, const void *buf, size_t size) +{ + const u8 *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} diff --git a/linux/ps4-kexec-1302-pro-baikal/crc32.h b/linux/ps4-kexec-1302-pro-baikal/crc32.h new file mode 100644 index 0000000..777278d --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/crc32.h @@ -0,0 +1,7 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +u32 crc32(u32 crc, const void *buf, size_t size); + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/elf.h b/linux/ps4-kexec-1302-pro-baikal/elf.h new file mode 100644 index 0000000..63371c7 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/elf.h @@ -0,0 +1,71 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + u8 e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + u64 e_entry; + u64 e_phoff; + u64 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf64_Ehdr; + +typedef struct { + u32 p_type; + u32 p_flags; + u64 p_offset; + void *p_vaddr; + u64 p_paddr; + u64 p_filesz; + u64 p_memsz; + u64 p_align; +} Elf64_Phdr; + +#define PT_DYNAMIC 2 +#define PT_PHDR 6 + +#define DT_NULL 0 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_STRSZ 10 + +typedef struct { + s64 d_tag; + union { + u64 d_val; + void *d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + void *st_value; + u64 st_size; +} Elf64_Sym; + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/firmware.c b/linux/ps4-kexec-1302-pro-baikal/firmware.c new file mode 100644 index 0000000..a831b1c --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/firmware.c @@ -0,0 +1,479 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "firmware.h" +#include "types.h" +#include "kernel.h" +#include "string.h" +#include "types.h" +#include "crc32.h" + +#define DIR 0040755 +#define FILE 0100644 + +struct firmware_header { + u32 size_bytes; + u32 header_size_bytes; + u16 header_version_major; + u16 header_version_minor; + u16 ip_version_major; + u16 ip_version_minor; + u32 ucode_version; + u32 ucode_size_bytes; + u32 ucode_array_offset_bytes; + u32 crc32; + union { + struct { + u32 ucode_feature_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } gfx1; + struct { + u32 ucode_feature_version; + u32 save_and_restore_offset; + u32 clear_state_descriptor_offset; + u32 avail_scratch_ram_locations; + u32 master_pkt_description_offset; + u8 end[]; + } rlc1; + struct { + u32 ucode_feature_version; + u32 ucode_change_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } sdma1; + u8 raw[0xe0]; + }; +}; + +static inline char hex(u8 c) +{ + if (c <= 9) + return '0' + c; + return 'a' + c - 10; +} + +static void hex8(u8 **p, u32 val) +{ + *(*p)++ = hex(val >> 28); + *(*p)++ = hex((val >> 24) & 0xf); + *(*p)++ = hex((val >> 20) & 0xf); + *(*p)++ = hex((val >> 16) & 0xf); + *(*p)++ = hex((val >> 12) & 0xf); + *(*p)++ = hex((val >> 8) & 0xf); + *(*p)++ = hex((val >> 4) & 0xf); + *(*p)++ = hex(val & 0xf); +} + +void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) +{ + size_t name_len = strlen(name); + + // Pad to 4 byte multiple + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; + + memcpy(*p, "070701", 6); + *p += 6; + hex8(p, 0); // c_ino + hex8(p, mode); // c_mode + hex8(p, 0); // c_uid + hex8(p, 0); // c_gid + hex8(p, 1); // c_nlink + hex8(p, 0); // c_mtime + hex8(p, size); // c_filesize + hex8(p, 0); // c_maj + hex8(p, 0); // c_min + hex8(p, 0); // c_rmaj + hex8(p, 0); // c_rmin + hex8(p, name_len + 1); // c_namesize + hex8(p, 0); // c_chksum + memcpy(*p, name, name_len); + *p += name_len; + *(*p)++ = 0; + + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; +} + +struct fw_header_t { + u64 size_words; + char *unk_ident; + u64 unk; + void *blob; + u64 unk2; +}; + +struct fw_info_t { + struct fw_header_t *rlc; + struct fw_header_t *sdma0; + struct fw_header_t *sdma1; + struct fw_header_t *ce; + struct fw_header_t *pfp; + struct fw_header_t *me; + struct fw_header_t *mec1; + struct fw_header_t *mec2; +}; + +struct fw_expected_sizes_t { + u64 rlc; + u64 sdma0; + u64 sdma1; + u64 ce; + u64 pfp; + u64 me; + u64 mec1; + u64 mec2; +}; +static const struct fw_expected_sizes_t liverpool_fw_sizes = { + LVP_FW_RLC_SIZE, + LVP_FW_SDMA_SIZE, + LVP_FW_SDMA1_SIZE, + LVP_FW_CE_SIZE, + LVP_FW_PFP_SIZE, + LVP_FW_ME_SIZE, + LVP_FW_MEC_SIZE, + LVP_FW_MEC2_SIZE +}; +static const struct fw_expected_sizes_t gladius_fw_sizes = { + GL_FW_RLC_SIZE, + GL_FW_SDMA_SIZE, + GL_FW_SDMA1_SIZE, + GL_FW_CE_SIZE, + GL_FW_PFP_SIZE, + GL_FW_ME_SIZE, + GL_FW_MEC_SIZE, + GL_FW_MEC2_SIZE +}; + +void copy_edid(u8 **p, int sz) +{ + int i; + u8 *edid = *p; + u8 *off_edid = kern.edid; + + memset(edid, 0, sz); + *p += sz; + + for(i = 0; i < sz; i++) + *(edid + i) = *(off_edid + i); + + *p += sz; +} + +void copy_eap_hdd_key(u8 **p) +{ + int i; + u8 *eap_key = *p; + u8 *off_eap_key = kern.eap_hdd_key; + + memset(eap_key, 0, 0x20); + *p += 0x20; + + for(i = 0; i < 0x20; i++) + { + if(i < 0x10) + *(eap_key + i) = *(off_eap_key + 0xF - i); + else + *(eap_key + i) = *(off_eap_key + 0x2F - i); + } + *p += 0x20; +} + +int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + kern.printf("Copying %s firmware\n", name); + if (expected_size != (hdr->size_words * 4)) { + kern.printf("copy_firmware: %s: expected size %d, got %d\n", + name, expected_size, hdr->size_words * 4); + return 0; + } + + struct firmware_header *fhdr = (struct firmware_header*)*p; + memset(fhdr, 0, sizeof(*fhdr)); + *p += sizeof(*fhdr); + + memcpy(*p, hdr->blob, expected_size); + + fhdr->size_bytes = expected_size + sizeof(*fhdr); + fhdr->header_size_bytes = offsetof(struct firmware_header, raw); + fhdr->header_version_major = 1; + fhdr->header_version_minor = 0; + fhdr->ucode_version = 0x10; + fhdr->ucode_size_bytes = expected_size; + fhdr->ucode_array_offset_bytes = sizeof(*fhdr); + + *p += expected_size; + + return 1; +} + +int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); + fhdr->gfx1.ucode_feature_version = 21; + fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); + fhdr->rlc1.ucode_feature_version = 1; + fhdr->rlc1.save_and_restore_offset = 0x90; + fhdr->rlc1.clear_state_descriptor_offset = 0x3d; + fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? + fhdr->rlc1.master_pkt_description_offset = 0; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 2; + fhdr->ip_version_minor = 1; + fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); + fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; + fhdr->sdma1.ucode_change_version = 0; + fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +static const u32 pfp_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc4200016, // ldw r8, [r0, #0x16] + 0xdc030000, // mov ctr, r0 + 0xcc000049, // stw r0, [r0, #0x49] + 0xcc200013, // stw r0, [r8, #0x13] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 ce_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc420000c, // ldw r8, [r0, #0xc] + 0xdc030000, // mov ctr, r0 + 0xcc00002f, // stw r0, [r0, #0x2f] + 0xcc200012, // stw r0, [r8, #0x12] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 mec_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc43c000c, // ldw r15, [r0, #0x9] + 0xdc030000, // mov ctr, r0 + 0xcc00002b, // stw r0, [r0, #0x2b] + 0xcc3c000d, // stw r0, [r15, #0xd] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +#define PACKET_TYPE_NOP 0x10 + +static void patch_fw(void *p, const u32 *handler, int handler_size) { + int size = ((struct firmware_header*)p)->ucode_size_bytes; + int code_size = (size & ~0xfff) / 4; + int nop_start = code_size - 0x10; + + u32 *fw = p + sizeof(struct firmware_header); + kern.printf("NOP handler at 0x%x\n", nop_start); + memcpy(&fw[nop_start], handler, handler_size); + + // patch the branch table entry + for (int off = code_size; off < size/4; off++) { + if ((fw[off] >> 16) == PACKET_TYPE_NOP) { + fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; + } + } +} + +struct fw_info_t *get_fw_info() { + if (kern.gc_get_fw_info) { + return kern.gc_get_fw_info(); + } else if (kern.Starsha_UcodeInfo) { + return kern.Starsha_UcodeInfo; + } else { + return NULL; + } +} + +const struct fw_expected_sizes_t *get_fw_expected_sizes() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return &gladius_fw_sizes; + } else { + return &liverpool_fw_sizes; + } +} + +const char * get_gpu_name() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return "gladius"; + } else { + return "liverpool"; + } +} + +ssize_t firmware_extract(void *dest) +{ + u8 *p = dest; + + // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. + struct fw_info_t *info = get_fw_info(); + if (!info) { + kern.printf("firmware_extract: Could not locate firmware table"); + return -1; + } + const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); + + //Eap hdd key + cpio_hdr(&p, "key", DIR, 0); + cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); + copy_eap_hdd_key(&p); + + cpio_hdr(&p, "lib", DIR, 0); + cpio_hdr(&p, "lib/firmware", DIR, 0); + + /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ + int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); + cpio_hdr(&p, "lib/firmware/edid", DIR, 0); + cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); + copy_edid(&p, edid_sz); + + char dir[7]; + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) + kern.snprintf(dir, sizeof(dir), "amdgpu"); + else + kern.snprintf(dir, sizeof(dir), "amdgpu"); + + char dir_path[64]; + kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); + cpio_hdr(&p, dir_path, DIR, 0); + + char pfp_path[64]; + kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", pfp_path); + cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); + u8 *pfp = p; + if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) + return -1; + patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); + + char me_path[64]; + kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", me_path); + cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); + if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) + return -1; + + char ce_path[64]; + kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", ce_path); + cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); + u8 *ce = p; + if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) + return -1; + patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); + + char mec_path[64]; + kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec_path); + cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); + u8 *mec1 = p; + if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) + return -1; + patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); + + char mec2_path[64]; + kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec2_path); + cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); + u8 *mec2 = p; + if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) + return -1; + patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); + + char rlc_path[64]; + kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", rlc_path); + cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); + if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) + return -1; + + char sdma_path[64]; + kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma_path); + cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); + if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + char sdma1_path[64]; + kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma1_path); + cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); + if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + size_t size = p - (u8*)dest; + if (size > FW_CPIO_SIZE) { + kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); + return -1; + } + + return size; +} diff --git a/linux/ps4-kexec-1302-pro-baikal/firmware.h b/linux/ps4-kexec-1302-pro-baikal/firmware.h new file mode 100644 index 0000000..4835983 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/firmware.h @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include "types.h" + +//sizes eap_hdd_key +#define EAP_HDD_KEY_SIZE 0x20 +#define EDID_SIZE 256 + +// sizes for liverpool +#define LVP_FW_CE_SIZE 8576 +#define LVP_FW_ME_SIZE 16768 +#define LVP_FW_MEC_SIZE 16768 +#define LVP_FW_MEC2_SIZE 16768 +#define LVP_FW_PFP_SIZE 16768 +#define LVP_FW_RLC_SIZE 6144 +#define LVP_FW_SDMA_SIZE 4200 +#define LVP_FW_SDMA1_SIZE 4200 +// sizes for gladius +#define GL_FW_CE_SIZE 8576 +#define GL_FW_ME_SIZE 16768 +#define GL_FW_MEC_SIZE 16768 +#define GL_FW_MEC2_SIZE 16768 +#define GL_FW_PFP_SIZE 16768 +#define GL_FW_RLC_SIZE 8192 +#define GL_FW_SDMA_SIZE 4200 +#define GL_FW_SDMA1_SIZE 4200 + +#define MAX(x ,y) (((x) > (y)) ? (x) : (y)) +#define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) + +#define FW_CE_SIZE MAX_FW_SIZE(CE) +#define FW_ME_SIZE MAX_FW_SIZE(ME) +#define FW_MEC_SIZE MAX_FW_SIZE(MEC) +#define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) +#define FW_PFP_SIZE MAX_FW_SIZE(PFP) +#define FW_RLC_SIZE MAX_FW_SIZE(RLC) +#define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) +#define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) + +// Conservative value (max 113 bytes plus name size plus alignment) +#define CPIO_HEADER_SIZE 256 + +#define FW_HEADER_SIZE 256 + +// Leave space for 16 files (currently 12) +#define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ + FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ + FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ + FW_HEADER_SIZE * 8) + +ssize_t firmware_extract(void *dest); + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/kernel.c b/linux/ps4-kexec-1302-pro-baikal/kernel.c new file mode 100644 index 0000000..b6d31b4 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/kernel.c @@ -0,0 +1,383 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "string.h" +#include "elf.h" +#include "x86.h" +#include "../magic.h" + +struct ksym_t kern; +int (*early_printf)(const char *fmt, ...) = NULL; + +#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) + +#ifdef NO_SYMTAB + +#define RESOLVE_NOERR(name) do { \ + if (kern_off_ ## name == 0) { \ + kern.name = 0; \ + } else { \ + kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ + } \ +} while (0); + +#define RESOLVE(name) do { \ + if (kern_off_ ## name == 0) { \ + return 0; \ + } \ + RESOLVE_NOERR(name) \ +} while (0); + +#else + +#define KERNSIZE 0x2000000 + +static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; +static Elf64_Sym *symtab; +static char *strtab; +static size_t strtab_size; + +static Elf64_Ehdr *find_kern_ehdr(void) +{ + // Search for the kernel copy embedded in ubios, then follow it to see + // where it was relocated to + for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; + if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { + for (size_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; + if (phdr->p_type == PT_PHDR) { + return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); + } + } + } + } + return NULL; +} + +static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf64_Dyn *)phdr->p_vaddr; + } + } + return NULL; +} + +static int elf_parse_dyn(Elf64_Dyn *dyn) +{ + for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_SYMTAB: + symtab = (Elf64_Sym *)dp->d_un.d_ptr; + break; + case DT_STRTAB: + strtab = (char *)dp->d_un.d_ptr; + break; + case DT_STRSZ: + strtab_size = dp->d_un.d_val; + break; + } + } + return symtab && strtab && strtab_size; +} + +void *kernel_resolve(const char *name) +{ + for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { + if (!strcmp(name, &strtab[sym->st_name])) { + eprintf("kern.%s = %p\n", name, (void*)sym->st_value); + return (void *)sym->st_value; + } + } + eprintf("Failed to resolve symbol '%s'\n", name); + return NULL; +} + +#define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) +#define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; + +#endif + +static int resolve_symbols(void) +{ + RESOLVE(printf); + RESOLVE(snprintf); + early_printf = kern.printf; + RESOLVE(copyin); + RESOLVE(copyout); + RESOLVE(copyinstr); + RESOLVE(kernel_map); + RESOLVE(kernel_pmap_store); + RESOLVE(kmem_alloc_contig); + RESOLVE(kmem_free); + RESOLVE(pmap_extract); + RESOLVE(pmap_protect); + RESOLVE(sysent); + RESOLVE(sched_pin); + RESOLVE(sched_unpin); + RESOLVE(smp_rendezvous); + RESOLVE(smp_no_rendevous_barrier); + RESOLVE(icc_query_nowait); + RESOLVE_NOERR(Starsha_UcodeInfo); + RESOLVE_NOERR(gpu_devid_is_9924); + RESOLVE_NOERR(gc_get_fw_info); + RESOLVE_NOERR(eap_hdd_key); + RESOLVE_NOERR(edid); + RESOLVE(wlanbt); + RESOLVE(kern_reboot); + RESOLVE(set_gpu_freq); + RESOLVE(set_pstate); + RESOLVE(update_vddnp); + RESOLVE(set_cu_power_gate); + RESOLVE_NOERR(pstate_before_shutdown); + return 1; +} + +#define M_WAITOK 0x0002 +#define M_ZERO 0x0100 + +#define VM_MEMATTR_DEFAULT 0x06 + +void *kernel_alloc_contig(size_t size) +{ + // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... + vm_offset_t ret = 0; + while(!(ret = kern.kmem_alloc_contig( + *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, + ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); + + /*if (!ret) { + kern.printf("Failed to allocate %zud bytes\n", size); + return NULL; + }*/ + return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); +} + +void kernel_free_contig(void *addr, size_t size) +{ + if (!addr) + return; + kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); +} + +int kernel_hook_install(void *target, void *hook) +{ + uintptr_t t = (uintptr_t)target; // addr to redirect to + uintptr_t h = (uintptr_t)hook; // place to write the thunk + + if (!hook || !target) { + return 0; + } + + kern.printf("kernel_hook_install(%p, %p)\n", target, hook); + + if (!(t & (1L << 63))) { + kern.printf("\n===================== WARNING =====================\n"); + kern.printf("hook target function address: %p\n", target); + kern.printf("It looks like we're running from userland memory.\n"); + kern.printf("Please run this code from a kernel memory mapping.\n\n"); + return 0; + } + s64 displacement = t - (h + 5); + + kern.sched_pin(); + u64 wp = write_protect_disable(); + if (displacement < -0x80000000 || displacement > 0x7fffffff) { + kern.printf(" Using 64bit absolute jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[2]; + s32 zero; + void *target; + } jmp = { + .op = { 0xff, 0x25 }, + .zero = 0, + .target = target, + }; + ASSERT_STRSIZE(struct jmp_t, 14); + memcpy(hook, &jmp, sizeof(jmp)); + } else { + kern.printf(" Using 32bit relative jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[1]; + s32 imm; + } jmp = { + .op = { 0xe9 }, + .imm = displacement, + }; + ASSERT_STRSIZE(struct jmp_t, 5); + memcpy(hook, &jmp, sizeof(jmp)); + } + wbinvd(); + write_protect_restore(wp); + kern.sched_unpin(); + + return 1; +} + +void kernel_syscall_install(int num, void *call, int narg) +{ + struct sysent_t *sy = &kern.sysent[num]; + + kern.sched_pin(); + u64 wp = write_protect_disable(); + + memset(sy, 0, sizeof(*sy)); + sy->sy_narg = narg; + sy->sy_call = call; + sy->sy_thrcnt = 1; + + write_protect_restore(wp); + kern.sched_unpin(); +} + +void kernel_remap(void *start, void *end, int perm) +{ + u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); + u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); + + kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); + kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); +} + +static volatile int _global_test = 0; + +#ifndef DO_NOT_REMAP_RWX +extern u8 _start[], _end[]; + +static int patch_pmap_check(void) +{ + u8 *p; + + for (p = (u8*)kern.pmap_protect; + p < ((u8*)kern.pmap_protect + 0x500); p++) { + #ifdef PS4_6_72 + if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 + p[5] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + /*#ifdef PS4_5_05 + if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { + p[1] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + }*/ + #else + if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { + p[2] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + #endif + } + kern.printf("pmap_protect patch failed!\n"); + return 0; +} +#endif + +int kernel_init(void *_early_printf) +{ + int rv = -1; + + if (_early_printf) + early_printf = _early_printf; + + eprintf("kernel_init()\n"); + +#ifdef KASLR + // use `early_printf` to calculate kernel base + if (early_printf == NULL) + return 0; + + kern.kern_base = (u64)(early_printf - kern_off_printf); + if ((kern.kern_base & PAGE_MASK) != 0) { + eprintf("Kernel base is not aligned\n"); + return 0; + } else { + eprintf("Kernel base = %llx\n", kern.kern_base); + } + + u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); + u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); + +#else + kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 + + u64 DMPML4I = 0x1fc; + u64 DMPDPI = 0; +#endif + + kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); + eprintf("Direct map base = %llx\n", kern.dmap_base); + + // We may not be mapped writable yet, so to be able to write to globals + // we need WP disabled. + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + +#ifndef NO_SYMTAB + Elf64_Ehdr *ehdr = find_kern_ehdr(); + if (!ehdr) { + eprintf("Could not find kernel ELF header\n"); + goto err; + } + eprintf("ELF header at %p\n", ehdr); + + Elf64_Dyn *dyn = elf_get_dyn(ehdr); + if (!dyn) { + eprintf("Could not find kernel dynamic header\n"); + goto err; + } + eprintf("ELF dynamic section at %p\n", dyn); + + if (!elf_parse_dyn(dyn)) { + eprintf("Failed to parse ELF dynamic section\n"); + goto err; + } +#endif + + if (!resolve_symbols()) { + eprintf("Failed to resolve all symbols\n"); + goto err; + } + + // Pin ourselves as soon as possible. This is expected to be released by the caller. + kern.sched_pin(); + +#ifndef DO_NOT_REMAP_RWX + if (!patch_pmap_check()) + goto err; +#endif + +#ifndef DO_NOT_REMAP_RWX + // kernel_remap may need interrupts, but may not write to globals! + enable_interrupts(); + kernel_remap(_start, _end, 7); + disable_interrupts(); +#endif + + // Writing to globals is now safe. + + kern.printf("Testing global variable access (write protection)...\n"); + _global_test = 1; + kern.printf("OK.\n"); + + kern.printf("Kernel interface initialized\n"); + rv = 0; + +err: + write_protect_restore(wp); + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1302-pro-baikal/kernel.h b/linux/ps4-kexec-1302-pro-baikal/kernel.h new file mode 100644 index 0000000..1833f5b --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/kernel.h @@ -0,0 +1,126 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KERNEL_H +#define KERNEL_H + +#include "types.h" +#include "reboot.h" + +#define PAGE_SIZE 0x4000 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define PML4SHIFT 39 +#define PDPSHIFT 30 +#define PDRSHIFT 21 +#define PAGE_SHIFT 12 + +#define KVADDR(l4, l3, l2, l1) ( \ + ((unsigned long)-1 << 47) | \ + ((unsigned long)(l4) << PML4SHIFT) | \ + ((unsigned long)(l3) << PDPSHIFT) | \ + ((unsigned long)(l2) << PDRSHIFT) | \ + ((unsigned long)(l1) << PAGE_SHIFT)) + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) +#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX + +typedef u64 vm_paddr_t; +typedef u64 vm_offset_t; +typedef u64 vm_size_t; +typedef void * vm_map_t; +typedef char vm_memattr_t; +typedef void * pmap_t; + +typedef void (*smp_rendezvous_callback_t)(void *); + +struct sysent_t { + int sy_narg; + void *sy_call; + u16 sy_auevent; + void *sy_systrace_args_func; + int sy_entry; + int sy_return; + int sy_flags; + int sy_thrcnt; +}; + +struct ksym_t { + // two parameters related to kaslr (they are not symbols) + uintptr_t kern_base; + uintptr_t dmap_base; + + int (*printf)(const char *fmt, ...); + int (*snprintf)(const char *fmt, ...); + int (*copyin)(const void *uaddr, void *kaddr, size_t len); + int (*copyout)(const void *kaddr, void *uaddr, size_t len); + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); + + void **kernel_map; + void *kernel_pmap_store; + vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, + vm_paddr_t low, vm_paddr_t high, + unsigned long alignment, + unsigned long boundary, + vm_memattr_t memattr); + void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); + vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); + void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); + + struct sysent_t *sysent; + + void (*sched_pin)(void); + void (*sched_unpin)(void); + void (*smp_rendezvous)(smp_rendezvous_callback_t, + smp_rendezvous_callback_t, + smp_rendezvous_callback_t, void *); + // yes...it is misspelled :) + void (*smp_no_rendevous_barrier)(void *); + void *icc_query_nowait; + void *Starsha_UcodeInfo; + int (*gpu_devid_is_9924)(); + void *(*gc_get_fw_info)(); + void *eap_hdd_key; + void *edid; + void (*wlanbt)(unsigned int state); + int (*kern_reboot)(int magic); + void(*set_gpu_freq)(unsigned int num, unsigned int freq); + void(*set_pstate)(unsigned int val); + void(*update_vddnp)(unsigned int val); + void(*set_cu_power_gate)(unsigned int val); + void *pstate_before_shutdown; +}; + +extern struct ksym_t kern; + +static inline int curcpu(void) +{ + int cpuid; + // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw + asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); + return cpuid; +} + +// Assign a working printf function to this to debug the symbol resolver +extern int (*early_printf)(const char *fmt, ...); + +void *kernel_resolve(const char *name); + +void *kernel_alloc_contig(size_t size); +void kernel_free_contig(void *addr, size_t size); + +void kernel_remap(void *start, void *end, int perm); + +void kernel_syscall_install(int num, void *call, int narg); +int kernel_hook_install(void *target, void *hook); + +int kernel_init(void *early_printf); + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/kexec.c b/linux/ps4-kexec-1302-pro-baikal/kexec.c new file mode 100644 index 0000000..fa55dfe --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/kexec.c @@ -0,0 +1,229 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "linux_boot.h" +#include "x86.h" +#include "kexec.h" +#include "firmware.h" +#include "string.h" +#include "acpi.h" + +static int k_copyin(const void *uaddr, void *kaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(kaddr, uaddr, len); + return 0; +} + +static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + const char *ustr = (const char*)uaddr; + char *kstr = (char*)kaddr; + size_t ret; + if (!uaddr || !kaddr) + return EFAULT; + ret = strlcpy(kstr, ustr, len); + if (ret >= len) { + if (done) + *done = len; + return ENAMETOOLONG; + } else { + if (done) + *done = ret + 1; + } + return 0; +} + +static int k_copyout(const void *kaddr, void *uaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(uaddr, kaddr, len); + return 0; +} + +int sys_kexec(void *td, struct sys_kexec_args *uap) +{ + int err = 0; + size_t initramfs_size = uap->initramfs_size; + void *image = NULL; + void *initramfs = NULL; + size_t firmware_size = 0; + struct boot_params *bp = NULL; + size_t cmd_line_maxlen = 0; + char *cmd_line = NULL; + + int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; + int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; + + kern.printf("sys_kexec invoked\n"); + kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, + uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); + + // Look up our shutdown hook point + void *icc_query_nowait = kern.icc_query_nowait; + if (!icc_query_nowait) { + err = ENOENT; + goto cleanup; + } + + // Set gpu frequencies and pstate + // FAT&SLIM / PRO + kern.set_pstate(3); + + kern.set_gpu_freq(0, 800); //800 //800 + kern.set_gpu_freq(1, 853); //673 //853 + kern.set_gpu_freq(2, 711); //610 //711 + kern.set_gpu_freq(3, 800); //800 //800 + kern.set_gpu_freq(4, 911); //800 //911 + kern.set_gpu_freq(5, 800); //711 //800 + kern.set_gpu_freq(6, 984); //711 //984 + kern.set_gpu_freq(7, 673); //673 //673 + + kern.update_vddnp(0x12); + kern.set_cu_power_gate(0x24); + + // Copy in kernel image + image = kernel_alloc_contig(uap->image_size); + if (!image) { + kern.printf("Failed to allocate image\n"); + err = ENOMEM; + goto cleanup; + } + err = copyin(uap->image, image, uap->image_size); + if (err) { + kern.printf("Failed to copy in image\n"); + goto cleanup; + } + + // Copy in initramfs + initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); + if (!initramfs) { + kern.printf("Failed to allocate initramfs\n"); + err = ENOMEM; + goto cleanup; + } + + err = firmware_extract(((u8*)initramfs)); + if (err < 0) { + kern.printf("Failed to extract GPU firmware - continuing anyway\n"); + } else { + firmware_size = err; + } + + if (initramfs_size) { + err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); + if (err) { + kern.printf("Failed to copy in initramfs\n"); + goto cleanup; + } + } + initramfs_size += firmware_size; + + // Copy in cmdline + cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; + cmd_line = kernel_alloc_contig(cmd_line_maxlen); + if (!cmd_line) { + kern.printf("Failed to allocate cmdline\n"); + err = ENOMEM; + goto cleanup; + } + err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); + if (err) { + kern.printf("Failed to copy in cmdline\n"); + goto cleanup; + } + cmd_line[cmd_line_maxlen - 1] = 0; + + kern.printf("\nkexec parameters:\n"); + kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); + kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", + initramfs_size, uap->initramfs_size); + kern.printf(" Kernel command line: %s\n", cmd_line); + kern.printf(" Kernel image buffer: %p\n", image); + kern.printf(" Initramfs buffer: %p\n", initramfs); + + // Allocate our boot params + bp = kernel_alloc_contig(sizeof(*bp)); + if (!bp) { + kern.printf("Failed to allocate bp\n"); + err = ENOMEM; + goto cleanup; + } + + // Initialize bp + // TODO should probably do this from cpu_quiesce_gate, then bp doesn't + // need to be allocated here, just placed directly into low mem + set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); + + prepare_boot_params(bp, image); + + // Hook the final ICC shutdown function + if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { + kern.printf("Failed to install shutdown hook\n"); + err = EINVAL; + goto cleanup; + } + + kern.printf("******************************************************\n"); + kern.printf("kexec successfully armed. Please shut down the system.\n"); + kern.printf("******************************************************\n\n"); + +/* + kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); + if (kern.kern_reboot(RB_POWEROFF) == -1) + kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); +*/ + return 0; + +cleanup: + kernel_free_contig(cmd_line, cmd_line_maxlen); + kernel_free_contig(bp, sizeof(*bp)); + kernel_free_contig(image, uap->image_size); + kernel_free_contig(initramfs, uap->initramfs_size); + return err; + + copyout(NULL, NULL, 0); +} + +int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) +{ + int rv = 0; + + // potentially needed to write early_printf + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + + if (kernel_init(_early_printf) < 0) { + rv = -1; + goto cleanup; + } + + kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); + kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); + kern.printf("kexec_init() successful\n\n"); + + if (sys_kexec_ptr) + *sys_kexec_ptr = sys_kexec; + +cleanup: + write_protect_restore(wp); + if (kern.sched_unpin && wp & CR0_WP) { + // If we're returning to a state with WP enabled, assume the caller + // wants the thread unpinned. Else the caller is expected to + // call kern.sched_unpin() manually. + kern.sched_unpin(); + } + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1302-pro-baikal/kexec.h b/linux/ps4-kexec-1302-pro-baikal/kexec.h new file mode 100644 index 0000000..6c31637 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/kexec.h @@ -0,0 +1,38 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KEXEC_H +#define KEXEC_H + +#include "types.h" + +#define SYS_KEXEC 153 +#define SYS_KEXEC_NARGS 6 + +struct sys_kexec_args { + void *image; + size_t image_size; + void *initramfs; + size_t initramfs_size; + char *cmd_line; + int vram_gb; +}; + +typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); + +// Note: td is unused, you can pass NULL if you call this directly. +int sys_kexec(void *td, struct sys_kexec_args *uap); + +int kernel_init(void *early_printf); + +int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) + __attribute__ ((section (".init"))); + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/kexec.ld b/linux/ps4-kexec-1302-pro-baikal/kexec.ld new file mode 100644 index 0000000..1bcf26c --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/kexec.ld @@ -0,0 +1,25 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +ENTRY(kexec_init) + +SECTIONS { + /* We don't do GOT relocation and rely on nothing ending up using the GOT + * (-fno-common helps here) */ + /DISCARD/ : { *(.comment) *(.got) } + _start = .; + .init : { *(.init) *(.init.*) } + .text : { *(.text) *(.text.*) } + .data : { *(.data) *(.data.*) } + .rodata : { *(.rodata) *(.rodata.*) } + .bss : { *(.bss) *(.bss.*) *(COMMON)} + .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ + _end = .; +} diff --git a/linux/ps4-kexec-1302-pro-baikal/linux_boot.c b/linux/ps4-kexec-1302-pro-baikal/linux_boot.c new file mode 100644 index 0000000..226dd4f --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/linux_boot.c @@ -0,0 +1,453 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "linux_boot.h" +#include "types.h" +#include "string.h" +#include "x86.h" +#include "kernel.h" +#include "uart.h" +#include "acpi.h" + +void uart_write_byte(u8 b); + +static u64 vram_base = 0x100000000; +// Current code assumes it's a power of two. +static u64 vram_size = 1024 * 1024 * 1024; +static int vram_gb = 2; + +#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) + +#define MSR_GS_BASE 0xc0000101 /* 64bit GS base */ + +struct desc_struct { + u16 limit0; + u16 base0; + u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; + u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, + uintptr_t new_cr3, uintptr_t gdt_ptr); +extern uint8_t *jmp_to_linux; +extern size_t jmp_to_linux_size; + +// FreeBSD DMAP addresses +struct linux_boot_info { + void *linux_image; + void *initramfs; + size_t initramfs_size; + struct boot_params *bp; + char *cmd_line; +}; +static struct linux_boot_info nix_info; + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v) +{ + nix_info.linux_image = linux_image; + nix_info.bp = bp; + nix_info.initramfs = initramfs; + nix_info.initramfs_size = initramfs_size; + nix_info.cmd_line = cmd_line; + vram_gb = v; +} + +static volatile int halted_cpus = 0; + +static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, + u32 type) +{ + uint8_t idx = bp->e820_entries; + bp->e820_map[idx].addr = addr; + bp->e820_map[idx].size = size; + bp->e820_map[idx].type = type; + bp->e820_entries++; +} + +void prepare_boot_params(struct boot_params *bp, u8 *linux_image) +{ + memset(bp, 0, sizeof(struct boot_params)); + struct boot_params *bp_src = (struct boot_params *)linux_image; + memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + + ((u8 *)&bp_src->hdr.jump)[1]); + + // These values are from fw 1.01 + bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); + bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + // This used to be VRAM, but we reclaim it as RAM + bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); + // Instead, carve out VRAM from the beginning of high memory + bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, + SMAP_TYPE_MEMORY); +} + +#define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_FB_OFFSET 0x2068 +#define HDP_NONSURFACE_BASE 0x2c04 +#define CONFIG_MEMSIZE 0x5428 + +static void configure_vram(void) +{ + u64 mmio_base = 0xe4800000; + u64 fb_base = 0x0f00000000; + u64 fb_top = fb_base + vram_gb * vram_size - 1; + + WR32(mmio_base + MC_VM_FB_LOCATION, 0); + WR32(mmio_base + HDP_NONSURFACE_BASE, 0); + + WR32(mmio_base + MC_VM_FB_LOCATION, + ((fb_top >> 24) << 16) | (fb_base >> 24)); + WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); + WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); + WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); +} + +#define IA32_MTRR_DEF_TYPE 0x2ff +#define MTRR_BASE(i) (0x200 + 2*i) +#define MTRR_MASK(i) (0x201 + 2*i) + +static void setup_mtrr(void) +{ + disable_interrupts(); + u64 cr0 = cr0_read(); + cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); + wbinvd(); + cr3_write(cr3_read()); // TLB flush + + wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled + + // Low memory (0GB-2GB) = WB + wrmsr(MTRR_BASE(0), 0x0000000006); + wrmsr(MTRR_MASK(0), 0xff80000800); + // High memory (4GB-8GB) = WB + wrmsr(MTRR_BASE(1), 0x0100000006); + wrmsr(MTRR_MASK(1), 0xff00000800); + // High memory (8GB-10GB) = WB + wrmsr(MTRR_BASE(2), 0x0200000006); + wrmsr(MTRR_MASK(2), 0xff80000800); + // VRAM (4GB-4GB+vram_size) = UC + wrmsr(MTRR_BASE(3), 0x0100000000); + wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); + + wbinvd(); + cr3_write(cr3_read()); // TLB flush + wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable + cr0_write(cr0); + enable_interrupts(); +} + +static void cleanup_interrupts(void) +{ + int i; + disable_interrupts(); + + // Reset APIC stuff (per-CPU) + *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; + for (i = 0x320; i < 0x380; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + for (i = 0x480; i < 0x500; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; + for (i = 0x500; i < 0x540; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; + + // Fix the LVT offset for thresholding + wrmsr(0x413, (1L<<24) | (1L<<52)); + wrmsr(0xc0000408, (1L<<24) | (1L<<52)); +} + +#define DEFAULT_STACK 0 + +#define DPL0 0x0 +#define DPL3 0x3 + +#define BPCIE_BAR2 0xc8800000 +#define BPCIE_HPET_BASE 0x109000 +#define BPCIE_HPET_SIZE 0x400 + +static void cpu_quiesce_gate(void *arg) +{ + + + // Ensure we can write anywhere + cr0_write(cr0_read() & ~CR0_WP); + + // Interrupt stuff local to each CPU + cleanup_interrupts(); + + // We want to set up MTRRs on all CPUs + setup_mtrr(); + + if (curcpu() != 0) { + // We're not on BSP. Try to halt. + __sync_fetch_and_add(&halted_cpus, 1); + cpu_stop(); + } + + uart_write_str("kexec: Waiting for secondary CPUs...\n"); + + // wait for all cpus to halt + while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); + + uart_write_str("kexec: Secondary CPUs quiesced\n"); + + //* Put ident mappings in current page tables + // Should not be needed, but maybe helps for debugging? + cr4_pge_disable(); + u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); + u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + // Clear (really) low mem. + // Linux reads from here to try and access EBDA... + // get_bios_ebda reads u16 from 0x40e + // reserve_ebda_region reads u16 from 0x413 + // Writing zeros causes linux to default to marking + // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. + // It doesn't match the ps4 e820 map, but that seems OK. + memset((void *)0, 0, 0x1000); + + // Create a new page table hierarchy out of the way of linux + // Accessed via freebsd direct map + pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt + // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| + memset(pml4_base, 0, 512 * sizeof(u64) * 2); + pdp_base = pml4_base + 512; + u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; + pml4_base[0] = pdpe; + // Maintain the freebsd direct map + pml4_base[DM_PML4_BASE] = pdpe; + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + uart_write_str("kexec: Setting up GDT...\n"); + + desc_ptr gdt_ptr; + struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); + gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; + gdt_ptr.address = DM_TO_ID(desc); + + // clear + memset(desc, 0, gdt_ptr.limit + 1); + // Most things are ignored in 64bit mode, and we will never be in + // 32bit/compat modes, so just setup another pure-64bit environment... + // Linux inits it's own GDT in secondary_startup_64 + // 0x10 + desc[2].limit0 = 0xffff; + desc[2].base0 = 0x0000; + desc[2].base1 = 0x0000; + desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; + desc[2].s = 1; + desc[2].dpl = 0; + desc[2].p = 1; + desc[2].limit = 0xf; + desc[2].avl = 0; + desc[2].l = 1; + desc[2].d = 0; + desc[2].g = 0; + desc[2].base2 = 0x00; + // 0x18 + desc[3].limit0 = 0xffff; + desc[3].base0 = 0x0000; + desc[3].base1 = 0x0000; + desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; + desc[3].s = 1; + desc[3].dpl = 0; + desc[3].p = 1; + desc[3].limit = 0xf; + desc[3].avl = 0; + desc[3].l = 0; + desc[3].d = 0; + desc[3].g = 0; + desc[3].base2 = 0x00; + // Task segment value + // 0x20 + desc[4].limit0 = 0x0000; + desc[4].base0 = 0x0000; + desc[4].base1 = 0x0000; + desc[4].type = SEG_TYPE_TSS; + desc[4].s = 1; + desc[4].dpl = 0; + desc[4].p = 1; + desc[4].limit = 0x0; + desc[4].avl = 0; + desc[4].l = 0; + desc[4].d = 0; + desc[4].g = 0; + desc[4].base2 = 0x00; + + uart_write_str("kexec: Relocating stub...\n"); + + // Relocate the stub and jump to it + // TODO should thunk_copy be DMAP here? + void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); + memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); + // XXX The +0x200 is for the iret stack in linux_thunk.S + uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; + + uart_write_str("kexec: Setting up boot params...\n"); + + // XXX we write into this bootargs and pass it to the kernel, but in + // jmp_to_linux we use the bootargs from the image as input. So they + // MUST MATCH! + struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; + *bp_lo = *nix_info.bp; + lowmem_pos += sizeof(struct boot_params); + + struct setup_header *shdr = &bp_lo->hdr; + shdr->cmd_line_ptr = lowmem_pos; + shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; + shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; + bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; + bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; + shdr->hardware_subarch = X86_SUBARCH_PS4; + // This needs to be nonzero for the initramfs to work + shdr->type_of_loader = 0xd0; // kexec + + strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, + nix_info.bp->hdr.cmdline_size); + lowmem_pos += strlen(nix_info.cmd_line) + 1; + + uart_write_str("kexec: Cleaning up hardware...\n"); + + // Disable IOMMU + *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; + + // Disable all MSIs on Baikal (bus=0, slot=20) + disableMSI(0xf80a00e0); //func = 0 Baikal ACPI + disableMSI(0xf80a10e0); //func = 1 Baikal Ethernet Controller + disableMSI(0xf80a20e0); //func = 2 Baikal SATA AHCI Controller + disableMSI(0xf80a30e0); //func = 3 Baikal SD/MMC Host Controller + disableMSI(0xf80a40e0); //func = 4 Baikal PCI Express Glue and Miscellaneous Devices + disableMSI(0xf80a50e0); //func = 5 Baikal DMA Controller + disableMSI(0xf80a60e0); //func = 6 Baikal Baikal Memory (DDR3/SPM) + disableMSI(0xf80a70e0); //func = 7 Baikal Baikal USB 3.0 xHCI Host Controller + + // Stop HPET timers + //*(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + 0x10) &= ~(1UL << 0); //General Configuration Register + /* + u64 NUM_TIM_CAP = *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE) & 0x1F00; + for (u64 N = 0; N <= NUM_TIM_CAP; N++) { + *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + (0x20*N) + 0x100) &= ~(1UL << 2); //Timer N Configuration and Capabilities Register + } + */ + uart_write_str("kexec: Reconfiguring VRAM...\n"); + + configure_vram(); + + uart_write_str("kexec: Resetting GPU...\n"); + + // Softreset GPU + *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks + *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC + *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 +// *(volatile u64 *)PA_TO_DM(0xe480d248) = 1; // Halt SDMA1 eeply + *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 + *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC + + *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply + +// *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x10003; // Softreset GFX/CP/RLC + *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x30005; // Softreset GFX/CP/RLC eeply + +// udelay(150); +// *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x10003; + *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x30005; //eeply +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) |= 0x00100140; // Softreset SDMA/GRBM +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~0x00100140; +// udelay(150); + + // Enable audio output + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; + *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; + +// // Set pin caps of pin 2 to vendor defined, to hide it +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// // Set pin caps of pin 3 to !HDMI +// *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; +// *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; + // Set pin configuration default + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; + + uart_write_str("kexec: About to relocate and jump to kernel\n"); + + ((jmp_to_linux_t)thunk_copy)( + DM_TO_ID(nix_info.linux_image), + DM_TO_ID(bp_lo), + DM_TO_ID(pml4_base), + (uintptr_t)&gdt_ptr + ); + // should never reach here + uart_write_str("kexec: unreachable (?)\n"); +} + +// Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) +int hook_icc_query_nowait(u8 *icc_msg) +{ + kern.printf("hook_icc_query_nowait called\n"); + + // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot + //In alternative we can re-enable it here, but sometimes that give problems.. + kern.wlanbt(0x2); + + fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); + + kern.printf("ACPI tables fixed\n"); + + // Transition to BSP and halt other cpus + // smp_no_rendevous_barrier is just nullsub, but it is treated specially by + // smp_rendezvous. This is the easiest way to do this, since we can't assume + // we're already running on BSP. Since smp_rendezvous normally waits on all + // cpus to finish the callbacks, we just never return... + kern.smp_rendezvous(kern.smp_no_rendevous_barrier, + cpu_quiesce_gate, + kern.smp_no_rendevous_barrier, NULL); + + // should never reach here + kern.printf("hook_icc_query_nowait: unreachable (?)\n"); + return 0; +} diff --git a/linux/ps4-kexec-1302-pro-baikal/linux_boot.h b/linux/ps4-kexec-1302-pro-baikal/linux_boot.h new file mode 100644 index 0000000..2232b35 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/linux_boot.h @@ -0,0 +1,88 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef LINUX_BOOT_H +#define LINUX_BOOT_H + +#include "types.h" + +#define SMAP_TYPE_MEMORY 1 +#define SMAP_TYPE_RESERVED 2 +#define SMAP_TYPE_ACPI_RECLAIM 3 +#define SMAP_TYPE_ACPI_NVS 4 +#define SMAP_TYPE_UNUSABLE 5 +#define SMAP_TYPE_PMEM 7 + +#define X86_SUBARCH_PS4 5 + +struct e820entry { + u64 addr; /* start of memory segment */ + u64 size; /* size of memory segment */ + u32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct setup_header { + u8 setup_sects; + u16 root_flags; + u32 syssize; + u16 ram_size; + u16 vid_mode; + u16 root_dev; + u16 boot_flag; + u16 jump; + u32 header; + u16 version; + u32 realmode_swtch; + u16 start_sys; + u16 kernel_version; + u8 type_of_loader; + u8 loadflags; + u16 setup_move_size; + u32 code32_start; + u32 ramdisk_image; + u32 ramdisk_size; + u32 bootsect_kludge; + u16 heap_end_ptr; + u8 ext_loader_ver; + u8 ext_loader_type; + u32 cmd_line_ptr; + u32 initrd_addr_max; + u32 kernel_alignment; + u8 relocatable_kernel; + u8 min_alignment; + u16 xloadflags; + u32 cmdline_size; + u32 hardware_subarch; + u64 hardware_subarch_data; + u32 payload_offset; + u32 payload_length; + u64 setup_data; + u64 pref_address; + u32 init_size; + u32 handover_offset; +} __attribute__((packed)); + +#define E820MAX 128 /* number of entries in E820MAP */ + +OSTRUCT(boot_params, 0x1000) +OFIELD(0x0c0, u32 ext_ramdisk_image); +OFIELD(0x0c4, u32 ext_ramdisk_size); +OFIELD(0x0c8, u32 ext_cmd_line_ptr); +OFIELD(0x1e8, u8 e820_entries); +OFIELD(0x1f1, struct setup_header hdr); +OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); +OSTRUCT_END + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v); +void prepare_boot_params(struct boot_params *bp, u8 *linux_image); +int hook_icc_query_nowait(u8 *icc_msg); + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/linux_thunk.S b/linux/ps4-kexec-1302-pro-baikal/linux_thunk.S new file mode 100644 index 0000000..551964e --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/linux_thunk.S @@ -0,0 +1,90 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +.intel_syntax noprefix + +.equ setup_sects, 0x1f1 +.equ shdr_syssize, 0x1f4 +.equ pref_address, 0x258 + +.text + +#void jmp_to_linux( +# uintptr_t image_base, rdi +# uintptr_t bootargs, rsi +# uintptr_t new_cr3, rdx +# uintptr_t gdt_ptr rcx +#); +.globl jmp_to_linux +jmp_to_linux: + # switch to new gdt + data segments + cli + lgdt [rcx] + #xor eax, eax + mov eax, 0x18 + mov ds, eax + mov ss, eax + mov es, eax + mov fs, eax + mov gs, eax + + # switch to our own page tables (in low mem) + mov cr3, rdx + + # now we're on our own page tables, so we can obliterate the rest of memory + # TODO make sure we don't inadvertently overwrite (important) smap regions + # I think on ps4 we'll actually want to load to 0x700000 + # since we have tons of free room there. + # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We + # should choose a higher address as pref_address. + + # save args + mov r12, rdi + mov r13, rsi + + # memmove(pref_address, , (syssize * 0x10) / 8) + #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go + #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) + mov rdi, 0x6000000 # should be far from bzImage and initramfs + mov r14, rdi # r14 = pref_address + xor edx, edx + mov dl, [r12 + setup_sects] + inc rdx + shl rdx, 9 # rdx = offsetof(image_base, startup_32) + lea rsi, [r12 + rdx] # src = image_base + startup_32 + mov ecx, [r12 + shdr_syssize] + shl rcx, 4 + add rdi, rcx + add rsi, rcx + sub rdi, 8 + sub rsi, 8 + shr rcx, 3 + std + rep movsq + cld + + # make a tiny stack - we just need it for the lretq. + # what we jump to will not use this stack + lea rsp, [rip + jmp_to_linux_end + 0x200] + and rsp, -0x10 + #push 0 # retaddr + push 0x10 # cs = GDT[2] + add r14, 0x200 # pref_address + startup_64 + push r14 # rip + mov rsi, r13 # bootargs + lretq +jmp_to_linux_end: + +.data + +.globl jmp_to_linux_size +jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux + +.att_syntax prefix \ No newline at end of file diff --git a/linux/ps4-kexec-1302-pro-baikal/reboot.h b/linux/ps4-kexec-1302-pro-baikal/reboot.h new file mode 100644 index 0000000..20b91f8 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/reboot.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)reboot.h 8.3 (Berkeley) 12/13/94 + * $FreeBSD$ + */ + +#ifndef _SYS_REBOOT_H_ +#define _SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. These are passed to + * the boot program and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ +#define RB_SINGLE 0x002 /* reboot to single user only */ +#define RB_NOSYNC 0x004 /* dont sync before reboot */ +#define RB_HALT 0x008 /* don't reboot, just halt */ +#define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ +#define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ +#define RB_KDB 0x040 /* give control to kernel debugger */ +#define RB_RDONLY 0x080 /* mount root fs read-only */ +#define RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define RB_MINIROOT 0x200 /* Unused placeholder */ +#define RB_VERBOSE 0x800 /* print all potentially useful info */ +#define RB_SERIAL 0x1000 /* use serial port as console */ +#define RB_CDROM 0x2000 /* use cdrom as root */ +#define RB_POWEROFF 0x4000 /* turn the power off if possible */ +#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ +#define RB_MUTE 0x10000 /* start up with the console muted */ +#define RB_SELFTEST 0x20000 /* unused placeholder */ +#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ +#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ +#define RB_PAUSE 0x100000 /* pause after each output line during probe */ +#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ +#define RB_PROBE 0x10000000 /* Probe multiple consoles */ +#define RB_MULTIPLE 0x20000000 /* use multiple consoles */ + +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/string.h b/linux/ps4-kexec-1302-pro-baikal/string.h new file mode 100644 index 0000000..70aa668 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/string.h @@ -0,0 +1,96 @@ +/* + * string.h -- standard C string-manipulation functions. + * + * Copyright (C) 2008 Segher Boessenkool + * Copyright (C) 2009 Haxx Enterprises + * Copyright (C) 2010-2016 Hector Martin "marcan" + * + * Portions taken from the Public Domain C Library (PDCLib). + * http://pdclib.rootdirectory.de/ + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +static inline int strcmp(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s1[i] == s2[i]; i++) + ; + + return s1[i] - s2[i]; +} + +static inline void *memset(void *b, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)b)[i] = c; + + return b; +} + +static inline void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} + +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) + return p1[i] - p2[i]; + + return 0; +} + +static inline size_t strlen(const char *s) +{ + size_t len; + + for (len = 0; s[len]; len++) + ; + + return len; +} + +static inline size_t strnlen(const char *s, size_t count) +{ + size_t len; + + for (len = 0; s[len] && len < count; len++) + ; + + return len; +} + +static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) +{ + size_t len, needed; + + len = needed = strnlen(src, maxlen - 1) + 1; + if (len >= maxlen) + len = maxlen - 1; + + memcpy(dest, src, len); + dest[len] = 0; + + return needed - 1; +} + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/types.h b/linux/ps4-kexec-1302-pro-baikal/types.h new file mode 100644 index 0000000..28d6619 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/types.h @@ -0,0 +1,51 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef TYPES_H +#define TYPES_H + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#ifndef TESTING +typedef u8 uint8_t; +typedef u64 size_t; +typedef s64 ssize_t; +typedef u64 uintptr_t; +typedef s64 off_t; +#endif + +#define NULL ((void *)0) + +#define CAT_(x, y) x ## y +#define CAT(x, y) CAT_(x, y) + +#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] +#define OSTRUCT(name, size) struct name { union { OPAD(size); +#define OSTRUCT_END };}; +#define OFIELD(off, field) struct { OPAD(off); field; } + +#define ASSERT_STRSIZE(struc, size) \ + _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#define ENOENT 2 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define ENAMETOOLONG 63 + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/uart.c b/linux/ps4-kexec-1302-pro-baikal/uart.c new file mode 100644 index 0000000..a401434 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/uart.c @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "uart.h" +#include "kernel.h" +#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) + +#define AEOLIA_UART_BASE 0xD0340000 +#define BAIKAL_UART_BASE 0xC890E000 + +#define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, BAIKAL_UART_BASE + (intf << 12) + (reg << 2)) + +#define UART_REG_DATA 0 +#define UART_REG_IER 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +# define LSR_TXRDY 0x20 +# define LSR_TEMT 0x40 +#define UART_REG_MSR 6 +#define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) +#define UART0_IER UART_REG(32, 0, UART_REG_IER) +#define UART0_IIR UART_REG(32, 0, UART_REG_IIR) +#define UART0_LCR UART_REG(32, 0, UART_REG_LCR) +#define UART0_MCR UART_REG(32, 0, UART_REG_MCR) +#define UART0_LSR UART_REG(32, 0, UART_REG_LSR) +#define UART0_MSR UART_REG(32, 0, UART_REG_MSR) + +void uart_write_byte(u8 b) +{ + int limit; + u64 barrier; + limit = 250000; + while (!(*UART0_LSR & LSR_TXRDY) && --limit) + ; + *UART0_DATA = b; + __sync_fetch_and_add(&barrier, 0); + limit = 250000; + while (!(*UART0_LSR & LSR_TEMT) && --limit) + ; +} + +void uart_write_char(char c) +{ + if (c == '\n') + uart_write_byte('\r'); + + uart_write_byte(c); +} + +void uart_write_str(const char *s) +{ + while (*s) { + uart_write_char(*s++); + } +} diff --git a/linux/ps4-kexec-1302-pro-baikal/uart.h b/linux/ps4-kexec-1302-pro-baikal/uart.h new file mode 100644 index 0000000..b803e29 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/uart.h @@ -0,0 +1,20 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef UART_H +#define UART_H + +#include "types.h" + +void uart_write_byte(u8 b); +void uart_write_char(char c); +void uart_write_str(const char *s); + +#endif diff --git a/linux/ps4-kexec-1302-pro-baikal/x86.h b/linux/ps4-kexec-1302-pro-baikal/x86.h new file mode 100644 index 0000000..2f6d8b5 --- /dev/null +++ b/linux/ps4-kexec-1302-pro-baikal/x86.h @@ -0,0 +1,195 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef X86_H +#define X86_H + +#define FLAGS_IF (1 << 9) + +#define CR0_WP (1 << 16) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) + +#define PG_V (1 << 0) +#define PG_RW (1 << 1) +#define PG_U (1 << 2) +#define PG_PS (1 << 7) + +#define SEG_TYPE_DATA (0 << 3) +#define SEG_TYPE_READ_WRITE (1 << 1) +#define SEG_TYPE_CODE (1 << 3) +#define SEG_TYPE_EXEC_READ (1 << 1) +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) + +static inline u64 cr0_read(void) +{ + u64 reg; + asm volatile("mov %0, cr0;" : "=r" (reg)); + return reg; +} + +static inline void cr0_write(u64 val) +{ + asm volatile("mov cr0, %0;" :: "r" (val)); +} + +static inline u64 write_protect_disable() +{ + u64 cr0 = cr0_read(); + cr0_write(cr0 & ~CR0_WP); + return cr0; +} + +static inline void write_protect_restore(u64 cr0) +{ + // Use only WP bit of input + cr0_write(cr0_read() | (cr0 & CR0_WP)); +} + +static inline u64 cr3_read(void) +{ + u64 reg; + asm volatile("mov %0, cr3;" : "=r" (reg)); + return reg; +} + +static inline void cr3_write(u64 val) +{ + asm volatile("mov cr3, %0;" :: "r" (val)); +} + +typedef struct { + u16 limit; + u64 address; +} __attribute__((packed)) desc_ptr; + +static inline desc_ptr gdt_read(void) +{ + desc_ptr gdtr; + asm volatile("sgdt %0;" : "=m" (gdtr)); + return gdtr; +} + +static inline void gdt_write(desc_ptr* val) +{ + asm volatile("lgdt %0;" :: "m" (*val)); +} + +//IDT +typedef struct { + u16 limit; + u64 address; +} __attribute__((packed)) idt_ptr; + +static inline idt_ptr idt_read(void) +{ + idt_ptr idtr; + asm volatile("sidt %0;" : "=m" (idtr)); + return idtr; +} + +static inline void idt_write(idt_ptr* val) +{ + asm volatile("lidt %0;" :: "m" (*val)); +} + +static inline void cr4_pge_disable(void) +{ + u64 cr4_temp; + asm volatile( + "mov %0, cr4;" + "and %0, ~0x80;" + "mov cr4, %0;" + : "=r" (cr4_temp) + ); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +static inline void cpu_stop(void) +{ + for (;;) + asm volatile("cli; hlt;" : : : "memory"); +} + +static inline void outl(int port, unsigned int data) +{ + asm volatile("out %w1, %0" : : "a" (data), "d" (port)); +} + +static inline void wrmsr(u32 msr_id, u64 msr_value) +{ + asm volatile( + "wrmsr" + : + : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) + ); +} + +static inline u64 rdmsr(u64 msr_id) +{ + u32 low, high; + asm volatile ( + "rdmsr" + : "=a"(low), "=d"(high) + : "c"(msr_id) + ); + return ((u64)high << 32) | low; +} + +static inline u64 rdtsc (void) +{ + unsigned int tickl, tickh; + asm volatile( + "rdtsc" + :"=a"(tickl),"=d"(tickh) + ); + return ((u64)tickh << 32) | tickl; +} + +static inline void udelay(unsigned int usec) { + u64 later = rdtsc() + usec * 1594ULL; + while (((s64)(later - rdtsc())) > 0); +} + +static inline void disable_interrupts(void) +{ + asm volatile("cli"); +} + +static inline void enable_interrupts(void) +{ + asm volatile("sti"); +} + +static inline u64 read_flags(void) +{ + u64 flags; + asm volatile("pushf; pop %0;" : "=r" (flags)); + return flags; +} + +static inline u64 intr_disable(void) +{ + u64 flags = read_flags(); + disable_interrupts(); + return flags; +} + +static inline void intr_restore(u64 flags) +{ + // TODO should only IF be or'd in? + asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); +} + +#endif diff --git a/linux/ps4-kexec-1302-pro/LICENSE b/linux/ps4-kexec-1302-pro/LICENSE new file mode 100644 index 0000000..6c1cbba --- /dev/null +++ b/linux/ps4-kexec-1302-pro/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2015-2016 shuffle2 +Copyright (C) 2015-2016 Hector Martin "marcan" +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/ps4-kexec-1302-pro/Makefile b/linux/ps4-kexec-1302-pro/Makefile new file mode 100644 index 0000000..41fe2d6 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/Makefile @@ -0,0 +1,37 @@ +TOOLCHAIN_PREFIX ?= +CC = $(TOOLCHAIN_PREFIX)gcc +AR = $(TOOLCHAIN_PREFIX)ar +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy + +CFLAGS=$(CFLAG) -DPS4_13_02 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX +CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ + -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ + -fno-asynchronous-unwind-tables \ + -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ + -mcmodel=small -mno-red-zone + +SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ + acpi.c crc32.c + +OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) +DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld + +all: libkexec.a kexec.bin + +%.o: %.c *.h + $(CC) -c $(CFLAGS) -o $@ $< + +%.o: %.S + $(CC) -c $(CFLAGS) -o $@ $< + +libkexec.a: $(OBJS) + $(AR) -rc $@ $(OBJS) + +kexec.elf: libkexec.a kexec.ld + $(CC) $(CFLAGS) -o $@ libkexec.a + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +clean: + rm -f libkexec.a kexec.elf kexec.bin $(OBJS) diff --git a/linux/ps4-kexec-1302-pro/README.md b/linux/ps4-kexec-1302-pro/README.md new file mode 100644 index 0000000..6500878 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/README.md @@ -0,0 +1,121 @@ +# PS4 kexec implementation + +This repo implements a kexec()-style system call for the PS4 Orbis kernel +(FreeBSD derivative). This is designed to boot a Linux kernel directly from +FreeBSD. + +This is not an exploit. It is useless without some mechanism of injecting code +into the PS4 OS kernel. + +## Building + +To build a kexec.bin relocatable binary using the supplied Makefile, just type +`make`. This will also build a kexec.a archive. You can either use the binary +directly, or link the archive into your own project. + +If you link kexec.a with your own code, you need to supply the two symbols +`_start` and `_end` in your linker script, as `kernel_init()` will try to remap +all pages covered by that range as RWX (to make global variable accesses work). +Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this +feature, if you have already taken care of page permissions for the code. + +If you use a compiler toolchain that have a special prefix you can declare it +by passing TOOLCHAIN_PREFIX option to the Makefile like this: + + make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' + +## Usage + +The code is designed to be completely standalone. There is a single entry point: + + int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); + +Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel +symbols and install the sys_kexec system call. The syscall is registered +as number 153 by default (you can change this in kexec.h). The return value +is 0 on success, or negative on error. + +You may pass something other than NULL as `early_printf`. In that case, that +function will be used for debug output during early symbol resolution, before +printf is available. + +Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is +enabled by default, symtab also disappears in newer kernel, we have to +hardcode offsets for some symbols. Currently we use the `early_printf` +given by user to caculate the base address of kernel, then relocate all the +symbols from the kernel base. You could enable this feature like this: + + make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' + +If you do not want to call the syscall from userspace, you can pass the address +of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the +address of `sys_kexec`, so you can invoke it manually (see kexec.h for +its prototype and how the arguments are passed). + +If you are using the standalone kexec.bin blob, then the `kexec_init` function +is always located at offset 0, so simply call the base address of the blob. +Don't forget to pass two NULL arguments (or the appropriate pointers). + +The injected `sys_kexec` system call takes (userspace) pointers to the kernel +and initramfs blobs, their sizes, and a pointer to the (null-terminated) command +line string. From userspace, this looks like this: + + int kexec(void *kernel_image, size_t image_size, + void *initramfs, size_t initramfs_size, + const char *cmdline); + + // syscall() usage: + syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); + +`kexec()` will load the kernel and initramfs into memory, but will not directly +boot them. To boot the loaded kernel, shut down the system. This can be +accomplished by pressing the power button, but can also be done more quickly +and reliably from userspace with the following sequence of system calls (this +kills userspace quickly but still does a controlled filesystem unmount): + + int evf = syscall(540, "SceSysCoreReboot"); + syscall(546, evf, 0x4000, 0); + syscall(541, evf); + // should be syscall(37, 1, 30) but only tested via kill symbol + kill(1, 30); + +Note that this software should be loaded into kernel memory space. If you are +running kernel code from userland mappings, you should either switch to kernel +mappings or separately copy kexec.bin to a location in kernel address space. +While syscalls or exploit code may run properly from userland, the shutdown hook +will not, as it will be called from a different process context. + +## Features + +`kernel_init()` will automatically find the Orbis OS kernel and resolve all +necessary symbols to work. There are no static symbol dependencies. If +`DO_NOT_REMAP_RWX` is not defined (the default), it will also patch +`pmap_protect` to disable the W^X restriction. + +In addition to loading the user-supplied initramfs, `kexec` will locate the +Radeon firmware blobs inside Orbis OS, extract them, convert them to a format +suitable for Linux, and append them as an additional initramfs cpio image to +the existing initramfs. This avoids the need to distribute the Radeon firmware +blobs. The `radeon` module, when compiled into the kernel, will automatically +load this firmware on boot. Note however that most typical initramfs scripts +will wipe the initramfs contents while pivoting to the real system, so if you +compile `radeon` as a module you may not be able to access the firmware after +boot. To cover that case, add some code to your initramfs `/init` script to +copy the firmware to a tmpfs mounted on the real filesystem: + + # assuming real root FS is mounted on /mnt + + mkdir -p /mnt/lib/firmware/radeon + mount -t tmpfs none /mnt/lib/firmware/radeon + cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ + + # now switch_root to /mnt + +This avoids having to permanently store copies of the Radeon firmware, which +isn't really necessary for most use cases. + +There is significant debug logging available, which will appear on the system +UART. Most of the code relies on the kernel `printf` implementation, and +therefore you should patch out the UART output blanker to see it. The final +code that runs on the boot CPU before booting the kernel uses direct UART +writes and is not affected by the blanking feature of Orbis OS. diff --git a/linux/ps4-kexec-1302-pro/acpi.c b/linux/ps4-kexec-1302-pro/acpi.c new file mode 100644 index 0000000..f53f04e --- /dev/null +++ b/linux/ps4-kexec-1302-pro/acpi.c @@ -0,0 +1,296 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "types.h" + +#ifdef TESTING +# include +# include +# include +# include +# include +# include +#else +# include "kernel.h" +# include "string.h" +# define printf kern.printf +#endif + +#define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) +#define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) + +#define PACKED __attribute__((packed)) + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +struct RSDP { + u64 sig; + u8 checksum; + u8 oemid[6]; + u8 rev; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 rsvd[3]; +} PACKED; + +struct SDTH { + u32 sig; + u32 length; + u8 rev; + u8 checksum; + u8 oem_id[6]; + u8 oem_tid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; +} PACKED; + +struct RSDT { + struct SDTH hdr; + u32 table_addr[]; +} PACKED; + +struct XSDT { + struct SDTH hdr; + u64 table_addr[]; +} PACKED; + +struct FADT { + struct SDTH hdr; + u32 facs; + u32 dsdt; + // more stuff... +} PACKED; + +struct ivhd_entry4 { + u8 type; + u16 devid; + u8 flags; +} PACKED; + +struct ivhd_header { + u8 type; + u8 flags; + u16 length; + u16 devid; + u16 cap_ptr; + u64 mmio_phys; + u16 pci_seg; + u16 info; + u32 efr_attr; +} PACKED; + +struct IVRS { + struct SDTH hdr; + u32 IVinfo; + u8 reserved[8]; + struct ivhd_header hd_hdr; + struct ivhd_entry4 hd_entries[3]; +} PACKED; + +// We have enough space to use the second half of the 64KB table area +// as scratch space for building the tables +#define BUFFER_OFF 0x8000 + +#define P2M(p) (((u64)(p)) - phys_base + map_base) +#define M2P(p) ((((void*)(p)) - map_base) + phys_base) +#define B2P(p) ((((void*)(p)) - buf_base) + phys_base) + +#define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) +#define PADB(s) p += (s) +#define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) +#define ALLOC(t) (t)ALLOCB(sizeof(t)) +#define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) +#define COPYT(s) COPYB(((struct SDTH*)s)->length, s) +#define COPYTP(s) COPYT(P2M(s)) +#define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) +#define COPYP(t, s) COPY(t, P2M(s)) + +static void rsdp_checksum(struct RSDP *rsdp) { + rsdp->checksum = rsdp->ext_checksum = 0; + + u8 sum = 0; + for (int i = 0; i < 20; i++) + sum += ((u8*)rsdp)[i]; + rsdp->checksum = -sum; + sum = 0; + for (int i = 0; i < sizeof(*rsdp); i++) + sum += ((u8*)rsdp)[i]; + rsdp->ext_checksum = -sum; +} + +static void table_checksum(void *table) { + struct SDTH *hdr = table; + hdr->checksum = 0; + u8 sum = 0; + for (int i = 0; i < hdr->length; i++) + sum += ((u8*)table)[i]; + hdr->checksum = -sum; +} + +#define IVHD_FLAG_ISOC_EN_MASK 0x08 +#define IVHD_DEV_ALL 0x01 +#define IVHD_DEV_SELECT 0x02 +#define IVHD_DEV_SELECT_RANGE_START 0x03 +#define IVHD_DEV_RANGE_END 0x04 + +#define ACPI_DEVFLAG_SYSMGT1 0x10 +#define ACPI_DEVFLAG_SYSMGT2 0x20 + +static void *build_ivrs(struct IVRS *ivrs) { + memset(ivrs, 0, sizeof(*ivrs)); + + ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); + ivrs->hdr.length = sizeof(*ivrs); + ivrs->hdr.rev = 1; + memcpy(ivrs->hdr.oem_id, "F0F ", 6); + memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); + ivrs->hdr.oem_rev = 0x20161225; + memcpy(ivrs->hdr.creator_id, "KEXC", 4); + ivrs->hdr.creator_rev = 0x20161225; + ivrs->IVinfo = 0x00203040; + + struct ivhd_header *hdr = &ivrs->hd_hdr; + hdr->type = 0x10; + hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; + hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); + hdr->devid = PCI_DEVFN(0, 2); + hdr->cap_ptr = 0x40; // from config space + 0x34 + hdr->mmio_phys = 0xfc000000; + hdr->pci_seg = 0; + hdr->info = 0; // msi msg num? (the pci cap should be written by software) + // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 + hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); + + struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; + // on fbsd, all aeolia devfns have active entries except memories (func 6) + // not sure if this is just because it wasn't in use when i dumped it? + // all entries are r/w + // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) + // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) + // Modes: + // 4 level: + // apcie + // 3 level: + // all others + + // the way to encode this info into the IVHD entries is fairly arbitrary... + entries[0].type = IVHD_DEV_SELECT; + entries[0].devid = PCI_DEVFN(20, 0); + entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; + + entries[1].type = IVHD_DEV_SELECT_RANGE_START; + entries[1].devid = PCI_DEVFN(20, 1); + entries[1].flags = 0; + entries[2].type = IVHD_DEV_RANGE_END; + entries[2].devid = PCI_DEVFN(20, 7); + entries[2].flags = 0; + + table_checksum(ivrs); + return ivrs + 1; +} + +void fix_acpi_tables(void *map_base, u64 phys_base) +{ + void *buf_base = map_base + 0x8000; + void *p = buf_base; + memset(buf_base, 0, 0x8000); + + printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); + + struct RSDP *rsdp = COPYP(struct RSDP, phys_base); + printf("RSDT at 0x%x\n", rsdp->rsdt_addr); + printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); + + struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); + struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); + rsdp->rsdt_addr = B2P(rsdt); + + PADB(0x30); // this gives us space for new tables + + struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); + rsdp->xsdt_addr = B2P(xsdt); + + PADB(0x60); + + struct FADT *fadt = NULL; + + int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; + int i; + for (i = 0; i < cnt; i++) { + struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); + printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); + switch (hdr->sig) { + case SIG32('F', 'A', 'C', 'P'): + { + fadt = (void*)hdr; + printf("FACS at 0x%x\n", fadt->facs); + printf("DSDT at 0x%x\n", fadt->dsdt); + // Sony puts the FACS before the FADT, unaligned, which is + // noncompliant, but let's keep it there + u8 *facs = COPYB(64, P2M(fadt->facs)); + fadt = (void*)(hdr = COPYT(hdr)); + fadt->facs = B2P(facs); + PADB(0x38); + break; + } + case SIG32('S', 'S', 'D', 'T'): + { + // Put the DSDT before the SSDT + if (fadt) { + PADB(0xf0); + u8 *dsdt = COPYTP(fadt->dsdt); + fadt->dsdt = B2P(dsdt); + PADB(0x174); + table_checksum(fadt); + } else { + printf("ERROR: no FADT yet?\n"); + } + hdr = COPYT(hdr); + break; + } + default: + hdr = COPYT(hdr); + } + table_checksum(hdr); + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); + } + + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); + i++; + p = build_ivrs(p); + + rsdt->hdr.length = sizeof(*rsdt) + 4 * i; + xsdt->hdr.length = sizeof(*xsdt) + 8 * i; + + rsdp_checksum(rsdp); + table_checksum(rsdt); + table_checksum(xsdt); + memcpy(map_base, buf_base, p - buf_base); +} + + +#ifdef TESTING + +int main(int argc, char **argv) +{ + int fd; + void *base; + + fd = open(argv[1], O_RDWR); + base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + fix_acpi_tables(base, 0xe0000); + return 0; +} + +#endif diff --git a/linux/ps4-kexec-1302-pro/acpi.h b/linux/ps4-kexec-1302-pro/acpi.h new file mode 100644 index 0000000..5723982 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/acpi.h @@ -0,0 +1,18 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ACPI_H +#define ACPI_H + +#include "types.h" + +void fix_acpi_tables(void *map_base, u64 phys_base); + +#endif diff --git a/linux/ps4-kexec-1302-pro/crc32.c b/linux/ps4-kexec-1302-pro/crc32.c new file mode 100644 index 0000000..7836c18 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/crc32.c @@ -0,0 +1,102 @@ +/*- +* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or +* code or tables extracted from it, as desired without restriction. +* +* First, the polynomial itself and its table of feedback terms. The +* polynomial is +* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 +* +* Note that we take it "backwards" and put the highest-order term in +* the lowest-order bit. The X^32 term is "implied"; the LSB is the +* X^31 term, etc. The X^0 term (usually shown as "+1") results in +* the MSB being 1 +* +* Note that the usual hardware shift register implementation, which +* is what we're using (we're merely optimizing it by doing eight-bit +* chunks at a time) shifts bits into the lowest-order term. In our +* implementation, that means shifting towards the right. Why do we +* do it this way? Because the calculated CRC must be transmitted in +* order from highest-order term to lowest-order term. UARTs transmit +* characters in order from LSB to MSB. By storing the CRC this way +* we hand it to the UART in the order low-byte to high-byte; the UART +* sends each low-bit to hight-bit; and the result is transmission bit +* by bit from highest- to lowest-order term without requiring any bit +* shuffling on our part. Reception works similarly +* +* The feedback terms table consists of 256, 32-bit entries. Notes +* +* The table can be generated at runtime if desired; code to do so +* is shown later. It might not be obvious, but the feedback +* terms simply represent the results of eight shift/xor opera +* tions for all combinations of data and CRC register values +* +* The values must be right-shifted by eight bits by the "updcrc +* logic; the shift must be unsigned (bring in zeroes). On some +* hardware you could probably optimize the shift in assembler by +* using byte-swap instructions +* polynomial $edb88320 +* +* +* CRC32 code derived from work by Gary S. Brown. +*/ + +#include "crc32.h" + +static u32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 crc32(u32 crc, const void *buf, size_t size) +{ + const u8 *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} diff --git a/linux/ps4-kexec-1302-pro/crc32.h b/linux/ps4-kexec-1302-pro/crc32.h new file mode 100644 index 0000000..777278d --- /dev/null +++ b/linux/ps4-kexec-1302-pro/crc32.h @@ -0,0 +1,7 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +u32 crc32(u32 crc, const void *buf, size_t size); + +#endif diff --git a/linux/ps4-kexec-1302-pro/elf.h b/linux/ps4-kexec-1302-pro/elf.h new file mode 100644 index 0000000..63371c7 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/elf.h @@ -0,0 +1,71 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + u8 e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + u64 e_entry; + u64 e_phoff; + u64 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf64_Ehdr; + +typedef struct { + u32 p_type; + u32 p_flags; + u64 p_offset; + void *p_vaddr; + u64 p_paddr; + u64 p_filesz; + u64 p_memsz; + u64 p_align; +} Elf64_Phdr; + +#define PT_DYNAMIC 2 +#define PT_PHDR 6 + +#define DT_NULL 0 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_STRSZ 10 + +typedef struct { + s64 d_tag; + union { + u64 d_val; + void *d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + void *st_value; + u64 st_size; +} Elf64_Sym; + +#endif diff --git a/linux/ps4-kexec-1302-pro/firmware.c b/linux/ps4-kexec-1302-pro/firmware.c new file mode 100644 index 0000000..a831b1c --- /dev/null +++ b/linux/ps4-kexec-1302-pro/firmware.c @@ -0,0 +1,479 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "firmware.h" +#include "types.h" +#include "kernel.h" +#include "string.h" +#include "types.h" +#include "crc32.h" + +#define DIR 0040755 +#define FILE 0100644 + +struct firmware_header { + u32 size_bytes; + u32 header_size_bytes; + u16 header_version_major; + u16 header_version_minor; + u16 ip_version_major; + u16 ip_version_minor; + u32 ucode_version; + u32 ucode_size_bytes; + u32 ucode_array_offset_bytes; + u32 crc32; + union { + struct { + u32 ucode_feature_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } gfx1; + struct { + u32 ucode_feature_version; + u32 save_and_restore_offset; + u32 clear_state_descriptor_offset; + u32 avail_scratch_ram_locations; + u32 master_pkt_description_offset; + u8 end[]; + } rlc1; + struct { + u32 ucode_feature_version; + u32 ucode_change_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } sdma1; + u8 raw[0xe0]; + }; +}; + +static inline char hex(u8 c) +{ + if (c <= 9) + return '0' + c; + return 'a' + c - 10; +} + +static void hex8(u8 **p, u32 val) +{ + *(*p)++ = hex(val >> 28); + *(*p)++ = hex((val >> 24) & 0xf); + *(*p)++ = hex((val >> 20) & 0xf); + *(*p)++ = hex((val >> 16) & 0xf); + *(*p)++ = hex((val >> 12) & 0xf); + *(*p)++ = hex((val >> 8) & 0xf); + *(*p)++ = hex((val >> 4) & 0xf); + *(*p)++ = hex(val & 0xf); +} + +void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) +{ + size_t name_len = strlen(name); + + // Pad to 4 byte multiple + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; + + memcpy(*p, "070701", 6); + *p += 6; + hex8(p, 0); // c_ino + hex8(p, mode); // c_mode + hex8(p, 0); // c_uid + hex8(p, 0); // c_gid + hex8(p, 1); // c_nlink + hex8(p, 0); // c_mtime + hex8(p, size); // c_filesize + hex8(p, 0); // c_maj + hex8(p, 0); // c_min + hex8(p, 0); // c_rmaj + hex8(p, 0); // c_rmin + hex8(p, name_len + 1); // c_namesize + hex8(p, 0); // c_chksum + memcpy(*p, name, name_len); + *p += name_len; + *(*p)++ = 0; + + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; +} + +struct fw_header_t { + u64 size_words; + char *unk_ident; + u64 unk; + void *blob; + u64 unk2; +}; + +struct fw_info_t { + struct fw_header_t *rlc; + struct fw_header_t *sdma0; + struct fw_header_t *sdma1; + struct fw_header_t *ce; + struct fw_header_t *pfp; + struct fw_header_t *me; + struct fw_header_t *mec1; + struct fw_header_t *mec2; +}; + +struct fw_expected_sizes_t { + u64 rlc; + u64 sdma0; + u64 sdma1; + u64 ce; + u64 pfp; + u64 me; + u64 mec1; + u64 mec2; +}; +static const struct fw_expected_sizes_t liverpool_fw_sizes = { + LVP_FW_RLC_SIZE, + LVP_FW_SDMA_SIZE, + LVP_FW_SDMA1_SIZE, + LVP_FW_CE_SIZE, + LVP_FW_PFP_SIZE, + LVP_FW_ME_SIZE, + LVP_FW_MEC_SIZE, + LVP_FW_MEC2_SIZE +}; +static const struct fw_expected_sizes_t gladius_fw_sizes = { + GL_FW_RLC_SIZE, + GL_FW_SDMA_SIZE, + GL_FW_SDMA1_SIZE, + GL_FW_CE_SIZE, + GL_FW_PFP_SIZE, + GL_FW_ME_SIZE, + GL_FW_MEC_SIZE, + GL_FW_MEC2_SIZE +}; + +void copy_edid(u8 **p, int sz) +{ + int i; + u8 *edid = *p; + u8 *off_edid = kern.edid; + + memset(edid, 0, sz); + *p += sz; + + for(i = 0; i < sz; i++) + *(edid + i) = *(off_edid + i); + + *p += sz; +} + +void copy_eap_hdd_key(u8 **p) +{ + int i; + u8 *eap_key = *p; + u8 *off_eap_key = kern.eap_hdd_key; + + memset(eap_key, 0, 0x20); + *p += 0x20; + + for(i = 0; i < 0x20; i++) + { + if(i < 0x10) + *(eap_key + i) = *(off_eap_key + 0xF - i); + else + *(eap_key + i) = *(off_eap_key + 0x2F - i); + } + *p += 0x20; +} + +int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + kern.printf("Copying %s firmware\n", name); + if (expected_size != (hdr->size_words * 4)) { + kern.printf("copy_firmware: %s: expected size %d, got %d\n", + name, expected_size, hdr->size_words * 4); + return 0; + } + + struct firmware_header *fhdr = (struct firmware_header*)*p; + memset(fhdr, 0, sizeof(*fhdr)); + *p += sizeof(*fhdr); + + memcpy(*p, hdr->blob, expected_size); + + fhdr->size_bytes = expected_size + sizeof(*fhdr); + fhdr->header_size_bytes = offsetof(struct firmware_header, raw); + fhdr->header_version_major = 1; + fhdr->header_version_minor = 0; + fhdr->ucode_version = 0x10; + fhdr->ucode_size_bytes = expected_size; + fhdr->ucode_array_offset_bytes = sizeof(*fhdr); + + *p += expected_size; + + return 1; +} + +int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); + fhdr->gfx1.ucode_feature_version = 21; + fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); + fhdr->rlc1.ucode_feature_version = 1; + fhdr->rlc1.save_and_restore_offset = 0x90; + fhdr->rlc1.clear_state_descriptor_offset = 0x3d; + fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? + fhdr->rlc1.master_pkt_description_offset = 0; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 2; + fhdr->ip_version_minor = 1; + fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); + fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; + fhdr->sdma1.ucode_change_version = 0; + fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +static const u32 pfp_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc4200016, // ldw r8, [r0, #0x16] + 0xdc030000, // mov ctr, r0 + 0xcc000049, // stw r0, [r0, #0x49] + 0xcc200013, // stw r0, [r8, #0x13] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 ce_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc420000c, // ldw r8, [r0, #0xc] + 0xdc030000, // mov ctr, r0 + 0xcc00002f, // stw r0, [r0, #0x2f] + 0xcc200012, // stw r0, [r8, #0x12] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 mec_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc43c000c, // ldw r15, [r0, #0x9] + 0xdc030000, // mov ctr, r0 + 0xcc00002b, // stw r0, [r0, #0x2b] + 0xcc3c000d, // stw r0, [r15, #0xd] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +#define PACKET_TYPE_NOP 0x10 + +static void patch_fw(void *p, const u32 *handler, int handler_size) { + int size = ((struct firmware_header*)p)->ucode_size_bytes; + int code_size = (size & ~0xfff) / 4; + int nop_start = code_size - 0x10; + + u32 *fw = p + sizeof(struct firmware_header); + kern.printf("NOP handler at 0x%x\n", nop_start); + memcpy(&fw[nop_start], handler, handler_size); + + // patch the branch table entry + for (int off = code_size; off < size/4; off++) { + if ((fw[off] >> 16) == PACKET_TYPE_NOP) { + fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; + } + } +} + +struct fw_info_t *get_fw_info() { + if (kern.gc_get_fw_info) { + return kern.gc_get_fw_info(); + } else if (kern.Starsha_UcodeInfo) { + return kern.Starsha_UcodeInfo; + } else { + return NULL; + } +} + +const struct fw_expected_sizes_t *get_fw_expected_sizes() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return &gladius_fw_sizes; + } else { + return &liverpool_fw_sizes; + } +} + +const char * get_gpu_name() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return "gladius"; + } else { + return "liverpool"; + } +} + +ssize_t firmware_extract(void *dest) +{ + u8 *p = dest; + + // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. + struct fw_info_t *info = get_fw_info(); + if (!info) { + kern.printf("firmware_extract: Could not locate firmware table"); + return -1; + } + const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); + + //Eap hdd key + cpio_hdr(&p, "key", DIR, 0); + cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); + copy_eap_hdd_key(&p); + + cpio_hdr(&p, "lib", DIR, 0); + cpio_hdr(&p, "lib/firmware", DIR, 0); + + /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ + int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); + cpio_hdr(&p, "lib/firmware/edid", DIR, 0); + cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); + copy_edid(&p, edid_sz); + + char dir[7]; + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) + kern.snprintf(dir, sizeof(dir), "amdgpu"); + else + kern.snprintf(dir, sizeof(dir), "amdgpu"); + + char dir_path[64]; + kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); + cpio_hdr(&p, dir_path, DIR, 0); + + char pfp_path[64]; + kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", pfp_path); + cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); + u8 *pfp = p; + if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) + return -1; + patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); + + char me_path[64]; + kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", me_path); + cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); + if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) + return -1; + + char ce_path[64]; + kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", ce_path); + cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); + u8 *ce = p; + if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) + return -1; + patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); + + char mec_path[64]; + kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec_path); + cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); + u8 *mec1 = p; + if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) + return -1; + patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); + + char mec2_path[64]; + kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec2_path); + cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); + u8 *mec2 = p; + if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) + return -1; + patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); + + char rlc_path[64]; + kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", rlc_path); + cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); + if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) + return -1; + + char sdma_path[64]; + kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma_path); + cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); + if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + char sdma1_path[64]; + kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma1_path); + cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); + if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + size_t size = p - (u8*)dest; + if (size > FW_CPIO_SIZE) { + kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); + return -1; + } + + return size; +} diff --git a/linux/ps4-kexec-1302-pro/firmware.h b/linux/ps4-kexec-1302-pro/firmware.h new file mode 100644 index 0000000..4835983 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/firmware.h @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include "types.h" + +//sizes eap_hdd_key +#define EAP_HDD_KEY_SIZE 0x20 +#define EDID_SIZE 256 + +// sizes for liverpool +#define LVP_FW_CE_SIZE 8576 +#define LVP_FW_ME_SIZE 16768 +#define LVP_FW_MEC_SIZE 16768 +#define LVP_FW_MEC2_SIZE 16768 +#define LVP_FW_PFP_SIZE 16768 +#define LVP_FW_RLC_SIZE 6144 +#define LVP_FW_SDMA_SIZE 4200 +#define LVP_FW_SDMA1_SIZE 4200 +// sizes for gladius +#define GL_FW_CE_SIZE 8576 +#define GL_FW_ME_SIZE 16768 +#define GL_FW_MEC_SIZE 16768 +#define GL_FW_MEC2_SIZE 16768 +#define GL_FW_PFP_SIZE 16768 +#define GL_FW_RLC_SIZE 8192 +#define GL_FW_SDMA_SIZE 4200 +#define GL_FW_SDMA1_SIZE 4200 + +#define MAX(x ,y) (((x) > (y)) ? (x) : (y)) +#define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) + +#define FW_CE_SIZE MAX_FW_SIZE(CE) +#define FW_ME_SIZE MAX_FW_SIZE(ME) +#define FW_MEC_SIZE MAX_FW_SIZE(MEC) +#define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) +#define FW_PFP_SIZE MAX_FW_SIZE(PFP) +#define FW_RLC_SIZE MAX_FW_SIZE(RLC) +#define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) +#define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) + +// Conservative value (max 113 bytes plus name size plus alignment) +#define CPIO_HEADER_SIZE 256 + +#define FW_HEADER_SIZE 256 + +// Leave space for 16 files (currently 12) +#define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ + FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ + FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ + FW_HEADER_SIZE * 8) + +ssize_t firmware_extract(void *dest); + +#endif diff --git a/linux/ps4-kexec-1302-pro/kernel.c b/linux/ps4-kexec-1302-pro/kernel.c new file mode 100644 index 0000000..b6d31b4 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/kernel.c @@ -0,0 +1,383 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "string.h" +#include "elf.h" +#include "x86.h" +#include "../magic.h" + +struct ksym_t kern; +int (*early_printf)(const char *fmt, ...) = NULL; + +#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) + +#ifdef NO_SYMTAB + +#define RESOLVE_NOERR(name) do { \ + if (kern_off_ ## name == 0) { \ + kern.name = 0; \ + } else { \ + kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ + } \ +} while (0); + +#define RESOLVE(name) do { \ + if (kern_off_ ## name == 0) { \ + return 0; \ + } \ + RESOLVE_NOERR(name) \ +} while (0); + +#else + +#define KERNSIZE 0x2000000 + +static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; +static Elf64_Sym *symtab; +static char *strtab; +static size_t strtab_size; + +static Elf64_Ehdr *find_kern_ehdr(void) +{ + // Search for the kernel copy embedded in ubios, then follow it to see + // where it was relocated to + for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; + if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { + for (size_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; + if (phdr->p_type == PT_PHDR) { + return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); + } + } + } + } + return NULL; +} + +static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf64_Dyn *)phdr->p_vaddr; + } + } + return NULL; +} + +static int elf_parse_dyn(Elf64_Dyn *dyn) +{ + for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_SYMTAB: + symtab = (Elf64_Sym *)dp->d_un.d_ptr; + break; + case DT_STRTAB: + strtab = (char *)dp->d_un.d_ptr; + break; + case DT_STRSZ: + strtab_size = dp->d_un.d_val; + break; + } + } + return symtab && strtab && strtab_size; +} + +void *kernel_resolve(const char *name) +{ + for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { + if (!strcmp(name, &strtab[sym->st_name])) { + eprintf("kern.%s = %p\n", name, (void*)sym->st_value); + return (void *)sym->st_value; + } + } + eprintf("Failed to resolve symbol '%s'\n", name); + return NULL; +} + +#define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) +#define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; + +#endif + +static int resolve_symbols(void) +{ + RESOLVE(printf); + RESOLVE(snprintf); + early_printf = kern.printf; + RESOLVE(copyin); + RESOLVE(copyout); + RESOLVE(copyinstr); + RESOLVE(kernel_map); + RESOLVE(kernel_pmap_store); + RESOLVE(kmem_alloc_contig); + RESOLVE(kmem_free); + RESOLVE(pmap_extract); + RESOLVE(pmap_protect); + RESOLVE(sysent); + RESOLVE(sched_pin); + RESOLVE(sched_unpin); + RESOLVE(smp_rendezvous); + RESOLVE(smp_no_rendevous_barrier); + RESOLVE(icc_query_nowait); + RESOLVE_NOERR(Starsha_UcodeInfo); + RESOLVE_NOERR(gpu_devid_is_9924); + RESOLVE_NOERR(gc_get_fw_info); + RESOLVE_NOERR(eap_hdd_key); + RESOLVE_NOERR(edid); + RESOLVE(wlanbt); + RESOLVE(kern_reboot); + RESOLVE(set_gpu_freq); + RESOLVE(set_pstate); + RESOLVE(update_vddnp); + RESOLVE(set_cu_power_gate); + RESOLVE_NOERR(pstate_before_shutdown); + return 1; +} + +#define M_WAITOK 0x0002 +#define M_ZERO 0x0100 + +#define VM_MEMATTR_DEFAULT 0x06 + +void *kernel_alloc_contig(size_t size) +{ + // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... + vm_offset_t ret = 0; + while(!(ret = kern.kmem_alloc_contig( + *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, + ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); + + /*if (!ret) { + kern.printf("Failed to allocate %zud bytes\n", size); + return NULL; + }*/ + return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); +} + +void kernel_free_contig(void *addr, size_t size) +{ + if (!addr) + return; + kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); +} + +int kernel_hook_install(void *target, void *hook) +{ + uintptr_t t = (uintptr_t)target; // addr to redirect to + uintptr_t h = (uintptr_t)hook; // place to write the thunk + + if (!hook || !target) { + return 0; + } + + kern.printf("kernel_hook_install(%p, %p)\n", target, hook); + + if (!(t & (1L << 63))) { + kern.printf("\n===================== WARNING =====================\n"); + kern.printf("hook target function address: %p\n", target); + kern.printf("It looks like we're running from userland memory.\n"); + kern.printf("Please run this code from a kernel memory mapping.\n\n"); + return 0; + } + s64 displacement = t - (h + 5); + + kern.sched_pin(); + u64 wp = write_protect_disable(); + if (displacement < -0x80000000 || displacement > 0x7fffffff) { + kern.printf(" Using 64bit absolute jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[2]; + s32 zero; + void *target; + } jmp = { + .op = { 0xff, 0x25 }, + .zero = 0, + .target = target, + }; + ASSERT_STRSIZE(struct jmp_t, 14); + memcpy(hook, &jmp, sizeof(jmp)); + } else { + kern.printf(" Using 32bit relative jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[1]; + s32 imm; + } jmp = { + .op = { 0xe9 }, + .imm = displacement, + }; + ASSERT_STRSIZE(struct jmp_t, 5); + memcpy(hook, &jmp, sizeof(jmp)); + } + wbinvd(); + write_protect_restore(wp); + kern.sched_unpin(); + + return 1; +} + +void kernel_syscall_install(int num, void *call, int narg) +{ + struct sysent_t *sy = &kern.sysent[num]; + + kern.sched_pin(); + u64 wp = write_protect_disable(); + + memset(sy, 0, sizeof(*sy)); + sy->sy_narg = narg; + sy->sy_call = call; + sy->sy_thrcnt = 1; + + write_protect_restore(wp); + kern.sched_unpin(); +} + +void kernel_remap(void *start, void *end, int perm) +{ + u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); + u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); + + kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); + kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); +} + +static volatile int _global_test = 0; + +#ifndef DO_NOT_REMAP_RWX +extern u8 _start[], _end[]; + +static int patch_pmap_check(void) +{ + u8 *p; + + for (p = (u8*)kern.pmap_protect; + p < ((u8*)kern.pmap_protect + 0x500); p++) { + #ifdef PS4_6_72 + if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 + p[5] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + /*#ifdef PS4_5_05 + if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { + p[1] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + }*/ + #else + if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { + p[2] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + #endif + } + kern.printf("pmap_protect patch failed!\n"); + return 0; +} +#endif + +int kernel_init(void *_early_printf) +{ + int rv = -1; + + if (_early_printf) + early_printf = _early_printf; + + eprintf("kernel_init()\n"); + +#ifdef KASLR + // use `early_printf` to calculate kernel base + if (early_printf == NULL) + return 0; + + kern.kern_base = (u64)(early_printf - kern_off_printf); + if ((kern.kern_base & PAGE_MASK) != 0) { + eprintf("Kernel base is not aligned\n"); + return 0; + } else { + eprintf("Kernel base = %llx\n", kern.kern_base); + } + + u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); + u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); + +#else + kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 + + u64 DMPML4I = 0x1fc; + u64 DMPDPI = 0; +#endif + + kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); + eprintf("Direct map base = %llx\n", kern.dmap_base); + + // We may not be mapped writable yet, so to be able to write to globals + // we need WP disabled. + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + +#ifndef NO_SYMTAB + Elf64_Ehdr *ehdr = find_kern_ehdr(); + if (!ehdr) { + eprintf("Could not find kernel ELF header\n"); + goto err; + } + eprintf("ELF header at %p\n", ehdr); + + Elf64_Dyn *dyn = elf_get_dyn(ehdr); + if (!dyn) { + eprintf("Could not find kernel dynamic header\n"); + goto err; + } + eprintf("ELF dynamic section at %p\n", dyn); + + if (!elf_parse_dyn(dyn)) { + eprintf("Failed to parse ELF dynamic section\n"); + goto err; + } +#endif + + if (!resolve_symbols()) { + eprintf("Failed to resolve all symbols\n"); + goto err; + } + + // Pin ourselves as soon as possible. This is expected to be released by the caller. + kern.sched_pin(); + +#ifndef DO_NOT_REMAP_RWX + if (!patch_pmap_check()) + goto err; +#endif + +#ifndef DO_NOT_REMAP_RWX + // kernel_remap may need interrupts, but may not write to globals! + enable_interrupts(); + kernel_remap(_start, _end, 7); + disable_interrupts(); +#endif + + // Writing to globals is now safe. + + kern.printf("Testing global variable access (write protection)...\n"); + _global_test = 1; + kern.printf("OK.\n"); + + kern.printf("Kernel interface initialized\n"); + rv = 0; + +err: + write_protect_restore(wp); + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1302-pro/kernel.h b/linux/ps4-kexec-1302-pro/kernel.h new file mode 100644 index 0000000..1833f5b --- /dev/null +++ b/linux/ps4-kexec-1302-pro/kernel.h @@ -0,0 +1,126 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KERNEL_H +#define KERNEL_H + +#include "types.h" +#include "reboot.h" + +#define PAGE_SIZE 0x4000 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define PML4SHIFT 39 +#define PDPSHIFT 30 +#define PDRSHIFT 21 +#define PAGE_SHIFT 12 + +#define KVADDR(l4, l3, l2, l1) ( \ + ((unsigned long)-1 << 47) | \ + ((unsigned long)(l4) << PML4SHIFT) | \ + ((unsigned long)(l3) << PDPSHIFT) | \ + ((unsigned long)(l2) << PDRSHIFT) | \ + ((unsigned long)(l1) << PAGE_SHIFT)) + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) +#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX + +typedef u64 vm_paddr_t; +typedef u64 vm_offset_t; +typedef u64 vm_size_t; +typedef void * vm_map_t; +typedef char vm_memattr_t; +typedef void * pmap_t; + +typedef void (*smp_rendezvous_callback_t)(void *); + +struct sysent_t { + int sy_narg; + void *sy_call; + u16 sy_auevent; + void *sy_systrace_args_func; + int sy_entry; + int sy_return; + int sy_flags; + int sy_thrcnt; +}; + +struct ksym_t { + // two parameters related to kaslr (they are not symbols) + uintptr_t kern_base; + uintptr_t dmap_base; + + int (*printf)(const char *fmt, ...); + int (*snprintf)(const char *fmt, ...); + int (*copyin)(const void *uaddr, void *kaddr, size_t len); + int (*copyout)(const void *kaddr, void *uaddr, size_t len); + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); + + void **kernel_map; + void *kernel_pmap_store; + vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, + vm_paddr_t low, vm_paddr_t high, + unsigned long alignment, + unsigned long boundary, + vm_memattr_t memattr); + void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); + vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); + void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); + + struct sysent_t *sysent; + + void (*sched_pin)(void); + void (*sched_unpin)(void); + void (*smp_rendezvous)(smp_rendezvous_callback_t, + smp_rendezvous_callback_t, + smp_rendezvous_callback_t, void *); + // yes...it is misspelled :) + void (*smp_no_rendevous_barrier)(void *); + void *icc_query_nowait; + void *Starsha_UcodeInfo; + int (*gpu_devid_is_9924)(); + void *(*gc_get_fw_info)(); + void *eap_hdd_key; + void *edid; + void (*wlanbt)(unsigned int state); + int (*kern_reboot)(int magic); + void(*set_gpu_freq)(unsigned int num, unsigned int freq); + void(*set_pstate)(unsigned int val); + void(*update_vddnp)(unsigned int val); + void(*set_cu_power_gate)(unsigned int val); + void *pstate_before_shutdown; +}; + +extern struct ksym_t kern; + +static inline int curcpu(void) +{ + int cpuid; + // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw + asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); + return cpuid; +} + +// Assign a working printf function to this to debug the symbol resolver +extern int (*early_printf)(const char *fmt, ...); + +void *kernel_resolve(const char *name); + +void *kernel_alloc_contig(size_t size); +void kernel_free_contig(void *addr, size_t size); + +void kernel_remap(void *start, void *end, int perm); + +void kernel_syscall_install(int num, void *call, int narg); +int kernel_hook_install(void *target, void *hook); + +int kernel_init(void *early_printf); + +#endif diff --git a/linux/ps4-kexec-1302-pro/kexec.c b/linux/ps4-kexec-1302-pro/kexec.c new file mode 100644 index 0000000..fa55dfe --- /dev/null +++ b/linux/ps4-kexec-1302-pro/kexec.c @@ -0,0 +1,229 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "linux_boot.h" +#include "x86.h" +#include "kexec.h" +#include "firmware.h" +#include "string.h" +#include "acpi.h" + +static int k_copyin(const void *uaddr, void *kaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(kaddr, uaddr, len); + return 0; +} + +static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + const char *ustr = (const char*)uaddr; + char *kstr = (char*)kaddr; + size_t ret; + if (!uaddr || !kaddr) + return EFAULT; + ret = strlcpy(kstr, ustr, len); + if (ret >= len) { + if (done) + *done = len; + return ENAMETOOLONG; + } else { + if (done) + *done = ret + 1; + } + return 0; +} + +static int k_copyout(const void *kaddr, void *uaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(uaddr, kaddr, len); + return 0; +} + +int sys_kexec(void *td, struct sys_kexec_args *uap) +{ + int err = 0; + size_t initramfs_size = uap->initramfs_size; + void *image = NULL; + void *initramfs = NULL; + size_t firmware_size = 0; + struct boot_params *bp = NULL; + size_t cmd_line_maxlen = 0; + char *cmd_line = NULL; + + int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; + int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; + + kern.printf("sys_kexec invoked\n"); + kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, + uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); + + // Look up our shutdown hook point + void *icc_query_nowait = kern.icc_query_nowait; + if (!icc_query_nowait) { + err = ENOENT; + goto cleanup; + } + + // Set gpu frequencies and pstate + // FAT&SLIM / PRO + kern.set_pstate(3); + + kern.set_gpu_freq(0, 800); //800 //800 + kern.set_gpu_freq(1, 853); //673 //853 + kern.set_gpu_freq(2, 711); //610 //711 + kern.set_gpu_freq(3, 800); //800 //800 + kern.set_gpu_freq(4, 911); //800 //911 + kern.set_gpu_freq(5, 800); //711 //800 + kern.set_gpu_freq(6, 984); //711 //984 + kern.set_gpu_freq(7, 673); //673 //673 + + kern.update_vddnp(0x12); + kern.set_cu_power_gate(0x24); + + // Copy in kernel image + image = kernel_alloc_contig(uap->image_size); + if (!image) { + kern.printf("Failed to allocate image\n"); + err = ENOMEM; + goto cleanup; + } + err = copyin(uap->image, image, uap->image_size); + if (err) { + kern.printf("Failed to copy in image\n"); + goto cleanup; + } + + // Copy in initramfs + initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); + if (!initramfs) { + kern.printf("Failed to allocate initramfs\n"); + err = ENOMEM; + goto cleanup; + } + + err = firmware_extract(((u8*)initramfs)); + if (err < 0) { + kern.printf("Failed to extract GPU firmware - continuing anyway\n"); + } else { + firmware_size = err; + } + + if (initramfs_size) { + err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); + if (err) { + kern.printf("Failed to copy in initramfs\n"); + goto cleanup; + } + } + initramfs_size += firmware_size; + + // Copy in cmdline + cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; + cmd_line = kernel_alloc_contig(cmd_line_maxlen); + if (!cmd_line) { + kern.printf("Failed to allocate cmdline\n"); + err = ENOMEM; + goto cleanup; + } + err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); + if (err) { + kern.printf("Failed to copy in cmdline\n"); + goto cleanup; + } + cmd_line[cmd_line_maxlen - 1] = 0; + + kern.printf("\nkexec parameters:\n"); + kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); + kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", + initramfs_size, uap->initramfs_size); + kern.printf(" Kernel command line: %s\n", cmd_line); + kern.printf(" Kernel image buffer: %p\n", image); + kern.printf(" Initramfs buffer: %p\n", initramfs); + + // Allocate our boot params + bp = kernel_alloc_contig(sizeof(*bp)); + if (!bp) { + kern.printf("Failed to allocate bp\n"); + err = ENOMEM; + goto cleanup; + } + + // Initialize bp + // TODO should probably do this from cpu_quiesce_gate, then bp doesn't + // need to be allocated here, just placed directly into low mem + set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); + + prepare_boot_params(bp, image); + + // Hook the final ICC shutdown function + if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { + kern.printf("Failed to install shutdown hook\n"); + err = EINVAL; + goto cleanup; + } + + kern.printf("******************************************************\n"); + kern.printf("kexec successfully armed. Please shut down the system.\n"); + kern.printf("******************************************************\n\n"); + +/* + kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); + if (kern.kern_reboot(RB_POWEROFF) == -1) + kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); +*/ + return 0; + +cleanup: + kernel_free_contig(cmd_line, cmd_line_maxlen); + kernel_free_contig(bp, sizeof(*bp)); + kernel_free_contig(image, uap->image_size); + kernel_free_contig(initramfs, uap->initramfs_size); + return err; + + copyout(NULL, NULL, 0); +} + +int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) +{ + int rv = 0; + + // potentially needed to write early_printf + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + + if (kernel_init(_early_printf) < 0) { + rv = -1; + goto cleanup; + } + + kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); + kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); + kern.printf("kexec_init() successful\n\n"); + + if (sys_kexec_ptr) + *sys_kexec_ptr = sys_kexec; + +cleanup: + write_protect_restore(wp); + if (kern.sched_unpin && wp & CR0_WP) { + // If we're returning to a state with WP enabled, assume the caller + // wants the thread unpinned. Else the caller is expected to + // call kern.sched_unpin() manually. + kern.sched_unpin(); + } + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1302-pro/kexec.h b/linux/ps4-kexec-1302-pro/kexec.h new file mode 100644 index 0000000..6c31637 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/kexec.h @@ -0,0 +1,38 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KEXEC_H +#define KEXEC_H + +#include "types.h" + +#define SYS_KEXEC 153 +#define SYS_KEXEC_NARGS 6 + +struct sys_kexec_args { + void *image; + size_t image_size; + void *initramfs; + size_t initramfs_size; + char *cmd_line; + int vram_gb; +}; + +typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); + +// Note: td is unused, you can pass NULL if you call this directly. +int sys_kexec(void *td, struct sys_kexec_args *uap); + +int kernel_init(void *early_printf); + +int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) + __attribute__ ((section (".init"))); + +#endif diff --git a/linux/ps4-kexec-1302-pro/kexec.ld b/linux/ps4-kexec-1302-pro/kexec.ld new file mode 100644 index 0000000..1bcf26c --- /dev/null +++ b/linux/ps4-kexec-1302-pro/kexec.ld @@ -0,0 +1,25 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +ENTRY(kexec_init) + +SECTIONS { + /* We don't do GOT relocation and rely on nothing ending up using the GOT + * (-fno-common helps here) */ + /DISCARD/ : { *(.comment) *(.got) } + _start = .; + .init : { *(.init) *(.init.*) } + .text : { *(.text) *(.text.*) } + .data : { *(.data) *(.data.*) } + .rodata : { *(.rodata) *(.rodata.*) } + .bss : { *(.bss) *(.bss.*) *(COMMON)} + .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ + _end = .; +} diff --git a/linux/ps4-kexec-1302-pro/linux_boot.c b/linux/ps4-kexec-1302-pro/linux_boot.c new file mode 100644 index 0000000..197ef63 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/linux_boot.c @@ -0,0 +1,441 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "linux_boot.h" +#include "types.h" +#include "string.h" +#include "x86.h" +#include "kernel.h" +#include "uart.h" +#include "acpi.h" + +void uart_write_byte(u8 b); + +static u64 vram_base = 0x100000000; +// Current code assumes it's a power of two. +static u64 vram_size = 1024 * 1024 * 1024; +static int vram_gb = 2; + +#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) + +struct desc_ptr { + u16 limit; + u64 address; +} __attribute__((packed)); + +struct desc_struct { + u16 limit0; + u16 base0; + u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; + u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, + uintptr_t new_cr3, uintptr_t gdt_ptr); +extern uint8_t *jmp_to_linux; +extern size_t jmp_to_linux_size; + +// FreeBSD DMAP addresses +struct linux_boot_info { + void *linux_image; + void *initramfs; + size_t initramfs_size; + struct boot_params *bp; + char *cmd_line; +}; +static struct linux_boot_info nix_info; + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v) +{ + nix_info.linux_image = linux_image; + nix_info.bp = bp; + nix_info.initramfs = initramfs; + nix_info.initramfs_size = initramfs_size; + nix_info.cmd_line = cmd_line; + vram_gb = v; +} + +static volatile int halted_cpus = 0; + +static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, + u32 type) +{ + uint8_t idx = bp->e820_entries; + bp->e820_map[idx].addr = addr; + bp->e820_map[idx].size = size; + bp->e820_map[idx].type = type; + bp->e820_entries++; +} + +void prepare_boot_params(struct boot_params *bp, u8 *linux_image) +{ + memset(bp, 0, sizeof(struct boot_params)); + struct boot_params *bp_src = (struct boot_params *)linux_image; + memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + + ((u8 *)&bp_src->hdr.jump)[1]); + + // These values are from fw 1.01 + bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); + bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + // This used to be VRAM, but we reclaim it as RAM + bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); + // Instead, carve out VRAM from the beginning of high memory + bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, + SMAP_TYPE_MEMORY); +} + +#define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_FB_OFFSET 0x2068 +#define HDP_NONSURFACE_BASE 0x2c04 +#define CONFIG_MEMSIZE 0x5428 + +static void configure_vram(void) +{ + u64 mmio_base = 0xe4800000; + u64 fb_base = 0x0f00000000; + u64 fb_top = fb_base + vram_gb * vram_size - 1; + + WR32(mmio_base + MC_VM_FB_LOCATION, 0); + WR32(mmio_base + HDP_NONSURFACE_BASE, 0); + + WR32(mmio_base + MC_VM_FB_LOCATION, + ((fb_top >> 24) << 16) | (fb_base >> 24)); + WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); + WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); + WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); +} + +#define IA32_MTRR_DEF_TYPE 0x2ff +#define MTRR_BASE(i) (0x200 + 2*i) +#define MTRR_MASK(i) (0x201 + 2*i) + +static void setup_mtrr(void) +{ + disable_interrupts(); + u64 cr0 = cr0_read(); + cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); + wbinvd(); + cr3_write(cr3_read()); // TLB flush + + wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled + + // Low memory (0GB-2GB) = WB + wrmsr(MTRR_BASE(0), 0x0000000006); + wrmsr(MTRR_MASK(0), 0xff80000800); + // High memory (4GB-8GB) = WB + wrmsr(MTRR_BASE(1), 0x0100000006); + wrmsr(MTRR_MASK(1), 0xff00000800); + // High memory (8GB-10GB) = WB + wrmsr(MTRR_BASE(2), 0x0200000006); + wrmsr(MTRR_MASK(2), 0xff80000800); + // VRAM (4GB-4GB+vram_size) = UC + wrmsr(MTRR_BASE(3), 0x0100000000); + wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); + + wbinvd(); + cr3_write(cr3_read()); // TLB flush + wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable + cr0_write(cr0); + enable_interrupts(); +} + +static void cleanup_interrupts(void) +{ + int i; + disable_interrupts(); + + // Reset APIC stuff (per-CPU) + *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; + for (i = 0x320; i < 0x380; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + for (i = 0x480; i < 0x500; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; + for (i = 0x500; i < 0x540; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; + + // Fix the LVT offset for thresholding + wrmsr(0x413, (1L<<24) | (1L<<52)); + wrmsr(0xc0000408, (1L<<24) | (1L<<52)); +} + +static void cpu_quiesce_gate(void *arg) +{ + int i; + + // Ensure we can write anywhere + cr0_write(cr0_read() & ~CR0_WP); + + // Interrupt stuff local to each CPU + cleanup_interrupts(); + + // We want to set up MTRRs on all CPUs + setup_mtrr(); + + if (curcpu() != 0) { + // We're not on BSP. Try to halt. + __sync_fetch_and_add(&halted_cpus, 1); + cpu_stop(); + } + + uart_write_str("kexec: Waiting for secondary CPUs...\n"); + + // wait for all cpus to halt + while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); + + uart_write_str("kexec: Secondary CPUs quiesced\n"); + + //* Put ident mappings in current page tables + // Should not be needed, but maybe helps for debugging? + cr4_pge_disable(); + u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); + u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + // Clear (really) low mem. + // Linux reads from here to try and access EBDA... + // get_bios_ebda reads u16 from 0x40e + // reserve_ebda_region reads u16 from 0x413 + // Writing zeros causes linux to default to marking + // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. + // It doesn't match the ps4 e820 map, but that seems OK. + memset((void *)0, 0, 0x1000); + + // Create a new page table hierarchy out of the way of linux + // Accessed via freebsd direct map + pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt + // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| + memset(pml4_base, 0, 512 * sizeof(u64) * 2); + pdp_base = pml4_base + 512; + u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; + pml4_base[0] = pdpe; + // Maintain the freebsd direct map + pml4_base[DM_PML4_BASE] = pdpe; + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + uart_write_str("kexec: Setting up GDT...\n"); + + struct desc_ptr gdt_ptr; + struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); + gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; + gdt_ptr.address = DM_TO_ID(desc); + + // clear + memset(desc, 0, gdt_ptr.limit + 1); + // Most things are ignored in 64bit mode, and we will never be in + // 32bit/compat modes, so just setup another pure-64bit environment... + // Linux inits it's own GDT in secondary_startup_64 + // 0x10 + desc[2].limit0 = 0xffff; + desc[2].base0 = 0x0000; + desc[2].base1 = 0x0000; + desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; + desc[2].s = 1; + desc[2].dpl = 0; + desc[2].p = 1; + desc[2].limit = 0xf; + desc[2].avl = 0; + desc[2].l = 1; + desc[2].d = 0; + desc[2].g = 0; + desc[2].base2 = 0x00; + // 0x18 + desc[3].limit0 = 0xffff; + desc[3].base0 = 0x0000; + desc[3].base1 = 0x0000; + desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; + desc[3].s = 1; + desc[3].dpl = 0; + desc[3].p = 1; + desc[3].limit = 0xf; + desc[3].avl = 0; + desc[3].l = 0; + desc[3].d = 0; + desc[3].g = 0; + desc[3].base2 = 0x00; + // Task segment value + // 0x20 + desc[4].limit0 = 0x0000; + desc[4].base0 = 0x0000; + desc[4].base1 = 0x0000; + desc[4].type = SEG_TYPE_TSS; + desc[4].s = 1; + desc[4].dpl = 0; + desc[4].p = 1; + desc[4].limit = 0x0; + desc[4].avl = 0; + desc[4].l = 0; + desc[4].d = 0; + desc[4].g = 0; + desc[4].base2 = 0x00; + + uart_write_str("kexec: Relocating stub...\n"); + + // Relocate the stub and jump to it + // TODO should thunk_copy be DMAP here? + void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); + memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); + // XXX The +0x200 is for the iret stack in linux_thunk.S + uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; + + uart_write_str("kexec: Setting up boot params...\n"); + + // XXX we write into this bootargs and pass it to the kernel, but in + // jmp_to_linux we use the bootargs from the image as input. So they + // MUST MATCH! + struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; + *bp_lo = *nix_info.bp; + lowmem_pos += sizeof(struct boot_params); + + struct setup_header *shdr = &bp_lo->hdr; + shdr->cmd_line_ptr = lowmem_pos; + shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; + shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; + bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; + bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; + shdr->hardware_subarch = X86_SUBARCH_PS4; + // This needs to be nonzero for the initramfs to work + shdr->type_of_loader = 0xd0; // kexec + + strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, + nix_info.bp->hdr.cmdline_size); + lowmem_pos += strlen(nix_info.cmd_line) + 1; + + uart_write_str("kexec: Cleaning up hardware...\n"); + + // Disable IOMMU + *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; + + // Disable all MSIs on Aeolia + for (i = 0; i < 8; i++) + *(volatile u32 *)PA_TO_DM(0xd03c844c + i*4) = 0; + + // Stop HPET timers + *(volatile u64 *)PA_TO_DM(0xd0382010) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382100) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382120) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382140) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382160) = 0; + + uart_write_str("kexec: Reconfiguring VRAM...\n"); + + configure_vram(); + + uart_write_str("kexec: Resetting GPU...\n"); + + // Softreset GPU + *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks + *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC + *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 +// *(volatile u64 *)PA_TO_DM(0xe480d248) = 1; // Halt SDMA1 eeply + *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 + *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC + + *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply + +// *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x10003; // Softreset GFX/CP/RLC + *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x30005; // Softreset GFX/CP/RLC eeply + +// udelay(150); +// *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x10003; + *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x30005; //eeply +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) |= 0x00100140; // Softreset SDMA/GRBM +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~0x00100140; +// udelay(150); + + // Enable audio output + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; + *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; + +// // Set pin caps of pin 2 to vendor defined, to hide it +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// // Set pin caps of pin 3 to !HDMI +// *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; +// *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; + // Set pin configuration default + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; + + uart_write_str("kexec: About to relocate and jump to kernel\n"); + + ((jmp_to_linux_t)thunk_copy)( + DM_TO_ID(nix_info.linux_image), + DM_TO_ID(bp_lo), + DM_TO_ID(pml4_base), + (uintptr_t)&gdt_ptr + ); + + // should never reach here + uart_write_str("kexec: unreachable (?)\n"); +} + +// Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) +int hook_icc_query_nowait(u8 *icc_msg) +{ + kern.printf("hook_icc_query_nowait called\n"); + + // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot + //In alternative we can re-enable it here, but sometimes that give problems.. + kern.wlanbt(0x2); + + fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); + + kern.printf("ACPI tables fixed\n"); + + // Transition to BSP and halt other cpus + // smp_no_rendevous_barrier is just nullsub, but it is treated specially by + // smp_rendezvous. This is the easiest way to do this, since we can't assume + // we're already running on BSP. Since smp_rendezvous normally waits on all + // cpus to finish the callbacks, we just never return... + kern.smp_rendezvous(kern.smp_no_rendevous_barrier, + cpu_quiesce_gate, + kern.smp_no_rendevous_barrier, NULL); + + // should never reach here + kern.printf("hook_icc_query_nowait: unreachable (?)\n"); + return 0; +} diff --git a/linux/ps4-kexec-1302-pro/linux_boot.h b/linux/ps4-kexec-1302-pro/linux_boot.h new file mode 100644 index 0000000..2232b35 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/linux_boot.h @@ -0,0 +1,88 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef LINUX_BOOT_H +#define LINUX_BOOT_H + +#include "types.h" + +#define SMAP_TYPE_MEMORY 1 +#define SMAP_TYPE_RESERVED 2 +#define SMAP_TYPE_ACPI_RECLAIM 3 +#define SMAP_TYPE_ACPI_NVS 4 +#define SMAP_TYPE_UNUSABLE 5 +#define SMAP_TYPE_PMEM 7 + +#define X86_SUBARCH_PS4 5 + +struct e820entry { + u64 addr; /* start of memory segment */ + u64 size; /* size of memory segment */ + u32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct setup_header { + u8 setup_sects; + u16 root_flags; + u32 syssize; + u16 ram_size; + u16 vid_mode; + u16 root_dev; + u16 boot_flag; + u16 jump; + u32 header; + u16 version; + u32 realmode_swtch; + u16 start_sys; + u16 kernel_version; + u8 type_of_loader; + u8 loadflags; + u16 setup_move_size; + u32 code32_start; + u32 ramdisk_image; + u32 ramdisk_size; + u32 bootsect_kludge; + u16 heap_end_ptr; + u8 ext_loader_ver; + u8 ext_loader_type; + u32 cmd_line_ptr; + u32 initrd_addr_max; + u32 kernel_alignment; + u8 relocatable_kernel; + u8 min_alignment; + u16 xloadflags; + u32 cmdline_size; + u32 hardware_subarch; + u64 hardware_subarch_data; + u32 payload_offset; + u32 payload_length; + u64 setup_data; + u64 pref_address; + u32 init_size; + u32 handover_offset; +} __attribute__((packed)); + +#define E820MAX 128 /* number of entries in E820MAP */ + +OSTRUCT(boot_params, 0x1000) +OFIELD(0x0c0, u32 ext_ramdisk_image); +OFIELD(0x0c4, u32 ext_ramdisk_size); +OFIELD(0x0c8, u32 ext_cmd_line_ptr); +OFIELD(0x1e8, u8 e820_entries); +OFIELD(0x1f1, struct setup_header hdr); +OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); +OSTRUCT_END + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v); +void prepare_boot_params(struct boot_params *bp, u8 *linux_image); +int hook_icc_query_nowait(u8 *icc_msg); + +#endif diff --git a/linux/ps4-kexec-1302-pro/linux_thunk.S b/linux/ps4-kexec-1302-pro/linux_thunk.S new file mode 100644 index 0000000..f6d2dfb --- /dev/null +++ b/linux/ps4-kexec-1302-pro/linux_thunk.S @@ -0,0 +1,90 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +.intel_syntax noprefix + +.equ setup_sects, 0x1f1 +.equ shdr_syssize, 0x1f4 +.equ pref_address, 0x258 + +.text + +#void jmp_to_linux( +# uintptr_t image_base, rdi +# uintptr_t bootargs, rsi +# uintptr_t new_cr3, rdx +# uintptr_t gdt_ptr rcx +#); +.globl jmp_to_linux +jmp_to_linux: + # switch to new gdt + data segments + cli + lgdt [rcx] + #xor eax, eax + mov eax, 0x18 + mov ds, eax + mov ss, eax + mov es, eax + mov fs, eax + mov gs, eax + + # switch to our own page tables (in low mem) + mov cr3, rdx + + # now we're on our own page tables, so we can obliterate the rest of memory + # TODO make sure we don't inadvertently overwrite (important) smap regions + # I think on ps4 we'll actually want to load to 0x700000 + # since we have tons of free room there. + # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We + # should choose a higher address as pref_address. + + # save args + mov r12, rdi + mov r13, rsi + + # memmove(pref_address, , (syssize * 0x10) / 8) + #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go + #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) + mov rdi, 0x6000000 # should be far from bzImage and initramfs + mov r14, rdi # r14 = pref_address + xor edx, edx + mov dl, [r12 + setup_sects] + inc rdx + shl rdx, 9 # rdx = offsetof(image_base, startup_32) + lea rsi, [r12 + rdx] # src = image_base + startup_32 + mov ecx, [r12 + shdr_syssize] + shl rcx, 4 + add rdi, rcx + add rsi, rcx + sub rdi, 8 + sub rsi, 8 + shr rcx, 3 + std + rep movsq + cld + + # make a tiny stack - we just need it for the lretq. + # what we jump to will not use this stack + lea rsp, [rip + jmp_to_linux_end + 0x200] + and rsp, -0x10 + #push 0 # retaddr + push 0x10 # cs = GDT[2] + add r14, 0x200 # pref_address + startup_64 + push r14 # rip + mov rsi, r13 # bootargs + lretq +jmp_to_linux_end: + +.data + +.globl jmp_to_linux_size +jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux + +.att_syntax prefix diff --git a/linux/ps4-kexec-1302-pro/reboot.h b/linux/ps4-kexec-1302-pro/reboot.h new file mode 100644 index 0000000..20b91f8 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/reboot.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)reboot.h 8.3 (Berkeley) 12/13/94 + * $FreeBSD$ + */ + +#ifndef _SYS_REBOOT_H_ +#define _SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. These are passed to + * the boot program and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ +#define RB_SINGLE 0x002 /* reboot to single user only */ +#define RB_NOSYNC 0x004 /* dont sync before reboot */ +#define RB_HALT 0x008 /* don't reboot, just halt */ +#define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ +#define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ +#define RB_KDB 0x040 /* give control to kernel debugger */ +#define RB_RDONLY 0x080 /* mount root fs read-only */ +#define RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define RB_MINIROOT 0x200 /* Unused placeholder */ +#define RB_VERBOSE 0x800 /* print all potentially useful info */ +#define RB_SERIAL 0x1000 /* use serial port as console */ +#define RB_CDROM 0x2000 /* use cdrom as root */ +#define RB_POWEROFF 0x4000 /* turn the power off if possible */ +#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ +#define RB_MUTE 0x10000 /* start up with the console muted */ +#define RB_SELFTEST 0x20000 /* unused placeholder */ +#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ +#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ +#define RB_PAUSE 0x100000 /* pause after each output line during probe */ +#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ +#define RB_PROBE 0x10000000 /* Probe multiple consoles */ +#define RB_MULTIPLE 0x20000000 /* use multiple consoles */ + +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + +#endif diff --git a/linux/ps4-kexec-1302-pro/string.h b/linux/ps4-kexec-1302-pro/string.h new file mode 100644 index 0000000..70aa668 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/string.h @@ -0,0 +1,96 @@ +/* + * string.h -- standard C string-manipulation functions. + * + * Copyright (C) 2008 Segher Boessenkool + * Copyright (C) 2009 Haxx Enterprises + * Copyright (C) 2010-2016 Hector Martin "marcan" + * + * Portions taken from the Public Domain C Library (PDCLib). + * http://pdclib.rootdirectory.de/ + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +static inline int strcmp(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s1[i] == s2[i]; i++) + ; + + return s1[i] - s2[i]; +} + +static inline void *memset(void *b, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)b)[i] = c; + + return b; +} + +static inline void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} + +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) + return p1[i] - p2[i]; + + return 0; +} + +static inline size_t strlen(const char *s) +{ + size_t len; + + for (len = 0; s[len]; len++) + ; + + return len; +} + +static inline size_t strnlen(const char *s, size_t count) +{ + size_t len; + + for (len = 0; s[len] && len < count; len++) + ; + + return len; +} + +static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) +{ + size_t len, needed; + + len = needed = strnlen(src, maxlen - 1) + 1; + if (len >= maxlen) + len = maxlen - 1; + + memcpy(dest, src, len); + dest[len] = 0; + + return needed - 1; +} + +#endif diff --git a/linux/ps4-kexec-1302-pro/types.h b/linux/ps4-kexec-1302-pro/types.h new file mode 100644 index 0000000..28d6619 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/types.h @@ -0,0 +1,51 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef TYPES_H +#define TYPES_H + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#ifndef TESTING +typedef u8 uint8_t; +typedef u64 size_t; +typedef s64 ssize_t; +typedef u64 uintptr_t; +typedef s64 off_t; +#endif + +#define NULL ((void *)0) + +#define CAT_(x, y) x ## y +#define CAT(x, y) CAT_(x, y) + +#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] +#define OSTRUCT(name, size) struct name { union { OPAD(size); +#define OSTRUCT_END };}; +#define OFIELD(off, field) struct { OPAD(off); field; } + +#define ASSERT_STRSIZE(struc, size) \ + _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#define ENOENT 2 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define ENAMETOOLONG 63 + +#endif diff --git a/linux/ps4-kexec-1302-pro/uart.c b/linux/ps4-kexec-1302-pro/uart.c new file mode 100644 index 0000000..62ea949 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/uart.c @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "uart.h" +#include "kernel.h" +#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) + +#define AEOLIA_UART_BASE 0xD0340000 +#define BAIKAL_UART_BASE 0xC890E000 + +#define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, AEOLIA_UART_BASE + (intf << 12) + (reg << 2)) + +#define UART_REG_DATA 0 +#define UART_REG_IER 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +# define LSR_TXRDY 0x20 +# define LSR_TEMT 0x40 +#define UART_REG_MSR 6 +#define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) +#define UART0_IER UART_REG(32, 0, UART_REG_IER) +#define UART0_IIR UART_REG(32, 0, UART_REG_IIR) +#define UART0_LCR UART_REG(32, 0, UART_REG_LCR) +#define UART0_MCR UART_REG(32, 0, UART_REG_MCR) +#define UART0_LSR UART_REG(32, 0, UART_REG_LSR) +#define UART0_MSR UART_REG(32, 0, UART_REG_MSR) + +void uart_write_byte(u8 b) +{ + int limit; + u64 barrier; + limit = 250000; + while (!(*UART0_LSR & LSR_TXRDY) && --limit) + ; + *UART0_DATA = b; + __sync_fetch_and_add(&barrier, 0); + limit = 250000; + while (!(*UART0_LSR & LSR_TEMT) && --limit) + ; +} + +void uart_write_char(char c) +{ + if (c == '\n') + uart_write_byte('\r'); + + uart_write_byte(c); +} + +void uart_write_str(const char *s) +{ + while (*s) { + uart_write_char(*s++); + } +} diff --git a/linux/ps4-kexec-1302-pro/uart.h b/linux/ps4-kexec-1302-pro/uart.h new file mode 100644 index 0000000..b803e29 --- /dev/null +++ b/linux/ps4-kexec-1302-pro/uart.h @@ -0,0 +1,20 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef UART_H +#define UART_H + +#include "types.h" + +void uart_write_byte(u8 b); +void uart_write_char(char c); +void uart_write_str(const char *s); + +#endif diff --git a/linux/ps4-kexec-1302-pro/x86.h b/linux/ps4-kexec-1302-pro/x86.h new file mode 100644 index 0000000..61b22ff --- /dev/null +++ b/linux/ps4-kexec-1302-pro/x86.h @@ -0,0 +1,149 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef X86_H +#define X86_H + +#define FLAGS_IF (1 << 9) + +#define CR0_WP (1 << 16) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) + +#define PG_V (1 << 0) +#define PG_RW (1 << 1) +#define PG_U (1 << 2) +#define PG_PS (1 << 7) + +#define SEG_TYPE_DATA (0 << 3) +#define SEG_TYPE_READ_WRITE (1 << 1) +#define SEG_TYPE_CODE (1 << 3) +#define SEG_TYPE_EXEC_READ (1 << 1) +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) + +static inline u64 cr0_read(void) +{ + u64 reg; + asm volatile("mov %0, cr0;" : "=r" (reg)); + return reg; +} + +static inline void cr0_write(u64 val) +{ + asm volatile("mov cr0, %0;" :: "r" (val)); +} + +static inline u64 write_protect_disable() +{ + u64 cr0 = cr0_read(); + cr0_write(cr0 & ~CR0_WP); + return cr0; +} + +static inline void write_protect_restore(u64 cr0) +{ + // Use only WP bit of input + cr0_write(cr0_read() | (cr0 & CR0_WP)); +} + +static inline u64 cr3_read(void) +{ + u64 reg; + asm volatile("mov %0, cr3;" : "=r" (reg)); + return reg; +} + +static inline void cr3_write(u64 val) +{ + asm volatile("mov cr3, %0;" :: "r" (val)); +} + +static inline void cr4_pge_disable(void) +{ + u64 cr4_temp; + asm volatile( + "mov %0, cr4;" + "and %0, ~0x80;" + "mov cr4, %0;" + : "=r" (cr4_temp) + ); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +static inline void cpu_stop(void) +{ + for (;;) + asm volatile("cli; hlt;" : : : "memory"); +} + +static inline void outl(int port, unsigned int data) +{ + asm volatile("out %w1, %0" : : "a" (data), "d" (port)); +} + +static inline void wrmsr(u32 msr_id, u64 msr_value) +{ + asm volatile( + "wrmsr" + : + : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) + ); +} + +static inline u64 rdtsc (void) +{ + unsigned int tickl, tickh; + asm volatile( + "rdtsc" + :"=a"(tickl),"=d"(tickh) + ); + return ((u64)tickh << 32) | tickl; +} + +static inline void udelay(unsigned int usec) { + u64 later = rdtsc() + usec * 1594ULL; + while (((s64)(later - rdtsc())) > 0); +} + +static inline void disable_interrupts(void) +{ + asm volatile("cli"); +} + +static inline void enable_interrupts(void) +{ + asm volatile("sti"); +} + +static inline u64 read_flags(void) +{ + u64 flags; + asm volatile("pushf; pop %0;" : "=r" (flags)); + return flags; +} + +static inline u64 intr_disable(void) +{ + u64 flags = read_flags(); + disable_interrupts(); + return flags; +} + +static inline void intr_restore(u64 flags) +{ + // TODO should only IF be or'd in? + asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); +} + +#endif diff --git a/linux/ps4-kexec-1302/LICENSE b/linux/ps4-kexec-1302/LICENSE new file mode 100644 index 0000000..6c1cbba --- /dev/null +++ b/linux/ps4-kexec-1302/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2015-2016 shuffle2 +Copyright (C) 2015-2016 Hector Martin "marcan" +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux/ps4-kexec-1302/Makefile b/linux/ps4-kexec-1302/Makefile new file mode 100644 index 0000000..41fe2d6 --- /dev/null +++ b/linux/ps4-kexec-1302/Makefile @@ -0,0 +1,37 @@ +TOOLCHAIN_PREFIX ?= +CC = $(TOOLCHAIN_PREFIX)gcc +AR = $(TOOLCHAIN_PREFIX)ar +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy + +CFLAGS=$(CFLAG) -DPS4_13_02 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX +CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ + -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ + -fno-asynchronous-unwind-tables \ + -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ + -mcmodel=small -mno-red-zone + +SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ + acpi.c crc32.c + +OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) +DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld + +all: libkexec.a kexec.bin + +%.o: %.c *.h + $(CC) -c $(CFLAGS) -o $@ $< + +%.o: %.S + $(CC) -c $(CFLAGS) -o $@ $< + +libkexec.a: $(OBJS) + $(AR) -rc $@ $(OBJS) + +kexec.elf: libkexec.a kexec.ld + $(CC) $(CFLAGS) -o $@ libkexec.a + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +clean: + rm -f libkexec.a kexec.elf kexec.bin $(OBJS) diff --git a/linux/ps4-kexec-1302/README.md b/linux/ps4-kexec-1302/README.md new file mode 100644 index 0000000..6500878 --- /dev/null +++ b/linux/ps4-kexec-1302/README.md @@ -0,0 +1,121 @@ +# PS4 kexec implementation + +This repo implements a kexec()-style system call for the PS4 Orbis kernel +(FreeBSD derivative). This is designed to boot a Linux kernel directly from +FreeBSD. + +This is not an exploit. It is useless without some mechanism of injecting code +into the PS4 OS kernel. + +## Building + +To build a kexec.bin relocatable binary using the supplied Makefile, just type +`make`. This will also build a kexec.a archive. You can either use the binary +directly, or link the archive into your own project. + +If you link kexec.a with your own code, you need to supply the two symbols +`_start` and `_end` in your linker script, as `kernel_init()` will try to remap +all pages covered by that range as RWX (to make global variable accesses work). +Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this +feature, if you have already taken care of page permissions for the code. + +If you use a compiler toolchain that have a special prefix you can declare it +by passing TOOLCHAIN_PREFIX option to the Makefile like this: + + make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' + +## Usage + +The code is designed to be completely standalone. There is a single entry point: + + int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); + +Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel +symbols and install the sys_kexec system call. The syscall is registered +as number 153 by default (you can change this in kexec.h). The return value +is 0 on success, or negative on error. + +You may pass something other than NULL as `early_printf`. In that case, that +function will be used for debug output during early symbol resolution, before +printf is available. + +Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is +enabled by default, symtab also disappears in newer kernel, we have to +hardcode offsets for some symbols. Currently we use the `early_printf` +given by user to caculate the base address of kernel, then relocate all the +symbols from the kernel base. You could enable this feature like this: + + make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' + +If you do not want to call the syscall from userspace, you can pass the address +of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the +address of `sys_kexec`, so you can invoke it manually (see kexec.h for +its prototype and how the arguments are passed). + +If you are using the standalone kexec.bin blob, then the `kexec_init` function +is always located at offset 0, so simply call the base address of the blob. +Don't forget to pass two NULL arguments (or the appropriate pointers). + +The injected `sys_kexec` system call takes (userspace) pointers to the kernel +and initramfs blobs, their sizes, and a pointer to the (null-terminated) command +line string. From userspace, this looks like this: + + int kexec(void *kernel_image, size_t image_size, + void *initramfs, size_t initramfs_size, + const char *cmdline); + + // syscall() usage: + syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); + +`kexec()` will load the kernel and initramfs into memory, but will not directly +boot them. To boot the loaded kernel, shut down the system. This can be +accomplished by pressing the power button, but can also be done more quickly +and reliably from userspace with the following sequence of system calls (this +kills userspace quickly but still does a controlled filesystem unmount): + + int evf = syscall(540, "SceSysCoreReboot"); + syscall(546, evf, 0x4000, 0); + syscall(541, evf); + // should be syscall(37, 1, 30) but only tested via kill symbol + kill(1, 30); + +Note that this software should be loaded into kernel memory space. If you are +running kernel code from userland mappings, you should either switch to kernel +mappings or separately copy kexec.bin to a location in kernel address space. +While syscalls or exploit code may run properly from userland, the shutdown hook +will not, as it will be called from a different process context. + +## Features + +`kernel_init()` will automatically find the Orbis OS kernel and resolve all +necessary symbols to work. There are no static symbol dependencies. If +`DO_NOT_REMAP_RWX` is not defined (the default), it will also patch +`pmap_protect` to disable the W^X restriction. + +In addition to loading the user-supplied initramfs, `kexec` will locate the +Radeon firmware blobs inside Orbis OS, extract them, convert them to a format +suitable for Linux, and append them as an additional initramfs cpio image to +the existing initramfs. This avoids the need to distribute the Radeon firmware +blobs. The `radeon` module, when compiled into the kernel, will automatically +load this firmware on boot. Note however that most typical initramfs scripts +will wipe the initramfs contents while pivoting to the real system, so if you +compile `radeon` as a module you may not be able to access the firmware after +boot. To cover that case, add some code to your initramfs `/init` script to +copy the firmware to a tmpfs mounted on the real filesystem: + + # assuming real root FS is mounted on /mnt + + mkdir -p /mnt/lib/firmware/radeon + mount -t tmpfs none /mnt/lib/firmware/radeon + cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ + + # now switch_root to /mnt + +This avoids having to permanently store copies of the Radeon firmware, which +isn't really necessary for most use cases. + +There is significant debug logging available, which will appear on the system +UART. Most of the code relies on the kernel `printf` implementation, and +therefore you should patch out the UART output blanker to see it. The final +code that runs on the boot CPU before booting the kernel uses direct UART +writes and is not affected by the blanking feature of Orbis OS. diff --git a/linux/ps4-kexec-1302/acpi.c b/linux/ps4-kexec-1302/acpi.c new file mode 100644 index 0000000..f53f04e --- /dev/null +++ b/linux/ps4-kexec-1302/acpi.c @@ -0,0 +1,296 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "types.h" + +#ifdef TESTING +# include +# include +# include +# include +# include +# include +#else +# include "kernel.h" +# include "string.h" +# define printf kern.printf +#endif + +#define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) +#define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) + +#define PACKED __attribute__((packed)) + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + +struct RSDP { + u64 sig; + u8 checksum; + u8 oemid[6]; + u8 rev; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 rsvd[3]; +} PACKED; + +struct SDTH { + u32 sig; + u32 length; + u8 rev; + u8 checksum; + u8 oem_id[6]; + u8 oem_tid[8]; + u32 oem_rev; + u8 creator_id[4]; + u32 creator_rev; +} PACKED; + +struct RSDT { + struct SDTH hdr; + u32 table_addr[]; +} PACKED; + +struct XSDT { + struct SDTH hdr; + u64 table_addr[]; +} PACKED; + +struct FADT { + struct SDTH hdr; + u32 facs; + u32 dsdt; + // more stuff... +} PACKED; + +struct ivhd_entry4 { + u8 type; + u16 devid; + u8 flags; +} PACKED; + +struct ivhd_header { + u8 type; + u8 flags; + u16 length; + u16 devid; + u16 cap_ptr; + u64 mmio_phys; + u16 pci_seg; + u16 info; + u32 efr_attr; +} PACKED; + +struct IVRS { + struct SDTH hdr; + u32 IVinfo; + u8 reserved[8]; + struct ivhd_header hd_hdr; + struct ivhd_entry4 hd_entries[3]; +} PACKED; + +// We have enough space to use the second half of the 64KB table area +// as scratch space for building the tables +#define BUFFER_OFF 0x8000 + +#define P2M(p) (((u64)(p)) - phys_base + map_base) +#define M2P(p) ((((void*)(p)) - map_base) + phys_base) +#define B2P(p) ((((void*)(p)) - buf_base) + phys_base) + +#define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) +#define PADB(s) p += (s) +#define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) +#define ALLOC(t) (t)ALLOCB(sizeof(t)) +#define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) +#define COPYT(s) COPYB(((struct SDTH*)s)->length, s) +#define COPYTP(s) COPYT(P2M(s)) +#define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) +#define COPYP(t, s) COPY(t, P2M(s)) + +static void rsdp_checksum(struct RSDP *rsdp) { + rsdp->checksum = rsdp->ext_checksum = 0; + + u8 sum = 0; + for (int i = 0; i < 20; i++) + sum += ((u8*)rsdp)[i]; + rsdp->checksum = -sum; + sum = 0; + for (int i = 0; i < sizeof(*rsdp); i++) + sum += ((u8*)rsdp)[i]; + rsdp->ext_checksum = -sum; +} + +static void table_checksum(void *table) { + struct SDTH *hdr = table; + hdr->checksum = 0; + u8 sum = 0; + for (int i = 0; i < hdr->length; i++) + sum += ((u8*)table)[i]; + hdr->checksum = -sum; +} + +#define IVHD_FLAG_ISOC_EN_MASK 0x08 +#define IVHD_DEV_ALL 0x01 +#define IVHD_DEV_SELECT 0x02 +#define IVHD_DEV_SELECT_RANGE_START 0x03 +#define IVHD_DEV_RANGE_END 0x04 + +#define ACPI_DEVFLAG_SYSMGT1 0x10 +#define ACPI_DEVFLAG_SYSMGT2 0x20 + +static void *build_ivrs(struct IVRS *ivrs) { + memset(ivrs, 0, sizeof(*ivrs)); + + ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); + ivrs->hdr.length = sizeof(*ivrs); + ivrs->hdr.rev = 1; + memcpy(ivrs->hdr.oem_id, "F0F ", 6); + memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); + ivrs->hdr.oem_rev = 0x20161225; + memcpy(ivrs->hdr.creator_id, "KEXC", 4); + ivrs->hdr.creator_rev = 0x20161225; + ivrs->IVinfo = 0x00203040; + + struct ivhd_header *hdr = &ivrs->hd_hdr; + hdr->type = 0x10; + hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; + hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); + hdr->devid = PCI_DEVFN(0, 2); + hdr->cap_ptr = 0x40; // from config space + 0x34 + hdr->mmio_phys = 0xfc000000; + hdr->pci_seg = 0; + hdr->info = 0; // msi msg num? (the pci cap should be written by software) + // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 + hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); + + struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; + // on fbsd, all aeolia devfns have active entries except memories (func 6) + // not sure if this is just because it wasn't in use when i dumped it? + // all entries are r/w + // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) + // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) + // Modes: + // 4 level: + // apcie + // 3 level: + // all others + + // the way to encode this info into the IVHD entries is fairly arbitrary... + entries[0].type = IVHD_DEV_SELECT; + entries[0].devid = PCI_DEVFN(20, 0); + entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; + + entries[1].type = IVHD_DEV_SELECT_RANGE_START; + entries[1].devid = PCI_DEVFN(20, 1); + entries[1].flags = 0; + entries[2].type = IVHD_DEV_RANGE_END; + entries[2].devid = PCI_DEVFN(20, 7); + entries[2].flags = 0; + + table_checksum(ivrs); + return ivrs + 1; +} + +void fix_acpi_tables(void *map_base, u64 phys_base) +{ + void *buf_base = map_base + 0x8000; + void *p = buf_base; + memset(buf_base, 0, 0x8000); + + printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); + + struct RSDP *rsdp = COPYP(struct RSDP, phys_base); + printf("RSDT at 0x%x\n", rsdp->rsdt_addr); + printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); + + struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); + struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); + rsdp->rsdt_addr = B2P(rsdt); + + PADB(0x30); // this gives us space for new tables + + struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); + rsdp->xsdt_addr = B2P(xsdt); + + PADB(0x60); + + struct FADT *fadt = NULL; + + int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; + int i; + for (i = 0; i < cnt; i++) { + struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); + printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); + switch (hdr->sig) { + case SIG32('F', 'A', 'C', 'P'): + { + fadt = (void*)hdr; + printf("FACS at 0x%x\n", fadt->facs); + printf("DSDT at 0x%x\n", fadt->dsdt); + // Sony puts the FACS before the FADT, unaligned, which is + // noncompliant, but let's keep it there + u8 *facs = COPYB(64, P2M(fadt->facs)); + fadt = (void*)(hdr = COPYT(hdr)); + fadt->facs = B2P(facs); + PADB(0x38); + break; + } + case SIG32('S', 'S', 'D', 'T'): + { + // Put the DSDT before the SSDT + if (fadt) { + PADB(0xf0); + u8 *dsdt = COPYTP(fadt->dsdt); + fadt->dsdt = B2P(dsdt); + PADB(0x174); + table_checksum(fadt); + } else { + printf("ERROR: no FADT yet?\n"); + } + hdr = COPYT(hdr); + break; + } + default: + hdr = COPYT(hdr); + } + table_checksum(hdr); + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); + } + + xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); + i++; + p = build_ivrs(p); + + rsdt->hdr.length = sizeof(*rsdt) + 4 * i; + xsdt->hdr.length = sizeof(*xsdt) + 8 * i; + + rsdp_checksum(rsdp); + table_checksum(rsdt); + table_checksum(xsdt); + memcpy(map_base, buf_base, p - buf_base); +} + + +#ifdef TESTING + +int main(int argc, char **argv) +{ + int fd; + void *base; + + fd = open(argv[1], O_RDWR); + base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + fix_acpi_tables(base, 0xe0000); + return 0; +} + +#endif diff --git a/linux/ps4-kexec-1302/acpi.h b/linux/ps4-kexec-1302/acpi.h new file mode 100644 index 0000000..5723982 --- /dev/null +++ b/linux/ps4-kexec-1302/acpi.h @@ -0,0 +1,18 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ACPI_H +#define ACPI_H + +#include "types.h" + +void fix_acpi_tables(void *map_base, u64 phys_base); + +#endif diff --git a/linux/ps4-kexec-1302/crc32.c b/linux/ps4-kexec-1302/crc32.c new file mode 100644 index 0000000..7836c18 --- /dev/null +++ b/linux/ps4-kexec-1302/crc32.c @@ -0,0 +1,102 @@ +/*- +* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or +* code or tables extracted from it, as desired without restriction. +* +* First, the polynomial itself and its table of feedback terms. The +* polynomial is +* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 +* +* Note that we take it "backwards" and put the highest-order term in +* the lowest-order bit. The X^32 term is "implied"; the LSB is the +* X^31 term, etc. The X^0 term (usually shown as "+1") results in +* the MSB being 1 +* +* Note that the usual hardware shift register implementation, which +* is what we're using (we're merely optimizing it by doing eight-bit +* chunks at a time) shifts bits into the lowest-order term. In our +* implementation, that means shifting towards the right. Why do we +* do it this way? Because the calculated CRC must be transmitted in +* order from highest-order term to lowest-order term. UARTs transmit +* characters in order from LSB to MSB. By storing the CRC this way +* we hand it to the UART in the order low-byte to high-byte; the UART +* sends each low-bit to hight-bit; and the result is transmission bit +* by bit from highest- to lowest-order term without requiring any bit +* shuffling on our part. Reception works similarly +* +* The feedback terms table consists of 256, 32-bit entries. Notes +* +* The table can be generated at runtime if desired; code to do so +* is shown later. It might not be obvious, but the feedback +* terms simply represent the results of eight shift/xor opera +* tions for all combinations of data and CRC register values +* +* The values must be right-shifted by eight bits by the "updcrc +* logic; the shift must be unsigned (bring in zeroes). On some +* hardware you could probably optimize the shift in assembler by +* using byte-swap instructions +* polynomial $edb88320 +* +* +* CRC32 code derived from work by Gary S. Brown. +*/ + +#include "crc32.h" + +static u32 crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +u32 crc32(u32 crc, const void *buf, size_t size) +{ + const u8 *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} diff --git a/linux/ps4-kexec-1302/crc32.h b/linux/ps4-kexec-1302/crc32.h new file mode 100644 index 0000000..777278d --- /dev/null +++ b/linux/ps4-kexec-1302/crc32.h @@ -0,0 +1,7 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +u32 crc32(u32 crc, const void *buf, size_t size); + +#endif diff --git a/linux/ps4-kexec-1302/elf.h b/linux/ps4-kexec-1302/elf.h new file mode 100644 index 0000000..63371c7 --- /dev/null +++ b/linux/ps4-kexec-1302/elf.h @@ -0,0 +1,71 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + u8 e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + u64 e_entry; + u64 e_phoff; + u64 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf64_Ehdr; + +typedef struct { + u32 p_type; + u32 p_flags; + u64 p_offset; + void *p_vaddr; + u64 p_paddr; + u64 p_filesz; + u64 p_memsz; + u64 p_align; +} Elf64_Phdr; + +#define PT_DYNAMIC 2 +#define PT_PHDR 6 + +#define DT_NULL 0 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_STRSZ 10 + +typedef struct { + s64 d_tag; + union { + u64 d_val; + void *d_ptr; + } d_un; +} Elf64_Dyn; + +typedef struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + void *st_value; + u64 st_size; +} Elf64_Sym; + +#endif diff --git a/linux/ps4-kexec-1302/firmware.c b/linux/ps4-kexec-1302/firmware.c new file mode 100644 index 0000000..a831b1c --- /dev/null +++ b/linux/ps4-kexec-1302/firmware.c @@ -0,0 +1,479 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "firmware.h" +#include "types.h" +#include "kernel.h" +#include "string.h" +#include "types.h" +#include "crc32.h" + +#define DIR 0040755 +#define FILE 0100644 + +struct firmware_header { + u32 size_bytes; + u32 header_size_bytes; + u16 header_version_major; + u16 header_version_minor; + u16 ip_version_major; + u16 ip_version_minor; + u32 ucode_version; + u32 ucode_size_bytes; + u32 ucode_array_offset_bytes; + u32 crc32; + union { + struct { + u32 ucode_feature_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } gfx1; + struct { + u32 ucode_feature_version; + u32 save_and_restore_offset; + u32 clear_state_descriptor_offset; + u32 avail_scratch_ram_locations; + u32 master_pkt_description_offset; + u8 end[]; + } rlc1; + struct { + u32 ucode_feature_version; + u32 ucode_change_version; + u32 jt_offset; + u32 jt_size; + u8 end[]; + } sdma1; + u8 raw[0xe0]; + }; +}; + +static inline char hex(u8 c) +{ + if (c <= 9) + return '0' + c; + return 'a' + c - 10; +} + +static void hex8(u8 **p, u32 val) +{ + *(*p)++ = hex(val >> 28); + *(*p)++ = hex((val >> 24) & 0xf); + *(*p)++ = hex((val >> 20) & 0xf); + *(*p)++ = hex((val >> 16) & 0xf); + *(*p)++ = hex((val >> 12) & 0xf); + *(*p)++ = hex((val >> 8) & 0xf); + *(*p)++ = hex((val >> 4) & 0xf); + *(*p)++ = hex(val & 0xf); +} + +void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) +{ + size_t name_len = strlen(name); + + // Pad to 4 byte multiple + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; + + memcpy(*p, "070701", 6); + *p += 6; + hex8(p, 0); // c_ino + hex8(p, mode); // c_mode + hex8(p, 0); // c_uid + hex8(p, 0); // c_gid + hex8(p, 1); // c_nlink + hex8(p, 0); // c_mtime + hex8(p, size); // c_filesize + hex8(p, 0); // c_maj + hex8(p, 0); // c_min + hex8(p, 0); // c_rmaj + hex8(p, 0); // c_rmin + hex8(p, name_len + 1); // c_namesize + hex8(p, 0); // c_chksum + memcpy(*p, name, name_len); + *p += name_len; + *(*p)++ = 0; + + while (((uintptr_t)*p) & 0x3) + *(*p)++ = 0; +} + +struct fw_header_t { + u64 size_words; + char *unk_ident; + u64 unk; + void *blob; + u64 unk2; +}; + +struct fw_info_t { + struct fw_header_t *rlc; + struct fw_header_t *sdma0; + struct fw_header_t *sdma1; + struct fw_header_t *ce; + struct fw_header_t *pfp; + struct fw_header_t *me; + struct fw_header_t *mec1; + struct fw_header_t *mec2; +}; + +struct fw_expected_sizes_t { + u64 rlc; + u64 sdma0; + u64 sdma1; + u64 ce; + u64 pfp; + u64 me; + u64 mec1; + u64 mec2; +}; +static const struct fw_expected_sizes_t liverpool_fw_sizes = { + LVP_FW_RLC_SIZE, + LVP_FW_SDMA_SIZE, + LVP_FW_SDMA1_SIZE, + LVP_FW_CE_SIZE, + LVP_FW_PFP_SIZE, + LVP_FW_ME_SIZE, + LVP_FW_MEC_SIZE, + LVP_FW_MEC2_SIZE +}; +static const struct fw_expected_sizes_t gladius_fw_sizes = { + GL_FW_RLC_SIZE, + GL_FW_SDMA_SIZE, + GL_FW_SDMA1_SIZE, + GL_FW_CE_SIZE, + GL_FW_PFP_SIZE, + GL_FW_ME_SIZE, + GL_FW_MEC_SIZE, + GL_FW_MEC2_SIZE +}; + +void copy_edid(u8 **p, int sz) +{ + int i; + u8 *edid = *p; + u8 *off_edid = kern.edid; + + memset(edid, 0, sz); + *p += sz; + + for(i = 0; i < sz; i++) + *(edid + i) = *(off_edid + i); + + *p += sz; +} + +void copy_eap_hdd_key(u8 **p) +{ + int i; + u8 *eap_key = *p; + u8 *off_eap_key = kern.eap_hdd_key; + + memset(eap_key, 0, 0x20); + *p += 0x20; + + for(i = 0; i < 0x20; i++) + { + if(i < 0x10) + *(eap_key + i) = *(off_eap_key + 0xF - i); + else + *(eap_key + i) = *(off_eap_key + 0x2F - i); + } + *p += 0x20; +} + +int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + kern.printf("Copying %s firmware\n", name); + if (expected_size != (hdr->size_words * 4)) { + kern.printf("copy_firmware: %s: expected size %d, got %d\n", + name, expected_size, hdr->size_words * 4); + return 0; + } + + struct firmware_header *fhdr = (struct firmware_header*)*p; + memset(fhdr, 0, sizeof(*fhdr)); + *p += sizeof(*fhdr); + + memcpy(*p, hdr->blob, expected_size); + + fhdr->size_bytes = expected_size + sizeof(*fhdr); + fhdr->header_size_bytes = offsetof(struct firmware_header, raw); + fhdr->header_version_major = 1; + fhdr->header_version_minor = 0; + fhdr->ucode_version = 0x10; + fhdr->ucode_size_bytes = expected_size; + fhdr->ucode_array_offset_bytes = sizeof(*fhdr); + + *p += expected_size; + + return 1; +} + +int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); + fhdr->gfx1.ucode_feature_version = 21; + fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 7; + fhdr->ip_version_minor = 2; + fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); + fhdr->rlc1.ucode_feature_version = 1; + fhdr->rlc1.save_and_restore_offset = 0x90; + fhdr->rlc1.clear_state_descriptor_offset = 0x3d; + fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? + fhdr->rlc1.master_pkt_description_offset = 0; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) +{ + struct firmware_header *fhdr = (struct firmware_header*)*p; + if (!copy_firmware(p, name, hdr, expected_size)) + return 0; + + fhdr->ip_version_major = 2; + fhdr->ip_version_minor = 1; + fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); + fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; + fhdr->sdma1.ucode_change_version = 0; + fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; + fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; + + fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); + return 1; +} + +static const u32 pfp_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc4200016, // ldw r8, [r0, #0x16] + 0xdc030000, // mov ctr, r0 + 0xcc000049, // stw r0, [r0, #0x49] + 0xcc200013, // stw r0, [r8, #0x13] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 ce_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc420000c, // ldw r8, [r0, #0xc] + 0xdc030000, // mov ctr, r0 + 0xcc00002f, // stw r0, [r0, #0x2f] + 0xcc200012, // stw r0, [r8, #0x12] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +static const u32 mec_nop_handler[] = { + 0xdc120000, // mov r4, ctr + 0x31144000, // seteq r5, r4, #0x4000 + 0x95400009, // cbz r5, l0 + 0xc43c000c, // ldw r15, [r0, #0x9] + 0xdc030000, // mov ctr, r0 + 0xcc00002b, // stw r0, [r0, #0x2b] + 0xcc3c000d, // stw r0, [r15, #0xd] + 0xc424007e, // ldw r9, [r0, #0x7e] + 0x96400000, // l1: cbz r9, l1 + 0x7c408001, // mov r2, r1 + 0x88000000, // btab + 0xd440007f, // l0: stm r1, [r0, #0x7f] + 0x7c408001, // mov r2, r1 + 0x88000000, // btab +}; + +#define PACKET_TYPE_NOP 0x10 + +static void patch_fw(void *p, const u32 *handler, int handler_size) { + int size = ((struct firmware_header*)p)->ucode_size_bytes; + int code_size = (size & ~0xfff) / 4; + int nop_start = code_size - 0x10; + + u32 *fw = p + sizeof(struct firmware_header); + kern.printf("NOP handler at 0x%x\n", nop_start); + memcpy(&fw[nop_start], handler, handler_size); + + // patch the branch table entry + for (int off = code_size; off < size/4; off++) { + if ((fw[off] >> 16) == PACKET_TYPE_NOP) { + fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; + } + } +} + +struct fw_info_t *get_fw_info() { + if (kern.gc_get_fw_info) { + return kern.gc_get_fw_info(); + } else if (kern.Starsha_UcodeInfo) { + return kern.Starsha_UcodeInfo; + } else { + return NULL; + } +} + +const struct fw_expected_sizes_t *get_fw_expected_sizes() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return &gladius_fw_sizes; + } else { + return &liverpool_fw_sizes; + } +} + +const char * get_gpu_name() { + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { + return "gladius"; + } else { + return "liverpool"; + } +} + +ssize_t firmware_extract(void *dest) +{ + u8 *p = dest; + + // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. + struct fw_info_t *info = get_fw_info(); + if (!info) { + kern.printf("firmware_extract: Could not locate firmware table"); + return -1; + } + const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); + + //Eap hdd key + cpio_hdr(&p, "key", DIR, 0); + cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); + copy_eap_hdd_key(&p); + + cpio_hdr(&p, "lib", DIR, 0); + cpio_hdr(&p, "lib/firmware", DIR, 0); + + /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ + int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); + cpio_hdr(&p, "lib/firmware/edid", DIR, 0); + cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); + copy_edid(&p, edid_sz); + + char dir[7]; + if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) + kern.snprintf(dir, sizeof(dir), "amdgpu"); + else + kern.snprintf(dir, sizeof(dir), "amdgpu"); + + char dir_path[64]; + kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); + cpio_hdr(&p, dir_path, DIR, 0); + + char pfp_path[64]; + kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", pfp_path); + cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); + u8 *pfp = p; + if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) + return -1; + patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); + + char me_path[64]; + kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", me_path); + cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); + if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) + return -1; + + char ce_path[64]; + kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", ce_path); + cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); + u8 *ce = p; + if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) + return -1; + patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); + + char mec_path[64]; + kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec_path); + cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); + u8 *mec1 = p; + if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) + return -1; + patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); + + char mec2_path[64]; + kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", mec2_path); + cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); + u8 *mec2 = p; + if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) + return -1; + patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); + + char rlc_path[64]; + kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", rlc_path); + cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); + if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) + return -1; + + char sdma_path[64]; + kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma_path); + cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); + if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + char sdma1_path[64]; + kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); + kern.printf("firmware_extract: Extract %s \n", sdma1_path); + cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); + if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) + return -1; + cpio_hdr(&p, "TRAILER!!!", FILE, 0); + + size_t size = p - (u8*)dest; + if (size > FW_CPIO_SIZE) { + kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); + return -1; + } + + return size; +} diff --git a/linux/ps4-kexec-1302/firmware.h b/linux/ps4-kexec-1302/firmware.h new file mode 100644 index 0000000..4835983 --- /dev/null +++ b/linux/ps4-kexec-1302/firmware.h @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include "types.h" + +//sizes eap_hdd_key +#define EAP_HDD_KEY_SIZE 0x20 +#define EDID_SIZE 256 + +// sizes for liverpool +#define LVP_FW_CE_SIZE 8576 +#define LVP_FW_ME_SIZE 16768 +#define LVP_FW_MEC_SIZE 16768 +#define LVP_FW_MEC2_SIZE 16768 +#define LVP_FW_PFP_SIZE 16768 +#define LVP_FW_RLC_SIZE 6144 +#define LVP_FW_SDMA_SIZE 4200 +#define LVP_FW_SDMA1_SIZE 4200 +// sizes for gladius +#define GL_FW_CE_SIZE 8576 +#define GL_FW_ME_SIZE 16768 +#define GL_FW_MEC_SIZE 16768 +#define GL_FW_MEC2_SIZE 16768 +#define GL_FW_PFP_SIZE 16768 +#define GL_FW_RLC_SIZE 8192 +#define GL_FW_SDMA_SIZE 4200 +#define GL_FW_SDMA1_SIZE 4200 + +#define MAX(x ,y) (((x) > (y)) ? (x) : (y)) +#define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) + +#define FW_CE_SIZE MAX_FW_SIZE(CE) +#define FW_ME_SIZE MAX_FW_SIZE(ME) +#define FW_MEC_SIZE MAX_FW_SIZE(MEC) +#define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) +#define FW_PFP_SIZE MAX_FW_SIZE(PFP) +#define FW_RLC_SIZE MAX_FW_SIZE(RLC) +#define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) +#define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) + +// Conservative value (max 113 bytes plus name size plus alignment) +#define CPIO_HEADER_SIZE 256 + +#define FW_HEADER_SIZE 256 + +// Leave space for 16 files (currently 12) +#define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ + FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ + FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ + FW_HEADER_SIZE * 8) + +ssize_t firmware_extract(void *dest); + +#endif diff --git a/linux/ps4-kexec-1302/kernel.c b/linux/ps4-kexec-1302/kernel.c new file mode 100644 index 0000000..b6d31b4 --- /dev/null +++ b/linux/ps4-kexec-1302/kernel.c @@ -0,0 +1,383 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "string.h" +#include "elf.h" +#include "x86.h" +#include "../magic.h" + +struct ksym_t kern; +int (*early_printf)(const char *fmt, ...) = NULL; + +#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) + +#ifdef NO_SYMTAB + +#define RESOLVE_NOERR(name) do { \ + if (kern_off_ ## name == 0) { \ + kern.name = 0; \ + } else { \ + kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ + } \ +} while (0); + +#define RESOLVE(name) do { \ + if (kern_off_ ## name == 0) { \ + return 0; \ + } \ + RESOLVE_NOERR(name) \ +} while (0); + +#else + +#define KERNSIZE 0x2000000 + +static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; +static Elf64_Sym *symtab; +static char *strtab; +static size_t strtab_size; + +static Elf64_Ehdr *find_kern_ehdr(void) +{ + // Search for the kernel copy embedded in ubios, then follow it to see + // where it was relocated to + for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; + if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { + for (size_t i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; + if (phdr->p_type == PT_PHDR) { + return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); + } + } + } + } + return NULL; +} + +static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf64_Dyn *)phdr->p_vaddr; + } + } + return NULL; +} + +static int elf_parse_dyn(Elf64_Dyn *dyn) +{ + for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { + switch (dp->d_tag) { + case DT_SYMTAB: + symtab = (Elf64_Sym *)dp->d_un.d_ptr; + break; + case DT_STRTAB: + strtab = (char *)dp->d_un.d_ptr; + break; + case DT_STRSZ: + strtab_size = dp->d_un.d_val; + break; + } + } + return symtab && strtab && strtab_size; +} + +void *kernel_resolve(const char *name) +{ + for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { + if (!strcmp(name, &strtab[sym->st_name])) { + eprintf("kern.%s = %p\n", name, (void*)sym->st_value); + return (void *)sym->st_value; + } + } + eprintf("Failed to resolve symbol '%s'\n", name); + return NULL; +} + +#define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) +#define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; + +#endif + +static int resolve_symbols(void) +{ + RESOLVE(printf); + RESOLVE(snprintf); + early_printf = kern.printf; + RESOLVE(copyin); + RESOLVE(copyout); + RESOLVE(copyinstr); + RESOLVE(kernel_map); + RESOLVE(kernel_pmap_store); + RESOLVE(kmem_alloc_contig); + RESOLVE(kmem_free); + RESOLVE(pmap_extract); + RESOLVE(pmap_protect); + RESOLVE(sysent); + RESOLVE(sched_pin); + RESOLVE(sched_unpin); + RESOLVE(smp_rendezvous); + RESOLVE(smp_no_rendevous_barrier); + RESOLVE(icc_query_nowait); + RESOLVE_NOERR(Starsha_UcodeInfo); + RESOLVE_NOERR(gpu_devid_is_9924); + RESOLVE_NOERR(gc_get_fw_info); + RESOLVE_NOERR(eap_hdd_key); + RESOLVE_NOERR(edid); + RESOLVE(wlanbt); + RESOLVE(kern_reboot); + RESOLVE(set_gpu_freq); + RESOLVE(set_pstate); + RESOLVE(update_vddnp); + RESOLVE(set_cu_power_gate); + RESOLVE_NOERR(pstate_before_shutdown); + return 1; +} + +#define M_WAITOK 0x0002 +#define M_ZERO 0x0100 + +#define VM_MEMATTR_DEFAULT 0x06 + +void *kernel_alloc_contig(size_t size) +{ + // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... + vm_offset_t ret = 0; + while(!(ret = kern.kmem_alloc_contig( + *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, + ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); + + /*if (!ret) { + kern.printf("Failed to allocate %zud bytes\n", size); + return NULL; + }*/ + return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); +} + +void kernel_free_contig(void *addr, size_t size) +{ + if (!addr) + return; + kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); +} + +int kernel_hook_install(void *target, void *hook) +{ + uintptr_t t = (uintptr_t)target; // addr to redirect to + uintptr_t h = (uintptr_t)hook; // place to write the thunk + + if (!hook || !target) { + return 0; + } + + kern.printf("kernel_hook_install(%p, %p)\n", target, hook); + + if (!(t & (1L << 63))) { + kern.printf("\n===================== WARNING =====================\n"); + kern.printf("hook target function address: %p\n", target); + kern.printf("It looks like we're running from userland memory.\n"); + kern.printf("Please run this code from a kernel memory mapping.\n\n"); + return 0; + } + s64 displacement = t - (h + 5); + + kern.sched_pin(); + u64 wp = write_protect_disable(); + if (displacement < -0x80000000 || displacement > 0x7fffffff) { + kern.printf(" Using 64bit absolute jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[2]; + s32 zero; + void *target; + } jmp = { + .op = { 0xff, 0x25 }, + .zero = 0, + .target = target, + }; + ASSERT_STRSIZE(struct jmp_t, 14); + memcpy(hook, &jmp, sizeof(jmp)); + } else { + kern.printf(" Using 32bit relative jump\n"); + struct __attribute__((packed)) jmp_t{ + u8 op[1]; + s32 imm; + } jmp = { + .op = { 0xe9 }, + .imm = displacement, + }; + ASSERT_STRSIZE(struct jmp_t, 5); + memcpy(hook, &jmp, sizeof(jmp)); + } + wbinvd(); + write_protect_restore(wp); + kern.sched_unpin(); + + return 1; +} + +void kernel_syscall_install(int num, void *call, int narg) +{ + struct sysent_t *sy = &kern.sysent[num]; + + kern.sched_pin(); + u64 wp = write_protect_disable(); + + memset(sy, 0, sizeof(*sy)); + sy->sy_narg = narg; + sy->sy_call = call; + sy->sy_thrcnt = 1; + + write_protect_restore(wp); + kern.sched_unpin(); +} + +void kernel_remap(void *start, void *end, int perm) +{ + u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); + u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); + + kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); + kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); +} + +static volatile int _global_test = 0; + +#ifndef DO_NOT_REMAP_RWX +extern u8 _start[], _end[]; + +static int patch_pmap_check(void) +{ + u8 *p; + + for (p = (u8*)kern.pmap_protect; + p < ((u8*)kern.pmap_protect + 0x500); p++) { + #ifdef PS4_6_72 + if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 + p[5] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + /*#ifdef PS4_5_05 + if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { + p[1] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + }*/ + #else + if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { + p[2] = 0; + kern.printf("pmap_protect patch successful (found at %p)\n", p); + return 1; + } + #endif + } + kern.printf("pmap_protect patch failed!\n"); + return 0; +} +#endif + +int kernel_init(void *_early_printf) +{ + int rv = -1; + + if (_early_printf) + early_printf = _early_printf; + + eprintf("kernel_init()\n"); + +#ifdef KASLR + // use `early_printf` to calculate kernel base + if (early_printf == NULL) + return 0; + + kern.kern_base = (u64)(early_printf - kern_off_printf); + if ((kern.kern_base & PAGE_MASK) != 0) { + eprintf("Kernel base is not aligned\n"); + return 0; + } else { + eprintf("Kernel base = %llx\n", kern.kern_base); + } + + u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); + u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); + +#else + kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 + + u64 DMPML4I = 0x1fc; + u64 DMPDPI = 0; +#endif + + kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); + eprintf("Direct map base = %llx\n", kern.dmap_base); + + // We may not be mapped writable yet, so to be able to write to globals + // we need WP disabled. + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + +#ifndef NO_SYMTAB + Elf64_Ehdr *ehdr = find_kern_ehdr(); + if (!ehdr) { + eprintf("Could not find kernel ELF header\n"); + goto err; + } + eprintf("ELF header at %p\n", ehdr); + + Elf64_Dyn *dyn = elf_get_dyn(ehdr); + if (!dyn) { + eprintf("Could not find kernel dynamic header\n"); + goto err; + } + eprintf("ELF dynamic section at %p\n", dyn); + + if (!elf_parse_dyn(dyn)) { + eprintf("Failed to parse ELF dynamic section\n"); + goto err; + } +#endif + + if (!resolve_symbols()) { + eprintf("Failed to resolve all symbols\n"); + goto err; + } + + // Pin ourselves as soon as possible. This is expected to be released by the caller. + kern.sched_pin(); + +#ifndef DO_NOT_REMAP_RWX + if (!patch_pmap_check()) + goto err; +#endif + +#ifndef DO_NOT_REMAP_RWX + // kernel_remap may need interrupts, but may not write to globals! + enable_interrupts(); + kernel_remap(_start, _end, 7); + disable_interrupts(); +#endif + + // Writing to globals is now safe. + + kern.printf("Testing global variable access (write protection)...\n"); + _global_test = 1; + kern.printf("OK.\n"); + + kern.printf("Kernel interface initialized\n"); + rv = 0; + +err: + write_protect_restore(wp); + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1302/kernel.h b/linux/ps4-kexec-1302/kernel.h new file mode 100644 index 0000000..1833f5b --- /dev/null +++ b/linux/ps4-kexec-1302/kernel.h @@ -0,0 +1,126 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KERNEL_H +#define KERNEL_H + +#include "types.h" +#include "reboot.h" + +#define PAGE_SIZE 0x4000 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define PML4SHIFT 39 +#define PDPSHIFT 30 +#define PDRSHIFT 21 +#define PAGE_SHIFT 12 + +#define KVADDR(l4, l3, l2, l1) ( \ + ((unsigned long)-1 << 47) | \ + ((unsigned long)(l4) << PML4SHIFT) | \ + ((unsigned long)(l3) << PDPSHIFT) | \ + ((unsigned long)(l2) << PDRSHIFT) | \ + ((unsigned long)(l1) << PAGE_SHIFT)) + +#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) +#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX + +typedef u64 vm_paddr_t; +typedef u64 vm_offset_t; +typedef u64 vm_size_t; +typedef void * vm_map_t; +typedef char vm_memattr_t; +typedef void * pmap_t; + +typedef void (*smp_rendezvous_callback_t)(void *); + +struct sysent_t { + int sy_narg; + void *sy_call; + u16 sy_auevent; + void *sy_systrace_args_func; + int sy_entry; + int sy_return; + int sy_flags; + int sy_thrcnt; +}; + +struct ksym_t { + // two parameters related to kaslr (they are not symbols) + uintptr_t kern_base; + uintptr_t dmap_base; + + int (*printf)(const char *fmt, ...); + int (*snprintf)(const char *fmt, ...); + int (*copyin)(const void *uaddr, void *kaddr, size_t len); + int (*copyout)(const void *kaddr, void *uaddr, size_t len); + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); + + void **kernel_map; + void *kernel_pmap_store; + vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, + vm_paddr_t low, vm_paddr_t high, + unsigned long alignment, + unsigned long boundary, + vm_memattr_t memattr); + void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); + vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); + void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); + + struct sysent_t *sysent; + + void (*sched_pin)(void); + void (*sched_unpin)(void); + void (*smp_rendezvous)(smp_rendezvous_callback_t, + smp_rendezvous_callback_t, + smp_rendezvous_callback_t, void *); + // yes...it is misspelled :) + void (*smp_no_rendevous_barrier)(void *); + void *icc_query_nowait; + void *Starsha_UcodeInfo; + int (*gpu_devid_is_9924)(); + void *(*gc_get_fw_info)(); + void *eap_hdd_key; + void *edid; + void (*wlanbt)(unsigned int state); + int (*kern_reboot)(int magic); + void(*set_gpu_freq)(unsigned int num, unsigned int freq); + void(*set_pstate)(unsigned int val); + void(*update_vddnp)(unsigned int val); + void(*set_cu_power_gate)(unsigned int val); + void *pstate_before_shutdown; +}; + +extern struct ksym_t kern; + +static inline int curcpu(void) +{ + int cpuid; + // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw + asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); + return cpuid; +} + +// Assign a working printf function to this to debug the symbol resolver +extern int (*early_printf)(const char *fmt, ...); + +void *kernel_resolve(const char *name); + +void *kernel_alloc_contig(size_t size); +void kernel_free_contig(void *addr, size_t size); + +void kernel_remap(void *start, void *end, int perm); + +void kernel_syscall_install(int num, void *call, int narg); +int kernel_hook_install(void *target, void *hook); + +int kernel_init(void *early_printf); + +#endif diff --git a/linux/ps4-kexec-1302/kexec.c b/linux/ps4-kexec-1302/kexec.c new file mode 100644 index 0000000..d659229 --- /dev/null +++ b/linux/ps4-kexec-1302/kexec.c @@ -0,0 +1,229 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "kernel.h" +#include "linux_boot.h" +#include "x86.h" +#include "kexec.h" +#include "firmware.h" +#include "string.h" +#include "acpi.h" + +static int k_copyin(const void *uaddr, void *kaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(kaddr, uaddr, len); + return 0; +} + +static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + const char *ustr = (const char*)uaddr; + char *kstr = (char*)kaddr; + size_t ret; + if (!uaddr || !kaddr) + return EFAULT; + ret = strlcpy(kstr, ustr, len); + if (ret >= len) { + if (done) + *done = len; + return ENAMETOOLONG; + } else { + if (done) + *done = ret + 1; + } + return 0; +} + +static int k_copyout(const void *kaddr, void *uaddr, size_t len) +{ + if (!uaddr || !kaddr) + return EFAULT; + memcpy(uaddr, kaddr, len); + return 0; +} + +int sys_kexec(void *td, struct sys_kexec_args *uap) +{ + int err = 0; + size_t initramfs_size = uap->initramfs_size; + void *image = NULL; + void *initramfs = NULL; + size_t firmware_size = 0; + struct boot_params *bp = NULL; + size_t cmd_line_maxlen = 0; + char *cmd_line = NULL; + + int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; + int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; + int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; + + kern.printf("sys_kexec invoked\n"); + kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, + uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); + + // Look up our shutdown hook point + void *icc_query_nowait = kern.icc_query_nowait; + if (!icc_query_nowait) { + err = ENOENT; + goto cleanup; + } + + // Set gpu frequencies and pstate + // FAT&SLIM / PRO + kern.set_pstate(3); + + kern.set_gpu_freq(0, 800); //800 //800 + kern.set_gpu_freq(1, 673); //673 //853 + kern.set_gpu_freq(2, 609); //610 //711 + kern.set_gpu_freq(3, 800); //800 //800 + kern.set_gpu_freq(4, 800); //800 //911 + kern.set_gpu_freq(5, 711); //711 //800 + kern.set_gpu_freq(6, 711); //711 //984 + kern.set_gpu_freq(7, 673); //673 //673 + + kern.update_vddnp(0x12); + kern.set_cu_power_gate(0x12); + + // Copy in kernel image + image = kernel_alloc_contig(uap->image_size); + if (!image) { + kern.printf("Failed to allocate image\n"); + err = ENOMEM; + goto cleanup; + } + err = copyin(uap->image, image, uap->image_size); + if (err) { + kern.printf("Failed to copy in image\n"); + goto cleanup; + } + + // Copy in initramfs + initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); + if (!initramfs) { + kern.printf("Failed to allocate initramfs\n"); + err = ENOMEM; + goto cleanup; + } + + err = firmware_extract(((u8*)initramfs)); + if (err < 0) { + kern.printf("Failed to extract GPU firmware - continuing anyway\n"); + } else { + firmware_size = err; + } + + if (initramfs_size) { + err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); + if (err) { + kern.printf("Failed to copy in initramfs\n"); + goto cleanup; + } + } + initramfs_size += firmware_size; + + // Copy in cmdline + cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; + cmd_line = kernel_alloc_contig(cmd_line_maxlen); + if (!cmd_line) { + kern.printf("Failed to allocate cmdline\n"); + err = ENOMEM; + goto cleanup; + } + err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); + if (err) { + kern.printf("Failed to copy in cmdline\n"); + goto cleanup; + } + cmd_line[cmd_line_maxlen - 1] = 0; + + kern.printf("\nkexec parameters:\n"); + kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); + kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", + initramfs_size, uap->initramfs_size); + kern.printf(" Kernel command line: %s\n", cmd_line); + kern.printf(" Kernel image buffer: %p\n", image); + kern.printf(" Initramfs buffer: %p\n", initramfs); + + // Allocate our boot params + bp = kernel_alloc_contig(sizeof(*bp)); + if (!bp) { + kern.printf("Failed to allocate bp\n"); + err = ENOMEM; + goto cleanup; + } + + // Initialize bp + // TODO should probably do this from cpu_quiesce_gate, then bp doesn't + // need to be allocated here, just placed directly into low mem + set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); + + prepare_boot_params(bp, image); + + // Hook the final ICC shutdown function + if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { + kern.printf("Failed to install shutdown hook\n"); + err = EINVAL; + goto cleanup; + } + + kern.printf("******************************************************\n"); + kern.printf("kexec successfully armed. Please shut down the system.\n"); + kern.printf("******************************************************\n\n"); + +/* + kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); + if (kern.kern_reboot(RB_POWEROFF) == -1) + kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); +*/ + return 0; + +cleanup: + kernel_free_contig(cmd_line, cmd_line_maxlen); + kernel_free_contig(bp, sizeof(*bp)); + kernel_free_contig(image, uap->image_size); + kernel_free_contig(initramfs, uap->initramfs_size); + return err; + + copyout(NULL, NULL, 0); +} + +int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) +{ + int rv = 0; + + // potentially needed to write early_printf + u64 flags = intr_disable(); + u64 wp = write_protect_disable(); + + if (kernel_init(_early_printf) < 0) { + rv = -1; + goto cleanup; + } + + kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); + kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); + kern.printf("kexec_init() successful\n\n"); + + if (sys_kexec_ptr) + *sys_kexec_ptr = sys_kexec; + +cleanup: + write_protect_restore(wp); + if (kern.sched_unpin && wp & CR0_WP) { + // If we're returning to a state with WP enabled, assume the caller + // wants the thread unpinned. Else the caller is expected to + // call kern.sched_unpin() manually. + kern.sched_unpin(); + } + intr_restore(flags); + return rv; +} diff --git a/linux/ps4-kexec-1302/kexec.h b/linux/ps4-kexec-1302/kexec.h new file mode 100644 index 0000000..6c31637 --- /dev/null +++ b/linux/ps4-kexec-1302/kexec.h @@ -0,0 +1,38 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef KEXEC_H +#define KEXEC_H + +#include "types.h" + +#define SYS_KEXEC 153 +#define SYS_KEXEC_NARGS 6 + +struct sys_kexec_args { + void *image; + size_t image_size; + void *initramfs; + size_t initramfs_size; + char *cmd_line; + int vram_gb; +}; + +typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); + +// Note: td is unused, you can pass NULL if you call this directly. +int sys_kexec(void *td, struct sys_kexec_args *uap); + +int kernel_init(void *early_printf); + +int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) + __attribute__ ((section (".init"))); + +#endif diff --git a/linux/ps4-kexec-1302/kexec.ld b/linux/ps4-kexec-1302/kexec.ld new file mode 100644 index 0000000..1bcf26c --- /dev/null +++ b/linux/ps4-kexec-1302/kexec.ld @@ -0,0 +1,25 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +ENTRY(kexec_init) + +SECTIONS { + /* We don't do GOT relocation and rely on nothing ending up using the GOT + * (-fno-common helps here) */ + /DISCARD/ : { *(.comment) *(.got) } + _start = .; + .init : { *(.init) *(.init.*) } + .text : { *(.text) *(.text.*) } + .data : { *(.data) *(.data.*) } + .rodata : { *(.rodata) *(.rodata.*) } + .bss : { *(.bss) *(.bss.*) *(COMMON)} + .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ + _end = .; +} diff --git a/linux/ps4-kexec-1302/linux_boot.c b/linux/ps4-kexec-1302/linux_boot.c new file mode 100644 index 0000000..197ef63 --- /dev/null +++ b/linux/ps4-kexec-1302/linux_boot.c @@ -0,0 +1,441 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "linux_boot.h" +#include "types.h" +#include "string.h" +#include "x86.h" +#include "kernel.h" +#include "uart.h" +#include "acpi.h" + +void uart_write_byte(u8 b); + +static u64 vram_base = 0x100000000; +// Current code assumes it's a power of two. +static u64 vram_size = 1024 * 1024 * 1024; +static int vram_gb = 2; + +#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) + +struct desc_ptr { + u16 limit; + u64 address; +} __attribute__((packed)); + +struct desc_struct { + u16 limit0; + u16 base0; + u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; + u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, + uintptr_t new_cr3, uintptr_t gdt_ptr); +extern uint8_t *jmp_to_linux; +extern size_t jmp_to_linux_size; + +// FreeBSD DMAP addresses +struct linux_boot_info { + void *linux_image; + void *initramfs; + size_t initramfs_size; + struct boot_params *bp; + char *cmd_line; +}; +static struct linux_boot_info nix_info; + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v) +{ + nix_info.linux_image = linux_image; + nix_info.bp = bp; + nix_info.initramfs = initramfs; + nix_info.initramfs_size = initramfs_size; + nix_info.cmd_line = cmd_line; + vram_gb = v; +} + +static volatile int halted_cpus = 0; + +static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, + u32 type) +{ + uint8_t idx = bp->e820_entries; + bp->e820_map[idx].addr = addr; + bp->e820_map[idx].size = size; + bp->e820_map[idx].type = type; + bp->e820_entries++; +} + +void prepare_boot_params(struct boot_params *bp, u8 *linux_image) +{ + memset(bp, 0, sizeof(struct boot_params)); + struct boot_params *bp_src = (struct boot_params *)linux_image; + memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + + ((u8 *)&bp_src->hdr.jump)[1]); + + // These values are from fw 1.01 + bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); + bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); + // This used to be VRAM, but we reclaim it as RAM + bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); + bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); + // Instead, carve out VRAM from the beginning of high memory + bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); + bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, + SMAP_TYPE_MEMORY); +} + +#define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_FB_OFFSET 0x2068 +#define HDP_NONSURFACE_BASE 0x2c04 +#define CONFIG_MEMSIZE 0x5428 + +static void configure_vram(void) +{ + u64 mmio_base = 0xe4800000; + u64 fb_base = 0x0f00000000; + u64 fb_top = fb_base + vram_gb * vram_size - 1; + + WR32(mmio_base + MC_VM_FB_LOCATION, 0); + WR32(mmio_base + HDP_NONSURFACE_BASE, 0); + + WR32(mmio_base + MC_VM_FB_LOCATION, + ((fb_top >> 24) << 16) | (fb_base >> 24)); + WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); + WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); + WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); +} + +#define IA32_MTRR_DEF_TYPE 0x2ff +#define MTRR_BASE(i) (0x200 + 2*i) +#define MTRR_MASK(i) (0x201 + 2*i) + +static void setup_mtrr(void) +{ + disable_interrupts(); + u64 cr0 = cr0_read(); + cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); + wbinvd(); + cr3_write(cr3_read()); // TLB flush + + wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled + + // Low memory (0GB-2GB) = WB + wrmsr(MTRR_BASE(0), 0x0000000006); + wrmsr(MTRR_MASK(0), 0xff80000800); + // High memory (4GB-8GB) = WB + wrmsr(MTRR_BASE(1), 0x0100000006); + wrmsr(MTRR_MASK(1), 0xff00000800); + // High memory (8GB-10GB) = WB + wrmsr(MTRR_BASE(2), 0x0200000006); + wrmsr(MTRR_MASK(2), 0xff80000800); + // VRAM (4GB-4GB+vram_size) = UC + wrmsr(MTRR_BASE(3), 0x0100000000); + wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); + + wbinvd(); + cr3_write(cr3_read()); // TLB flush + wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable + cr0_write(cr0); + enable_interrupts(); +} + +static void cleanup_interrupts(void) +{ + int i; + disable_interrupts(); + + // Reset APIC stuff (per-CPU) + *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; + for (i = 0x320; i < 0x380; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + for (i = 0x480; i < 0x500; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; + for (i = 0x500; i < 0x540; i += 0x10) + *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; + *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; + + // Fix the LVT offset for thresholding + wrmsr(0x413, (1L<<24) | (1L<<52)); + wrmsr(0xc0000408, (1L<<24) | (1L<<52)); +} + +static void cpu_quiesce_gate(void *arg) +{ + int i; + + // Ensure we can write anywhere + cr0_write(cr0_read() & ~CR0_WP); + + // Interrupt stuff local to each CPU + cleanup_interrupts(); + + // We want to set up MTRRs on all CPUs + setup_mtrr(); + + if (curcpu() != 0) { + // We're not on BSP. Try to halt. + __sync_fetch_and_add(&halted_cpus, 1); + cpu_stop(); + } + + uart_write_str("kexec: Waiting for secondary CPUs...\n"); + + // wait for all cpus to halt + while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); + + uart_write_str("kexec: Secondary CPUs quiesced\n"); + + //* Put ident mappings in current page tables + // Should not be needed, but maybe helps for debugging? + cr4_pge_disable(); + u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); + u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + // Clear (really) low mem. + // Linux reads from here to try and access EBDA... + // get_bios_ebda reads u16 from 0x40e + // reserve_ebda_region reads u16 from 0x413 + // Writing zeros causes linux to default to marking + // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. + // It doesn't match the ps4 e820 map, but that seems OK. + memset((void *)0, 0, 0x1000); + + // Create a new page table hierarchy out of the way of linux + // Accessed via freebsd direct map + pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt + // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| + memset(pml4_base, 0, 512 * sizeof(u64) * 2); + pdp_base = pml4_base + 512; + u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; + pml4_base[0] = pdpe; + // Maintain the freebsd direct map + pml4_base[DM_PML4_BASE] = pdpe; + for (u64 i = 0; i < 4; i++) { + pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; + } + + uart_write_str("kexec: Setting up GDT...\n"); + + struct desc_ptr gdt_ptr; + struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); + gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; + gdt_ptr.address = DM_TO_ID(desc); + + // clear + memset(desc, 0, gdt_ptr.limit + 1); + // Most things are ignored in 64bit mode, and we will never be in + // 32bit/compat modes, so just setup another pure-64bit environment... + // Linux inits it's own GDT in secondary_startup_64 + // 0x10 + desc[2].limit0 = 0xffff; + desc[2].base0 = 0x0000; + desc[2].base1 = 0x0000; + desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; + desc[2].s = 1; + desc[2].dpl = 0; + desc[2].p = 1; + desc[2].limit = 0xf; + desc[2].avl = 0; + desc[2].l = 1; + desc[2].d = 0; + desc[2].g = 0; + desc[2].base2 = 0x00; + // 0x18 + desc[3].limit0 = 0xffff; + desc[3].base0 = 0x0000; + desc[3].base1 = 0x0000; + desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; + desc[3].s = 1; + desc[3].dpl = 0; + desc[3].p = 1; + desc[3].limit = 0xf; + desc[3].avl = 0; + desc[3].l = 0; + desc[3].d = 0; + desc[3].g = 0; + desc[3].base2 = 0x00; + // Task segment value + // 0x20 + desc[4].limit0 = 0x0000; + desc[4].base0 = 0x0000; + desc[4].base1 = 0x0000; + desc[4].type = SEG_TYPE_TSS; + desc[4].s = 1; + desc[4].dpl = 0; + desc[4].p = 1; + desc[4].limit = 0x0; + desc[4].avl = 0; + desc[4].l = 0; + desc[4].d = 0; + desc[4].g = 0; + desc[4].base2 = 0x00; + + uart_write_str("kexec: Relocating stub...\n"); + + // Relocate the stub and jump to it + // TODO should thunk_copy be DMAP here? + void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); + memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); + // XXX The +0x200 is for the iret stack in linux_thunk.S + uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; + + uart_write_str("kexec: Setting up boot params...\n"); + + // XXX we write into this bootargs and pass it to the kernel, but in + // jmp_to_linux we use the bootargs from the image as input. So they + // MUST MATCH! + struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; + *bp_lo = *nix_info.bp; + lowmem_pos += sizeof(struct boot_params); + + struct setup_header *shdr = &bp_lo->hdr; + shdr->cmd_line_ptr = lowmem_pos; + shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; + shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; + bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; + bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; + shdr->hardware_subarch = X86_SUBARCH_PS4; + // This needs to be nonzero for the initramfs to work + shdr->type_of_loader = 0xd0; // kexec + + strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, + nix_info.bp->hdr.cmdline_size); + lowmem_pos += strlen(nix_info.cmd_line) + 1; + + uart_write_str("kexec: Cleaning up hardware...\n"); + + // Disable IOMMU + *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; + + // Disable all MSIs on Aeolia + for (i = 0; i < 8; i++) + *(volatile u32 *)PA_TO_DM(0xd03c844c + i*4) = 0; + + // Stop HPET timers + *(volatile u64 *)PA_TO_DM(0xd0382010) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382100) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382120) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382140) = 0; + *(volatile u64 *)PA_TO_DM(0xd0382160) = 0; + + uart_write_str("kexec: Reconfiguring VRAM...\n"); + + configure_vram(); + + uart_write_str("kexec: Resetting GPU...\n"); + + // Softreset GPU + *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks + *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC + *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 +// *(volatile u64 *)PA_TO_DM(0xe480d248) = 1; // Halt SDMA1 eeply + *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 + *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC + + *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply + +// *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x10003; // Softreset GFX/CP/RLC + *(volatile u64 *)PA_TO_DM(0xe4808020) |= 0x30005; // Softreset GFX/CP/RLC eeply + +// udelay(150); +// *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x10003; + *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~0x30005; //eeply +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) |= 0x00100140; // Softreset SDMA/GRBM +// udelay(150); + *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~0x00100140; +// udelay(150); + + // Enable audio output + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; + *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; + *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; + +// // Set pin caps of pin 2 to vendor defined, to hide it +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; +// *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; +// // Set pin caps of pin 3 to !HDMI +// *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; +// *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; + // Set pin configuration default + *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; + *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; + *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; + *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; + + uart_write_str("kexec: About to relocate and jump to kernel\n"); + + ((jmp_to_linux_t)thunk_copy)( + DM_TO_ID(nix_info.linux_image), + DM_TO_ID(bp_lo), + DM_TO_ID(pml4_base), + (uintptr_t)&gdt_ptr + ); + + // should never reach here + uart_write_str("kexec: unreachable (?)\n"); +} + +// Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) +int hook_icc_query_nowait(u8 *icc_msg) +{ + kern.printf("hook_icc_query_nowait called\n"); + + // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot + //In alternative we can re-enable it here, but sometimes that give problems.. + kern.wlanbt(0x2); + + fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); + + kern.printf("ACPI tables fixed\n"); + + // Transition to BSP and halt other cpus + // smp_no_rendevous_barrier is just nullsub, but it is treated specially by + // smp_rendezvous. This is the easiest way to do this, since we can't assume + // we're already running on BSP. Since smp_rendezvous normally waits on all + // cpus to finish the callbacks, we just never return... + kern.smp_rendezvous(kern.smp_no_rendevous_barrier, + cpu_quiesce_gate, + kern.smp_no_rendevous_barrier, NULL); + + // should never reach here + kern.printf("hook_icc_query_nowait: unreachable (?)\n"); + return 0; +} diff --git a/linux/ps4-kexec-1302/linux_boot.h b/linux/ps4-kexec-1302/linux_boot.h new file mode 100644 index 0000000..2232b35 --- /dev/null +++ b/linux/ps4-kexec-1302/linux_boot.h @@ -0,0 +1,88 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef LINUX_BOOT_H +#define LINUX_BOOT_H + +#include "types.h" + +#define SMAP_TYPE_MEMORY 1 +#define SMAP_TYPE_RESERVED 2 +#define SMAP_TYPE_ACPI_RECLAIM 3 +#define SMAP_TYPE_ACPI_NVS 4 +#define SMAP_TYPE_UNUSABLE 5 +#define SMAP_TYPE_PMEM 7 + +#define X86_SUBARCH_PS4 5 + +struct e820entry { + u64 addr; /* start of memory segment */ + u64 size; /* size of memory segment */ + u32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct setup_header { + u8 setup_sects; + u16 root_flags; + u32 syssize; + u16 ram_size; + u16 vid_mode; + u16 root_dev; + u16 boot_flag; + u16 jump; + u32 header; + u16 version; + u32 realmode_swtch; + u16 start_sys; + u16 kernel_version; + u8 type_of_loader; + u8 loadflags; + u16 setup_move_size; + u32 code32_start; + u32 ramdisk_image; + u32 ramdisk_size; + u32 bootsect_kludge; + u16 heap_end_ptr; + u8 ext_loader_ver; + u8 ext_loader_type; + u32 cmd_line_ptr; + u32 initrd_addr_max; + u32 kernel_alignment; + u8 relocatable_kernel; + u8 min_alignment; + u16 xloadflags; + u32 cmdline_size; + u32 hardware_subarch; + u64 hardware_subarch_data; + u32 payload_offset; + u32 payload_length; + u64 setup_data; + u64 pref_address; + u32 init_size; + u32 handover_offset; +} __attribute__((packed)); + +#define E820MAX 128 /* number of entries in E820MAP */ + +OSTRUCT(boot_params, 0x1000) +OFIELD(0x0c0, u32 ext_ramdisk_image); +OFIELD(0x0c4, u32 ext_ramdisk_size); +OFIELD(0x0c8, u32 ext_cmd_line_ptr); +OFIELD(0x1e8, u8 e820_entries); +OFIELD(0x1f1, struct setup_header hdr); +OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); +OSTRUCT_END + +void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, + size_t initramfs_size, char *cmd_line, int v); +void prepare_boot_params(struct boot_params *bp, u8 *linux_image); +int hook_icc_query_nowait(u8 *icc_msg); + +#endif diff --git a/linux/ps4-kexec-1302/linux_thunk.S b/linux/ps4-kexec-1302/linux_thunk.S new file mode 100644 index 0000000..f6d2dfb --- /dev/null +++ b/linux/ps4-kexec-1302/linux_thunk.S @@ -0,0 +1,90 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +.intel_syntax noprefix + +.equ setup_sects, 0x1f1 +.equ shdr_syssize, 0x1f4 +.equ pref_address, 0x258 + +.text + +#void jmp_to_linux( +# uintptr_t image_base, rdi +# uintptr_t bootargs, rsi +# uintptr_t new_cr3, rdx +# uintptr_t gdt_ptr rcx +#); +.globl jmp_to_linux +jmp_to_linux: + # switch to new gdt + data segments + cli + lgdt [rcx] + #xor eax, eax + mov eax, 0x18 + mov ds, eax + mov ss, eax + mov es, eax + mov fs, eax + mov gs, eax + + # switch to our own page tables (in low mem) + mov cr3, rdx + + # now we're on our own page tables, so we can obliterate the rest of memory + # TODO make sure we don't inadvertently overwrite (important) smap regions + # I think on ps4 we'll actually want to load to 0x700000 + # since we have tons of free room there. + # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We + # should choose a higher address as pref_address. + + # save args + mov r12, rdi + mov r13, rsi + + # memmove(pref_address, , (syssize * 0x10) / 8) + #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go + #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) + mov rdi, 0x6000000 # should be far from bzImage and initramfs + mov r14, rdi # r14 = pref_address + xor edx, edx + mov dl, [r12 + setup_sects] + inc rdx + shl rdx, 9 # rdx = offsetof(image_base, startup_32) + lea rsi, [r12 + rdx] # src = image_base + startup_32 + mov ecx, [r12 + shdr_syssize] + shl rcx, 4 + add rdi, rcx + add rsi, rcx + sub rdi, 8 + sub rsi, 8 + shr rcx, 3 + std + rep movsq + cld + + # make a tiny stack - we just need it for the lretq. + # what we jump to will not use this stack + lea rsp, [rip + jmp_to_linux_end + 0x200] + and rsp, -0x10 + #push 0 # retaddr + push 0x10 # cs = GDT[2] + add r14, 0x200 # pref_address + startup_64 + push r14 # rip + mov rsi, r13 # bootargs + lretq +jmp_to_linux_end: + +.data + +.globl jmp_to_linux_size +jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux + +.att_syntax prefix diff --git a/linux/ps4-kexec-1302/reboot.h b/linux/ps4-kexec-1302/reboot.h new file mode 100644 index 0000000..20b91f8 --- /dev/null +++ b/linux/ps4-kexec-1302/reboot.h @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)reboot.h 8.3 (Berkeley) 12/13/94 + * $FreeBSD$ + */ + +#ifndef _SYS_REBOOT_H_ +#define _SYS_REBOOT_H_ + +/* + * Arguments to reboot system call. These are passed to + * the boot program and on to init. + */ +#define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ + +#define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ +#define RB_SINGLE 0x002 /* reboot to single user only */ +#define RB_NOSYNC 0x004 /* dont sync before reboot */ +#define RB_HALT 0x008 /* don't reboot, just halt */ +#define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ +#define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ +#define RB_KDB 0x040 /* give control to kernel debugger */ +#define RB_RDONLY 0x080 /* mount root fs read-only */ +#define RB_DUMP 0x100 /* dump kernel memory before reboot */ +#define RB_MINIROOT 0x200 /* Unused placeholder */ +#define RB_VERBOSE 0x800 /* print all potentially useful info */ +#define RB_SERIAL 0x1000 /* use serial port as console */ +#define RB_CDROM 0x2000 /* use cdrom as root */ +#define RB_POWEROFF 0x4000 /* turn the power off if possible */ +#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ +#define RB_MUTE 0x10000 /* start up with the console muted */ +#define RB_SELFTEST 0x20000 /* unused placeholder */ +#define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ +#define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ +#define RB_PAUSE 0x100000 /* pause after each output line during probe */ +#define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ +#define RB_PROBE 0x10000000 /* Probe multiple consoles */ +#define RB_MULTIPLE 0x20000000 /* use multiple consoles */ + +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + +#endif diff --git a/linux/ps4-kexec-1302/string.h b/linux/ps4-kexec-1302/string.h new file mode 100644 index 0000000..70aa668 --- /dev/null +++ b/linux/ps4-kexec-1302/string.h @@ -0,0 +1,96 @@ +/* + * string.h -- standard C string-manipulation functions. + * + * Copyright (C) 2008 Segher Boessenkool + * Copyright (C) 2009 Haxx Enterprises + * Copyright (C) 2010-2016 Hector Martin "marcan" + * + * Portions taken from the Public Domain C Library (PDCLib). + * http://pdclib.rootdirectory.de/ + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef STRING_H +#define STRING_H + +#include "types.h" + +static inline int strcmp(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s1[i] == s2[i]; i++) + ; + + return s1[i] - s2[i]; +} + +static inline void *memset(void *b, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)b)[i] = c; + + return b; +} + +static inline void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} + +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) + return p1[i] - p2[i]; + + return 0; +} + +static inline size_t strlen(const char *s) +{ + size_t len; + + for (len = 0; s[len]; len++) + ; + + return len; +} + +static inline size_t strnlen(const char *s, size_t count) +{ + size_t len; + + for (len = 0; s[len] && len < count; len++) + ; + + return len; +} + +static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) +{ + size_t len, needed; + + len = needed = strnlen(src, maxlen - 1) + 1; + if (len >= maxlen) + len = maxlen - 1; + + memcpy(dest, src, len); + dest[len] = 0; + + return needed - 1; +} + +#endif diff --git a/linux/ps4-kexec-1302/types.h b/linux/ps4-kexec-1302/types.h new file mode 100644 index 0000000..28d6619 --- /dev/null +++ b/linux/ps4-kexec-1302/types.h @@ -0,0 +1,51 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef TYPES_H +#define TYPES_H + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +#ifndef TESTING +typedef u8 uint8_t; +typedef u64 size_t; +typedef s64 ssize_t; +typedef u64 uintptr_t; +typedef s64 off_t; +#endif + +#define NULL ((void *)0) + +#define CAT_(x, y) x ## y +#define CAT(x, y) CAT_(x, y) + +#define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] +#define OSTRUCT(name, size) struct name { union { OPAD(size); +#define OSTRUCT_END };}; +#define OFIELD(off, field) struct { OPAD(off); field; } + +#define ASSERT_STRSIZE(struc, size) \ + _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#define ENOENT 2 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define ENAMETOOLONG 63 + +#endif diff --git a/linux/ps4-kexec-1302/uart.c b/linux/ps4-kexec-1302/uart.c new file mode 100644 index 0000000..62ea949 --- /dev/null +++ b/linux/ps4-kexec-1302/uart.c @@ -0,0 +1,64 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#include "uart.h" +#include "kernel.h" +#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) + +#define AEOLIA_UART_BASE 0xD0340000 +#define BAIKAL_UART_BASE 0xC890E000 + +#define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, AEOLIA_UART_BASE + (intf << 12) + (reg << 2)) + +#define UART_REG_DATA 0 +#define UART_REG_IER 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +# define LSR_TXRDY 0x20 +# define LSR_TEMT 0x40 +#define UART_REG_MSR 6 +#define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) +#define UART0_IER UART_REG(32, 0, UART_REG_IER) +#define UART0_IIR UART_REG(32, 0, UART_REG_IIR) +#define UART0_LCR UART_REG(32, 0, UART_REG_LCR) +#define UART0_MCR UART_REG(32, 0, UART_REG_MCR) +#define UART0_LSR UART_REG(32, 0, UART_REG_LSR) +#define UART0_MSR UART_REG(32, 0, UART_REG_MSR) + +void uart_write_byte(u8 b) +{ + int limit; + u64 barrier; + limit = 250000; + while (!(*UART0_LSR & LSR_TXRDY) && --limit) + ; + *UART0_DATA = b; + __sync_fetch_and_add(&barrier, 0); + limit = 250000; + while (!(*UART0_LSR & LSR_TEMT) && --limit) + ; +} + +void uart_write_char(char c) +{ + if (c == '\n') + uart_write_byte('\r'); + + uart_write_byte(c); +} + +void uart_write_str(const char *s) +{ + while (*s) { + uart_write_char(*s++); + } +} diff --git a/linux/ps4-kexec-1302/uart.h b/linux/ps4-kexec-1302/uart.h new file mode 100644 index 0000000..b803e29 --- /dev/null +++ b/linux/ps4-kexec-1302/uart.h @@ -0,0 +1,20 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef UART_H +#define UART_H + +#include "types.h" + +void uart_write_byte(u8 b); +void uart_write_char(char c); +void uart_write_str(const char *s); + +#endif diff --git a/linux/ps4-kexec-1302/x86.h b/linux/ps4-kexec-1302/x86.h new file mode 100644 index 0000000..61b22ff --- /dev/null +++ b/linux/ps4-kexec-1302/x86.h @@ -0,0 +1,149 @@ +/* + * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD + * + * Copyright (C) 2015-2016 shuffle2 + * Copyright (C) 2015-2016 Hector Martin "marcan" + * + * This code is licensed to you under the 2-clause BSD license. See the LICENSE + * file for more information. + */ + +#ifndef X86_H +#define X86_H + +#define FLAGS_IF (1 << 9) + +#define CR0_WP (1 << 16) +#define CR0_NW (1 << 29) +#define CR0_CD (1 << 30) + +#define PG_V (1 << 0) +#define PG_RW (1 << 1) +#define PG_U (1 << 2) +#define PG_PS (1 << 7) + +#define SEG_TYPE_DATA (0 << 3) +#define SEG_TYPE_READ_WRITE (1 << 1) +#define SEG_TYPE_CODE (1 << 3) +#define SEG_TYPE_EXEC_READ (1 << 1) +#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) + +static inline u64 cr0_read(void) +{ + u64 reg; + asm volatile("mov %0, cr0;" : "=r" (reg)); + return reg; +} + +static inline void cr0_write(u64 val) +{ + asm volatile("mov cr0, %0;" :: "r" (val)); +} + +static inline u64 write_protect_disable() +{ + u64 cr0 = cr0_read(); + cr0_write(cr0 & ~CR0_WP); + return cr0; +} + +static inline void write_protect_restore(u64 cr0) +{ + // Use only WP bit of input + cr0_write(cr0_read() | (cr0 & CR0_WP)); +} + +static inline u64 cr3_read(void) +{ + u64 reg; + asm volatile("mov %0, cr3;" : "=r" (reg)); + return reg; +} + +static inline void cr3_write(u64 val) +{ + asm volatile("mov cr3, %0;" :: "r" (val)); +} + +static inline void cr4_pge_disable(void) +{ + u64 cr4_temp; + asm volatile( + "mov %0, cr4;" + "and %0, ~0x80;" + "mov cr4, %0;" + : "=r" (cr4_temp) + ); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +static inline void cpu_stop(void) +{ + for (;;) + asm volatile("cli; hlt;" : : : "memory"); +} + +static inline void outl(int port, unsigned int data) +{ + asm volatile("out %w1, %0" : : "a" (data), "d" (port)); +} + +static inline void wrmsr(u32 msr_id, u64 msr_value) +{ + asm volatile( + "wrmsr" + : + : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) + ); +} + +static inline u64 rdtsc (void) +{ + unsigned int tickl, tickh; + asm volatile( + "rdtsc" + :"=a"(tickl),"=d"(tickh) + ); + return ((u64)tickh << 32) | tickl; +} + +static inline void udelay(unsigned int usec) { + u64 later = rdtsc() + usec * 1594ULL; + while (((s64)(later - rdtsc())) > 0); +} + +static inline void disable_interrupts(void) +{ + asm volatile("cli"); +} + +static inline void enable_interrupts(void) +{ + asm volatile("sti"); +} + +static inline u64 read_flags(void) +{ + u64 flags; + asm volatile("pushf; pop %0;" : "=r" (flags)); + return flags; +} + +static inline u64 intr_disable(void) +{ + u64 flags = read_flags(); + disable_interrupts(); + return flags; +} + +static inline void intr_restore(u64 flags) +{ + // TODO should only IF be or'd in? + asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); +} + +#endif From 454680a478b9598456ad5e2342f98d11f70bfbee Mon Sep 17 00:00:00 2001 From: ArabPixel Date: Mon, 5 Jan 2026 01:17:17 +0100 Subject: [PATCH 4/4] Remakeing Makefile --- linux/Makefile | 1012 +++--------------------------------------------- 1 file changed, 52 insertions(+), 960 deletions(-) diff --git a/linux/Makefile b/linux/Makefile index 3b46808..95d830d 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -1,979 +1,71 @@ FIRMWARES = 505 672 700 900 903 960 1000 1050 1100 1102 1150 1200 1250 1300 1302 SIZES = 1gb 2gb 3gb 4gb -# This uses a nested loop style logic to generate the file names automatically + +# Generate payload targets automatically PAYLOADS = $(foreach fw,$(FIRMWARES), \ $(foreach sz,$(SIZES), \ fw$(fw)/payload-$(fw)-$(sz).bin \ fw$(fw)/payload-$(fw)-$(sz)-pro.bin \ fw$(fw)/payload-$(fw)-$(sz)-baikal.bin \ - fw$(fw)/payload-$(fw)-$(sz)-pro-baikal.bin \ - ) \ - ) - -all: $(PAYLOADS) - -clean: - rm -f fw*/* - - cd ps4-kexec-505; make clean - cd ps4-kexec-505-pro; make clean - cd ps4-kexec-505-baikal; make clean - cd ps4-kexec-505-pro-baikal; make clean - cd ps4-kexec-672; make clean - cd ps4-kexec-672-pro; make clean - cd ps4-kexec-672-baikal; make clean - cd ps4-kexec-672-pro-baikal; make clean - cd ps4-kexec-700; make clean - cd ps4-kexec-700-pro; make clean - cd ps4-kexec-700-baikal; make clean - cd ps4-kexec-700-pro-baikal; make clean - cd ps4-kexec-900; make clean - cd ps4-kexec-900-pro; make clean - cd ps4-kexec-900-baikal; make clean - cd ps4-kexec-900-pro-baikal; make clean - cd ps4-kexec-903; make clean - cd ps4-kexec-903-pro; make clean - cd ps4-kexec-903-baikal; make clean - cd ps4-kexec-903-pro-baikal; make clean - cd ps4-kexec-960; make clean - cd ps4-kexec-960-pro; make clean - cd ps4-kexec-960-baikal; make clean - cd ps4-kexec-960-pro-baikal; make clean - cd ps4-kexec-1000; make clean - cd ps4-kexec-1000-pro; make clean - cd ps4-kexec-1000-baikal; make clean - cd ps4-kexec-1000-pro-baikal; make clean - cd ps4-kexec-1050; make clean - cd ps4-kexec-1050-pro; make clean - cd ps4-kexec-1050-baikal; make clean - cd ps4-kexec-1050-pro-baikal; make clean - cd ps4-kexec-1100; make clean - cd ps4-kexec-1100-pro; make clean - cd ps4-kexec-1100-baikal; make clean - cd ps4-kexec-1100-pro-baikal; make clean - cd ps4-kexec-1102; make clean - cd ps4-kexec-1102-pro; make clean - cd ps4-kexec-1102-baikal; make clean - cd ps4-kexec-1102-pro-baikal; make clean - cd ps4-kexec-1150; make clean - cd ps4-kexec-1150-pro; make clean - cd ps4-kexec-1150-baikal; make clean - cd ps4-kexec-1150-pro-baikal; make clean - cd ps4-kexec-1200; make clean - cd ps4-kexec-1200-pro; make clean - cd ps4-kexec-1200-baikal; make clean - cd ps4-kexec-1200-pro-baikal; make clean - cd ps4-kexec-1250; make clean - cd ps4-kexec-1250-pro; make clean - cd ps4-kexec-1250-baikal; make clean - cd ps4-kexec-1250-pro-baikal; make clean - cd ps4-kexec-1300; make clean - cd ps4-kexec-1300-pro; make clean - cd ps4-kexec-1300-baikal; make clean - cd ps4-kexec-1300-pro-baikal; make clean - cd ps4-kexec-1302; make clean - cd ps4-kexec-1302-pro; make clean - cd ps4-kexec-1302-baikal; make clean - cd ps4-kexec-1302-pro-baikal; make clean - cd ../lib/; make clean - -../lib/lib.a: - cd ../lib; make - -ps4-kexec-505/kexec.bin: - cd ps4-kexec-505; make - -fw505/payload-505-1gb.elf: ../lib/lib.a main.c ps4-kexec-505/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 main.c -Wl,-gc-sections -o fw505/payload-505-1gb.elf -fPIE -ffreestanding - -fw505/payload-505-2gb.elf: ../lib/lib.a main.c ps4-kexec-505/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw505/payload-505-2gb.elf -fPIE -ffreestanding - -fw505/payload-505-3gb.elf: ../lib/lib.a main.c ps4-kexec-505/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw505/payload-505-3gb.elf -fPIE -ffreestanding - -fw505/payload-505-4gb.elf: ../lib/lib.a main.c ps4-kexec-505/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw505/payload-505-4gb.elf -fPIE -ffreestanding - -ps4-kexec-505-pro/kexec.bin: - cd ps4-kexec-505-pro; make - -fw505/payload-505-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-505-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 main.c -Wl,-gc-sections -o fw505/payload-505-1gb-pro.elf -fPIE -ffreestanding - -fw505/payload-505-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-505-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw505/payload-505-2gb-pro.elf -fPIE -ffreestanding - -fw505/payload-505-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-505-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw505/payload-505-3gb-pro.elf -fPIE -ffreestanding - -fw505/payload-505-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-505-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw505/payload-505-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-505-baikal/kexec.bin: - cd ps4-kexec-505-baikal; make - -fw505/payload-505-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-505-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 main-baikal.c -Wl,-gc-sections -o fw505/payload-505-1gb-baikal.elf -fPIE -ffreestanding - -fw505/payload-505-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-505-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw505/payload-505-2gb-baikal.elf -fPIE -ffreestanding - -fw505/payload-505-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-505-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw505/payload-505-3gb-baikal.elf -fPIE -ffreestanding - -fw505/payload-505-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-505-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw505/payload-505-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-505-pro-baikal/kexec.bin: - cd ps4-kexec-505-pro-baikal; make - -fw505/payload-505-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-505-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 main-baikal.c -Wl,-gc-sections -o fw505/payload-505-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw505/payload-505-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-505-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw505/payload-505-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw505/payload-505-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-505-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw505/payload-505-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw505/payload-505-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-505-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__5_05__ -DPS4_5_05 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw505/payload-505-4gb-pro-baikal.elf -fPIE -ffreestanding - -ps4-kexec-672/kexec.bin: - cd ps4-kexec-672; make - -fw672/payload-672-1gb.elf: ../lib/lib.a main.c ps4-kexec-672/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 main.c -Wl,-gc-sections -o fw672/payload-672-1gb.elf -fPIE -ffreestanding - -fw672/payload-672-2gb.elf: ../lib/lib.a main.c ps4-kexec-672/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw672/payload-672-2gb.elf -fPIE -ffreestanding - -fw672/payload-672-3gb.elf: ../lib/lib.a main.c ps4-kexec-672/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw672/payload-672-3gb.elf -fPIE -ffreestanding - -fw672/payload-672-4gb.elf: ../lib/lib.a main.c ps4-kexec-672/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw672/payload-672-4gb.elf -fPIE -ffreestanding - -ps4-kexec-672-pro/kexec.bin: - cd ps4-kexec-672-pro; make - -fw672/payload-672-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-672-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 main.c -Wl,-gc-sections -o fw672/payload-672-1gb-pro.elf -fPIE -ffreestanding - -fw672/payload-672-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-672-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw672/payload-672-2gb-pro.elf -fPIE -ffreestanding - -fw672/payload-672-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-672-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw672/payload-672-3gb-pro.elf -fPIE -ffreestanding - -fw672/payload-672-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-672-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw672/payload-672-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-672-baikal/kexec.bin: - cd ps4-kexec-672-baikal; make - -fw672/payload-672-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-672-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 main-baikal.c -Wl,-gc-sections -o fw672/payload-672-1gb-baikal.elf -fPIE -ffreestanding - -fw672/payload-672-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-672-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw672/payload-672-2gb-baikal.elf -fPIE -ffreestanding - -fw672/payload-672-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-672-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw672/payload-672-3gb-baikal.elf -fPIE -ffreestanding - -fw672/payload-672-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-672-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw672/payload-672-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-672-pro-baikal/kexec.bin: - cd ps4-kexec-672-pro-baikal; make - -fw672/payload-672-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-672-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 main-baikal.c -Wl,-gc-sections -o fw672/payload-672-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw672/payload-672-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-672-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw672/payload-672-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw672/payload-672-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-672-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw672/payload-672-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw672/payload-672-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-672-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__6_72__ -DPS4_6_72 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw672/payload-672-4gb-pro-baikal.elf -fPIE -ffreestanding - -ps4-kexec-700/kexec.bin: - cd ps4-kexec-700; make - -fw700/payload-700-1gb.elf: ../lib/lib.a main.c ps4-kexec-700/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 main.c -Wl,-gc-sections -o fw700/payload-700-1gb.elf -fPIE -ffreestanding - -fw700/payload-700-2gb.elf: ../lib/lib.a main.c ps4-kexec-700/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw700/payload-700-2gb.elf -fPIE -ffreestanding - -fw700/payload-700-3gb.elf: ../lib/lib.a main.c ps4-kexec-700/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw700/payload-700-3gb.elf -fPIE -ffreestanding - -fw700/payload-700-4gb.elf: ../lib/lib.a main.c ps4-kexec-700/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw700/payload-700-4gb.elf -fPIE -ffreestanding - -ps4-kexec-700-pro/kexec.bin: - cd ps4-kexec-700-pro; make - -fw700/payload-700-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-700-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 main.c -Wl,-gc-sections -o fw700/payload-700-1gb-pro.elf -fPIE -ffreestanding - -fw700/payload-700-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-700-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw700/payload-700-2gb-pro.elf -fPIE -ffreestanding - -fw700/payload-700-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-700-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw700/payload-700-3gb-pro.elf -fPIE -ffreestanding - -fw700/payload-700-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-700-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw700/payload-700-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-700-baikal/kexec.bin: - cd ps4-kexec-700-baikal; make - -fw700/payload-700-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-700-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 main-baikal.c -Wl,-gc-sections -o fw700/payload-700-1gb-baikal.elf -fPIE -ffreestanding - -fw700/payload-700-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-700-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw700/payload-700-2gb-baikal.elf -fPIE -ffreestanding - -fw700/payload-700-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-700-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw700/payload-700-3gb-baikal.elf -fPIE -ffreestanding - -fw700/payload-700-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-700-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw700/payload-700-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-700-pro-baikal/kexec.bin: - cd ps4-kexec-700-pro-baikal; make - -fw700/payload-700-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-700-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 main-baikal.c -Wl,-gc-sections -o fw700/payload-700-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw700/payload-700-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-700-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw700/payload-700-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw700/payload-700-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-700-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw700/payload-700-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw700/payload-700-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-700-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__7_00__ -DPS4_7_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw700/payload-700-4gb-pro-baikal.elf -fPIE -ffreestanding - -ps4-kexec-900/kexec.bin: - cd ps4-kexec-900; make - -fw900/payload-900-1gb.elf: ../lib/lib.a main.c ps4-kexec-900/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 main.c -Wl,-gc-sections -o fw900/payload-900-1gb.elf -fPIE -ffreestanding - -fw900/payload-900-2gb.elf: ../lib/lib.a main.c ps4-kexec-900/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw900/payload-900-2gb.elf -fPIE -ffreestanding - -fw900/payload-900-3gb.elf: ../lib/lib.a main.c ps4-kexec-900/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw900/payload-900-3gb.elf -fPIE -ffreestanding - -fw900/payload-900-4gb.elf: ../lib/lib.a main.c ps4-kexec-900/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw900/payload-900-4gb.elf -fPIE -ffreestanding - -ps4-kexec-900-pro/kexec.bin: - cd ps4-kexec-900-pro; make - -fw900/payload-900-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-900-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 main.c -Wl,-gc-sections -o fw900/payload-900-1gb-pro.elf -fPIE -ffreestanding - -fw900/payload-900-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-900-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw900/payload-900-2gb-pro.elf -fPIE -ffreestanding - -fw900/payload-900-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-900-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw900/payload-900-3gb-pro.elf -fPIE -ffreestanding - -fw900/payload-900-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-900-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw900/payload-900-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-900-baikal/kexec.bin: - cd ps4-kexec-900-baikal; make - -fw900/payload-900-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-900-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 main-baikal.c -Wl,-gc-sections -o fw900/payload-900-1gb-baikal.elf -fPIE -ffreestanding - -fw900/payload-900-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-900-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw900/payload-900-2gb-baikal.elf -fPIE -ffreestanding - -fw900/payload-900-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-900-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw900/payload-900-3gb-baikal.elf -fPIE -ffreestanding - -fw900/payload-900-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-900-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw900/payload-900-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-900-pro-baikal/kexec.bin: - cd ps4-kexec-900-pro-baikal; make - -fw900/payload-900-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-900-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 main-baikal.c -Wl,-gc-sections -o fw900/payload-900-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw900/payload-900-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-900-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw900/payload-900-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw900/payload-900-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-900-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw900/payload-900-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw900/payload-900-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-900-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_00__ -DPS4_9_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw900/payload-900-4gb-pro-baikal.elf -fPIE -ffreestanding - -ps4-kexec-903/kexec.bin: - cd ps4-kexec-903; make - -fw903/payload-903-1gb.elf: ../lib/lib.a main.c ps4-kexec-903/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 main.c -Wl,-gc-sections -o fw903/payload-903-1gb.elf -fPIE -ffreestanding - -fw903/payload-903-2gb.elf: ../lib/lib.a main.c ps4-kexec-903/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw903/payload-903-2gb.elf -fPIE -ffreestanding - -fw903/payload-903-3gb.elf: ../lib/lib.a main.c ps4-kexec-903/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw903/payload-903-3gb.elf -fPIE -ffreestanding - -fw903/payload-903-4gb.elf: ../lib/lib.a main.c ps4-kexec-903/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw903/payload-903-4gb.elf -fPIE -ffreestanding - -ps4-kexec-903-pro/kexec.bin: - cd ps4-kexec-903-pro; make - -fw903/payload-903-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-903-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 main.c -Wl,-gc-sections -o fw903/payload-903-1gb-pro.elf -fPIE -ffreestanding - -fw903/payload-903-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-903-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw903/payload-903-2gb-pro.elf -fPIE -ffreestanding - -fw903/payload-903-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-903-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw903/payload-903-3gb-pro.elf -fPIE -ffreestanding - -fw903/payload-903-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-903-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw903/payload-903-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-903-baikal/kexec.bin: - cd ps4-kexec-903-baikal; make - -fw903/payload-903-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-903-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 main-baikal.c -Wl,-gc-sections -o fw903/payload-903-1gb-baikal.elf -fPIE -ffreestanding - -fw903/payload-903-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-903-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw903/payload-903-2gb-baikal.elf -fPIE -ffreestanding - -fw903/payload-903-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-903-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw903/payload-903-3gb-baikal.elf -fPIE -ffreestanding - -fw903/payload-903-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-903-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw903/payload-903-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-903-pro-baikal/kexec.bin: - cd ps4-kexec-903-pro-baikal; make - -fw903/payload-903-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-903-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 main-baikal.c -Wl,-gc-sections -o fw903/payload-903-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw903/payload-903-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-903-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw903/payload-903-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw903/payload-903-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-903-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw903/payload-903-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw903/payload-903-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-903-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_03__ -DPS4_9_03 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw903/payload-903-4gb-pro-baikal.elf -fPIE -ffreestanding - -ps4-kexec-960/kexec.bin: - cd ps4-kexec-960; make - -fw960/payload-960-1gb.elf: ../lib/lib.a main.c ps4-kexec-960/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 main.c -Wl,-gc-sections -o fw960/payload-960-1gb.elf -fPIE -ffreestanding - -fw960/payload-960-2gb.elf: ../lib/lib.a main.c ps4-kexec-960/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw960/payload-960-2gb.elf -fPIE -ffreestanding - -fw960/payload-960-3gb.elf: ../lib/lib.a main.c ps4-kexec-960/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw960/payload-960-3gb.elf -fPIE -ffreestanding - -fw960/payload-960-4gb.elf: ../lib/lib.a main.c ps4-kexec-960/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw960/payload-960-4gb.elf -fPIE -ffreestanding - -ps4-kexec-960-pro/kexec.bin: - cd ps4-kexec-960-pro; make - -fw960/payload-960-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-960-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 main.c -Wl,-gc-sections -o fw960/payload-960-1gb-pro.elf -fPIE -ffreestanding - -fw960/payload-960-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-960-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw960/payload-960-2gb-pro.elf -fPIE -ffreestanding - -fw960/payload-960-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-960-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw960/payload-960-3gb-pro.elf -fPIE -ffreestanding - -fw960/payload-960-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-960-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw960/payload-960-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-960-baikal/kexec.bin: - cd ps4-kexec-960-baikal; make - -fw960/payload-960-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-960-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 main-baikal.c -Wl,-gc-sections -o fw960/payload-960-1gb-baikal.elf -fPIE -ffreestanding - -fw960/payload-960-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-960-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw960/payload-960-2gb-baikal.elf -fPIE -ffreestanding - -fw960/payload-960-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-960-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw960/payload-960-3gb-baikal.elf -fPIE -ffreestanding - -fw960/payload-960-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-960-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw960/payload-960-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-960-pro-baikal/kexec.bin: - cd ps4-kexec-960-pro-baikal; make - -fw960/payload-960-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-960-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 main-baikal.c -Wl,-gc-sections -o fw960/payload-960-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw960/payload-960-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-960-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw960/payload-960-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw960/payload-960-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-960-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw960/payload-960-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw960/payload-960-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-960-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__9_60__ -DPS4_9_60 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw960/payload-960-4gb-pro-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1000/kexec.bin: - cd ps4-kexec-1000; make - -fw1000/payload-1000-1gb.elf: ../lib/lib.a main.c ps4-kexec-1000/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 main.c -Wl,-gc-sections -o fw1000/payload-1000-1gb.elf -fPIE -ffreestanding - -fw1000/payload-1000-2gb.elf: ../lib/lib.a main.c ps4-kexec-1000/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1000/payload-1000-2gb.elf -fPIE -ffreestanding - -fw1000/payload-1000-3gb.elf: ../lib/lib.a main.c ps4-kexec-1000/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1000/payload-1000-3gb.elf -fPIE -ffreestanding - -fw1000/payload-1000-4gb.elf: ../lib/lib.a main.c ps4-kexec-1000/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1000/payload-1000-4gb.elf -fPIE -ffreestanding - -ps4-kexec-1000-pro/kexec.bin: - cd ps4-kexec-1000-pro; make - -fw1000/payload-1000-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1000-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 main.c -Wl,-gc-sections -o fw1000/payload-1000-1gb-pro.elf -fPIE -ffreestanding - -fw1000/payload-1000-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1000-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1000/payload-1000-2gb-pro.elf -fPIE -ffreestanding - -fw1000/payload-1000-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1000-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1000/payload-1000-3gb-pro.elf -fPIE -ffreestanding - -fw1000/payload-1000-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1000-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1000/payload-1000-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-1000-baikal/kexec.bin: - cd ps4-kexec-1000-baikal; make - -fw1000/payload-1000-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1000-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 main-baikal.c -Wl,-gc-sections -o fw1000/payload-1000-1gb-baikal.elf -fPIE -ffreestanding - -fw1000/payload-1000-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1000-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1000/payload-1000-2gb-baikal.elf -fPIE -ffreestanding - -fw1000/payload-1000-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1000-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1000/payload-1000-3gb-baikal.elf -fPIE -ffreestanding - -fw1000/payload-1000-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1000-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1000/payload-1000-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1000-pro-baikal/kexec.bin: - cd ps4-kexec-1000-pro-baikal; make - -fw1000/payload-1000-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1000-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 main-baikal.c -Wl,-gc-sections -o fw1000/payload-1000-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw1000/payload-1000-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1000-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1000/payload-1000-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw1000/payload-1000-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1000-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1000/payload-1000-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw1000/payload-1000-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1000-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_00__ -DPS4_10_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1000/payload-1000-4gb-pro-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1050/kexec.bin: - cd ps4-kexec-1050; make - -fw1050/payload-1050-1gb.elf: ../lib/lib.a main.c ps4-kexec-1050/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 main.c -Wl,-gc-sections -o fw1050/payload-1050-1gb.elf -fPIE -ffreestanding - -fw1050/payload-1050-2gb.elf: ../lib/lib.a main.c ps4-kexec-1050/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1050/payload-1050-2gb.elf -fPIE -ffreestanding + fw$(fw)/payload-$(fw)-$(sz)-pro-baikal.bin)) -fw1050/payload-1050-3gb.elf: ../lib/lib.a main.c ps4-kexec-1050/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1050/payload-1050-3gb.elf -fPIE -ffreestanding +# Common compiler flags +CC = gcc +CFLAGS = -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static -fPIE -ffreestanding +LDFLAGS = -Wl,-gc-sections -fw1050/payload-1050-4gb.elf: ../lib/lib.a main.c ps4-kexec-1050/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1050/payload-1050-4gb.elf -fPIE -ffreestanding +# VRAM size flags - using conditional to avoid empty variable issues +define VRAM_FLAGS +$(if $(filter 2gb,$(1)),-DVRAM_GB_DEFAULT=2,$(if $(filter 3gb,$(1)),-DVRAM_GB_DEFAULT=3,$(if $(filter 4gb,$(1)),-DVRAM_GB_DEFAULT=4))) +endef -ps4-kexec-1050-pro/kexec.bin: - cd ps4-kexec-1050-pro; make +# Firmware version formatting helper (handles both 3-digit and 4-digit versions) +# 505 -> 5_05, 1000 -> 10_00, 1302 -> 13_02 +define FW_VERSION +$(shell printf '%s' '$(1)' | sed 's/\(.*\)\(..\)/\1_\2/') +endef -fw1050/payload-1050-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1050-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 main.c -Wl,-gc-sections -o fw1050/payload-1050-1gb-pro.elf -fPIE -ffreestanding +.PHONY: all clean -fw1050/payload-1050-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1050-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1050/payload-1050-2gb-pro.elf -fPIE -ffreestanding - -fw1050/payload-1050-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1050-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1050/payload-1050-3gb-pro.elf -fPIE -ffreestanding - -fw1050/payload-1050-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1050-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1050/payload-1050-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-1050-baikal/kexec.bin: - cd ps4-kexec-1050-baikal; make - -fw1050/payload-1050-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1050-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 main-baikal.c -Wl,-gc-sections -o fw1050/payload-1050-1gb-baikal.elf -fPIE -ffreestanding - -fw1050/payload-1050-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1050-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1050/payload-1050-2gb-baikal.elf -fPIE -ffreestanding - -fw1050/payload-1050-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1050-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1050/payload-1050-3gb-baikal.elf -fPIE -ffreestanding - -fw1050/payload-1050-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1050-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1050/payload-1050-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1050-pro-baikal/kexec.bin: - cd ps4-kexec-1050-pro-baikal; make - -fw1050/payload-1050-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1050-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 main-baikal.c -Wl,-gc-sections -o fw1050/payload-1050-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw1050/payload-1050-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1050-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1050/payload-1050-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw1050/payload-1050-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1050-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1050/payload-1050-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw1050/payload-1050-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1050-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__10_50__ -DPS4_10_50 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1050/payload-1050-4gb-pro-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1100/kexec.bin: - cd ps4-kexec-1100; make - -fw1100/payload-1100-1gb.elf: ../lib/lib.a main.c ps4-kexec-1100/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 main.c -Wl,-gc-sections -o fw1100/payload-1100-1gb.elf -fPIE -ffreestanding - -fw1100/payload-1100-2gb.elf: ../lib/lib.a main.c ps4-kexec-1100/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1100/payload-1100-2gb.elf -fPIE -ffreestanding - -fw1100/payload-1100-3gb.elf: ../lib/lib.a main.c ps4-kexec-1100/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1100/payload-1100-3gb.elf -fPIE -ffreestanding - -fw1100/payload-1100-4gb.elf: ../lib/lib.a main.c ps4-kexec-1100/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1100/payload-1100-4gb.elf -fPIE -ffreestanding - -ps4-kexec-1100-pro/kexec.bin: - cd ps4-kexec-1100-pro; make - -fw1100/payload-1100-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1100-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 main.c -Wl,-gc-sections -o fw1100/payload-1100-1gb-pro.elf -fPIE -ffreestanding - -fw1100/payload-1100-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1100-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1100/payload-1100-2gb-pro.elf -fPIE -ffreestanding - -fw1100/payload-1100-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1100-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1100/payload-1100-3gb-pro.elf -fPIE -ffreestanding - -fw1100/payload-1100-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1100-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1100/payload-1100-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-1100-baikal/kexec.bin: - cd ps4-kexec-1100-baikal; make - -fw1100/payload-1100-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1100-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 main-baikal.c -Wl,-gc-sections -o fw1100/payload-1100-1gb-baikal.elf -fPIE -ffreestanding - -fw1100/payload-1100-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1100-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1100/payload-1100-2gb-baikal.elf -fPIE -ffreestanding - -fw1100/payload-1100-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1100-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1100/payload-1100-3gb-baikal.elf -fPIE -ffreestanding - -fw1100/payload-1100-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1100-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1100/payload-1100-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1100-pro-baikal/kexec.bin: - cd ps4-kexec-1100-pro-baikal; make - -fw1100/payload-1100-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1100-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 main-baikal.c -Wl,-gc-sections -o fw1100/payload-1100-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw1100/payload-1100-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1100-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1100/payload-1100-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw1100/payload-1100-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1100-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1100/payload-1100-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw1100/payload-1100-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1100-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_00__ -DPS4_11_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1100/payload-1100-4gb-pro-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1102/kexec.bin: - cd ps4-kexec-1102; make - -fw1102/payload-1102-1gb.elf: ../lib/lib.a main.c ps4-kexec-1102/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 main.c -Wl,-gc-sections -o fw1102/payload-1102-1gb.elf -fPIE -ffreestanding - -fw1102/payload-1102-2gb.elf: ../lib/lib.a main.c ps4-kexec-1102/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1102/payload-1102-2gb.elf -fPIE -ffreestanding - -fw1102/payload-1102-3gb.elf: ../lib/lib.a main.c ps4-kexec-1102/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1102/payload-1102-3gb.elf -fPIE -ffreestanding - -fw1102/payload-1102-4gb.elf: ../lib/lib.a main.c ps4-kexec-1102/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1102/payload-1102-4gb.elf -fPIE -ffreestanding - -ps4-kexec-1102-pro/kexec.bin: - cd ps4-kexec-1102-pro; make - -fw1102/payload-1102-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1102-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 main.c -Wl,-gc-sections -o fw1102/payload-1102-1gb-pro.elf -fPIE -ffreestanding - -fw1102/payload-1102-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1102-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1102/payload-1102-2gb-pro.elf -fPIE -ffreestanding - -fw1102/payload-1102-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1102-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1102/payload-1102-3gb-pro.elf -fPIE -ffreestanding - -fw1102/payload-1102-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1102-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1102/payload-1102-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-1102-baikal/kexec.bin: - cd ps4-kexec-1102-baikal; make - -fw1102/payload-1102-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1102-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 main-baikal.c -Wl,-gc-sections -o fw1102/payload-1102-1gb-baikal.elf -fPIE -ffreestanding - -fw1102/payload-1102-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1102-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1102/payload-1102-2gb-baikal.elf -fPIE -ffreestanding - -fw1102/payload-1102-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1102-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1102/payload-1102-3gb-baikal.elf -fPIE -ffreestanding - -fw1102/payload-1102-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1102-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1102/payload-1102-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1102-pro-baikal/kexec.bin: - cd ps4-kexec-1102-pro-baikal; make - -fw1102/payload-1102-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1102-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 main-baikal.c -Wl,-gc-sections -o fw1102/payload-1102-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw1102/payload-1102-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1102-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1102/payload-1102-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw1102/payload-1102-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1102-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1102/payload-1102-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw1102/payload-1102-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1102-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_02__ -DPS4_11_02 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1102/payload-1102-4gb-pro-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1150/kexec.bin: - cd ps4-kexec-1150; make - -fw1150/payload-1150-1gb.elf: ../lib/lib.a main.c ps4-kexec-1150/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 main.c -Wl,-gc-sections -o fw1150/payload-1150-1gb.elf -fPIE -ffreestanding - -fw1150/payload-1150-2gb.elf: ../lib/lib.a main.c ps4-kexec-1150/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1150/payload-1150-2gb.elf -fPIE -ffreestanding - -fw1150/payload-1150-3gb.elf: ../lib/lib.a main.c ps4-kexec-1150/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1150/payload-1150-3gb.elf -fPIE -ffreestanding - -fw1150/payload-1150-4gb.elf: ../lib/lib.a main.c ps4-kexec-1150/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1150/payload-1150-4gb.elf -fPIE -ffreestanding - -ps4-kexec-1150-pro/kexec.bin: - cd ps4-kexec-1150-pro; make - -fw1150/payload-1150-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1150-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 main.c -Wl,-gc-sections -o fw1150/payload-1150-1gb-pro.elf -fPIE -ffreestanding - -fw1150/payload-1150-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1150-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1150/payload-1150-2gb-pro.elf -fPIE -ffreestanding - -fw1150/payload-1150-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1150-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1150/payload-1150-3gb-pro.elf -fPIE -ffreestanding - -fw1150/payload-1150-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1150-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1150/payload-1150-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-1150-baikal/kexec.bin: - cd ps4-kexec-1150-baikal; make - -fw1150/payload-1150-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1150-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 main-baikal.c -Wl,-gc-sections -o fw1150/payload-1150-1gb-baikal.elf -fPIE -ffreestanding - -fw1150/payload-1150-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1150-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1150/payload-1150-2gb-baikal.elf -fPIE -ffreestanding - -fw1150/payload-1150-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1150-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1150/payload-1150-3gb-baikal.elf -fPIE -ffreestanding - -fw1150/payload-1150-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1150-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1150/payload-1150-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1150-pro-baikal/kexec.bin: - cd ps4-kexec-1150-pro-baikal; make - -fw1150/payload-1150-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1150-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 main-baikal.c -Wl,-gc-sections -o fw1150/payload-1150-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw1150/payload-1150-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1150-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1150/payload-1150-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw1150/payload-1150-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1150-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1150/payload-1150-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw1150/payload-1150-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1150-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__11_50__ -DPS4_11_50 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1150/payload-1150-4gb-pro-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1200/kexec.bin: - cd ps4-kexec-1200; make - -fw1200/payload-1200-1gb.elf: ../lib/lib.a main.c ps4-kexec-1200/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 main.c -Wl,-gc-sections -o fw1200/payload-1200-1gb.elf -fPIE -ffreestanding - -fw1200/payload-1200-2gb.elf: ../lib/lib.a main.c ps4-kexec-1200/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1200/payload-1200-2gb.elf -fPIE -ffreestanding - -fw1200/payload-1200-3gb.elf: ../lib/lib.a main.c ps4-kexec-1200/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1200/payload-1200-3gb.elf -fPIE -ffreestanding - -fw1200/payload-1200-4gb.elf: ../lib/lib.a main.c ps4-kexec-1200/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1200/payload-1200-4gb.elf -fPIE -ffreestanding - -ps4-kexec-1200-pro/kexec.bin: - cd ps4-kexec-1200-pro; make - -fw1200/payload-1200-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1200-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 main.c -Wl,-gc-sections -o fw1200/payload-1200-1gb-pro.elf -fPIE -ffreestanding - -fw1200/payload-1200-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1200-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1200/payload-1200-2gb-pro.elf -fPIE -ffreestanding - -fw1200/payload-1200-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1200-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1200/payload-1200-3gb-pro.elf -fPIE -ffreestanding - -fw1200/payload-1200-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1200-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1200/payload-1200-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-1200-baikal/kexec.bin: - cd ps4-kexec-1200-baikal; make - -fw1200/payload-1200-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1200-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 main-baikal.c -Wl,-gc-sections -o fw1200/payload-1200-1gb-baikal.elf -fPIE -ffreestanding - -fw1200/payload-1200-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1200-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1200/payload-1200-2gb-baikal.elf -fPIE -ffreestanding - -fw1200/payload-1200-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1200-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1200/payload-1200-3gb-baikal.elf -fPIE -ffreestanding - -fw1200/payload-1200-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1200-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1200/payload-1200-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1200-pro-baikal/kexec.bin: - cd ps4-kexec-1200-pro-baikal; make - -fw1200/payload-1200-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1200-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 main-baikal.c -Wl,-gc-sections -o fw1200/payload-1200-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw1200/payload-1200-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1200-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1200/payload-1200-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw1200/payload-1200-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1200-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1200/payload-1200-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw1200/payload-1200-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1200-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_00__ -DPS4_12_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1200/payload-1200-4gb-pro-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1250/kexec.bin: - cd ps4-kexec-1250; make - -fw1250/payload-1250-1gb.elf: ../lib/lib.a main.c ps4-kexec-1250/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 main.c -Wl,-gc-sections -o fw1250/payload-1250-1gb.elf -fPIE -ffreestanding - -fw1250/payload-1250-2gb.elf: ../lib/lib.a main.c ps4-kexec-1250/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1250/payload-1250-2gb.elf -fPIE -ffreestanding - -fw1250/payload-1250-3gb.elf: ../lib/lib.a main.c ps4-kexec-1250/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1250/payload-1250-3gb.elf -fPIE -ffreestanding - -fw1250/payload-1250-4gb.elf: ../lib/lib.a main.c ps4-kexec-1250/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1250/payload-1250-4gb.elf -fPIE -ffreestanding -ps4-kexec-1250-pro/kexec.bin: - cd ps4-kexec-1250-pro; make - -fw1250/payload-1250-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1250-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 main.c -Wl,-gc-sections -o fw1250/payload-1250-1gb-pro.elf -fPIE -ffreestanding - -fw1250/payload-1250-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1250-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1250/payload-1250-2gb-pro.elf -fPIE -ffreestanding - -fw1250/payload-1250-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1250-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1250/payload-1250-3gb-pro.elf -fPIE -ffreestanding - -fw1250/payload-1250-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1250-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1250/payload-1250-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-1250-baikal/kexec.bin: - cd ps4-kexec-1250-baikal; make - -fw1250/payload-1250-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-1gb-baikal.elf -fPIE -ffreestanding - -fw1250/payload-1250-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-2gb-baikal.elf -fPIE -ffreestanding - -fw1250/payload-1250-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-3gb-baikal.elf -fPIE -ffreestanding - -fw1250/payload-1250-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1250-pro-baikal/kexec.bin: - cd ps4-kexec-1250-pro-baikal; make - -fw1250/payload-1250-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw1250/payload-1250-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw1250/payload-1250-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw1250/payload-1250-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1250-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__12_50__ -DPS4_12_50 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1250/payload-1250-4gb-pro-baikal.elf -fPIE -ffreestanding - -fw1300/payload-1300-1gb.elf: ../lib/lib.a main.c ps4-kexec-1300/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 main.c -Wl,-gc-sections -o fw1300/payload-1300-1gb.elf -fPIE -ffreestanding - -fw1300/payload-1300-2gb.elf: ../lib/lib.a main.c ps4-kexec-1300/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1300/payload-1300-2gb.elf -fPIE -ffreestanding - -fw1300/payload-1300-3gb.elf: ../lib/lib.a main.c ps4-kexec-1300/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1300/payload-1300-3gb.elf -fPIE -ffreestanding - -fw1300/payload-1300-4gb.elf: ../lib/lib.a main.c ps4-kexec-1300/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1300/payload-1300-4gb.elf -fPIE -ffreestanding -ps4-kexec-1300-pro/kexec.bin: - cd ps4-kexec-1300-pro; make - -fw1300/payload-1300-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1300-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 main.c -Wl,-gc-sections -o fw1300/payload-1300-1gb-pro.elf -fPIE -ffreestanding - -fw1300/payload-1300-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1300-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1300/payload-1300-2gb-pro.elf -fPIE -ffreestanding - -fw1300/payload-1300-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1300-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1300/payload-1300-3gb-pro.elf -fPIE -ffreestanding - -fw1300/payload-1300-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1300-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1300/payload-1300-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-1300-baikal/kexec.bin: - cd ps4-kexec-1300-baikal; make - -fw1300/payload-1300-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-1gb-baikal.elf -fPIE -ffreestanding - -fw1300/payload-1300-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-2gb-baikal.elf -fPIE -ffreestanding - -fw1300/payload-1300-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-3gb-baikal.elf -fPIE -ffreestanding - -fw1300/payload-1300-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1300-pro-baikal/kexec.bin: - cd ps4-kexec-1300-pro-baikal; make - -fw1300/payload-1300-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw1300/payload-1300-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-2gb-pro-baikal.elf -fPIE -ffreestanding - -fw1300/payload-1300-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-3gb-pro-baikal.elf -fPIE -ffreestanding - -fw1300/payload-1300-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1300-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_00__ -DPS4_13_00 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1300/payload-1300-4gb-pro-baikal.elf -fPIE -ffreestanding - -fw1302/payload-1302-1gb.elf: ../lib/lib.a main.c ps4-kexec-1302/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 main.c -Wl,-gc-sections -o fw1302/payload-1302-1gb.elf -fPIE -ffreestanding - -fw1302/payload-1302-2gb.elf: ../lib/lib.a main.c ps4-kexec-1302/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1302/payload-1302-2gb.elf -fPIE -ffreestanding - -fw1302/payload-1302-3gb.elf: ../lib/lib.a main.c ps4-kexec-1302/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1302/payload-1302-3gb.elf -fPIE -ffreestanding - -fw1302/payload-1302-4gb.elf: ../lib/lib.a main.c ps4-kexec-1302/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1302/payload-1302-4gb.elf -fPIE -ffreestanding -ps4-kexec-1302-pro/kexec.bin: - cd ps4-kexec-1302-pro; make - -fw1302/payload-1302-1gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1302-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 main.c -Wl,-gc-sections -o fw1302/payload-1302-1gb-pro.elf -fPIE -ffreestanding - -fw1302/payload-1302-2gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1302-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=2 main.c -Wl,-gc-sections -o fw1302/payload-1302-2gb-pro.elf -fPIE -ffreestanding - -fw1302/payload-1302-3gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1302-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=3 main.c -Wl,-gc-sections -o fw1302/payload-1302-3gb-pro.elf -fPIE -ffreestanding - -fw1302/payload-1302-4gb-pro.elf: ../lib/lib.a main.c ps4-kexec-1302-pro/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=4 main.c -Wl,-gc-sections -o fw1302/payload-1302-4gb-pro.elf -fPIE -ffreestanding - -ps4-kexec-1302-baikal/kexec.bin: - cd ps4-kexec-1302-baikal; make - -fw1302/payload-1302-1gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-1gb-baikal.elf -fPIE -ffreestanding - -fw1302/payload-1302-2gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-2gb-baikal.elf -fPIE -ffreestanding - -fw1302/payload-1302-3gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-3gb-baikal.elf -fPIE -ffreestanding - -fw1302/payload-1302-4gb-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-4gb-baikal.elf -fPIE -ffreestanding - -ps4-kexec-1302-pro-baikal/kexec.bin: - cd ps4-kexec-1302-pro-baikal; make - -fw1302/payload-1302-1gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-1gb-pro-baikal.elf -fPIE -ffreestanding - -fw1302/payload-1302-2gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=2 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-2gb-pro-baikal.elf -fPIE -ffreestanding +all: $(PAYLOADS) -fw1302/payload-1302-3gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=3 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-3gb-pro-baikal.elf -fPIE -ffreestanding +clean: + rm -f fw*/* + @for fw in $(FIRMWARES); do \ + for var in "" "-pro" "-baikal" "-pro-baikal"; do \ + if [ -d "ps4-kexec-$${fw}$${var}" ]; then \ + cd "ps4-kexec-$${fw}$${var}" && $(MAKE) clean && cd ..; \ + fi; \ + done; \ + done + cd ../lib/ && $(MAKE) clean + +# Library dependency +../lib/lib.a: + cd ../lib && $(MAKE) -fw1302/payload-1302-4gb-pro-baikal.elf: ../lib/lib.a main-baikal.c ps4-kexec-1302-pro-baikal/kexec.bin - gcc -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib.a -D__13_02__ -DPS4_13_02 -DVRAM_GB_DEFAULT=4 main-baikal.c -Wl,-gc-sections -o fw1302/payload-1302-4gb-pro-baikal.elf -fPIE -ffreestanding +# Pattern rules for kexec binaries +ps4-kexec-%/kexec.bin: + cd ps4-kexec-$* && $(MAKE) +# Convert ELF to BIN %.bin: %.elf objcopy $< --only-section .text --only-section .data --only-section .bss --only-section .rodata -O binary $@ file $@ | fgrep -q '$@: DOS executable (COM)' -ps4-kexec-%/kexec.bin: - cd ps4-kexec-$*; make +# Template function to generate rules +# Usage: $(call make-payload-rule,firmware,size,main-source,variant-suffix) +define make-payload-rule +fw$(1)/payload-$(1)-$(2)$(4).elf: ../lib/lib.a $(3) ps4-kexec-$(1)$(4)/kexec.bin + $(CC) $(CFLAGS) ../lib/lib.a -D__$(call FW_VERSION,$(1))__ -DPS4_$(call FW_VERSION,$(1)) $(call VRAM_FLAGS,$(2)) $(3) $(LDFLAGS) -o $$@ +endef + +# Generate all rules for each firmware +$(foreach fw,$(FIRMWARES), \ + $(foreach sz,$(SIZES), \ + $(eval $(call make-payload-rule,$(fw),$(sz),main.c,)) \ + $(eval $(call make-payload-rule,$(fw),$(sz),main.c,-pro)) \ + $(eval $(call make-payload-rule,$(fw),$(sz),main-baikal.c,-baikal)) \ + $(eval $(call make-payload-rule,$(fw),$(sz),main-baikal.c,-pro-baikal)) \ + ) \ +) \ No newline at end of file