diff --git a/README.md b/README.md index c27b446..c8209bb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ps5-linux -**ps5-linux** leverages patched HV vulnerabilities to transform your **PS5 Phat and Slim** console running **3.00-13.60 firmwares** into a highly capable Linux PC, unlocking its full hardware potential for desktop use. Powered by 8 CPU cores (16 threads) at **3.5 GHz** and a GPU at **2.23 GHz**, it provides enough performance to run Steam games and various emulators with impressive fluidity. +**ps5-linux** leverages patched HV vulnerabilities to transform your **PS5 Phat, Slim and Pro** console running **3.00-13.60 firmwares** into a highly capable Linux PC, unlocking its full hardware potential for desktop use. Powered by 8 CPU cores (16 threads) at **3.5 GHz** and a GPU at **2.23 GHz**, it provides enough performance to run Steam games and various emulators with impressive fluidity. Features: @@ -16,8 +16,9 @@ Features: ## PS5 firmware -*ps5-linux* is only supported on PS5 Phat and Slim on the following firmwares: +*ps5-linux* is supported on the following firmwares: +**PS5 Phat and Slim:** - **3.00**, **3.10**, **3.20**, **3.21** without M.2 support - **4.00**, **4.02**, **4.03**, **4.50**, **4.51** with M.2 support - **5.00**, **5.02**, **5.10**, **5.50** with M.2 support @@ -25,6 +26,9 @@ Features: - **7.00**, **7.01**, **7.20**, **7.40**, **7.60**, **7.61** with M.2 support - **13.00**, **13.20**, **13.60** with M.2 support +**PS5 Pro:** +- **13.60** with M.2 support + Support for 1.xx and 2.xx firmwares may be added in the future, but we will not prioritize this effort. If you are on firmwares in-between or you want to update to a specific firmware, [download the correct PUP](https://darthsternie.net/ps5-firmwares/) and follow the [official guide](https://www.playstation.com/en-us/support/hardware/reinstall-playstation-system-software-safe-mode) to upgrade your PS5. **Obviously you cannot downgrade.** @@ -273,7 +277,7 @@ For any future ps5-linux updates, you can download the `.deb` or `.pkg.tar.zst` - Q: Can I continue using my PS5 if I install Linux? - A: Yes, the internal SSD is not modified - Q: Can I use the PS5's NIC/WLAN module in Linux? - - A: WLAN is only supported for Marvell chipsets at the moment. Ethernet is supported on all models. + - A: WLAN is supported for Marvell chipsets (Phat/Slim) and the MediaTek MT7925 (Pro) — both work out of the box with the included firmware. Ethernet is supported on all models. - Q: Does hardware video decode and transcode work? - A: Yes, since [ps5-linux-patches #38](https://github.com/ps5-linux/ps5-linux-patches/pull/38). Decode works for H.264, HEVC Main/Main 10, VP9 Profile 0/2, VC-1 and MPEG-2. Encode works for H.264, and for HEVC with I-frames only. - Q: Does the DualSense controller work? @@ -326,3 +330,4 @@ Join our [Discord server](https://discord.gg/PeMGVB7BAm) to celebrate Linux on P - [echostretch](https://github.com/echostretch): Offsets and testing - [kirathenotebook](https://github.com/kirathenotebook): Betatesting and readme contribution - 15432: Tests on BC-250 +- [DeathlySin](https://github.com/DeathlySin): PS5 Pro support (E820 memory map, I/O fix) diff --git a/include/config.h b/include/config.h index df29b7d..037244a 100644 --- a/include/config.h +++ b/include/config.h @@ -33,6 +33,11 @@ "root=/dev/sda2 rw rootwait console=ttyTitania0 console=tty0 " \ "video=DP-1:1920x1080@60 mitigations=off idle=halt pci=pcie_bus_perf" +// HV layout: derive_h() returns the region base at runtime. On regular the +// HV binary starts at the region base; on Pro it sits 8MB in. +#define HV_BASE_REGULAR 0x062800000ULL +#define HV_CODE_OFFSET_PRO 0x800000ULL + #define DEBUG 0 // Toggle to 0 to disable logs #endif diff --git a/include/hv_defeat_13xx.h b/include/hv_defeat_13xx.h index 55bdefb..0aa1421 100644 --- a/include/hv_defeat_13xx.h +++ b/include/hv_defeat_13xx.h @@ -2,7 +2,9 @@ #define HV_DEFEAT_13XX_H #include +#include +uint64_t derive_h(void); int hv_defeat_13xx(void *shellcode_kernel, size_t shellcode_kernel_len); #endif diff --git a/include/utils.h b/include/utils.h index aab463d..31b8482 100644 --- a/include/utils.h +++ b/include/utils.h @@ -8,6 +8,7 @@ #include int sceKernelGetCurrentCpu(); +int sceKernelGetMainSocId(); int sceKernelSendNotificationRequest(int, void *, size_t, int); int sceKernelOpenEventFlag(void *, const char *); int sceKernelNotifySystemSuspendStart(void); @@ -162,7 +163,7 @@ bool if_exists(const char *path); bool sceKernelIsTestKit(void); bool sceKernelIsDevKit(void); -enum kit_type { KIT_RETAIL, KIT_TESTKIT, KIT_DEVKIT }; +enum kit_type { KIT_RETAIL, KIT_TESTKIT, KIT_DEVKIT, KIT_RETAIL_PRO }; enum kit_type get_kit_type(void); diff --git a/shellcode_hv/boot_linux.c b/shellcode_hv/boot_linux.c index 3265396..997a056 100644 --- a/shellcode_hv/boot_linux.c +++ b/shellcode_hv/boot_linux.c @@ -57,14 +57,27 @@ static void e820_memory_setup(struct boot_params *bp) { append_e820_table(bp, 0x000070000, 0x0000a0000, E820_TYPE_RESERVED); append_e820_table(bp, 0x0000a0000, 0x0000c0000, E820_TYPE_RESERVED); append_e820_table(bp, 0x0000c0000, 0x000100000, E820_TYPE_RESERVED); // VBIOS - append_e820_table(bp, 0x000100000, 0x03fffc000, E820_TYPE_RAM); - append_e820_table(bp, 0x03fffc000, 0x040000000, E820_TYPE_RESERVED); - append_e820_table(bp, 0x040000000, 0x060000000, E820_TYPE_RAM); - append_e820_table(bp, 0x060000000, 0x060800000, E820_TYPE_RESERVED); // MP4 + append_e820_table(bp, 0x000100000, 0x001000000, E820_TYPE_RAM); + if (info.kit_type == KIT_RETAIL_PRO) { + append_e820_table(bp, 0x001000000, 0x040000000, E820_TYPE_RAM); + } else { + append_e820_table(bp, 0x001000000, 0x040000000, E820_TYPE_RESERVED); + } + if (info.kit_type == KIT_RETAIL_PRO) { + append_e820_table(bp, 0x040000000, 0x060800000, E820_TYPE_RAM); + } else { + append_e820_table(bp, 0x040000000, 0x060000000, E820_TYPE_RAM); + append_e820_table(bp, 0x060000000, 0x060800000, E820_TYPE_RESERVED); // MP4 + } append_e820_table(bp, 0x060800000, 0x060c00000, E820_TYPE_RESERVED); // VCN FW - append_e820_table(bp, 0x060c00000, 0x062800000, E820_TYPE_RAM); - append_e820_table(bp, 0x062800000, 0x064800000, E820_TYPE_RESERVED); // HV - append_e820_table(bp, 0x064800000, 0x064829000, E820_TYPE_RESERVED); // MP3 + if (info.kit_type == KIT_RETAIL_PRO) { + append_e820_table(bp, 0x060c00000, 0x064000000, E820_TYPE_RAM); + append_e820_table(bp, 0x064000000, 0x064829000, E820_TYPE_RESERVED); // MP3 + } else { + append_e820_table(bp, 0x060c00000, 0x062800000, E820_TYPE_RAM); + append_e820_table(bp, 0x062800000, 0x064800000, E820_TYPE_RESERVED); // HV + append_e820_table(bp, 0x064800000, 0x064829000, E820_TYPE_RESERVED); // MP3 + } append_e820_table(bp, 0x064829000, 0x07f9d0000, E820_TYPE_RAM); append_e820_table(bp, 0x07f9d0000, 0x07fd5f000, E820_TYPE_RESERVED); append_e820_table(bp, 0x07fd5f000, 0x07fd63000, E820_TYPE_RESERVED); @@ -79,8 +92,15 @@ static void e820_memory_setup(struct boot_params *bp) { append_e820_table(bp, 0x100000000, VRAM_BASE, E820_TYPE_RAM); append_e820_table(bp, VRAM_BASE, 0x470000000, E820_TYPE_RESERVED); // VRAM + if (info.kit_type == KIT_RETAIL_PRO) { + append_e820_table(bp, 0x470000000, 0x47f300000, E820_TYPE_RAM); + append_e820_table(bp, 0x47f300000, 0x880000000, E820_TYPE_RESERVED); + append_e820_table(bp, 0x880000000, 0x8e0000000, E820_TYPE_RAM); + append_e820_table(bp, 0x8e0000000, 0x8e2000000, E820_TYPE_RESERVED); // HV + append_e820_table(bp, 0x8e2000000, 0x900000000, E820_TYPE_RAM); + } // DevKits have 32GB - if (info.kit_type != KIT_DEVKIT) { + else if (info.kit_type != KIT_DEVKIT) { append_e820_table(bp, 0x470000000, 0x47f300000, E820_TYPE_RAM); append_e820_table(bp, 0x47f300000, 0x480000000, E820_TYPE_RESERVED); } else { @@ -116,7 +136,9 @@ void boot_linux(void) { shdr->ramdisk_size = info.initrd_size & 0xffffffff; bp->ext_ramdisk_image = info.initrd >> 32; bp->ext_ramdisk_size = info.initrd_size >> 32; - bp->acpi_rsdp_addr = ACPI_RSDP_ADDRESS; + bp->acpi_rsdp_addr = info.kit_type == KIT_RETAIL_PRO + ? ACPI_RSDP_ADDRESS_PRO + : ACPI_RSDP_ADDRESS; strcpy((char *)cmdline_pa, info.cmdline); diff --git a/shellcode_hv/boot_linux.h b/shellcode_hv/boot_linux.h index dfa23d4..6a10456 100644 --- a/shellcode_hv/boot_linux.h +++ b/shellcode_hv/boot_linux.h @@ -16,6 +16,8 @@ #define ACPI_RSDP_ADDRESS 0x7fd8e014 +#define ACPI_RSDP_ADDRESS_PRO 0x7fd8e000 + #define AMDGPU_MMIO_BASE 0xe0600000 #define RCC_CONFIG_MEMSIZE 0x378c @@ -45,4 +47,4 @@ void entry(void); void boot_linux(void); -enum kit_type { KIT_RETAIL, KIT_TESTKIT, KIT_DEVKIT }; +enum kit_type { KIT_RETAIL, KIT_TESTKIT, KIT_DEVKIT, KIT_RETAIL_PRO }; diff --git a/shellcode_kernel/boot_linux.c b/shellcode_kernel/boot_linux.c index b3f6440..8573a1e 100644 --- a/shellcode_kernel/boot_linux.c +++ b/shellcode_kernel/boot_linux.c @@ -144,23 +144,41 @@ static void install_hv_code(void) { *(uint64_t *)PHYS_TO_DMAP(l40_l3_addr + i * 8) = identity_pml40_l3[i]; } + // Keep the HV trampoline mapped after switching CR3. + uint64_t cave_index = args.hv_code_cave_pa >> 30; + if (cave_index >= l3_size) { + *(uint64_t *)PHYS_TO_DMAP(l40_l3_addr + cave_index * 8) = + (cave_index << 30) | 0x83; + } + // Install shellcode_hv memcpy((void *)PHYS_TO_DMAP(cave_hv_code), shellcode_hv_bin, shellcode_hv_bin_len); } void patch_hv(void) { - // Jump to shellcode final identity mapping - uint8_t shellcode_jmp[] = {0x48, 0xC7, 0xC0, 0xAA, - 0xAA, 0xAA, 0xAA, // mov rax, 0xAAAAAAAA - 0xFF, 0xE0}; // jmp rax + if (args.hv_code_cave_pa > 0x7fffffffULL) { + // Pro HV code cave is above 4GB, needs 64-bit movabs. + uint8_t shellcode_jmp[] = {0x48, 0xB8, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // movabs rax, imm64 + 0xFF, 0xE0}; // jmp rax - // Update code cave in hv 1:1 region - *(uint32_t *)(&shellcode_jmp[3]) = (uint32_t)args.hv_code_cave_pa; + *(uint64_t *)(&shellcode_jmp[2]) = args.hv_code_cave_pa; + memcpy((void *)PHYS_TO_DMAP(args.hv_handle_vmexit_pa), shellcode_jmp, + sizeof(shellcode_jmp)); + } else { + // Jump to shellcode final identity mapping + uint8_t shellcode_jmp[] = {0x48, 0xC7, 0xC0, 0xAA, + 0xAA, 0xAA, 0xAA, // mov rax, 0xAAAAAAAA + 0xFF, 0xE0}; // jmp rax - // Just patch the VMEXIT handler directly, avoiding all checks - memcpy((void *)PHYS_TO_DMAP(args.hv_handle_vmexit_pa), shellcode_jmp, - sizeof(shellcode_jmp)); + // Update code cave in hv 1:1 region + *(uint32_t *)(&shellcode_jmp[3]) = (uint32_t)args.hv_code_cave_pa; + + // Just patch the VMEXIT handler directly, avoiding all checks + memcpy((void *)PHYS_TO_DMAP(args.hv_handle_vmexit_pa), shellcode_jmp, + sizeof(shellcode_jmp)); + } uint8_t shellcode_identity_and_jmp[] = { 0x48, 0xB8, 0xAA, 0xAA, 0xAA, diff --git a/shellcode_kernel/main.c b/shellcode_kernel/main.c index ac91135..ba9d66e 100644 --- a/shellcode_kernel/main.c +++ b/shellcode_kernel/main.c @@ -36,6 +36,8 @@ __attribute__((section(".entry_point"))) uint32_t main(uint64_t add1, } else if ((0x0500 <= args_ptr->fw_version) && (args_ptr->fw_version < 0x0800)) { // Already escaped. + } else if (args_ptr->fw_version == 0x1360) { + // Already escaped. } else { return 0; } diff --git a/source/hv_defeat_13xx.c b/source/hv_defeat_13xx.c index 5cb510d..ff9459b 100644 --- a/source/hv_defeat_13xx.c +++ b/source/hv_defeat_13xx.c @@ -182,7 +182,7 @@ static int check_nvs_signature(void) { return 0; } -static uint64_t derive_h(void) { +uint64_t derive_h(void) { uint64_t record; if (kernel_read(ktext + env_offset.HV_EFI_MAP_ROOT, &record, sizeof(record)) || @@ -230,7 +230,7 @@ static uint64_t derive_h(void) { return 0; if (memory_type == 0 && pages <= (UINT64_MAX >> 12) && - physical_base && physical_base < 0x100000000) { + physical_base && physical_base < 0x1000000000) { uint64_t region_size = pages << 12; if (region_size > 0x17FFFFF && region_size < 0x4000001) @@ -340,7 +340,7 @@ static int pin_to_core_reborn(int core) { return cpuset_setaffinity(3, 1, -1, 0x10, (const cpuset_t *)affinity); } -static int install_startup_hook(uint64_t hv_base) { +static int install_startup_hook(uint64_t hv_region, uint64_t hv_base) { khook_t hook; kh_chain_t chain; uint8_t chain_buffer[0x800]; @@ -375,7 +375,7 @@ static int install_startup_hook(uint64_t hv_base) { ktext + env_offset.MP_STARTUP_INIT_VECTOR); kh_chain_store(&chain, carrier + 0xF08, dmap); kh_chain_store(&chain, carrier + 0xF10, oracle); - kh_chain_store(&chain, carrier + 0xF18, hv_base); + kh_chain_store(&chain, carrier + 0xF18, hv_region); kh_chain_store(&chain, carrier + 0xF20, heap_pointer_slot); kh_chain_store(&chain, carrier + 0xF28, pcpus_pointer_slot); kh_chain_store(&chain, carrier + 0xF30, 1); @@ -392,6 +392,7 @@ static int install_startup_hook(uint64_t hv_base) { } int hv_defeat_13xx(void *shellcode_kernel, size_t shellcode_kernel_len) { + uint64_t hv_region; uint64_t hv_base; uint64_t ist_pa; uint64_t ist; @@ -412,12 +413,15 @@ int hv_defeat_13xx(void *shellcode_kernel, size_t shellcode_kernel_len) { return -1; } - hv_base = derive_h(); - if (!hv_base) { + hv_region = derive_h(); + if (!hv_region) { if (fw == 0x1360) notify("13.60 hypervisor base was not found\n"); return -1; } + hv_base = hv_region + (linux_i.kit_type == KIT_RETAIL_PRO + ? HV_CODE_OFFSET_PRO + : 0); kernel_set_ucred_authid(getpid(), 0x4800000000000007ULL); result = pin_to_core_reborn(0); @@ -426,7 +430,8 @@ int hv_defeat_13xx(void *shellcode_kernel, size_t shellcode_kernel_len) { notify("13.60 core pin failed (%d)\n", result); return -1; } - result = install_startup_hook(hv_base); + + result = install_startup_hook(hv_region, hv_base); if (result) { if (fw == 0x1360) notify("13.60 startup hook failed (%d)\n", result); diff --git a/source/prepare_resume.c b/source/prepare_resume.c index 2e465a3..eb0f86a 100644 --- a/source/prepare_resume.c +++ b/source/prepare_resume.c @@ -2,6 +2,7 @@ #include "../shellcode_kernel/shellcode_kernel.h" #include "../shellcode_kernel/shellcode_kernel_args.h" #include "config.h" +#include "hv_defeat_13xx.h" #include "iommu.h" #include "offsets.h" #include "shellcode_1360.h" @@ -106,7 +107,7 @@ int prepare_resume(void **shellcode_kernel, size_t *shellcode_kernel_len) { if (fw == 0x1300 || fw == 0x1320) { shellcode_src = shellcode_kernel_13xx_bin; shellcode_len = shellcode_kernel_13xx_bin_len; - } else if (fw == 0x1360) { + } else if (fw == 0x1360 && linux_i.kit_type != KIT_RETAIL_PRO) { shellcode_src = stock_shellcode_kernel_1360; shellcode_len = stock_shellcode_kernel_1360_len; } @@ -121,7 +122,7 @@ int prepare_resume(void **shellcode_kernel, size_t *shellcode_kernel_len) { return -1; *shellcode_kernel_len = shellcode_len; memcpy(*shellcode_kernel, shellcode_src, shellcode_len); - if (fw == 0x1360 && + if (fw == 0x1360 && linux_i.kit_type != KIT_RETAIL_PRO && apply_1360_dma_e820_map(*shellcode_kernel, shellcode_len)) { notify("13.60 DMA E820 patch did not match stock shellcode\n"); return -1; @@ -196,6 +197,12 @@ uint64_t prepare_sck_args(void) { args.kernel_cfi_check = ktext + env_offset.KERNEL_CFI_CHECK; args.hv_handle_vmexit_pa = env_offset.HV_HANDLE_VMEXIT_PA; args.hv_code_cave_pa = env_offset.HV_CODE_CAVE_PA; + if (fw == 0x1360 && linux_i.kit_type == KIT_RETAIL_PRO) { + uint64_t pro_base = derive_h() + HV_CODE_OFFSET_PRO; + uint64_t hv_offset = pro_base - HV_BASE_REGULAR; + args.hv_handle_vmexit_pa += hv_offset; + args.hv_code_cave_pa += hv_offset; + } args.linux_info_va = linux_i.linux_info; diff --git a/source/utils.c b/source/utils.c index 4a247d9..d8ba258 100644 --- a/source/utils.c +++ b/source/utils.c @@ -141,7 +141,7 @@ uint64_t getpmap(uint64_t proc) { uint64_t vtophys_user(uint64_t va) { uintptr_t self_pmap = getpmap(kernel_get_proc(getpid())); uintptr_t self_pml4 = get_pml4(self_pmap); - uint64_t pa = vtophys_custom(va, self_pml4 & 0xFFFFFFFF); + uint64_t pa = vtophys_custom(va, self_pml4 - dmap); return pa; } @@ -150,7 +150,7 @@ uint64_t vtophys(uint64_t va) { return vtophys_custom(va, cr3); } // Source: PS5_kldload uint64_t vtophys_custom(uint64_t va, uint64_t cr3_custom) { - uint64_t table_phys = cr3_custom & 0xFFFFFFFF; + uint64_t table_phys = cr3_custom; for (int level = 0; level < 4; level++) { int shift = 39 - (level * 9); @@ -319,6 +319,10 @@ bool sceKernelIsDevKit(void) { } enum kit_type get_kit_type(void) { + if (sceKernelGetMainSocId() == 0x840fd0) { + notify("PS5 Pro detected\n"); + return KIT_RETAIL_PRO; + } if (sceKernelIsDevKit()) { notify("DevKit detected\n"); return KIT_DEVKIT;