├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── acpi.c ├── acpi.h ├── acpi_caps.h ├── crc32.c ├── crc32.h ├── elf.h ├── firmware.c ├── firmware.h ├── kernel.c ├── kernel.h ├── kexec.c ├── kexec.h ├── kexec.ld ├── linux_boot.c ├── linux_boot.h ├── linux_thunk.S ├── magic.h ├── reboot.h ├── string.h ├── types.h ├── uart.c ├── uart.h └── x86.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | kexec.elf 4 | kexec.bin 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015-2016 shuffle2 2 | Copyright (C) 2015-2016 Hector Martin "marcan" 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TOOLCHAIN_PREFIX ?= 2 | CC = $(TOOLCHAIN_PREFIX)gcc 3 | AR = $(TOOLCHAIN_PREFIX)ar 4 | OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy 5 | 6 | CFLAGS=$(CFLAG) -DPS4_9_00 -DKASLR -DNO_SYMTAB -DDO_NOT_REMAP_RWX 7 | CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \ 8 | -fPIE -pie -fno-stack-protector -fomit-frame-pointer -nostdlib -nostdinc \ 9 | -fno-asynchronous-unwind-tables \ 10 | -Os -Wall -Werror -Wl,--no-dynamic-linker,--build-id=none,-T,kexec.ld,--nmagic \ 11 | -mcmodel=small -mno-red-zone 12 | 13 | SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c \ 14 | acpi.c crc32.c 15 | 16 | OBJS := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES))) 17 | DEPS := $(OBJS) $(SOURCES) $(INCLUDES:%=$(INC_DIR)/%) Makefile kexec.ld 18 | 19 | all: libkexec.a kexec.bin 20 | 21 | %.o: %.c *.h 22 | $(CC) -c $(CFLAGS) -o $@ $< 23 | 24 | %.o: %.S 25 | $(CC) -c $(CFLAGS) -o $@ $< 26 | 27 | libkexec.a: $(OBJS) 28 | $(AR) -rc $@ $(OBJS) 29 | 30 | kexec.elf: libkexec.a kexec.ld 31 | $(CC) $(CFLAGS) -o $@ libkexec.a 32 | 33 | %.bin: %.elf 34 | $(OBJCOPY) -O binary $< $@ 35 | 36 | clean: 37 | rm -f libkexec.a kexec.elf kexec.bin $(OBJS) 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PS4 kexec implementation 2 | 3 | This repo implements a kexec()-style system call for the PS4 Orbis kernel 4 | (FreeBSD derivative). This is designed to boot a Linux kernel directly from 5 | FreeBSD. 6 | 7 | This is not an exploit. It is useless without some mechanism of injecting code 8 | into the PS4 OS kernel. 9 | 10 | ## Building 11 | 12 | To build a kexec.bin relocatable binary using the supplied Makefile, just type 13 | `make`. This will also build a kexec.a archive. You can either use the binary 14 | directly, or link the archive into your own project. 15 | 16 | If you link kexec.a with your own code, you need to supply the two symbols 17 | `_start` and `_end` in your linker script, as `kernel_init()` will try to remap 18 | all pages covered by that range as RWX (to make global variable accesses work). 19 | Alternatively, you can add `-DDO_NOT_REMAP_RWX` to CFLAGS to disable this 20 | feature, if you have already taken care of page permissions for the code. 21 | 22 | If you use a compiler toolchain that have a special prefix you can declare it 23 | by passing TOOLCHAIN_PREFIX option to the Makefile like this: 24 | 25 | make TOOLCHAIN_PREFIX='amd64-marcel-freebsd9.0-' 26 | 27 | ## Usage 28 | 29 | The code is designed to be completely standalone. There is a single entry point: 30 | 31 | int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr); 32 | 33 | Simply call `kexec_init(NULL, NULL)`. This will locate all the required kernel 34 | symbols and install the sys_kexec system call. The syscall is registered 35 | as number 153 by default (you can change this in kexec.h). The return value 36 | is 0 on success, or negative on error. 37 | 38 | You may pass something other than NULL as `early_printf`. In that case, that 39 | function will be used for debug output during early symbol resolution, before 40 | printf is available. 41 | 42 | Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is 43 | enabled by default, symtab also disappears in newer kernel, we have to 44 | hardcode offsets for some symbols. Currently we use the `early_printf` 45 | given by user to caculate the base address of kernel, then relocate all the 46 | symbols from the kernel base. You could enable this feature like this: 47 | 48 | make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB' 49 | 50 | If you do not want to call the syscall from userspace, you can pass the address 51 | of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the 52 | address of `sys_kexec`, so you can invoke it manually (see kexec.h for 53 | its prototype and how the arguments are passed). 54 | 55 | If you are using the standalone kexec.bin blob, then the `kexec_init` function 56 | is always located at offset 0, so simply call the base address of the blob. 57 | Don't forget to pass two NULL arguments (or the appropriate pointers). 58 | 59 | The injected `sys_kexec` system call takes (userspace) pointers to the kernel 60 | and initramfs blobs, their sizes, and a pointer to the (null-terminated) command 61 | line string. From userspace, this looks like this: 62 | 63 | int kexec(void *kernel_image, size_t image_size, 64 | void *initramfs, size_t initramfs_size, 65 | const char *cmdline); 66 | 67 | // syscall() usage: 68 | syscall(153, kernel_image, image_size, initramfs, initramfs_size, cmdline); 69 | 70 | `kexec()` will load the kernel and initramfs into memory, but will not directly 71 | boot them. To boot the loaded kernel, shut down the system. This can be 72 | accomplished by pressing the power button, but can also be done more quickly 73 | and reliably from userspace with the following sequence of system calls (this 74 | kills userspace quickly but still does a controlled filesystem unmount): 75 | 76 | int evf = syscall(540, "SceSysCoreReboot"); 77 | syscall(546, evf, 0x4000, 0); 78 | syscall(541, evf); 79 | // should be syscall(37, 1, 30) but only tested via kill symbol 80 | kill(1, 30); 81 | 82 | Note that this software should be loaded into kernel memory space. If you are 83 | running kernel code from userland mappings, you should either switch to kernel 84 | mappings or separately copy kexec.bin to a location in kernel address space. 85 | While syscalls or exploit code may run properly from userland, the shutdown hook 86 | will not, as it will be called from a different process context. 87 | 88 | ## Features 89 | 90 | `kernel_init()` will automatically find the Orbis OS kernel and resolve all 91 | necessary symbols to work. There are no static symbol dependencies. If 92 | `DO_NOT_REMAP_RWX` is not defined (the default), it will also patch 93 | `pmap_protect` to disable the W^X restriction. 94 | 95 | In addition to loading the user-supplied initramfs, `kexec` will locate the 96 | Radeon firmware blobs inside Orbis OS, extract them, convert them to a format 97 | suitable for Linux, and append them as an additional initramfs cpio image to 98 | the existing initramfs. This avoids the need to distribute the Radeon firmware 99 | blobs. The `radeon` module, when compiled into the kernel, will automatically 100 | load this firmware on boot. Note however that most typical initramfs scripts 101 | will wipe the initramfs contents while pivoting to the real system, so if you 102 | compile `radeon` as a module you may not be able to access the firmware after 103 | boot. To cover that case, add some code to your initramfs `/init` script to 104 | copy the firmware to a tmpfs mounted on the real filesystem: 105 | 106 | # assuming real root FS is mounted on /mnt 107 | 108 | mkdir -p /mnt/lib/firmware/radeon 109 | mount -t tmpfs none /mnt/lib/firmware/radeon 110 | cp /lib/firmware/radeon/* /mnt/lib/firmware/radeon/ 111 | 112 | # now switch_root to /mnt 113 | 114 | This avoids having to permanently store copies of the Radeon firmware, which 115 | isn't really necessary for most use cases. 116 | 117 | There is significant debug logging available, which will appear on the system 118 | UART. Most of the code relies on the kernel `printf` implementation, and 119 | therefore you should patch out the UART output blanker to see it. The final 120 | code that runs on the boot CPU before booting the kernel uses direct UART 121 | writes and is not affected by the blanking feature of Orbis OS. 122 | -------------------------------------------------------------------------------- /acpi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #include "types.h" 12 | #include "kernel.h" 13 | #include "acpi.h" 14 | #include "acpi_caps.h" 15 | 16 | #ifdef TESTING 17 | # include 18 | # include 19 | # include 20 | # include 21 | # include 22 | # include 23 | #else 24 | # include "kernel.h" 25 | # include "string.h" 26 | # define printf kern.printf 27 | #endif 28 | 29 | #define SIG32(s0, s1, s2, s3) (s0 | (s1 << 8) | (s2 << 16) | (s3 << 24)) 30 | #define PSIG32(s) (u8)s, (u8)(s >> 8), (u8)(s >> 16), (u8)(s >> 24) 31 | 32 | #define PACKED __attribute__((packed)) 33 | 34 | #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) 35 | 36 | struct RSDP { 37 | u64 sig; 38 | u8 checksum; 39 | u8 oemid[6]; 40 | u8 rev; 41 | u32 rsdt_addr; 42 | u32 length; 43 | u64 xsdt_addr; 44 | u8 ext_checksum; 45 | u8 rsvd[3]; 46 | } PACKED; 47 | 48 | struct SDTH { 49 | u32 sig; 50 | u32 length; 51 | u8 rev; 52 | u8 checksum; 53 | u8 oem_id[6]; 54 | u8 oem_tid[8]; 55 | u32 oem_rev; 56 | u8 creator_id[4]; 57 | u32 creator_rev; 58 | } PACKED; 59 | 60 | struct RSDT { 61 | struct SDTH hdr; 62 | u32 table_addr[]; 63 | } PACKED; 64 | 65 | struct XSDT { 66 | struct SDTH hdr; 67 | u64 table_addr[]; 68 | } PACKED; 69 | 70 | struct FADT { 71 | struct SDTH hdr; 72 | u32 facs; 73 | u32 dsdt; 74 | // more stuff... 75 | } PACKED; 76 | 77 | struct ivhd_entry4 { 78 | u8 type; 79 | u16 devid; 80 | u8 flags; 81 | } PACKED; 82 | 83 | struct ivhd_header { 84 | u8 type; 85 | u8 flags; 86 | u16 length; 87 | u16 devid; 88 | u16 cap_ptr; 89 | u64 mmio_phys; 90 | u16 pci_seg; 91 | u16 info; 92 | u32 efr_attr; 93 | } PACKED; 94 | 95 | struct IVRS { 96 | struct SDTH hdr; 97 | u32 IVinfo; 98 | u8 reserved[8]; 99 | struct ivhd_header hd_hdr; 100 | struct ivhd_entry4 hd_entries[3]; 101 | } PACKED; 102 | 103 | struct MMIO { 104 | u64 baseAddressECM; 105 | u16 pciSegmentGroup; 106 | u8 startPCIBus; 107 | u8 endPCIBus; 108 | u32 reserved; 109 | } PACKED; 110 | 111 | // We have enough space to use the second half of the 64KB table area 112 | // as scratch space for building the tables 113 | #define BUFFER_OFF 0x8000 114 | 115 | #define P2M(p) (((u64)(p)) - phys_base + map_base) 116 | #define M2P(p) ((((void*)(p)) - map_base) + phys_base) 117 | #define B2P(p) ((((void*)(p)) - buf_base) + phys_base) 118 | #define P2B(p) ((((void*)(p)) - phys_base) + buf_base) 119 | 120 | #define ALIGN(s) p = (void*)((u64)(p + s - 1) & (-s)) 121 | #define PADB(s) p += (s) 122 | #define ALLOCB(s) ({void *tmp=p; PADB(s); tmp;}) 123 | #define ALLOC(t) (t)ALLOCB(sizeof(t)) 124 | #define COPYB(sz, s) ({void *tmp=p; memcpy(p, s, (sz)); p += (sz); tmp;}) 125 | #define COPYT(s) COPYB(((struct SDTH*)s)->length, s) 126 | #define COPYTP(s) COPYT(P2M(s)) 127 | #define COPY(t, s) ({void *tmp=p; *(t*)p = *(t*)s; p += sizeof(t); (t*)tmp;}) 128 | #define COPYP(t, s) COPY(t, P2M(s)) 129 | 130 | static void rsdp_checksum(struct RSDP *rsdp) { 131 | rsdp->checksum = rsdp->ext_checksum = 0; 132 | 133 | u8 sum = 0; 134 | for (int i = 0; i < 20; i++) 135 | sum += ((u8*)rsdp)[i]; 136 | rsdp->checksum = -sum; 137 | sum = 0; 138 | for (int i = 0; i < sizeof(*rsdp); i++) 139 | sum += ((u8*)rsdp)[i]; 140 | rsdp->ext_checksum = -sum; 141 | } 142 | 143 | static void table_checksum(void *table) { 144 | struct SDTH *hdr = table; 145 | hdr->checksum = 0; 146 | u8 sum = 0; 147 | for (int i = 0; i < hdr->length; i++) 148 | sum += ((u8*)table)[i]; 149 | hdr->checksum = -sum; 150 | } 151 | 152 | #define IVHD_FLAG_ISOC_EN_MASK 0x08 153 | #define IVHD_DEV_ALL 0x01 154 | #define IVHD_DEV_SELECT 0x02 155 | #define IVHD_DEV_SELECT_RANGE_START 0x03 156 | #define IVHD_DEV_RANGE_END 0x04 157 | 158 | #define ACPI_DEVFLAG_SYSMGT1 0x10 159 | #define ACPI_DEVFLAG_SYSMGT2 0x20 160 | 161 | static void *build_ivrs(struct IVRS *ivrs) { 162 | memset(ivrs, 0, sizeof(*ivrs)); 163 | 164 | ivrs->hdr.sig = SIG32('I', 'V', 'R', 'S'); 165 | ivrs->hdr.length = sizeof(*ivrs); 166 | ivrs->hdr.rev = 1; 167 | memcpy(ivrs->hdr.oem_id, "F0F ", 6); 168 | memcpy(ivrs->hdr.oem_tid, "PS4KEXEC", 8); 169 | ivrs->hdr.oem_rev = 0x20161225; 170 | memcpy(ivrs->hdr.creator_id, "KEXC", 4); 171 | ivrs->hdr.creator_rev = 0x20161225; 172 | ivrs->IVinfo = 0x00203040; //48882_IOMMU.pdf page 251 173 | 174 | struct ivhd_header *hdr = &ivrs->hd_hdr; //48882_IOMMU.pdf page 254 175 | hdr->type = 0x10; 176 | hdr->flags = /*coherent | */(1 << 5) | IVHD_FLAG_ISOC_EN_MASK; 177 | hdr->length = sizeof(ivrs->hd_hdr) + sizeof(ivrs->hd_entries); 178 | hdr->devid = PCI_DEVFN(0, 2); 179 | hdr->cap_ptr = 0x40; // from config space + 0x34 180 | hdr->mmio_phys = 0xfc000000; //Base address of IOMMU control registers in MMIO space 181 | hdr->pci_seg = 0; 182 | hdr->info = 0; // msi msg num? (the pci cap should be written by software) 183 | // HATS = 0b10, PNBanks = 2, PNCounters = 4, IASup = 1 184 | hdr->efr_attr = (2 << 30) | (2 << 17) | (4 << 13) | (1 << 5); //Feature Reporting Field, 48882_IOMMU.pdf page 255 185 | 186 | struct ivhd_entry4 *entries = &ivrs->hd_entries[0]; 187 | // on fbsd, all aeolia devfns have active entries except memories (func 6) 188 | // not sure if this is just because it wasn't in use when i dumped it? 189 | // all entries are r/w 190 | // IntCtl = 0b01 and IV = 1 are set for all entries (irqs are forwarded) 191 | // apcie has SysMgt = 0b11 (others are 0b00). (device-initiated dmas are translated) 192 | // Modes: 193 | // 4 level: 194 | // apcie 195 | // 3 level: 196 | // all others 197 | 198 | // the way to encode this info into the IVHD entries is fairly arbitrary... 199 | entries[0].type = IVHD_DEV_SELECT; //DTE setting applies to the device specifed in DevID field. 200 | entries[0].devid = PCI_DEVFN(20, 0); //vendorId: 104D, deviceId: 90D7; Sony Baikal ACPI 201 | entries[0].flags = ACPI_DEVFLAG_SYSMGT1 | ACPI_DEVFLAG_SYSMGT2; 202 | 203 | entries[1].type = IVHD_DEV_SELECT_RANGE_START; 204 | entries[1].devid = PCI_DEVFN(20, 1); 205 | entries[1].flags = 0; //Identifies a device able to assert INIT interrupts (page 262) 206 | entries[2].type = IVHD_DEV_RANGE_END; 207 | entries[2].devid = PCI_DEVFN(20, 7); 208 | entries[2].flags = 0; //Identifies a device able to assert INIT interrupts 209 | 210 | table_checksum(ivrs); 211 | return ivrs + 1; 212 | } 213 | 214 | void fix_acpi_tables(void *map_base, u64 phys_base) 215 | { 216 | void *buf_base = map_base + 0x8000; 217 | void *p = buf_base; 218 | memset(buf_base, 0, 0x8000); 219 | 220 | printf("Fixing ACPI tables at 0x%llx (%p)\n", phys_base, map_base); 221 | 222 | struct RSDP *rsdp = COPYP(struct RSDP, phys_base); 223 | printf("RSDT at 0x%x\n", rsdp->rsdt_addr); 224 | printf("XSDT at 0x%llx\n", rsdp->xsdt_addr); 225 | 226 | struct RSDT *rsdt_src = P2M(rsdp->rsdt_addr); 227 | struct RSDT *rsdt = COPYTP(rsdp->rsdt_addr); 228 | rsdp->rsdt_addr = B2P(rsdt); 229 | 230 | PADB(0x30); // this gives us space for new tables 231 | 232 | struct XSDT *xsdt = COPYTP(rsdp->xsdt_addr); 233 | rsdp->xsdt_addr = B2P(xsdt); 234 | 235 | PADB(0x60); 236 | 237 | struct FADT *fadt = NULL; 238 | 239 | int cnt = (rsdt_src->hdr.length - sizeof(*rsdt)) / 4; 240 | int i; 241 | for (i = 0; i < cnt; i++) { 242 | struct SDTH *hdr = P2M(rsdt_src->table_addr[i]); 243 | printf("%c%c%c%c at 0x%x\n", PSIG32(hdr->sig), rsdt_src->table_addr[i]); 244 | switch (hdr->sig) { 245 | case SIG32('F', 'A', 'C', 'P'): 246 | { 247 | fadt = (void*)hdr; 248 | printf("FACS at 0x%x\n", fadt->facs); 249 | printf("DSDT at 0x%x\n", fadt->dsdt); 250 | // Sony puts the FACS before the FADT, unaligned, which is 251 | // noncompliant, but let's keep it there 252 | u8 *facs = COPYB(64, P2M(fadt->facs)); 253 | fadt = (void*)(hdr = COPYT(hdr)); 254 | fadt->facs = B2P(facs); 255 | PADB(0x38); 256 | break; 257 | } 258 | case SIG32('S', 'S', 'D', 'T'): 259 | { 260 | // Put the DSDT before the SSDT 261 | if (fadt) { 262 | PADB(0xf0); 263 | u8 *dsdt = COPYTP(fadt->dsdt); 264 | fadt->dsdt = B2P(dsdt); 265 | PADB(0x174); 266 | table_checksum(fadt); 267 | } else { 268 | printf("ERROR: no FADT yet?\n"); 269 | } 270 | hdr = COPYT(hdr); 271 | break; 272 | } 273 | default: 274 | hdr = COPYT(hdr); 275 | } 276 | table_checksum(hdr); 277 | xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(hdr); 278 | } 279 | 280 | xsdt->table_addr[i] = rsdt->table_addr[i] = B2P(p); 281 | i++; 282 | p = build_ivrs(p); 283 | 284 | rsdt->hdr.length = sizeof(*rsdt) + 4 * i; 285 | xsdt->hdr.length = sizeof(*xsdt) + 8 * i; 286 | 287 | rsdp_checksum(rsdp); 288 | table_checksum(rsdt); 289 | table_checksum(xsdt); 290 | memcpy(map_base, buf_base, p - buf_base); 291 | } 292 | 293 | u32 msi_mask(unsigned x) { 294 | /* Don't shift by >= width of type */ 295 | if (x >= 5) 296 | return 0xffffffff; 297 | return (1 << (1 << x)) - 1; 298 | } 299 | void disableMSI(u64 MSICapabilityRegAddr) { 300 | PPCI_MSI_CAPABILITY pMSICapability = (PPCI_MSI_CAPABILITY)PA_TO_DM(MSICapabilityRegAddr); 301 | if (pMSICapability->msiEnable == 1) 302 | pMSICapability->msiEnable = 0; 303 | pMSICapability->mask64 = msi_mask(pMSICapability->multipleMessageCapable); 304 | } 305 | 306 | #ifdef TESTING 307 | 308 | int main(int argc, char **argv) 309 | { 310 | int fd; 311 | void *base; 312 | 313 | fd = open(argv[1], O_RDWR); 314 | base = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 315 | 316 | fix_acpi_tables(base, 0xe0000); 317 | return 0; 318 | } 319 | 320 | #endif 321 | -------------------------------------------------------------------------------- /acpi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #ifndef ACPI_H 12 | #define ACPI_H 13 | 14 | #define PACKED __attribute__((packed)) 15 | 16 | void fix_acpi_tables(void *map_base, u64 phys_base); 17 | 18 | #define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) 19 | 20 | void disableMSI(u64 MSICapabilityRegAddr); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /acpi_caps.h: -------------------------------------------------------------------------------- 1 | #ifndef ACPI_CAPS_H 2 | #define ACPI_CAPS_H 3 | 4 | #define UCHAR u8 5 | #define USHORT u16 6 | #define ULONG u32 7 | #define PACKED __attribute__((packed)) 8 | typedef struct PACKED _PCI_CAPABILITIES_HEADER { 9 | UCHAR CapabilityID; 10 | UCHAR Next; 11 | } PCI_CAPABILITIES_HEADER, *PPCI_CAPABILITIES_HEADER; 12 | 13 | typedef struct PACKED _PCI_MSI_CAPABILITY { 14 | PCI_CAPABILITIES_HEADER Header; 15 | u16 msiEnable : 1, multipleMessageCapable : 3, multipleMessageEnable : 3, address64Capable : 1, reserved0 : 8; 16 | u32 lowerAddress : 30, reserved1 : 2; 17 | union { 18 | struct { 19 | u32 upperAddress; 20 | u32 messageData64 : 16, reservedData64 : 16; 21 | u32 mask64; 22 | }; 23 | struct { 24 | u32 messageData32 : 16, reservedData32 : 16; 25 | u32 mask32; 26 | }; 27 | }; 28 | } PCI_MSI_CAPABILITY, *PPCI_MSI_CAPABILITY; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /crc32.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or 3 | * code or tables extracted from it, as desired without restriction. 4 | * 5 | * First, the polynomial itself and its table of feedback terms. The 6 | * polynomial is 7 | * 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 8 | * 9 | * Note that we take it "backwards" and put the highest-order term in 10 | * the lowest-order bit. The X^32 term is "implied"; the LSB is the 11 | * X^31 term, etc. The X^0 term (usually shown as "+1") results in 12 | * the MSB being 1 13 | * 14 | * Note that the usual hardware shift register implementation, which 15 | * is what we're using (we're merely optimizing it by doing eight-bit 16 | * chunks at a time) shifts bits into the lowest-order term. In our 17 | * implementation, that means shifting towards the right. Why do we 18 | * do it this way? Because the calculated CRC must be transmitted in 19 | * order from highest-order term to lowest-order term. UARTs transmit 20 | * characters in order from LSB to MSB. By storing the CRC this way 21 | * we hand it to the UART in the order low-byte to high-byte; the UART 22 | * sends each low-bit to hight-bit; and the result is transmission bit 23 | * by bit from highest- to lowest-order term without requiring any bit 24 | * shuffling on our part. Reception works similarly 25 | * 26 | * The feedback terms table consists of 256, 32-bit entries. Notes 27 | * 28 | * The table can be generated at runtime if desired; code to do so 29 | * is shown later. It might not be obvious, but the feedback 30 | * terms simply represent the results of eight shift/xor opera 31 | * tions for all combinations of data and CRC register values 32 | * 33 | * The values must be right-shifted by eight bits by the "updcrc 34 | * logic; the shift must be unsigned (bring in zeroes). On some 35 | * hardware you could probably optimize the shift in assembler by 36 | * using byte-swap instructions 37 | * polynomial $edb88320 38 | * 39 | * 40 | * CRC32 code derived from work by Gary S. Brown. 41 | */ 42 | 43 | #include "crc32.h" 44 | 45 | static u32 crc32_tab[] = { 46 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 47 | 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 48 | 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 49 | 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 50 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 51 | 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 52 | 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 53 | 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 54 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 55 | 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 56 | 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 57 | 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 58 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 59 | 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 60 | 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 61 | 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 62 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 63 | 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 64 | 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 65 | 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 66 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 67 | 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 68 | 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 69 | 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 70 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 71 | 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 72 | 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 73 | 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 74 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 75 | 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 76 | 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 77 | 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 78 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 79 | 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 80 | 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 81 | 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 82 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 83 | 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 84 | 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 85 | 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 86 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 87 | 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 88 | 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 89 | }; 90 | 91 | u32 crc32(u32 crc, const void *buf, size_t size) 92 | { 93 | const u8 *p; 94 | 95 | p = buf; 96 | crc = crc ^ ~0U; 97 | 98 | while (size--) 99 | crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); 100 | 101 | return crc ^ ~0U; 102 | } 103 | -------------------------------------------------------------------------------- /crc32.h: -------------------------------------------------------------------------------- 1 | #ifndef CRC32_H 2 | #define CRC32_H 3 | #include "types.h" 4 | 5 | u32 crc32(u32 crc, const void *buf, size_t size); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /elf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #ifndef ELF_H 12 | #define ELF_H 13 | 14 | #include "types.h" 15 | 16 | #define EI_NIDENT 16 17 | 18 | typedef struct { 19 | u8 e_ident[EI_NIDENT]; 20 | u16 e_type; 21 | u16 e_machine; 22 | u32 e_version; 23 | u64 e_entry; 24 | u64 e_phoff; 25 | u64 e_shoff; 26 | u32 e_flags; 27 | u16 e_ehsize; 28 | u16 e_phentsize; 29 | u16 e_phnum; 30 | u16 e_shentsize; 31 | u16 e_shnum; 32 | u16 e_shtrndx; 33 | } Elf64_Ehdr; 34 | 35 | typedef struct { 36 | u32 p_type; 37 | u32 p_flags; 38 | u64 p_offset; 39 | void *p_vaddr; 40 | u64 p_paddr; 41 | u64 p_filesz; 42 | u64 p_memsz; 43 | u64 p_align; 44 | } Elf64_Phdr; 45 | 46 | #define PT_DYNAMIC 2 47 | #define PT_PHDR 6 48 | 49 | #define DT_NULL 0 50 | #define DT_STRTAB 5 51 | #define DT_SYMTAB 6 52 | #define DT_STRSZ 10 53 | 54 | typedef struct { 55 | s64 d_tag; 56 | union { 57 | u64 d_val; 58 | void *d_ptr; 59 | } d_un; 60 | } Elf64_Dyn; 61 | 62 | typedef struct { 63 | u32 st_name; 64 | u8 st_info; 65 | u8 st_other; 66 | u16 st_shndx; 67 | void *st_value; 68 | u64 st_size; 69 | } Elf64_Sym; 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /firmware.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #include "firmware.h" 12 | #include "types.h" 13 | #include "kernel.h" 14 | #include "string.h" 15 | #include "types.h" 16 | #include "crc32.h" 17 | 18 | #define DIR 0040755 19 | #define FILE 0100644 20 | 21 | struct firmware_header { 22 | u32 size_bytes; 23 | u32 header_size_bytes; 24 | u16 header_version_major; 25 | u16 header_version_minor; 26 | u16 ip_version_major; 27 | u16 ip_version_minor; 28 | u32 ucode_version; 29 | u32 ucode_size_bytes; 30 | u32 ucode_array_offset_bytes; 31 | u32 crc32; 32 | union { 33 | struct { 34 | u32 ucode_feature_version; 35 | u32 jt_offset; 36 | u32 jt_size; 37 | u8 end[]; 38 | } gfx1; 39 | struct { 40 | u32 ucode_feature_version; 41 | u32 save_and_restore_offset; 42 | u32 clear_state_descriptor_offset; 43 | u32 avail_scratch_ram_locations; 44 | u32 master_pkt_description_offset; 45 | u8 end[]; 46 | } rlc1; 47 | struct { 48 | u32 ucode_feature_version; 49 | u32 ucode_change_version; 50 | u32 jt_offset; 51 | u32 jt_size; 52 | u8 end[]; 53 | } sdma1; 54 | u8 raw[0xe0]; 55 | }; 56 | }; 57 | 58 | static inline char hex(u8 c) 59 | { 60 | if (c <= 9) 61 | return '0' + c; 62 | return 'a' + c - 10; 63 | } 64 | 65 | static void hex8(u8 **p, u32 val) 66 | { 67 | *(*p)++ = hex(val >> 28); 68 | *(*p)++ = hex((val >> 24) & 0xf); 69 | *(*p)++ = hex((val >> 20) & 0xf); 70 | *(*p)++ = hex((val >> 16) & 0xf); 71 | *(*p)++ = hex((val >> 12) & 0xf); 72 | *(*p)++ = hex((val >> 8) & 0xf); 73 | *(*p)++ = hex((val >> 4) & 0xf); 74 | *(*p)++ = hex(val & 0xf); 75 | } 76 | 77 | void cpio_hdr(u8 **p, const char *name, u32 mode, size_t size) 78 | { 79 | size_t name_len = strlen(name); 80 | 81 | // Pad to 4 byte multiple 82 | while (((uintptr_t)*p) & 0x3) 83 | *(*p)++ = 0; 84 | 85 | memcpy(*p, "070701", 6); 86 | *p += 6; 87 | hex8(p, 0); // c_ino 88 | hex8(p, mode); // c_mode 89 | hex8(p, 0); // c_uid 90 | hex8(p, 0); // c_gid 91 | hex8(p, 1); // c_nlink 92 | hex8(p, 0); // c_mtime 93 | hex8(p, size); // c_filesize 94 | hex8(p, 0); // c_maj 95 | hex8(p, 0); // c_min 96 | hex8(p, 0); // c_rmaj 97 | hex8(p, 0); // c_rmin 98 | hex8(p, name_len + 1); // c_namesize 99 | hex8(p, 0); // c_chksum 100 | memcpy(*p, name, name_len); 101 | *p += name_len; 102 | *(*p)++ = 0; 103 | 104 | while (((uintptr_t)*p) & 0x3) 105 | *(*p)++ = 0; 106 | } 107 | 108 | struct fw_header_t { 109 | u64 size_words; 110 | char *unk_ident; 111 | u64 unk; 112 | void *blob; 113 | u64 unk2; 114 | }; 115 | 116 | struct fw_info_t { 117 | struct fw_header_t *rlc; 118 | struct fw_header_t *sdma0; 119 | struct fw_header_t *sdma1; 120 | struct fw_header_t *ce; 121 | struct fw_header_t *pfp; 122 | struct fw_header_t *me; 123 | struct fw_header_t *mec1; 124 | struct fw_header_t *mec2; 125 | }; 126 | 127 | struct fw_expected_sizes_t { 128 | u64 rlc; 129 | u64 sdma0; 130 | u64 sdma1; 131 | u64 ce; 132 | u64 pfp; 133 | u64 me; 134 | u64 mec1; 135 | u64 mec2; 136 | }; 137 | static const struct fw_expected_sizes_t liverpool_fw_sizes = { 138 | LVP_FW_RLC_SIZE, 139 | LVP_FW_SDMA_SIZE, 140 | LVP_FW_SDMA1_SIZE, 141 | LVP_FW_CE_SIZE, 142 | LVP_FW_PFP_SIZE, 143 | LVP_FW_ME_SIZE, 144 | LVP_FW_MEC_SIZE, 145 | LVP_FW_MEC2_SIZE 146 | }; 147 | static const struct fw_expected_sizes_t gladius_fw_sizes = { 148 | GL_FW_RLC_SIZE, 149 | GL_FW_SDMA_SIZE, 150 | GL_FW_SDMA1_SIZE, 151 | GL_FW_CE_SIZE, 152 | GL_FW_PFP_SIZE, 153 | GL_FW_ME_SIZE, 154 | GL_FW_MEC_SIZE, 155 | GL_FW_MEC2_SIZE 156 | }; 157 | 158 | void copy_edid(u8 **p, int sz) 159 | { 160 | int i; 161 | u8 *edid = *p; 162 | u8 *off_edid = kern.edid; 163 | 164 | memset(edid, 0, sz); 165 | *p += sz; 166 | 167 | for(i = 0; i < sz; i++) 168 | *(edid + i) = *(off_edid + i); 169 | 170 | *p += sz; 171 | } 172 | 173 | void copy_eap_hdd_key(u8 **p) 174 | { 175 | int i; 176 | u8 *eap_key = *p; 177 | u8 *off_eap_key = kern.eap_hdd_key; 178 | 179 | memset(eap_key, 0, 0x20); 180 | *p += 0x20; 181 | 182 | for(i = 0; i < 0x20; i++) 183 | { 184 | if(i < 0x10) 185 | *(eap_key + i) = *(off_eap_key + 0xF - i); 186 | else 187 | *(eap_key + i) = *(off_eap_key + 0x2F - i); 188 | } 189 | *p += 0x20; 190 | } 191 | 192 | int copy_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) 193 | { 194 | kern.printf("Copying %s firmware\n", name); 195 | if (expected_size != (hdr->size_words * 4)) { 196 | kern.printf("copy_firmware: %s: expected size %d, got %d\n", 197 | name, expected_size, hdr->size_words * 4); 198 | return 0; 199 | } 200 | 201 | struct firmware_header *fhdr = (struct firmware_header*)*p; 202 | memset(fhdr, 0, sizeof(*fhdr)); 203 | *p += sizeof(*fhdr); 204 | 205 | memcpy(*p, hdr->blob, expected_size); 206 | 207 | fhdr->size_bytes = expected_size + sizeof(*fhdr); 208 | fhdr->header_size_bytes = offsetof(struct firmware_header, raw); 209 | fhdr->header_version_major = 1; 210 | fhdr->header_version_minor = 0; 211 | fhdr->ucode_version = 0x10; 212 | fhdr->ucode_size_bytes = expected_size; 213 | fhdr->ucode_array_offset_bytes = sizeof(*fhdr); 214 | 215 | *p += expected_size; 216 | 217 | return 1; 218 | } 219 | 220 | int copy_gfx_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) 221 | { 222 | struct firmware_header *fhdr = (struct firmware_header*)*p; 223 | if (!copy_firmware(p, name, hdr, expected_size)) 224 | return 0; 225 | 226 | fhdr->ip_version_major = 7; 227 | fhdr->ip_version_minor = 2; 228 | fhdr->header_size_bytes = offsetof(struct firmware_header, gfx1.end); 229 | fhdr->gfx1.ucode_feature_version = 21; 230 | fhdr->gfx1.jt_offset = (expected_size & ~0xfff) >> 2; 231 | fhdr->gfx1.jt_size = (expected_size & 0xfff) >> 2; 232 | 233 | fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); 234 | return 1; 235 | } 236 | 237 | int copy_rlc_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size) 238 | { 239 | struct firmware_header *fhdr = (struct firmware_header*)*p; 240 | if (!copy_firmware(p, name, hdr, expected_size)) 241 | return 0; 242 | 243 | fhdr->ip_version_major = 7; 244 | fhdr->ip_version_minor = 2; 245 | fhdr->header_size_bytes = offsetof(struct firmware_header, rlc1.end); 246 | fhdr->rlc1.ucode_feature_version = 1; 247 | fhdr->rlc1.save_and_restore_offset = 0x90; 248 | fhdr->rlc1.clear_state_descriptor_offset = 0x3d; 249 | fhdr->rlc1.avail_scratch_ram_locations = 0x270; // 0x170 for bonaire, 0x270 for kabini?? 250 | fhdr->rlc1.master_pkt_description_offset = 0; 251 | 252 | fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); 253 | return 1; 254 | } 255 | 256 | int copy_sdma_firmware(u8 **p, const char *name, struct fw_header_t *hdr, size_t expected_size, int idx) 257 | { 258 | struct firmware_header *fhdr = (struct firmware_header*)*p; 259 | if (!copy_firmware(p, name, hdr, expected_size)) 260 | return 0; 261 | 262 | fhdr->ip_version_major = 2; 263 | fhdr->ip_version_minor = 1; 264 | fhdr->header_size_bytes = offsetof(struct firmware_header, sdma1.end); 265 | fhdr->sdma1.ucode_feature_version = idx == 0 ? 9 : 0; 266 | fhdr->sdma1.ucode_change_version = 0; 267 | fhdr->sdma1.jt_offset = (expected_size & ~0xfff) >> 2; 268 | fhdr->sdma1.jt_size = (expected_size & 0xfff) >> 2; 269 | 270 | fhdr->crc32 = crc32(0, fhdr->raw, sizeof(fhdr->raw) + expected_size); 271 | return 1; 272 | } 273 | 274 | static const u32 pfp_nop_handler[] = { 275 | 0xdc120000, // mov r4, ctr 276 | 0x31144000, // seteq r5, r4, #0x4000 277 | 0x95400009, // cbz r5, l0 278 | 0xc4200016, // ldw r8, [r0, #0x16] 279 | 0xdc030000, // mov ctr, r0 280 | 0xcc000049, // stw r0, [r0, #0x49] 281 | 0xcc200013, // stw r0, [r8, #0x13] 282 | 0xc424007e, // ldw r9, [r0, #0x7e] 283 | 0x96400000, // l1: cbz r9, l1 284 | 0x7c408001, // mov r2, r1 285 | 0x88000000, // btab 286 | 0xd440007f, // l0: stm r1, [r0, #0x7f] 287 | 0x7c408001, // mov r2, r1 288 | 0x88000000, // btab 289 | }; 290 | 291 | static const u32 ce_nop_handler[] = { 292 | 0xdc120000, // mov r4, ctr 293 | 0x31144000, // seteq r5, r4, #0x4000 294 | 0x95400009, // cbz r5, l0 295 | 0xc420000c, // ldw r8, [r0, #0xc] 296 | 0xdc030000, // mov ctr, r0 297 | 0xcc00002f, // stw r0, [r0, #0x2f] 298 | 0xcc200012, // stw r0, [r8, #0x12] 299 | 0xc424007e, // ldw r9, [r0, #0x7e] 300 | 0x96400000, // l1: cbz r9, l1 301 | 0x7c408001, // mov r2, r1 302 | 0x88000000, // btab 303 | 0xd440007f, // l0: stm r1, [r0, #0x7f] 304 | 0x7c408001, // mov r2, r1 305 | 0x88000000, // btab 306 | }; 307 | 308 | static const u32 mec_nop_handler[] = { 309 | 0xdc120000, // mov r4, ctr 310 | 0x31144000, // seteq r5, r4, #0x4000 311 | 0x95400009, // cbz r5, l0 312 | 0xc43c000c, // ldw r15, [r0, #0x9] 313 | 0xdc030000, // mov ctr, r0 314 | 0xcc00002b, // stw r0, [r0, #0x2b] 315 | 0xcc3c000d, // stw r0, [r15, #0xd] 316 | 0xc424007e, // ldw r9, [r0, #0x7e] 317 | 0x96400000, // l1: cbz r9, l1 318 | 0x7c408001, // mov r2, r1 319 | 0x88000000, // btab 320 | 0xd440007f, // l0: stm r1, [r0, #0x7f] 321 | 0x7c408001, // mov r2, r1 322 | 0x88000000, // btab 323 | }; 324 | 325 | #define PACKET_TYPE_NOP 0x10 326 | 327 | static void patch_fw(void *p, const u32 *handler, int handler_size) { 328 | int size = ((struct firmware_header*)p)->ucode_size_bytes; 329 | int code_size = (size & ~0xfff) / 4; 330 | int nop_start = code_size - 0x10; 331 | 332 | u32 *fw = p + sizeof(struct firmware_header); 333 | kern.printf("NOP handler at 0x%x\n", nop_start); 334 | memcpy(&fw[nop_start], handler, handler_size); 335 | 336 | // patch the branch table entry 337 | for (int off = code_size; off < size/4; off++) { 338 | if ((fw[off] >> 16) == PACKET_TYPE_NOP) { 339 | fw[off] = (PACKET_TYPE_NOP << 16) | nop_start; 340 | } 341 | } 342 | } 343 | 344 | struct fw_info_t *get_fw_info() { 345 | if (kern.gc_get_fw_info) { 346 | return kern.gc_get_fw_info(); 347 | } else if (kern.Starsha_UcodeInfo) { 348 | return kern.Starsha_UcodeInfo; 349 | } else { 350 | return NULL; 351 | } 352 | } 353 | 354 | const struct fw_expected_sizes_t *get_fw_expected_sizes() { 355 | if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { 356 | return &gladius_fw_sizes; 357 | } else { 358 | return &liverpool_fw_sizes; 359 | } 360 | } 361 | 362 | const char * get_gpu_name() { 363 | if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) { 364 | return "gladius"; 365 | } else { 366 | return "liverpool"; 367 | } 368 | } 369 | 370 | ssize_t firmware_extract(void *dest) 371 | { 372 | u8 *p = dest; 373 | 374 | // Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever. 375 | struct fw_info_t *info = get_fw_info(); 376 | if (!info) { 377 | kern.printf("firmware_extract: Could not locate firmware table"); 378 | return -1; 379 | } 380 | const struct fw_expected_sizes_t *fw_sizes = get_fw_expected_sizes(); 381 | 382 | //Eap hdd key 383 | cpio_hdr(&p, "key", DIR, 0); 384 | cpio_hdr(&p, "key/eap_hdd_key.bin", FILE, 0x20); 385 | copy_eap_hdd_key(&p); 386 | 387 | cpio_hdr(&p, "lib", DIR, 0); 388 | cpio_hdr(&p, "lib/firmware", DIR, 0); 389 | 390 | /** We need detect the size of edid first, on some monitor it is 128 on other 256 bytes, so for now remove it **/ 391 | int edid_sz = (((unsigned char*)kern.edid)[126]?256:128); 392 | cpio_hdr(&p, "lib/firmware/edid", DIR, 0); 393 | cpio_hdr(&p, "lib/firmware/edid/my_edid.bin", FILE, edid_sz); 394 | copy_edid(&p, edid_sz); 395 | 396 | char dir[7]; 397 | if (kern.gpu_devid_is_9924 && kern.gpu_devid_is_9924()) 398 | kern.snprintf(dir, sizeof(dir), "amdgpu"); 399 | else 400 | kern.snprintf(dir, sizeof(dir), "amdgpu"); 401 | 402 | char dir_path[64]; 403 | kern.snprintf(dir_path, sizeof(dir_path), "lib/firmware/%s/", dir); 404 | cpio_hdr(&p, dir_path, DIR, 0); 405 | 406 | char pfp_path[64]; 407 | kern.snprintf(pfp_path, sizeof(pfp_path), "%s%s_pfp.bin", dir_path, get_gpu_name()); 408 | kern.printf("firmware_extract: Extract %s \n", pfp_path); 409 | cpio_hdr(&p, pfp_path, FILE, FW_HEADER_SIZE + fw_sizes->pfp); 410 | u8 *pfp = p; 411 | if (!copy_gfx_firmware(&p, "PFP", info->pfp, fw_sizes->pfp)) 412 | return -1; 413 | patch_fw(pfp, pfp_nop_handler, sizeof(pfp_nop_handler)); 414 | 415 | char me_path[64]; 416 | kern.snprintf(me_path, sizeof(me_path), "%s%s_me.bin", dir_path, get_gpu_name()); 417 | kern.printf("firmware_extract: Extract %s \n", me_path); 418 | cpio_hdr(&p, me_path, FILE, FW_HEADER_SIZE + fw_sizes->me); 419 | if (!copy_gfx_firmware(&p, "ME", info->me, fw_sizes->me)) 420 | return -1; 421 | 422 | char ce_path[64]; 423 | kern.snprintf(ce_path, sizeof(ce_path), "%s%s_ce.bin", dir_path, get_gpu_name()); 424 | kern.printf("firmware_extract: Extract %s \n", ce_path); 425 | cpio_hdr(&p, ce_path, FILE, FW_HEADER_SIZE + fw_sizes->ce); 426 | u8 *ce = p; 427 | if (!copy_gfx_firmware(&p, "CE", info->ce, fw_sizes->ce)) 428 | return -1; 429 | patch_fw(ce, ce_nop_handler, sizeof(ce_nop_handler)); 430 | 431 | char mec_path[64]; 432 | kern.snprintf(mec_path, sizeof(mec_path), "%s%s_mec.bin", dir_path, get_gpu_name()); 433 | kern.printf("firmware_extract: Extract %s \n", mec_path); 434 | cpio_hdr(&p, mec_path, FILE, FW_HEADER_SIZE + fw_sizes->mec1); 435 | u8 *mec1 = p; 436 | if (!copy_gfx_firmware(&p, "MEC", info->mec1, fw_sizes->mec1)) 437 | return -1; 438 | patch_fw(mec1, mec_nop_handler, sizeof(mec_nop_handler)); 439 | 440 | char mec2_path[64]; 441 | kern.snprintf(mec2_path, sizeof(mec2_path), "%s%s_mec2.bin", dir_path, get_gpu_name()); 442 | kern.printf("firmware_extract: Extract %s \n", mec2_path); 443 | cpio_hdr(&p, mec2_path, FILE, FW_HEADER_SIZE + fw_sizes->mec2); 444 | u8 *mec2 = p; 445 | if (!copy_gfx_firmware(&p, "MEC2", info->mec2, fw_sizes->mec2)) 446 | return -1; 447 | patch_fw(mec2, mec_nop_handler, sizeof(mec_nop_handler)); 448 | 449 | char rlc_path[64]; 450 | kern.snprintf(rlc_path, sizeof(rlc_path), "%s%s_rlc.bin", dir_path, get_gpu_name()); 451 | kern.printf("firmware_extract: Extract %s \n", rlc_path); 452 | cpio_hdr(&p, rlc_path, FILE, FW_HEADER_SIZE + fw_sizes->rlc); 453 | if (!copy_rlc_firmware(&p, "RLC", info->rlc, fw_sizes->rlc)) 454 | return -1; 455 | 456 | char sdma_path[64]; 457 | kern.snprintf(sdma_path, sizeof(sdma_path), "%s%s_sdma.bin", dir_path, get_gpu_name()); 458 | kern.printf("firmware_extract: Extract %s \n", sdma_path); 459 | cpio_hdr(&p, sdma_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma0); 460 | if (!copy_sdma_firmware(&p, "SDMA", info->sdma0, fw_sizes->sdma0, 0)) 461 | return -1; 462 | cpio_hdr(&p, "TRAILER!!!", FILE, 0); 463 | 464 | char sdma1_path[64]; 465 | kern.snprintf(sdma1_path, sizeof(sdma1_path), "%s%s_sdma1.bin", dir_path, get_gpu_name()); 466 | kern.printf("firmware_extract: Extract %s \n", sdma1_path); 467 | cpio_hdr(&p, sdma1_path, FILE, FW_HEADER_SIZE + fw_sizes->sdma1); 468 | if (!copy_sdma_firmware(&p, "SDMA1", info->sdma1, fw_sizes->sdma1, 1)) 469 | return -1; 470 | cpio_hdr(&p, "TRAILER!!!", FILE, 0); 471 | 472 | size_t size = p - (u8*)dest; 473 | if (size > FW_CPIO_SIZE) { 474 | kern.printf("firmware_extract: overflow! %d > %d\n", size, FW_CPIO_SIZE); 475 | return -1; 476 | } 477 | 478 | return size; 479 | } 480 | -------------------------------------------------------------------------------- /firmware.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #ifndef FIRMWARE_H 12 | #define FIRMWARE_H 13 | 14 | #include "types.h" 15 | 16 | //sizes eap_hdd_key 17 | #define EAP_HDD_KEY_SIZE 0x20 18 | #define EDID_SIZE 256 19 | 20 | // sizes for liverpool 21 | #define LVP_FW_CE_SIZE 8576 22 | #define LVP_FW_ME_SIZE 16768 23 | #define LVP_FW_MEC_SIZE 16768 24 | #define LVP_FW_MEC2_SIZE 16768 25 | #define LVP_FW_PFP_SIZE 16768 26 | #define LVP_FW_RLC_SIZE 6144 27 | #define LVP_FW_SDMA_SIZE 4200 28 | #define LVP_FW_SDMA1_SIZE 4200 29 | // sizes for gladius 30 | #define GL_FW_CE_SIZE 8576 31 | #define GL_FW_ME_SIZE 16768 32 | #define GL_FW_MEC_SIZE 16768 33 | #define GL_FW_MEC2_SIZE 16768 34 | #define GL_FW_PFP_SIZE 16768 35 | #define GL_FW_RLC_SIZE 8192 36 | #define GL_FW_SDMA_SIZE 4200 37 | #define GL_FW_SDMA1_SIZE 4200 38 | 39 | #define MAX(x ,y) (((x) > (y)) ? (x) : (y)) 40 | #define MAX_FW_SIZE(engine) MAX(LVP_FW_ ## engine ## _SIZE, GL_FW_ ## engine ## _SIZE) 41 | 42 | #define FW_CE_SIZE MAX_FW_SIZE(CE) 43 | #define FW_ME_SIZE MAX_FW_SIZE(ME) 44 | #define FW_MEC_SIZE MAX_FW_SIZE(MEC) 45 | #define FW_MEC2_SIZE MAX_FW_SIZE(MEC2) 46 | #define FW_PFP_SIZE MAX_FW_SIZE(PFP) 47 | #define FW_RLC_SIZE MAX_FW_SIZE(RLC) 48 | #define FW_SDMA_SIZE MAX_FW_SIZE(SDMA) 49 | #define FW_SDMA1_SIZE MAX_FW_SIZE(SDMA1) 50 | 51 | // Conservative value (max 113 bytes plus name size plus alignment) 52 | #define CPIO_HEADER_SIZE 256 53 | 54 | #define FW_HEADER_SIZE 256 55 | 56 | // Leave space for 16 files (currently 12) 57 | #define FW_CPIO_SIZE (EAP_HDD_KEY_SIZE /* + EDID_SIZE */ + (CPIO_HEADER_SIZE * 16) + FW_CE_SIZE + FW_ME_SIZE + \ 58 | FW_MEC_SIZE + FW_MEC2_SIZE + FW_PFP_SIZE + \ 59 | FW_RLC_SIZE + FW_SDMA_SIZE + FW_SDMA1_SIZE + \ 60 | FW_HEADER_SIZE * 8) 61 | 62 | ssize_t firmware_extract(void *dest); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /kernel.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #include "kernel.h" 12 | #include "string.h" 13 | #include "elf.h" 14 | #include "x86.h" 15 | #include "magic.h" 16 | 17 | struct ksym_t kern; 18 | int (*early_printf)(const char *fmt, ...) = NULL; 19 | 20 | #define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0) 21 | 22 | #ifdef NO_SYMTAB 23 | 24 | #define RESOLVE_NOERR(name) do { \ 25 | if (kern_off_ ## name == 0) { \ 26 | kern.name = 0; \ 27 | } else { \ 28 | kern.name = (void *)(kern.kern_base + kern_off_ ## name); \ 29 | } \ 30 | } while (0); 31 | 32 | #define RESOLVE(name) do { \ 33 | if (kern_off_ ## name == 0) { \ 34 | return 0; \ 35 | } \ 36 | RESOLVE_NOERR(name) \ 37 | } while (0); 38 | 39 | #else 40 | 41 | #define KERNSIZE 0x2000000 42 | 43 | static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00"; 44 | static Elf64_Sym *symtab; 45 | static char *strtab; 46 | static size_t strtab_size; 47 | 48 | static Elf64_Ehdr *find_kern_ehdr(void) 49 | { 50 | // Search for the kernel copy embedded in ubios, then follow it to see 51 | // where it was relocated to 52 | for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) { 53 | Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p; 54 | if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) { 55 | for (size_t i = 0; i < ehdr->e_phnum; i++) { 56 | Elf64_Phdr *phdr = (Elf64_Phdr *)(p + ehdr->e_phoff) + i; 57 | if (phdr->p_type == PT_PHDR) { 58 | return (Elf64_Ehdr *)(phdr->p_vaddr - ehdr->e_phoff); 59 | } 60 | } 61 | } 62 | } 63 | return NULL; 64 | } 65 | 66 | static Elf64_Dyn *elf_get_dyn(Elf64_Ehdr *ehdr) 67 | { 68 | Elf64_Phdr *phdr = (Elf64_Phdr *)((uintptr_t)ehdr + ehdr->e_phoff); 69 | for (size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { 70 | if (phdr->p_type == PT_DYNAMIC) { 71 | return (Elf64_Dyn *)phdr->p_vaddr; 72 | } 73 | } 74 | return NULL; 75 | } 76 | 77 | static int elf_parse_dyn(Elf64_Dyn *dyn) 78 | { 79 | for (Elf64_Dyn *dp = dyn; dp->d_tag != DT_NULL; dp++) { 80 | switch (dp->d_tag) { 81 | case DT_SYMTAB: 82 | symtab = (Elf64_Sym *)dp->d_un.d_ptr; 83 | break; 84 | case DT_STRTAB: 85 | strtab = (char *)dp->d_un.d_ptr; 86 | break; 87 | case DT_STRSZ: 88 | strtab_size = dp->d_un.d_val; 89 | break; 90 | } 91 | } 92 | return symtab && strtab && strtab_size; 93 | } 94 | 95 | void *kernel_resolve(const char *name) 96 | { 97 | for (Elf64_Sym *sym = symtab; (uintptr_t)(sym + 1) < (uintptr_t)strtab; sym++) { 98 | if (!strcmp(name, &strtab[sym->st_name])) { 99 | eprintf("kern.%s = %p\n", name, (void*)sym->st_value); 100 | return (void *)sym->st_value; 101 | } 102 | } 103 | eprintf("Failed to resolve symbol '%s'\n", name); 104 | return NULL; 105 | } 106 | 107 | #define RESOLVE_NOERR(name) (kern.name = kernel_resolve(#name)) 108 | #define RESOLVE(name) if (!RESOLVE_NOERR(name)) return 0; 109 | 110 | #endif 111 | 112 | static int resolve_symbols(void) 113 | { 114 | RESOLVE(printf); 115 | RESOLVE(snprintf); 116 | early_printf = kern.printf; 117 | RESOLVE(copyin); 118 | RESOLVE(copyout); 119 | RESOLVE(copyinstr); 120 | RESOLVE(kernel_map); 121 | RESOLVE(kernel_pmap_store); 122 | RESOLVE(kmem_alloc_contig); 123 | RESOLVE(kmem_free); 124 | RESOLVE(pmap_extract); 125 | RESOLVE(pmap_protect); 126 | RESOLVE(sysent); 127 | RESOLVE(sched_pin); 128 | RESOLVE(sched_unpin); 129 | RESOLVE(smp_rendezvous); 130 | RESOLVE(smp_no_rendevous_barrier); 131 | RESOLVE(icc_query_nowait); 132 | RESOLVE_NOERR(Starsha_UcodeInfo); 133 | RESOLVE_NOERR(gpu_devid_is_9924); 134 | RESOLVE_NOERR(gc_get_fw_info); 135 | RESOLVE_NOERR(eap_hdd_key); 136 | RESOLVE_NOERR(edid); 137 | RESOLVE(wlanbt); 138 | RESOLVE(kern_reboot); 139 | RESOLVE(set_gpu_freq); 140 | RESOLVE(set_pstate); 141 | RESOLVE(update_vddnp); 142 | RESOLVE(set_cu_power_gate); 143 | return 1; 144 | } 145 | 146 | #define M_WAITOK 0x0002 147 | #define M_ZERO 0x0100 148 | 149 | #define VM_MEMATTR_DEFAULT 0x06 150 | 151 | void *kernel_alloc_contig(size_t size) 152 | { 153 | // use kmem_alloc_contig instead of contigalloc to avoid messing with a malloc_type... 154 | vm_offset_t ret = 0; 155 | while(!(ret = kern.kmem_alloc_contig( 156 | *kern.kernel_map, size, M_ZERO | M_WAITOK, (vm_paddr_t)0, 157 | ~(vm_paddr_t)0, 1, 0, VM_MEMATTR_DEFAULT))); 158 | 159 | /*if (!ret) { 160 | kern.printf("Failed to allocate %zud bytes\n", size); 161 | return NULL; 162 | }*/ 163 | return (void *)PA_TO_DM(kern.pmap_extract(kern.kernel_pmap_store, ret)); 164 | } 165 | 166 | void kernel_free_contig(void *addr, size_t size) 167 | { 168 | if (!addr) 169 | return; 170 | kern.kmem_free(*kern.kernel_map, (vm_offset_t)addr, size); 171 | } 172 | 173 | int kernel_hook_install(void *target, void *hook) 174 | { 175 | uintptr_t t = (uintptr_t)target; // addr to redirect to 176 | uintptr_t h = (uintptr_t)hook; // place to write the thunk 177 | 178 | if (!hook || !target) { 179 | return 0; 180 | } 181 | 182 | kern.printf("kernel_hook_install(%p, %p)\n", target, hook); 183 | 184 | if (!(t & (1L << 63))) { 185 | kern.printf("\n===================== WARNING =====================\n"); 186 | kern.printf("hook target function address: %p\n", target); 187 | kern.printf("It looks like we're running from userland memory.\n"); 188 | kern.printf("Please run this code from a kernel memory mapping.\n\n"); 189 | return 0; 190 | } 191 | s64 displacement = t - (h + 5); 192 | 193 | kern.sched_pin(); 194 | u64 wp = write_protect_disable(); 195 | if (displacement < -0x80000000 || displacement > 0x7fffffff) { 196 | kern.printf(" Using 64bit absolute jump\n"); 197 | struct __attribute__((packed)) jmp_t{ 198 | u8 op[2]; 199 | s32 zero; 200 | void *target; 201 | } jmp = { 202 | .op = { 0xff, 0x25 }, 203 | .zero = 0, 204 | .target = target, 205 | }; 206 | ASSERT_STRSIZE(struct jmp_t, 14); 207 | memcpy(hook, &jmp, sizeof(jmp)); 208 | } else { 209 | kern.printf(" Using 32bit relative jump\n"); 210 | struct __attribute__((packed)) jmp_t{ 211 | u8 op[1]; 212 | s32 imm; 213 | } jmp = { 214 | .op = { 0xe9 }, 215 | .imm = displacement, 216 | }; 217 | ASSERT_STRSIZE(struct jmp_t, 5); 218 | memcpy(hook, &jmp, sizeof(jmp)); 219 | } 220 | wbinvd(); 221 | write_protect_restore(wp); 222 | kern.sched_unpin(); 223 | 224 | return 1; 225 | } 226 | 227 | void kernel_syscall_install(int num, void *call, int narg) 228 | { 229 | struct sysent_t *sy = &kern.sysent[num]; 230 | 231 | kern.sched_pin(); 232 | u64 wp = write_protect_disable(); 233 | 234 | memset(sy, 0, sizeof(*sy)); 235 | sy->sy_narg = narg; 236 | sy->sy_call = call; 237 | sy->sy_thrcnt = 1; 238 | 239 | write_protect_restore(wp); 240 | kern.sched_unpin(); 241 | } 242 | 243 | void kernel_remap(void *start, void *end, int perm) 244 | { 245 | u64 s = ((u64)start) & ~(u64)(PAGE_SIZE-1); 246 | u64 e = ((u64)end + PAGE_SIZE - 1) & ~(u64)(PAGE_SIZE-1); 247 | 248 | kern.printf("pmap_protect(pmap, %p, %p, %d)\n", (void*)s, (void*)e, perm); 249 | kern.pmap_protect(kern.kernel_pmap_store, s, e, perm); 250 | } 251 | 252 | static volatile int _global_test = 0; 253 | 254 | #ifndef DO_NOT_REMAP_RWX 255 | extern u8 _start[], _end[]; 256 | 257 | static int patch_pmap_check(void) 258 | { 259 | u8 *p; 260 | 261 | for (p = (u8*)kern.pmap_protect; 262 | p < ((u8*)kern.pmap_protect + 0x500); p++) { 263 | #ifdef PS4_6_72 264 | if (!memcmp(p, "\xF8\xF7\xD0\x83\xE0\x06", 6)) { // bytes were slightly different on 6.72 265 | p[5] = 0; 266 | kern.printf("pmap_protect patch successful (found at %p)\n", p); 267 | return 1; 268 | } 269 | /*#ifdef PS4_5_05 270 | if (!memcmp(p, "\xB8\x06\x00\x00\x00\xC4", 6)) { 271 | p[1] = 0; 272 | kern.printf("pmap_protect patch successful (found at %p)\n", p); 273 | return 1; 274 | }*/ 275 | #else 276 | if (!memcmp(p, "x83\xe0\x06\x83\xf8\x06", 6)) { 277 | p[2] = 0; 278 | kern.printf("pmap_protect patch successful (found at %p)\n", p); 279 | return 1; 280 | } 281 | #endif 282 | } 283 | kern.printf("pmap_protect patch failed!\n"); 284 | return 0; 285 | } 286 | #endif 287 | 288 | int kernel_init(void *_early_printf) 289 | { 290 | int rv = -1; 291 | 292 | if (_early_printf) 293 | early_printf = _early_printf; 294 | 295 | eprintf("kernel_init()\n"); 296 | 297 | #ifdef KASLR 298 | // use `early_printf` to calculate kernel base 299 | if (early_printf == NULL) 300 | return 0; 301 | 302 | kern.kern_base = (u64)(early_printf - kern_off_printf); 303 | if ((kern.kern_base & PAGE_MASK) != 0) { 304 | eprintf("Kernel base is not aligned\n"); 305 | return 0; 306 | } else { 307 | eprintf("Kernel base = %llx\n", kern.kern_base); 308 | } 309 | 310 | u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i); 311 | u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi); 312 | 313 | #else 314 | kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000 315 | 316 | u64 DMPML4I = 0x1fc; 317 | u64 DMPDPI = 0; 318 | #endif 319 | 320 | kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0); 321 | eprintf("Direct map base = %llx\n", kern.dmap_base); 322 | 323 | // We may not be mapped writable yet, so to be able to write to globals 324 | // we need WP disabled. 325 | u64 flags = intr_disable(); 326 | u64 wp = write_protect_disable(); 327 | 328 | #ifndef NO_SYMTAB 329 | Elf64_Ehdr *ehdr = find_kern_ehdr(); 330 | if (!ehdr) { 331 | eprintf("Could not find kernel ELF header\n"); 332 | goto err; 333 | } 334 | eprintf("ELF header at %p\n", ehdr); 335 | 336 | Elf64_Dyn *dyn = elf_get_dyn(ehdr); 337 | if (!dyn) { 338 | eprintf("Could not find kernel dynamic header\n"); 339 | goto err; 340 | } 341 | eprintf("ELF dynamic section at %p\n", dyn); 342 | 343 | if (!elf_parse_dyn(dyn)) { 344 | eprintf("Failed to parse ELF dynamic section\n"); 345 | goto err; 346 | } 347 | #endif 348 | 349 | if (!resolve_symbols()) { 350 | eprintf("Failed to resolve all symbols\n"); 351 | goto err; 352 | } 353 | 354 | // Pin ourselves as soon as possible. This is expected to be released by the caller. 355 | kern.sched_pin(); 356 | 357 | #ifndef DO_NOT_REMAP_RWX 358 | if (!patch_pmap_check()) 359 | goto err; 360 | #endif 361 | 362 | #ifndef DO_NOT_REMAP_RWX 363 | // kernel_remap may need interrupts, but may not write to globals! 364 | enable_interrupts(); 365 | kernel_remap(_start, _end, 7); 366 | disable_interrupts(); 367 | #endif 368 | 369 | // Writing to globals is now safe. 370 | 371 | kern.printf("Testing global variable access (write protection)...\n"); 372 | _global_test = 1; 373 | kern.printf("OK.\n"); 374 | 375 | kern.printf("Kernel interface initialized\n"); 376 | rv = 0; 377 | 378 | err: 379 | write_protect_restore(wp); 380 | intr_restore(flags); 381 | return rv; 382 | } 383 | -------------------------------------------------------------------------------- /kernel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #ifndef KERNEL_H 12 | #define KERNEL_H 13 | 14 | #include "types.h" 15 | #include "reboot.h" 16 | 17 | #define PAGE_SIZE 0x4000 18 | #define PAGE_MASK (PAGE_SIZE - 1) 19 | 20 | #define PML4SHIFT 39 21 | #define PDPSHIFT 30 22 | #define PDRSHIFT 21 23 | #define PAGE_SHIFT 12 24 | 25 | #define KVADDR(l4, l3, l2, l1) ( \ 26 | ((unsigned long)-1 << 47) | \ 27 | ((unsigned long)(l4) << PML4SHIFT) | \ 28 | ((unsigned long)(l3) << PDPSHIFT) | \ 29 | ((unsigned long)(l2) << PDRSHIFT) | \ 30 | ((unsigned long)(l1) << PAGE_SHIFT)) 31 | 32 | #define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base) 33 | #define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX 34 | 35 | typedef u64 vm_paddr_t; 36 | typedef u64 vm_offset_t; 37 | typedef u64 vm_size_t; 38 | typedef void * vm_map_t; 39 | typedef char vm_memattr_t; 40 | typedef void * pmap_t; 41 | 42 | typedef void (*smp_rendezvous_callback_t)(void *); 43 | 44 | struct sysent_t { 45 | int sy_narg; 46 | void *sy_call; 47 | u16 sy_auevent; 48 | void *sy_systrace_args_func; 49 | int sy_entry; 50 | int sy_return; 51 | int sy_flags; 52 | int sy_thrcnt; 53 | }; 54 | 55 | struct ksym_t { 56 | // two parameters related to kaslr (they are not symbols) 57 | uintptr_t kern_base; 58 | uintptr_t dmap_base; 59 | 60 | int (*printf)(const char *fmt, ...); 61 | int (*snprintf)(const char *fmt, ...); 62 | int (*copyin)(const void *uaddr, void *kaddr, size_t len); 63 | int (*copyout)(const void *kaddr, void *uaddr, size_t len); 64 | int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done); 65 | 66 | void **kernel_map; 67 | void *kernel_pmap_store; 68 | vm_offset_t (*kmem_alloc_contig)(vm_map_t map, vm_size_t size, int flags, 69 | vm_paddr_t low, vm_paddr_t high, 70 | unsigned long alignment, 71 | unsigned long boundary, 72 | vm_memattr_t memattr); 73 | void (*kmem_free)(vm_map_t, vm_offset_t, vm_size_t); 74 | vm_paddr_t (*pmap_extract)(pmap_t pmap, vm_offset_t va); 75 | void (*pmap_protect)(pmap_t pmap, u64 sva, u64 eva, u8 pr); 76 | 77 | struct sysent_t *sysent; 78 | 79 | void (*sched_pin)(void); 80 | void (*sched_unpin)(void); 81 | void (*smp_rendezvous)(smp_rendezvous_callback_t, 82 | smp_rendezvous_callback_t, 83 | smp_rendezvous_callback_t, void *); 84 | // yes...it is misspelled :) 85 | void (*smp_no_rendevous_barrier)(void *); 86 | void *icc_query_nowait; 87 | void *Starsha_UcodeInfo; 88 | int (*gpu_devid_is_9924)(); 89 | void *(*gc_get_fw_info)(); 90 | void *eap_hdd_key; 91 | void *edid; 92 | void (*wlanbt)(unsigned int state); 93 | int (*kern_reboot)(int magic); 94 | void(*set_gpu_freq)(unsigned int num, unsigned int freq); 95 | void(*set_pstate)(unsigned int val); 96 | void(*update_vddnp)(unsigned int val); 97 | void(*set_cu_power_gate)(unsigned int val); 98 | }; 99 | 100 | extern struct ksym_t kern; 101 | 102 | static inline int curcpu(void) 103 | { 104 | int cpuid; 105 | // TODO ensure offsetof(struct pcpu, pc_cpuid) == 0x34 on all fw 106 | asm volatile("mov %0, gs:0x34;" : "=r" (cpuid)); 107 | return cpuid; 108 | } 109 | 110 | // Assign a working printf function to this to debug the symbol resolver 111 | extern int (*early_printf)(const char *fmt, ...); 112 | 113 | void *kernel_resolve(const char *name); 114 | 115 | void *kernel_alloc_contig(size_t size); 116 | void kernel_free_contig(void *addr, size_t size); 117 | 118 | void kernel_remap(void *start, void *end, int perm); 119 | 120 | void kernel_syscall_install(int num, void *call, int narg); 121 | int kernel_hook_install(void *target, void *hook); 122 | 123 | int kernel_init(void *early_printf); 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /kexec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #include "kernel.h" 12 | #include "linux_boot.h" 13 | #include "x86.h" 14 | #include "kexec.h" 15 | #include "firmware.h" 16 | #include "string.h" 17 | #include "acpi.h" 18 | 19 | static int k_copyin(const void *uaddr, void *kaddr, size_t len) 20 | { 21 | if (!uaddr || !kaddr) 22 | return EFAULT; 23 | memcpy(kaddr, uaddr, len); 24 | return 0; 25 | } 26 | 27 | static int k_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) 28 | { 29 | const char *ustr = (const char*)uaddr; 30 | char *kstr = (char*)kaddr; 31 | size_t ret; 32 | if (!uaddr || !kaddr) 33 | return EFAULT; 34 | ret = strlcpy(kstr, ustr, len); 35 | if (ret >= len) { 36 | if (done) 37 | *done = len; 38 | return ENAMETOOLONG; 39 | } else { 40 | if (done) 41 | *done = ret + 1; 42 | } 43 | return 0; 44 | } 45 | 46 | static int k_copyout(const void *kaddr, void *uaddr, size_t len) 47 | { 48 | if (!uaddr || !kaddr) 49 | return EFAULT; 50 | memcpy(uaddr, kaddr, len); 51 | return 0; 52 | } 53 | 54 | int sys_kexec(void *td, struct sys_kexec_args *uap) 55 | { 56 | int err = 0; 57 | size_t initramfs_size = uap->initramfs_size; 58 | void *image = NULL; 59 | void *initramfs = NULL; 60 | size_t firmware_size = 0; 61 | struct boot_params *bp = NULL; 62 | size_t cmd_line_maxlen = 0; 63 | char *cmd_line = NULL; 64 | 65 | int (*copyin)(const void *uaddr, void *kaddr, size_t len) = td ? kern.copyin : k_copyin; 66 | int (*copyinstr)(const void *uaddr, void *kaddr, size_t len, size_t *done) = td ? kern.copyinstr : k_copyinstr; 67 | int (*copyout)(const void *kaddr, void *uaddr, size_t len) = td ? kern.copyout : k_copyout; 68 | 69 | kern.printf("sys_kexec invoked\n"); 70 | kern.printf("sys_kexec(%p, %zu, %p, %zu, \"%s\")\n", uap->image, 71 | uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line); 72 | 73 | // Look up our shutdown hook point 74 | void *icc_query_nowait = kern.icc_query_nowait; 75 | if (!icc_query_nowait) { 76 | err = ENOENT; 77 | goto cleanup; 78 | } 79 | 80 | // Set gpu frequencies and pstate 81 | kern.set_pstate(3); 82 | kern.set_gpu_freq(0, 800); //800 83 | kern.set_gpu_freq(1, 800); //674 84 | kern.set_gpu_freq(2, 800); //610 85 | kern.set_gpu_freq(3, 800); //800 86 | kern.set_gpu_freq(4, 800); //800 87 | kern.set_gpu_freq(5, 800); //720 88 | kern.set_gpu_freq(6, 800); //720 89 | kern.set_gpu_freq(7, 800); //720 90 | 91 | kern.update_vddnp(0x12); 92 | kern.set_cu_power_gate(0x12); 93 | 94 | // Copy in kernel image 95 | image = kernel_alloc_contig(uap->image_size); 96 | if (!image) { 97 | kern.printf("Failed to allocate image\n"); 98 | err = ENOMEM; 99 | goto cleanup; 100 | } 101 | err = copyin(uap->image, image, uap->image_size); 102 | if (err) { 103 | kern.printf("Failed to copy in image\n"); 104 | goto cleanup; 105 | } 106 | 107 | // Copy in initramfs 108 | initramfs = kernel_alloc_contig(initramfs_size + FW_CPIO_SIZE); 109 | if (!initramfs) { 110 | kern.printf("Failed to allocate initramfs\n"); 111 | err = ENOMEM; 112 | goto cleanup; 113 | } 114 | 115 | err = firmware_extract(((u8*)initramfs)); 116 | if (err < 0) { 117 | kern.printf("Failed to extract GPU firmware - continuing anyway\n"); 118 | } else { 119 | firmware_size = err; 120 | } 121 | 122 | if (initramfs_size) { 123 | err = copyin(uap->initramfs, initramfs + firmware_size, initramfs_size); 124 | if (err) { 125 | kern.printf("Failed to copy in initramfs\n"); 126 | goto cleanup; 127 | } 128 | } 129 | initramfs_size += firmware_size; 130 | 131 | // Copy in cmdline 132 | cmd_line_maxlen = ((struct boot_params *)image)->hdr.cmdline_size + 1; 133 | cmd_line = kernel_alloc_contig(cmd_line_maxlen); 134 | if (!cmd_line) { 135 | kern.printf("Failed to allocate cmdline\n"); 136 | err = ENOMEM; 137 | goto cleanup; 138 | } 139 | err = copyinstr(uap->cmd_line, cmd_line, cmd_line_maxlen, NULL); 140 | if (err) { 141 | kern.printf("Failed to copy in cmdline\n"); 142 | goto cleanup; 143 | } 144 | cmd_line[cmd_line_maxlen - 1] = 0; 145 | 146 | kern.printf("\nkexec parameters:\n"); 147 | kern.printf(" Kernel image size: %zu bytes\n", uap->image_size); 148 | kern.printf(" Initramfs size: %zu bytes (%zu from user)\n", 149 | initramfs_size, uap->initramfs_size); 150 | kern.printf(" Kernel command line: %s\n", cmd_line); 151 | kern.printf(" Kernel image buffer: %p\n", image); 152 | kern.printf(" Initramfs buffer: %p\n", initramfs); 153 | 154 | // Allocate our boot params 155 | bp = kernel_alloc_contig(sizeof(*bp)); 156 | if (!bp) { 157 | kern.printf("Failed to allocate bp\n"); 158 | err = ENOMEM; 159 | goto cleanup; 160 | } 161 | 162 | // Initialize bp 163 | // TODO should probably do this from cpu_quiesce_gate, then bp doesn't 164 | // need to be allocated here, just placed directly into low mem 165 | set_nix_info(image, bp, initramfs, initramfs_size, cmd_line, uap->vram_gb); 166 | 167 | prepare_boot_params(bp, image); 168 | 169 | // Hook the final ICC shutdown function 170 | if (!kernel_hook_install(hook_icc_query_nowait, icc_query_nowait)) { 171 | kern.printf("Failed to install shutdown hook\n"); 172 | err = EINVAL; 173 | goto cleanup; 174 | } 175 | 176 | kern.printf("******************************************************\n"); 177 | kern.printf("kexec successfully armed. Please shut down the system.\n"); 178 | kern.printf("******************************************************\n\n"); 179 | 180 | /* 181 | kern.printf("\nkern_reboot(0x%x)...\n", RB_POWEROFF); 182 | if (kern.kern_reboot(RB_POWEROFF) == -1) 183 | kern.printf("\nkern_reboot(0x%x) failed\n", RB_POWEROFF); 184 | */ 185 | return 0; 186 | 187 | cleanup: 188 | kernel_free_contig(cmd_line, cmd_line_maxlen); 189 | kernel_free_contig(bp, sizeof(*bp)); 190 | kernel_free_contig(image, uap->image_size); 191 | kernel_free_contig(initramfs, uap->initramfs_size); 192 | return err; 193 | 194 | copyout(NULL, NULL, 0); 195 | } 196 | 197 | int kexec_init(void *_early_printf, sys_kexec_t *sys_kexec_ptr) 198 | { 199 | int rv = 0; 200 | 201 | // potentially needed to write early_printf 202 | u64 flags = intr_disable(); 203 | u64 wp = write_protect_disable(); 204 | 205 | if (kernel_init(_early_printf) < 0) { 206 | rv = -1; 207 | goto cleanup; 208 | } 209 | 210 | kern.printf("Installing sys_kexec to system call #%d\n", SYS_KEXEC); 211 | kernel_syscall_install(SYS_KEXEC, sys_kexec, SYS_KEXEC_NARGS); 212 | kern.printf("kexec_init() successful\n\n"); 213 | 214 | if (sys_kexec_ptr) 215 | *sys_kexec_ptr = sys_kexec; 216 | 217 | cleanup: 218 | write_protect_restore(wp); 219 | if (kern.sched_unpin && wp & CR0_WP) { 220 | // If we're returning to a state with WP enabled, assume the caller 221 | // wants the thread unpinned. Else the caller is expected to 222 | // call kern.sched_unpin() manually. 223 | kern.sched_unpin(); 224 | } 225 | intr_restore(flags); 226 | return rv; 227 | } 228 | -------------------------------------------------------------------------------- /kexec.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #ifndef KEXEC_H 12 | #define KEXEC_H 13 | 14 | #include "types.h" 15 | 16 | #define SYS_KEXEC 153 17 | #define SYS_KEXEC_NARGS 6 18 | 19 | struct sys_kexec_args { 20 | void *image; 21 | size_t image_size; 22 | void *initramfs; 23 | size_t initramfs_size; 24 | char *cmd_line; 25 | int vram_gb; 26 | }; 27 | 28 | typedef int (*sys_kexec_t)(void *td, struct sys_kexec_args *uap); 29 | 30 | // Note: td is unused, you can pass NULL if you call this directly. 31 | int sys_kexec(void *td, struct sys_kexec_args *uap); 32 | 33 | int kernel_init(void *early_printf); 34 | 35 | int kexec_init(void *early_printf, sys_kexec_t *sys_kexec_ptr) 36 | __attribute__ ((section (".init"))); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /kexec.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | ENTRY(kexec_init) 12 | 13 | SECTIONS { 14 | /* We don't do GOT relocation and rely on nothing ending up using the GOT 15 | * (-fno-common helps here) */ 16 | /DISCARD/ : { *(.comment) *(.got) } 17 | _start = .; 18 | .init : { *(.init) *(.init.*) } 19 | .text : { *(.text) *(.text.*) } 20 | .data : { *(.data) *(.data.*) } 21 | .rodata : { *(.rodata) *(.rodata.*) } 22 | .bss : { *(.bss) *(.bss.*) *(COMMON)} 23 | .footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out in raw binary */ 24 | _end = .; 25 | } 26 | -------------------------------------------------------------------------------- /linux_boot.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #include "linux_boot.h" 12 | #include "types.h" 13 | #include "string.h" 14 | #include "x86.h" 15 | #include "kernel.h" 16 | #include "uart.h" 17 | #include "acpi.h" 18 | 19 | void uart_write_byte(u8 b); 20 | 21 | static u64 vram_base = 0x100000000; 22 | // Current code assumes it's a power of two. 23 | static u64 vram_size = 1024 * 1024 * 1024; 24 | static int vram_gb = 3; 25 | 26 | #define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff) 27 | 28 | #define MSR_GS_BASE 0xc0000101 /* 64bit GS base */ 29 | 30 | struct desc_struct { 31 | u16 limit0; 32 | u16 base0; 33 | u16 base1: 8, type: 4, s: 1, dpl: 2, p: 1; 34 | u16 limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; 35 | } __attribute__((packed)); 36 | 37 | typedef void (*jmp_to_linux_t)(uintptr_t linux_startup, uintptr_t bootargs, 38 | uintptr_t new_cr3, uintptr_t gdt_ptr); 39 | extern uint8_t *jmp_to_linux; 40 | extern size_t jmp_to_linux_size; 41 | 42 | // FreeBSD DMAP addresses 43 | struct linux_boot_info { 44 | void *linux_image; 45 | void *initramfs; 46 | size_t initramfs_size; 47 | struct boot_params *bp; 48 | char *cmd_line; 49 | }; 50 | static struct linux_boot_info nix_info; 51 | 52 | void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, 53 | size_t initramfs_size, char *cmd_line, int v) 54 | { 55 | nix_info.linux_image = linux_image; 56 | nix_info.bp = bp; 57 | nix_info.initramfs = initramfs; 58 | nix_info.initramfs_size = initramfs_size; 59 | nix_info.cmd_line = cmd_line; 60 | vram_gb = v; 61 | } 62 | 63 | static volatile int halted_cpus = 0; 64 | 65 | static void bp_add_smap_entry(struct boot_params *bp, u64 addr, u64 size, 66 | u32 type) 67 | { 68 | uint8_t idx = bp->e820_entries; 69 | bp->e820_map[idx].addr = addr; 70 | bp->e820_map[idx].size = size; 71 | bp->e820_map[idx].type = type; 72 | bp->e820_entries++; 73 | } 74 | 75 | void prepare_boot_params(struct boot_params *bp, u8 *linux_image) 76 | { 77 | memset(bp, 0, sizeof(struct boot_params)); 78 | struct boot_params *bp_src = (struct boot_params *)linux_image; 79 | memcpy(&bp->hdr, &bp_src->hdr, offsetof(struct setup_header, header) + 80 | ((u8 *)&bp_src->hdr.jump)[1]); 81 | 82 | // These values are from fw 1.01 83 | bp_add_smap_entry(bp, 0x0000000000, 0x0000008000, SMAP_TYPE_MEMORY); 84 | bp_add_smap_entry(bp, 0x0000008000, 0x0000078000, SMAP_TYPE_RESERVED); 85 | bp_add_smap_entry(bp, 0x0000080000, 0x000001a000, SMAP_TYPE_MEMORY); 86 | bp_add_smap_entry(bp, 0x000009a000, 0x0000006000, SMAP_TYPE_RESERVED); 87 | bp_add_smap_entry(bp, 0x00000a0000, 0x0000020000, SMAP_TYPE_RESERVED); 88 | bp_add_smap_entry(bp, 0x00000e0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); 89 | bp_add_smap_entry(bp, 0x0000100000, 0x0000300000, SMAP_TYPE_MEMORY); 90 | bp_add_smap_entry(bp, 0x0000400000, 0x0000080000, SMAP_TYPE_RESERVED); 91 | bp_add_smap_entry(bp, 0x0000480000, 0x0000200000, SMAP_TYPE_MEMORY); 92 | bp_add_smap_entry(bp, 0x0000680000, 0x0000080000, SMAP_TYPE_RESERVED); 93 | bp_add_smap_entry(bp, 0x0000700000, 0x007e8e8000, SMAP_TYPE_MEMORY); 94 | bp_add_smap_entry(bp, 0x007efe8000, 0x0000008000, SMAP_TYPE_ACPI_NVS); 95 | bp_add_smap_entry(bp, 0x007eff0000, 0x0000010000, SMAP_TYPE_ACPI_RECLAIM); 96 | // This used to be VRAM, but we reclaim it as RAM 97 | bp_add_smap_entry(bp, 0x007f000000, 0x0001000000, SMAP_TYPE_MEMORY); 98 | bp_add_smap_entry(bp, 0x0080000000, 0x0060000000, SMAP_TYPE_RESERVED); 99 | bp_add_smap_entry(bp, 0x00e0000000, 0x0018000000, SMAP_TYPE_RESERVED); 100 | bp_add_smap_entry(bp, 0x00f8000000, 0x0004000000, SMAP_TYPE_RESERVED); 101 | // Instead, carve out VRAM from the beginning of high memory 102 | bp_add_smap_entry(bp, vram_base, vram_gb * vram_size, SMAP_TYPE_RESERVED); 103 | bp_add_smap_entry(bp, vram_base + vram_gb * vram_size, 0x017f000000 - vram_gb * vram_size, 104 | SMAP_TYPE_MEMORY); 105 | } 106 | 107 | #define WR32(a, v) *(volatile u32 *)PA_TO_DM(a) = (v) 108 | 109 | #define MC_VM_FB_LOCATION 0x2024 110 | #define MC_VM_FB_OFFSET 0x2068 111 | #define HDP_NONSURFACE_BASE 0x2c04 112 | #define CONFIG_MEMSIZE 0x5428 113 | 114 | static void configure_vram(void) 115 | { 116 | u64 mmio_base = 0xe4800000; 117 | u64 fb_base = 0x0f00000000; 118 | u64 fb_top = fb_base + vram_gb * vram_size - 1; 119 | 120 | WR32(mmio_base + MC_VM_FB_LOCATION, 0); 121 | WR32(mmio_base + HDP_NONSURFACE_BASE, 0); 122 | 123 | WR32(mmio_base + MC_VM_FB_LOCATION, 124 | ((fb_top >> 24) << 16) | (fb_base >> 24)); 125 | WR32(mmio_base + MC_VM_FB_OFFSET, vram_base >> 22); 126 | WR32(mmio_base + HDP_NONSURFACE_BASE, fb_base >> 8); 127 | WR32(mmio_base + CONFIG_MEMSIZE, vram_gb * vram_size >> 20); 128 | } 129 | 130 | #define IA32_MTRR_DEF_TYPE 0x2ff 131 | #define MTRR_BASE(i) (0x200 + 2*i) 132 | #define MTRR_MASK(i) (0x201 + 2*i) 133 | 134 | static void setup_mtrr(void) 135 | { 136 | disable_interrupts(); 137 | u64 cr0 = cr0_read(); 138 | cr0_write((cr0 | CR0_CD) & (~(u64)CR0_NW)); 139 | wbinvd(); 140 | cr3_write(cr3_read()); // TLB flush 141 | 142 | wrmsr(IA32_MTRR_DEF_TYPE, 0); // MTRRs disabled 143 | 144 | // Low memory (0GB-2GB) = WB 145 | wrmsr(MTRR_BASE(0), 0x0000000006); 146 | wrmsr(MTRR_MASK(0), 0xff80000800); 147 | // High memory (4GB-8GB) = WB 148 | wrmsr(MTRR_BASE(1), 0x0100000006); 149 | wrmsr(MTRR_MASK(1), 0xff00000800); 150 | // High memory (8GB-10GB) = WB 151 | wrmsr(MTRR_BASE(2), 0x0200000006); 152 | wrmsr(MTRR_MASK(2), 0xff80000800); 153 | // VRAM (4GB-4GB+vram_size) = UC 154 | wrmsr(MTRR_BASE(3), 0x0100000000); 155 | wrmsr(MTRR_MASK(3), (0xffffffffff - vram_gb * vram_size + 1) | 0x800); 156 | 157 | wbinvd(); 158 | cr3_write(cr3_read()); // TLB flush 159 | wrmsr(IA32_MTRR_DEF_TYPE, (3<<10)); // MTRRs enabled, default uncachable 160 | cr0_write(cr0); 161 | enable_interrupts(); 162 | } 163 | 164 | static void cleanup_interrupts(void) 165 | { 166 | int i; 167 | disable_interrupts(); 168 | 169 | // Reset APIC stuff (per-CPU) 170 | *(volatile u32 *)PA_TO_DM(0xfee00410) = 1; 171 | for (i = 0x320; i < 0x380; i += 0x10) 172 | *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; 173 | for (i = 0x480; i < 0x500; i += 0x10) 174 | *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0xffffffff; 175 | for (i = 0x500; i < 0x540; i += 0x10) 176 | *(volatile u32 *)PA_TO_DM(0xfee00000 + i) = 0x10000; 177 | *(volatile u32 *)PA_TO_DM(0xfee00410) = 0; 178 | 179 | // Fix the LVT offset for thresholding 180 | wrmsr(0x413, (1L<<24) | (1L<<52)); 181 | wrmsr(0xc0000408, (1L<<24) | (1L<<52)); 182 | } 183 | 184 | #define DEFAULT_STACK 0 185 | 186 | #define DPL0 0x0 187 | #define DPL3 0x3 188 | 189 | #define BPCIE_BAR2 0xc8800000 190 | #define BPCIE_HPET_BASE 0x109000 191 | #define BPCIE_HPET_SIZE 0x400 192 | 193 | //GRBM 194 | #define SOFT_RESET_CP (1 << 0) 195 | #define SOFT_RESET_CB (1 << 1) 196 | #define SOFT_RESET_RLC (1 << 2) 197 | #define SOFT_RESET_DB (1 << 3) 198 | #define SOFT_RESET_GDS (1 << 4) 199 | #define SOFT_RESET_PA (1 << 5) 200 | #define SOFT_RESET_SC (1 << 6) 201 | #define SOFT_RESET_BCI (1 << 7) 202 | #define SOFT_RESET_SPI (1 << 8) 203 | #define SOFT_RESET_SX (1 << 10) 204 | #define SOFT_RESET_TC (1 << 11) 205 | #define SOFT_RESET_TA (1 << 12) 206 | #define SOFT_RESET_VGT (1 << 14) 207 | #define SOFT_RESET_IA (1 << 15) 208 | 209 | //SRBM 210 | #define SOFT_RESET_BIF (1 << 1) 211 | #define SOFT_RESET_DC (1 << 5) 212 | #define SOFT_RESET_DMA1 (1 << 6) 213 | #define SOFT_RESET_GRBM (1 << 8) 214 | #define SOFT_RESET_HDP (1 << 9) 215 | #define SOFT_RESET_IH (1 << 10) 216 | #define SOFT_RESET_MC (1 << 11) 217 | #define SOFT_RESET_ROM (1 << 14) 218 | #define SOFT_RESET_SEM (1 << 15) 219 | #define SOFT_RESET_VMC (1 << 17) 220 | #define SOFT_RESET_DMA (1 << 20) 221 | #define SOFT_RESET_TST (1 << 21) 222 | #define SOFT_RESET_REGBB (1 << 22) 223 | #define SOFT_RESET_ORB (1 << 23) 224 | 225 | static void cpu_quiesce_gate(void *arg) 226 | { 227 | 228 | 229 | // Ensure we can write anywhere 230 | cr0_write(cr0_read() & ~CR0_WP); 231 | 232 | // Interrupt stuff local to each CPU 233 | cleanup_interrupts(); 234 | 235 | // We want to set up MTRRs on all CPUs 236 | setup_mtrr(); 237 | 238 | if (curcpu() != 0) { 239 | // We're not on BSP. Try to halt. 240 | __sync_fetch_and_add(&halted_cpus, 1); 241 | cpu_stop(); 242 | } 243 | 244 | uart_write_str("kexec: Waiting for secondary CPUs...\n"); 245 | 246 | // wait for all cpus to halt 247 | while (!__sync_bool_compare_and_swap(&halted_cpus, 7, 7)); 248 | 249 | uart_write_str("kexec: Secondary CPUs quiesced\n"); 250 | 251 | //* Put ident mappings in current page tables 252 | // Should not be needed, but maybe helps for debugging? 253 | cr4_pge_disable(); 254 | u64 *pml4_base = (u64 *)PA_TO_DM(cr3_read() & 0x000ffffffffff000ull); 255 | u64 *pdp_base = (u64 *)PA_TO_DM(*pml4_base & 0x000ffffffffff000ull); 256 | for (u64 i = 0; i < 4; i++) { 257 | pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; 258 | } 259 | 260 | // Clear (really) low mem. 261 | // Linux reads from here to try and access EBDA... 262 | // get_bios_ebda reads u16 from 0x40e 263 | // reserve_ebda_region reads u16 from 0x413 264 | // Writing zeros causes linux to default to marking 265 | // LOWMEM_CAP(0x9f000)-1MB(0x100000) as reserved. 266 | // It doesn't match the ps4 e820 map, but that seems OK. 267 | memset((void *)0, 0, 0x1000); 268 | 269 | // Create a new page table hierarchy out of the way of linux 270 | // Accessed via freebsd direct map 271 | pml4_base = (u64 *)PA_TO_DM(0x1000); // "boot loader" as per linux boot.txt 272 | // We only use 1Gbyte mappings. So we need 2 * 0x200 * 8 = 0x2000 bytes :| 273 | memset(pml4_base, 0, 512 * sizeof(u64) * 2); 274 | pdp_base = pml4_base + 512; 275 | u64 pdpe = DM_TO_ID(pdp_base) | PG_RW | PG_V | PG_U; 276 | pml4_base[0] = pdpe; 277 | // Maintain the freebsd direct map 278 | pml4_base[DM_PML4_BASE] = pdpe; 279 | for (u64 i = 0; i < 4; i++) { 280 | pdp_base[i] = (i << 30) | PG_RW | PG_V | PG_U | PG_PS; 281 | } 282 | 283 | uart_write_str("kexec: Setting up GDT...\n"); 284 | 285 | desc_ptr gdt_ptr; 286 | struct desc_struct *desc = (struct desc_struct *)(pdp_base + 512); 287 | gdt_ptr.limit = sizeof(struct desc_struct) * 0x100 - 1; 288 | gdt_ptr.address = DM_TO_ID(desc); 289 | 290 | // clear 291 | memset(desc, 0, gdt_ptr.limit + 1); 292 | // Most things are ignored in 64bit mode, and we will never be in 293 | // 32bit/compat modes, so just setup another pure-64bit environment... 294 | // Linux inits it's own GDT in secondary_startup_64 295 | // 0x10 296 | desc[2].limit0 = 0xffff; 297 | desc[2].base0 = 0x0000; 298 | desc[2].base1 = 0x0000; 299 | desc[2].type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; 300 | desc[2].s = 1; 301 | desc[2].dpl = 0; 302 | desc[2].p = 1; 303 | desc[2].limit = 0xf; 304 | desc[2].avl = 0; 305 | desc[2].l = 1; 306 | desc[2].d = 0; 307 | desc[2].g = 0; 308 | desc[2].base2 = 0x00; 309 | // 0x18 310 | desc[3].limit0 = 0xffff; 311 | desc[3].base0 = 0x0000; 312 | desc[3].base1 = 0x0000; 313 | desc[3].type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; 314 | desc[3].s = 1; 315 | desc[3].dpl = 0; 316 | desc[3].p = 1; 317 | desc[3].limit = 0xf; 318 | desc[3].avl = 0; 319 | desc[3].l = 0; 320 | desc[3].d = 0; 321 | desc[3].g = 0; 322 | desc[3].base2 = 0x00; 323 | // Task segment value 324 | // 0x20 325 | desc[4].limit0 = 0x0000; 326 | desc[4].base0 = 0x0000; 327 | desc[4].base1 = 0x0000; 328 | desc[4].type = SEG_TYPE_TSS; 329 | desc[4].s = 1; 330 | desc[4].dpl = 0; 331 | desc[4].p = 1; 332 | desc[4].limit = 0x0; 333 | desc[4].avl = 0; 334 | desc[4].l = 0; 335 | desc[4].d = 0; 336 | desc[4].g = 0; 337 | desc[4].base2 = 0x00; 338 | 339 | uart_write_str("kexec: Relocating stub...\n"); 340 | 341 | // Relocate the stub and jump to it 342 | // TODO should thunk_copy be DMAP here? 343 | void *thunk_copy = (void *)(gdt_ptr.address + gdt_ptr.limit + 1); 344 | memcpy(thunk_copy, &jmp_to_linux, jmp_to_linux_size); 345 | // XXX The +0x200 is for the iret stack in linux_thunk.S 346 | uintptr_t lowmem_pos = DM_TO_ID(thunk_copy) + jmp_to_linux_size + 0x200; 347 | 348 | uart_write_str("kexec: Setting up boot params...\n"); 349 | 350 | // XXX we write into this bootargs and pass it to the kernel, but in 351 | // jmp_to_linux we use the bootargs from the image as input. So they 352 | // MUST MATCH! 353 | struct boot_params *bp_lo = (struct boot_params *)lowmem_pos; 354 | *bp_lo = *nix_info.bp; 355 | lowmem_pos += sizeof(struct boot_params); 356 | 357 | struct setup_header *shdr = &bp_lo->hdr; 358 | shdr->cmd_line_ptr = lowmem_pos; 359 | shdr->ramdisk_image = DM_TO_ID(nix_info.initramfs) & 0xffffffff; 360 | shdr->ramdisk_size = nix_info.initramfs_size & 0xffffffff; 361 | bp_lo->ext_ramdisk_image = DM_TO_ID(nix_info.initramfs) >> 32; 362 | bp_lo->ext_ramdisk_size = nix_info.initramfs_size >> 32; 363 | shdr->hardware_subarch = X86_SUBARCH_PS4; 364 | // This needs to be nonzero for the initramfs to work 365 | shdr->type_of_loader = 0xd0; // kexec 366 | 367 | strlcpy((char *)DM_TO_ID(shdr->cmd_line_ptr), nix_info.cmd_line, 368 | nix_info.bp->hdr.cmdline_size); 369 | lowmem_pos += strlen(nix_info.cmd_line) + 1; 370 | 371 | uart_write_str("kexec: Cleaning up hardware...\n"); 372 | 373 | // Disable IOMMU 374 | *(volatile u64 *)PA_TO_DM(0xfc000018) &= ~1; 375 | 376 | // Disable all MSIs on Baikal (bus=0, slot=20) 377 | disableMSI(0xf80a00e0); //func = 0 Baikal ACPI 378 | disableMSI(0xf80a10e0); //func = 1 Baikal Ethernet Controller 379 | disableMSI(0xf80a20e0); //func = 2 Baikal SATA AHCI Controller 380 | disableMSI(0xf80a30e0); //func = 3 Baikal SD/MMC Host Controller 381 | disableMSI(0xf80a40e0); //func = 4 Baikal PCI Express Glue and Miscellaneous Devices 382 | disableMSI(0xf80a50e0); //func = 5 Baikal DMA Controller 383 | disableMSI(0xf80a60e0); //func = 6 Baikal Baikal Memory (DDR3/SPM) 384 | disableMSI(0xf80a70e0); //func = 7 Baikal Baikal USB 3.0 xHCI Host Controller 385 | 386 | // Stop HPET timers 387 | //*(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + 0x10) &= ~(1UL << 0); //General Configuration Register 388 | /* 389 | u64 NUM_TIM_CAP = *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE) & 0x1F00; 390 | for (u64 N = 0; N <= NUM_TIM_CAP; N++) { 391 | *(volatile u64 *)PA_TO_DM(BPCIE_BAR2 + BPCIE_HPET_BASE + (0x20*N) + 0x100) &= ~(1UL << 2); //Timer N Configuration and Capabilities Register 392 | } 393 | */ 394 | uart_write_str("kexec: Reconfiguring VRAM...\n"); 395 | 396 | configure_vram(); 397 | 398 | uart_write_str("kexec: Resetting GPU...\n"); 399 | 400 | // Softreset GPU 401 | u32 grbm_soft_reset = 0, srbm_soft_reset = 0; 402 | 403 | //grbm 404 | grbm_soft_reset = SOFT_RESET_CB | 405 | SOFT_RESET_DB | 406 | SOFT_RESET_GDS | 407 | SOFT_RESET_PA | 408 | SOFT_RESET_SC | 409 | SOFT_RESET_BCI | 410 | SOFT_RESET_SPI | 411 | SOFT_RESET_SX | 412 | SOFT_RESET_TC | 413 | SOFT_RESET_TA | 414 | SOFT_RESET_VGT | 415 | SOFT_RESET_IA; 416 | 417 | grbm_soft_reset |= SOFT_RESET_CP | SOFT_RESET_VGT; 418 | grbm_soft_reset |= SOFT_RESET_RLC; 419 | 420 | //srmb 421 | srbm_soft_reset |= SOFT_RESET_GRBM; 422 | srbm_soft_reset |= SOFT_RESET_DMA; 423 | srbm_soft_reset |= SOFT_RESET_DMA1; 424 | srbm_soft_reset |= SOFT_RESET_DC; 425 | srbm_soft_reset |= SOFT_RESET_SEM; 426 | srbm_soft_reset |= SOFT_RESET_IH; 427 | //srbm_soft_reset |= SOFT_RESET_GRBM; 428 | srbm_soft_reset |= SOFT_RESET_VMC; 429 | //srbm_soft_reset |= SOFT_RESET_MC; 430 | 431 | *(volatile u64 *)PA_TO_DM(0xe480C40C) = 0; // Disable PG 432 | udelay(50); 433 | *(volatile u64 *)PA_TO_DM(0xe480C424) &= ~(0x1 | 0x2); //Disable CG 434 | udelay(50); 435 | *(volatile u64 *)PA_TO_DM(0xe480c300) = 0; // Halt RLC 436 | udelay(50); 437 | *(volatile u64 *)PA_TO_DM(0xe48086d8) = 0x15000000; // Halt CP blocks 438 | udelay(50); 439 | *(volatile u64 *)PA_TO_DM(0xe4808234) = 0x50000000; // Halt MEC 440 | udelay(50); 441 | 442 | *(volatile u64 *)PA_TO_DM(0xe480d048) = 1; // Halt SDMA0 eeply 443 | udelay(50); 444 | *(volatile u64 *)PA_TO_DM(0xe480d848) = 1; // Halt SDMA1 445 | udelay(50); 446 | 447 | *(volatile u64 *)PA_TO_DM(0xe480c1a8) &= ~0x180000; // CP_INT_CNTL_RING0 eeply 448 | udelay(50); 449 | 450 | *(volatile u64 *)PA_TO_DM(0xe4808020) |= grbm_soft_reset; 451 | udelay(50); 452 | *(volatile u64 *)PA_TO_DM(0xe4808020) &= ~grbm_soft_reset; 453 | udelay(50); 454 | *(volatile u64 *)PA_TO_DM(0xe4800e60) |= srbm_soft_reset; 455 | udelay(50); 456 | *(volatile u64 *)PA_TO_DM(0xe4800e60) &= ~srbm_soft_reset; 457 | udelay(100); 458 | 459 | // Enable audio output 460 | *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x154; 461 | *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x80000000; 462 | *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x154; 463 | *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x80000000; 464 | *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x154; 465 | *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x80000000; 466 | *(volatile u64 *)PA_TO_DM(0xe4813404) = 1; 467 | *(volatile u64 *)PA_TO_DM(0xe481340c) = 1; 468 | 469 | // // Set pin caps of pin 2 to vendor defined, to hide it 470 | // *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x101; 471 | // *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; 472 | // *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x120; 473 | // *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0xf00000; 474 | // // Set pin caps of pin 3 to !HDMI 475 | // *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x121; 476 | // *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x10; 477 | // Set pin configuration default 478 | *(volatile u64 *)PA_TO_DM(0xe4805e00) = 0x156; 479 | *(volatile u64 *)PA_TO_DM(0xe4805e04) = 0x185600f0; 480 | *(volatile u64 *)PA_TO_DM(0xe4805e18) = 0x156; 481 | *(volatile u64 *)PA_TO_DM(0xe4805e1c) = 0x500000f0; 482 | *(volatile u64 *)PA_TO_DM(0xe4805e30) = 0x156; 483 | *(volatile u64 *)PA_TO_DM(0xe4805e34) = 0x014510f0; 484 | 485 | uart_write_str("kexec: About to relocate and jump to kernel\n"); 486 | 487 | ((jmp_to_linux_t)thunk_copy)( 488 | DM_TO_ID(nix_info.linux_image), 489 | DM_TO_ID(bp_lo), 490 | DM_TO_ID(pml4_base), 491 | (uintptr_t)&gdt_ptr 492 | ); 493 | // should never reach here 494 | uart_write_str("kexec: unreachable (?)\n"); 495 | } 496 | 497 | // Hook for int icc_query_nowait(u8 icc_msg[0x7f0]) 498 | int hook_icc_query_nowait(u8 *icc_msg) 499 | { 500 | kern.printf("hook_icc_query_nowait called\n"); 501 | 502 | // We need reset bt/wifi, so disable it, we re-enable it when the kernel boot 503 | //In alternative we can re-enable it here, but sometimes that give problems.. 504 | kern.wlanbt(0x2); 505 | 506 | fix_acpi_tables((void*)PA_TO_DM(0xe0000), 0xe0000); 507 | 508 | kern.printf("ACPI tables fixed\n"); 509 | 510 | // Transition to BSP and halt other cpus 511 | // smp_no_rendevous_barrier is just nullsub, but it is treated specially by 512 | // smp_rendezvous. This is the easiest way to do this, since we can't assume 513 | // we're already running on BSP. Since smp_rendezvous normally waits on all 514 | // cpus to finish the callbacks, we just never return... 515 | kern.smp_rendezvous(kern.smp_no_rendevous_barrier, 516 | cpu_quiesce_gate, 517 | kern.smp_no_rendevous_barrier, NULL); 518 | 519 | // should never reach here 520 | kern.printf("hook_icc_query_nowait: unreachable (?)\n"); 521 | return 0; 522 | } 523 | -------------------------------------------------------------------------------- /linux_boot.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #ifndef LINUX_BOOT_H 12 | #define LINUX_BOOT_H 13 | 14 | #include "types.h" 15 | 16 | #define SMAP_TYPE_MEMORY 1 17 | #define SMAP_TYPE_RESERVED 2 18 | #define SMAP_TYPE_ACPI_RECLAIM 3 19 | #define SMAP_TYPE_ACPI_NVS 4 20 | #define SMAP_TYPE_UNUSABLE 5 21 | #define SMAP_TYPE_PMEM 7 22 | 23 | #define X86_SUBARCH_PS4 5 24 | 25 | struct e820entry { 26 | u64 addr; /* start of memory segment */ 27 | u64 size; /* size of memory segment */ 28 | u32 type; /* type of memory segment */ 29 | } __attribute__((packed)); 30 | 31 | struct setup_header { 32 | u8 setup_sects; 33 | u16 root_flags; 34 | u32 syssize; 35 | u16 ram_size; 36 | u16 vid_mode; 37 | u16 root_dev; 38 | u16 boot_flag; 39 | u16 jump; 40 | u32 header; 41 | u16 version; 42 | u32 realmode_swtch; 43 | u16 start_sys; 44 | u16 kernel_version; 45 | u8 type_of_loader; 46 | u8 loadflags; 47 | u16 setup_move_size; 48 | u32 code32_start; 49 | u32 ramdisk_image; 50 | u32 ramdisk_size; 51 | u32 bootsect_kludge; 52 | u16 heap_end_ptr; 53 | u8 ext_loader_ver; 54 | u8 ext_loader_type; 55 | u32 cmd_line_ptr; 56 | u32 initrd_addr_max; 57 | u32 kernel_alignment; 58 | u8 relocatable_kernel; 59 | u8 min_alignment; 60 | u16 xloadflags; 61 | u32 cmdline_size; 62 | u32 hardware_subarch; 63 | u64 hardware_subarch_data; 64 | u32 payload_offset; 65 | u32 payload_length; 66 | u64 setup_data; 67 | u64 pref_address; 68 | u32 init_size; 69 | u32 handover_offset; 70 | } __attribute__((packed)); 71 | 72 | #define E820MAX 128 /* number of entries in E820MAP */ 73 | 74 | OSTRUCT(boot_params, 0x1000) 75 | OFIELD(0x0c0, u32 ext_ramdisk_image); 76 | OFIELD(0x0c4, u32 ext_ramdisk_size); 77 | OFIELD(0x0c8, u32 ext_cmd_line_ptr); 78 | OFIELD(0x1e8, u8 e820_entries); 79 | OFIELD(0x1f1, struct setup_header hdr); 80 | OFIELD(0x2d0, struct e820entry e820_map[E820MAX]); 81 | OSTRUCT_END 82 | 83 | void set_nix_info(void *linux_image, struct boot_params *bp, void *initramfs, 84 | size_t initramfs_size, char *cmd_line, int v); 85 | void prepare_boot_params(struct boot_params *bp, u8 *linux_image); 86 | int hook_icc_query_nowait(u8 *icc_msg); 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /linux_thunk.S: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | .intel_syntax noprefix 12 | 13 | .equ setup_sects, 0x1f1 14 | .equ shdr_syssize, 0x1f4 15 | .equ pref_address, 0x258 16 | 17 | .text 18 | 19 | #void jmp_to_linux( 20 | # uintptr_t image_base, rdi 21 | # uintptr_t bootargs, rsi 22 | # uintptr_t new_cr3, rdx 23 | # uintptr_t gdt_ptr rcx 24 | #); 25 | .globl jmp_to_linux 26 | jmp_to_linux: 27 | # switch to new gdt + data segments 28 | cli 29 | lgdt [rcx] 30 | #xor eax, eax 31 | mov eax, 0x18 32 | mov ds, eax 33 | mov ss, eax 34 | mov es, eax 35 | mov fs, eax 36 | mov gs, eax 37 | 38 | # switch to our own page tables (in low mem) 39 | mov cr3, rdx 40 | 41 | # now we're on our own page tables, so we can obliterate the rest of memory 42 | # TODO make sure we don't inadvertently overwrite (important) smap regions 43 | # I think on ps4 we'll actually want to load to 0x700000 44 | # since we have tons of free room there. 45 | # on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We 46 | # should choose a higher address as pref_address. 47 | 48 | # save args 49 | mov r12, rdi 50 | mov r13, rsi 51 | 52 | # memmove(pref_address, , (syssize * 0x10) / 8) 53 | #mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go 54 | #mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself) 55 | mov rdi, 0x6000000 # should be far from bzImage and initramfs 56 | mov r14, rdi # r14 = pref_address 57 | xor edx, edx 58 | mov dl, [r12 + setup_sects] 59 | inc rdx 60 | shl rdx, 9 # rdx = offsetof(image_base, startup_32) 61 | lea rsi, [r12 + rdx] # src = image_base + startup_32 62 | mov ecx, [r12 + shdr_syssize] 63 | shl rcx, 4 64 | add rdi, rcx 65 | add rsi, rcx 66 | sub rdi, 8 67 | sub rsi, 8 68 | shr rcx, 3 69 | std 70 | rep movsq 71 | cld 72 | 73 | # make a tiny stack - we just need it for the lretq. 74 | # what we jump to will not use this stack 75 | lea rsp, [rip + jmp_to_linux_end + 0x200] 76 | and rsp, -0x10 77 | #push 0 # retaddr 78 | push 0x10 # cs = GDT[2] 79 | add r14, 0x200 # pref_address + startup_64 80 | push r14 # rip 81 | mov rsi, r13 # bootargs 82 | lretq 83 | jmp_to_linux_end: 84 | 85 | .data 86 | 87 | .globl jmp_to_linux_size 88 | jmp_to_linux_size: .quad jmp_to_linux_end - jmp_to_linux 89 | 90 | .att_syntax prefix -------------------------------------------------------------------------------- /magic.h: -------------------------------------------------------------------------------- 1 | #ifdef PS4_3_55 2 | 3 | #define kern_off_printf 0x1df550 4 | #define kern_off_copyin 0x3b96e0 5 | #define kern_off_copyout 0x3b9660 6 | #define kern_off_copyinstr 0x3b9a50 7 | #define kern_off_kmem_alloc_contig 0x337ea0 8 | #define kern_off_kmem_free 0x33bca0 9 | #define kern_off_pmap_extract 0x3afd70 10 | #define kern_off_pmap_protect 0x3b1f50 11 | #define kern_off_sched_pin 0x1ced60 12 | #define kern_off_sched_unpin 0x1cedc0 13 | #define kern_off_smp_rendezvous 0x1e7810 14 | #define kern_off_smp_no_rendevous_barrier 0x1e75d0 15 | #define kern_off_icc_query_nowait 0x3ed450 16 | #define kern_off_kernel_map 0x196acc8 17 | #define kern_off_sysent 0xeed880 18 | #define kern_off_kernel_pmap_store 0x19bd628 19 | #define kern_off_Starsha_UcodeInfo 0x1869fa0 20 | 21 | #define kern_off_pml4pml4i 0x19bd618 22 | #define kern_off_dmpml4i 0x19bd61c 23 | #define kern_off_dmpdpi 0x19bd620 24 | 25 | #elif defined PS4_3_70 26 | 27 | #define kern_off_printf 0x1df620 28 | #define kern_off_copyin 0x3b97d0 29 | #define kern_off_copyout 0x3b9750 30 | #define kern_off_copyinstr 0x3b9b40 31 | #define kern_off_kmem_alloc_contig 0x337f70 32 | #define kern_off_kmem_free 0x33bd70 33 | #define kern_off_pmap_extract 0x3afe60 34 | #define kern_off_pmap_protect 0x3b2040 35 | #define kern_off_sched_pin 0x1cee30 36 | #define kern_off_sched_unpin 0x1cee90 37 | #define kern_off_smp_rendezvous 0x1e78e0 38 | #define kern_off_smp_no_rendevous_barrier 0x1e76a0 39 | #define kern_off_icc_query_nowait 0x3ed7f0 40 | #define kern_off_kernel_map 0x1976cc8 41 | #define kern_off_sysent 0xef6d90 42 | #define kern_off_kernel_pmap_store 0x19c9628 43 | #define kern_off_Starsha_UcodeInfo 0 44 | #define kern_off_gpu_devid_is_9924 0x443a20 45 | #define kern_off_gc_get_fw_info 0x44b5a0 46 | 47 | #define kern_off_pml4pml4i 0x19c9618 48 | #define kern_off_dmpml4i 0x19c961c 49 | #define kern_off_dmpdpi 0x19c9620 50 | 51 | #elif defined PS4_4_00 || PS4_4_01 52 | 53 | #define kern_off_printf 0x347450 54 | #define kern_off_copyin 0x286cc0 55 | #define kern_off_copyout 0x286c40 56 | #define kern_off_copyinstr 0x287030 57 | #define kern_off_kmem_alloc_contig 0x275da0 58 | #define kern_off_kmem_free 0x369580 59 | #define kern_off_pmap_extract 0x3eeed0 60 | #define kern_off_pmap_protect 0x3f1120 61 | #define kern_off_sched_pin 0x1d1120 62 | #define kern_off_sched_unpin 0x1d1180 63 | #define kern_off_smp_rendezvous 0x34a020 64 | #define kern_off_smp_no_rendevous_barrier 0x349de0 65 | #define kern_off_icc_query_nowait 0x46c5a0 66 | #define kern_off_kernel_map 0x1fe71b8 67 | #define kern_off_sysent 0xf17790 68 | #define kern_off_kernel_pmap_store 0x200c310 69 | #define kern_off_Starsha_UcodeInfo 0x18dafb0 70 | 71 | #define kern_off_pml4pml4i 0x200c300 72 | #define kern_off_dmpml4i 0x200c304 73 | #define kern_off_dmpdpi 0x200c308 74 | 75 | #elif defined PS4_4_05 76 | 77 | #define kern_off_printf 0x347580 78 | #define kern_off_copyin 0x286df0 79 | #define kern_off_copyout 0x286d70 80 | #define kern_off_copyinstr 0x287160 81 | #define kern_off_kmem_alloc_contig 0x275ed0 82 | #define kern_off_kmem_free 0x3696b0 83 | #define kern_off_pmap_extract 0x3ef000 84 | #define kern_off_pmap_protect 0x3f1250 85 | #define kern_off_sched_pin 0x1d1250 86 | #define kern_off_sched_unpin 0x1d12B0 87 | #define kern_off_smp_rendezvous 0x34a150 88 | #define kern_off_smp_no_rendevous_barrier 0x349f10 89 | #define kern_off_icc_query_nowait 0x46c6d0 90 | #define kern_off_kernel_map 0x1fe71b8 91 | #define kern_off_sysent 0xf17790 92 | #define kern_off_kernel_pmap_store 0x200c310 93 | #define kern_off_Starsha_UcodeInfo 0 94 | #define kern_off_gpu_devid_is_9924 0x4b9030 95 | #define kern_off_gc_get_fw_info 0x4a19a0 96 | 97 | #define kern_off_pml4pml4i 0x200c300 98 | #define kern_off_dmpml4i 0x200c304 99 | #define kern_off_dmpdpi 0x200c308 100 | 101 | #elif defined PS4_4_55 102 | 103 | #define kern_off_printf 0x17F30 104 | #define kern_off_copyin 0x14A890 105 | #define kern_off_copyout 0x14A7B0 106 | #define kern_off_copyinstr 0x14AD00 107 | #define kern_off_kmem_alloc_contig 0x250320 108 | #define kern_off_kmem_free 0x16EEA0 109 | #define kern_off_pmap_extract 0x41DBC0 110 | #define kern_off_pmap_protect 0x420310 111 | #define kern_off_sched_pin 0x73770 112 | #define kern_off_sched_unpin 0x73780 113 | #define kern_off_smp_rendezvous 0xB2BB0 114 | #define kern_off_smp_no_rendevous_barrier 0xB2970 115 | #define kern_off_icc_query_nowait 0x808C0 116 | #define kern_off_kernel_map 0x1B31218 117 | #define kern_off_sysent 0x102B690 118 | #define kern_off_kernel_pmap_store 0x21BCC38 119 | #define kern_off_Starsha_UcodeInfo 0 120 | #define kern_off_gpu_devid_is_9924 0x496720 121 | #define kern_off_gc_get_fw_info 0x4A12D0 122 | 123 | #define kern_off_pml4pml4i 0x21BCC28 124 | #define kern_off_dmpml4i 0x21BCC2C 125 | #define kern_off_dmpdpi 0x21BCC30 126 | 127 | #elif defined PS4_5_01 128 | 129 | #define kern_off_printf 0x00435C70 130 | #define kern_off_copyin 0x1EA600 131 | #define kern_off_copyout 0x1EA520 132 | #define kern_off_copyinstr 0x1EAA30 133 | #define kern_off_kmem_alloc_contig 0xF1B80 134 | #define kern_off_kmem_free 0xFCD40 135 | #define kern_off_pmap_extract 0x2E02A0 136 | #define kern_off_pmap_protect 0x2E2D00 137 | #define kern_off_sched_pin 0x31FB70 138 | #define kern_off_sched_unpin 0x31FB80 139 | #define kern_off_smp_rendezvous 0x1B84A0 140 | #define kern_off_smp_no_rendevous_barrier 0x1B8260 141 | #define kern_off_icc_query_nowait 0x44020 142 | #define kern_off_kernel_map 0x1AC60E0 143 | #define kern_off_sysent 0x107C610 144 | #define kern_off_kernel_pmap_store 0x22CB4F0 145 | #define kern_off_Starsha_UcodeInfo 0 146 | #define kern_off_gpu_devid_is_9924 0x4DDC40 147 | #define kern_off_gc_get_fw_info 0x4D33D0 148 | 149 | #define kern_off_pml4pml4i 0x22CB4E0 150 | #define kern_off_dmpml4i 0x22CB4E4 151 | #define kern_off_dmpdpi 0x22CB4E8 152 | 153 | #elif defined PS4_5_05 154 | #define kern_off_printf 0x436040 155 | #define kern_off_snprintf 0x436350 156 | #define kern_off_copyin 0x1EA710 157 | #define kern_off_copyout 0x1EA630 158 | #define kern_off_copyinstr 0x1EAB40 159 | #define kern_off_kmem_alloc_contig 0xF1C90 160 | #define kern_off_kmem_free 0xFCE50 161 | #define kern_off_pmap_extract 0x2E0570 162 | #define kern_off_pmap_protect 0x2E3090 163 | #define kern_off_sched_pin 0x31FF40 164 | #define kern_off_sched_unpin 0x31FF50 165 | #define kern_off_smp_rendezvous 0x1B85B0 166 | #define kern_off_smp_no_rendevous_barrier 0x1B8370 167 | #define kern_off_icc_query_nowait 0x44020 168 | #define kern_off_kernel_map 0x1AC60E0 169 | #define kern_off_sysent 0x107C610 170 | #define kern_off_kernel_pmap_store 0x22CB570 171 | #define kern_off_Starsha_UcodeInfo 0 172 | #define kern_off_gpu_devid_is_9924 0x4DE010 173 | #define kern_off_gc_get_fw_info 0x4D37A0 174 | #define kern_off_pml4pml4i 0x22CB560 175 | #define kern_off_dmpml4i 0x22CB564 176 | #define kern_off_dmpdpi 0x22CB568 177 | #define kern_off_eap_hdd_key 0x2790C90 178 | #define kern_off_edid 0x281A3F4 179 | #define kern_off_wlanbt 0x2BFBA0 180 | #define kern_off_kern_reboot 0x10D390 181 | 182 | #elif defined PS4_6_72 183 | #define kern_off_printf 0x123280 184 | #define kern_off_snprintf 0x123590 185 | #define kern_off_copyin 0x3C17A0 186 | #define kern_off_copyout 0x3C16B0 187 | #define kern_off_copyinstr 0x3C1C50 188 | #define kern_off_kmem_alloc_contig 0x0B71C0 189 | #define kern_off_kmem_free 0x250900 190 | #define kern_off_pmap_extract 0x04E420 191 | #define kern_off_pmap_protect 0x050F50 192 | #define kern_off_sched_pin 0x446A30 193 | #define kern_off_sched_unpin 0x446A40 194 | #define kern_off_smp_rendezvous 0x2C93C0 195 | #define kern_off_smp_no_rendevous_barrier 0x2C9180 196 | #define kern_off_icc_query_nowait 0x3F290 197 | #define kern_off_kernel_map 0x220DFC0 198 | #define kern_off_sysent 0x111E000 199 | #define kern_off_kernel_pmap_store 0x1BB7880 200 | #define kern_off_Starsha_UcodeInfo 0 201 | #define kern_off_gpu_devid_is_9924 0x4E82D0 202 | #define kern_off_gc_get_fw_info 0x4E1A50 203 | #define kern_off_pml4pml4i 0x1BB7870 204 | #define kern_off_dmpml4i 0x1BB7874 205 | #define kern_off_dmpdpi 0x1BB7878 206 | #define kern_off_eap_hdd_key 0x26DCCD0 207 | #define kern_off_edid 0x2764100 208 | #define kern_off_wlanbt 0x1CDD80 209 | #define kern_off_kern_reboot 0x206D50 210 | 211 | #elif defined PS4_7_00 212 | //credit: https://github.com/tihmstar 213 | #define kern_off_printf 0x000BC730 //mira 214 | #define kern_off_snprintf 0x000BCA30 //mira 215 | #define kern_off_copyin 0x0002F230 //mira 216 | #define kern_off_copyout 0x0002F140 //mira 217 | #define kern_off_copyinstr 0x0002F6E0 //mira 218 | #define kern_off_kmem_alloc_contig 0x430780 //unsure 219 | #define kern_off_kmem_free 0x001172C0 //mira 220 | #define kern_off_pmap_extract 0x3ded30 221 | #define kern_off_pmap_protect 0x3dff70 222 | #define kern_off_sched_pin 0x329870 223 | #define kern_off_sched_unpin 0x329890 224 | #define kern_off_smp_rendezvous 0x4447a0 225 | #define kern_off_smp_no_rendevous_barrier 0x444560 226 | #define kern_off_icc_query_nowait 0x3b2f80 227 | #define kern_off_kernel_map 0x021C8EE0 //mira 228 | #define kern_off_sysent 0x01125660 //mira 229 | #define kern_off_kernel_pmap_store 0x22c5268 230 | #define kern_off_Starsha_UcodeInfo 0 231 | #define kern_off_gpu_devid_is_9924 0x4ee3c0 232 | #define kern_off_gc_get_fw_info 0x4f6830 233 | #define kern_off_pml4pml4i 0x22c5258 234 | #define kern_off_dmpml4i 0x22c525c 235 | #define kern_off_dmpdpi 0x22c5260 236 | #define kern_off_eap_hdd_key 0x26e0cd0 237 | #define kern_off_edid 0x27645e0 238 | #define kern_off_wlanbt 0xdecb0 239 | #define kern_off_kern_reboot 0x002CD780 //mira 240 | 241 | #elif defined PS4_7_55 242 | #define kern_off_printf 0x26F740 //mira 243 | #define kern_off_snprintf 0x26FA40 //mira 244 | #define kern_off_copyin 0x28F9F0 //mira 245 | #define kern_off_copyout 0x28F900 //mira 246 | #define kern_off_copyinstr 0x28FEA0 //mira 247 | #define kern_off_kmem_alloc_contig 0x49DF40 248 | #define kern_off_kmem_free 0x1755B0 //mira 249 | #define kern_off_pmap_extract 0x1A6D70 250 | #define kern_off_pmap_protect 0x1A7F10 251 | #define kern_off_sched_pin 0x191410 252 | #define kern_off_sched_unpin 0x191430 253 | #define kern_off_smp_rendezvous 0x26CEC0 254 | #define kern_off_smp_no_rendevous_barrier 0x26CC90 255 | #define kern_off_icc_query_nowait 0x1629D0 256 | #define kern_off_kernel_map 0x21405B8 //mira 257 | #define kern_off_sysent 0x1122340 //mira 258 | #define kern_off_kernel_pmap_store 0x215EA40 259 | #define kern_off_Starsha_UcodeInfo 0 260 | #define kern_off_gpu_devid_is_9924 0x4E4560 261 | #define kern_off_gc_get_fw_info 0x4F8FE0 262 | #define kern_off_pml4pml4i 0x215EA30 // (lea rdx, kern_off_pml4pml4i => movsxd rdi, cs:kern_off_pml4pml4i)c 263 | #define kern_off_dmpml4i 0x215EA34 264 | #define kern_off_dmpdpi 0x215EA38 265 | #define kern_off_eap_hdd_key 0x26D4C90 266 | #define kern_off_edid 0x275C0D0 267 | #define kern_off_wlanbt 0xE3B70 268 | #define kern_off_kern_reboot 0x02CD780 // mira 269 | 270 | #elif defined PS4_9_00 271 | #define kern_off_printf 0x0B7A30 //mira 272 | #define kern_off_snprintf 0x0B7D30 //mira 273 | #define kern_off_copyin 0x2716A0 //mira 274 | #define kern_off_copyout 0x2715B0 //mira 275 | #define kern_off_copyinstr 0x271B50 //mira 276 | #define kern_off_kmem_alloc_contig 0x270880 277 | #define kern_off_kmem_free 0x37C040 //mira 278 | #define kern_off_pmap_extract 0x12D050 279 | #define kern_off_pmap_protect 0x12E1F0 280 | #define kern_off_sched_pin 0x1CD0D0 281 | #define kern_off_sched_unpin 0x1CD0F0 282 | #define kern_off_smp_rendezvous 0x432BF0 283 | #define kern_off_smp_no_rendevous_barrier 0x432A00 284 | #define kern_off_icc_query_nowait 0x2E1760 285 | #define kern_off_kernel_map 0x2268D48 //mira 286 | #define kern_off_sysent 0x1100310 //mira 287 | #define kern_off_kernel_pmap_store 0x1B904B0 288 | #define kern_off_Starsha_UcodeInfo 0 289 | #define kern_off_gpu_devid_is_9924 0x4AC260 290 | #define kern_off_gc_get_fw_info 0x4DF280 291 | #define kern_off_pml4pml4i 0x1B904A0 // (lea rdx, kern_off_pml4pml4i => movsxd rdi, cs:kern_off_pml4pml4i)c 292 | #define kern_off_dmpml4i 0x1B904A4 293 | #define kern_off_dmpdpi 0x1B904A8 294 | #define kern_off_eap_hdd_key 0x26C4C90 295 | #define kern_off_edid 0x274C058 296 | #define kern_off_wlanbt 0x180860 297 | #define kern_off_kern_reboot 0x29A380 // mira 298 | #define kern_off_set_gpu_freq 0x4DDDC0 299 | #define kern_off_set_pstate 0x4D6FC0 300 | #define kern_off_update_vddnp 0x4DE360 301 | #define kern_off_set_cu_power_gate 0x4DE770 302 | #define kern_off_pstate_before_shutdown 0x29A970 303 | #endif 304 | -------------------------------------------------------------------------------- /reboot.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-3-Clause 3 | * 4 | * Copyright (c) 1982, 1986, 1988, 1993, 1994 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)reboot.h 8.3 (Berkeley) 12/13/94 32 | * $FreeBSD$ 33 | */ 34 | 35 | #ifndef _SYS_REBOOT_H_ 36 | #define _SYS_REBOOT_H_ 37 | 38 | /* 39 | * Arguments to reboot system call. These are passed to 40 | * the boot program and on to init. 41 | */ 42 | #define RB_AUTOBOOT 0 /* flags for system auto-booting itself */ 43 | 44 | #define RB_ASKNAME 0x001 /* force prompt of device of root filesystem */ 45 | #define RB_SINGLE 0x002 /* reboot to single user only */ 46 | #define RB_NOSYNC 0x004 /* dont sync before reboot */ 47 | #define RB_HALT 0x008 /* don't reboot, just halt */ 48 | #define RB_INITNAME 0x010 /* Unused placeholder to specify init path */ 49 | #define RB_DFLTROOT 0x020 /* use compiled-in rootdev */ 50 | #define RB_KDB 0x040 /* give control to kernel debugger */ 51 | #define RB_RDONLY 0x080 /* mount root fs read-only */ 52 | #define RB_DUMP 0x100 /* dump kernel memory before reboot */ 53 | #define RB_MINIROOT 0x200 /* Unused placeholder */ 54 | #define RB_VERBOSE 0x800 /* print all potentially useful info */ 55 | #define RB_SERIAL 0x1000 /* use serial port as console */ 56 | #define RB_CDROM 0x2000 /* use cdrom as root */ 57 | #define RB_POWEROFF 0x4000 /* turn the power off if possible */ 58 | #define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */ 59 | #define RB_MUTE 0x10000 /* start up with the console muted */ 60 | #define RB_SELFTEST 0x20000 /* unused placeholder */ 61 | #define RB_RESERVED1 0x40000 /* reserved for internal use of boot blocks */ 62 | #define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ 63 | #define RB_PAUSE 0x100000 /* pause after each output line during probe */ 64 | #define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ 65 | #define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ 66 | #define RB_PROBE 0x10000000 /* Probe multiple consoles */ 67 | #define RB_MULTIPLE 0x20000000 /* use multiple consoles */ 68 | 69 | #define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ 70 | 71 | #endif -------------------------------------------------------------------------------- /string.h: -------------------------------------------------------------------------------- 1 | /* 2 | * string.h -- standard C string-manipulation functions. 3 | * 4 | * Copyright (C) 2008 Segher Boessenkool 5 | * Copyright (C) 2009 Haxx Enterprises 6 | * Copyright (C) 2010-2016 Hector Martin "marcan" 7 | * 8 | * Portions taken from the Public Domain C Library (PDCLib). 9 | * http://pdclib.rootdirectory.de/ 10 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 11 | * file for more information. 12 | */ 13 | 14 | #ifndef STRING_H 15 | #define STRING_H 16 | 17 | #include "types.h" 18 | 19 | static inline int strcmp(const char *s1, const char *s2) 20 | { 21 | size_t i; 22 | 23 | for (i = 0; s1[i] && s1[i] == s2[i]; i++) 24 | ; 25 | 26 | return s1[i] - s2[i]; 27 | } 28 | 29 | static inline void *memset(void *b, int c, size_t len) 30 | { 31 | size_t i; 32 | 33 | for (i = 0; i < len; i++) 34 | ((unsigned char *)b)[i] = c; 35 | 36 | return b; 37 | } 38 | 39 | static inline void *memcpy(void *dst, const void *src, size_t len) 40 | { 41 | size_t i; 42 | 43 | for (i = 0; i < len; i++) 44 | ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; 45 | 46 | return dst; 47 | } 48 | 49 | static inline int memcmp(const void *s1, const void *s2, size_t len) 50 | { 51 | size_t i; 52 | const unsigned char *p1 = (const unsigned char *)s1; 53 | const unsigned char *p2 = (const unsigned char *)s2; 54 | 55 | for (i = 0; i < len; i++) 56 | if (p1[i] != p2[i]) 57 | return p1[i] - p2[i]; 58 | 59 | return 0; 60 | } 61 | 62 | static inline size_t strlen(const char *s) 63 | { 64 | size_t len; 65 | 66 | for (len = 0; s[len]; len++) 67 | ; 68 | 69 | return len; 70 | } 71 | 72 | static inline size_t strnlen(const char *s, size_t count) 73 | { 74 | size_t len; 75 | 76 | for (len = 0; s[len] && len < count; len++) 77 | ; 78 | 79 | return len; 80 | } 81 | 82 | static inline size_t strlcpy(char *dest, const char *src, size_t maxlen) 83 | { 84 | size_t len, needed; 85 | 86 | len = needed = strnlen(src, maxlen - 1) + 1; 87 | if (len >= maxlen) 88 | len = maxlen - 1; 89 | 90 | memcpy(dest, src, len); 91 | dest[len] = 0; 92 | 93 | return needed - 1; 94 | } 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #ifndef TYPES_H 12 | #define TYPES_H 13 | 14 | typedef signed char s8; 15 | typedef signed short s16; 16 | typedef signed int s32; 17 | typedef signed long long s64; 18 | typedef unsigned char u8; 19 | typedef unsigned short u16; 20 | typedef unsigned int u32; 21 | typedef unsigned long long u64; 22 | #ifndef TESTING 23 | typedef u8 uint8_t; 24 | typedef u64 size_t; 25 | typedef s64 ssize_t; 26 | typedef u64 uintptr_t; 27 | typedef s64 off_t; 28 | #endif 29 | 30 | #define NULL ((void *)0) 31 | 32 | #define CAT_(x, y) x ## y 33 | #define CAT(x, y) CAT_(x, y) 34 | 35 | #define OPAD(size) u8 CAT(_pad_, __COUNTER__)[size] 36 | #define OSTRUCT(name, size) struct name { union { OPAD(size); 37 | #define OSTRUCT_END };}; 38 | #define OFIELD(off, field) struct { OPAD(off); field; } 39 | 40 | #define ASSERT_STRSIZE(struc, size) \ 41 | _Static_assert(sizeof( struc ) == (size), "size of " #struc " != " #size ) 42 | 43 | #define offsetof(type, member) __builtin_offsetof (type, member) 44 | 45 | #define ENOENT 2 46 | #define ENOMEM 12 47 | #define EFAULT 14 48 | #define EINVAL 22 49 | #define ENAMETOOLONG 63 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /uart.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #include "uart.h" 12 | #include "kernel.h" 13 | #define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr))) 14 | 15 | #define AEOLIA_UART_BASE 0xD0340000 16 | #define BAIKAL_UART_BASE 0xC890E000 17 | 18 | #define UART_REG(size, intf, reg) PHYS_TO_DMAP(size, BAIKAL_UART_BASE + (intf << 12) + (reg << 2)) 19 | 20 | #define UART_REG_DATA 0 21 | #define UART_REG_IER 1 22 | #define UART_REG_IIR 2 23 | #define UART_REG_LCR 3 24 | #define UART_REG_MCR 4 25 | #define UART_REG_LSR 5 26 | # define LSR_TXRDY 0x20 27 | # define LSR_TEMT 0x40 28 | #define UART_REG_MSR 6 29 | #define UART0_DATA UART_REG( 8, 0, UART_REG_DATA) 30 | #define UART0_IER UART_REG(32, 0, UART_REG_IER) 31 | #define UART0_IIR UART_REG(32, 0, UART_REG_IIR) 32 | #define UART0_LCR UART_REG(32, 0, UART_REG_LCR) 33 | #define UART0_MCR UART_REG(32, 0, UART_REG_MCR) 34 | #define UART0_LSR UART_REG(32, 0, UART_REG_LSR) 35 | #define UART0_MSR UART_REG(32, 0, UART_REG_MSR) 36 | 37 | void uart_write_byte(u8 b) 38 | { 39 | int limit; 40 | u64 barrier; 41 | limit = 250000; 42 | while (!(*UART0_LSR & LSR_TXRDY) && --limit) 43 | ; 44 | *UART0_DATA = b; 45 | __sync_fetch_and_add(&barrier, 0); 46 | limit = 250000; 47 | while (!(*UART0_LSR & LSR_TEMT) && --limit) 48 | ; 49 | } 50 | 51 | void uart_write_char(char c) 52 | { 53 | if (c == '\n') 54 | uart_write_byte('\r'); 55 | 56 | uart_write_byte(c); 57 | } 58 | 59 | void uart_write_str(const char *s) 60 | { 61 | while (*s) { 62 | uart_write_char(*s++); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /uart.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #ifndef UART_H 12 | #define UART_H 13 | 14 | #include "types.h" 15 | 16 | void uart_write_byte(u8 b); 17 | void uart_write_char(char c); 18 | void uart_write_str(const char *s); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /x86.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps4-kexec - a kexec() implementation for Orbis OS / FreeBSD 3 | * 4 | * Copyright (C) 2015-2016 shuffle2 5 | * Copyright (C) 2015-2016 Hector Martin "marcan" 6 | * 7 | * This code is licensed to you under the 2-clause BSD license. See the LICENSE 8 | * file for more information. 9 | */ 10 | 11 | #ifndef X86_H 12 | #define X86_H 13 | 14 | #define FLAGS_IF (1 << 9) 15 | 16 | #define CR0_WP (1 << 16) 17 | #define CR0_NW (1 << 29) 18 | #define CR0_CD (1 << 30) 19 | 20 | #define PG_V (1 << 0) 21 | #define PG_RW (1 << 1) 22 | #define PG_U (1 << 2) 23 | #define PG_PS (1 << 7) 24 | 25 | #define SEG_TYPE_DATA (0 << 3) 26 | #define SEG_TYPE_READ_WRITE (1 << 1) 27 | #define SEG_TYPE_CODE (1 << 3) 28 | #define SEG_TYPE_EXEC_READ (1 << 1) 29 | #define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) 30 | 31 | static inline u64 cr0_read(void) 32 | { 33 | u64 reg; 34 | asm volatile("mov %0, cr0;" : "=r" (reg)); 35 | return reg; 36 | } 37 | 38 | static inline void cr0_write(u64 val) 39 | { 40 | asm volatile("mov cr0, %0;" :: "r" (val)); 41 | } 42 | 43 | static inline u64 write_protect_disable() 44 | { 45 | u64 cr0 = cr0_read(); 46 | cr0_write(cr0 & ~CR0_WP); 47 | return cr0; 48 | } 49 | 50 | static inline void write_protect_restore(u64 cr0) 51 | { 52 | // Use only WP bit of input 53 | cr0_write(cr0_read() | (cr0 & CR0_WP)); 54 | } 55 | 56 | static inline u64 cr3_read(void) 57 | { 58 | u64 reg; 59 | asm volatile("mov %0, cr3;" : "=r" (reg)); 60 | return reg; 61 | } 62 | 63 | static inline void cr3_write(u64 val) 64 | { 65 | asm volatile("mov cr3, %0;" :: "r" (val)); 66 | } 67 | 68 | typedef struct { 69 | u16 limit; 70 | u64 address; 71 | } __attribute__((packed)) desc_ptr; 72 | 73 | static inline desc_ptr gdt_read(void) 74 | { 75 | desc_ptr gdtr; 76 | asm volatile("sgdt %0;" : "=m" (gdtr)); 77 | return gdtr; 78 | } 79 | 80 | static inline void gdt_write(desc_ptr* val) 81 | { 82 | asm volatile("lgdt %0;" :: "m" (*val)); 83 | } 84 | 85 | //IDT 86 | typedef struct { 87 | u16 limit; 88 | u64 address; 89 | } __attribute__((packed)) idt_ptr; 90 | 91 | static inline idt_ptr idt_read(void) 92 | { 93 | idt_ptr idtr; 94 | asm volatile("sidt %0;" : "=m" (idtr)); 95 | return idtr; 96 | } 97 | 98 | static inline void idt_write(idt_ptr* val) 99 | { 100 | asm volatile("lidt %0;" :: "m" (*val)); 101 | } 102 | 103 | static inline void cr4_pge_disable(void) 104 | { 105 | u64 cr4_temp; 106 | asm volatile( 107 | "mov %0, cr4;" 108 | "and %0, ~0x80;" 109 | "mov cr4, %0;" 110 | : "=r" (cr4_temp) 111 | ); 112 | } 113 | 114 | static inline void wbinvd(void) 115 | { 116 | asm volatile("wbinvd"); 117 | } 118 | 119 | static inline void cpu_stop(void) 120 | { 121 | for (;;) 122 | asm volatile("cli; hlt;" : : : "memory"); 123 | } 124 | 125 | static inline void outl(int port, unsigned int data) 126 | { 127 | asm volatile("out %w1, %0" : : "a" (data), "d" (port)); 128 | } 129 | 130 | static inline void wrmsr(u32 msr_id, u64 msr_value) 131 | { 132 | asm volatile( 133 | "wrmsr" 134 | : 135 | : "c" (msr_id), "a" (msr_value & 0xffffffff), "d" (msr_value >> 32) 136 | ); 137 | } 138 | 139 | static inline u64 rdmsr(u64 msr_id) 140 | { 141 | u32 low, high; 142 | asm volatile ( 143 | "rdmsr" 144 | : "=a"(low), "=d"(high) 145 | : "c"(msr_id) 146 | ); 147 | return ((u64)high << 32) | low; 148 | } 149 | 150 | static inline u64 rdtsc (void) 151 | { 152 | unsigned int tickl, tickh; 153 | asm volatile( 154 | "rdtsc" 155 | :"=a"(tickl),"=d"(tickh) 156 | ); 157 | return ((u64)tickh << 32) | tickl; 158 | } 159 | 160 | static inline void udelay(unsigned int usec) { 161 | u64 later = rdtsc() + usec * 1594ULL; 162 | while (((s64)(later - rdtsc())) > 0); 163 | } 164 | 165 | static inline void disable_interrupts(void) 166 | { 167 | asm volatile("cli"); 168 | } 169 | 170 | static inline void enable_interrupts(void) 171 | { 172 | asm volatile("sti"); 173 | } 174 | 175 | static inline u64 read_flags(void) 176 | { 177 | u64 flags; 178 | asm volatile("pushf; pop %0;" : "=r" (flags)); 179 | return flags; 180 | } 181 | 182 | static inline u64 intr_disable(void) 183 | { 184 | u64 flags = read_flags(); 185 | disable_interrupts(); 186 | return flags; 187 | } 188 | 189 | static inline void intr_restore(u64 flags) 190 | { 191 | // TODO should only IF be or'd in? 192 | asm volatile("push %0; popf;" : : "rm" (flags) : "memory"); 193 | } 194 | 195 | #endif 196 | --------------------------------------------------------------------------------