initial commit support 3.xx and 4.xx

This commit is contained in:
Mateico
2026-04-24 17:30:06 +02:00
commit 4fc5de4d36
41 changed files with 4509 additions and 0 deletions
+674
View File
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
.PHONY: all clean
ifndef PS5_PAYLOAD_SDK
PS5_PAYLOAD_SDK = /opt/ps5-payload-sdk/
endif
include $(PS5_PAYLOAD_SDK)/toolchain/prospero.mk
BIN := bin/ps5-linux-loader.elf
SRC := $(wildcard source/*.c)
OBJS := $(SRC:.c=.o)
# Agregamos los headers de los shellcodes a los CFLAGS para que los encuentre en source/main.c
CFLAGS := -std=c23 -Wall -Iinclude -Ishellcode_hypervisor -Ishellcode_kernel
LDFLAGS :=
# Rutas de los headers generados
SC_HV_H := shellcode_hypervisor/shellcode_hypervisor.h
SC_K_H := shellcode_kernel/shellcode_kernel.h
# 1. Regla principal: Construir los shellcodes antes que los objetos del main
all: $(SC_HV_H) $(SC_K_H) $(BIN)
# 2. Reglas para disparar el Make en las subcarpetas
# Usamos .PHONY de forma indirecta o forzamos la entrada a la carpeta
$(SC_HV_H):
$(MAKE) -C shellcode_hypervisor
$(SC_K_H):
$(MAKE) -C shellcode_kernel
# 3. Los objetos dependen de que los headers de los shellcodes existan
# Si main.c hace #include "shellcode_hypervisor.h", necesita estos targets
$(OBJS): %.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
# 4. Link final
$(BIN): $(OBJS)
@mkdir -p $(dir $@)
$(CC) $(OBJS) $(LDFLAGS) -o $@
clean:
rm -f $(BIN) $(OBJS)
$(MAKE) -C shellcode_hypervisor clean
$(MAKE) -C shellcode_kernel clean
+83
View File
@@ -0,0 +1,83 @@
# ps5-linux-loader
This payload let's you boot a Linux kernel on bare metal hardware on the PS5 on firmwares up to 04.51
Currently firmwares 1.xx and 2.xx are not supported. They will be soon.
To do so, it uses a disclosed vulnerability that gives access to Trusted Memory Regions (TMRs).
The process to boot Linux is divided in 3 stages:
- Payload on Prospero OS that disables HV protections to install a shellcode in Kernel mode to be loader on system wake-up and loads Linux boot bins on memory.
- Kernel shellcode that disables HV protections and patches "VMEXIT_HANDLER" to install identity mapping and jump to HV shellcode.
- Hypervisor shellcode (real bare metal) that loads the required structure and boots the Linux kernel.
PLEASE USE AT YOUR OWN RISK. IT MAY DAMAGE YOUR CONSOLE TURNING IT UNREPAIRABLE.
## Credits
[theflow](https://github.com/TheOfficialFloW): Linux kernel patches, main boot config and boot strategy
[c0w](https://github.com/c0w-ar): Loader code and kernel/hv shellcodes
[fail0verflow](https://github.com/fail0verflow): HV defeat vulnerability (https://github.com/fail0verflow/prosperous)
[flatz](github.com/flatz): HV defeat vulnerability (https://gist.github.com/flatz/620ddda6d64acca6d1c990dc3080ac0e)
[cragson](https://github.com/cragson): HV defeat implementation for <= 04.51 on PS5 HEN (https://github.com/cragson/ps5-hen)
[john-tornblom](https://github.com/john-tornblom): PS5 SDK (https://github.com/ps5-payload-dev/sdk)
[echostretch](https://github.com/echostretch): Offsets and testing
## Own compilation
First, install the [ps5-payload-sdk](https://github.com/ps5-payload-dev/sdk)
Second, clone this repository and make
```
git clone git@github.com:c0w-ar/ps5-linux-loader.git
cd ps5-linux-loader
make
```
## Prework
To be able to boot linux, you first have to create a fresh install image of your desired distribution (Ubuntu recommended) on an external USB drive (SSD recommended).
You can use any live CD / USB to install to the external drive. It's recommended that you take note of your partition UUID to use it on the boot command.
Recommended boot command:
```
root=UUID={your-partition-hash} rw rootwait console=ttyTitania0 console=tty0 video=DP-1:1920x1080@60 mitigations=off idle=halt pci=pcie_bus_perf
```
## How to Use
Place the bzImage and initrd.img files on the folder "PS5/Linux/" on your USB drive.
If you wish to customize the Video Ram (vram) or Boot Command you can optionally add the files "vram.txt" and "cmdline.txt".
```
USB0 Root
└── 📁 PS5
└── 📁 Linux
└── 📄 bzImage
└── 📄 initrd.img
└── 📄 vram.txt (hex string)
└── 📄 cmdline.txt (custom boot command)
```
Send the payload
```sh
socat -t 99999 - TCP:YOUR_PS5_IP:9021 < ps5-linux-loader.elf
```
Wait until you see the message that is time to put the system to sleep.
When the orange light stop blinking and stays on, you can wake up your console and the shellcodes will be executed.
You should get the Linux kernel booted and an available console on the UART Titania.
+34
View File
@@ -0,0 +1,34 @@
#ifndef CONFIG_H
#define CONFIG_H
#define PAGE_SIZE 0x4000ULL
// This is used to allocate resources for HV shellcode and Linux boot
#define cave 0x100000000ULL
#define cave_hv_paging cave
#define cave_hv_code \
cave_hv_paging + 0x3000ULL // Leave space for 3 pages but we only use 2 for
// 1GB 1:1 mapping
#define cave_linux_files cave_hv_code + 0x2000ULL
#define cave_linux_info cave_linux_files
#define cave_bzImage cave_linux_info + PAGE_SIZE
// #define cave_initrd // Allocated dynamically after bzImage
#define hv_base_rsp (cave + 0x10000000ULL)
#define hv_stack_size 0x1000ULL
// This is used as transitional storage from ProsperoOS to Kernel shellcode
#define kernel_cave_files 0xFFFF800000000000
#define kernel_cave_linux_info kernel_cave_files
#define kernel_cave_bzImage kernel_cave_linux_info + PAGE_SIZE
// #define kernel_cave_initrd // Allocated dynamically after bzImage
// Linux boot config
#define VRAM_SIZE (512ULL * 1024 * 1024)
#define CMD_LINE \
"root=/dev/sda2 rw rootwait console=ttyTitania0 console=tty0 " \
"video=DP-1:1920x1080@60 mitigations=off idle=halt pci=pcie_bus_perf"
#define DEBUG 0 // Toggle to 0 to disable logs
#endif
+70
View File
@@ -0,0 +1,70 @@
/*** Source: ps5-hen by cragson ***/
#ifndef GPU_H
#define GPU_H
#include <stdint.h>
#define GPU_PDE_VALID_BIT 0
#define GPU_PDE_IS_PTE_BIT 54
#define GPU_PDE_TF_BIT 56
#define GPU_PDE_BLOCK_FRAG_BIT 59
#define GPU_PDE_ADDR_MASK 0x0000FFFFFFFFFFC0ULL
#define PROT_GPU_READ 0x10
#define PROT_GPU_WRITE 0x20
#define MAP_NO_COALESCE 0x00400000
#define GPU_SUBMIT_IOCTL 0xC0108102
#define PM4_TYPE3 3
#define PM4_SHADER_COMPUTE 1
#define PM4_OPCODE_DMA_DATA 0x50
#define PM4_OPCODE_INDIRECT_BUF 0x3F
struct gpu_kernel_offsets {
uint64_t proc_vmspace; // proc->p_vmspace offset
uint64_t vmspace_vm_vmid; // vmspace->vm_vmid offset
uint64_t data_base_gvmspace; // offset from kernel data base to gvmspace array
uint64_t sizeof_gvmspace; // size of each gvmspace entry
uint64_t gvmspace_page_dir_va; // gvmspace->page_dir_va offset (GPU PDB2)
uint64_t gvmspace_size; // gvmspace->size offset
uint64_t gvmspace_start_va; // gvmspace->start_va offset
};
struct gpu_ctx {
int fd; // /dev/gc file descriptor
int initialized; // 1 if gpu_init() succeeded
uint64_t victim_va; // CPU VA of victim buffer (GPU PTE remapped)
uint64_t transfer_va; // CPU VA of transfer/staging buffer
uint64_t cmd_va; // CPU VA of PM4 command buffer
uint64_t victim_real_pa; // original physical address of victim buffer
uint64_t victim_ptbe_va; // kernel VA of the GPU PTE for victim buffer
uint64_t cleared_ptbe; // GPU PTE with physical address cleared (template)
uint64_t page_size; // GPU page size for victim allocation (should be 2MB)
uint64_t dmem_size; // allocation size (2MB)
};
void gpu_set_offsets(struct gpu_kernel_offsets *offsets);
int gpu_init(void);
int gpu_init_internal(void);
int gpu_test(void);
int gpu_read_phys(uint64_t phys_addr, void *out_buf, uint32_t size);
uint8_t gpu_read_phys1(uint64_t phys_addr);
uint32_t gpu_read_phys4(uint64_t phys_addr);
uint64_t gpu_read_phys8(uint64_t phys_addr);
int gpu_write_phys(uint64_t phys_addr, const void *in_buf, uint32_t size);
void gpu_write_phys4(uint64_t phys_addr, uint32_t value);
void gpu_write_phys8(uint64_t phys_addr, uint64_t value);
void gpu_cleanup(void);
struct gpu_ctx *gpu_get_ctx(void);
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef HV_DEFEAT_H
#define HV_DEFEAT_H
#include "iommu.h"
#include <stdint.h>
int hv_defeat(void);
int stage1_tmr_relax(void);
int stage2_find_vmcbs(void);
uint64_t get_vmcb(int core);
int iommu_selftest(void);
int stage3_patch_vmcbs(void);
int stage4_force_vmcb_reload(void);
int stage5_remove_xotext(void);
int stage6_kernel_pmap_invalidate_all(void);
int stage7_install_kexec(void);
int kexec(uint64_t fptr);
#endif
+46
View File
@@ -0,0 +1,46 @@
/*** Source: ps5-hen by cragson ***/
#ifndef IOMMU_H
#define IOMMU_H
#include <stdint.h>
// Command buffer MMIO offsets
#define IOMMU_MMIO_CB_HEAD 0xa000
#define IOMMU_MMIO_CB_TAIL 0xa008
// Queue constants
#define IOMMU_CB_SIZE 0x2000
#define IOMMU_CB_MASK (IOMMU_CB_SIZE - 1)
#define IOMMU_CMD_ENTRY_SIZE 0x10
// IOMMU softc field offsets
#define IOMMU_SC_MMIO_VA 0x40
#define IOMMU_SC_CB2_PTR 0x78
#define IOMMU_SC_CB3_PTR 0x80
#define IOMMU_SC_EB_PTR 0x60b90
typedef struct _iommu_ctx {
uint64_t cb2_base; // kernel VA of command buffer 2 (hv terminology)
uint64_t cb3_base; // kernel VA of command buffer 3 (hv terminology)
uint64_t eb_base; // kernel VA of event buffer
uint64_t mmio_va; // DMAP VA of IOMMU MMIO base
} iommu_ctx;
extern iommu_ctx iommu_store;
extern iommu_ctx *iommu;
int iommu_init(void);
// Submit a single 16-byte command and wait for completion
void iommu_submit_cmd(const void *cmd);
// Write 8 bytes to a physical address using IOMMU completion wait store
void iommu_write8_pa(uint64_t pa, uint64_t val);
// Write 4 bytes to a physical address
void iommu_write4_pa(uint64_t pa, uint32_t val);
// Write arbitrary length to a physical address in 8-byte chunks
void iommu_write_pa(uint64_t pa, const void *data, uint32_t len);
#endif
+11
View File
@@ -0,0 +1,11 @@
#include "utils.h"
#include <stdint.h>
static uint64_t alloc_page(void);
static void install_page(uintptr_t pml4, vm_offset_t va, vm_paddr_t pa,
int bits);
void pte_store(uintptr_t ptep, uint64_t pte);
static int read_file(const char *path, void *buf, size_t bufsize);
static void trim_newline(char *s);
static size_t fetch_file(int port, void *buf, size_t bufsize);
int fetch_linux(struct linux_info *info);
+8
View File
@@ -0,0 +1,8 @@
#ifndef MAIN_H
#define MAIN_H
int main(void);
int setup_env(void);
int prepare_resume(void);
#endif
+51
View File
@@ -0,0 +1,51 @@
#ifndef OFFSETS_H
#define OFFSETS_H
#include <stdint.h>
typedef struct _offset_list {
uint64_t PMAP_STORE;
uint64_t HV_BSS_OFF; // Needed for 1.xx and 2.xx
uint64_t HV_VCPU; // Needed for 1.xx and 2.xx
uint64_t HV_VCPU_CPUID; // Needed for 1.xx and 2.xx
uint64_t HV_VCPU_ARRAY_OFF; // Needed for 1.xx and 2.xx
uint64_t HV_VCPU_STRIDE; // Needed for 1.xx and 2.xx
uint64_t HV_VCPU_VMCB_PTR; // Needed for 1.xx and 2.xx
uint64_t KERNEL_CODE_CAVE;
uint64_t KERNEL_DATA_CAVE;
uint64_t IOMMU_SOFTC;
uint64_t VMSPACE_VM_VMID;
uint64_t VMSPACE_VM_PMAP;
uint64_t PMAP_PM_PML4;
uint64_t PMAP_PM_CR3;
uint64_t DATA_BASE_GVMSPACE;
uint64_t HOOK_ACPI_WAKEUP_MACHDEP;
uint64_t FUN_PRINTF;
uint64_t FUN_VA_TO_PA;
uint64_t FUN_HV_IOMMU_SET_BUFFERS;
uint64_t FUN_HV_IOMM_WAIT_COMPLETION;
uint64_t FUN_SMP_RENDEZVOUS;
uint64_t FUN_SMP_NO_RENDEVOUS_BARRIER;
uint64_t HV_HANDLE_VMEXIT_PA;
uint64_t HV_CODE_CAVE_PA;
uint64_t HV_UART_OVERRIDE_PA;
uint64_t G_VBIOS;
uint64_t FUN_TRANSMITTER_CONTROL;
uint64_t FUN_MP3_INITIALIZE;
uint64_t FUN_MP3_INVOKE;
uint64_t KERNEL_UART_OVERRIDE;
uint64_t KERNEL_DEBUG_PATCH;
uint64_t KERNEL_CFI_CHECK;
} offset_list;
extern offset_list off_0300;
extern offset_list off_0310;
extern offset_list off_0320;
extern offset_list off_0321;
extern offset_list off_0400;
extern offset_list off_0402;
extern offset_list off_0403;
extern offset_list off_0450;
extern offset_list off_0451;
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef TMR_H
#define TMR_H
#include <stdint.h>
#define ECAM_B0D18F2 dmap + (0xF0000000ULL + 0x18ULL * 0x8000 + 2 * 0x1000)
#define TMR_INDEX_OFF 0x80
#define TMR_DATA_OFF 0x84
#define TMR_BASE(n) ((n) * 0x10 + 0x00)
#define TMR_LIMIT(n) ((n) * 0x10 + 0x04)
#define TMR_CONFIG(n) ((n) * 0x10 + 0x08)
#define TMR_REQUESTORS(n) ((n) * 0x10 + 0x0C)
#define TMR_CFG_PERMISSIVE 0x3F07
uint32_t tmr_read(uint32_t addr);
void tmr_write(uint32_t addr, uint32_t val);
#endif
+162
View File
@@ -0,0 +1,162 @@
#ifndef UTILS_H
#define UTILS_H
#include "offsets.h"
#include <ps5/kernel.h>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
int sceKernelGetCurrentCpu();
int sceKernelSendNotificationRequest(int, void *, size_t, int);
typedef struct _sysent {
uint32_t n_arg;
uint32_t pad;
uint64_t sy_call;
uint64_t sy_auevent;
uint64_t sy_systrace_args;
uint32_t sy_entry;
uint32_t sy_return;
uint32_t sy_flags;
uint32_t sy_thrcnt;
} sysent;
typedef struct __flat_pmap {
uint64_t mtx_name_ptr;
uint64_t mtx_flags;
uint64_t mtx_data;
uint64_t mtx_lock;
uint64_t pm_pml4;
uint64_t pm_cr3;
} flat_pmap;
struct linux_info {
uintptr_t bzimage;
size_t bzimage_size;
uintptr_t initrd;
size_t initrd_size;
size_t vram_size;
char cmdline[2048];
uintptr_t linux_info; // PA of linux_info
};
/** These vars are global for the payload to simplify things */
extern offset_list env_offset; // Defined on utils.c
extern uint64_t ktext; // Defined on utils.c
extern uint64_t kdata; // Defined on utils.c
extern uint64_t dmap; // Defined on utils.c
extern uint64_t cr3; // Defined on utils.c
extern uint32_t fw; // Defined on utils.c
extern uint64_t vmcb_pa[16]; // Defined on hv_defeat.c
extern struct linux_info linux_i; // Declared on main.c
static inline void kwrite(uint64_t ka, void *src, uint64_t len) {
kernel_copyin(src, ka, len);
}
static inline void kwrite64(uint64_t dst, uint64_t val) {
kernel_copyin(&val, dst, 8);
}
static inline void kwrite32(uint64_t dst, uint32_t val) {
kernel_copyin(&val, dst, 4);
}
static inline void kwrite8(uint64_t dst, uint8_t val) {
kernel_copyin(&val, dst, 1);
}
static inline void kread(uint64_t ka, void *dst, uint64_t len) {
kernel_copyout(ka, dst, len);
}
static inline uint64_t kread64(uint64_t src) {
uint64_t val;
kernel_copyout(src, &val, 8);
return val;
}
static inline uint32_t kread32(uint64_t src) {
uint32_t val;
kernel_copyout(src, &val, 4);
return val;
}
static inline uint8_t kread8(uint64_t src) {
uint8_t val;
kernel_copyout(src, &val, 1);
return val;
}
int set_offsets(void);
int init_global_vars(void);
uint64_t get_offset_va(uint64_t offset);
// Defines for Page management
#define ALIGN_UP(size, align) (((size) + (align) - 1) & ~((align) - 1))
#define INKERNEL(va) (va & 0xFFFF000000000000)
enum page_bits {
P = 0,
RW,
US,
PWT,
PCD,
A,
D,
PS,
G,
XO = 58,
PK = 59,
NX = 63
};
#define PG_B_P (1ULL << P)
#define PG_B_RW (1ULL << RW)
#define PAGE_P(x) (x & (1ULL << P))
#define PAGE_RW(x) (x & (1ULL << RW))
#define PAGE_PS(x) (x & (1ULL << PS))
#define PAGE_XO(x) (x & (1ULL << XO))
#define PAGE_CLEAR_XO(x) (x &= ~(1ULL << XO))
#define PAGE_CLEAR_G(x) (x &= ~(1ULL << G))
#define PAGE_SET_RW(x) (x |= (1ULL << RW))
#define PAGE_PA(x) (x & 0x000FFFFFFFFFF000ULL)
#define P_SIZE(l) ((l == 1) ? (1ULL << 30) : (1ULL << 21))
#define pmap_pml4e_index(va) ((va >> 39) & 0x1FF)
#define pmap_pdpe_index(va) ((va >> 30) & 0x1FF)
#define pmap_pde_index(va) ((va >> 21) & 0x1FF)
#define pmap_pte_index(va) ((va >> 12) & 0x1FF)
uint64_t va_to_pa_user(uint64_t va);
uint64_t va_to_pa_kernel(uint64_t va);
uint64_t va_to_pa_custom(uint64_t va, uint64_t cr3_custom);
uint64_t pa_to_dmap(uint64_t pa);
void page_chain_set_rw(uint64_t va);
uint64_t page_remove_global(uint64_t va);
uint64_t getpmap(uint64_t proc_ptr);
uint64_t get_pml4(uint64_t pmap);
int pin_to_core(int n);
int pin_to_first_available_core(void);
void unpin(void);
static inline void notify(uint8_t *msg) {
struct {
char pad[45];
char msg[3075];
} req;
uint64_t len =
strlen(msg) < (sizeof(req.msg) - 1) ? strlen(msg) : (sizeof(req.msg) - 1);
memcpy(req.msg, msg, len);
sceKernelSendNotificationRequest(0, &req, sizeof(req), 0);
}
#if DEBUG
#define DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...)
#endif
#endif
+32
View File
@@ -0,0 +1,32 @@
ifndef PS5_PAYLOAD_SDK
PS5_PAYLOAD_SDK = /opt/ps5-payload-sdk/
endif
# 1. Variables
CC = gcc
LD = ld
CFLAGS = -O2 -fno-stack-protector -ffreestanding -nostdlib -fcf-protection=none -I$(PS5_PAYLOAD_SDK)/target/include
LDFLAGS = -T linker.ld
TARGET = shellcode_hypervisor.elf
TEXT_BIN = shellcode_hypervisor.bin
dump = shellcode_hypervisor.h
SRC = main.c utils.c boot_linux.c
OBJ = $(SRC:.c=.o)
all: $(dump)
$(TARGET): $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) -o $(TARGET)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(TEXT_BIN): $(TARGET)
objcopy -O binary -j .shell_code $(TARGET) $(TEXT_BIN)
clean:
rm -f $(OBJ) $(TARGET) $(TEXT_BIN) $(dump)
$(dump): $(TEXT_BIN)
python3 bin_to_c_hypervisor.py $(TEXT_BIN)
@@ -0,0 +1,46 @@
import sys
import os
def create_shellcode_header(input_file):
if not os.path.exists(input_file):
print(f"Error: {input_file} not found.")
return
# Read binary data_text
with open(input_file, "rb") as f:
data_text = f.read()
# Hardcoded output name
output_name = "shellcode_hypervisor.h"
array_name = "shellcode_hypervisor"
with open(output_name, "w") as f:
f.write(f"// Generated from {input_file}\n")
f.write(f"#ifndef SHELLCODE_HV_H\n")
f.write(f"#define SHELLCODE_HV_H\n\n")
f.write(f"#include <unistd.h>\n\n")
f.write(f"uint8_t {array_name}[] = {{\n ")
for i, byte in enumerate(data_text):
f.write(f"0x{byte:02X}")
if i < len(data_text) - 1:
f.write(", ")
# New line every 12 bytes
if (i + 1) % 12 == 0:
f.write("\n ")
f.write(f"\n}};\n\n")
f.write(f"uint64_t {array_name}_len = {len(data_text)};\n\n")
f.write(f"#endif // SHELLCODE_HV_H\n")
print(f"Done! Created {output_name} ({len(data_text)} bytes)")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python bin_to_c_hypervisor.py <shellcode.bin>")
else:
create_shellcode_header(sys.argv[1])
+173
View File
@@ -0,0 +1,173 @@
#include "boot_linux.h"
#include "../include/config.h"
#include "linux.h"
#include "utils.h"
#include <stddef.h>
#include <stdint.h>
struct linux_info {
uintptr_t bzimage;
size_t bzimage_size;
uintptr_t initrd;
size_t initrd_size;
size_t vram_size;
char cmdline[2048];
};
static struct linux_info info;
static volatile int exited_cpus = 0;
static void configure_vram(uint64_t fb_start, uint64_t vram_start,
uint64_t vram_size) {
uint64_t vram_end = vram_start + vram_size - 1;
uint64_t fb_top = fb_start + vram_size - 1;
*(uint32_t *)(AMDGPU_MMIO_BASE + RCC_CONFIG_MEMSIZE) = vram_size >> 20;
*(uint32_t *)(AMDGPU_MMIO_BASE + GCMC_VM_FB_OFFSET) = vram_start >> 24;
*(uint32_t *)(AMDGPU_MMIO_BASE + GCMC_VM_LOCAL_HBM_ADDRESS_START) =
vram_start >> 24;
*(uint32_t *)(AMDGPU_MMIO_BASE + GCMC_VM_LOCAL_HBM_ADDRESS_END) =
vram_end >> 24;
*(uint32_t *)(AMDGPU_MMIO_BASE + GCMC_VM_FB_LOCATION_BASE) = fb_start >> 24;
*(uint32_t *)(AMDGPU_MMIO_BASE + GCMC_VM_FB_LOCATION_TOP) = fb_top >> 24;
*(uint32_t *)(AMDGPU_MMIO_BASE + MMMC_VM_FB_OFFSET) = vram_start >> 24;
*(uint32_t *)(AMDGPU_MMIO_BASE + MMMC_VM_LOCAL_HBM_ADDRESS_START) =
vram_start >> 24;
*(uint32_t *)(AMDGPU_MMIO_BASE + MMMC_VM_LOCAL_HBM_ADDRESS_END) =
vram_end >> 24;
*(uint32_t *)(AMDGPU_MMIO_BASE + MMMC_VM_FB_LOCATION_BASE) = fb_start >> 24;
*(uint32_t *)(AMDGPU_MMIO_BASE + MMMC_VM_FB_LOCATION_TOP) = fb_top >> 24;
*(uint32_t *)(AMDGPU_MMIO_BASE + MMHUBBUB_WHITELIST_BASE_ADDR_0) =
vram_start >> 12;
*(uint32_t *)(AMDGPU_MMIO_BASE + MMHUBBUB_WHITELIST_TOP_ADDR_0) =
vram_end >> 12;
*(uint32_t *)(AMDGPU_MMIO_BASE + DCHUBBUB_WHITELIST_BASE_ADDR_0) =
vram_start >> 12;
*(uint32_t *)(AMDGPU_MMIO_BASE + DCHUBBUB_WHITELIST_TOP_ADDR_0) =
vram_end >> 12;
}
static void append_e820_table(struct boot_params *bp, uint64_t start,
uint64_t end, uint32_t type) {
uint8_t idx = bp->e820_entries;
bp->e820_table[idx].addr = start;
bp->e820_table[idx].size = end - start;
bp->e820_table[idx].type = type;
bp->e820_entries++;
}
static void e820_memory_setup(struct boot_params *bp) {
append_e820_table(bp, 0x000000000, 0x000001000, E820_TYPE_RESERVED);
append_e820_table(bp, 0x000001000, 0x000070000, E820_TYPE_RAM);
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, 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
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);
append_e820_table(bp, 0x07fd63000, 0x07fd67000, E820_TYPE_RESERVED);
append_e820_table(bp, 0x07fd67000, 0x07fd6f000, E820_TYPE_NVS);
append_e820_table(bp, 0x07fd6f000, 0x07fd8f000, E820_TYPE_ACPI);
append_e820_table(bp, 0x07fd8f000, 0x07fd90000, E820_TYPE_RESERVED);
append_e820_table(bp, 0x07fd90000, 0x080000000, E820_TYPE_RESERVED);
append_e820_table(bp, 0x080000000, 0x0c4400000, E820_TYPE_RESERVED);
append_e820_table(bp, 0x0d0000000, 0x0e0700000, E820_TYPE_RESERVED);
append_e820_table(bp, 0x0f0000000, 0x0f8000000, E820_TYPE_RESERVED);
append_e820_table(bp, 0x100000000, VRAM_BASE, E820_TYPE_RAM);
append_e820_table(bp, VRAM_BASE, 0x470000000, E820_TYPE_RESERVED); // VRAM
append_e820_table(bp, 0x470000000, 0x47f300000, E820_TYPE_RAM);
append_e820_table(bp, 0x47f300000, 0x480000000, E820_TYPE_RESERVED);
}
void boot_linux(void) {
uintptr_t kernel_pa = 0x100000;
uintptr_t setup_pa = 0x10000;
uintptr_t cmdline_pa = 0x20000;
struct boot_params *bzimage_bp = (struct boot_params *)info.bzimage;
struct boot_params *bp = (struct boot_params *)setup_pa;
struct setup_header *shdr = &bp->hdr;
memset(bp, 0, sizeof(struct boot_params));
memcpy(shdr, &bzimage_bp->hdr, sizeof(struct setup_header));
e820_memory_setup(bp);
shdr->hardware_subarch = X86_SUBARCH_PS5;
shdr->type_of_loader = 0xff;
shdr->cmd_line_ptr = cmdline_pa;
shdr->ramdisk_image = info.initrd & 0xffffffff;
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;
strcpy((char *)cmdline_pa, info.cmdline);
size_t setup_size = (shdr->setup_sects + 1) * 512;
size_t kernel_size = shdr->syssize * 16;
memcpy((void *)kernel_pa, (void *)(info.bzimage + setup_size), kernel_size);
// printf("This is kernel_pa: "); print_val64(kernel_pa); printf("\n");
void (*startup_64)(uint64_t physaddr, struct boot_params *bp) =
(void *)(kernel_pa + 0x200);
startup_64(kernel_pa, bp);
}
void entry(void) {
disable_intr();
// Set global interrupt flag.
__asm__ volatile("stgi\n");
// Clear SVM flag.
wrmsr(MSR_EFER, rdmsr(MSR_EFER) & ~EFER_SVM);
// Disable INIT redirection.
wrmsr(MSR_VM_CR, rdmsr(MSR_VM_CR) & ~VM_CR_R_INIT);
// Clean up mtrr.
wrmsr(MSR_MTRR4kBase + 0, 0);
wrmsr(MSR_MTRR4kBase + 1, 0);
wrmsr(MSR_MTRRVarBase + 7 * 2 + 1, 0);
atomic_add_32(&exited_cpus, 1);
while (atomic_cmpset_32(&exited_cpus, MAXCPU, MAXCPU) == 0)
;
if (get_cpu() != 0) {
while (1) {
halt();
}
}
// Disable IOMMU.
*(volatile uint64_t *)0xfdd80018 &= ~1;
memcpy(&info, (void *)(cave_linux_info), sizeof(struct linux_info));
configure_vram(FB_BASE, VRAM_BASE, info.vram_size);
printf("[*] Booting Linux in bare metal...\n");
boot_linux();
}
+43
View File
@@ -0,0 +1,43 @@
#define MSR_EFER 0xc0000080
#define EFER_SVM (1ULL << 12) // Bit 12: Secure Virtual Machine Enable
// // Virtual Machine Control Register (VM_CR)
#define MSR_VM_CR 0xc0010114
#define VM_CR_R_INIT (1ULL << 1) // Bit 1: Intercept INIT
// // MTRRs (Memory Type Range Registers)
#define MSR_MTRR4kBase 0x00000268 // MSR_MTRRfix4K_C0000 (primer registro 4k)
#define MSR_MTRRVarBase 0x00000200 // MTRR variable base (MSR_MTRRphysBase0)
#define VRAM_BASE (0x470000000 - info.vram_size)
#define FB_BASE 0xf400000000
#define ACPI_RSDP_ADDRESS 0x7fd8e014
#define AMDGPU_MMIO_BASE 0xe0600000
#define RCC_CONFIG_MEMSIZE 0x378c
#define GCMC_VM_FB_OFFSET 0xa5ac
#define GCMC_VM_LOCAL_HBM_ADDRESS_START 0xa5d4
#define GCMC_VM_LOCAL_HBM_ADDRESS_END 0xa5d8
#define GCMC_VM_FB_LOCATION_BASE 0xa600
#define GCMC_VM_FB_LOCATION_TOP 0xa604
#define MMMC_VM_FB_OFFSET 0x6a15c
#define MMMC_VM_LOCAL_HBM_ADDRESS_START 0x6a184
#define MMMC_VM_LOCAL_HBM_ADDRESS_END 0x6a188
#define MMMC_VM_FB_LOCATION_BASE 0x6a1b0
#define MMMC_VM_FB_LOCATION_TOP 0x6a1b4
#define MMHUBBUB_WHITELIST_BASE_ADDR_0 0x24850
#define MMHUBBUB_WHITELIST_TOP_ADDR_0 0x24854
#define DCHUBBUB_WHITELIST_BASE_ADDR_0 0x24878
#define DCHUBBUB_WHITELIST_TOP_ADDR_0 0x2487c
#define MAXCPU 16
void entry(void);
void boot_linux(void);
+18
View File
@@ -0,0 +1,18 @@
/* linker.ld */
ENTRY(main)
SECTIONS
{
. = 0x1000; /* 0x1000 to avoid warnings from linker */
/* Place our custom header first */
.shell_code :
{
*(.entry_point)
*(.text)
*(.text.*)
*(.data*)
*(.rodata*)
*(.bss)
*(.bss.*)
}
}
+114
View File
@@ -0,0 +1,114 @@
/*
* Copyright (C) 2026 Andy Nguyen
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#ifndef __LINUX_H__
#define __LINUX_H__
#include <stdint.h>
#define X86_SUBARCH_PS5 5
enum e820_type {
E820_TYPE_RAM = 1,
E820_TYPE_RESERVED = 2,
E820_TYPE_ACPI = 3,
E820_TYPE_NVS = 4,
E820_TYPE_UNUSABLE = 5,
E820_TYPE_PMEM = 7,
E820_TYPE_PRAM = 12,
E820_TYPE_SOFT_RESERVED = 0xefffffff,
};
struct boot_e820_entry {
uint64_t addr;
uint64_t size;
uint32_t type;
} __attribute__((packed));
struct setup_header {
uint8_t setup_sects;
uint16_t root_flags;
uint32_t syssize;
uint16_t ram_size;
uint16_t vid_mode;
uint16_t root_dev;
uint16_t boot_flag;
uint16_t jump;
uint32_t header;
uint16_t version;
uint32_t realmode_swtch;
uint16_t start_sys_seg;
uint16_t kernel_version;
uint8_t type_of_loader;
uint8_t loadflags;
uint16_t setup_move_size;
uint32_t code32_start;
uint32_t ramdisk_image;
uint32_t ramdisk_size;
uint32_t bootsect_kludge;
uint16_t heap_end_ptr;
uint8_t ext_loader_ver;
uint8_t ext_loader_type;
uint32_t cmd_line_ptr;
uint32_t initrd_addr_max;
uint32_t kernel_alignment;
uint8_t relocatable_kernel;
uint8_t min_alignment;
uint16_t xloadflags;
uint32_t cmdline_size;
uint32_t hardware_subarch;
uint64_t hardware_subarch_data;
uint32_t payload_offset;
uint32_t payload_length;
uint64_t setup_data;
uint64_t pref_address;
uint32_t init_size;
uint32_t handover_offset;
uint32_t kernel_info_offset;
} __attribute__((packed));
#define E820_MAX_ENTRIES_ZEROPAGE 128
struct boot_params {
uint8_t screen_info[0x40]; // 0x000
uint8_t apm_bios_info[0x14]; // 0x040
uint8_t _pad2[4]; // 0x054
uint64_t tboot_addr; // 0x058
uint8_t ist_info[0x10]; // 0x060
uint64_t acpi_rsdp_addr; // 0x070
uint8_t _pad3[8]; // 0x078
uint8_t hd0_info[16]; // 0x080
uint8_t hd1_info[16]; // 0x090
uint8_t sys_desc_table[0x10]; // 0x0a0
uint8_t olpc_ofw_header[0x10]; // 0x0b0
uint32_t ext_ramdisk_image; // 0x0c0
uint32_t ext_ramdisk_size; // 0x0c4
uint32_t ext_cmd_line_ptr; // 0x0c8
uint8_t _pad4[112]; // 0x0cc
uint32_t cc_blob_address; // 0x13c
uint8_t edid_info[0x80]; // 0x140
uint8_t efi_info[0x20]; // 0x1c0
uint32_t alt_mem_k; // 0x1e0
uint32_t scratch; // 0x1e4
uint8_t e820_entries; // 0x1e8
uint8_t eddbuf_entries; // 0x1e9
uint8_t edd_mbr_sig_buf_entries; // 0x1ea
uint8_t kbd_status; // 0x1eb
uint8_t secure_boot; // 0x1ec
uint8_t _pad5[2]; // 0x1ed
uint8_t sentinel; // 0x1ef
uint8_t _pad6[1]; // 0x1f0
struct setup_header hdr; // 0x1f1
uint8_t _pad7[0x290 - 0x1f1 - sizeof(struct setup_header)];
uint32_t edd_mbr_sig_buffer[16]; // 0x290
struct boot_e820_entry e820_table[E820_MAX_ENTRIES_ZEROPAGE]; // 0x2d0
uint8_t _pad8[48]; // 0xcd0
uint8_t eddbuf[0x1ec]; // 0xd00
uint8_t _pad9[276]; // 0xeec
} __attribute__((packed));
#endif
+37
View File
@@ -0,0 +1,37 @@
#include "main.h"
#include "../include/config.h"
#include "boot_linux.h"
#include "utils.h"
#include <cpuid.h>
#include <machine/atomic.h>
#include <machine/cpufunc.h>
#include <stdint.h>
#include <sys/types.h>
__attribute__((section(".entry_point"), naked)) uint32_t main(void) {
// We enter this function after CR3 was updated to 1:1 mapping
// We need to point RSP/RBP to a good known valid address
uint32_t ebax, ebx, ecx, edx;
uint32_t cpu_id;
__asm__ volatile("cpuid"
: "=a"(ebax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "a"(1));
cpu_id = (ebx >> 24) & 0xFF;
// We point to a location after the main linux boot code
// Each CPU should have a different location
uintptr_t new_rsp =
(uintptr_t)hv_base_rsp + ((uint64_t)(cpu_id)*hv_stack_size);
// WARNING: This invalidates current local variables
__asm__ volatile("movq %0, %%rsp \n\t"
"movq %%rsp, %%rbp \n\t"
:
: "r"(new_rsp)
: "rsp", "rbp", "memory");
entry();
}
+3
View File
@@ -0,0 +1,3 @@
#include "shellcode_hypervisor_args.h"
#include <stdarg.h>
#include <stdint.h>

Some files were not shown because too many files have changed in this diff Show More