├── .cargo └── config.toml ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode └── settings.json ├── COPYING ├── Cargo.toml ├── README.md ├── build.py ├── build.rs ├── kernel-dumper.ld ├── korbis-1100 ├── Cargo.toml ├── build.rs └── src │ ├── file.rs │ ├── lib.rs │ ├── thread.rs │ └── uio.rs ├── korbis-macros ├── Cargo.toml └── src │ ├── lib.rs │ └── offset.rs ├── korbis ├── Cargo.toml └── src │ ├── elf │ └── mod.rs │ ├── file │ └── mod.rs │ ├── lib.rs │ ├── thread │ └── mod.rs │ └── uio │ └── mod.rs ├── rust-toolchain.toml └── src ├── direct.rs ├── main.rs ├── method.rs └── syscall.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-none" 3 | rustflags = ["--cfg", "fw=\"1100\"", "--cfg", "method=\"direct\""] 4 | 5 | [unstable] 6 | build-std = ["alloc", "core", "panic_abort"] 7 | build-std-features = ["panic_immediate_abort"] 8 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | jobs: 10 | build: 11 | name: Build 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout source 15 | uses: actions/checkout@v4 16 | - name: Check Rust styles 17 | run: cargo fmt --check 18 | - name: Install Rust components 19 | run: rustup component add rust-src llvm-tools 20 | - name: Build 21 | run: ./build.py 22 | - name: Upload artifact 23 | uses: actions/upload-artifact@v4 24 | with: 25 | name: kernel-dumper 26 | path: kernel-dumper.bin 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Cargo.lock 2 | /kernel-dumper.bin 3 | /target/ 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[rust]": { 3 | "editor.formatOnSave": true 4 | }, 5 | "files.insertFinalNewline": true, 6 | "files.trimFinalNewlines": true, 7 | "files.trimTrailingWhitespace": true, 8 | "rust-analyzer.cargo.allTargets": false, 9 | "rust-analyzer.cargo.extraArgs": [ 10 | "-Z", "build-std=std,panic_abort", 11 | "-Z", "build-std-features=panic_immediate_abort" 12 | ], 13 | "rust-analyzer.imports.granularity.enforce": true, 14 | "rust-analyzer.imports.granularity.group": "module", 15 | "rust-analyzer.imports.group.enable": false 16 | } 17 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kernel-dumper" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | bitflags = "2.5.0" 8 | korbis = { path = "korbis" } 9 | x86_64 = { version = "0.15.1", features = ["instructions"], default-features = false } 10 | 11 | [target.'cfg(fw = "1100")'.dependencies] 12 | korbis-1100 = { path = "korbis-1100" } 13 | 14 | [profile.release] 15 | opt-level = "z" 16 | 17 | [workspace] 18 | members = ["korbis", "korbis-1100", "korbis-macros"] 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kernel Dumper 2 | [![CI](https://github.com/obhq/kernel-dumper/actions/workflows/ci.yml/badge.svg)](https://github.com/obhq/kernel-dumper/actions/workflows/ci.yml) 3 | 4 | Kernel Dumper is a payload for PS4 kernel to dump the kernel. Only 11.00 is supported. 5 | 6 | ## Setup 7 | 8 | Plug a USB drive to the PS4 and make sure the PS4 can write some files to it. You can test this by copy some game screenshots to it to see if it success without any errors. 9 | 10 | ## Running 11 | 12 | You need to use TheFloW [PPPwn](https://github.com/TheOfficialFloW/PPPwn) with `--stage2` pointed to `kernel-dumper.bin` like the following: 13 | 14 | ```sh 15 | sudo python3 pppwn.py --interface=enp0s3 --fw=1100 --stage2=kernel-dumper.bin 16 | ``` 17 | 18 | Wait for a notification `Dump completed!`. This may take a couple of minutes depend on how fast is your USB drive. Then shutdown the PS4 (not putting it into rest mode). Once the PS4 completely shutdown unplug the USB drive to grab `kernel.elf`. 19 | 20 | To load this dump in Ghidra you need to load as a `Raw Binary` with a correct `Base Address` and `x86:default:64:little:gcc` as a `Language`. Use `readelf` to find a value for `Base Address`: 21 | 22 | ```sh 23 | readelf -l kernel.elf 24 | ``` 25 | 26 | It will output something like: 27 | 28 | ``` 29 | Elf file type is EXEC (Executable file) 30 | Entry point 0xffffffff9b68c8b0 31 | There are 6 program headers, starting at offset 64 32 | 33 | Program Headers: 34 | Type Offset VirtAddr PhysAddr 35 | FileSiz MemSiz Flags Align 36 | PHDR 0x0000000000000040 0xffffffff9b480040 0xffffffff9b480040 37 | 0x0000000000000150 0x0000000000000150 R 0x8 38 | INTERP 0x0000000000000190 0xffffffff9b480190 0xffffffff9b480190 39 | 0x0000000000000007 0x0000000000000007 R 0x1 40 | [Requesting program interpreter: /DUMMY] 41 | LOAD 0x0000000000000000 0xffffffff9b480000 0xffffffff9b480000 42 | 0x0000000000cfe6d8 0x0000000000cfe6d8 R E 0x200000 43 | LOOS+0x1000010 0x0000000000cff000 0xffffffff9c57f000 0xffffffff9c57f000 44 | 0x0000000000020c70 0x0000000000200000 R 0x200000 45 | LOAD 0x0000000000d20000 0xffffffff9c9a0000 0xffffffff9c9a0000 46 | 0x0000000000605280 0x00000000012fda10 RW 0x200000 47 | DYNAMIC 0x0000000000cfe5c8 0xffffffff9c17e5c8 0xffffffff9c17e5c8 48 | 0x0000000000000110 0x0000000000000110 RW 0x8 49 | ``` 50 | 51 | The value for `Base Address` is `VirtAddr` of the first `LOAD` program (e.g. `0xffffffff9b480000` for the above dump). Each dump will have a different address due to ASLR so you can't use the above information with your dump. 52 | 53 | ## Building from source 54 | 55 | ### Prerequisites 56 | 57 | - Rust on nightly channel 58 | 59 | ### Install additional Rust component 60 | 61 | ```sh 62 | rustup component add rust-src llvm-tools 63 | ``` 64 | 65 | ### Build 66 | 67 | ```sh 68 | ./build.py 69 | ``` 70 | 71 | ## License 72 | 73 | MIT 74 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import json 3 | from subprocess import Popen, PIPE, run 4 | import sys 5 | 6 | # Build. 7 | with Popen(["cargo", "build", "-r", "--message-format", "json-render-diagnostics"], stdout=PIPE) as proc: 8 | for line in proc.stdout: 9 | line = json.loads(line) 10 | reason = line["reason"] 11 | if reason == "build-finished": 12 | if line["success"]: 13 | break 14 | else: 15 | sys.exit(1) 16 | elif reason == "compiler-artifact": 17 | if line["target"]["name"] == "kernel-dumper": 18 | out = line["executable"] 19 | 20 | # Create payload. 21 | run(["rustup", "run", "nightly", "objcopy", "-O", "binary", out, "kernel-dumper.bin"], check=True) 22 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | fn main() { 4 | let root = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()); 5 | let link = root.join("kernel-dumper.ld"); 6 | let link = link.to_str().unwrap(); 7 | 8 | println!("cargo::rustc-link-arg-bins=-T{link}"); 9 | println!("cargo::rustc-check-cfg=cfg(method, values(\"syscall\"))"); 10 | println!("cargo::rustc-check-cfg=cfg(fw, values(\"1100\"))"); 11 | } 12 | -------------------------------------------------------------------------------- /kernel-dumper.ld: -------------------------------------------------------------------------------- 1 | SECTIONS { 2 | .text 0x00 : { 3 | *(.text.entry) 4 | } 5 | 6 | .dynamic 0x80 : { 7 | *(.dynamic) 8 | } 9 | 10 | .text : { 11 | *(.text.*) 12 | } 13 | 14 | .bss : { 15 | *(.bss.*) 16 | } 17 | 18 | .relro : { 19 | *(.rela.dyn) 20 | } 21 | 22 | .rodata : { 23 | *(.rodata.*) 24 | } 25 | 26 | /DISCARD/ : { 27 | *(.dynsym) 28 | *(.gnu.hash) 29 | *(.hash) 30 | *(.dynstr) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /korbis-1100/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "korbis-1100" 3 | version = "0.1.0" 4 | edition = "2021" 5 | links = "orbiskernel" 6 | 7 | [dependencies] 8 | korbis = { path = "../korbis" } 9 | -------------------------------------------------------------------------------- /korbis-1100/build.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /korbis-1100/src/file.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::AtomicU32; 2 | 3 | /// Implementation of [`korbis::file::File`] for 11.00. 4 | #[repr(C)] 5 | pub struct File { 6 | pad: [u8; 0x28], 7 | refcnt: AtomicU32, 8 | } 9 | 10 | impl korbis::file::File for File { 11 | fn refcnt(&self) -> &AtomicU32 { 12 | &self.refcnt 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /korbis-1100/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use self::file::File; 4 | use self::thread::Thread; 5 | use self::uio::Uio; 6 | use core::ffi::{c_char, c_int}; 7 | use korbis::offset; 8 | use korbis::uio::UioSeg; 9 | 10 | mod file; 11 | mod thread; 12 | mod uio; 13 | 14 | /// Implementation of [`korbis::Kernel`] for 11.00. 15 | #[derive(Clone, Copy)] 16 | pub struct Kernel(&'static [u8]); 17 | 18 | impl korbis::Kernel for Kernel { 19 | type File = File; 20 | type Thread = Thread; 21 | type Uio = Uio; 22 | 23 | unsafe fn new(base: *const u8) -> Self { 24 | Self(Self::get_mapped_elf(base)) 25 | } 26 | 27 | unsafe fn elf(self) -> &'static [u8] { 28 | self.0 29 | } 30 | 31 | #[offset(0x4191C0)] 32 | unsafe fn fget_write( 33 | self, 34 | td: *mut Self::Thread, 35 | fd: c_int, 36 | unused: c_int, 37 | fp: *mut *mut Self::File, 38 | ) -> c_int; 39 | 40 | #[offset(0x4161B0)] 41 | unsafe fn fdrop(self, fp: *mut Self::File, td: *mut Self::Thread) -> c_int; 42 | 43 | #[offset(0xE63B0)] 44 | unsafe fn kern_openat( 45 | self, 46 | td: *mut Self::Thread, 47 | fd: c_int, 48 | path: *const c_char, 49 | seg: UioSeg, 50 | flags: c_int, 51 | mode: c_int, 52 | ) -> c_int; 53 | 54 | #[offset(0x416920)] 55 | unsafe fn kern_close(self, td: *mut Self::Thread, fd: c_int) -> c_int; 56 | 57 | #[offset(0xEAD50)] 58 | unsafe fn kern_fsync(self, td: *mut Self::Thread, fd: c_int, fullsync: c_int) -> c_int; 59 | 60 | #[offset(0xDD340)] 61 | unsafe fn kern_writev(self, td: *mut Self::Thread, fd: c_int, auio: *mut Self::Uio) -> c_int; 62 | } 63 | -------------------------------------------------------------------------------- /korbis-1100/src/thread.rs: -------------------------------------------------------------------------------- 1 | /// Implementation of [`korbis::thread::Thread`] for 11.00. 2 | #[repr(C)] 3 | pub struct Thread { 4 | pad: [u8; 0x398], 5 | ret: [usize; 2], // td_retval 6 | } 7 | 8 | impl korbis::thread::Thread for Thread { 9 | fn ret(&self, i: usize) -> usize { 10 | self.ret[i] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /korbis-1100/src/uio.rs: -------------------------------------------------------------------------------- 1 | use crate::thread::Thread; 2 | use crate::Kernel; 3 | use core::ffi::c_int; 4 | use korbis::uio::{IoVec, UioRw, UioSeg}; 5 | 6 | /// Implementation of [`korbis::uio::Uio`] for 11.00. 7 | #[repr(C)] 8 | pub struct Uio { 9 | iov: *mut IoVec, 10 | len: c_int, 11 | off: isize, 12 | res: isize, 13 | seg: UioSeg, 14 | op: UioRw, 15 | td: *mut Thread, 16 | } 17 | 18 | impl korbis::uio::Uio for Uio { 19 | unsafe fn new( 20 | td: *mut Thread, 21 | op: UioRw, 22 | seg: UioSeg, 23 | iov: *mut IoVec, 24 | len: usize, 25 | ) -> Option { 26 | // Check vec count. 27 | if len > Self::vec_max() { 28 | return None; 29 | } 30 | 31 | // Get total length. 32 | let mut res = 0usize; 33 | 34 | for i in 0..len { 35 | res = res.checked_add((*iov.add(i)).len)?; 36 | } 37 | 38 | if res > Self::io_max() { 39 | return None; 40 | } 41 | 42 | Some(Self { 43 | iov, 44 | len: len.try_into().unwrap(), 45 | off: -1, 46 | res: res.try_into().unwrap(), 47 | seg, 48 | op, 49 | td, 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /korbis-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "korbis-macros" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | proc-macro = true 8 | 9 | [dependencies] 10 | proc-macro2 = "1.0.81" 11 | quote = "1.0.36" 12 | syn = { version = "2.0.60", features = ["full"] } 13 | -------------------------------------------------------------------------------- /korbis-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use syn::{parse_macro_input, Error, LitInt}; 3 | 4 | mod offset; 5 | 6 | #[proc_macro_attribute] 7 | pub fn offset(args: TokenStream, item: TokenStream) -> TokenStream { 8 | let args = parse_macro_input!(args as LitInt); 9 | let item = parse_macro_input!(item as self::offset::OffsetItem); 10 | 11 | self::offset::transform(args, item) 12 | .unwrap_or_else(Error::into_compile_error) 13 | .into() 14 | } 15 | -------------------------------------------------------------------------------- /korbis-macros/src/offset.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | use syn::parse::{Parse, ParseStream}; 4 | use syn::punctuated::Punctuated; 5 | use syn::{parenthesized, Error, Ident, LitInt, Pat, PatType, Receiver, ReturnType, Token}; 6 | 7 | pub fn transform(args: LitInt, item: OffsetItem) -> syn::Result { 8 | match item { 9 | OffsetItem::Method(v) => transform_method(args, v), 10 | } 11 | } 12 | 13 | fn transform_method(args: LitInt, item: Method) -> syn::Result { 14 | // Assemble. 15 | let offset: usize = args.base10_parse()?; 16 | let unsafety = item.unsafety; 17 | let ident = item.ident; 18 | let receiver = item.receiver; 19 | let params = item.params; 20 | let ret = item.ret; 21 | let args: Punctuated<&Pat, Token![,]> = params.iter().map(|p| p.pat.as_ref()).collect(); 22 | 23 | Ok(quote! { 24 | #unsafety fn #ident(#receiver, #params) #ret { 25 | let _addr = unsafe { self.elf().as_ptr().add(#offset) }; 26 | let _fp: unsafe extern "C" fn(#params) #ret = unsafe { core::mem::transmute(_addr) }; 27 | unsafe { _fp(#args) } 28 | } 29 | }) 30 | } 31 | 32 | /// Item of `offset` attribute. 33 | pub enum OffsetItem { 34 | Method(Method), 35 | } 36 | 37 | impl Parse for OffsetItem { 38 | fn parse(input: ParseStream) -> syn::Result { 39 | let unsafety = input.parse()?; 40 | let item = if input.parse::>()?.is_some() { 41 | // Parse name. 42 | let ident = input.parse()?; 43 | let params; 44 | 45 | parenthesized!(params in input); 46 | 47 | // Parse receiver. 48 | let receiver = params.parse()?; 49 | 50 | params.parse::>()?; 51 | 52 | // Parse return type. 53 | let ret = input.parse()?; 54 | 55 | input.parse::()?; 56 | 57 | Self::Method(Method { 58 | unsafety, 59 | ident, 60 | receiver, 61 | params: params.parse_terminated(PatType::parse, Token![,])?, 62 | ret, 63 | }) 64 | } else { 65 | return Err(Error::new(input.span(), "unsupported offset item")); 66 | }; 67 | 68 | Ok(item) 69 | } 70 | } 71 | 72 | /// A method that have `offset` attribute. 73 | pub struct Method { 74 | unsafety: Option, 75 | ident: Ident, 76 | receiver: Receiver, 77 | params: Punctuated, 78 | ret: ReturnType, 79 | } 80 | -------------------------------------------------------------------------------- /korbis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "korbis" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | korbis-macros = { path = "../korbis-macros" } 8 | -------------------------------------------------------------------------------- /korbis/src/elf/mod.rs: -------------------------------------------------------------------------------- 1 | //! Copied from https://github.com/obhq/obliteration. 2 | use core::fmt::{Display, Formatter}; 3 | 4 | /// Represents type of an ELF program. 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 6 | pub struct ProgramType(u32); 7 | 8 | impl ProgramType { 9 | pub const PT_NULL: ProgramType = ProgramType(0x0); 10 | pub const PT_LOAD: ProgramType = ProgramType(0x1); 11 | pub const PT_DYNAMIC: ProgramType = ProgramType(0x2); 12 | pub const PT_INTERP: ProgramType = ProgramType(0x3); 13 | pub const PT_NOTE: ProgramType = ProgramType(0x4); 14 | pub const PT_SHLIB: ProgramType = ProgramType(0x5); 15 | pub const PT_PHDR: ProgramType = ProgramType(0x6); 16 | pub const PT_TLS: ProgramType = ProgramType(0x7); 17 | pub const PT_NUM: ProgramType = ProgramType(0x8); 18 | pub const PT_SCE_DYNLIBDATA: ProgramType = ProgramType(0x61000000); 19 | pub const PT_SCE_PROCPARAM: ProgramType = ProgramType(0x61000001); 20 | pub const PT_SCE_MODULEPARAM: ProgramType = ProgramType(0x61000002); 21 | pub const PT_SCE_RELRO: ProgramType = ProgramType(0x61000010); 22 | pub const PT_GNU_EH_FRAME: ProgramType = ProgramType(0x6474e550); 23 | pub const PT_GNU_STACK: ProgramType = ProgramType(0x6474e551); 24 | pub const PT_SCE_COMMENT: ProgramType = ProgramType(0x6fffff00); 25 | pub const PT_SCE_VERSION: ProgramType = ProgramType(0x6fffff01); 26 | pub const PT_HIOS: ProgramType = ProgramType(0x6fffffff); 27 | pub const PT_LOPROC: ProgramType = ProgramType(0x70000000); 28 | pub const PT_SCE_SEGSYM: ProgramType = ProgramType(0x700000A8); 29 | pub const PT_HIPROC: ProgramType = ProgramType(0x7FFFFFFF); 30 | 31 | pub fn new(v: u32) -> Self { 32 | Self(v) 33 | } 34 | } 35 | 36 | impl Display for ProgramType { 37 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 38 | match *self { 39 | Self::PT_NULL => f.write_str("PT_NULL"), 40 | Self::PT_LOAD => f.write_str("PT_LOAD"), 41 | Self::PT_DYNAMIC => f.write_str("PT_DYNAMIC"), 42 | Self::PT_INTERP => f.write_str("PT_INTERP"), 43 | Self::PT_NOTE => f.write_str("PT_NOTE"), 44 | Self::PT_SHLIB => f.write_str("PT_SHLIB"), 45 | Self::PT_PHDR => f.write_str("PT_PHDR"), 46 | Self::PT_TLS => f.write_str("PT_TLS"), 47 | Self::PT_NUM => f.write_str("PT_NUM"), 48 | Self::PT_SCE_DYNLIBDATA => f.write_str("PT_SCE_DYNLIBDATA"), 49 | Self::PT_SCE_PROCPARAM => f.write_str("PT_SCE_PROCPARAM"), 50 | Self::PT_SCE_MODULEPARAM => f.write_str("PT_SCE_MODULEPARAM"), 51 | Self::PT_SCE_RELRO => f.write_str("PT_SCE_RELRO"), 52 | Self::PT_GNU_EH_FRAME => f.write_str("PT_GNU_EH_FRAME"), 53 | Self::PT_GNU_STACK => f.write_str("PT_GNU_STACK"), 54 | Self::PT_SCE_COMMENT => f.write_str("PT_SCE_COMMENT"), 55 | Self::PT_SCE_VERSION => f.write_str("PT_SCE_VERSION"), 56 | Self::PT_HIOS => f.write_str("PT_HIOS"), 57 | Self::PT_LOPROC => f.write_str("PT_LOPROC"), 58 | Self::PT_SCE_SEGSYM => f.write_str("PT_SCE_SEGSYM"), 59 | Self::PT_HIPROC => f.write_str("PT_HIPROC"), 60 | t => write!(f, "{:#010x}", t.0), 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /korbis/src/file/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::thread::Thread; 2 | use crate::Kernel; 3 | use core::sync::atomic::{fence, AtomicU32, Ordering}; 4 | 5 | /// Represents `file` structure. 6 | pub trait File: Sized { 7 | /// Returns `f_count` field. 8 | fn refcnt(&self) -> &AtomicU32; 9 | } 10 | 11 | /// RAII struct to decrease `file::f_count` when dropped. 12 | pub struct OwnedFile { 13 | kernel: K, 14 | file: *mut K::File, 15 | } 16 | 17 | impl OwnedFile { 18 | /// # Safety 19 | /// `file` cannot be null and the caller must own a strong reference to it. This method do 20 | /// **not** increase the reference count of this file. 21 | pub unsafe fn new(kernel: K, file: *mut K::File) -> Self { 22 | Self { kernel, file } 23 | } 24 | } 25 | 26 | impl Drop for OwnedFile { 27 | fn drop(&mut self) { 28 | // See Drop implementation on Arc how this thing work. 29 | if unsafe { (*self.file).refcnt().fetch_sub(1, Ordering::Release) } != 1 { 30 | return; 31 | } 32 | 33 | fence(Ordering::Acquire); 34 | 35 | // The kernel itself does not check if fdrop is success so we don't need to. 36 | unsafe { self.kernel.fdrop(self.file, K::Thread::current()) }; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /korbis/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use self::elf::ProgramType; 4 | use self::file::{File, OwnedFile}; 5 | use self::thread::Thread; 6 | use self::uio::{Uio, UioSeg}; 7 | use core::ffi::{c_char, c_int}; 8 | use core::num::NonZeroI32; 9 | use core::ptr::null_mut; 10 | 11 | pub use korbis_macros::*; 12 | 13 | pub mod elf; 14 | pub mod file; 15 | pub mod thread; 16 | pub mod uio; 17 | 18 | /// Provides methods to access the PS4 kernel for a specific version. 19 | /// 20 | /// Most methods here are a direct call to the kernel so most of them are unsafe. A safe wrapper for 21 | /// those methods are provides by [`KernelExt`], which is automatically implemented for any type 22 | /// that implement [`Kernel`]. 23 | pub trait Kernel: Copy + Send + Sync + 'static { 24 | type File: File; 25 | type Thread: Thread; 26 | type Uio: Uio; 27 | 28 | /// # Safety 29 | /// `base` must point to a valid address of the kernel. Behavior is undefined if format of the 30 | /// kernel is unknown. 31 | /// 32 | /// # Panics 33 | /// This function may panic if format of the kernel is unknown. 34 | unsafe fn new(base: *const u8) -> Self; 35 | 36 | /// Returns mapped ELF of the kernel. 37 | /// 38 | /// # Safety 39 | /// The returned slice can contains `PF_W` programs. That mean the memory covered by this slice 40 | /// can mutate at any time. The whole slice is guarantee to be readable. 41 | unsafe fn elf(self) -> &'static [u8]; 42 | 43 | /// # Safety 44 | /// `fp` cannot be null. 45 | unsafe fn fget_write( 46 | self, 47 | td: *mut Self::Thread, 48 | fd: c_int, 49 | unused: c_int, 50 | fp: *mut *mut Self::File, 51 | ) -> c_int; 52 | 53 | /// # Panics 54 | /// If [`File::refcnt()`] of `fp` is not zero. 55 | /// 56 | /// # Safety 57 | /// - `fp` cannot be null. 58 | unsafe fn fdrop(self, fp: *mut Self::File, td: *mut Self::Thread) -> c_int; 59 | 60 | /// # Safety 61 | /// - `td` cannot be null. 62 | /// - `path` cannot be null and must point to a null-terminated string if `seg` is [`UioSeg::Kernel`]. 63 | unsafe fn kern_openat( 64 | self, 65 | td: *mut Self::Thread, 66 | fd: c_int, 67 | path: *const c_char, 68 | seg: UioSeg, 69 | flags: c_int, 70 | mode: c_int, 71 | ) -> c_int; 72 | 73 | /// # Safety 74 | /// `td` cannot be null. 75 | unsafe fn kern_close(self, td: *mut Self::Thread, fd: c_int) -> c_int; 76 | 77 | /// # Safety 78 | /// `td` cannot be null. 79 | unsafe fn kern_fsync(self, td: *mut Self::Thread, fd: c_int, fullsync: c_int) -> c_int; 80 | 81 | /// # Safety 82 | /// - `td` cannot be null. 83 | /// - `auio` cannot be null. 84 | unsafe fn kern_writev(self, td: *mut Self::Thread, fd: c_int, auio: *mut Self::Uio) -> c_int; 85 | 86 | /// # Safety 87 | /// `base` must point to a valid address of the kernel. Behavior is undefined if format of the 88 | /// kernel is unknown. 89 | /// 90 | /// # Panics 91 | /// This function may panic if format of the kernel is unknown. 92 | unsafe fn get_mapped_elf(base: *const u8) -> &'static [u8] { 93 | // Get ELF loaded size. 94 | let e_phnum = base.add(0x38).cast::().read() as usize; 95 | let progs = core::slice::from_raw_parts(base.add(0x40), e_phnum * 0x38); 96 | let mut end = base as usize; 97 | 98 | for h in progs.chunks_exact(0x38) { 99 | // Skip non-loadable. 100 | let ty = ProgramType::new(u32::from_le_bytes(h[0x00..0x04].try_into().unwrap())); 101 | 102 | if !matches!(ty, ProgramType::PT_LOAD | ProgramType::PT_SCE_RELRO) { 103 | continue; 104 | } 105 | 106 | // Update end address. 107 | let addr = usize::from_le_bytes(h[0x10..0x18].try_into().unwrap()); 108 | let len = usize::from_le_bytes(h[0x28..0x30].try_into().unwrap()); 109 | let align = usize::from_le_bytes(h[0x30..0x38].try_into().unwrap()); 110 | 111 | assert!(addr >= end); // Just in case if Sony re-order the programs. 112 | 113 | end = addr + len.next_multiple_of(align); 114 | } 115 | 116 | // Get loaded ELF. 117 | let len = end - (base as usize); 118 | 119 | core::slice::from_raw_parts(base, len) 120 | } 121 | } 122 | 123 | /// Provides wrapper methods for methods on [`Kernel`]. 124 | /// 125 | /// This trait is automatically implemented for any type that implement [`Kernel`]. 126 | pub trait KernelExt: Kernel { 127 | fn fget_write(self, td: *mut Self::Thread, fd: c_int) -> Result, NonZeroI32>; 128 | } 129 | 130 | impl KernelExt for T { 131 | fn fget_write(self, td: *mut Self::Thread, fd: c_int) -> Result, NonZeroI32> { 132 | let mut fp = null_mut(); 133 | let errno = unsafe { self.fget_write(td, fd, 0, &mut fp) }; 134 | 135 | match NonZeroI32::new(errno) { 136 | Some(v) => Err(v), 137 | None => Ok(unsafe { OwnedFile::new(self, fp) }), 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /korbis/src/thread/mod.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | 3 | /// Represents `thread` structure. 4 | pub trait Thread: Sized { 5 | fn current() -> *mut Self { 6 | let mut p; 7 | 8 | unsafe { 9 | asm!("mov {}, gs:[0]", out(reg) p, options(readonly, pure, preserves_flags, nostack)) 10 | }; 11 | 12 | p 13 | } 14 | 15 | /// Returns value of `td_retval[i]`. 16 | /// 17 | /// # Panics 18 | /// If `i` is not `0` or `1`. 19 | fn ret(&self, i: usize) -> usize; 20 | } 21 | -------------------------------------------------------------------------------- /korbis/src/uio/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::Kernel; 2 | 3 | /// Represents `uio` structure. 4 | pub trait Uio: Sized { 5 | /// Returns [`None`] if `len` is geater than [`Uio::vec_max()`] or total length of `iov` is 6 | /// greater than [`Uio::io_max()`]. 7 | /// 8 | /// # Safety 9 | /// - `td` cannot be null. 10 | /// - `iov` cannot be null and must be valid up to `len`. 11 | unsafe fn new( 12 | td: *mut K::Thread, 13 | op: UioRw, 14 | seg: UioSeg, 15 | iov: *mut IoVec, 16 | len: usize, 17 | ) -> Option; 18 | 19 | /// Returns value of `UIO_MAXIOV`. 20 | fn vec_max() -> usize { 21 | 1024 22 | } 23 | 24 | /// Returns value of `IOSIZE_MAX`. 25 | fn io_max() -> usize { 26 | 0x7fffffff 27 | } 28 | } 29 | 30 | /// Represents `uio_seg` enum. 31 | #[repr(C)] 32 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 33 | pub enum UioSeg { 34 | /// UIO_USERSPACE 35 | User, 36 | /// UIO_SYSSPACE 37 | Kernel, 38 | } 39 | 40 | /// Represents `uio_rw` enum. 41 | #[repr(C)] 42 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 43 | pub enum UioRw { 44 | /// UIO_READ 45 | Read, 46 | /// UIO_WRITE 47 | Write, 48 | } 49 | 50 | /// Represents `iovec` structure. 51 | #[repr(C)] 52 | pub struct IoVec { 53 | pub ptr: *mut u8, 54 | pub len: usize, 55 | } 56 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /src/direct.rs: -------------------------------------------------------------------------------- 1 | use crate::method::{OpenFlags, OwnedFd}; 2 | use crate::DumpMethod; 3 | use core::ffi::{c_int, CStr}; 4 | use core::num::NonZeroI32; 5 | use korbis::thread::Thread; 6 | use korbis::uio::{IoVec, Uio, UioRw, UioSeg}; 7 | use korbis::Kernel; 8 | 9 | /// Implementation of [`DumpMethod`] using internal kernel functions. 10 | /// 11 | /// This method require a first dump from syscall method for required function addresses. 12 | pub struct DirectMethod { 13 | kernel: K, 14 | } 15 | 16 | impl DirectMethod { 17 | pub fn new(kernel: K) -> Self { 18 | Self { kernel } 19 | } 20 | } 21 | 22 | impl DumpMethod for DirectMethod { 23 | fn open( 24 | &self, 25 | path: &CStr, 26 | flags: OpenFlags, 27 | mode: c_int, 28 | ) -> Result, NonZeroI32> { 29 | let td = Thread::current(); 30 | let errno = unsafe { 31 | self.kernel.kern_openat( 32 | td, 33 | -100, 34 | path.as_ptr(), 35 | UioSeg::Kernel, 36 | flags.bits() as _, 37 | mode, 38 | ) 39 | }; 40 | 41 | match NonZeroI32::new(errno) { 42 | Some(v) => Err(v), 43 | None => Ok(unsafe { OwnedFd::new(self, (*td).ret(0).try_into().unwrap()) }), 44 | } 45 | } 46 | 47 | fn write(&self, fd: c_int, buf: *const u8, len: usize) -> Result { 48 | // Setup iovec. 49 | let mut iov = IoVec { 50 | ptr: buf.cast_mut(), 51 | len, 52 | }; 53 | 54 | // Write. 55 | let td = Thread::current(); 56 | let mut io = unsafe { Uio::new(td, UioRw::Write, UioSeg::Kernel, &mut iov, 1).unwrap() }; 57 | let errno = unsafe { self.kernel.kern_writev(td, fd, &mut io) }; 58 | 59 | match NonZeroI32::new(errno) { 60 | Some(v) => Err(v), 61 | None => Ok(unsafe { (*td).ret(0) }), 62 | } 63 | } 64 | 65 | fn fsync(&self, fd: c_int) -> Result<(), NonZeroI32> { 66 | let td = Thread::current(); 67 | let errno = unsafe { self.kernel.kern_fsync(td, fd, 1) }; 68 | 69 | match NonZeroI32::new(errno) { 70 | Some(v) => Err(v), 71 | None => Ok(()), 72 | } 73 | } 74 | 75 | fn close(&self, fd: c_int) -> Result<(), NonZeroI32> { 76 | let td = Thread::current(); 77 | let errno = unsafe { self.kernel.kern_close(td, fd) }; 78 | 79 | match NonZeroI32::new(errno) { 80 | Some(v) => Err(v), 81 | None => Ok(()), 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use crate::method::{DumpMethod, OpenFlags}; 5 | use core::arch::global_asm; 6 | use core::cmp::min; 7 | use core::ffi::c_int; 8 | use core::mem::{size_of_val, zeroed}; 9 | use core::panic::PanicInfo; 10 | use korbis::Kernel; 11 | use x86_64::registers::model_specific::LStar; 12 | 13 | #[cfg(method = "direct")] 14 | mod direct; 15 | mod method; 16 | #[cfg(method = "syscall")] 17 | mod syscall; 18 | 19 | // The job of this custom entry point is: 20 | // 21 | // - Get address where our payload is loaded. 22 | // - Do ELF relocation on our payload. 23 | global_asm!( 24 | ".globl _start", 25 | ".section .text.entry", 26 | "_start:", 27 | "lea rdi, [rip]", 28 | "sub rdi, 7", // 7 is size of "lea rdi, [rip]". 29 | "mov rax, rdi", 30 | "add rax, 0x80", // Offset of dynamic section configured in kernel-dumper.ld. 31 | "xor r8, r8", 32 | "0:", 33 | "mov rsi, [rax]", 34 | "mov rcx, [rax+8]", 35 | "add rax, 16", 36 | "test rsi, rsi", // Check if DT_NULL. 37 | "jz 1f", 38 | "cmp rsi, 7", // Check if DT_RELA. 39 | "jz 2f", 40 | "cmp rsi, 8", // Check if DT_RELASZ. 41 | "jz 3f", 42 | "jmp 0b", 43 | "2:", // Keep DT_RELA. 44 | "mov rdx, rdi", 45 | "add rdx, rcx", 46 | "jmp 0b", 47 | "3:", // Keep DT_RELASZ. 48 | "mov r8, rcx", 49 | "jmp 0b", 50 | "1:", 51 | "test r8, r8", // Check if no more DT_RELA entries. 52 | "jz main", 53 | "mov rsi, [rdx]", 54 | "mov rax, [rdx+8]", 55 | "mov rcx, [rdx+16]", 56 | "add rdx, 24", 57 | "sub r8, 24", 58 | "test eax, eax", // Check if R_X86_64_NONE. 59 | "jz main", 60 | "cmp eax, 8", // Check if R_X86_64_RELATIVE. 61 | "jnz 1b", 62 | "add rsi, rdi", 63 | "add rcx, rdi", 64 | "mov [rsi], rcx", 65 | "jmp 1b", 66 | ); 67 | 68 | #[no_mangle] 69 | pub extern "C" fn main(_: *const u8) { 70 | // Get base address of the kernel. 71 | let aslr = LStar::read() - 0xffffffff822001c0; // AFAIK syscall handler is same for all version. 72 | let base = aslr + 0xffffffff82200000; 73 | let kernel = unsafe { init(base.as_ptr()) }; 74 | 75 | // Setup dumping method. 76 | #[cfg(method = "syscall")] 77 | let method = unsafe { crate::syscall::SyscallMethod::new(&kernel) }; 78 | #[cfg(method = "direct")] 79 | let method = crate::direct::DirectMethod::new(kernel); 80 | 81 | // Create dump file. 82 | let out = match method.open( 83 | c"/mnt/usb0/kernel.elf", 84 | OpenFlags::O_WRONLY | OpenFlags::O_CREAT | OpenFlags::O_TRUNC, 85 | 0o777, 86 | ) { 87 | Ok(v) => v, 88 | Err(_) => { 89 | notify(&method, "Failed to open /mnt/usb0/kernel.elf"); 90 | return; 91 | } 92 | }; 93 | 94 | // Dump. 95 | let mut data = unsafe { kernel.elf() }; 96 | 97 | while !data.is_empty() { 98 | // Write file. 99 | let fd = out.as_raw_fd(); 100 | let len = min(data.len(), 0x4000); 101 | let buf = &data[..len]; 102 | let written = match method.write(fd, buf.as_ptr(), buf.len()) { 103 | Ok(v) => v, 104 | Err(_) => { 105 | notify(&method, "Failed to write /mnt/usb0/kernel.elf"); 106 | return; 107 | } 108 | }; 109 | 110 | if written == 0 { 111 | notify(&method, "Not enough space to dump the kernel"); 112 | return; 113 | } 114 | 115 | data = &data[written..]; 116 | } 117 | 118 | // Sync. 119 | if method.fsync(out.as_raw_fd()).is_err() { 120 | notify( 121 | &method, 122 | "Failed to synchronize changes to a /mnt/usb0/kernel.elf", 123 | ); 124 | 125 | return; 126 | } 127 | 128 | notify(&method, "Dump completed!"); 129 | } 130 | 131 | fn notify(method: &impl DumpMethod, msg: impl AsRef<[u8]>) { 132 | // Open notification device. 133 | let devs = [c"/dev/notification0", c"/dev/notification1"]; 134 | let mut fd = None; 135 | 136 | for dev in devs { 137 | if let Ok(v) = method.open(dev, OpenFlags::O_WRONLY, 0) { 138 | fd = Some(v); 139 | break; 140 | } 141 | } 142 | 143 | // Check if we have a device to write to. 144 | let fd = match fd { 145 | Some(v) => v, 146 | None => return, 147 | }; 148 | 149 | // Setup notification. 150 | let mut data: OrbisNotificationRequest = unsafe { zeroed() }; 151 | let msg = msg.as_ref(); 152 | let len = min(data.message.len() - 1, msg.len()); 153 | 154 | data.target_id = -1; 155 | data.use_icon_image_uri = 1; 156 | data.message[..len].copy_from_slice(&msg[..len]); 157 | 158 | // Write notification. 159 | method 160 | .write( 161 | fd.as_raw_fd(), 162 | &data as *const OrbisNotificationRequest as _, 163 | size_of_val(&data), 164 | ) 165 | .ok(); 166 | } 167 | 168 | #[cfg(fw = "1100")] 169 | unsafe fn init(base: *const u8) -> impl Kernel { 170 | korbis_1100::Kernel::new(base) 171 | } 172 | 173 | #[panic_handler] 174 | fn panic(_: &PanicInfo) -> ! { 175 | loop {} 176 | } 177 | 178 | /// By OSM-Made. 179 | #[repr(C)] 180 | struct OrbisNotificationRequest { 181 | ty: c_int, 182 | req_id: c_int, 183 | priority: c_int, 184 | msg_id: c_int, 185 | target_id: c_int, 186 | user_id: c_int, 187 | unk1: c_int, 188 | unk2: c_int, 189 | app_id: c_int, 190 | error_num: c_int, 191 | unk3: c_int, 192 | use_icon_image_uri: u8, 193 | message: [u8; 1024], 194 | icon_uri: [u8; 1024], 195 | unk: [u8; 1024], 196 | } 197 | -------------------------------------------------------------------------------- /src/method.rs: -------------------------------------------------------------------------------- 1 | use bitflags::bitflags; 2 | use core::ffi::{c_int, CStr}; 3 | use core::num::NonZeroI32; 4 | 5 | /// Provides method to dump the kernel. 6 | pub trait DumpMethod: Sized { 7 | fn open(&self, path: &CStr, flags: OpenFlags, mode: c_int) 8 | -> Result, NonZeroI32>; 9 | fn write(&self, fd: c_int, buf: *const u8, len: usize) -> Result; 10 | fn fsync(&self, fd: c_int) -> Result<(), NonZeroI32>; 11 | fn close(&self, fd: c_int) -> Result<(), NonZeroI32>; 12 | } 13 | 14 | bitflags! { 15 | /// Flags for [`DumpMethod::open()`]. 16 | #[derive(Clone, Copy, PartialEq, Eq)] 17 | pub struct OpenFlags: u32 { 18 | const O_RDONLY = 0x00000000; 19 | const O_WRONLY = 0x00000001; 20 | const O_RDWR = 0x00000002; 21 | const O_ACCMODE = Self::O_WRONLY.bits() | Self::O_RDWR.bits(); 22 | const O_SHLOCK = 0x00000010; 23 | const O_EXLOCK = 0x00000020; 24 | const O_CREAT = 0x00000200; 25 | const O_TRUNC = 0x00000400; 26 | const O_EXCL = 0x00000800; 27 | const O_EXEC = 0x00040000; 28 | const O_CLOEXEC = 0x00100000; 29 | const UNK1 = 0x00400000; 30 | } 31 | } 32 | 33 | /// Encapsulate an opened file descriptor. 34 | pub struct OwnedFd<'a, T: DumpMethod> { 35 | method: &'a T, 36 | fd: c_int, 37 | } 38 | 39 | impl<'a, T: DumpMethod> OwnedFd<'a, T> { 40 | pub fn new(method: &'a T, fd: c_int) -> Self { 41 | Self { method, fd } 42 | } 43 | 44 | pub fn as_raw_fd(&self) -> c_int { 45 | self.fd 46 | } 47 | } 48 | 49 | impl<'a, T: DumpMethod> Drop for OwnedFd<'a, T> { 50 | fn drop(&mut self) { 51 | self.method.close(self.fd).unwrap(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/syscall.rs: -------------------------------------------------------------------------------- 1 | use crate::method::{DumpMethod, OpenFlags, OwnedFd}; 2 | use core::ffi::{c_int, c_void, CStr}; 3 | use core::mem::transmute; 4 | use core::num::NonZeroI32; 5 | use korbis::thread::Thread; 6 | use korbis::Kernel; 7 | use x86_64::registers::control::Cr0; 8 | 9 | /// Implementation of [`DumpMethod`] using syscalls. 10 | /// 11 | /// This method need to patch `copyin`, `copyout` and `copyinstr` in order to be able to call those 12 | /// functions from the kernel. That mean you cannot obtain an unpatched kernel with this method. 13 | /// 14 | /// The reason this method exists because in order for the direct method to work we need to get the 15 | /// first dump to find the required function addresses. 16 | pub struct SyscallMethod { 17 | sysents: &'static [Sysent; 678], 18 | } 19 | 20 | impl SyscallMethod { 21 | pub unsafe fn new(kernel: &K) -> Self { 22 | // Remove address checking from copyin, copyout and copyinstr. 23 | let base = kernel.elf().as_ptr(); 24 | let cr0 = Cr0::read_raw(); 25 | 26 | unsafe { Cr0::write_raw(cr0 & !(1 << 16)) }; 27 | unsafe { Self::patch_kernel(base.cast_mut()) }; 28 | unsafe { Cr0::write_raw(cr0) }; 29 | 30 | Self { 31 | #[cfg(fw = "1100")] 32 | sysents: transmute(base.add(0x1101760)), 33 | } 34 | } 35 | 36 | /// # Safety 37 | /// - `base` must be a valid base address of the kernel. 38 | /// - `WP` flag must not be set on `CR0`. 39 | #[cfg(fw = "1100")] 40 | unsafe fn patch_kernel(base: *mut u8) { 41 | let patches = [ 42 | (0x2DDF42usize, [0x90u8; 2].as_slice()), // copyout_patch1 43 | (0x2DDF4E, &[0x90; 3]), // copyout_patch2 44 | (0x2DE037, &[0x90; 2]), // copyin_patch1 45 | (0x2DE043, &[0x90; 3]), // copyin_patch2 46 | (0x2DE4E3, &[0x90; 2]), // copyinstr_patch1 47 | (0x2DE4EF, &[0x90; 3]), // copyinstr_patch2 48 | (0x2DE520, &[0x90; 2]), // copyinstr_patch3 49 | ]; 50 | 51 | for (off, patch) in patches { 52 | base.add(off) 53 | .copy_from_nonoverlapping(patch.as_ptr(), patch.len()); 54 | } 55 | } 56 | } 57 | 58 | impl DumpMethod for SyscallMethod { 59 | fn open( 60 | &self, 61 | path: &CStr, 62 | flags: OpenFlags, 63 | mode: c_int, 64 | ) -> Result, NonZeroI32> { 65 | // Setup arguments. 66 | let td = Thread::current(); 67 | let args = [path.as_ptr() as usize, flags.bits() as usize, mode as usize]; 68 | 69 | // Invoke handler. 70 | let handler = self.sysents[5].handler; 71 | let errno = unsafe { handler(td, args.as_ptr().cast()) }; 72 | 73 | match NonZeroI32::new(errno) { 74 | Some(v) => Err(v), 75 | None => Ok(OwnedFd::new(self, unsafe { 76 | (*td).ret(0).try_into().unwrap() 77 | })), 78 | } 79 | } 80 | 81 | fn write(&self, fd: c_int, buf: *const u8, len: usize) -> Result { 82 | // Setup arguments. 83 | let td = Thread::current(); 84 | let args = [fd as usize, buf as usize, len]; 85 | 86 | // Invoke handler. 87 | let handler = self.sysents[4].handler; 88 | let errno = unsafe { handler(td, args.as_ptr().cast()) }; 89 | 90 | match NonZeroI32::new(errno) { 91 | Some(v) => Err(v), 92 | None => Ok(unsafe { (*td).ret(0) }), 93 | } 94 | } 95 | 96 | fn fsync(&self, fd: c_int) -> Result<(), NonZeroI32> { 97 | // Setup arguments. 98 | let td = Thread::current(); 99 | let args = [fd as usize]; 100 | 101 | // Invoke handler. 102 | let handler = self.sysents[95].handler; 103 | let errno = unsafe { handler(td, args.as_ptr().cast()) }; 104 | 105 | match NonZeroI32::new(errno) { 106 | Some(v) => Err(v), 107 | None => Ok(()), 108 | } 109 | } 110 | 111 | fn close(&self, fd: c_int) -> Result<(), NonZeroI32> { 112 | // Setup arguments. 113 | let td = Thread::current(); 114 | let args = [fd as usize]; 115 | 116 | // Invoke handler. 117 | let handler = self.sysents[6].handler; 118 | let errno = unsafe { handler(td, args.as_ptr().cast()) }; 119 | 120 | match NonZeroI32::new(errno) { 121 | Some(v) => Err(v), 122 | None => Ok(()), 123 | } 124 | } 125 | } 126 | 127 | /// Implementation of `sysent` structure. 128 | #[repr(C)] 129 | struct Sysent { 130 | narg: c_int, 131 | handler: unsafe extern "C" fn(td: *mut K::Thread, uap: *const c_void) -> c_int, 132 | pad: [u8; 0x20], 133 | } 134 | --------------------------------------------------------------------------------