diff --git a/.vscode/launch.json b/.vscode/launch.json index 38c5e7567..6e704befc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,13 +1,13 @@ { "configurations": [ { - "name": "Debug - Main", + "name": "Frontend", "type": "lldb", "request": "launch", "args": [], "cwd": "${workspaceFolder}", "windows": { - "program": "${workspaceFolder}/build/src/Debug/Obliteration.exe", + "program": "${workspaceFolder}/build/src/Obliteration.exe", "env": { "Path": "${env:Path};${env:CMAKE_PREFIX_PATH}\\bin" } @@ -20,7 +20,7 @@ } }, { - "name": "Debug - Kernel", + "name": "Kernel | Debug", "type": "lldb", "request": "launch", "program": "${workspaceFolder}/src/target/debug/obkrnl", @@ -28,26 +28,7 @@ "cwd": "${workspaceFolder}" }, { - "name": "Release - Main", - "type": "lldb", - "request": "launch", - "args": [], - "cwd": "${workspaceFolder}", - "windows": { - "program": "${workspaceFolder}/build/src/Release/Obliteration.exe", - "env": { - "Path": "${env:Path};${env:CMAKE_PREFIX_PATH}\\bin" - } - }, - "linux": { - "program": "${workspaceFolder}/build/src/obliteration" - }, - "osx": { - "program": "${workspaceFolder}/build/src/obliteration.app/Contents/MacOS/obliteration" - } - }, - { - "name": "Release - Kernel", + "name": "Kernel | Release", "type": "lldb", "request": "launch", "program": "${workspaceFolder}/src/target/release/obkrnl", diff --git a/.vscode/settings.json b/.vscode/settings.json index 78290e746..1622798f3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,7 @@ { - "cmake.configureOnOpen": true, "cmake.debugConfig": { "cwd": "${workspaceFolder}" }, - "cmake.skipConfigureIfCachePresent": true, "files.insertFinalNewline": true, "files.trimFinalNewlines": true, "files.trimTrailingWhitespace": true, @@ -11,8 +9,8 @@ "settings set target.x86-disassembly-flavor intel" ], "rust-analyzer.imports.granularity.group": "module", + "rust-analyzer.imports.group.enable": false, "[rust]": { - "editor.defaultFormatter": "rust-lang.rust-analyzer", "editor.formatOnSave": true } } diff --git a/README.md b/README.md index 036eef826..0fa48d4df 100644 --- a/README.md +++ b/README.md @@ -33,143 +33,22 @@ We have a Discord server for discussion about Obliteration and its development. - Windows 10, Linux or macOS 11+. - x86-64 CPU. We want to support non-x86 but currently we don't have any developers who are willing to work on this. +- CPU with hardware virtualization supports. + - Windows and Linux users may need to enable this feature on the BIOS/UEFI settings. - A jailbroken PS4 with FTP server that supports SELF decryption. ### Windows-specific requirements - [Microsoft Visual C++ 2022 Redistributable](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist). If there is an error related to `msvcp140.dll`, `vcruntime140.dll`, or `vcruntime140_1.dll` that means you need to install this manually. It's likely your system already has it, so try to run Obliteration first. +- [Virtual Machine Platform](https://github.com/obhq/obliteration/wiki/Common-Issues) ### Linux-specific requirements Obliteration supports only 4KB/8KB/16KB pages. Most people should not have any problem with this because 4KB is the default for most distros. -## Building from source +## Building and Development -### Windows prerequisites - -- Visual Studio 2022 - - `Desktop development with C++` workload is required -- Rust on the latest stable channel -- CMake 3.21+ - - Make sure you have `Add CMake to the system PATH` selected when installing -- [Ninja](https://ninja-build.org) - - Make sure Ninja is added to `PATH` - -### Linux prerequisites - -- GCC 9.4+ -- Rust on the latest stable channel -- CMake 3.21+ - -### macOS prerequisites - -- macOS 12+ -- Homebrew -- Clang 13+ -- Rust on the latest stable channel -- CMake 3.21+ - -### Install Qt 6 - -You need to install Qt 6 on your system before you proceed. The minimum version is 6.5. You also need to enable SVG supports when installing. - -#### Windows-specific requirements - -You need `Qt Online Installer` for open-source to install Qt, downloaded from https://www.qt.io. The installer will ask you to sign in with a Qt account, which you can create for free. You need to check `Custom installation` and do not check `Qt for desktop development` that is using the MinGW toolchain. Make sure you have checked the `MSVC 2019 64-bit` component in the `Select Components` page for the version you wish to install and uncheck all of the other components. - -Once installation is completed you need to set the `CMAKE_PREFIX_PATH` environment variable to the full path of the installed version (e.g. `C:\Qt\6.5.1\msvc2019_64`). To set an environment variable: - -1. Open a run dialog with Win + R. -2. Enter `sysdm.cpl` then click `OK`. -3. Go to the `Advanced` tab then click on `Environment Variables...`. -4. Click `New...` to create a new environment variable. Just create for either `User variables` or `System variables`, not both. -5. Restart your terminal or IDE to load the new PATH. - -#### Install Qt with Homebrew (macOS only) - -```sh -brew install qt@6 -``` - -### Configure build system - -```sh -cmake --preset PRESET . -``` - -The value of `PRESET` will depend on your platform and the build configuration you want to use. The current available presets are: - -- windows-release -- windows-debug -- linux-release -- linux-debug -- mac-release -- mac-debug - -If all you want is to use the emulator, choose `[YOUR-PLATFORM]-release` for optimized outputs. But if you want to edit the code, choose `*-debug`. - -### Build - -```sh -cmake --build --preset PRESET -``` - -You can use `-j` to enable parallel building (e.g. `cmake --build --preset PRESET -j 2`). Each parallel build on Linux consumes a lot of memory so don't use the number of your CPU cores otherwise your system might crash due to out of memory. On Windows and macOS it seems like it is safe to use the number of your CPU cores. - -## Development - -Before proceeding, make sure the build preset you are using is `*-debug`. We recommend Visual Studio Code as a code editor with the following extensions: - -- CMake Tools ([Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) | [Open VSX](https://open-vsx.org/extension/ms-vscode/cmake-tools)) -- clangd ([Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd) | [Open VSX](https://open-vsx.org/extension/llvm-vs-code-extensions/vscode-clangd)) -- rust-analyzer ([Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) | [Open VSX](https://open-vsx.org/extension/rust-lang/rust-analyzer)) -- CodeLLDB ([Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) | [Open VSX](https://open-vsx.org/extension/vadimcn/vscode-lldb)) - -Then open this folder with VS Code. It will ask which CMake preset to use and you need to choose the same one that you were using when building. Everything should work out of the box (e.g. code completion, debugging, etc). - -### macOS debugging issues - -If you can't launch or debug Obliteration from VS Code, try [this](https://github.com/vadimcn/codelldb/discussions/456#discussioncomment-874122) solution. - -### Get a homebrew application for testing - -If you don't have a PS4 application for testing you can download PS Scene Quiz for free [here](https://pkg-zone.com/details/LAPY10010). - -### Rules for Rust sources - -- Use unsafe code only when you know what you are doing. When you do try to wrap it in a safe function so other people who are not familiar with unsafe code can have a safe life. -- Don't chain method calls without an intermediate variable if the result code is hard to follow. We encourage code readability as a pleasure when writing so try to make it easy to read and understand for other people. -- Do not blindly cast an integer. Make sure the value can fit in a destination type. We don't have any plans to support non-64-bit systems so the pointer size and its related types like `usize` are always 64-bits. -- Beware of deadlock and memory leak. Rust can protect us from most mistakes, except those two. Deadlock can be happen easily in Rust because Rust requires us to wrap the data we want to protect with a mutex and get a mutable reference through it. The problem with this is it become natural for you to lock the mutex to operate on the inner data, which can easily cause a deadlock if you are not aware when there are another locks being active. The only cases for memory leak you need to aware is when working with `Arc`. Just make sure you don't create a reference cycle that will never be dropped. - -### Rules for C++ sources - -Just follow how Qt is written (e.g. coding style, etc.). Always prefers Qt classes over `std` when possible so you don't need to handle exceptions. Do not use the Qt `ui` file to design the UI because it will break on a high-DPI screen. - -### Starting point - -The application consists of 2 binaries: - -1. Main application. This is what users will see when they launch Obliteration. Its entry point is inside `src/main.cpp`. -2. Emulator kernel. This is where emulation takes place. Its entry point is inside `src/kernel/src/main.rs`. - -### Debugging the kernel - -Create `.kernel-debug` in the root of the repository. The contents of this file is YAML and the kernel will deserialize it to the `Args` struct in `src/kernel/src/main.rs` when passing the `--debug` flag to the kernel. See `Args` struct for available options. - -We already provide a launch configuration for VS Code so all you need to do is choose `Debug - Kernel` as the configuration and start debugging. - -### UI Icons - -We use icons from https://materialdesignicons.com for UI (e.g. on the menu and toolbar). - -### Additional informations - -[PS4 Developer Wiki](https://www.psdevwiki.com/ps4) has a lot of useful information about the PS4 internal. We also have a PS4 reverse engineering [project](https://github.com/obhq/reverse-engineering). - -## Code contribution - -If you want to make some contributions but don't know what to work on you can look for `TODO` comment or `todo!` macro invocation in the source code. You can also take a look on the unassigned issues. +Information on building Obliteration and preparing to be a developer can be found on our [Wiki.](https://github.com/obhq/obliteration/wiki/Compilation-&-Development) ## License @@ -177,3 +56,7 @@ If you want to make some contributions but don't know what to work on you can lo - `src/param`, `src/pfs` and `src/pkg` are licensed under LGPL-3.0 license. - All other source code is licensed under MIT license. - All release binaries are under GPL-3.0 license. + +### UI Icons + +We use icons from https://materialdesignicons.com for UI (e.g. on the menu and toolbar). diff --git a/src/core/src/param.rs b/src/core/src/param.rs index cd048629d..edb6b9015 100644 --- a/src/core/src/param.rs +++ b/src/core/src/param.rs @@ -65,5 +65,8 @@ pub unsafe extern "C" fn param_title_id_get(param: &Param, buf: &mut QString) { #[no_mangle] pub unsafe extern "C" fn param_version_get(param: &Param, buf: &mut QString) { - buf.set(param.version()); + match param.version() { + Some(version) => buf.set(version), + None => buf.set(""), + } } diff --git a/src/game_models.cpp b/src/game_models.cpp index 96e76478e..dff755551 100644 --- a/src/game_models.cpp +++ b/src/game_models.cpp @@ -1,5 +1,6 @@ #include "game_models.hpp" #include "path.hpp" +#include Game::Game(const QString &id, const QString &name, const QString &directory) : m_id(id), @@ -12,23 +13,30 @@ Game::~Game() { } -QPixmap Game::icon() const -{ +QPixmap Game::icon() const { + if (!m_cachedIcon.isNull()) { + return m_cachedIcon; + } + // Get icon path. auto dir = joinPath(m_directory, "sce_sys"); auto path = joinPath(dir.c_str(), "icon0.png"); - // Construct icon object. - QPixmap icon(path.c_str()); + if (QFile::exists(path.c_str())) { + m_cachedIcon.load(path.c_str()); + } else { + // Load fallback icon if icon0 doesn't exist. + m_cachedIcon.load(":/resources/fallbackicon0.png"); + } // For games with large icon sizes. - if (icon.width() != 512 || icon.height() != 512) { - icon = icon.scaled(512, 512, Qt::KeepAspectRatio, Qt::SmoothTransformation); + if (m_cachedIcon.width() != 512 || m_cachedIcon.height() != 512) { + m_cachedIcon = m_cachedIcon.scaled(512, 512, Qt::KeepAspectRatio, Qt::SmoothTransformation); } - icon.setDevicePixelRatio(2.0); + m_cachedIcon.setDevicePixelRatio(2.0); - return icon; + return m_cachedIcon; } GameListModel::GameListModel(QObject *parent) : diff --git a/src/game_models.hpp b/src/game_models.hpp index 2f0eb860f..f9a382b19 100644 --- a/src/game_models.hpp +++ b/src/game_models.hpp @@ -19,6 +19,7 @@ class Game final : public QObject { QString m_id; QString m_name; QString m_directory; + mutable QPixmap m_cachedIcon; }; class GameListModel final : public QAbstractListModel { diff --git a/src/kernel/Cargo.toml b/src/kernel/Cargo.toml index 8e8f5754f..ec0ae520d 100644 --- a/src/kernel/Cargo.toml +++ b/src/kernel/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "kernel" version = "0.1.0" +description = "Free and open-source alternative PlayStation 4 kernel for Windows, Linux and macOS" edition = "2021" [[bin]] @@ -11,7 +12,7 @@ path = "src/main.rs" bitflags = "2.1" bytemuck = "1.14.0" byteorder = "1.4" -clap = { version = "4.1", features = ["derive"] } +clap = { version = "4.1", features = ["cargo", "derive"] } discord-rich-presence = "0.2" gmtx = { path = "../gmtx" } iced-x86 = { version = "1.18", features = ["code_asm"] } @@ -44,6 +45,7 @@ features = [ "Win32_System_IO", "Win32_System_Kernel", "Win32_System_Memory", + "Win32_System_Performance", "Win32_System_SystemInformation", "Win32_System_Threading", "Win32_System_Time", diff --git a/src/kernel/src/budget/mod.rs b/src/kernel/src/budget/mod.rs index 449f17fb4..eae69f220 100644 --- a/src/kernel/src/budget/mod.rs +++ b/src/kernel/src/budget/mod.rs @@ -25,7 +25,7 @@ impl BudgetManager { let name = budget.name.clone(); let mut budgets = self.budgets.lock().unwrap(); - budgets.alloc_infallible(|_| Entry::new(Some(name), Arc::new(budget), 0x2000)) + budgets.alloc(Entry::new(Some(name), Arc::new(budget), 0x2000)) } fn sys_budget_get_ptype(self: &Arc, td: &VThread, i: &SysIn) -> Result { @@ -34,8 +34,8 @@ impl BudgetManager { info!("Getting budget process type for process {pid}."); - if td.cred().is_system() || pid == -1 || pid == td.proc().id().get() { - if pid == -1 || pid == td.proc().id().get() { + if td.cred().is_system() || pid == -1 || pid == td.proc().id() { + if pid == -1 || pid == td.proc().id() { // Lookup budget. let id = td.proc().budget_id(); @@ -82,21 +82,18 @@ pub enum BudgetType { FdIpcSocket = 11, } -#[allow(dead_code)] #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ProcType { - BigApp, - MiniApp, - System, // TODO: Verify this. + BigApp = 0, + #[allow(unused)] + MiniApp = 1, + #[allow(unused)] + System = 2, // TODO: Verify this. } impl Into for ProcType { fn into(self) -> u32 { - match self { - ProcType::BigApp => 0, - ProcType::MiniApp => 1, - ProcType::System => 2, - } + self as u32 } } diff --git a/src/kernel/src/dev/camera.rs b/src/kernel/src/dev/camera.rs new file mode 100644 index 000000000..2ecdfa46e --- /dev/null +++ b/src/kernel/src/dev/camera.rs @@ -0,0 +1,92 @@ +use crate::errno::Errno; +use crate::fs::{ + make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, IoLen, IoVec, IoVecMut, + MakeDevError, MakeDevFlags, Mode, OpenFlags, +}; +use crate::process::VThread; +use crate::ucred::{Gid, Uid}; +use std::sync::Arc; +use thiserror::Error; + +#[derive(Debug)] +struct Camera {} + +impl Camera { + fn new() -> Self { + Self {} + } +} + +impl DeviceDriver for Camera { + #[allow(unused_variables)] // TODO: remove when implementing + fn open( + &self, + dev: &Arc, + mode: OpenFlags, + devtype: i32, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } + + #[allow(unused_variables)] // TODO: remove when implementing + fn read( + &self, + dev: &Arc, + off: Option, + buf: &mut [IoVecMut], + td: Option<&VThread>, + ) -> Result> { + todo!() + } + + #[allow(unused_variables)] // TODO: remove when implementing + fn write( + &self, + dev: &Arc, + off: Option, + buf: &[IoVec], + td: Option<&VThread>, + ) -> Result> { + todo!() + } + + #[allow(unused_variables)] // TODO: remove when implementing + fn ioctl( + &self, + dev: &Arc, + cmd: IoCmd, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } +} + +pub struct CameraManager { + camera: Arc, +} + +impl CameraManager { + pub fn new() -> Result, CameraInitError> { + let camera = make_dev( + Camera::new(), + DriverFlags::from_bits_retain(0x80000004), + 0, + "camera", + Uid::ROOT, + Gid::ROOT, + Mode::new(0o666).unwrap(), + None, + MakeDevFlags::ETERNAL, + )?; + + Ok(Arc::new(Self { camera })) + } +} + +/// Represents an error when [`CameraManager`] fails to initialize. +#[derive(Debug, Error)] +pub enum CameraInitError { + #[error("cannot create camera device")] + CreateGcFailed(#[from] MakeDevError), +} diff --git a/src/kernel/src/dev/dce.rs b/src/kernel/src/dev/dce.rs new file mode 100644 index 000000000..372093784 --- /dev/null +++ b/src/kernel/src/dev/dce.rs @@ -0,0 +1,115 @@ +use crate::{ + errno::Errno, + fs::{ + make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, MakeDevError, MakeDevFlags, + Mode, OpenFlags, + }, + process::VThread, + ucred::{Gid, Uid}, +}; +use std::sync::Arc; +use thiserror::Error; + +#[derive(Debug)] +struct Dce {} + +impl Dce { + fn new() -> Self { + Self {} + } +} + +impl DeviceDriver for Dce { + #[allow(unused_variables)] // TODO: remove when implementing + fn open( + &self, + dev: &Arc, + mode: OpenFlags, + devtype: i32, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } + + fn ioctl( + &self, + _: &Arc, + cmd: IoCmd, + _: Option<&VThread>, + ) -> Result<(), Box> { + match cmd { + IoCmd::DCEFLIPCONTROL(_) => todo!("DCEFLIPCONTROL ioctl"), + IoCmd::DCESUBMITFLIP(_) => todo!("DCESUBMITFLIP ioctl"), + IoCmd::DCEREGBUFPTRS(_) => todo!("DCEREGBUFPOINTERS ioctl"), + IoCmd::DCEREGBUFATTR(_) => todo!("DCEREGBUFATTR ioctl"), + IoCmd::DCEDEREGIDENT(_) => todo!("DCEDEREGIDENT ioctl"), + _ => todo!(), + } + } +} + +pub struct DceManager { + dce: Arc, +} + +impl DceManager { + pub fn new() -> Result, DceInitError> { + let dce = make_dev( + Dce::new(), + DriverFlags::INIT, + 0, + "dce", + Uid::ROOT, + Gid::ROOT, + Mode::new(0o666).unwrap(), + None, + MakeDevFlags::empty(), + )?; + + Ok(Arc::new(Self { dce })) + } +} + +#[repr(C)] +#[derive(Debug)] +pub struct DceFlipControlArg { + id: u32, + arg1: usize, + arg2: usize, + arg3: usize, + arg4: u64, + arg5: u64, +} + +#[repr(C)] +#[derive(Debug)] +pub struct DceSubmitFlipArg { + canary: usize, + buffer_index: usize, + flip_mode: u32, + arg1: usize, + arg2: usize, + eop_nz: u32, + eop_val: usize, + unk: usize, + rout: usize, +} + +#[repr(C)] +#[derive(Debug)] +pub struct DceRegisterBufferPtrsArg { + canary: usize, + index: u32, + attrid: u32, + left: usize, + right: usize, + unk: u32, + _align: u64, +} + +/// Represents an error when [`DceManager`] fails to initialize. +#[derive(Debug, Error)] +pub enum DceInitError { + #[error("cannot create dce device")] + CreateDceFailed(#[from] MakeDevError), +} diff --git a/src/kernel/src/dev/deci.rs b/src/kernel/src/dev/deci.rs index 18eeff066..7d169981e 100644 --- a/src/kernel/src/dev/deci.rs +++ b/src/kernel/src/dev/deci.rs @@ -1,9 +1,10 @@ +use crate::errno::Errno; use crate::fs::{ - make_dev, CharacterDevice, DeviceDriver, DriverFlags, MakeDevError, MakeDevFlags, Mode, - OpenFlags, Uio, UioMut, + make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoLen, IoVec, IoVecMut, MakeDevError, + MakeDevFlags, Mode, OpenFlags, }; +use crate::process::VThread; use crate::ucred::{Gid, Uid}; -use crate::{errno::Errno, process::VThread}; use std::sync::Arc; use thiserror::Error; @@ -32,9 +33,10 @@ impl DeviceDriver for Driver { fn read( &self, dev: &Arc, - data: &mut UioMut, + off: Option, + buf: &mut [IoVecMut], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { todo!() } @@ -42,9 +44,10 @@ impl DeviceDriver for Driver { fn write( &self, dev: &Arc, - data: &mut Uio, + off: Option, + buf: &[IoVec], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { todo!() } } diff --git a/src/kernel/src/dev/dipsw.rs b/src/kernel/src/dev/dipsw.rs index 59beec616..e773a3287 100644 --- a/src/kernel/src/dev/dipsw.rs +++ b/src/kernel/src/dev/dipsw.rs @@ -31,13 +31,13 @@ impl DeviceDriver for Dipsw { let td = td.unwrap(); if !td.cred().is_system() { - todo!() - } else { match cmd { // TODO: properly implement this - IoCmd::DIPSWCHECK2(val) => *val = false as i32, + IoCmd::DIPSWCHECK2(val) | IoCmd::DIPSWUNK(val) => *val = false as i32, _ => todo!(), } + } else { + todo!() } Ok(()) @@ -59,16 +59,16 @@ impl DipswManager { Gid::ROOT, Mode::new(0o644).unwrap(), None, - MakeDevFlags::MAKEDEV_ETERNAL, + MakeDevFlags::ETERNAL, )?; Ok(Arc::new(Self { dipsw })) } } -/// Represents an error when [`TtyManager`] fails to initialize. +/// Represents an error when [`DipswManager`] fails to initialize. #[derive(Debug, Error)] pub enum DipswInitError { #[error("cannot create dipsw device")] - CreateConsoleFailed(#[from] MakeDevError), + CreateDipswFailed(#[from] MakeDevError), } diff --git a/src/kernel/src/dev/dmem.rs b/src/kernel/src/dev/dmem.rs index 6270b5398..965cb3848 100644 --- a/src/kernel/src/dev/dmem.rs +++ b/src/kernel/src/dev/dmem.rs @@ -30,14 +30,14 @@ pub enum DmemContainer { Two, } -impl TryInto for i32 { +impl TryFrom for DmemContainer { type Error = SysErr; - fn try_into(self) -> Result { - match self { - 0 => Ok(DmemContainer::Zero), - 1 => Ok(DmemContainer::One), - 2 => Ok(DmemContainer::Two), + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(Self::Zero), + 1 => Ok(Self::One), + 2 => Ok(Self::Two), _ => Err(SysErr::Raw(EINVAL)), } } @@ -74,6 +74,7 @@ impl DeviceDriver for Dmem { IoCmd::DMEMGETAVAIL(_avail) => todo!(), IoCmd::DMEMALLOC(_alloc) => todo!(), IoCmd::DMEMQUERY(_query) => todo!(), + IoCmd::DMEMALLOCMAIN(_alloc) => todo!(), _ => todo!(), } diff --git a/src/kernel/src/dev/gc.rs b/src/kernel/src/dev/gc.rs index 83ed84c80..d9c608bc9 100644 --- a/src/kernel/src/dev/gc.rs +++ b/src/kernel/src/dev/gc.rs @@ -1,12 +1,25 @@ use crate::{ errno::Errno, - fs::{CharacterDevice, DeviceDriver, IoCmd, OpenFlags}, + fs::{ + make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, MakeDevError, MakeDevFlags, + Mode, OpenFlags, + }, process::VThread, + ucred::{Gid, Uid}, }; use std::sync::Arc; +use thiserror::Error; #[derive(Debug)] -struct Gc {} +struct Gc { + suspended: bool, +} + +impl Gc { + fn new() -> Self { + Self { suspended: false } + } +} impl DeviceDriver for Gc { #[allow(unused_variables)] // TODO: remove when implementing @@ -20,13 +33,124 @@ impl DeviceDriver for Gc { todo!() } - #[allow(unused_variables)] // TODO: remove when implementing fn ioctl( &self, - dev: &Arc, + _: &Arc, cmd: IoCmd, td: Option<&VThread>, ) -> Result<(), Box> { - todo!() + if self.suspended { + todo!("gc suspended") + } + + let td = td.unwrap(); + + let gc_check_passed = td.cred().unk_gc_check(); + // TODO: implement devfs_get_cdevpriv + + match cmd { + IoCmd::GCSETWAVELIMITMULTIPLIER(mult) => todo!("GCSETWAVELIMITMULTIPLIER: {mult:?}"), + IoCmd::GCSUBMIT(submit_arg) => todo!("GCSUBMIT ioctl: {submit_arg:?}"), + IoCmd::GCGETCUMASK(mask) => todo!("GCGETCUMASK ioctl: {mask:?}"), + IoCmd::GCMAPCOMPUTEQUEUE(queue) => todo!("GCMAPCOMPUTEQUEUE ioctl: {queue:?}"), + IoCmd::GCUNMAPCOMPUTEQUEUE(unk) => todo!("GCUNMAPCOMPUTEQUEUE ioctl: {unk:?}"), + IoCmd::GCSETGSRINGSIZES(unk1) => { + for _ in 0..100 { + todo!() + } + + todo!("GCSETGSRINGSIZES ioctl: {unk1:?}") + } + IoCmd::GCMIPSTATSREPORT(report) => todo!("GCMIPSTATSREPORT ioctl: {report:?}"), + IoCmd::GCARESUBMITSALLOWED(unk) => todo!("GCARESUBMITSALLOWED ioctl: {unk:?}"), + IoCmd::GCGETNUMTCAUNITS(num) => todo!("GCGETNUMTCAUNITS ioctl: {num:?}"), + IoCmd::GCDINGDONGFORWORKLOAD(unk) => todo!("GCDINGDONGFORWORKLOAD ioctl: {unk:?}"), + _ => todo!(), + } } } + +pub struct GcManager { + gc: Arc, +} + +impl GcManager { + pub fn new() -> Result, GcInitError> { + let gc = make_dev( + Gc::new(), + DriverFlags::from_bits_retain(0x80000004), + 0, + "gc", + Uid::ROOT, + Gid::ROOT, + Mode::new(0o666).unwrap(), + None, + MakeDevFlags::ETERNAL, + )?; + + Ok(Arc::new(Self { gc })) + } +} + +#[derive(Debug)] +#[repr(C)] +pub struct SubmitArg { + pid: i32, + count: i32, + commands: usize, // TODO: this is actually an address +} + +#[derive(Debug)] +#[repr(C)] +pub struct CuMask { + unk1: i32, + unk2: i32, + unk3: i32, + unk4: i32, +} + +#[derive(Debug)] +#[repr(C)] +pub struct MapComputeQueueArg { + pipe_hi: u32, + pipe_lo: u32, + queue_id: u32, + offset: u32, + ring_base_address: usize, + read_ptr_address: usize, + ding_dong: usize, + len_log: u32, +} + +#[derive(Debug)] +#[repr(C)] +pub struct UnmapComputeQueueArg { + unk1: u32, + unk2: u32, + unk3: u32, +} + +#[derive(Debug)] +#[repr(C)] +pub struct MipStatsReport { + ty: i32, + unk1: i32, + unk2: i32, + unk4: [u8; 120], +} + +#[derive(Debug)] +#[repr(C)] +pub struct DingDongForWorkload { + unk1: i32, + unk2: i32, + unk3: i32, + unk4: i32, +} + +/// Represents an error when [`GcManager`] fails to initialize. +#[derive(Debug, Error)] +pub enum GcInitError { + #[error("cannot create gc device")] + CreateGcFailed(#[from] MakeDevError), +} diff --git a/src/kernel/src/dev/hid.rs b/src/kernel/src/dev/hid.rs index 4be4db498..2a6592fed 100644 --- a/src/kernel/src/dev/hid.rs +++ b/src/kernel/src/dev/hid.rs @@ -1,8 +1,6 @@ -use crate::{ - errno::Errno, - fs::{CharacterDevice, DeviceDriver, IoCmd, UioMut}, - process::VThread, -}; +use crate::errno::Errno; +use crate::fs::{CharacterDevice, DeviceDriver, IoCmd, IoLen, IoVecMut}; +use crate::process::VThread; use std::sync::Arc; #[derive(Debug)] @@ -13,9 +11,10 @@ impl DeviceDriver for Hid { fn read( &self, dev: &Arc, - data: &mut UioMut, + off: Option, + buf: &mut [IoVecMut], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { todo!() } diff --git a/src/kernel/src/dev/hmd.rs b/src/kernel/src/dev/hmd.rs new file mode 100644 index 000000000..92ef8e5cd --- /dev/null +++ b/src/kernel/src/dev/hmd.rs @@ -0,0 +1,208 @@ +use crate::{ + errno::Errno, + fs::{ + make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, MakeDevError, MakeDevFlags, + Mode, OpenFlags, + }, + process::VThread, + ucred::{Gid, Uid}, +}; +use std::sync::Arc; +use thiserror::Error; + +#[derive(Debug)] +struct HmdCmd {} + +impl DeviceDriver for HmdCmd { + #[allow(unused_variables)] // TODO: remove when implementing + fn open( + &self, + dev: &Arc, + mode: OpenFlags, + devtype: i32, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } + + #[allow(unused_variables)] // TODO: remove when implementing + fn ioctl( + &self, + dev: &Arc, + cmd: IoCmd, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } +} + +#[derive(Debug)] +struct HmdSnsr {} + +impl DeviceDriver for HmdSnsr { + #[allow(unused_variables)] // TODO: remove when implementing + fn ioctl( + &self, + dev: &Arc, + cmd: IoCmd, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } +} + +#[derive(Debug)] +struct Hmd3da {} + +impl DeviceDriver for Hmd3da { + #[allow(unused_variables)] // TODO: remove when implementing + fn ioctl( + &self, + dev: &Arc, + cmd: IoCmd, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } +} + +#[derive(Debug)] +struct HmdDist {} + +impl DeviceDriver for HmdDist { + #[allow(unused_variables)] // TODO: remove when implementing + fn ioctl( + &self, + dev: &Arc, + cmd: IoCmd, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } +} + +#[derive(Debug)] +struct HmdCr {} + +impl DeviceDriver for HmdCr { + #[allow(unused_variables)] // TODO: remove when implementing + fn open( + &self, + dev: &Arc, + mode: OpenFlags, + devtype: i32, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } + + #[allow(unused_variables)] // TODO: remove when implementing + fn ioctl( + &self, + dev: &Arc, + cmd: IoCmd, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } +} + +pub struct HmdManager { + hmd_cmd: Arc, + hmd_snsr: Arc, + hmd_3da: Arc, + hmd_dist: Arc, + hmd_cr: Arc, +} + +impl HmdManager { + pub fn new() -> Result, HmdInitError> { + let hmd_cmd = make_dev( + HmdCmd {}, + DriverFlags::INIT, + 0, + "hmd_cmd", + Uid::ROOT, + Gid::ROOT, + Mode::new(0o666).unwrap(), + None, + MakeDevFlags::empty(), + ) + .map_err(HmdInitError::CreateHmdCmdFailed)?; + + let hmd_snsr = make_dev( + HmdSnsr {}, + DriverFlags::from_bits_retain(0x80080000), + 0, + "hmd_snsr", + Uid::ROOT, + Gid::ROOT, + Mode::new(0o666).unwrap(), + None, + MakeDevFlags::empty(), + ) + .map_err(HmdInitError::CreateHmdSnsrFailed)?; + + let hmd_3da = make_dev( + Hmd3da {}, + DriverFlags::INIT, + 0, + "hmd_3da", + Uid::ROOT, + Gid::ROOT, + Mode::new(0o666).unwrap(), + None, + MakeDevFlags::empty(), + ) + .map_err(HmdInitError::CreateHmd3daFailed)?; + + let hmd_dist = make_dev( + HmdDist {}, + DriverFlags::INIT, + 0, + "hmd_dist", + Uid::ROOT, + Gid::ROOT, + Mode::new(0o666).unwrap(), + None, + MakeDevFlags::empty(), + ) + .map_err(HmdInitError::CreateHmdDistFailed)?; + + let hmd_cr = make_dev( + HmdCr {}, + DriverFlags::INIT, + 0, + "hmd_cr", + Uid::ROOT, + Gid::ROOT, + Mode::new(0o600).unwrap(), + None, + MakeDevFlags::empty(), + ) + .map_err(HmdInitError::CreateHmdCrFailed)?; + + Ok(Arc::new(Self { + hmd_cmd, + hmd_snsr, + hmd_3da, + hmd_dist, + hmd_cr, + })) + } +} + +/// Represents an error when [`HmdManager`] fails to initialize. +#[derive(Debug, Error)] +pub enum HmdInitError { + #[error("cannot create hmd_cmd device")] + CreateHmdCmdFailed(#[source] MakeDevError), + #[error("cannot create hmd_snsr device")] + CreateHmdSnsrFailed(#[source] MakeDevError), + #[error("cannot create hmd_3da device")] + CreateHmd3daFailed(#[source] MakeDevError), + #[error("cannot create hmd_dist device")] + CreateHmdDistFailed(#[source] MakeDevError), + #[error("cannot create hmd_cr device")] + CreateHmdCrFailed(#[source] MakeDevError), +} diff --git a/src/kernel/src/dev/mod.rs b/src/kernel/src/dev/mod.rs index 3708bda70..47349bea3 100644 --- a/src/kernel/src/dev/mod.rs +++ b/src/kernel/src/dev/mod.rs @@ -1,18 +1,24 @@ +pub use camera::*; +pub use dce::*; pub use deci::*; pub use dipsw::*; pub use dmem::*; pub use gc::*; pub use hid::*; +pub use hmd::*; pub use random::*; pub use rng::*; pub use sbl_srv::*; pub use ttyconsole::*; +mod camera; +mod dce; mod deci; mod dipsw; mod dmem; mod gc; mod hid; +mod hmd; mod random; mod rng; mod sbl_srv; diff --git a/src/kernel/src/dev/random.rs b/src/kernel/src/dev/random.rs index 45e33fdbb..51ea7e7b8 100644 --- a/src/kernel/src/dev/random.rs +++ b/src/kernel/src/dev/random.rs @@ -1,8 +1,6 @@ -use crate::{ - errno::Errno, - fs::{CharacterDevice, DefaultDeviceError, DeviceDriver, IoCmd, Uio, UioMut}, - process::VThread, -}; +use crate::errno::Errno; +use crate::fs::{CharacterDevice, DefaultDeviceError, DeviceDriver, IoCmd, IoLen, IoVec, IoVecMut}; +use crate::process::VThread; use std::sync::Arc; #[derive(Debug)] @@ -13,9 +11,10 @@ impl DeviceDriver for Random { fn read( &self, dev: &Arc, - data: &mut UioMut, + off: Option, + buf: &mut [IoVecMut], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { todo!() } @@ -23,9 +22,10 @@ impl DeviceDriver for Random { fn write( &self, dev: &Arc, - data: &mut Uio, + off: Option, + buf: &[IoVec], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { todo!() } diff --git a/src/kernel/src/dev/rng.rs b/src/kernel/src/dev/rng.rs index 14bf1927a..f9c7d9cbb 100644 --- a/src/kernel/src/dev/rng.rs +++ b/src/kernel/src/dev/rng.rs @@ -1,24 +1,80 @@ use crate::{ + arnd, errno::Errno, - fs::{CharacterDevice, DeviceDriver, IoCmd}, + fs::{ + make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, MakeDevError, MakeDevFlags, + Mode, + }, process::VThread, + ucred::{Gid, Uid}, }; use std::sync::Arc; +use thiserror::Error; #[derive(Debug)] struct Rng {} +impl Rng { + fn new() -> Self { + Self {} + } +} + impl DeviceDriver for Rng { fn ioctl( &self, - dev: &Arc, + _: &Arc, cmd: IoCmd, _: Option<&VThread>, ) -> Result<(), Box> { match cmd { - IoCmd::RNG1 => todo!(), - IoCmd::RNG2 => todo!(), + // TODO: these are separate algorithms, and should be implemented as such, + // however arc4rand seems sufficient for now + IoCmd::RNGGETGENUINE(input) | IoCmd::RNGFIPS(input) => { + input.error = 0; + + arnd::rand_bytes(&mut input.data); + + Ok(()) + } _ => todo!(), // ENOIOCTL, } } } + +#[repr(C)] +#[derive(Debug)] +pub struct RngInput { + /// This field seems to be treated as an error + error: i32, + data: [u8; 64], +} + +pub struct RngManager { + rng: Arc, +} + +impl RngManager { + pub fn new() -> Result, RngInitError> { + let rng = make_dev( + Rng::new(), + DriverFlags::from_bits_retain(0x80000004), + 0, + "rng", + Uid::ROOT, + Gid::ROOT, + Mode::new(0o444).unwrap(), + None, + MakeDevFlags::ETERNAL, + )?; + + Ok(Arc::new(Self { rng })) + } +} + +/// Represents an error when [`RngManager`] fails to initialize. +#[derive(Debug, Error)] +pub enum RngInitError { + #[error("cannot create rng device")] + CreateRngFailed(#[from] MakeDevError), +} diff --git a/src/kernel/src/dev/sbl_srv.rs b/src/kernel/src/dev/sbl_srv.rs index 27f1f775f..d6db3e9c8 100644 --- a/src/kernel/src/dev/sbl_srv.rs +++ b/src/kernel/src/dev/sbl_srv.rs @@ -1,13 +1,24 @@ use crate::{ errno::Errno, - fs::{CharacterDevice, DeviceDriver, IoCmd}, + fs::{ + make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, MakeDevError, MakeDevFlags, + Mode, + }, process::VThread, + ucred::{Gid, Uid}, }; use std::sync::Arc; +use thiserror::Error; #[derive(Debug)] struct SblSrv {} +impl SblSrv { + fn new() -> Self { + Self {} + } +} + impl DeviceDriver for SblSrv { #[allow(unused_variables)] // TODO: remove when implementing fn ioctl( @@ -19,3 +30,33 @@ impl DeviceDriver for SblSrv { todo!() } } + +pub struct SblSrvManager { + sbl: Arc, +} + +impl SblSrvManager { + pub fn new() -> Result, SblSrvInitError> { + let sbl = make_dev( + SblSrv {}, + DriverFlags::INIT, + 0, + "sbl_srv", + Uid::ROOT, + Gid::ROOT, + Mode::new(0o600).unwrap(), + None, + MakeDevFlags::empty(), + ) + .map_err(SblSrvInitError::CreateSblSrvFailed)?; + + Ok(Arc::new(Self { sbl })) + } +} + +/// Represents an error when [`SblSrvManager`] fails to initialize. +#[derive(Debug, Error)] +pub enum SblSrvInitError { + #[error("cannot create sbl_srv device")] + CreateSblSrvFailed(#[source] MakeDevError), +} diff --git a/src/kernel/src/dev/ttyconsole.rs b/src/kernel/src/dev/ttyconsole.rs index b0ecb0317..20a774365 100644 --- a/src/kernel/src/dev/ttyconsole.rs +++ b/src/kernel/src/dev/ttyconsole.rs @@ -1,10 +1,10 @@ -use crate::errno::EPERM; +use crate::errno::Errno; use crate::fs::{ - make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, MakeDevError, MakeDevFlags, Mode, - OpenFlags, Uio, UioMut, + make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, IoLen, IoVec, IoVecMut, + MakeDevError, MakeDevFlags, Mode, OpenFlags, }; +use crate::process::VThread; use crate::ucred::{Gid, Uid}; -use crate::{errno::Errno, process::VThread}; use macros::Errno; use std::sync::Arc; use thiserror::Error; @@ -36,9 +36,10 @@ impl DeviceDriver for TtyConsole { fn read( &self, dev: &Arc, - data: &mut UioMut, + off: Option, + buf: &mut [IoVecMut], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { todo!() } @@ -46,9 +47,10 @@ impl DeviceDriver for TtyConsole { fn write( &self, dev: &Arc, - data: &mut Uio, + off: Option, + buf: &[IoVec], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { todo!() } @@ -86,13 +88,10 @@ impl Tty { self.generic_ioctl(cmd, td) } - /// See `tty_ioctl` on the PS4 for a reference. - fn generic_ioctl(&self, cmd: IoCmd, td: Option<&VThread>) -> Result<(), TtyIoctlError> { - // TODO: implement ttydevsw_ioctl - + /// See `tty_generic_ioctl` on the PS4 for a reference. + fn generic_ioctl(&self, cmd: IoCmd, _td: Option<&VThread>) -> Result<(), TtyIoctlError> { match cmd { - // TODO: implement this properly - IoCmd::TIOCSCTTY => return Ok(()), + IoCmd::TIOCSCTTY => todo!(), _ => todo!(), } } @@ -118,7 +117,7 @@ impl TtyManager { Gid::ROOT, Mode::new(0o600).unwrap(), None, - MakeDevFlags::MAKEDEV_ETERNAL, + MakeDevFlags::ETERNAL, )?; Ok(Arc::new(Self { console })) @@ -134,8 +133,4 @@ pub enum TtyManagerInitError { /// Represents an error when [`Tty::ioctl`] fails to initialize. #[derive(Debug, Error, Errno)] -pub enum TtyIoctlError { - #[error("process is not leader")] - #[errno(EPERM)] - ProcessNotLeader, -} +pub enum TtyIoctlError {} diff --git a/src/kernel/src/dmem/blockpool.rs b/src/kernel/src/dmem/blockpool.rs index 53b8f5bfe..727582898 100644 --- a/src/kernel/src/dmem/blockpool.rs +++ b/src/kernel/src/dmem/blockpool.rs @@ -1,5 +1,5 @@ use crate::errno::Errno; -use crate::fs::{DefaultFileBackendError, FileBackend, IoCmd, PollEvents, Stat, VFile}; +use crate::fs::{DefaultFileBackendError, FileBackend, IoCmd, PollEvents, Stat, VFile, Vnode}; use crate::process::VThread; use std::sync::Arc; @@ -7,19 +7,18 @@ use std::sync::Arc; pub struct BlockPool {} impl BlockPool { - pub fn new() -> Arc { - Arc::new(Self {}) + pub fn new() -> Self { + Self {} } } impl FileBackend for BlockPool { + fn is_seekable(&self) -> bool { + todo!() + } + #[allow(unused_variables)] // TODO: remove when implementing - fn ioctl( - self: &Arc, - file: &VFile, - cmd: IoCmd, - td: Option<&VThread>, - ) -> Result<(), Box> { + fn ioctl(&self, file: &VFile, cmd: IoCmd, td: Option<&VThread>) -> Result<(), Box> { match cmd { IoCmd::BPOOLEXPAND(args) => todo!(), IoCmd::BPOOLSTATS(out) => todo!(), @@ -28,11 +27,11 @@ impl FileBackend for BlockPool { } #[allow(unused_variables)] // TODO: remove when implementing - fn poll(self: &Arc, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents { + fn poll(&self, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents { todo!() } - fn stat(self: &Arc, _: &VFile, _: Option<&VThread>) -> Result> { + fn stat(&self, _: &VFile, _: Option<&VThread>) -> Result> { let mut stat = Stat::zeroed(); stat.block_size = 0x10000; @@ -40,6 +39,10 @@ impl FileBackend for BlockPool { todo!() } + + fn vnode(&self) -> Option<&Arc> { + None + } } #[repr(C)] diff --git a/src/kernel/src/dmem/mod.rs b/src/kernel/src/dmem/mod.rs index b26afc621..8c41a5aab 100644 --- a/src/kernel/src/dmem/mod.rs +++ b/src/kernel/src/dmem/mod.rs @@ -1,7 +1,8 @@ +use self::blockpool::BlockPool; use crate::dev::{Dmem, DmemContainer}; use crate::errno::EINVAL; use crate::fs::{ - make_dev, CharacterDevice, DriverFlags, Fs, MakeDevError, MakeDevFlags, Mode, VFile, VFileType, + make_dev, CharacterDevice, DriverFlags, Fs, MakeDevError, MakeDevFlags, Mode, VFile, VFileFlags, }; use crate::info; use crate::process::VThread; @@ -11,7 +12,7 @@ use std::ops::Index; use std::sync::Arc; use thiserror::Error; -pub use self::blockpool::*; +pub use self::blockpool::{BlockpoolExpandArgs, BlockpoolStats}; mod blockpool; @@ -43,7 +44,7 @@ impl DmemManager { let name = "dmem0"; match make_dev( Dmem::new(Self::DMEM_TOTAL_SIZE, DmemContainer::Zero), - DriverFlags::D_INIT, + DriverFlags::INIT, 0, name, Uid::ROOT, @@ -61,7 +62,7 @@ impl DmemManager { let name = "dmem1"; match make_dev( Dmem::new(Self::DMEM_TOTAL_SIZE, DmemContainer::One), - DriverFlags::D_INIT, + DriverFlags::INIT, 0, name, Uid::ROOT, @@ -79,7 +80,7 @@ impl DmemManager { let name = "dmem2"; match make_dev( Dmem::new(Self::DMEM_TOTAL_SIZE, DmemContainer::Two), - DriverFlags::D_INIT, + DriverFlags::INIT, 0, name, Uid::ROOT, @@ -116,6 +117,8 @@ impl DmemManager { let dmem_container = td.proc().dmem_container_mut(); let current_container = *dmem_container; + info!("Getting dmem container"); + if dmem_id != -1 { todo!() } @@ -131,11 +134,13 @@ impl DmemManager { } let bp = BlockPool::new(); - + let flags = VFileFlags::from_bits_retain(flags) | VFileFlags::WRITE; let fd = td .proc() .files() - .alloc(Arc::new(VFile::new(VFileType::Blockpool(bp)))); + .alloc(Arc::new(VFile::new(flags, Box::new(bp)))); + + info!("Opened a blockpool at fd = {fd}"); Ok(fd.into()) } diff --git a/src/kernel/src/errno.rs b/src/kernel/src/errno.rs index 151174301..c5ee44c9a 100644 --- a/src/kernel/src/errno.rs +++ b/src/kernel/src/errno.rs @@ -1,10 +1,10 @@ +//! This module contains errno used in a PS4 system. The value of each errno must be the same as the +//! PS4. use std::convert::Infallible; use std::error::Error; +use std::hint::unreachable_unchecked; use std::num::NonZeroI32; -// This file contains errno used in a PS4 system. The value of each errno must be the same as the -// PS4. - macro_rules! error_numbers { ($($name:ident($num:expr) => $desc:literal,)*) => { $( @@ -127,7 +127,7 @@ error_numbers! { } /// An object that is mappable to PS4 errno. -pub trait Errno: Error { +pub trait Errno: Error + Send + Sync { fn errno(&self) -> NonZeroI32; } @@ -143,14 +143,16 @@ impl From for Box { } } +impl Errno for Infallible { + fn errno(&self) -> NonZeroI32 { + // SAFETY: This is safe because Infallible type guarantee its value cannot be constructed, + // which imply this method cannot be called because it required a value of Infallible type. + unsafe { unreachable_unchecked() }; + } +} + /// Get human readable text. pub fn strerror(num: NonZeroI32) -> &'static str { // This function is generated inside the macro `error_numbers!`. strerror_impl(num) } - -impl Errno for Infallible { - fn errno(&self) -> NonZeroI32 { - match *self {} - } -} diff --git a/src/kernel/src/fs/dev/cdev.rs b/src/kernel/src/fs/dev/cdev.rs index 96419661b..109a08206 100644 --- a/src/kernel/src/fs/dev/cdev.rs +++ b/src/kernel/src/fs/dev/cdev.rs @@ -1,8 +1,8 @@ use super::dirent::Dirent; use crate::errno::{Errno, ENODEV, ENOTTY}; -use crate::fs::Uio; use crate::fs::{ - FileBackend, IoCmd, Mode, OpenFlags, PollEvents, Stat, TruncateLength, UioMut, VFile, + FileBackend, IoCmd, IoLen, IoVec, IoVecMut, Mode, OpenFlags, PollEvents, Stat, TruncateLength, + VFile, Vnode, }; use crate::process::VThread; use crate::time::TimeSpec; @@ -109,62 +109,74 @@ impl CharacterDevice { } } -impl FileBackend for CharacterDevice { - #[allow(unused_variables)] // TODO: remove when implementing +/// Implementation of `devfs_ops_f`. +#[derive(Debug)] +pub(super) struct CdevFileBackend { + vnode: Arc, + dev: Arc, +} + +impl CdevFileBackend { + pub fn new(vnode: Arc, dev: Arc) -> Self { + Self { vnode, dev } + } +} + +impl FileBackend for CdevFileBackend { + fn is_seekable(&self) -> bool { + true + } + fn read( - self: &Arc, + &self, file: &VFile, - buf: &mut UioMut, + off: u64, + buf: &mut [IoVecMut], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { todo!() } - #[allow(unused_variables)] // TODO: remove when implementing fn write( - self: &Arc, + &self, file: &VFile, - buf: &mut Uio, + off: u64, + buf: &[IoVec], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { todo!() } - #[allow(unused_variables)] // TODO: remove when implementing - fn ioctl( - self: &Arc, - file: &VFile, - cmd: IoCmd, - td: Option<&VThread>, - ) -> Result<(), Box> { + fn ioctl(&self, file: &VFile, cmd: IoCmd, td: Option<&VThread>) -> Result<(), Box> { match cmd { IoCmd::FIODTYPE(_) => todo!(), IoCmd::FIODGNAME(_) => todo!(), - _ => self.driver.ioctl(self, cmd, td)?, + _ => self.dev.driver.ioctl(&self.dev, cmd, td)?, } Ok(()) } - #[allow(unused_variables)] // TODO: remove when implementing - fn poll(self: &Arc, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents { + fn poll(&self, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents { todo!() } - #[allow(unused_variables)] // TODO: remove when implementing - fn stat(self: &Arc, file: &VFile, td: Option<&VThread>) -> Result> { + fn stat(&self, file: &VFile, td: Option<&VThread>) -> Result> { todo!() } - #[allow(unused_variables)] // TODO: remove when implementing fn truncate( - self: &Arc, + &self, file: &VFile, length: TruncateLength, td: Option<&VThread>, ) -> Result<(), Box> { todo!() } + + fn vnode(&self) -> Option<&Arc> { + Some(&self.vnode) + } } bitflags! { @@ -180,8 +192,8 @@ bitflags! { /// Flags for [`CdevSw`]. #[derive(Debug, Clone, Copy)] pub struct DriverFlags: u32 { - const D_NEEDMINOR = 0x00800000; - const D_INIT = 0x80000000; + const NEEDMINOR = 0x00800000; + const INIT = 0x80000000; } } @@ -209,9 +221,10 @@ pub trait DeviceDriver: Debug + Sync + Send + 'static { fn read( &self, dev: &Arc, - data: &mut UioMut, + off: Option, // TODO: Check if we actually need this for a character device. + buf: &mut [IoVecMut], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { Err(Box::new(DefaultDeviceError::ReadNotSupported)) } @@ -219,9 +232,10 @@ pub trait DeviceDriver: Debug + Sync + Send + 'static { fn write( &self, dev: &Arc, - data: &mut Uio, + off: Option, // TODO: Check if we actually need this for a character device. + buf: &[IoVec], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { Err(Box::new(DefaultDeviceError::WriteNotSupported)) } diff --git a/src/kernel/src/fs/dev/mod.rs b/src/kernel/src/fs/dev/mod.rs index b93368751..b49dc16a0 100644 --- a/src/kernel/src/fs/dev/mod.rs +++ b/src/kernel/src/fs/dev/mod.rs @@ -30,7 +30,7 @@ pub fn make_dev( cred: Option>, flags: MakeDevFlags, ) -> Result, MakeDevError> { - if driver_flags.intersects(DriverFlags::D_NEEDMINOR) { + if driver_flags.intersects(DriverFlags::NEEDMINOR) { todo!("make_dev with D_NEEDMINOR"); } @@ -44,7 +44,7 @@ pub fn make_dev( // Get device flags. let mut df = DeviceFlags::empty(); - if flags.intersects(MakeDevFlags::MAKEDEV_ETERNAL) { + if flags.intersects(MakeDevFlags::ETERNAL) { df |= DeviceFlags::SI_ETERNAL; } @@ -293,7 +293,7 @@ bitflags! { /// Flags for [`make_dev()`]. #[derive(Clone, Copy)] pub struct MakeDevFlags: u32 { - const MAKEDEV_ETERNAL = 0x10; + const ETERNAL = 0x10; } } @@ -313,7 +313,7 @@ impl Devices { /// Represents an error when [`make_dev()`] is failed. #[derive(Debug, Error, Errno)] pub enum MakeDevError { - #[error("the device with the same name already exist")] + #[error("the device with the same name already exists")] #[errno(EEXIST)] AlreadyExist(String), } diff --git a/src/kernel/src/fs/dev/vnode.rs b/src/kernel/src/fs/dev/vnode.rs index ce0748469..990939dd5 100644 --- a/src/kernel/src/fs/dev/vnode.rs +++ b/src/kernel/src/fs/dev/vnode.rs @@ -1,11 +1,13 @@ use super::dirent::Dirent; -use super::{AllocVnodeError, DevFs}; +use super::{AllocVnodeError, CdevFileBackend, DevFs}; use crate::errno::{Errno, EIO, ENOENT, ENOTDIR, ENXIO}; use crate::fs::{ - check_access, Access, IoCmd, RevokeFlags, Uio, UioMut, Vnode, VnodeAttrs, VnodeType, + check_access, Access, FileBackend, IoCmd, IoLen, IoVec, IoVecMut, RevokeFlags, Vnode, + VnodeAttrs, VnodeFileBackend, VnodeItem, VnodeType, }; use crate::process::VThread; use macros::Errno; +use std::ops::Deref; use std::sync::Arc; use thiserror::Error; @@ -169,27 +171,35 @@ impl crate::fs::VnodeBackend for VnodeBackend { } fn revoke(&self, vn: &Arc, _flags: RevokeFlags) -> Result<(), Box> { - // TODO: Implement this. - Ok(()) + todo!() } fn read( &self, - #[allow(unused_variables)] vn: &Arc, - #[allow(unused_variables)] buf: &mut UioMut, - #[allow(unused_variables)] td: Option<&VThread>, - ) -> Result<(), Box> { + vn: &Arc, + off: u64, + buf: &mut [IoVecMut], + td: Option<&VThread>, + ) -> Result> { todo!() } fn write( &self, - #[allow(unused_variables)] vn: &Arc, - #[allow(unused_variables)] buf: &mut Uio, - #[allow(unused_variables)] td: Option<&VThread>, - ) -> Result<(), Box> { + vn: &Arc, + off: u64, + buf: &[IoVec], + td: Option<&VThread>, + ) -> Result> { todo!() } + + fn to_file_backend(&self, vn: &Arc) -> Box { + match vn.item().deref() { + Some(VnodeItem::Device(d)) => Box::new(CdevFileBackend::new(vn.clone(), d.clone())), + _ => Box::new(VnodeFileBackend::new(vn.clone())), + } + } } /// Represents an error when [`lookup()`] is failed. diff --git a/src/kernel/src/fs/file.rs b/src/kernel/src/fs/file.rs index 60c4f4098..38cacf186 100644 --- a/src/kernel/src/fs/file.rs +++ b/src/kernel/src/fs/file.rs @@ -1,15 +1,11 @@ -use super::{CharacterDevice, IoCmd, Offset, Stat, TruncateLength, Uio, UioMut, Vnode}; -use crate::dmem::BlockPool; -use crate::errno::Errno; -use crate::errno::{EINVAL, ENOTTY, ENXIO, EOPNOTSUPP}; +use super::{IoCmd, IoLen, IoVecMut, Stat, TruncateLength, Vnode}; +use crate::errno::{Errno, EINVAL, ENOTTY, ENXIO, EOPNOTSUPP}; use crate::fs::{IoVec, PollEvents}; -use crate::kqueue::KernelQueue; -use crate::net::Socket; use crate::process::VThread; -use crate::shm::SharedMemory; use bitflags::bitflags; use gmtx::{Gutex, GutexGroup}; use macros::Errno; +use std::any::{Any, TypeId}; use std::fmt::Debug; use std::io::{ErrorKind, Read, Seek, SeekFrom}; use std::sync::Arc; @@ -18,19 +14,19 @@ use thiserror::Error; /// An implementation of `file` structure. #[derive(Debug)] pub struct VFile { - ty: VFileType, // f_type flags: VFileFlags, // f_flag - offset: Gutex, // f_offset + offset: Gutex, // f_offset + backend: Box, } impl VFile { - pub fn new(ty: VFileType) -> Self { + pub fn new(flags: VFileFlags, backend: Box) -> Self { let gg = GutexGroup::new(); Self { - ty, - flags: VFileFlags::empty(), + flags, offset: gg.spawn(0), + backend, } } @@ -38,82 +34,20 @@ impl VFile { self.flags } - pub fn flags_mut(&mut self) -> &mut VFileFlags { - &mut self.flags + pub fn is_seekable(&self) -> bool { + self.backend.is_seekable() } - /// Checking if this returns `Some` is equivalent to when FreeBSD and the PS4 check - /// fp->f_ops->fo_flags & DFLAG_SEEKABLE != 0, therefore we use this instead. - pub fn seekable_vnode(&self) -> Option<&Arc> { - match &self.ty { - VFileType::Vnode(vn) => Some(vn), - VFileType::Device(_) => todo!(), - _ => None, - } - } - - /// See `dofileread` on the PS4 for a reference. - pub fn do_read(&self, mut uio: UioMut, td: Option<&VThread>) -> Result> { - if uio.bytes_left == 0 { - return Ok(0); - } - - // TODO: consider implementing ktrace. - - todo!() - } - - /// See `dofilewrite` on the PS4 for a reference. - pub fn do_write(&self, mut uio: Uio, td: Option<&VThread>) -> Result> { - // TODO: consider implementing ktrace. - // TODO: implement bwillwrite. - - todo!() - } - - fn read(&self, buf: &mut UioMut, td: Option<&VThread>) -> Result<(), Box> { - match &self.ty { - VFileType::Vnode(vn) => FileBackend::read(vn, self, buf, td), - VFileType::Socket(so) | VFileType::IpcSocket(so) => so.read(self, buf, td), - VFileType::KernelQueue(kq) => kq.read(self, buf, td), - VFileType::SharedMemory(shm) => shm.read(self, buf, td), - VFileType::Device(dev) => dev.read(self, buf, td), - VFileType::Blockpool(bp) => bp.read(self, buf, td), - } + pub fn vnode(&self) -> Option<&Arc> { + self.backend.vnode() } - fn write(&self, buf: &mut Uio, td: Option<&VThread>) -> Result<(), Box> { - match &self.ty { - VFileType::Vnode(vn) => FileBackend::write(vn, self, buf, td), - VFileType::Socket(so) | VFileType::IpcSocket(so) => so.write(self, buf, td), - VFileType::KernelQueue(kq) => kq.write(self, buf, td), - VFileType::SharedMemory(shm) => shm.write(self, buf, td), - VFileType::Device(dev) => dev.write(self, buf, td), - VFileType::Blockpool(bp) => bp.write(self, buf, td), - } - } - - /// See `fo_ioctl` on the PS4 for a reference. pub fn ioctl(&self, cmd: IoCmd, td: Option<&VThread>) -> Result<(), Box> { - match &self.ty { - VFileType::Vnode(vn) => vn.ioctl(self, cmd, td), - VFileType::Socket(so) | VFileType::IpcSocket(so) => so.ioctl(self, cmd, td), - VFileType::KernelQueue(kq) => kq.ioctl(self, cmd, td), - VFileType::SharedMemory(shm) => shm.ioctl(self, cmd, td), - VFileType::Device(dev) => dev.ioctl(self, cmd, td), - VFileType::Blockpool(bp) => bp.ioctl(self, cmd, td), - } + self.backend.ioctl(self, cmd, td) } pub fn stat(&self, td: Option<&VThread>) -> Result> { - match &self.ty { - VFileType::Vnode(vn) => vn.stat(self, td), - VFileType::Socket(so) | VFileType::IpcSocket(so) => so.stat(self, td), - VFileType::KernelQueue(kq) => kq.stat(self, td), - VFileType::SharedMemory(shm) => shm.stat(self, td), - VFileType::Device(dev) => dev.stat(self, td), - VFileType::Blockpool(bp) => bp.stat(self, td), - } + self.backend.stat(self, td) } pub fn truncate( @@ -121,20 +55,37 @@ impl VFile { length: TruncateLength, td: Option<&VThread>, ) -> Result<(), Box> { - match &self.ty { - VFileType::Vnode(vn) => vn.truncate(self, length, td), - VFileType::Socket(so) | VFileType::IpcSocket(so) => so.truncate(self, length, td), - VFileType::KernelQueue(kq) => kq.truncate(self, length, td), - VFileType::SharedMemory(shm) => shm.truncate(self, length, td), - VFileType::Device(dev) => dev.truncate(self, length, td), - VFileType::Blockpool(bp) => bp.truncate(self, length, td), + self.backend.truncate(self, length, td) + } + + /// Gets the `f_data` associated with this file. + /// + /// File implementation should use this method to check if the file has expected type. This + /// method should be impossible for non-file implementation to call because it required an + /// implementation of [`FileBackend`], which should not exposed by the subsystem itself. This + /// also imply the other subsystems cannot call this method with the other subsystem + /// implementation. + pub fn backend(&self) -> Option<&T> { + // TODO: Use Any::downcast_ref() when https://github.com/rust-lang/rust/issues/65991 is + // stabilized. Our current implementation here is copied from Any::downcast_ref(). + let b = self.backend.as_ref(); + + if b.type_id() == TypeId::of::() { + Some(unsafe { &*(b as *const dyn FileBackend as *const T) }) + } else { + None } } } impl Seek for VFile { fn seek(&mut self, pos: SeekFrom) -> std::io::Result { - self.seekable_vnode().ok_or(ErrorKind::Other)?; + use std::io::Error; + + // Check if seekable. + if !self.backend.is_seekable() { + return Err(Error::from(ErrorKind::Unsupported)); + } // Negative seeks should not be allowed here let offset: u64 = match pos { @@ -147,70 +98,37 @@ impl Seek for VFile { } }; - *self.offset.write() = if let Ok(offset) = offset.try_into() { - offset - } else { - todo!() - }; + *self.offset.get_mut() = offset; - Ok(offset as u64) + Ok(offset) } fn rewind(&mut self) -> std::io::Result<()> { - *self.offset.write() = 0; - + *self.offset.get_mut() = 0; Ok(()) } fn stream_position(&mut self) -> std::io::Result { - if let Ok(offset) = (*self.offset.read()).try_into() { - Ok(offset) - } else { - todo!() - } + Ok(*self.offset.get_mut()) } } impl Read for VFile { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let total = buf.len(); - - let ref mut iovec = IoVec::from_slice(buf); - - let mut offset = self.offset.write(); - - let mut uio = UioMut::from_single_vec(iovec, *offset); - - if let Err(e) = VFile::read(self, &mut uio, None) { - println!("Error: {:?}", e); - - todo!() - }; - - let read = total - uio.bytes_left; + let len = IoLen::from_usize(buf.len()).unwrap_or(IoLen::MAX); + let mut buf = unsafe { IoVecMut::new(buf.as_mut_ptr(), len) }; + let off = *self.offset.get_mut(); + let read = self + .backend + .read(self, off, std::slice::from_mut(&mut buf), None) + .map_err(|e| std::io::Error::new(ErrorKind::Other, e))?; - if let Ok(read) = TryInto::::try_into(read) { - *offset += read; - } else { - todo!() - } + *self.offset.get_mut() += read.get() as u64; - Ok(read) + Ok(read.get()) } } -/// Type of [`VFile`]. -#[derive(Debug)] -pub enum VFileType { - Vnode(Arc), // DTYPE_VNODE = 1 - Socket(Arc), // DTYPE_SOCKET = 2, - KernelQueue(Arc), // DTYPE_KQUEUE = 5, - SharedMemory(Arc), // DTYPE_SHM = 8, - Device(Arc), // DTYPE_DEV = 11, - IpcSocket(Arc), // DTYPE_IPCSOCKET = 15, - Blockpool(Arc), // DTYPE_BLOCKPOOL = 17, -} - bitflags! { /// Flags for [`VFile`]. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -221,58 +139,61 @@ bitflags! { } /// An implementation of `fileops` structure. -pub trait FileBackend: Debug + Send + Sync + 'static { - #[allow(unused_variables)] +/// +/// The implementation is internal to the subsystem itself so it should not expose itself to the +/// outside. +pub trait FileBackend: Any + Debug + Send + Sync + 'static { + /// Implementation of `fo_flags` with `DFLAG_SEEKABLE`. + fn is_seekable(&self) -> bool; + /// An implementation of `fo_read`. fn read( - self: &Arc, + &self, file: &VFile, - buf: &mut UioMut, + off: u64, + buf: &mut [IoVecMut], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { Err(Box::new(DefaultFileBackendError::ReadNotSupported)) } - #[allow(unused_variables)] /// An implementation of `fo_write`. fn write( - self: &Arc, + &self, file: &VFile, - buf: &mut Uio, + off: u64, + buf: &[IoVec], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { Err(Box::new(DefaultFileBackendError::WriteNotSupported)) } - #[allow(unused_variables)] /// An implementation of `fo_ioctl`. - fn ioctl( - self: &Arc, - file: &VFile, - cmd: IoCmd, - td: Option<&VThread>, - ) -> Result<(), Box> { + fn ioctl(&self, file: &VFile, cmd: IoCmd, td: Option<&VThread>) -> Result<(), Box> { Err(Box::new(DefaultFileBackendError::IoctlNotSupported)) } - #[allow(unused_variables)] /// An implementation of `fo_poll`. - fn poll(self: &Arc, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents; + fn poll(&self, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents; - #[allow(unused_variables)] /// An implementation of `fo_stat`. - fn stat(self: &Arc, file: &VFile, td: Option<&VThread>) -> Result>; + fn stat(&self, file: &VFile, td: Option<&VThread>) -> Result>; - #[allow(unused_variables)] /// An implementation of `fo_truncate`. fn truncate( - self: &Arc, + &self, file: &VFile, length: TruncateLength, td: Option<&VThread>, ) -> Result<(), Box> { Err(Box::new(DefaultFileBackendError::TruncateNotSupported)) } + + /// Get a vnode associated with this file (if any). + /// + /// Usually this will be [`Some`] if the file was opened from a filesystem (e.g. `/dev/null`) + /// and [`None`] if the file is not living on the filesystem (e.g. kqueue). + fn vnode(&self) -> Option<&Arc>; } #[derive(Debug, Error, Errno)] diff --git a/src/kernel/src/fs/host/file.rs b/src/kernel/src/fs/host/file.rs index d001e4f6f..807c4edcc 100644 --- a/src/kernel/src/fs/host/file.rs +++ b/src/kernel/src/fs/host/file.rs @@ -1,4 +1,4 @@ -use crate::fs::UioMut; +use crate::fs::{IoLen, IoVecMut}; use std::collections::HashMap; use std::io::Error; use std::mem::zeroed; @@ -46,6 +46,7 @@ impl HostFile { }; use windows_sys::Win32::Storage::FileSystem::{ FILE_READ_ATTRIBUTES, FILE_READ_EA, FILE_SHARE_READ, FILE_SHARE_WRITE, + FILE_WRITE_ATTRIBUTES, FILE_WRITE_EA, }; use windows_sys::Win32::System::Kernel::OBJ_CASE_INSENSITIVE; @@ -78,14 +79,14 @@ impl HostFile { let err = unsafe { NtCreateFile( &mut handle, - FILE_READ_ATTRIBUTES | FILE_READ_EA, + Self::directory_access_flags(), &mut attr, &mut status, null_mut(), 0, - FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, FILE_OPEN, - FILE_DIRECTORY_FILE, + Self::directory_options(), null_mut(), 0, ) @@ -207,10 +208,12 @@ impl HostFile { fn raw_mkdir(parent: RawFile, name: &str, _mode: u32) -> Result { use std::mem::size_of; use std::ptr::null_mut; + use windows_sys::Wdk::Storage::FileSystem::FILE_CREATE; use windows_sys::Wdk::{ Foundation::OBJECT_ATTRIBUTES, - Storage::FileSystem::{NtCreateFile, FILE_DIRECTORY_FILE, FILE_OPEN}, + Storage::FileSystem::{NtCreateFile, FILE_DIRECTORY_FILE}, }; + use windows_sys::Win32::Storage::FileSystem::{DELETE, SYNCHRONIZE}; use windows_sys::Win32::{ Foundation::{RtlNtStatusToDosError, STATUS_SUCCESS, UNICODE_STRING}, Storage::FileSystem::{ @@ -245,19 +248,14 @@ impl HostFile { let error = unsafe { NtCreateFile( &mut handle, - FILE_READ_ATTRIBUTES - | FILE_READ_EA - | FILE_WRITE_ATTRIBUTES - | FILE_WRITE_EA - | FILE_LIST_DIRECTORY - | FILE_TRAVERSE, + DELETE | Self::directory_access_flags(), &mut attr, &mut status, null_mut(), FILE_ATTRIBUTE_DIRECTORY, - FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_OPEN, - FILE_DIRECTORY_FILE, + 0, + FILE_CREATE, + Self::directory_options(), null_mut(), 0, ) @@ -273,90 +271,59 @@ impl HostFile { } } - #[cfg(unix)] - pub(super) fn read(&self, buf: &mut UioMut) -> Result<(), Error> { - use libc::pread; - - buf.write_with::(|iov, mut offset| { - let nbytes = if let Ok(nbytes) = iov.len().try_into() { - nbytes - } else { - todo!() - }; - - let nread = unsafe { pread(self.raw, iov.ptr().cast(), nbytes, offset) }; + #[cfg(any(target_os = "linux", target_os = "macos"))] + pub fn read(&self, off: u64, buf: &mut [IoVecMut]) -> Result { + use libc::preadv; - match nread { - 0.. if nread == nbytes as isize => Ok(nread as u64), - 0.. => todo!(), - _ => todo!(), - } - })?; + // Our iovec implementation has the same layout as iovec on Linux and macOS so we can pass + // it directly to preadv. + let vecs = buf.as_ptr().cast(); + let off = off.try_into().unwrap(); + let read = unsafe { preadv(self.raw, vecs, buf.len().try_into().unwrap(), off) }; - Ok(()) + if read < 0 { + Err(Error::last_os_error()) + } else { + Ok(IoLen::from_usize(read as _).unwrap()) + } } #[cfg(windows)] - pub(super) fn read(&self, buf: &mut UioMut) -> Result<(), Error> { - use std::ptr::null_mut; - use windows_sys::{ - Wdk::Storage::FileSystem::NtReadFile, - Win32::{ - Foundation::{STATUS_END_OF_FILE, STATUS_PENDING}, - System::{ - Threading::{WaitForSingleObject, INFINITE}, - IO::{IO_STATUS_BLOCK, IO_STATUS_BLOCK_0}, - }, - }, + pub fn read(&self, off: u64, buf: &mut [IoVecMut]) -> Result { + use std::ptr::null; + use windows_sys::Wdk::Storage::FileSystem::NtReadFile; + use windows_sys::Win32::Foundation::{ + RtlNtStatusToDosError, STATUS_END_OF_FILE, STATUS_SUCCESS, }; - buf.write_with::(|iov, mut offset| { - let nbytes = if let Ok(nbytes) = iov.len().try_into() { - nbytes - } else { - todo!() - }; - - let mut io_status = IO_STATUS_BLOCK { - Anonymous: IO_STATUS_BLOCK_0 { - Status: STATUS_PENDING, - }, - Information: 0, - }; - - let status = unsafe { - NtReadFile( - self.raw, - 0, - None, - null_mut(), - &mut io_status, - iov.ptr().cast(), - nbytes, - &mut offset, - null_mut(), - ) - }; - - let status = if status == STATUS_PENDING { - unsafe { - WaitForSingleObject(self.raw, INFINITE); - io_status.Anonymous.Status - } - } else { - status - }; - - match status { - STATUS_PENDING => todo!(), - STATUS_END_OF_FILE => todo!(), - 0.. if io_status.Information == nbytes as usize => Ok(io_status.Information as u64), - 0.. => todo!(), - _ => todo!(), + // Windows does not provide preadv equivalent so we can fill only the first IoVecMut to + // achieve atomic read. + let off = off.try_into().unwrap(); + let buf = &mut buf[0]; + let mut status = unsafe { zeroed() }; + let read = match unsafe { + NtReadFile( + self.raw, + 0, + None, + null(), + &mut status, + buf.as_mut_ptr().cast(), + buf.len().get().try_into().unwrap(), + &off, + null(), + ) + } { + STATUS_SUCCESS => status.Information, + STATUS_END_OF_FILE => 0, + v => { + return Err(Error::from_raw_os_error(unsafe { + RtlNtStatusToDosError(v).try_into().unwrap() + })); } - })?; + }; - Ok(()) + Ok(IoLen::from_usize(read).unwrap()) } #[cfg(unix)] @@ -432,13 +399,16 @@ impl HostFile { use windows_sys::Wdk::Foundation::OBJECT_ATTRIBUTES; use windows_sys::Wdk::Storage::FileSystem::{ NtCreateFile, FILE_DIRECTORY_FILE, FILE_NON_DIRECTORY_FILE, FILE_OPEN, - FILE_RANDOM_ACCESS, + FILE_RANDOM_ACCESS, FILE_SYNCHRONOUS_IO_NONALERT, }; use windows_sys::Win32::Foundation::{ - RtlNtStatusToDosError, STATUS_SUCCESS, UNICODE_STRING, + RtlNtStatusToDosError, STATUS_FILE_IS_A_DIRECTORY, STATUS_NOT_A_DIRECTORY, + STATUS_SUCCESS, UNICODE_STRING, }; use windows_sys::Win32::Storage::FileSystem::{ - DELETE, FILE_GENERIC_READ, FILE_GENERIC_WRITE, + DELETE, FILE_APPEND_DATA, FILE_LIST_DIRECTORY, FILE_READ_ATTRIBUTES, FILE_READ_DATA, + FILE_READ_EA, FILE_TRAVERSE, FILE_WRITE_ATTRIBUTES, FILE_WRITE_DATA, FILE_WRITE_EA, + SYNCHRONIZE, }; // Encode name. @@ -463,51 +433,91 @@ impl HostFile { // Try open as a file first. let mut handle = 0; let mut status = unsafe { zeroed() }; - let err = unsafe { - NtCreateFile( - &mut handle, - DELETE | FILE_GENERIC_READ | FILE_GENERIC_WRITE, - &mut attr, - &mut status, - null_mut(), - 0, - 0, - FILE_OPEN, - FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS, - null_mut(), - 0, - ) - }; - if err == STATUS_SUCCESS { - Ok(handle) - } else { + loop { + let err = unsafe { + NtCreateFile( + &mut handle, + DELETE + | FILE_READ_DATA + | FILE_READ_ATTRIBUTES + | FILE_READ_EA + | FILE_WRITE_DATA + | FILE_WRITE_ATTRIBUTES + | FILE_WRITE_EA + | FILE_APPEND_DATA + | SYNCHRONIZE, + &mut attr, + &mut status, + null_mut(), + 0, + 0, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS | FILE_SYNCHRONOUS_IO_NONALERT, + null_mut(), + 0, + ) + }; + + if err == STATUS_SUCCESS { + break Ok(handle); + } else if err != STATUS_FILE_IS_A_DIRECTORY { + break Err(Error::from_raw_os_error(unsafe { + RtlNtStatusToDosError(err).try_into().unwrap() + })); + } + // Try open as a directory. let err = unsafe { NtCreateFile( &mut handle, - DELETE | FILE_GENERIC_READ | FILE_GENERIC_WRITE, + DELETE | Self::directory_access_flags(), &mut attr, &mut status, null_mut(), 0, 0, FILE_OPEN, - FILE_DIRECTORY_FILE, + Self::directory_options(), null_mut(), 0, ) }; if err == STATUS_SUCCESS { - Ok(handle) - } else { - Err(Error::from_raw_os_error(unsafe { + break Ok(handle); + } else if err != STATUS_NOT_A_DIRECTORY { + break Err(Error::from_raw_os_error(unsafe { RtlNtStatusToDosError(err).try_into().unwrap() - })) + })); } } } + + #[cfg(windows)] + const fn directory_access_flags() -> u32 { + use windows_sys::Win32::Storage::FileSystem::{ + FILE_LIST_DIRECTORY, FILE_READ_ATTRIBUTES, FILE_READ_EA, FILE_TRAVERSE, + FILE_WRITE_ATTRIBUTES, FILE_WRITE_EA, SYNCHRONIZE, + }; + + FILE_READ_ATTRIBUTES + | FILE_READ_EA + | FILE_WRITE_ATTRIBUTES + | FILE_WRITE_EA + | SYNCHRONIZE + | FILE_LIST_DIRECTORY + | FILE_TRAVERSE + } + + #[cfg(windows)] + const fn directory_options() -> u32 { + use windows_sys::Wdk::Storage::FileSystem::{ + FILE_DIRECTORY_FILE, FILE_SYNCHRONOUS_IO_NONALERT, + }; + + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + } } impl Drop for HostFile { diff --git a/src/kernel/src/fs/host/vnode.rs b/src/kernel/src/fs/host/vnode.rs index 483a18456..5c6c15ba4 100644 --- a/src/kernel/src/fs/host/vnode.rs +++ b/src/kernel/src/fs/host/vnode.rs @@ -1,7 +1,7 @@ use super::file::HostFile; use super::{GetVnodeError, HostFs}; use crate::errno::{Errno, EEXIST, EIO, ENOENT, ENOTDIR}; -use crate::fs::{Access, IoCmd, Mode, Uio, UioMut, Vnode, VnodeAttrs, VnodeType}; +use crate::fs::{Access, IoCmd, IoLen, IoVec, IoVecMut, Mode, Vnode, VnodeAttrs, VnodeType}; use crate::process::VThread; use crate::ucred::{Gid, Uid}; use macros::Errno; @@ -127,20 +127,23 @@ impl crate::fs::VnodeBackend for VnodeBackend { fn read( &self, _: &Arc, - buf: &mut UioMut, + off: u64, + buf: &mut [IoVecMut], _: Option<&VThread>, - ) -> Result<(), Box> { - let read = self.file.read(buf).map_err(ReadError::ReadFailed)?; - - Ok(read) + ) -> Result> { + match self.file.read(off, buf) { + Ok(v) => Ok(v), + Err(e) => Err(Box::new(ReadError::ReadFailed(e))), + } } fn write( &self, - #[allow(unused_variables)] vn: &Arc, - #[allow(unused_variables)] buf: &mut Uio, - #[allow(unused_variables)] td: Option<&VThread>, - ) -> Result<(), Box> { + vn: &Arc, + off: u64, + buf: &[IoVec], + td: Option<&VThread>, + ) -> Result> { todo!() } } diff --git a/src/kernel/src/fs/ioctl.rs b/src/kernel/src/fs/ioctl.rs index 57fd72b71..d03830397 100644 --- a/src/kernel/src/fs/ioctl.rs +++ b/src/kernel/src/fs/ioctl.rs @@ -1,5 +1,9 @@ use super::FioDeviceGetNameArg; -use crate::dev::{DmemAllocate, DmemAvailable, DmemQuery, PrtAperture}; +use crate::dev::{ + CuMask, DceFlipControlArg, DceRegisterBufferPtrsArg, DceSubmitFlipArg, DingDongForWorkload, + DmemAllocate, DmemAvailable, DmemQuery, MapComputeQueueArg, MipStatsReport, PrtAperture, + RngInput, SubmitArg, UnmapComputeQueueArg, +}; use crate::dmem::{BlockpoolExpandArgs, BlockpoolStats}; use crate::errno::ENOTTY; use crate::syscalls::SysErr; @@ -89,9 +93,23 @@ macro_rules! commands { commands! { pub enum IoCmd { /// sceKernelMemoryPoolExpand - BPOOLEXPAND(&mut BlockpoolExpandArgs) = 0xC020A801, + BPOOLEXPAND(&mut BlockpoolExpandArgs) = 0xc020a801, /// sceKernelMemoryPoolGetBlockStats - BPOOLSTATS(&mut BlockpoolStats) = 0x4010A802, + BPOOLSTATS(&mut BlockpoolStats) = 0x4010a802, + + /// An unkown bnet command, called from libSceNet + BNETUNK(&Unknown36) = 0x802450c9, + + /// Flip control. + DCEFLIPCONTROL(&mut DceFlipControlArg) = 0xC0308203, + /// Submit flip + DCESUBMITFLIP(&mut DceSubmitFlipArg) = 0xC0488204, + /// Register buffer pointers + DCEREGBUFPTRS(&mut DceRegisterBufferPtrsArg) = 0xC0308206, + /// Register buffer attribute + DCEREGBUFATTR(&mut Unknown48) = 0xC0308207, + /// Deregister identifier + DCEDEREGIDENT(&u64) = 0x80088209, /// Get media size in bytes. DIOCGMEDIASIZE(&mut i64) = 0x40086418, @@ -103,24 +121,28 @@ commands! { /// sceKernelUnsetDipsw DIPSWUNSET(&Unknown2) = 0x80028802, /// sceKernelCheckDipsw - DIPSWCHECK(&mut Unknown8) = 0xC0088803, + DIPSWCHECK(&mut Unknown8) = 0xc0088803, /// sceKernelReadDipswData DIPSWREAD(&Unknown16) = 0x80108804, /// sceKernelWriteDipswData DIPSWWRITE(&Unknown16) = 0x80108805, /// sceKernelCheckDipsw DIPSWCHECK2(&mut i32) = 0x40048806, + /// Unkown dipsw command + DIPSWUNK(&mut i32) = 0x40048807, + /// Allocate direct memory + DMEMALLOC(&mut DmemAllocate) = 0xc0288001, /// Get total size? DMEMTOTAL(&mut usize) = 0x4008800a, /// Get PRT aperture - DMEMGETPRT(&mut PrtAperture) = 0xC018800C, - /// Get available memory size - DMEMGETAVAIL(&mut DmemAvailable) = 0xC0208016, - /// Allocate direct memory - DMEMALLOC(&mut DmemAllocate) = 0xC0288001, + DMEMGETPRT(&mut PrtAperture) = 0xc018800c, + /// Allocate main direct memory + DMEMALLOCMAIN(&mut DmemAllocate) = 0xc0288011, /// Query direct memory DMEMQUERY(&DmemQuery) = 0x80288012, + /// Get available memory size + DMEMGETAVAIL(&mut DmemAvailable) = 0xc0208016, /// Set close on exec on fd. FIOCLEX = 0x20006601, @@ -142,15 +164,47 @@ commands! { FIOGETLBA(&mut i32) = 0x40046679, /// Get dev. name FIODGNAME(&FioDeviceGetNameArg) = 0x80106678, + /// Get # bytes (yet) to write + FIONWRITE(&mut i32) = 0x40046677, + /// Get space in send queue + FIONSPACE(&mut i32) = 0x40046676, /// Seek data. - FIOSEEKDATA(&mut i64) = 0xC0086661, + FIOSEEKDATA(&mut i64) = 0xc0086661, /// Seek hole. - FIOSEEKHOLE(&mut i64) = 0xC0086662, - - /// Unkown rng command - RNG1 = 0x40445301, - /// Unkown rng command - RNG2 = 0x40445302, + FIOSEEKHOLE(&mut i64) = 0xc0086662, + + /// Set wave limit multiplier + GCSETWAVELIMITMULTIPLIER(&mut i64) = 0xc0088101, + /// Submit + GCSUBMIT(&mut SubmitArg) = 0xc0108102, + /// Get CU mask + GCGETCUMASK(&mut CuMask) = 0xc010810b, + /// Map compute queue + GCMAPCOMPUTEQUEUE(&mut MapComputeQueueArg) = 0xc030810d, + /// Unmap compute queue + GCUNMAPCOMPUTEQUEUE(&mut UnmapComputeQueueArg) = 0xc00c810e, + /// Set GS ring queue sizes + GCSETGSRINGSIZES(&mut Unknown12) = 0xc00c8110, + /// Get mip stats report + GCMIPSTATSREPORT(&mut MipStatsReport) = 0xc0848119, + /// Currently unknown gc command + GCARESUBMITSALLOWED(&mut Unknown8) = 0xc008811b, + /// Ding dong for workload + GCDINGDONGFORWORKLOAD(&mut DingDongForWorkload) = 0xc010811c, + /// Get number of tca units + GCGETNUMTCAUNITS(&mut i32) = 0xc004811f, + + /// Get genuine random + RNGGETGENUINE(&mut RngInput) = 0x40445301, + /// Fips186Prng + RNGFIPS(&mut RngInput) = 0x40445302, + + /// Cat oob mark? + SIOCATMARK(&mut i32) = 0x40047307, + /// Set process group + SIOCSPGRP(&i32) = 0x80047308, + /// Get process group + SIOCGPGRP(&mut i32) = 0x40047309, /// Become controlling terminal. TIOCSCTTY = 0x20007461, @@ -159,7 +213,10 @@ commands! { type Unknown2 = Unknown<2>; type Unknown8 = Unknown<8>; +type Unknown12 = Unknown<12>; type Unknown16 = Unknown<16>; +type Unknown36 = Unknown<36>; +type Unknown48 = Unknown<48>; /// A dummy type to be used as a placeholder for unknown data. #[derive(Debug)] diff --git a/src/kernel/src/fs/mod.rs b/src/kernel/src/fs/mod.rs index a2534722e..09f6d1b7f 100644 --- a/src/kernel/src/fs/mod.rs +++ b/src/kernel/src/fs/mod.rs @@ -152,10 +152,10 @@ impl Fs { sys.register(188, &fs, Self::sys_stat); sys.register(189, &fs, Self::sys_fstat); sys.register(190, &fs, Self::sys_lstat); - sys.register(191, &fs, Self::sys_pread); sys.register(209, &fs, Self::sys_poll); sys.register(289, &fs, Self::sys_preadv); sys.register(290, &fs, Self::sys_pwritev); + sys.register(475, &fs, Self::sys_pread); sys.register(476, &fs, Self::sys_pwrite); sys.register(478, &fs, Self::sys_lseek); sys.register(479, &fs, Self::sys_truncate); @@ -170,18 +170,18 @@ impl Fs { self.root.read().clone() } - pub fn open(&self, path: impl AsRef, td: Option<&VThread>) -> Result { + pub fn open( + &self, + path: impl AsRef, + flags: VFileFlags, + td: Option<&VThread>, + ) -> Result { let vnode = self .lookup(path, true, td) .map_err(OpenError::LookupFailed)?; + let backend = vnode.to_file_backend(); - let ty = if let Some(VnodeItem::Device(dev)) = vnode.item().as_ref() { - VFileType::Device(dev.clone()) - } else { - VFileType::Vnode(vnode.clone()) - }; - - Ok(VFile::new(ty)) + Ok(VFile::new(flags, backend)) } pub fn lookup( @@ -368,28 +368,27 @@ impl Fs { fn sys_read(self: &Arc, td: &VThread, i: &SysIn) -> Result { let fd: i32 = i.args[0].try_into().unwrap(); let ptr: *mut u8 = i.args[1].into(); - let len: usize = i.args[2].try_into().unwrap(); + let len: IoLen = i.args[2].try_into()?; - let iovec = unsafe { IoVec::try_from_raw_parts(ptr, len) }?; + info!("Reading {len} bytes from fd {fd}."); - todo!() + todo!("sys_read") } fn sys_write(self: &Arc, td: &VThread, i: &SysIn) -> Result { let fd: i32 = i.args[0].try_into().unwrap(); let ptr: *const u8 = i.args[1].into(); - let len: usize = i.args[2].into(); + let len: IoLen = i.args[2].try_into()?; - let iovec = unsafe { IoVec::try_from_raw_parts(ptr, len) }?; + info!("Writing {len} bytes to fd {fd}."); - todo!() + todo!("sys_write") } fn sys_open(self: &Arc, td: &VThread, i: &SysIn) -> Result { // Get arguments. let path = unsafe { i.args[0].to_path()?.unwrap() }; let flags: OpenFlags = i.args[1].try_into().unwrap(); - let mode: u32 = i.args[2].try_into().unwrap(); // Check flags. if flags.intersects(OpenFlags::O_EXEC) { @@ -409,16 +408,17 @@ impl Fs { todo!("open({path}) with flags & O_EXLOCK"); } else if flags.intersects(OpenFlags::O_TRUNC) { todo!("open({path}) with flags & O_TRUNC"); - } else if mode != 0 { - todo!("open({path}, {flags}) with mode = {mode}"); + } else { + let mode: u32 = i.args[2].try_into().unwrap(); + if mode != 0 { + todo!("open({path}, {flags}) with mode = {mode}"); + } } info!("Opening {path} with flags = {flags}."); // Lookup file. - let mut file = self.open(path, Some(td))?; - - *file.flags_mut() = flags.into_fflags(); + let file = self.open(path, flags.into_fflags(), Some(td))?; // Install to descriptor table. let fd = td.proc().files().alloc(Arc::new(file)); @@ -431,7 +431,7 @@ impl Fs { fn sys_close(self: &Arc, td: &VThread, i: &SysIn) -> Result { let fd: i32 = i.args[0].try_into().unwrap(); - info!("Attempting to close fd {fd}."); + info!("Closing fd {fd}."); td.proc().files().free(fd)?; @@ -519,12 +519,8 @@ impl Fs { todo!() } - fn readv(&self, fd: i32, uio: UioMut, td: &VThread) -> Result { - let file = td.proc().files().get_for_read(fd)?; - - let read = file.do_read(uio, Some(td))?; - - Ok(read) + fn readv(&self, fd: i32, iov: &mut [IoVecMut], td: &VThread) -> Result { + todo!() } fn sys_writev(self: &Arc, td: &VThread, i: &SysIn) -> Result { @@ -535,12 +531,10 @@ impl Fs { todo!() } - fn writev(&self, fd: i32, uio: Uio, td: &VThread) -> Result { + fn writev(&self, fd: i32, td: &VThread) -> Result { let file = td.proc().files().get_for_write(fd)?; - let written = file.do_write(uio, Some(td))?; - - Ok(written) + todo!() } fn sys_stat(self: &Arc, td: &VThread, i: &SysIn) -> Result { @@ -606,93 +600,133 @@ impl Fs { fn sys_pread(self: &Arc, td: &VThread, i: &SysIn) -> Result { let fd: i32 = i.args[0].try_into().unwrap(); let ptr: *mut u8 = i.args[1].into(); - let len: usize = i.args[2].try_into().unwrap(); + let len: IoLen = i.args[2].try_into()?; let offset: i64 = i.args[3].into(); + let mut buf = unsafe { IoVecMut::new(ptr, len) }; - let iovec = unsafe { IoVec::try_from_raw_parts(ptr, len) }?; - - let uio = UioMut { - vecs: &mut [iovec], - bytes_left: len, - offset, - }; + info!("Reading {len} bytes from fd {fd} at offset {offset}."); - let read = self.preadv(fd, uio, td)?; + let read = self.preadv(fd, offset, std::slice::from_mut(&mut buf), td)?; Ok(read.into()) } fn sys_pwrite(self: &Arc, td: &VThread, i: &SysIn) -> Result { let fd: i32 = i.args[0].try_into().unwrap(); - let ptr: *mut u8 = i.args[1].into(); - let len: usize = i.args[2].try_into().unwrap(); + let ptr: *const u8 = i.args[1].into(); + let len: IoLen = i.args[2].try_into()?; let offset: i64 = i.args[3].into(); + let buf = unsafe { IoVec::new(ptr, len) }; - let iovec = unsafe { IoVec::try_from_raw_parts(ptr, len) }?; + info!("Writing {len} bytes to fd {fd} at offset {offset}."); - let uio = Uio { - vecs: &[iovec], - bytes_left: len, - offset, - }; - - let written = self.pwritev(fd, uio, td)?; + let written = self.pwritev(fd, offset, std::slice::from_ref(&buf), td)?; Ok(written.into()) } fn sys_preadv(self: &Arc, td: &VThread, i: &SysIn) -> Result { + // Get arguments. let fd: i32 = i.args[0].try_into().unwrap(); - let iovec: *mut IoVec = i.args[1].into(); + let iovec: *mut IoVecMut = i.args[1].into(); let count: u32 = i.args[2].try_into().unwrap(); let offset: i64 = i.args[3].into(); + let buf = if count >= 1025 { + return Err(SysErr::Raw(EINVAL)); + } else { + unsafe { std::slice::from_raw_parts_mut(iovec, count.try_into().unwrap()) } + }; - let uio = unsafe { UioMut::copyin(iovec, count, offset) }?; + // Check if total length exceed the limit. + let mut total = IoLen::ZERO; - let read = self.preadv(fd, uio, td)?; + for v in buf.iter() { + match total.checked_add(v.len()) { + Some(v) => total = v, + None => return Err(SysErr::Raw(EINVAL)), + } + } + + // Read the file. + let read = self.preadv(fd, offset, buf, td)?; Ok(read.into()) } - fn preadv(&self, fd: i32, uio: UioMut, td: &VThread) -> Result { + /// See `kern_preadv` on the PS4 for a reference. + fn preadv( + &self, + fd: i32, + off: i64, + buf: &mut [IoVecMut], + td: &VThread, + ) -> Result { + // Check if file seekable. let file = td.proc().files().get_for_read(fd)?; - let vnode = file.seekable_vnode().ok_or(SysErr::Raw(ESPIPE))?; - - if uio.offset < 0 && !vnode.is_character() { - return Err(SysErr::Raw(EINVAL)); + if !file.is_seekable() { + return Err(SysErr::Raw(ESPIPE)); } - let read = file.do_read(uio, Some(td))?; + // Check offset. + let off = if off > -1 { + Some(TryInto::::try_into(off).unwrap()) + } else if !file.vnode().unwrap().is_character() { + return Err(SysErr::Raw(EINVAL)); + } else { + None + }; - Ok(read) + todo!() } fn sys_pwritev(self: &Arc, td: &VThread, i: &SysIn) -> Result { + // Get arguments. let fd: i32 = i.args[0].try_into().unwrap(); let iovec: *const IoVec = i.args[1].into(); let count: u32 = i.args[2].try_into().unwrap(); let offset: i64 = i.args[3].into(); + let buf = if count >= 1025 { + return Err(SysErr::Raw(EINVAL)); + } else { + unsafe { std::slice::from_raw_parts(iovec, count.try_into().unwrap()) } + }; - let uio = unsafe { Uio::copyin(iovec, count, offset) }?; + // Check if total length exceed the limit. + let mut total = IoLen::ZERO; - let written = self.pwritev(fd, uio, td)?; + for v in buf { + match total.checked_add(v.len()) { + Some(v) => total = v, + None => return Err(SysErr::Raw(EINVAL)), + } + } + + // Write the file. + let written = self.pwritev(fd, offset, buf, td)?; Ok(written.into()) } - fn pwritev(&self, fd: i32, uio: Uio, td: &VThread) -> Result { + /// See `kern_pwritev` on the PS4 for a reference. + fn pwritev(&self, fd: i32, off: i64, buf: &[IoVec], td: &VThread) -> Result { + // Check if file seekable. let file = td.proc().files().get_for_write(fd)?; - let vnode = file.seekable_vnode().ok_or(SysErr::Raw(ESPIPE))?; - - if uio.offset < 0 && !vnode.is_character() { - return Err(SysErr::Raw(EINVAL)); + if !file.is_seekable() { + return Err(SysErr::Raw(ESPIPE)); } - let written = file.do_write(uio, Some(td))?; + // Check offset. + let off = if off > -1 { + Some(TryInto::::try_into(off).unwrap()) + } else if !file.vnode().unwrap().is_character() { + return Err(SysErr::Raw(EINVAL)); + } else { + None + }; - Ok(written) + todo!() } fn sys_fstatat(self: &Arc, td: &VThread, i: &SysIn) -> Result { @@ -738,6 +772,10 @@ impl Fs { let nfds: u32 = i.args[1].try_into().unwrap(); let timeout: i32 = i.args[2].try_into().unwrap(); + let fds = unsafe { std::slice::from_raw_parts_mut(fds, nfds.try_into().unwrap()) }; + + info!("Polling {nfds} file descriptors: {fds:?}."); + todo!() } @@ -750,9 +788,13 @@ impl Fs { whence.try_into()? }; + info!("Seeking fd {fd} with whence = {whence:?} and offset = {offset}."); + let file = td.proc().files().get(fd)?; - let vnode = file.seekable_vnode().ok_or(SysErr::Raw(ESPIPE))?; + if !file.is_seekable() { + return Err(SysErr::Raw(ESPIPE)); + } // check vnode type @@ -967,6 +1009,7 @@ enum At { Fd(i32), } +#[derive(Debug)] pub enum Whence { Set = 0, // SEEK_SET Current = 1, // SEEK_CUR @@ -1026,6 +1069,7 @@ impl TryFrom for TruncateLength { pub struct TruncateLengthError(()); #[repr(C)] +#[derive(Debug)] pub struct PollFd { fd: i32, events: PollEvents, diff --git a/src/kernel/src/fs/null/vnode.rs b/src/kernel/src/fs/null/vnode.rs index dde238c6d..3531e45d9 100644 --- a/src/kernel/src/fs/null/vnode.rs +++ b/src/kernel/src/fs/null/vnode.rs @@ -1,36 +1,26 @@ -use crate::{ - errno::{Errno, EISDIR, EROFS}, - fs::{ - null::hash::NULL_HASHTABLE, perm::Access, Mount, MountFlags, Uio, UioMut, Vnode, - VnodeAttrs, VnodeType, - }, - process::VThread, -}; +use super::hash::NULL_HASHTABLE; +use crate::errno::{Errno, EISDIR, EROFS}; +use crate::fs::{Access, IoLen, IoVec, IoVecMut, Mount, MountFlags, Vnode, VnodeAttrs, VnodeType}; +use crate::process::VThread; use macros::Errno; -use std::sync::{Arc, Weak}; +use std::sync::Arc; use thiserror::Error; #[derive(Debug)] pub(super) struct VnodeBackend { lower: Arc, - null_node: Weak, } impl VnodeBackend { - pub(super) fn new(lower: &Arc, null_node: &Weak) -> Self { + pub(super) fn new(lower: &Arc) -> Self { Self { lower: lower.clone(), - null_node: null_node.clone(), } } pub(super) fn lower(&self) -> &Arc { &self.lower } - - pub(super) fn null_node(&self) -> &Weak { - &self.null_node - } } impl crate::fs::VnodeBackend for VnodeBackend { @@ -94,27 +84,27 @@ impl crate::fs::VnodeBackend for VnodeBackend { fn read( &self, _: &Arc, - buf: &mut UioMut, + off: u64, + buf: &mut [IoVecMut], td: Option<&VThread>, - ) -> Result<(), Box> { - self.lower - .read(buf, td) - .map_err(ReadError::ReadFromLowerFailed)?; - - Ok(()) + ) -> Result> { + match self.lower.read(off, buf, td) { + Ok(v) => Ok(v), + Err(e) => Err(Box::new(ReadError::ReadFromLowerFailed(e))), + } } fn write( &self, - _: &Arc, - buf: &mut Uio, + vn: &Arc, + off: u64, + buf: &[IoVec], td: Option<&VThread>, - ) -> Result<(), Box> { - self.lower - .write(buf, td) - .map_err(WriteError::WriteFromLowerFailed)?; - - Ok(()) + ) -> Result> { + match self.lower.write(off, buf, td) { + Ok(v) => Ok(v), + Err(e) => Err(Box::new(WriteError::WriteFromLowerFailed(e))), + } } } @@ -129,11 +119,7 @@ pub(super) fn null_nodeget( return Ok(nullnode); } - let nullnode = Vnode::new_cyclic(|null_node| { - let backend = Arc::new(VnodeBackend::new(lower, null_node)); - - Vnode::new_plain(mnt, lower.ty().clone(), "nullfs", backend) - }); + let nullnode = Vnode::new(mnt, lower.ty().clone(), "nullfs", VnodeBackend::new(lower)); table.insert(mnt, lower, &nullnode); diff --git a/src/kernel/src/fs/tmp/node.rs b/src/kernel/src/fs/tmp/node.rs index 97e778d8e..b36fee15b 100644 --- a/src/kernel/src/fs/tmp/node.rs +++ b/src/kernel/src/fs/tmp/node.rs @@ -1,6 +1,6 @@ use super::{AllocVnodeError, TempFs}; use crate::errno::{Errno, ENOENT, ENOSPC}; -use crate::fs::{Access, Uio, UioMut, Vnode, VnodeAttrs, VnodeType}; +use crate::fs::{Access, Vnode, VnodeAttrs, VnodeType}; use crate::process::VThread; use gmtx::{Gutex, GutexGroup, GutexWriteGuard}; use macros::Errno; @@ -204,19 +204,21 @@ impl crate::fs::VnodeBackend for VnodeBackend { fn read( &self, - #[allow(unused_variables)] vn: &Arc, - #[allow(unused_variables)] buf: &mut UioMut, - #[allow(unused_variables)] td: Option<&VThread>, - ) -> Result<(), Box> { + vn: &Arc, + off: u64, + buf: &mut [crate::fs::IoVecMut], + td: Option<&VThread>, + ) -> Result> { todo!() } fn write( &self, - #[allow(unused_variables)] vn: &Arc, - #[allow(unused_variables)] buf: &mut Uio, - #[allow(unused_variables)] td: Option<&VThread>, - ) -> Result<(), Box> { + vn: &Arc, + off: u64, + buf: &[crate::fs::IoVec], + td: Option<&VThread>, + ) -> Result> { todo!() } } diff --git a/src/kernel/src/fs/uio.rs b/src/kernel/src/fs/uio.rs index 847c22f44..1c4834fcb 100644 --- a/src/kernel/src/fs/uio.rs +++ b/src/kernel/src/fs/uio.rs @@ -1,163 +1,162 @@ use crate::errno::{Errno, EINVAL}; -use macros::Errno; +use crate::syscalls::{SysArg, SysOut}; +use std::fmt::{Display, Formatter}; +use std::marker::PhantomData; +use std::num::NonZeroI32; +use std::ops::{Deref, DerefMut}; use thiserror::Error; -const UIO_MAXIOV: u32 = 1024; -const IOSIZE_MAX: usize = 0x7fffffff; - +/// Implementation of `iovec` structure for writing. #[repr(C)] pub struct IoVec<'a> { ptr: *const u8, - len: usize, - _phantom: std::marker::PhantomData<&'a [u8]>, + len: IoLen, + phantom: PhantomData<&'a [u8]>, } impl<'a> IoVec<'a> { - /// This is for when the PS4 DOES check the length (such as in read, write, pread and pwrite) - pub unsafe fn try_from_raw_parts(base: *const u8, len: usize) -> Result { - if len > IOSIZE_MAX { - return Err(IoVecError::MaxLenExceeded); - } - - Ok(Self { - ptr: base, - len, - _phantom: std::marker::PhantomData, - }) - } - - /// This is for when the PS4 DOES NOT check the length (such as in recvmsg, recvfrom, sendmsg and sendto) - pub unsafe fn from_raw_parts(base: *const u8, len: usize) -> Self { + /// # Safety + /// `ptr` must outlive `'a`. + pub unsafe fn new(ptr: *const u8, len: IoLen) -> Self { Self { - ptr: base, + ptr, len, - _phantom: std::marker::PhantomData, + phantom: PhantomData, } } - pub fn from_slice(slice: &'a mut [u8]) -> Self { - Self { - ptr: slice.as_ptr(), - len: slice.len(), - _phantom: std::marker::PhantomData, - } + pub fn len(&self) -> IoLen { + self.len } +} - pub fn ptr(&self) -> *mut u8 { - self.ptr as _ - } +impl<'a> Deref for IoVec<'a> { + type Target = [u8]; - pub fn len(&self) -> usize { - self.len + fn deref(&self) -> &Self::Target { + unsafe { std::slice::from_raw_parts(self.ptr, self.len.get()) } } } -pub struct Uio<'a> { - pub(super) vecs: &'a [IoVec<'a>], // uio_iov + uio_iovcnt - pub(super) bytes_left: usize, // uio_resid - pub(super) offset: i64, // uio_offset +/// Implementation of `iovec` structure for reading. +#[repr(C)] +pub struct IoVecMut<'a> { + ptr: *mut u8, + len: IoLen, + phantom: PhantomData<&'a mut [u8]>, } -impl<'a> Uio<'a> { - /// See `copyinuio` on the PS4 for a reference. - pub unsafe fn copyin( - first: *const IoVec<'a>, - count: u32, - offset: i64, - ) -> Result { - if count > UIO_MAXIOV { - return Err(CopyInUioError::TooManyVecs); +impl<'a> IoVecMut<'a> { + /// # Safety + /// `ptr` must outlive `'a`. + pub unsafe fn new(ptr: *mut u8, len: IoLen) -> Self { + Self { + ptr, + len, + phantom: PhantomData, } + } - let vecs = std::slice::from_raw_parts(first, count as usize); - let bytes_left = vecs.iter().map(|v| v.len).try_fold(0, |acc, len| { - if acc > IOSIZE_MAX - len { - Err(CopyInUioError::MaxLenExceeded) - } else { - Ok(acc + len) - } - })?; - - Ok(Self { - vecs, - bytes_left, - offset, - }) + pub fn len(&self) -> IoLen { + self.len } } -pub struct UioMut<'a> { - pub(super) vecs: &'a mut [IoVec<'a>], // uio_iov + uio_iovcnt - pub(super) bytes_left: usize, // uio_resid - pub(super) offset: i64, // uio_offset +impl<'a> Deref for IoVecMut<'a> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + unsafe { std::slice::from_raw_parts(self.ptr, self.len.get()) } + } } -impl<'a> UioMut<'a> { - /// See `copyinuio` on the PS4 for a reference. - pub unsafe fn copyin( - first: *mut IoVec<'a>, - count: u32, - offset: i64, - ) -> Result { - if count > UIO_MAXIOV { - return Err(CopyInUioError::TooManyVecs); - } +impl<'a> DerefMut for IoVecMut<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len.get()) } + } +} + +/// Represents a length of [`IoVec`] and [`IoVecMut`]. +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct IoLen(usize); + +impl IoLen { + pub const ZERO: Self = Self(0); + pub const MAX: Self = Self(0x7fffffff); - let vecs = std::slice::from_raw_parts_mut(first, count as usize); - let bytes_left = vecs.iter().map(|v| v.len).try_fold(0, |acc, len| { - if acc > IOSIZE_MAX - len { - Err(CopyInUioError::MaxLenExceeded) - } else { - Ok(acc + len) - } - })?; + pub fn from_usize(v: usize) -> Result { + let v = Self(v); + + if v > Self::MAX { + Err(IoLenError(())) + } else { + Ok(v) + } + } - Ok(Self { - vecs, - bytes_left, - offset, - }) + pub fn get(self) -> usize { + self.0 } - pub fn from_single_vec(vec: &'a mut IoVec<'a>, offset: i64) -> Self { - let bytes_left = vec.len; + pub fn checked_add(self, rhs: Self) -> Option { + let r = self.0.checked_add(rhs.0).map(IoLen)?; - Self { - vecs: std::slice::from_mut(vec), - bytes_left, - offset, + if r > Self::MAX { + None + } else { + Some(r) } } - pub fn write_with( - &mut self, - func: impl Fn(&mut IoVec, i64) -> Result, - ) -> Result<(), E> { - for vec in self.vecs.iter_mut() { - let written = func(vec, self.offset)?; + pub fn saturating_add(self, rhs: Self) -> Self { + let r = Self(self.0.saturating_add(rhs.0)); - self.offset = self.offset.checked_add_unsigned(written).unwrap(); - self.bytes_left -= written as usize; + if r > Self::MAX { + Self::MAX + } else { + r } + } + + pub fn checked_sub(self, rhs: Self) -> Option { + self.0.checked_sub(rhs.0).map(IoLen) + } +} + +impl TryFrom for IoLen { + type Error = IoLenError; + + fn try_from(value: SysArg) -> Result { + Self::from_usize(value.get()) + } +} - Ok(()) +impl PartialEq for IoLen { + fn eq(&self, other: &usize) -> bool { + self.0 == *other } } -#[derive(Debug, Error, Errno)] -pub enum IoVecError { - #[error("len exceed the maximum value")] - #[errno(EINVAL)] - MaxLenExceeded, +impl Display for IoLen { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } } -#[derive(Debug, Error, Errno)] -pub enum CopyInUioError { - #[error("too many iovecs")] - #[errno(EINVAL)] - TooManyVecs, +impl From for SysOut { + fn from(value: IoLen) -> Self { + value.0.into() + } +} - #[error("the sum of iovec lengths is too large")] - #[errno(EINVAL)] - MaxLenExceeded, +/// Represents an error when [`IoLen`] fails to construct. +#[derive(Debug, Error)] +#[error("invalid value")] +pub struct IoLenError(()); + +impl Errno for IoLenError { + fn errno(&self) -> NonZeroI32 { + EINVAL + } } diff --git a/src/kernel/src/fs/vnode.rs b/src/kernel/src/fs/vnode.rs index a96fd9a16..cf3a16ae9 100644 --- a/src/kernel/src/fs/vnode.rs +++ b/src/kernel/src/fs/vnode.rs @@ -1,10 +1,8 @@ use super::{ - unixify_access, Access, CharacterDevice, FileBackend, IoCmd, Mode, Mount, RevokeFlags, Stat, - TruncateLength, Uio, UioMut, VFile, + unixify_access, Access, CharacterDevice, FileBackend, IoCmd, IoLen, IoVec, IoVecMut, Mode, + Mount, PollEvents, RevokeFlags, Stat, TruncateLength, VFile, }; -use crate::arnd; use crate::errno::{Errno, ENOTDIR, ENOTTY, EOPNOTSUPP, EPERM}; -use crate::fs::PollEvents; use crate::process::VThread; use crate::ucred::{Gid, Uid}; use gmtx::{Gutex, GutexGroup, GutexReadGuard, GutexWriteGuard}; @@ -24,7 +22,7 @@ pub struct Vnode { mount: Arc, // v_mount ty: VnodeType, // v_type tag: &'static str, // v_tag - backend: Arc, // v_op + v_data + backend: Box, // v_op + v_data hash: u32, // v_hash item: Gutex>, // v_un } @@ -37,36 +35,22 @@ impl Vnode { tag: &'static str, backend: impl VnodeBackend, ) -> Arc { - Arc::new(Self::new_plain(mount, ty, tag, Arc::new(backend))) - } - - pub(super) fn new_plain( - mount: &Arc, - ty: VnodeType, - tag: &'static str, - backend: Arc, - ) -> Self { let gg = GutexGroup::new(); ACTIVE.fetch_add(1, Ordering::Relaxed); - Self { + Arc::new(Self { mount: mount.clone(), ty, tag, - backend, + backend: Box::new(backend), hash: { let mut buf = [0u8; 4]; - arnd::rand_bytes(&mut buf); - + crate::arnd::rand_bytes(&mut buf); u32::from_ne_bytes(buf) }, item: gg.spawn(None), - } - } - - pub fn new_cyclic(f: impl FnOnce(&Weak) -> Vnode) -> Arc { - Arc::new_cyclic(f) + }) } pub fn mount(&self) -> &Arc { @@ -141,68 +125,24 @@ impl Vnode { pub fn read( self: &Arc, - buf: &mut UioMut, + off: u64, + buf: &mut [IoVecMut], td: Option<&VThread>, - ) -> Result<(), Box> { - self.backend.read(self, buf, td) + ) -> Result> { + self.backend.read(self, off, buf, td) } pub fn write( self: &Arc, - buf: &mut Uio, + off: u64, + buf: &[IoVec], td: Option<&VThread>, - ) -> Result<(), Box> { - self.backend.write(self, buf, td) + ) -> Result> { + self.backend.write(self, off, buf, td) } -} -impl FileBackend for Vnode { - fn read( - self: &Arc, - _: &VFile, - buf: &mut UioMut, - td: Option<&VThread>, - ) -> Result<(), Box> { - self.backend.read(self, buf, td) - } - - fn write( - self: &Arc, - _: &VFile, - buf: &mut Uio, - td: Option<&VThread>, - ) -> Result<(), Box> { - self.backend.write(self, buf, td) - } - - #[allow(unused_variables)] // TODO: remove when implementing - fn ioctl( - self: &Arc, - file: &VFile, - cmd: IoCmd, - td: Option<&VThread>, - ) -> Result<(), Box> { - todo!() - } - - #[allow(unused_variables)] // TODO: remove when implementing - fn poll(self: &Arc, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents { - todo!() - } - - #[allow(unused_variables)] // TODO: remove when implementing - fn stat(self: &Arc, file: &VFile, td: Option<&VThread>) -> Result> { - todo!() - } - - #[allow(unused_variables)] // TODO: remove when implementing - fn truncate( - self: &Arc, - file: &VFile, - length: TruncateLength, - td: Option<&VThread>, - ) -> Result<(), Box> { - todo!() + pub fn to_file_backend(self: &Arc) -> Box { + self.backend.to_file_backend(self) } } @@ -318,17 +258,23 @@ pub(super) trait VnodeBackend: Debug + Send + Sync + 'static { fn read( &self, #[allow(unused_variables)] vn: &Arc, - #[allow(unused_variables)] buf: &mut UioMut, + #[allow(unused_variables)] off: u64, + #[allow(unused_variables)] buf: &mut [IoVecMut], #[allow(unused_variables)] td: Option<&VThread>, - ) -> Result<(), Box>; + ) -> Result>; /// An implementation of `vop_write`. fn write( &self, #[allow(unused_variables)] vn: &Arc, - #[allow(unused_variables)] buf: &mut Uio, + #[allow(unused_variables)] off: u64, + #[allow(unused_variables)] buf: &[IoVec], #[allow(unused_variables)] td: Option<&VThread>, - ) -> Result<(), Box>; + ) -> Result>; + + fn to_file_backend(&self, vn: &Arc) -> Box { + Box::new(VnodeFileBackend(vn.clone())) + } } /// An implementation of `vattr` struct. @@ -341,7 +287,68 @@ pub struct VnodeAttrs { pub fsid: u32, // va_fsid } -/// Represents an error when [`DEFAULT_VNODEOPS`] fails. +/// Implementation of `vnops`. +#[derive(Debug)] +pub(super) struct VnodeFileBackend(Arc); + +impl VnodeFileBackend { + pub fn new(vn: Arc) -> Self { + Self(vn) + } +} + +impl FileBackend for VnodeFileBackend { + fn is_seekable(&self) -> bool { + true + } + + fn read( + &self, + _: &VFile, + off: u64, + buf: &mut [IoVecMut], + td: Option<&VThread>, + ) -> Result> { + self.0.read(off, buf, td) + } + + fn write( + &self, + _: &VFile, + off: u64, + buf: &[IoVec], + td: Option<&VThread>, + ) -> Result> { + self.0.write(off, buf, td) + } + + fn ioctl(&self, file: &VFile, cmd: IoCmd, td: Option<&VThread>) -> Result<(), Box> { + todo!() + } + + fn poll(&self, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents { + todo!() + } + + fn stat(&self, file: &VFile, td: Option<&VThread>) -> Result> { + todo!() + } + + fn truncate( + &self, + file: &VFile, + length: TruncateLength, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } + + fn vnode(&self) -> Option<&Arc> { + Some(&self.0) + } +} + +/// Represents an error when default implementation of [`VnodeBackend`] fails. #[derive(Debug, Error, Errno)] enum DefaultError { #[error("operation not supported")] diff --git a/src/kernel/src/hv/linux/kvm.cpp b/src/kernel/src/hv/linux/kvm.cpp index f431c89e6..286457136 100644 --- a/src/kernel/src/hv/linux/kvm.cpp +++ b/src/kernel/src/hv/linux/kvm.cpp @@ -66,3 +66,36 @@ extern "C" int kvm_set_user_memory_region( return 0; } + +extern "C" int kvm_get_vcpu_mmap_size(int kvm) +{ + return ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE); +} + +extern "C" int kvm_create_vcpu(int vm, int id, int *fd) +{ + auto vcpu = ioctl(vm, KVM_CREATE_VCPU, id); + + if (vcpu < 0) { + return errno; + } + + *fd = vcpu; + + return 0; +} + +extern "C" int kvm_run(int vcpu) +{ + return ioctl(vcpu, KVM_RUN); +} + +extern "C" int kvm_get_regs(int vcpu, kvm_regs *regs) +{ + return ioctl(vcpu, KVM_GET_REGS, regs); +} + +extern "C" int kvm_set_regs(int vcpu, kvm_regs *const regs) +{ + return ioctl(vcpu, KVM_SET_REGS, regs); +} diff --git a/src/kernel/src/hv/linux/mod.rs b/src/kernel/src/hv/linux/mod.rs index 986524774..94fc65528 100644 --- a/src/kernel/src/hv/linux/mod.rs +++ b/src/kernel/src/hv/linux/mod.rs @@ -1,65 +1,186 @@ +use self::regs::KvmRegs; +use self::run::KvmRun; use super::HypervisorError; -use libc::{open, O_RDWR}; use std::ffi::{c_int, c_void}; use std::io::Error; -use std::os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd}; +use std::mem::MaybeUninit; +use std::os::fd::{AsRawFd, FromRawFd, OwnedFd}; +use std::ptr::NonNull; +use thiserror::Error; -pub fn open_kvm() -> Result { - // Open KVM. - let fd = unsafe { open(c"/dev/kvm".as_ptr(), O_RDWR) }; +mod regs; +mod run; - if fd < 0 { - return Err(HypervisorError::OpenKvmFailed(Error::last_os_error())); - } +pub struct Kvm(OwnedFd); + +impl Kvm { + pub fn open() -> Result { + use libc::{open, O_RDWR}; + + let fd = unsafe { open(c"/dev/kvm".as_ptr(), O_RDWR) }; - // Check KVM version. - let fd = unsafe { OwnedFd::from_raw_fd(fd) }; - let mut compat = false; + if fd < 0 { + return Err(HypervisorError::OpenKvmFailed(Error::last_os_error())); + } + + // Check KVM version. + let fd = unsafe { OwnedFd::from_raw_fd(fd) }; + let mut compat = false; - match unsafe { kvm_check_version(fd.as_raw_fd(), &mut compat) } { - 0 => { - if !compat { + match unsafe { kvm_check_version(fd.as_raw_fd(), &mut compat) } { + 0 if !compat => { return Err(HypervisorError::KvmVersionMismatched); } + 0 => {} + v => { + return Err(HypervisorError::GetKvmVersionFailed( + Error::from_raw_os_error(v), + )) + } } - v => { - return Err(HypervisorError::GetKvmVersionFailed( - Error::from_raw_os_error(v), - )) + + Ok(Self(fd)) + } + + pub fn get_vcpu_mmap_size(&self) -> Result { + match unsafe { kvm_get_vcpu_mmap_size(self.0.as_raw_fd()) } { + size @ 0.. => Ok(size as usize), + _ => Err(HypervisorError::GetMmapSizeFailed(Error::last_os_error())), + } + } + + pub fn max_vcpus(&self) -> Result { + let mut max = 0; + + match unsafe { kvm_max_vcpus(self.0.as_raw_fd(), &mut max) } { + 0 => Ok(max), + v => Err(HypervisorError::GetMaxCpuFailed(Error::from_raw_os_error( + v, + ))), } } - Ok(fd) + pub fn create_vm(&self) -> Result { + let mut vm = -1; + + match unsafe { kvm_create_vm(self.0.as_raw_fd(), &mut vm) } { + 0 => Ok(Vm(unsafe { OwnedFd::from_raw_fd(vm) })), + v => Err(HypervisorError::CreateVmFailed(Error::from_raw_os_error(v))), + } + } } -pub fn max_vcpus(kvm: BorrowedFd) -> Result { - let mut max = 0; +pub struct Vm(OwnedFd); + +impl Vm { + pub fn set_user_memory_region( + &self, + slot: u32, + addr: u64, + len: u64, + mem: *mut c_void, + ) -> Result<(), HypervisorError> { + match unsafe { kvm_set_user_memory_region(self.0.as_raw_fd(), slot, addr, len, mem) } { + 0 => Ok(()), + v => Err(HypervisorError::MapRamFailed(Error::from_raw_os_error(v))), + } + } + + pub fn create_vcpus(&self, mmap_size: usize) -> Result { + let vcpus = [ + self.create_vcpu(0, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 0))?, + self.create_vcpu(1, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 1))?, + self.create_vcpu(2, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 2))?, + self.create_vcpu(3, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 3))?, + self.create_vcpu(4, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 4))?, + self.create_vcpu(5, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 5))?, + self.create_vcpu(6, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 6))?, + self.create_vcpu(7, mmap_size) + .map_err(|e| CreateVCpusError::CreateVcpuFailed(e, 7))?, + ]; - match unsafe { kvm_max_vcpus(kvm.as_raw_fd(), &mut max) } { - 0 => Ok(max), - v => Err(Error::from_raw_os_error(v)), + Ok(VCpus(vcpus)) } + + fn create_vcpu(&self, id: i32, mmap_size: usize) -> Result { + use libc::{mmap, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE}; + + let mut vcpu = -1; + + let fd = match unsafe { kvm_create_vcpu(self.0.as_raw_fd(), id, &mut vcpu) } { + 0 => Ok(unsafe { OwnedFd::from_raw_fd(vcpu) }), + v => Err(CreateVCpuError::CreateVcpuFailed(Error::from_raw_os_error( + v, + ))), + }?; + + let kvm_run = unsafe { + mmap( + std::ptr::null_mut(), + mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd.as_raw_fd(), + 0, + ) + }; + + if kvm_run == MAP_FAILED { + return Err(CreateVCpuError::MmapFailed(Error::last_os_error())); + } + + Ok(VCpu { + fd, + kvm_run: NonNull::new(kvm_run.cast()).unwrap(), + mmap_size, + }) + } +} + +#[derive(Debug)] +pub struct VCpus([VCpu; 8]); + +#[derive(Debug)] +struct VCpu { + fd: OwnedFd, + kvm_run: NonNull, + mmap_size: usize, } -pub fn create_vm(kvm: BorrowedFd) -> Result { - let mut vm = -1; +impl Drop for VCpu { + fn drop(&mut self) { + use libc::munmap; - match unsafe { kvm_create_vm(kvm.as_raw_fd(), &mut vm) } { - 0 => Ok(unsafe { OwnedFd::from_raw_fd(vm) }), - v => Err(Error::from_raw_os_error(v)), + unsafe { + if munmap(self.kvm_run.as_ptr().cast(), self.mmap_size) < 0 { + panic!("failed to munmap KVM_RUN: {}", Error::last_os_error()); + }; + } } } -pub fn set_user_memory_region( - vm: BorrowedFd, - slot: u32, - addr: u64, - len: u64, - mem: *mut c_void, -) -> Result<(), Error> { - match unsafe { kvm_set_user_memory_region(vm.as_raw_fd(), slot, addr, len, mem) } { - 0 => Ok(()), - v => Err(Error::from_raw_os_error(v)), +impl VCpu { + pub fn get_regs(&self) -> Result { + let mut regs = MaybeUninit::uninit(); + + match unsafe { kvm_get_regs(self.fd.as_raw_fd(), regs.as_mut_ptr()) } { + 0 => Ok(unsafe { regs.assume_init() }), + _ => Err(Error::last_os_error()), + } + } + + pub fn set_regs(&self, regs: KvmRegs) -> Result<(), Error> { + match unsafe { kvm_set_regs(self.fd.as_raw_fd(), ®s) } { + 0 => Ok(()), + _ => Err(Error::last_os_error()), + } } } @@ -67,6 +188,8 @@ extern "C" { fn kvm_check_version(kvm: c_int, compat: *mut bool) -> c_int; fn kvm_max_vcpus(kvm: c_int, max: *mut usize) -> c_int; fn kvm_create_vm(kvm: c_int, fd: *mut c_int) -> c_int; + fn kvm_get_vcpu_mmap_size(kvm: c_int) -> c_int; + fn kvm_set_user_memory_region( vm: c_int, slot: u32, @@ -74,4 +197,24 @@ extern "C" { len: u64, mem: *mut c_void, ) -> c_int; + fn kvm_create_vcpu(vm: c_int, id: c_int, fd: *mut c_int) -> c_int; + + fn kvm_run(vcpu: c_int) -> c_int; + fn kvm_get_regs(vcpu: c_int, regs: *mut KvmRegs) -> c_int; + fn kvm_set_regs(vcpu: c_int, regs: *const KvmRegs) -> c_int; +} + +#[derive(Debug, Error)] +pub enum CreateVCpusError { + #[error("failed to create vcpu #{1}")] + CreateVcpuFailed(#[source] CreateVCpuError, u8), +} + +#[derive(Debug, Error)] +pub enum CreateVCpuError { + #[error("failed to create vcpu")] + CreateVcpuFailed(#[source] Error), + + #[error("failed to mmap KVM_RUN")] + MmapFailed(#[source] Error), } diff --git a/src/kernel/src/hv/linux/regs.rs b/src/kernel/src/hv/linux/regs.rs new file mode 100644 index 000000000..b9bb280f7 --- /dev/null +++ b/src/kernel/src/hv/linux/regs.rs @@ -0,0 +1,75 @@ +#[repr(C)] +pub struct KvmRegs { + rax: u64, + rbx: u64, + rcx: u64, + rdx: u64, + + rsi: u64, + rdi: u64, + rsp: u64, + rbp: u64, + + r8: u64, + r9: u64, + r10: u64, + r11: u64, + + r12: u64, + r13: u64, + r14: u64, + r15: u64, + + rip: u64, + rflags: u64, +} + +#[repr(C)] +pub struct KvmSpecialRegs { + cs: KvmSegment, + ds: KvmSegment, + es: KvmSegment, + fs: KvmSegment, + gs: KvmSegment, + ss: KvmSegment, + + tr: KvmSegment, + ldt: KvmSegment, + + gdt: KvmDTable, + idt: KvmDTable, + + cr0: u64, + cr2: u64, + cr3: u64, + cr4: u64, + cr8: u64, + + efer: u64, + apic_base: u64, + interrupt_bitmap: [u64; 4], +} + +#[repr(C)] +struct KvmSegment { + base: u64, + limit: u32, + selector: u16, + ty: u8, + present: u8, + dpl: u8, + db: u8, + s: u8, + l: u8, + g: u8, + avl: u8, + unusable: u8, + padding: u8, +} + +#[repr(C)] +struct KvmDTable { + base: u64, + limit: u16, + padding: [u16; 3], +} diff --git a/src/kernel/src/hv/linux/run.rs b/src/kernel/src/hv/linux/run.rs new file mode 100644 index 000000000..28a8e70a3 --- /dev/null +++ b/src/kernel/src/hv/linux/run.rs @@ -0,0 +1,307 @@ +use std::mem::ManuallyDrop; + +#[repr(C)] +pub struct KvmRun { + request_interrupt_window: u8, + immediate_exit: u8, + padding1: [u8; 6], + + exit_reason: u32, + ready_for_interrupt_injection: u8, + if_flag: u8, + flags: u16, + + cr8: u64, + apic_base: u64, + exit: Exit, +} + +#[repr(C)] +union Exit { + hw: ManuallyDrop, + fail_entry: ManuallyDrop, + ex: ManuallyDrop, + io: ManuallyDrop, + debug: ManuallyDrop, + mmio: ManuallyDrop, + iocsr_io: ManuallyDrop, + hypercall: ManuallyDrop, + tpr_access: ManuallyDrop, + s390_sieic: ManuallyDrop, + s390_reset_flags: u64, + s390_ucontrol: ManuallyDrop, + dcr: ManuallyDrop, + internal: ManuallyDrop, + emulation_failure: ManuallyDrop, + osi: ManuallyDrop, + papr_hcall: ManuallyDrop, + s390_tsch: ManuallyDrop, + epr: ManuallyDrop, + system_event: ManuallyDrop, + s390_stsi: ManuallyDrop, + eoi: ManuallyDrop, + hyperv: ManuallyDrop, + arm_nisv: ManuallyDrop, + msr: ManuallyDrop, + xen: ManuallyDrop, + riscv_sbi: ManuallyDrop, + riscv_csr: ManuallyDrop, + notify: ManuallyDrop, + padding: [u8; 256], +} + +#[repr(C)] +struct Hw { + hardware_exit_reason: u64, +} + +#[repr(C)] +struct FailEntry { + hardware_entry_failure_reason: u64, + cpu: u32, +} + +#[repr(C)] +struct Ex { + exception: u32, + error_code: u32, +} + +#[repr(C)] +struct Io { + direction: u8, + size: u8, + port: u16, + count: u32, + data_offset: u64, +} + +#[repr(C)] +struct Debug { + arch: KvmDebugExitArch, +} + +#[repr(C)] +struct KvmDebugExitArch { + exception: u32, + pad: u32, + pc: u64, + dr6: u64, + dr7: u64, +} + +#[repr(C)] +struct Mmio { + phys_addr: u64, + data: [u8; 8], + len: u32, + is_write: u8, +} + +#[repr(C)] +struct IocsrIo { + phys_addr: u64, + data: [u8; 8], + len: u32, + is_write: u8, +} + +#[repr(C)] +struct Hypercall { + nr: u64, + args: [u64; 6], + ret: u64, + inner: HypercallInner, +} + +/// This struct has to be named in Rust +#[repr(C)] +union HypercallInner { + longmode: u32, + flags: u64, +} + +#[repr(C)] +struct TprAccess { + rip: u64, + is_write: u32, + pad: u32, +} + +#[repr(C)] +struct S390Sieic { + iptcode: u32, + ipa: u16, + ipb: u32, +} + +#[repr(C)] +struct S390Ucontrol { + trans_exc_code: u64, + pgm_code: u32, +} + +#[repr(C)] +struct Dcr { + dcrn: u32, + data: u32, + is_write: u8, +} + +#[repr(C)] +struct Internal { + suberror: u32, + ndata: u32, + data: [u64; 16], +} + +#[repr(C)] +struct EmulationFailure { + suberror: u32, + ndata: u32, + flags: u64, + insn_size: u8, + insn_bytes: [u8; 15], +} + +#[repr(C)] +struct Osi { + gprs: [u64; 32], +} + +#[repr(C)] +struct PaprHcall { + nr: u64, + ret: u64, + args: [u64; 9], +} + +#[repr(C)] +struct S390Tsch { + subchannel_id: u16, + subchannel_nr: u16, + io_int_parm: u32, + io_int_word: u32, + dequeued: u8, +} + +#[repr(C)] +struct Epr { + epr: u32, +} + +#[repr(C)] +struct SystemEvent { + ty: u32, + ndata: u32, + inner: SystemEventInner, +} + +/// This struct has to have a name in Rust +#[repr(C)] +union SystemEventInner { + flags: u64, + data: [u64; 16], +} + +#[repr(C)] +struct S390Stsi { + addr: u64, + ar: u8, + reserver: u8, + fc: u8, + sel1: u8, + sel2: u8, +} + +#[repr(C)] +struct Eoi { + vector: u8, +} + +#[repr(C)] +struct KvmHypervExit { + ty: u32, + pad1: u32, + u: KvmHypervExitInner, +} + +#[repr(C)] +union KvmHypervExitInner { + synic: ManuallyDrop, + hcall: ManuallyDrop, + debug: ManuallyDrop, +} + +#[repr(C)] +struct Synic { + msr: u32, + pad2: u32, + control: u64, + evt_page: u64, + msg_page: u64, +} + +#[repr(C)] +struct Hcall { + input: u64, + result: u64, + params: [u64; 2], +} + +#[repr(C)] +struct Syndbg { + msr: u32, + pad2: u32, + control: u64, + status: u64, + send_page: u64, + recv_page: u64, + pending_page: u64, +} + +#[repr(C)] +struct ArmNisv { + esr_iss: u64, + fault_ipa: u64, +} + +#[repr(C)] +struct Msr { + error: u8, + pad: [u8; 7], + reason: u32, + index: u32, + data: u64, +} + +#[repr(C)] +struct KvmXenExit { + ty: u32, + longmode: u32, + cp1: u32, + input: u64, + result: u64, + params: [u64; 6], +} + +#[repr(C)] +struct RiscvSbi { + extension_id: u64, + function_id: u64, + args: [u64; 6], + ret: [u64; 2], +} + +#[repr(C)] +struct RiscvCsr { + csr_num: u64, + new_value: u64, + write_mask: u64, + ret_value: u64, +} + +#[repr(C)] +struct Notify { + flags: u32, +} diff --git a/src/kernel/src/hv/mod.rs b/src/kernel/src/hv/mod.rs index 184df616d..0ea1ac3db 100644 --- a/src/kernel/src/hv/mod.rs +++ b/src/kernel/src/hv/mod.rs @@ -11,16 +11,21 @@ mod win32; /// Manage a virtual machine for running the PS4 processes. /// -/// Do not create more than one Hypervisor because it will not work macOS. +/// Do not create more than one Hypervisor because it will not work on macOS. pub struct Hypervisor { #[cfg(target_os = "linux")] - vm: std::os::fd::OwnedFd, // Drop before KVM. + vcpus: self::linux::VCpus, // Drop before VM. #[cfg(target_os = "linux")] - kvm: std::os::fd::OwnedFd, + vm: self::linux::Vm, // Drop before KVM. + #[cfg(target_os = "linux")] + kvm: self::linux::Kvm, + #[cfg(target_os = "windows")] part: self::win32::Partition, + #[cfg(target_os = "macos")] vm: self::mac::Vm, + ram: Ram, // Drop after a VM. } @@ -41,28 +46,34 @@ impl Hypervisor { #[cfg(target_os = "linux")] fn new_linux(ram: Ram) -> Result { - use std::os::fd::AsFd; - // Open KVM device. - let kvm = self::linux::open_kvm()?; + let kvm = self::linux::Kvm::open()?; - if self::linux::max_vcpus(kvm.as_fd()).map_err(HypervisorError::GetMaxCpuFailed)? < 8 { + if kvm.max_vcpus()? < 8 { return Err(HypervisorError::MaxCpuTooLow); } // Create a new VM. - let vm = self::linux::create_vm(kvm.as_fd()).map_err(HypervisorError::CreateVmFailed)?; + let vm = kvm.create_vm()?; - self::linux::set_user_memory_region( - vm.as_fd(), + vm.set_user_memory_region( 0, ram.vm_addr().try_into().unwrap(), ram.len().try_into().unwrap(), ram.host_addr().cast(), - ) - .map_err(HypervisorError::MapRamFailed)?; - - Ok(Self { vm, kvm, ram }) + )?; + + let mmap_size = kvm.get_vcpu_mmap_size()?; + let vcpus = vm + .create_vcpus(mmap_size) + .map_err(HypervisorError::CreateVCpusError)?; + + Ok(Self { + vcpus, + vm, + kvm, + ram, + }) } #[cfg(target_os = "windows")] @@ -126,6 +137,10 @@ pub enum HypervisorError { #[error("couldn't create a RAM")] CreateRamFailed(#[source] std::io::Error), + #[cfg(target_os = "linux")] + #[error("couldn't get maximum number of CPU for a VM")] + GetMaxCpuFailed(#[source] std::io::Error), + #[error("your OS does not support 8 vCPU on a VM")] MaxCpuTooLow, @@ -141,10 +156,6 @@ pub enum HypervisorError { #[error("unexpected KVM version")] KvmVersionMismatched, - #[cfg(target_os = "linux")] - #[error("couldn't get maximum number of CPU for a VM")] - GetMaxCpuFailed(#[source] std::io::Error), - #[cfg(target_os = "linux")] #[error("couldn't create a VM")] CreateVmFailed(#[source] std::io::Error), @@ -153,6 +164,14 @@ pub enum HypervisorError { #[error("couldn't map the RAM to the VM")] MapRamFailed(#[source] std::io::Error), + #[cfg(target_os = "linux")] + #[error("couldn't get the size of vCPU mmap")] + GetMmapSizeFailed(#[source] std::io::Error), + + #[cfg(target_os = "linux")] + #[error("couldn't create vCPUs")] + CreateVCpusError(#[source] self::linux::CreateVCpusError), + #[cfg(target_os = "windows")] #[error("couldn't create WHP partition object ({0:#x})")] CreatePartitionFailed(windows_sys::core::HRESULT), diff --git a/src/kernel/src/hv/ram.rs b/src/kernel/src/hv/ram.rs index 025b08072..dc262f5e1 100644 --- a/src/kernel/src/hv/ram.rs +++ b/src/kernel/src/hv/ram.rs @@ -5,7 +5,7 @@ use std::io::Error; /// /// This struct will allocate a 8GB of memory immediately but not commit any parts of it until there /// is an allocation request. That mean the actual memory usage is not fixed at 8GB but will be -/// depend on what PS4 applications currently running. If it is a simple game the memory usage might +/// dependent on what PS4 applications currently running. If it is a simple game the memory usage might be /// just a hundred of megabytes. pub struct Ram { addr: usize, @@ -16,7 +16,6 @@ impl Ram { pub const SIZE: usize = 1024 * 1024 * 1024 * 8; // 8GB pub fn new(addr: usize) -> Result { - // Reserve a memory range on *nix. #[cfg(unix)] let mem = { use libc::{mmap, MAP_ANON, MAP_FAILED, MAP_PRIVATE, PROT_NONE}; @@ -40,7 +39,6 @@ impl Ram { mem.cast() }; - // Reserve a memory range on Windows. #[cfg(windows)] let mem = { use std::ptr::null; diff --git a/src/kernel/src/idps/mod.rs b/src/kernel/src/idps/mod.rs new file mode 100644 index 000000000..f41eb4f19 --- /dev/null +++ b/src/kernel/src/idps/mod.rs @@ -0,0 +1,85 @@ +use serde::{Deserialize, Deserializer}; +use std::str::FromStr; +use thiserror::Error; + +/// Implementation of [IDPS]. +/// +/// All fields here are big-endian the same as PS3. +/// +/// [IDPS]: https://www.psdevwiki.com/ps3/IDPS +#[repr(C)] +#[derive(Clone)] +pub struct ConsoleId { + magic: u16, + company: CompanyId, + product: ProductId, + prodsub: u16, + serial: [u8; 8], +} + +impl ConsoleId { + pub fn new(company: CompanyId, product: ProductId, prodsub: u16, serial: [u8; 8]) -> Self { + Self { + magic: 0, + company, + product, + prodsub, + serial, + } + } +} + +impl Default for ConsoleId { + fn default() -> Self { + Self::new( + CompanyId::SONY, + ProductId::USA, + 0x1200, + [0x10, 0, 0, 0, 0, 0, 0, 0], + ) + } +} + +impl FromStr for ConsoleId { + type Err = FromStrError; + + fn from_str(s: &str) -> Result { + todo!() + } +} + +impl<'de> Deserialize<'de> for ConsoleId { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + todo!() + } +} + +/// Company identifier for [`ConsoleId`]. +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct CompanyId(u16); + +impl CompanyId { + pub const SONY: Self = Self(0x100); +} + +/// Product identifier for [`ConsoleId`]. +/// +/// See https://www.psdevwiki.com/ps4/Console_ID for a list of known IDs. +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct ProductId(u16); + +#[allow(dead_code)] +impl ProductId { + pub const DEVKIT: Self = Self(0x8101); + pub const TESTKIT: Self = Self(0x8201); + pub const USA: Self = Self(0x8401); +} + +/// Represents an error when [`ConsoleId`] fails to construct from a string. +#[derive(Debug, Error)] +pub enum FromStrError {} diff --git a/src/kernel/src/idt/mod.rs b/src/kernel/src/idt/mod.rs index 966f8eb39..e4ba45ce2 100644 --- a/src/kernel/src/idt/mod.rs +++ b/src/kernel/src/idt/mod.rs @@ -29,11 +29,8 @@ impl Idt { } } - pub fn alloc_infallible(&mut self, factory: F) -> usize - where - F: FnOnce(usize) -> Entry, - { - let Ok((_, id)) = self.alloc::<_, Infallible>(|id| Ok(factory(id))) else { + pub fn alloc(&mut self, entry: Entry) -> usize { + let Ok((_, id)) = self.try_alloc_with::<_, Infallible>(|id| Ok(entry)) else { unreachable!(); }; @@ -41,7 +38,7 @@ impl Idt { } /// See `id_alloc` on the PS4 for a reference. - pub fn alloc(&mut self, factory: F) -> Result<(&mut Entry, usize), E> + pub fn try_alloc_with(&mut self, factory: F) -> Result<(&mut Entry, usize), E> where F: FnOnce(usize) -> Result, E>, { diff --git a/src/kernel/src/kqueue/mod.rs b/src/kernel/src/kqueue/mod.rs index 72ce736d5..a5e973efb 100644 --- a/src/kernel/src/kqueue/mod.rs +++ b/src/kernel/src/kqueue/mod.rs @@ -1,17 +1,12 @@ -use crate::{ - budget::BudgetType, - errno::Errno, - fs::{ - DefaultFileBackendError, FileBackend, PollEvents, Stat, TruncateLength, VFile, VFileFlags, - VFileType, - }, - process::{FileDesc, VThread}, - syscalls::{SysErr, SysIn, SysOut, Syscalls}, -}; -use std::{ - convert::Infallible, - sync::{Arc, Weak}, +use crate::budget::BudgetType; +use crate::errno::Errno; +use crate::fs::{ + DefaultFileBackendError, PollEvents, Stat, TruncateLength, VFile, VFileFlags, Vnode, }; +use crate::process::{FileDesc, VThread}; +use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; +use std::convert::Infallible; +use std::sync::{Arc, Weak}; pub struct KernelQueueManager {} @@ -38,9 +33,11 @@ impl KernelQueueManager { filedesc.insert_kqueue(kq.clone()); - Ok(VFileType::KernelQueue(kq)) + Ok(VFile::new( + VFileFlags::READ | VFileFlags::WRITE, + Box::new(FileBackend(kq)), + )) }, - VFileFlags::READ | VFileFlags::WRITE, BudgetType::FdEqueue, )?; @@ -61,13 +58,21 @@ impl KernelQueue { } } -impl FileBackend for KernelQueue { +/// Implementation of [`crate::fs::FileBackend`] for kqueue. +#[derive(Debug)] +struct FileBackend(Arc); + +impl crate::fs::FileBackend for FileBackend { + fn is_seekable(&self) -> bool { + todo!() + } + #[allow(unused_variables)] // TODO: remove when implementing - fn poll(self: &Arc, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents { + fn poll(&self, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents { todo!() } - fn stat(self: &Arc, _: &VFile, _: Option<&VThread>) -> Result> { + fn stat(&self, _: &VFile, _: Option<&VThread>) -> Result> { let mut stat = Stat::zeroed(); stat.mode = 0o10000; @@ -76,11 +81,15 @@ impl FileBackend for KernelQueue { } fn truncate( - self: &Arc, + &self, _: &VFile, _: TruncateLength, _: Option<&VThread>, ) -> Result<(), Box> { Err(Box::new(DefaultFileBackendError::InvalidValue)) } + + fn vnode(&self) -> Option<&Arc> { + None + } } diff --git a/src/kernel/src/main.rs b/src/kernel/src/main.rs index 978c58b68..5b678e646 100644 --- a/src/kernel/src/main.rs +++ b/src/kernel/src/main.rs @@ -1,8 +1,9 @@ use crate::arch::MachDep; use crate::budget::{Budget, BudgetManager, ProcType}; use crate::dev::{ - DebugManager, DebugManagerInitError, DipswInitError, DipswManager, DmemContainer, TtyManager, - TtyManagerInitError, + CameraInitError, CameraManager, DceManager, DebugManager, DebugManagerInitError, + DipswInitError, DipswManager, DmemContainer, GcInitError, GcManager, HmdManager, RngInitError, + RngManager, SblSrvManager, TtyManager, TtyManagerInitError, }; use crate::dmem::{DmemManager, DmemManagerInitError}; use crate::ee::native::NativeEngine; @@ -10,21 +11,25 @@ use crate::ee::EntryArg; use crate::errno::EEXIST; use crate::fs::{Fs, FsInitError, MkdirError, MountError, MountFlags, MountOpts, VPath, VPathBuf}; use crate::hv::Hypervisor; +use crate::idps::ConsoleId; use crate::kqueue::KernelQueueManager; use crate::log::{print, LOGGER}; use crate::namedobj::NamedObjManager; use crate::net::NetManager; use crate::osem::OsemManager; use crate::process::{ProcManager, VThread}; +use crate::rcmgr::RcMgr; use crate::regmgr::RegMgr; use crate::rtld::{ExecError, LoadFlags, ModuleFlags, RuntimeLinker}; use crate::shm::SharedMemoryManager; +use crate::signal::SignalManager; use crate::syscalls::Syscalls; use crate::sysctl::Sysctl; use crate::time::TimeManager; use crate::ucred::{AuthAttrs, AuthCaps, AuthInfo, AuthPaid, Gid, Ucred, Uid}; use crate::umtx::UmtxManager; use clap::Parser; +use dev::{DceInitError, HmdInitError, SblSrvInitError}; use llt::{OsThread, SpawnError}; use macros::vpath; use param::Param; @@ -32,8 +37,8 @@ use serde::Deserialize; use std::error::Error; use std::fs::{create_dir_all, remove_dir_all, File}; use std::io::Write; -use std::path::PathBuf; -use std::process::{ExitCode, Termination}; +use std::path::{Path, PathBuf}; +use std::process::ExitCode; use std::sync::Arc; use std::time::SystemTime; use sysinfo::{MemoryRefreshKind, System}; @@ -48,6 +53,7 @@ mod ee; mod errno; mod fs; mod hv; +mod idps; mod idt; mod imgact; mod kqueue; @@ -56,6 +62,7 @@ mod namedobj; mod net; mod osem; mod process; +mod rcmgr; mod regmgr; mod rtld; mod shm; @@ -68,23 +75,42 @@ mod ucred; mod umtx; mod vm; -fn main() -> Exit { - run().into() -} - -fn run() -> Result<(), KernelError> { - // Begin logger. - log::init(); - +fn main() -> ExitCode { // Load arguments. - let args = if std::env::args().any(|a| a == "--debug") { - let file = File::open(".kernel-debug").map_err(KernelError::FailedToOpenDebugConfig)?; + let args = if std::env::args_os().any(|a| a == "--debug") { + let path = Path::new(".kernel-debug"); + let file = match File::open(path) { + Ok(v) => v, + Err(e) => { + eprintln!("Failed to open {}: {}.", path.display(), e); + return ExitCode::FAILURE; + } + }; - serde_yaml::from_reader(file).map_err(KernelError::FailedToParseDebugConfig)? + match serde_yaml::from_reader(file) { + Ok(v) => v, + Err(e) => { + eprintln!("Failed to parse {}: {}.", path.display(), e); + return ExitCode::FAILURE; + } + } } else { - Args::try_parse().map_err(KernelError::FailedToParseArgs)? + Args::parse() }; + // Run the kernel. + log::init(); + + match run(args) { + Ok(_) => ExitCode::SUCCESS, + Err(e) => { + error!(e, "Error while running kernel"); + ExitCode::FAILURE + } + } +} + +fn run(args: Args) -> Result<(), KernelError> { // Initialize debug dump. if let Some(path) = &args.debug_dump { // Remove previous dump. @@ -192,8 +218,9 @@ fn run() -> Result<(), KernelError> { // Initialize foundations. #[allow(unused_variables)] // TODO: Remove this when someone uses hv. let hv = Hypervisor::new()?; - let mut syscalls = Syscalls::new(); - let fs = Fs::new(args.system, &cred, &mut syscalls)?; + let mut sys = Syscalls::new(); + let fs = Fs::new(args.system, &cred, &mut sys).map_err(KernelError::FilesystemInitFailed)?; + let rc = RcMgr::new(); // TODO: Check permission of /mnt on the PS4. let path = vpath!("/mnt"); @@ -344,31 +371,44 @@ fn run() -> Result<(), KernelError> { // Initialize TTY system. #[allow(unused_variables)] // TODO: Remove this when someone uses tty. - let tty = TtyManager::new()?; + let tty = TtyManager::new().map_err(KernelError::TtyInitFailed)?; #[allow(unused_variables)] // TODO: Remove this when someone uses dipsw. - let dipsw = DipswManager::new()?; + let dipsw = DipswManager::new().map_err(KernelError::DipswInitFailed)?; + #[allow(unused_variables)] // TODO: Remove this when someone uses gc. + let gc = GcManager::new().map_err(KernelError::GcManagerInitFailed)?; + #[allow(unused_variables)] // TODO: Remove this when someone uses camera. + let camera = CameraManager::new().map_err(KernelError::CameraManagerInitFailed)?; + #[allow(unused_variables)] // TODO: Remove this when someone uses rng. + let rng = RngManager::new().map_err(KernelError::RngManagerInitFailed)?; + #[allow(unused_variables)] // TODO: Remove this when someone uses sbl_srv. + let sbl_srv = SblSrvManager::new().map_err(KernelError::SblSrvManagerInitFailed)?; + #[allow(unused_variables)] // TODO: Remove this when someone uses hmd. + let hmd_cmd = HmdManager::new().map_err(KernelError::HmdManagerInitFailed)?; + #[allow(unused_variables)] // TODO: Remove this when someone uses dce. + let dce = DceManager::new().map_err(KernelError::DceManagerInitFailed)?; // Initialize kernel components. #[allow(unused_variables)] // TODO: Remove this when someone uses debug. - let debug = DebugManager::new()?; - RegMgr::new(&mut syscalls); - let machdep = MachDep::new(&mut syscalls); - let budget = BudgetManager::new(&mut syscalls); - - DmemManager::new(&fs, &mut syscalls)?; - SharedMemoryManager::new(&mut syscalls); - Sysctl::new(&machdep, &mut syscalls); - TimeManager::new(&mut syscalls); - KernelQueueManager::new(&mut syscalls); - NetManager::new(&mut syscalls); - NamedObjManager::new(&mut syscalls); - OsemManager::new(&mut syscalls); - UmtxManager::new(&mut syscalls); - let pmgr = ProcManager::new(&mut syscalls); + let debug = DebugManager::new().map_err(KernelError::DebugManagerInitFailed)?; + RegMgr::new(&mut sys); + let machdep = MachDep::new(&mut sys); + let budget = BudgetManager::new(&mut sys); + + SignalManager::new(&mut sys); + DmemManager::new(&fs, &mut sys).map_err(KernelError::DmemManagerInitFailed)?; + SharedMemoryManager::new(&mut sys); + Sysctl::new(&machdep, &mut sys); + TimeManager::new(&mut sys); + KernelQueueManager::new(&mut sys); + NetManager::new(&mut sys); + NamedObjManager::new(&mut sys); + OsemManager::new(&mut sys); + UmtxManager::new(&mut sys); + let pmgr = ProcManager::new(&fs, &rc, &mut sys); // Initialize runtime linker. let ee = NativeEngine::new(); - let ld = RuntimeLinker::new(&fs, &ee, &mut syscalls); + let ld = RuntimeLinker::new(&fs, &ee, &mut sys); // TODO: Get correct budget name from the PS4. let budget_id = budget.create(Budget::new("big app", ProcType::BigApp)); @@ -381,7 +421,7 @@ fn run() -> Result<(), KernelError> { DmemContainer::One, // See sys_budget_set on the PS4. proc_root, system_component, - syscalls, + sys, ) .map_err(KernelError::CreateProcessFailed)?; @@ -542,6 +582,7 @@ fn join_thread(thr: OsThread) -> Result<(), std::io::Error> { } #[derive(Parser, Deserialize)] +#[command(about)] #[serde(rename_all = "kebab-case")] struct Args { #[arg(long)] @@ -560,6 +601,10 @@ struct Args { #[arg(long)] #[serde(default)] pro: bool, + + #[arg(long)] + #[serde(default)] + idps: ConsoleId, } #[derive(Debug, Error)] @@ -573,15 +618,6 @@ enum DiscordPresenceError { #[derive(Debug, Error)] enum KernelError { - #[error("couldn't open .kernel-debug")] - FailedToOpenDebugConfig(#[source] std::io::Error), - - #[error("couldn't parse .kernel-debug")] - FailedToParseDebugConfig(#[source] serde_yaml::Error), - - #[error("couldn't parse arguments")] - FailedToParseArgs(#[source] clap::Error), - #[error("couldn't open param.sfo")] FailedToOpenGameParam(#[source] std::io::Error), @@ -592,7 +628,7 @@ enum KernelError { InvalidTitleId(PathBuf), #[error("filesystem initialization failed")] - FilesystemInitFailed(#[from] FsInitError), + FilesystemInitFailed(#[source] FsInitError), #[error("couldn't create {0}")] CreateDirectoryFailed(VPathBuf, #[source] MkdirError), @@ -601,16 +637,34 @@ enum KernelError { MountFailed(VPathBuf, #[source] MountError), #[error("tty initialization failed")] - TtyInitFailed(#[from] TtyManagerInitError), + TtyInitFailed(#[source] TtyManagerInitError), #[error("dipsw initialization failed")] - DipswInitFailed(#[from] DipswInitError), + DipswInitFailed(#[source] DipswInitError), #[error("debug manager initialization failed")] - DebugManagerInitFailed(#[from] DebugManagerInitError), + DebugManagerInitFailed(#[source] DebugManagerInitError), + + #[error("gc manager initialization failed")] + GcManagerInitFailed(#[source] GcInitError), + + #[error("camera manager initialization failed")] + CameraManagerInitFailed(#[source] CameraInitError), + + #[error("rng manager initialization failed")] + RngManagerInitFailed(#[source] RngInitError), #[error("dmem manager initialization failed")] - DmemManagerInitFailes(#[from] DmemManagerInitError), + DmemManagerInitFailed(#[source] DmemManagerInitError), + + #[error("sbl_srv manager initialization failed")] + SblSrvManagerInitFailed(#[source] SblSrvInitError), + + #[error("hmd manager initialization failed")] + HmdManagerInitFailed(#[source] HmdInitError), + + #[error("dce manager initialization failed")] + DceManagerInitFailed(#[source] DceInitError), #[error("couldn't create application process")] CreateProcessFailed(#[source] self::process::SpawnError), @@ -633,32 +687,3 @@ enum KernelError { #[error("failed to join with main thread")] FailedToJoinMainThread(#[source] std::io::Error), } - -/// We have to use this for a custom implementation of the [`Termination`] trait, because -/// we need to log the error using our own error! macro instead of [`std::fmt::Debug::fmt`], -/// which is what the default implementation of Termination uses for [`Result`]. -enum Exit { - Ok, - Err(KernelError), -} - -impl Termination for Exit { - fn report(self) -> ExitCode { - match self { - Exit::Ok => ExitCode::SUCCESS, - Exit::Err(e) => { - error!(e, "Error while running kernel"); - ExitCode::FAILURE - } - } - } -} - -impl From> for Exit { - fn from(r: Result<(), KernelError>) -> Self { - match r { - Ok(_) => Exit::Ok, - Err(e) => Exit::Err(e), - } - } -} diff --git a/src/kernel/src/namedobj/mod.rs b/src/kernel/src/namedobj/mod.rs index f7f8c151d..611185004 100644 --- a/src/kernel/src/namedobj/mod.rs +++ b/src/kernel/src/namedobj/mod.rs @@ -27,7 +27,7 @@ impl NamedObjManager { let obj = NamedObj::new(name, data); - let id = table.alloc_infallible(|_| { + let id = table.alloc({ Entry::new( Some(name.to_owned()), Arc::new(obj), diff --git a/src/kernel/src/net/mod.rs b/src/kernel/src/net/mod.rs index 6d6203849..4131c6c50 100644 --- a/src/kernel/src/net/mod.rs +++ b/src/kernel/src/net/mod.rs @@ -1,25 +1,21 @@ +use self::socket::{Socket, SocketCreateError, SocketFileBackend}; use crate::budget::BudgetType; -use crate::errno::{Errno, EFAULT, EINVAL}; -use crate::fs::{IoVec, VFileFlags, VFileType}; +use crate::errno::{Errno, EFAULT, EINVAL, ENAMETOOLONG, ENOTSOCK}; +use crate::fs::{IoVec, VFile, VFileFlags}; use crate::info; -use crate::{ - process::VThread, - syscalls::{SysErr, SysIn, SysOut, Syscalls}, -}; +use crate::process::VThread; +use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; use bitflags::bitflags; -use core::fmt; use macros::Errno; +use std::fmt::Debug; use std::num::NonZeroI32; -use std::{ - fmt::{Display, Formatter}, - sync::Arc, -}; +use std::sync::Arc; use thiserror::Error; -pub use self::socket::*; - +mod proto; mod socket; +/// Provides networking services (e.g. socket). pub struct NetManager {} impl NetManager { @@ -29,8 +25,13 @@ impl NetManager { sys.register(27, &net, Self::sys_recvmsg); sys.register(28, &net, Self::sys_sendmsg); sys.register(29, &net, Self::sys_recvfrom); + sys.register(30, &net, Self::sys_accept); sys.register(97, &net, Self::sys_socket); + sys.register(98, &net, Self::sys_connect); + sys.register(99, &net, Self::sys_netcontrol); + sys.register(104, &net, Self::sys_bind); sys.register(105, &net, Self::sys_setsockopt); + sys.register(106, &net, Self::sys_listen); sys.register(113, &net, Self::sys_socketex); sys.register(114, &net, Self::sys_socketclose); sys.register(118, &net, Self::sys_getsockopt); @@ -39,7 +40,6 @@ impl NetManager { net } - #[allow(unused_variables)] // TODO: Remove this when implementing fn sys_recvmsg(self: &Arc, td: &VThread, i: &SysIn) -> Result { let fd: i32 = i.args[0].try_into().unwrap(); let msg: *mut MsgHdr = i.args[1].into(); @@ -48,6 +48,10 @@ impl NetManager { MessageFlags::from_bits_retain(flags) }; + let msg = unsafe { &mut *msg }; + + info!("Receiving a message {msg:?} from fd {fd} with flags {flags:?}."); + todo!() } @@ -59,6 +63,10 @@ impl NetManager { MessageFlags::from_bits_retain(flags) }; + let msg = unsafe { &*msg }; + + info!("Sending a message {msg:?} to fd {fd} with flags {flags:?}."); + let sent = self.sendit(fd, unsafe { &*msg }, flags, td)?; Ok(sent.into()) @@ -77,6 +85,93 @@ impl NetManager { todo!() } + #[allow(unused_variables)] // TODO: Remove this when implementing + fn sys_accept(self: &Arc, td: &VThread, i: &SysIn) -> Result { + let fd: i32 = i.args[0].try_into().unwrap(); + let addr: *mut u8 = i.args[1].into(); + let addrlen: *mut u32 = i.args[2].try_into().unwrap(); + + todo!() + } + + fn sys_bind(self: &Arc, td: &VThread, i: &SysIn) -> Result { + let fd: i32 = i.args[0].try_into().unwrap(); + let ptr: *const u8 = i.args[1].into(); + let len: i32 = i.args[2].try_into().unwrap(); + let addr = unsafe { SockAddr::get(ptr, len) }?; + + info!("Binding socket at fd {fd} to {addr:?}."); + + self.bind(fd, &addr, td)?; + + Ok(SysOut::ZERO) + } + + /// See `kern_bind` on the PS4 for a reference. + fn bind(&self, fd: i32, addr: &SockAddr, td: &VThread) -> Result<(), SysErr> { + let file = td.proc().files().get(fd)?; + let sock = file + .backend::() + .ok_or(SysErr::Raw(ENOTSOCK))? + .as_sock(); + + sock.bind(addr, td)?; + + Ok(()) + } + + fn sys_netcontrol(self: &Arc, _: &VThread, i: &SysIn) -> Result { + let fd: i32 = i.args[0].try_into().unwrap(); + let op: i32 = i.args[1].try_into().unwrap(); + let ptr: *mut u8 = i.args[2].into(); + let buflen: u32 = i.args[3].try_into().unwrap(); + + info!("Netcontrol called with op = {op}."); + + let mut buf = if ptr.is_null() { + None + } else { + if buflen > 160 { + return Err(SysErr::Raw(EINVAL)); + } + + let buf = Box::new([0u8; 160]); + + if op & 0x30000000 != 0 { + // TODO: copyin + todo!() + } + + Some(buf) + }; + + let _ = if fd < 0 { + } else { + todo!() + }; + + match buf.as_mut() { + Some(buf) => match op { + // bnet_get_secure_seed + 0x14 if buflen > 3 => crate::arnd::rand_bytes(&mut buf[..4]), + _ => todo!("netcontrol with op = {op}"), + }, + None => todo!("netcontrol with buf = null"), + } + + if fd > -1 { + todo!() + } + + if let Some(buf) = buf { + if op & 0x30000000 != 0x20000000 { + unsafe { std::ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buflen as usize) }; + } + } + + Ok(SysOut::ZERO) + } + fn sys_setsockopt(self: &Arc, td: &VThread, i: &SysIn) -> Result { let fd: i32 = i.args[0].try_into().unwrap(); let level: i32 = i.args[1].try_into().unwrap(); @@ -89,6 +184,20 @@ impl NetManager { Ok(SysOut::ZERO) } + fn sys_listen(self: &Arc, td: &VThread, i: &SysIn) -> Result { + let fd: i32 = i.args[0].try_into().unwrap(); + let backlog: i32 = i.args[1].try_into().unwrap(); + let file = td.proc().files().get(fd)?; + let socket = file + .backend::() + .ok_or(SysErr::Raw(ENOTSOCK))? + .as_sock(); + + socket.listen(backlog, Some(td))?; + + Ok(SysOut::ZERO) + } + fn sys_socket(self: &Arc, td: &VThread, i: &SysIn) -> Result { let domain: i32 = i.args[0].try_into().unwrap(); let ty: i32 = i.args[1].try_into().unwrap(); @@ -104,21 +213,38 @@ impl NetManager { |_| { let so = Socket::new(domain, ty, proto, td.cred(), td, None)?; - let ty = if domain == 1 { - VFileType::IpcSocket(so) - } else { - VFileType::Socket(so) - }; - - Ok(ty) + Ok(VFile::new( + VFileFlags::READ | VFileFlags::WRITE, + SocketFileBackend::new(so), + )) }, - VFileFlags::WRITE | VFileFlags::READ, budget, )?; + info!("Opened a socket at fd {fd} with domain {domain}, type {ty}, and proto {proto:?}."); + Ok(fd.into()) } + fn sys_connect(self: &Arc, td: &VThread, i: &SysIn) -> Result { + let fd: i32 = i.args[0].try_into().unwrap(); + let ptr: *const u8 = i.args[1].into(); + let len: i32 = i.args[2].try_into().unwrap(); + + let addr = unsafe { SockAddr::get(ptr, len) }?; + + info!("Connecting socket at fd {fd} to {addr:?}."); + + self.connect(fd, &addr, td)?; + + Ok(SysOut::ZERO) + } + + #[allow(unused_variables)] // TODO: Remove this when implementing + fn connect(&self, fd: i32, addr: &SockAddr, td: &VThread) -> Result<(), SysErr> { + todo!("connect") + } + fn sys_getsockopt(self: &Arc, td: &VThread, i: &SysIn) -> Result { let fd: i32 = i.args[0].try_into().unwrap(); let level: i32 = i.args[1].try_into().unwrap(); @@ -147,31 +273,30 @@ impl NetManager { |_| { let so = Socket::new(domain, ty, proto, td.cred(), td, name)?; - let ty = if domain == 1 { - VFileType::IpcSocket(so) - } else { - VFileType::Socket(so) - }; - - Ok(ty) + Ok(VFile::new( + VFileFlags::READ | VFileFlags::WRITE, + SocketFileBackend::new(so), + )) }, - VFileFlags::WRITE | VFileFlags::READ, budget, )?; + info!("Opened a socket at fd {fd} with domain {domain}, type {ty}, proto {proto:?} and name {name:?}."); + Ok(fd.into()) } fn sys_socketclose(self: &Arc, td: &VThread, i: &SysIn) -> Result { let fd: i32 = i.args[0].try_into().unwrap(); - info!("Attempting to close socket at fd {fd}."); + info!("Closing socket at fd {fd}."); td.proc().files().free(fd)?; Ok(SysOut::ZERO) } + #[allow(unused_variables)] // TODO: Remove this when implementing fn sys_sendto(self: &Arc, td: &VThread, i: &SysIn) -> Result { let fd: i32 = i.args[0].try_into().unwrap(); let buf: *const u8 = i.args[1].into(); @@ -183,12 +308,10 @@ impl NetManager { let to: *const u8 = i.args[4].into(); let tolen: u32 = i.args[5].try_into().unwrap(); - let ref iovec = unsafe { IoVec::from_raw_parts(buf, buflen) }; - let msg = MsgHdr { name: to, len: tolen, - iovec: iovec as *const IoVec, + iovec: todo!(), iovec_len: 1, control: core::ptr::null(), control_len: 0, @@ -260,43 +383,58 @@ impl NetManager { } bitflags! { + #[derive(Debug)] #[repr(C)] pub struct MessageFlags: u32 {} } +#[derive(Debug)] #[repr(C)] struct MsgHdr<'a> { name: *const u8, len: u32, - iovec: *const IoVec<'a>, + iovec: *mut IoVec<'a>, iovec_len: u32, control: *const u8, control_len: u32, flags: u32, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct AddressFamily(i32); +#[repr(transparent)] +pub struct SockAddr([u8]); -impl AddressFamily { - pub const UNSPEC: Self = Self(0); - pub const LOCAL: Self = Self::UNIX; - pub const UNIX: Self = Self(1); - pub const INET: Self = Self(2); - pub const ROUTE: Self = Self(17); - pub const INET6: Self = Self(28); -} +impl SockAddr { + pub unsafe fn get(ptr: *const u8, len: i32) -> Result, GetSockAddrError> { + if len > 255 { + return Err(GetSockAddrError::TooLong); + } -impl Display for AddressFamily { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match *self { - Self::UNSPEC => write!(f, "UNSPEC"), - Self::LOCAL => write!(f, "LOCAL"), - Self::INET => write!(f, "INET"), - Self::ROUTE => write!(f, "ROUTE"), - Self::INET6 => write!(f, "INET6"), - _ => todo!(), + if len < 2 { + return Err(GetSockAddrError::TooShort); } + + todo!() + } + + #[allow(unused)] // TODO: remove this when used + pub fn family(&self) -> u8 { + // SAFETY: this is ok because we know that the slice is big enough + unsafe { *self.0.get_unchecked(1) } + } + + #[allow(unused)] // TODO: remove this when used + pub fn addr(&self) -> &[u8] { + // SAFETY: this is ok because we know that the slice is big enough + unsafe { &self.0.get_unchecked(2..) } + } +} + +impl std::fmt::Debug for SockAddr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SockAddr") + .field("family", &self.family()) + .field("addr", &self.addr()) + .finish() } } @@ -322,3 +460,14 @@ enum GetOptError { #[derive(Debug, Error, Errno)] enum SendItError {} + +#[derive(Debug, Error, Errno)] +pub enum GetSockAddrError { + #[error("length too big")] + #[errno(ENAMETOOLONG)] + TooLong, + + #[error("too short")] + #[errno(EINVAL)] + TooShort, +} diff --git a/src/kernel/src/net/proto/inet/mod.rs b/src/kernel/src/net/proto/inet/mod.rs new file mode 100644 index 000000000..de59f38b9 --- /dev/null +++ b/src/kernel/src/net/proto/inet/mod.rs @@ -0,0 +1,64 @@ +use super::{ListenError, SockAddr, Socket, SocketBackend}; +use crate::errno::Errno; +use crate::fs::IoCmd; +use crate::process::VThread; +use std::sync::Arc; + +#[derive(Debug)] +pub(super) enum InetProtocol { + UdpPeerToPeer, +} + +impl SocketBackend for InetProtocol { + fn attach(&self, _: &Arc, _: &VThread) -> Result<(), Box> { + //TODO: properly implement this. + match self { + Self::UdpPeerToPeer => Ok(()), + } + } + + fn bind( + &self, + _socket: &Arc, + _addr: &SockAddr, + _td: &VThread, + ) -> Result<(), Box> { + todo!() + } + + fn connect( + &self, + _socket: &Arc, + _addr: &SockAddr, + _td: &VThread, + ) -> Result<(), Box> { + todo!() + } + + fn control( + &self, + _: &Arc, + cmd: IoCmd, + _: Option<&VThread>, + ) -> Result<(), Box> { + match self { + Self::UdpPeerToPeer => match cmd { + // TODO: properly implement this. It is difficult to judge what it currently does, + // because the socket is simply created, this ioctl is called and then the socket is immediately closed. + IoCmd::BNETUNK(_) => Ok(()), + _ => todo!(), + }, + } + } + + fn listen( + &self, + _socket: &Arc, + _backlog: i32, + _td: Option<&VThread>, + ) -> Result<(), Box> { + match self { + Self::UdpPeerToPeer => Err(Box::new(ListenError::NotSupported)), + } + } +} diff --git a/src/kernel/src/net/proto/mod.rs b/src/kernel/src/net/proto/mod.rs new file mode 100644 index 000000000..367163b66 --- /dev/null +++ b/src/kernel/src/net/proto/mod.rs @@ -0,0 +1,189 @@ +use super::{SockAddr, Socket}; +use crate::errno::{Errno, EOPNOTSUPP}; +use crate::fs::IoCmd; +use crate::process::VThread; +use macros::Errno; +use std::num::NonZeroI32; +use std::sync::Arc; +use thiserror::Error; + +use self::inet::*; +use self::unix::*; + +mod inet; +mod unix; + +/// An implementation of the `pr_usrreqs` struct. This is subject to potential refactors, as it has to cover a lot of code +/// and therefore it is impossible to fully predict the correct implementation. In the future, this struct might end up containing functions from the +/// `protosw` struct as well. +pub(super) trait SocketBackend { + #[allow(unused_variables)] + fn attach(&self, socket: &Arc, td: &VThread) -> Result<(), Box> { + Err(Box::new(AttachError::NotSupported)) + } + + #[allow(unused_variables)] + fn bind( + &self, + socket: &Arc, + addr: &SockAddr, + td: &VThread, + ) -> Result<(), Box> { + Err(Box::new(AttachError::NotSupported)) + } + + #[allow(unused_variables)] + fn connect( + &self, + socket: &Arc, + addr: &SockAddr, + td: &VThread, + ) -> Result<(), Box> { + Err(Box::new(ConnectError::NotSupported)) + } + + // TODO: a ifnet argument might have to be added in the future + #[allow(unused_variables)] + fn control( + &self, + socket: &Arc, + cmd: IoCmd, + td: Option<&VThread>, + ) -> Result<(), Box> { + Err(Box::new(ControlError::NotSupported)) + } + + #[allow(unused_variables)] + fn listen( + &self, + socket: &Arc, + backlog: i32, + td: Option<&VThread>, + ) -> Result<(), Box> { + Err(Box::new(ListenError::NotSupported)) + } +} +#[derive(Debug)] +pub(super) enum Protocol { + Unix(UnixProtocol), // 1 + Inet(InetProtocol), // 2 +} + +impl Protocol { + pub fn find(domain: i32, ty: i32, proto: Option) -> Option { + let protocol = match domain { + 1 => { + let protocol = match (ty, proto) { + (1, None) => UnixProtocol::Stream, + (2, None) => UnixProtocol::Datagram, + (5, None) => UnixProtocol::SeqPacket, + _ => todo!(), + }; + + Protocol::Unix(protocol) + } + 2 => { + let protocol = match (ty, proto) { + (6, None) => InetProtocol::UdpPeerToPeer, + _ => todo!(), + }; + Protocol::Inet(protocol) + } + _ => todo!(), + }; + + Some(protocol) + } +} + +impl SocketBackend for Protocol { + fn attach(&self, socket: &Arc, td: &VThread) -> Result<(), Box> { + match self { + Self::Unix(protocol) => protocol.attach(socket, td), + Self::Inet(protocol) => protocol.attach(socket, td), + } + } + + fn bind( + &self, + socket: &Arc, + addr: &SockAddr, + td: &VThread, + ) -> Result<(), Box> { + match self { + Self::Unix(protocol) => protocol.connect(socket, addr, td), + Self::Inet(protocol) => protocol.connect(socket, addr, td), + } + } + + fn connect( + &self, + socket: &Arc, + addr: &SockAddr, + td: &VThread, + ) -> Result<(), Box> { + match self { + Self::Unix(protocol) => protocol.connect(socket, addr, td), + Self::Inet(protocol) => protocol.connect(socket, addr, td), + } + } + + fn control( + &self, + socket: &Arc, + cmd: IoCmd, + td: Option<&VThread>, + ) -> Result<(), Box> { + match self { + Self::Unix(protocol) => protocol.control(socket, cmd, td), + Self::Inet(protocol) => protocol.control(socket, cmd, td), + } + } + + fn listen( + &self, + socket: &Arc, + backlog: i32, + td: Option<&VThread>, + ) -> Result<(), Box> { + match self { + Self::Unix(protocol) => protocol.listen(socket, backlog, td), + Self::Inet(protocol) => protocol.listen(socket, backlog, td), + } + } +} + +#[derive(Debug, Error, Errno)] +pub(super) enum AttachError { + #[error("attaching is not supported for this protocol")] + #[errno(EOPNOTSUPP)] + NotSupported, +} + +#[derive(Debug, Error, Errno)] +pub(super) enum BindError { + #[error("binding is not supported for this protocol")] + #[errno(EOPNOTSUPP)] + NotSupported, +} + +#[derive(Debug, Error, Errno)] +pub(super) enum ConnectError { + #[error("connecting is not supported for this protocol")] + #[errno(EOPNOTSUPP)] + NotSupported, +} + +#[derive(Debug, Error, Errno)] +pub(super) enum ControlError { + #[error("controlling is not supported for this protocol")] + #[errno(EOPNOTSUPP)] + NotSupported, +} + +#[derive(Debug, Error, Errno)] +pub(super) enum ListenError { + #[error("listening is not supported for this protocol")] + #[errno(EOPNOTSUPP)] + NotSupported, +} diff --git a/src/kernel/src/net/proto/unix/mod.rs b/src/kernel/src/net/proto/unix/mod.rs new file mode 100644 index 000000000..04d068f1e --- /dev/null +++ b/src/kernel/src/net/proto/unix/mod.rs @@ -0,0 +1,54 @@ +use super::{SockAddr, Socket, SocketBackend}; +use crate::errno::Errno; +use crate::fs::IoCmd; +use crate::process::VThread; +use std::sync::Arc; + +#[derive(Debug)] +pub(super) enum UnixProtocol { + Stream = 1, + Datagram = 2, + SeqPacket = 5, +} + +impl SocketBackend for UnixProtocol { + fn attach(&self, _: &Arc, _: &VThread) -> Result<(), Box> { + todo!() + } + + fn bind( + &self, + _socket: &Arc, + _addr: &SockAddr, + _td: &VThread, + ) -> Result<(), Box> { + todo!() + } + + fn connect( + &self, + _socket: &Arc, + _addr: &SockAddr, + _td: &VThread, + ) -> Result<(), Box> { + todo!() + } + + fn control( + &self, + _: &Arc, + _: IoCmd, + _: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } + + fn listen( + &self, + _socket: &Arc, + _backlog: i32, + _td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } +} diff --git a/src/kernel/src/net/socket.rs b/src/kernel/src/net/socket.rs index 587daf594..a921927c7 100644 --- a/src/kernel/src/net/socket.rs +++ b/src/kernel/src/net/socket.rs @@ -1,14 +1,12 @@ -use super::{GetOptError, SetOptError, SockOpt}; +use super::proto::{Protocol, SocketBackend}; +use super::{GetOptError, SetOptError, SockAddr, SockOpt}; +use crate::errno::{Errno, EPROTONOSUPPORT}; use crate::fs::{ - DefaultFileBackendError, FileBackend, IoCmd, PollEvents, Stat, TruncateLength, Uio, UioMut, - VFile, + DefaultFileBackendError, FileBackend, IoCmd, IoLen, IoVec, IoVecMut, PollEvents, Stat, + TruncateLength, VFile, Vnode, }; +use crate::process::VThread; use crate::ucred::Ucred; -use crate::{ - errno::{Errno, EPIPE}, - process::VThread, -}; -use bitflags::bitflags; use macros::Errno; use std::num::NonZeroI32; use std::sync::Arc; @@ -16,14 +14,12 @@ use thiserror::Error; #[derive(Debug)] pub struct Socket { - ty: i32, // so_type - options: SocketOptions, // so_options - cred: Arc, // so_cred + cred: Arc, // so_cred name: Option>, + backend: Protocol, // so_proto + so_type } impl Socket { - #[allow(unused_variables)] // TODO: remove when implementing /// See `socreate` on the PS4 for a reference. pub fn new( domain: i32, @@ -33,11 +29,22 @@ impl Socket { td: &VThread, name: Option<&str>, ) -> Result, SocketCreateError> { - todo!() - } - - fn options(&self) -> SocketOptions { - self.options + // TODO: implement prison_check_af + let backend = + Protocol::find(domain, ty, proto).ok_or(SocketCreateError::NoProtocolFound)?; + + let socket = Arc::new(Self { + cred: Arc::clone(cred), + name: name.map(|s| s.into()), + backend, + }); + + socket + .backend + .attach(&socket, td) + .map_err(SocketCreateError::AttachError)?; + + Ok(socket) } /// See `sosetopt` on the PS4 for a reference. @@ -53,87 +60,142 @@ impl Socket { } /// See `sosend` on the PS4 for a reference. - #[allow(unused_variables)] // TODO: remove when implementing - fn send(&self, buf: &mut Uio, td: Option<&VThread>) -> Result { + #[allow(unused)] // TODO: remove when used + fn send(&self, buf: &[IoVec], td: Option<&VThread>) -> Result { todo!() } /// See `soreceive` on the PS4 for a reference. - #[allow(unused_variables)] // TODO: remove when implementing - fn receive(&self, buf: &mut UioMut, td: Option<&VThread>) -> Result { + #[allow(unused)] // TODO: remove when used + fn receive(&self, buf: &mut [IoVecMut], td: Option<&VThread>) -> Result { todo!() } + + /// See `sobind` on the PS4 for a reference. + pub fn bind(self: &Arc, addr: &SockAddr, td: &VThread) -> Result<(), Box> { + self.backend.bind(self, addr, td)?; + + Ok(()) + } + + /// See `soconnect` on the PS4 for a reference. + #[allow(unused)] // TODO: remove when used + pub fn connect(self: &Arc, addr: &SockAddr, td: &VThread) -> Result<(), Box> { + self.backend.connect(self, addr, td)?; + + Ok(()) + } + + /// See `solisten` on the PS4 for a reference. + pub fn listen(self: &Arc, backlog: i32, td: Option<&VThread>) -> Result<(), ListenError> { + self.backend.listen(self, backlog, td)?; + + Ok(()) + } } -bitflags! { - #[derive(Debug, Clone, Copy)] - struct SocketOptions: i16 { - const NOSIGPIPE = 0x0800; +/// Implementation of [`FileBackend`] for [`Socket`]. +#[derive(Debug)] +pub struct SocketFileBackend(Arc); + +impl SocketFileBackend { + pub fn new(sock: Arc) -> Box { + Box::new(Self(sock)) + } + + pub fn as_sock(&self) -> &Arc { + &self.0 } } -impl FileBackend for Socket { +impl FileBackend for SocketFileBackend { + fn is_seekable(&self) -> bool { + todo!() + } + #[allow(unused_variables)] // TODO: remove when implementing /// See soo_read on the PS4 for a reference. fn read( - self: &Arc, + &self, _: &VFile, - buf: &mut UioMut, + off: u64, + buf: &mut [IoVecMut], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { todo!() } #[allow(unused_variables)] // TODO: remove when implementing /// See soo_write on the PS4 for a reference. fn write( - self: &Arc, + &self, _: &VFile, - buf: &mut Uio, + off: u64, + buf: &[IoVec], td: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { todo!() } #[allow(unused_variables)] // TODO: remove when implementing - fn ioctl( - self: &Arc, - file: &VFile, - cmd: IoCmd, - td: Option<&VThread>, - ) -> Result<(), Box> { - todo!() + fn ioctl(&self, file: &VFile, cmd: IoCmd, td: Option<&VThread>) -> Result<(), Box> { + match cmd { + IoCmd::FIONBIO(_) => todo!("socket ioctl with FIONBIO"), + IoCmd::FIOASYNC(_) => todo!("socket ioctl with FIOASYNC"), + IoCmd::FIONREAD(_) => todo!("socket ioctl with FIONREAD"), + IoCmd::FIONWRITE(_) => todo!("socket ioctl with FIONWRITE"), + IoCmd::FIONSPACE(_) => todo!("socket ioctl with FIONSPACE"), + IoCmd::FIOSETOWN(_) => todo!("socket ioctl with FIOSETOWN"), + IoCmd::FIOGETOWN(_) => todo!("socket ioctl with FIOGETOWN"), + IoCmd::SIOCSPGRP(_) => todo!("socket ioctl with SIOCSPGRP"), + IoCmd::SIOCGPGRP(_) => todo!("socket ioctl with SIOCGPGRP"), + IoCmd::SIOCATMARK(_) => todo!("socket ioctl with SIOCATMARK"), + _ => self.0.backend.control(&self.0, cmd, td), + } } #[allow(unused_variables)] // TODO: remove when implementing - fn poll(self: &Arc, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents { + fn poll(&self, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents { todo!() } #[allow(unused_variables)] // TODO: remove when implementing - fn stat(self: &Arc, file: &VFile, td: Option<&VThread>) -> Result> { + fn stat(&self, file: &VFile, td: Option<&VThread>) -> Result> { todo!() } fn truncate( - self: &Arc, + &self, _: &VFile, _: TruncateLength, _: Option<&VThread>, ) -> Result<(), Box> { Err(Box::new(DefaultFileBackendError::InvalidValue)) } + + fn vnode(&self) -> Option<&Arc> { + None + } } #[derive(Debug, Error, Errno)] -pub enum SocketCreateError {} +pub enum SocketCreateError { + #[error("no protocol found")] + #[errno(EPROTONOSUPPORT)] + NoProtocolFound, + + #[error("couldn't attach socket")] + AttachError(#[source] Box), +} #[derive(Debug, Error, Errno)] enum ReceiveError {} #[derive(Debug, Error, Errno)] -enum SendError { - #[error("Broken pipe")] - #[errno(EPIPE)] - BrokenPipe, +enum SendError {} + +#[derive(Debug, Error, Errno)] +pub enum ListenError { + #[error("listen failed")] + ListenFailed(#[from] Box), } diff --git a/src/kernel/src/osem/mod.rs b/src/kernel/src/osem/mod.rs index 9ec370f39..1b485754b 100644 --- a/src/kernel/src/osem/mod.rs +++ b/src/kernel/src/osem/mod.rs @@ -35,8 +35,7 @@ impl OsemManager { let mut objects = td.proc().objects_mut(); - let id = objects - .alloc_infallible(|_| Entry::new(Some(name.to_owned()), Osem::new(flags), 0x120)); + let id = objects.alloc(Entry::new(Some(name.to_owned()), Osem::new(flags), 0x120)); Ok(id.into()) } diff --git a/src/kernel/src/process/filedesc.rs b/src/kernel/src/process/filedesc.rs index d57675529..ee002fbec 100644 --- a/src/kernel/src/process/filedesc.rs +++ b/src/kernel/src/process/filedesc.rs @@ -1,6 +1,6 @@ use crate::budget::BudgetType; use crate::errno::{Errno, EBADF}; -use crate::fs::{VFile, VFileFlags, VFileType, Vnode}; +use crate::fs::{VFile, VFileFlags, Vnode}; use crate::kqueue::KernelQueue; use gmtx::{Gutex, GutexGroup}; use macros::Errno; @@ -55,20 +55,40 @@ impl FileDesc { #[allow(unused_variables)] // TODO: remove when implementing; add budget argument pub fn alloc_with_budget( &self, - constructor: impl FnOnce(i32) -> Result, - flags: VFileFlags, - budget: BudgetType, + constructor: impl FnOnce(i32) -> Result, + _budget: BudgetType, ) -> Result> { - todo!() + // TODO: check budget + + self.alloc_without_budget(constructor) } #[allow(unused_variables)] // TODO: remove when implementing; pub fn alloc_without_budget( &self, - constructor: impl FnOnce(i32) -> Result, - flags: VFileFlags, + constructor: impl FnOnce(i32) -> Result, ) -> Result> { - todo!() + // TODO: Implement fdalloc. + let mut files = self.files.write(); + + for i in 0..=(i32::MAX) as usize { + if i == files.len() { + let file = constructor(i as i32).map_err(FileAllocError::Inner)?; + + files.push(Some(Arc::new(file))); + } else if files[i].is_none() { + let file = constructor(i as i32).map_err(FileAllocError::Inner)?; + + files[i] = Some(Arc::new(file)); + } else { + continue; + } + + return Ok(i as i32); + } + + // This should never happen. + panic!("Too many files has been opened."); } /// See `finstall` on the PS4 for a reference. @@ -92,8 +112,6 @@ impl FileDesc { panic!("Too many files has been opened."); } - // TODO: (maybe) implement capabilities - /// See `fget` on the PS4 for a reference. pub fn get(&self, fd: i32) -> Result, GetFileError> { self.get_internal(fd, VFileFlags::empty()) diff --git a/src/kernel/src/process/group.rs b/src/kernel/src/process/group.rs index d8f9eb854..2488a3922 100644 --- a/src/kernel/src/process/group.rs +++ b/src/kernel/src/process/group.rs @@ -1,17 +1,16 @@ -use super::VSession; +use super::{Pid, VSession}; use gmtx::{Gutex, GutexGroup, GutexReadGuard, GutexWriteGuard}; -use std::num::NonZeroI32; use std::sync::Arc; /// An implementation of `pgrp` struct. #[derive(Debug)] pub struct VProcGroup { - id: NonZeroI32, // pg_id + id: Pid, // pg_id session: Gutex>, // pg_session } impl VProcGroup { - pub fn new(id: NonZeroI32, session: Arc) -> Arc { + pub fn new(id: Pid, session: Arc) -> Arc { let gg = GutexGroup::new(); Arc::new(Self { diff --git a/src/kernel/src/process/mod.rs b/src/kernel/src/process/mod.rs index 2c07a4acb..9c5fef179 100644 --- a/src/kernel/src/process/mod.rs +++ b/src/kernel/src/process/mod.rs @@ -1,10 +1,21 @@ use crate::budget::ProcType; use crate::dev::DmemContainer; -use crate::fs::Vnode; -use crate::syscalls::Syscalls; -use crate::ucred::AuthInfo; +use crate::errno::{EINVAL, ENAMETOOLONG, EPERM, ESRCH}; +use crate::fs::{Fs, Vnode}; +use crate::info; +use crate::rcmgr::RcMgr; +use crate::signal::{ + strsignal, SigChldFlags, Signal, SignalAct, SignalFlags, SIGCHLD, SIGKILL, SIGSTOP, SIG_DFL, + SIG_IGN, +}; +use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; +use crate::ucred::{AuthInfo, Privilege}; use crate::vm::MemoryManagerError; -use std::sync::atomic::AtomicI32; +use bitflags::bitflags; +use std::cmp::min; +use std::ffi::c_char; +use std::num::NonZeroI32; +use std::sync::atomic::{AtomicI32, Ordering}; use std::sync::Arc; use thiserror::Error; @@ -13,6 +24,7 @@ pub use self::binary::*; pub use self::cpuset::*; pub use self::filedesc::*; pub use self::group::*; +pub use self::pid::*; pub use self::proc::*; pub use self::rlimit::*; pub use self::session::*; @@ -24,6 +36,7 @@ mod binary; mod cpuset; mod filedesc; mod group; +mod pid; mod proc; mod rlimit; mod session; @@ -31,11 +44,28 @@ mod signal; mod thread; /// Manage all PS4 processes. -pub struct ProcManager {} +pub struct ProcManager { + fs: Arc, + rc: Arc, +} impl ProcManager { - pub fn new(sys: &mut Syscalls) -> Arc { - let pmgr = Arc::new(Self {}); + pub fn new(fs: &Arc, rc: &Arc, sys: &mut Syscalls) -> Arc { + // Register syscalls. + let pmgr = Arc::new(Self { + fs: fs.clone(), + rc: rc.clone(), + }); + + sys.register(20, &pmgr, Self::sys_getpid); + sys.register(50, &pmgr, Self::sys_setlogin); + sys.register(147, &pmgr, Self::sys_setsid); + sys.register(416, &pmgr, Self::sys_sigaction); + sys.register(432, &pmgr, Self::sys_thr_self); + sys.register(464, &pmgr, Self::sys_thr_set_name); + sys.register(585, &pmgr, Self::sys_is_in_sandbox); + sys.register(602, &pmgr, Self::sys_randomized_path); + sys.register(612, &pmgr, Self::sys_get_proc_type_info); pmgr } @@ -49,9 +79,10 @@ impl ProcManager { dmem_container: DmemContainer, root: Arc, system_path: impl Into, - mut sys: Syscalls, + sys: Syscalls, ) -> Result, SpawnError> { VProc::new( + Self::new_id().into(), auth, budget_id, budget_ptype, @@ -61,6 +92,353 @@ impl ProcManager { sys, ) } + + fn sys_getpid(self: &Arc, td: &VThread, _: &SysIn) -> Result { + Ok(td.proc().id().into()) + } + + fn sys_setlogin(self: &Arc, td: &VThread, i: &SysIn) -> Result { + // Check current thread privilege. + td.priv_check(Privilege::PROC_SETLOGIN)?; + + // Get login name. + let login = unsafe { i.args[0].to_str(17) } + .map_err(|e| { + if e.errno() == ENAMETOOLONG { + SysErr::Raw(EINVAL) + } else { + e + } + })? + .unwrap(); + + // Set login name. + let mut group = td.proc().group_mut(); + let session = group.as_mut().unwrap().session_mut(); + + session.set_login(login); + + info!("Login name was changed to '{login}'."); + + Ok(SysOut::ZERO) + } + + fn sys_setsid(self: &Arc, td: &VThread, _: &SysIn) -> Result { + // Check if current thread has privilege. + td.priv_check(Privilege::SCE680)?; + + // Check if the process already become a group leader. + let mut group = td.proc().group_mut(); + + if group.is_some() { + return Err(SysErr::Raw(EPERM)); + } + + // TODO: Find out the correct login name for VSession. + let pid = td.proc().id(); + let session = VSession::new(pid, String::from("root")); + + *group = Some(VProcGroup::new(pid, session)); + drop(group); + + info!("Virtual process now set as group leader."); + + Ok(pid.into()) + } + + fn sys_sigaction(self: &Arc, td: &VThread, i: &SysIn) -> Result { + // Get arguments. + let sig = { + let sig: i32 = i.args[0].try_into().unwrap(); + Signal::new(sig).ok_or(SysErr::Raw(EINVAL))? + }; + let act: *const SignalAct = i.args[1].into(); + let oact: *mut SignalAct = i.args[2].into(); + + // Save the old actions. + let proc = td.proc(); + let mut acts = proc.sigacts_mut(); + + if !oact.is_null() { + let handler = acts.handler(sig); + let flags = acts.signal_flags(sig); + let mask = acts.catchmask(sig); + let old_act = SignalAct { + handler, + flags, + mask, + }; + + unsafe { + *oact = old_act; + } + } + + if act.is_null() { + return Ok(SysOut::ZERO); + } + + // Set new actions. + let handler = unsafe { (*act).handler }; + let flags = unsafe { (*act).flags }; + let mut mask = unsafe { (*act).mask }; + + info!( + "Setting {} handler to {:#x} with flags = {} and mask = {}.", + strsignal(sig), + handler, + flags, + mask + ); + + if (sig == SIGKILL || sig == SIGSTOP) && handler != 0 { + return Err(SysErr::Raw(EINVAL)); + } + + mask.remove(SIGKILL); + mask.remove(SIGSTOP); + acts.set_catchmask(sig, mask); + acts.set_handler(sig, handler); + + if flags.intersects(SignalFlags::SA_SIGINFO) { + acts.set_modern(sig); + + if flags.intersects(SignalFlags::SA_RESTART) { + todo!("sys_sigaction with act.flags & 0x2 != 0"); + } else { + acts.set_interupt(sig); + } + + if flags.intersects(SignalFlags::SA_ONSTACK) { + todo!("sys_sigaction with act.flags & 0x1 != 0"); + } else { + acts.remove_stack(sig); + } + + if flags.intersects(SignalFlags::SA_RESETHAND) { + todo!("sys_sigaction with act.flags & 0x4 != 0"); + } else { + acts.remove_reset(sig); + } + + if flags.intersects(SignalFlags::SA_NODEFER) { + todo!("sys_sigaction with act.flags & 0x10 != 0"); + } else { + acts.remove_nodefer(sig); + } + } else { + todo!("sys_sigaction with act.flags & 0x40 = 0"); + } + + if sig == SIGCHLD { + let mut flag = acts.flag(); + + if flags.intersects(SignalFlags::SA_NOCLDSTOP) { + flag |= SigChldFlags::PS_NOCLDSTOP; + } else { + flag &= !SigChldFlags::PS_NOCLDSTOP; + } + + if !flags.intersects(SignalFlags::SA_NOCLDWAIT) || proc.id() == 1 { + flag &= !SigChldFlags::PS_NOCLDWAIT; + } else { + flag |= SigChldFlags::PS_NOCLDWAIT; + } + + if acts.handler(sig) == SIG_IGN { + flag |= SigChldFlags::PS_CLDSIGIGN; + } else { + flag &= !SigChldFlags::PS_CLDSIGIGN; + } + + acts.set_flag(flag); + } + + // TODO: Refactor this for readability. + if acts.handler(sig) == SIG_IGN + || (sig.get() < 32 + && ((0x184c8000u32 >> sig.get()) & 1) != 0 + && acts.handler(sig) == SIG_DFL) + { + todo!("sys_sigaction with SIG_IGN"); + } else { + acts.remove_ignore(sig); + + if acts.handler(sig) == SIG_DFL { + acts.remove_catch(sig); + } else { + acts.set_catch(sig); + } + } + + Ok(SysOut::ZERO) + } + + fn sys_thr_self(self: &Arc, td: &VThread, i: &SysIn) -> Result { + let id: *mut i64 = i.args[0].into(); + + unsafe { *id = td.id().get().into() }; + + Ok(SysOut::ZERO) + } + + fn sys_thr_set_name(self: &Arc, td: &VThread, i: &SysIn) -> Result { + let tid: i64 = i.args[0].into(); + let name: Option<&str> = unsafe { i.args[1].to_str(32) }?; + let proc = td.proc(); + + if tid == -1 { + info!("Setting process name to '{}'.", name.unwrap_or("NULL")); + + proc.set_name(name); + } else { + let threads = proc.threads(); + let thr = threads + .iter() + .find(|t| t.id().get() == tid as i32) + .ok_or(SysErr::Raw(ESRCH))?; + + info!( + "Setting name of thread {} to '{}'.", + thr.id(), + name.unwrap_or("NULL") + ); + + thr.set_name(name); + } + + Ok(SysOut::ZERO) + } + + fn sys_is_in_sandbox(self: &Arc, td: &VThread, _: &SysIn) -> Result { + let v = !Arc::ptr_eq(&td.proc().files().root(), &self.fs.root()); + + Ok(v.into()) + } + + fn sys_randomized_path(self: &Arc, td: &VThread, i: &SysIn) -> Result { + let set = i.args[0]; + let get: *mut c_char = i.args[1].into(); + let len: *mut usize = i.args[2].into(); + let proc = td.proc(); + + // Get the value. + let len = if get.is_null() || len.is_null() { + 0 + } else { + let v = unsafe { *len }; + unsafe { *len = proc.system_path().len() }; + v + }; + + if len > 0 && !proc.system_path().is_empty() { + let len = min(len - 1, proc.system_path().len()); + + unsafe { get.copy_from_nonoverlapping(proc.system_path().as_ptr().cast(), len) }; + unsafe { *get.add(len) = 0 }; + } + + // Set the value. + if set != 0 { + todo!("sys_randomized_path with non-null set"); + } + + Ok(SysOut::ZERO) + } + + fn sys_get_proc_type_info(self: &Arc, td: &VThread, i: &SysIn) -> Result { + // Check buffer size. + let info: *mut ProcTypeInfo = i.args[0].into(); + + if unsafe { (*info).nbuf != 16 } { + return Err(SysErr::Raw(EINVAL)); + } + + // Set output size and process type. + unsafe { (*info).len = (*info).nbuf }; + unsafe { (*info).ptype = td.proc().budget_ptype() }; + + // Set flags. + let cred = td.proc().cred(); + let mut flags = ProcTypeInfoFlags::empty(); + + flags.set( + ProcTypeInfoFlags::IS_JIT_COMPILER_PROCESS, + cred.is_jit_compiler_process(), + ); + + flags.set( + ProcTypeInfoFlags::IS_JIT_APPLICATION_PROCESS, + cred.is_jit_application_process(), + ); + + flags.set( + ProcTypeInfoFlags::IS_VIDEOPLAYER_PROCESS, + cred.is_videoplayer_process(), + ); + + flags.set( + ProcTypeInfoFlags::IS_DISKPLAYERUI_PROCESS, + cred.is_diskplayerui_process(), + ); + + flags.set( + ProcTypeInfoFlags::HAS_USE_VIDEO_SERVICE_CAPABILITY, + cred.has_use_video_service_capability(), + ); + + flags.set( + ProcTypeInfoFlags::IS_WEBCORE_PROCESS, + cred.is_webcore_process(), + ); + + flags.set( + ProcTypeInfoFlags::HAS_SCE_PROGRAM_ATTRIBUTE, + cred.has_sce_program_attribute(), + ); + + flags.set( + ProcTypeInfoFlags::IS_DEBUGGABLE_PROCESS, + cred.is_debuggable_process(&self.rc), + ); + + unsafe { (*info).flags = flags }; + + Ok(SysOut::ZERO) + } + + fn new_id() -> NonZeroI32 { + let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); + + // Just in case if the user manage to spawn 2,147,483,647 threads in a single run so we + // don't encountered a weird bug. + assert!(id > 0); + + NonZeroI32::new(id).unwrap() + } +} + +/// Output of [`ProcManager::sys_get_proc_type_info()`]. +#[repr(C)] +struct ProcTypeInfo { + nbuf: usize, + len: usize, + ptype: ProcType, + flags: ProcTypeInfoFlags, +} + +bitflags! { + #[repr(transparent)] + struct ProcTypeInfoFlags: u32 { + const IS_JIT_COMPILER_PROCESS = 0x1; + const IS_JIT_APPLICATION_PROCESS = 0x2; + const IS_VIDEOPLAYER_PROCESS = 0x4; + const IS_DISKPLAYERUI_PROCESS = 0x8; + const HAS_USE_VIDEO_SERVICE_CAPABILITY = 0x10; + const IS_WEBCORE_PROCESS = 0x20; + const HAS_SCE_PROGRAM_ATTRIBUTE = 0x40; + const IS_DEBUGGABLE_PROCESS = 0x80; + } } /// Represents an error when [`ProcManager::spawn()`] was failed. diff --git a/src/kernel/src/process/pid.rs b/src/kernel/src/process/pid.rs new file mode 100644 index 000000000..06164fc3b --- /dev/null +++ b/src/kernel/src/process/pid.rs @@ -0,0 +1,34 @@ +use crate::syscalls::SysOut; +use std::num::NonZeroI32; + +/// Unique identifier of a process. +/// +/// Unlike FreeBSD, our implementation does not allow zero value for PID. That mean we don't have +/// `proc0` to represent the kernel itself. +#[repr(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Pid(i32); + +impl From for Pid { + fn from(value: NonZeroI32) -> Self { + Self(value.get()) + } +} + +impl PartialEq for Pid { + fn eq(&self, other: &i32) -> bool { + self.0 == *other + } +} + +impl PartialEq for i32 { + fn eq(&self, other: &Pid) -> bool { + *self == other.0 + } +} + +impl From for SysOut { + fn from(value: Pid) -> Self { + value.0.into() + } +} diff --git a/src/kernel/src/process/proc.rs b/src/kernel/src/process/proc.rs index 61f501734..6e7169822 100644 --- a/src/kernel/src/process/proc.rs +++ b/src/kernel/src/process/proc.rs @@ -1,35 +1,22 @@ use super::{ - AppInfo, Binaries, CpuLevel, CpuWhich, FileDesc, Limits, ResourceLimit, ResourceType, - SignalActs, SpawnError, VProcGroup, VSession, VThread, NEXT_ID, + AppInfo, Binaries, CpuLevel, CpuWhich, FileDesc, Limits, Pid, ResourceLimit, ResourceType, + SignalActs, SpawnError, VProcGroup, VThread, }; use crate::budget::ProcType; use crate::dev::DmemContainer; -use crate::errno::Errno; -use crate::errno::{EINVAL, ENAMETOOLONG, EPERM, ERANGE, ESRCH}; +use crate::errno::{Errno, EINVAL, ERANGE, ESRCH}; use crate::fs::Vnode; use crate::idt::Idt; -use crate::info; -use crate::signal::{ - strsignal, SignalAct, SignalFlags, SignalSet, SIGCHLD, SIGKILL, SIGSTOP, SIG_BLOCK, SIG_DFL, - SIG_IGN, SIG_SETMASK, SIG_UNBLOCK, -}; -use crate::signal::{SigChldFlags, Signal}; use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; use crate::sysent::ProcAbi; use crate::ucred::{AuthInfo, Gid, Privilege, Ucred, Uid}; use crate::vm::Vm; -use bitflags::bitflags; use gmtx::{Gutex, GutexGroup, GutexReadGuard, GutexWriteGuard}; use macros::Errno; use std::any::Any; -use std::cmp::min; -use std::ffi::c_char; -use std::mem::size_of; -use std::mem::zeroed; -use std::num::NonZeroI32; -use std::ptr::null; -use std::ptr::null_mut; -use std::sync::atomic::{AtomicPtr, Ordering}; +use std::mem::{size_of, zeroed}; +use std::ptr::{null, null_mut}; +use std::sync::atomic::AtomicPtr; use std::sync::{Arc, OnceLock}; use thiserror::Error; @@ -39,7 +26,7 @@ use thiserror::Error; /// once we have migrated the PS4 code to run inside a virtual machine. #[derive(Debug)] pub struct VProc { - id: NonZeroI32, // p_pid + id: Pid, // p_pid threads: Gutex>>, // p_threads cred: Arc, // p_ucred group: Gutex>>, // p_pgrp @@ -62,6 +49,7 @@ pub struct VProc { impl VProc { pub(super) fn new( + id: Pid, auth: AuthInfo, budget_id: usize, budget_ptype: ProcType, @@ -82,7 +70,7 @@ impl VProc { let limits = Limits::load()?; let vp = Arc::new(Self { - id: Self::new_id(), + id, threads: gg.spawn(Vec::new()), cred: Arc::new(cred), group: gg.spawn(None), @@ -104,31 +92,25 @@ impl VProc { }); // TODO: Move all syscalls here to somewhere else. - sys.register(20, &vp, Self::sys_getpid); - sys.register(50, &vp, Self::sys_setlogin); - sys.register(147, &vp, Self::sys_setsid); - sys.register(340, &vp, Self::sys_sigprocmask); - sys.register(416, &vp, Self::sys_sigaction); - sys.register(432, &vp, Self::sys_thr_self); sys.register(455, &vp, Self::sys_thr_new); - sys.register(464, &vp, Self::sys_thr_set_name); sys.register(466, &vp, Self::sys_rtprio_thread); sys.register(487, &vp, Self::sys_cpuset_getaffinity); sys.register(488, &vp, Self::sys_cpuset_setaffinity); - sys.register(585, &vp, Self::sys_is_in_sandbox); sys.register(587, &vp, Self::sys_get_authinfo); - sys.register(602, &vp, Self::sys_randomized_path); - sys.register(612, &vp, Self::sys_get_proc_type_info); vp.abi.set(ProcAbi::new(sys)).unwrap(); Ok(vp) } - pub fn id(&self) -> NonZeroI32 { + pub fn id(&self) -> Pid { self.id } + pub fn threads(&self) -> GutexReadGuard>> { + self.threads.read() + } + pub fn threads_mut(&self) -> GutexWriteGuard>> { self.threads.write() } @@ -137,6 +119,10 @@ impl VProc { &self.cred } + pub fn group_mut(&self) -> GutexWriteGuard>> { + self.group.write() + } + pub fn abi(&self) -> &ProcAbi { self.abi.get().unwrap() } @@ -145,10 +131,18 @@ impl VProc { &self.vm } + pub fn sigacts_mut(&self) -> GutexWriteGuard { + self.sigacts.write() + } + pub fn files(&self) -> &Arc { &self.files } + pub fn system_path(&self) -> &str { + &self.system_path + } + pub fn limit(&self, ty: ResourceType) -> &ResourceLimit { &self.limits[ty] } @@ -197,253 +191,6 @@ impl VProc { &self.uptc } - fn sys_getpid(self: &Arc, _: &VThread, _: &SysIn) -> Result { - Ok(self.id.into()) - } - - fn sys_setlogin(self: &Arc, td: &VThread, i: &SysIn) -> Result { - // Check current thread privilege. - td.priv_check(Privilege::PROC_SETLOGIN)?; - - // Get login name. - let login = unsafe { i.args[0].to_str(17) } - .map_err(|e| { - if e.errno() == ENAMETOOLONG { - SysErr::Raw(EINVAL) - } else { - e - } - })? - .unwrap(); - - // Set login name. - let mut group = self.group.write(); - let session = group.as_mut().unwrap().session_mut(); - - session.set_login(login); - - info!("Login name was changed to '{login}'."); - - Ok(SysOut::ZERO) - } - - fn sys_setsid(self: &Arc, td: &VThread, _: &SysIn) -> Result { - // Check if current thread has privilege. - td.priv_check(Privilege::SCE680)?; - - // Check if the process already become a group leader. - let mut group = self.group.write(); - - if group.is_some() { - return Err(SysErr::Raw(EPERM)); - } - - // TODO: Find out the correct login name for VSession. - let session = VSession::new(self.id, String::from("root")); - - *group = Some(VProcGroup::new(self.id, session)); - info!("Virtual process now set as group leader."); - - Ok(self.id.into()) - } - - fn sys_sigprocmask(self: &Arc, td: &VThread, i: &SysIn) -> Result { - // Get arguments. - let how: How = { - let how: i32 = i.args[0].try_into().unwrap(); - how.try_into()? - }; - - let set: *const SignalSet = i.args[1].into(); - let oset: *mut SignalSet = i.args[2].into(); - - // Convert set to an option. - let set = if set.is_null() { - None - } else { - Some(unsafe { *set }) - }; - - // Keep the current mask for copying to the oset. We need to copy to the oset only when this - // function succees. - let mut mask = td.sigmask_mut(); - let prev = *mask; - - // Update the mask. - if let Some(mut set) = set { - match how { - How::Block => { - // Remove uncatchable signals. - set.remove(SIGKILL); - set.remove(SIGSTOP); - - // Update mask. - *mask |= set; - } - How::Unblock => { - // Update mask. - *mask &= !set; - - // TODO: Invoke signotify at the end. - } - How::SetMask => { - // Remove uncatchable signals. - set.remove(SIGKILL); - set.remove(SIGSTOP); - - // Replace mask. - *mask = set; - - // TODO: Invoke signotify at the end. - } - } - - // TODO: Check if we need to invoke reschedule_signals. - } - - // Copy output. - if !oset.is_null() { - unsafe { *oset = prev }; - } - - Ok(SysOut::ZERO) - } - - fn sys_sigaction(self: &Arc, _: &VThread, i: &SysIn) -> Result { - // Get arguments. - let sig = { - let sig: i32 = i.args[0].try_into().unwrap(); - Signal::new(sig).ok_or(SysErr::Raw(EINVAL))? - }; - let act: *const SignalAct = i.args[1].into(); - let oact: *mut SignalAct = i.args[2].into(); - - // Save the old actions. - let mut acts = self.sigacts.write(); - - if !oact.is_null() { - let handler = acts.handler(sig); - let flags = acts.signal_flags(sig); - let mask = acts.catchmask(sig); - let old_act = SignalAct { - handler: handler, - flags: flags, - mask: mask, - }; - - unsafe { - *oact = old_act; - } - } - - if act.is_null() { - return Ok(SysOut::ZERO); - } - - // Set new actions. - let handler = unsafe { (*act).handler }; - let flags = unsafe { (*act).flags }; - let mut mask = unsafe { (*act).mask }; - - info!( - "Setting {} handler to {:#x} with flags = {} and mask = {}.", - strsignal(sig), - handler, - flags, - mask - ); - - if (sig == SIGKILL || sig == SIGSTOP) && handler != 0 { - return Err(SysErr::Raw(EINVAL)); - } - - mask.remove(SIGKILL); - mask.remove(SIGSTOP); - acts.set_catchmask(sig, mask); - acts.set_handler(sig, handler); - - if flags.intersects(SignalFlags::SA_SIGINFO) { - acts.set_modern(sig); - - if flags.intersects(SignalFlags::SA_RESTART) { - todo!("sys_sigaction with act.flags & 0x2 != 0"); - } else { - acts.set_interupt(sig); - } - - if flags.intersects(SignalFlags::SA_ONSTACK) { - todo!("sys_sigaction with act.flags & 0x1 != 0"); - } else { - acts.remove_stack(sig); - } - - if flags.intersects(SignalFlags::SA_RESETHAND) { - todo!("sys_sigaction with act.flags & 0x4 != 0"); - } else { - acts.remove_reset(sig); - } - - if flags.intersects(SignalFlags::SA_NODEFER) { - todo!("sys_sigaction with act.flags & 0x10 != 0"); - } else { - acts.remove_nodefer(sig); - } - } else { - todo!("sys_sigaction with act.flags & 0x40 = 0"); - } - - if sig == SIGCHLD { - let mut flag = acts.flag(); - - if flags.intersects(SignalFlags::SA_NOCLDSTOP) { - flag |= SigChldFlags::PS_NOCLDSTOP; - } else { - flag &= !SigChldFlags::PS_NOCLDSTOP; - } - - if !flags.intersects(SignalFlags::SA_NOCLDWAIT) || self.id.get() == 1 { - flag &= !SigChldFlags::PS_NOCLDWAIT; - } else { - flag |= SigChldFlags::PS_NOCLDWAIT; - } - - if acts.handler(sig) == SIG_IGN { - flag |= SigChldFlags::PS_CLDSIGIGN; - } else { - flag &= !SigChldFlags::PS_CLDSIGIGN; - } - - acts.set_flag(flag); - } - - // TODO: Refactor this for readability. - if acts.handler(sig) == SIG_IGN - || (sig.get() < 32 - && ((0x184c8000u32 >> sig.get()) & 1) != 0 - && acts.handler(sig) == SIG_DFL) - { - todo!("sys_sigaction with SIG_IGN"); - } else { - acts.remove_ignore(sig); - - if acts.handler(sig) == SIG_DFL { - acts.remove_catch(sig); - } else { - acts.set_catch(sig); - } - } - - Ok(SysOut::ZERO) - } - - fn sys_thr_self(self: &Arc, td: &VThread, i: &SysIn) -> Result { - let id: *mut i64 = i.args[0].into(); - - unsafe { *id = td.id().get().into() }; - - Ok(SysOut::ZERO) - } - fn sys_thr_new(self: &Arc, td: &VThread, i: &SysIn) -> Result { let param: *const ThrParam = i.args[0].into(); let param_size: i32 = i.args[1].try_into().unwrap(); @@ -500,34 +247,6 @@ impl VProc { todo!() } - fn sys_thr_set_name(self: &Arc, _: &VThread, i: &SysIn) -> Result { - let tid: i64 = i.args[0].into(); - let name: Option<&str> = unsafe { i.args[1].to_str(32) }?; - - if tid == -1 { - info!("Setting process name to '{}'.", name.unwrap_or("NULL")); - - self.set_name(name); - } else { - let threads = self.threads.read(); - - let thr = threads - .iter() - .find(|t| t.id().get() == tid as i32) - .ok_or(SysErr::Raw(ESRCH))?; - - info!( - "Setting name of thread {} to '{}'.", - thr.id(), - name.unwrap_or("NULL") - ); - - thr.set_name(name); - } - - Ok(SysOut::ZERO) - } - fn sys_rtprio_thread(self: &Arc, td: &VThread, i: &SysIn) -> Result { let function: RtpFunction = TryInto::::try_into(i.args[0]).unwrap().try_into()?; let lwpid: i32 = i.args[1].try_into().unwrap(); @@ -648,18 +367,13 @@ impl VProc { } } - fn sys_is_in_sandbox(self: &Arc, _: &VThread, _: &SysIn) -> Result { - // TODO: Implement this once FS rework has been usable. - Ok(1.into()) - } - fn sys_get_authinfo(self: &Arc, td: &VThread, i: &SysIn) -> Result { // Get arguments. let pid: i32 = i.args[0].try_into().unwrap(); let buf: *mut AuthInfo = i.args[1].into(); // Check if PID is our process. - if pid != 0 && pid != self.id.get() { + if pid != 0 && pid != self.id { return Err(SysErr::Raw(ESRCH)); } @@ -689,124 +403,6 @@ impl VProc { Ok(SysOut::ZERO) } - - fn sys_randomized_path(self: &Arc, _: &VThread, i: &SysIn) -> Result { - let set = i.args[0]; - let get: *mut c_char = i.args[1].into(); - let len: *mut usize = i.args[2].into(); - - // Get the value. - let len = if get.is_null() || len.is_null() { - 0 - } else { - let v = unsafe { *len }; - unsafe { *len = self.system_path.len() }; - v - }; - - if len > 0 && !self.system_path.is_empty() { - let len = min(len - 1, self.system_path.len()); - - unsafe { get.copy_from_nonoverlapping(self.system_path.as_ptr().cast(), len) }; - unsafe { *get.add(len) = 0 }; - } - - // Set the value. - if set != 0 { - todo!("sys_randomized_path with non-null set"); - } - - Ok(SysOut::ZERO) - } - - fn sys_get_proc_type_info(self: &Arc, td: &VThread, i: &SysIn) -> Result { - let info = unsafe { &mut *Into::<*mut ProcTypeInfo>::into(i.args[0]) }; - - if info.len != size_of::() { - return Err(SysErr::Raw(EINVAL)); - } - - *info = td.proc().get_proc_type_info(); - - Ok(SysOut::ZERO) - } - - fn get_proc_type_info(&self) -> ProcTypeInfo { - let cred = self.cred(); - - let mut flags = ProcTypeInfoFlags::empty(); - - flags.set( - ProcTypeInfoFlags::IS_JIT_COMPILER_PROCESS, - cred.is_jit_compiler_process(), - ); - flags.set( - ProcTypeInfoFlags::IS_JIT_APPLICATION_PROCESS, - cred.is_jit_application_process(), - ); - flags.set( - ProcTypeInfoFlags::IS_VIDEOPLAYER_PROCESS, - cred.is_videoplayer_process(), - ); - flags.set( - ProcTypeInfoFlags::IS_DISKPLAYERUI_PROCESS, - cred.is_diskplayerui_process(), - ); - flags.set( - ProcTypeInfoFlags::HAS_USE_VIDEO_SERVICE_CAPABILITY, - cred.has_use_video_service_capability(), - ); - flags.set( - ProcTypeInfoFlags::IS_WEBCORE_PROCESS, - cred.is_webcore_process(), - ); - flags.set( - ProcTypeInfoFlags::HAS_SCE_PROGRAM_ATTRIBUTE, - cred.has_sce_program_attribute(), - ); - flags.set( - ProcTypeInfoFlags::IS_DEBUGGABLE_PROCESS, - cred.is_debuggable_process(), - ); - - ProcTypeInfo { - len: size_of::(), - ty: self.budget_ptype.into(), - flags, - } - } - - fn new_id() -> NonZeroI32 { - let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); - - // Just in case if the user manage to spawn 2,147,483,647 threads in a single run so we - // don't encountered a weird bug. - assert!(id > 0); - - NonZeroI32::new(id).unwrap() - } -} - -#[derive(Debug)] -enum How { - Block, - Unblock, - SetMask, -} - -impl TryFrom for How { - type Error = SysErr; - - fn try_from(value: i32) -> Result { - let how = match value { - SIG_BLOCK => How::Block, - SIG_UNBLOCK => How::Unblock, - SIG_SETMASK => How::SetMask, - _ => return Err(SysErr::Raw(EINVAL)), - }; - - Ok(how) - } } #[repr(C)] @@ -856,27 +452,5 @@ struct RtPrio { prio: u16, } -/// Outout of sys_get_proc_type_info. -#[repr(C)] -struct ProcTypeInfo { - len: usize, - ty: u32, - flags: ProcTypeInfoFlags, -} - -bitflags! { - #[repr(transparent)] - struct ProcTypeInfoFlags: u32 { - const IS_JIT_COMPILER_PROCESS = 0x1; - const IS_JIT_APPLICATION_PROCESS = 0x2; - const IS_VIDEOPLAYER_PROCESS = 0x4; - const IS_DISKPLAYERUI_PROCESS = 0x8; - const HAS_USE_VIDEO_SERVICE_CAPABILITY = 0x10; - const IS_WEBCORE_PROCESS = 0x20; - const HAS_SCE_PROGRAM_ATTRIBUTE = 0x40; - const IS_DEBUGGABLE_PROCESS = 0x80; - } -} - #[derive(Debug, Error, Errno)] pub enum CreateThreadError {} diff --git a/src/kernel/src/process/session.rs b/src/kernel/src/process/session.rs index 810fa7ab0..0f3cdeb1b 100644 --- a/src/kernel/src/process/session.rs +++ b/src/kernel/src/process/session.rs @@ -1,17 +1,16 @@ -use std::num::NonZeroI32; -use std::sync::Arc; - +use super::Pid; use gmtx::{Gutex, GutexGroup}; +use std::sync::Arc; /// An implementation of `session` structure. #[derive(Debug)] pub struct VSession { - id: NonZeroI32, // s_sid + id: Pid, // s_sid login: Gutex, // s_login } impl VSession { - pub fn new(id: NonZeroI32, login: String) -> Arc { + pub fn new(id: Pid, login: String) -> Arc { let gg = GutexGroup::new(); Arc::new(Self { diff --git a/src/kernel/src/rcmgr/mod.rs b/src/kernel/src/rcmgr/mod.rs new file mode 100644 index 000000000..efcc4985f --- /dev/null +++ b/src/kernel/src/rcmgr/mod.rs @@ -0,0 +1,34 @@ +use std::sync::Arc; + +/// Implementation of RcMgr kernel services. +/// +/// Not sure what the meaning of "Rc". +pub struct RcMgr {} + +impl RcMgr { + pub fn new() -> Arc { + Arc::new(Self {}) + } + + /// See `sceSblRcMgrIsAllowULDebugger` on the PS4 for a reference. + pub fn is_allow_ul_debugger(&self) -> bool { + if !self.is_qa_enabled() { + return false; + } + + todo!() + } + + /// See `sceSblRcMgrIsSoftwagnerQafForAcmgr` on the PS4 for a reference, + pub fn is_softwagner_qaf_for_acmgr(&self) -> bool { + if !self.is_qa_enabled() { + return false; + } + + todo!() + } + + fn is_qa_enabled(&self) -> bool { + false + } +} diff --git a/src/kernel/src/regmgr/key.rs b/src/kernel/src/regmgr/key.rs index ed88c2a96..d1d0c5c78 100644 --- a/src/kernel/src/regmgr/key.rs +++ b/src/kernel/src/regmgr/key.rs @@ -5,7 +5,6 @@ use std::fmt::{Display, Formatter}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct RegKey(u32); -#[allow(dead_code)] // Removes complaints about unused Regkeys. impl RegKey { pub const REGISTRY_VERSION: Self = Self(0x01010000); pub const REGISTRY_INSTALL: Self = Self(0x01020000); @@ -49,10 +48,12 @@ impl RegKey { pub const BROWSER_DEBUG_NOTIFICATION: Self = Self(0x3CC80700); pub const MORPHEUS_DEBUG_VR_CAPTURE: Self = Self(0x58800C00); pub const DEVENV_TOOL_BOOT_PARAM: Self = Self(0x78020300); + pub const DEVENV_TOOL_PRELOAD_CHK_OFF: Self = Self(0x78020500); pub const DEVENV_TOOL_TRC_NOTIFY: Self = Self(0x78026400); pub const DEVENV_TOOL_USE_DEFAULT_LIB: Self = Self(0x78028300); pub const DEVENV_TOOL_SYS_PRX_PRELOAD: Self = Self(0x78028A00); pub const DEVENV_TOOL_GAME_INTMEM_DBG: Self = Self(0x7802BF00); + pub const DEVENV_TOOL_SCE_MODULE_DBG: Self = Self(0x7802C000); pub(super) const fn new(v: u32) -> Self { Self(v) @@ -65,96 +66,60 @@ impl RegKey { impl Display for RegKey { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match *self { - Self::REGISTRY_VERSION => f.write_str("SCE_REGMGR_ENT_KEY_REGISTRY_version"), - Self::REGISTRY_INSTALL => f.write_str("SCE_REGMGR_ENT_KEY_REGISTRY_install"), - Self::REGISTRY_UPDATE => f.write_str("SCE_REGMGR_ENT_KEY_REGISTRY_update"), - Self::REGISTRY_NOT_SAVE => f.write_str("SCE_REGMGR_ENT_KEY_REGISTRY_not_save"), - Self::REGISTRY_RECOVER => f.write_str("SCE_REGMGR_ENT_KEY_REGISTRY_recover"), - Self::REGISTRY_DOWNGRADE => f.write_str("SCE_REGMGR_ENT_KEY_REGISTRY_downgrade"), - Self::REGISTRY_BOOTCOUNT => f.write_str("SCE_REGMGR_ENT_KEY_REGISTRY_bootcount"), - Self::REGISTRY_LASTVER => f.write_str("SCE_REGMGR_ENT_KEY_REGISTRY_lastver"), - Self::REGISTRY_INIT_FLAG => f.write_str("SCE_REGMGR_ENT_KEY_REGISTRY_init_flag"), - Self::SYSTEM_UPDATE_MODE => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_update_mode"), - Self::SYSTEM_LANGUAGE => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_language"), - Self::SYSTEM_INITIALIZE => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_initialize"), - Self::SYSTEM_NICKNAME => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_nickname"), - Self::SYSTEM_DIMMER_INTERVAL => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_dimmer_interval") - } - Self::SYSTEM_EAPFUNCTION => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_eapfunction"), - Self::SYSTEM_ENABLE_VOICERCG => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_enable_voicercg") - } - Self::SYSTEM_SOFT_VERSION => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_soft_version"), - Self::SYSTEM_PROFILECH_VER => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_profilech_ver"), - Self::SYSTEM_BUTTON_ASSIGN => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_button_assign"), - Self::SYSTEM_BACKUP_MODE => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_backup_mode"), - Self::SYSTEM_PON_MEMORY_TEST => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_pon_memory_test") - } - Self::SYSTEM_GAME_REC_MODE => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_game_rec_mode"), - Self::SYSTEM_SHELL_FUNCTION => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_shell_function"), - Self::SYSTEM_PAD_CONNECTION => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_pad_connection"), - Self::SYSTEM_DATA_TRANSFER => f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_data_transfer"), - Self::SYSTEM_BASE_MODE_CLKUP => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_base_mode_clkup") - } - Self::SYSTEM_NEO_VDDNB_VID_OFFSET => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_neo_vddnb_vid_offset") - } - Self::SYSTEM_TESTBUTTON_MODE => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_testbutton_mode") - } - Self::SYSTEM_TESTBUTTON_PARAM => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_testbutton_param") - } - Self::SYSTEM_POWER_SHUTDOWN_STATUS => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_POWER_shutdown_status") - } - Self::SYSTEM_SPECIFIC_IDU_MODE => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_SPECIFIC_idu_mode") - } - Self::SYSTEM_SPECIFIC_SHOW_MODE => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_SPECIFIC_show_mode") - } - Self::SYSTEM_SPECIFIC_ARCADE_MODE => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_SPECIFIC_arcade_mode") - } - Self::SYSTEM_LIBC_INTMEM_PEAK_SIZE => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_LIBC_intmem_peak_size") - } + let name = match *self { + Self::REGISTRY_VERSION => "SCE_REGMGR_ENT_KEY_REGISTRY_version", + Self::REGISTRY_INSTALL => "SCE_REGMGR_ENT_KEY_REGISTRY_install", + Self::REGISTRY_UPDATE => "SCE_REGMGR_ENT_KEY_REGISTRY_update", + Self::REGISTRY_NOT_SAVE => "SCE_REGMGR_ENT_KEY_REGISTRY_not_save", + Self::REGISTRY_RECOVER => "SCE_REGMGR_ENT_KEY_REGISTRY_recover", + Self::REGISTRY_DOWNGRADE => "SCE_REGMGR_ENT_KEY_REGISTRY_downgrade", + Self::REGISTRY_BOOTCOUNT => "SCE_REGMGR_ENT_KEY_REGISTRY_bootcount", + Self::REGISTRY_LASTVER => "SCE_REGMGR_ENT_KEY_REGISTRY_lastver", + Self::REGISTRY_INIT_FLAG => "SCE_REGMGR_ENT_KEY_REGISTRY_init_flag", + Self::SYSTEM_UPDATE_MODE => "SCE_REGMGR_ENT_KEY_SYSTEM_update_mode", + Self::SYSTEM_LANGUAGE => "SCE_REGMGR_ENT_KEY_SYSTEM_language", + Self::SYSTEM_INITIALIZE => "SCE_REGMGR_ENT_KEY_SYSTEM_initialize", + Self::SYSTEM_NICKNAME => "SCE_REGMGR_ENT_KEY_SYSTEM_nickname", + Self::SYSTEM_DIMMER_INTERVAL => "SCE_REGMGR_ENT_KEY_SYSTEM_dimmer_interval", + Self::SYSTEM_EAPFUNCTION => "SCE_REGMGR_ENT_KEY_SYSTEM_eapfunction", + Self::SYSTEM_ENABLE_VOICERCG => "SCE_REGMGR_ENT_KEY_SYSTEM_enable_voicercg", + Self::SYSTEM_SOFT_VERSION => "SCE_REGMGR_ENT_KEY_SYSTEM_soft_version", + Self::SYSTEM_PROFILECH_VER => "SCE_REGMGR_ENT_KEY_SYSTEM_profilech_ver", + Self::SYSTEM_BUTTON_ASSIGN => "SCE_REGMGR_ENT_KEY_SYSTEM_button_assign", + Self::SYSTEM_BACKUP_MODE => "SCE_REGMGR_ENT_KEY_SYSTEM_backup_mode", + Self::SYSTEM_PON_MEMORY_TEST => "SCE_REGMGR_ENT_KEY_SYSTEM_pon_memory_test", + Self::SYSTEM_GAME_REC_MODE => "SCE_REGMGR_ENT_KEY_SYSTEM_game_rec_mode", + Self::SYSTEM_SHELL_FUNCTION => "SCE_REGMGR_ENT_KEY_SYSTEM_shell_function", + Self::SYSTEM_PAD_CONNECTION => "SCE_REGMGR_ENT_KEY_SYSTEM_pad_connection", + Self::SYSTEM_DATA_TRANSFER => "SCE_REGMGR_ENT_KEY_SYSTEM_data_transfer", + Self::SYSTEM_BASE_MODE_CLKUP => "SCE_REGMGR_ENT_KEY_SYSTEM_base_mode_clkup", + Self::SYSTEM_NEO_VDDNB_VID_OFFSET => "SCE_REGMGR_ENT_KEY_SYSTEM_neo_vddnb_vid_offset", + Self::SYSTEM_TESTBUTTON_MODE => "SCE_REGMGR_ENT_KEY_SYSTEM_testbutton_mode", + Self::SYSTEM_TESTBUTTON_PARAM => "SCE_REGMGR_ENT_KEY_SYSTEM_testbutton_param", + Self::SYSTEM_POWER_SHUTDOWN_STATUS => "SCE_REGMGR_ENT_KEY_SYSTEM_POWER_shutdown_status", + Self::SYSTEM_SPECIFIC_IDU_MODE => "SCE_REGMGR_ENT_KEY_SYSTEM_SPECIFIC_idu_mode", + Self::SYSTEM_SPECIFIC_SHOW_MODE => "SCE_REGMGR_ENT_KEY_SYSTEM_SPECIFIC_show_mode", + Self::SYSTEM_SPECIFIC_ARCADE_MODE => "SCE_REGMGR_ENT_KEY_SYSTEM_SPECIFIC_arcade_mode", + Self::SYSTEM_LIBC_INTMEM_PEAK_SIZE => "SCE_REGMGR_ENT_KEY_SYSTEM_LIBC_intmem_peak_size", Self::SYSTEM_LIBC_INTMEM_SHORTAGE_COUNT => { - f.write_str("SCE_REGMGR_ENT_KEY_SYSTEM_LIBC_intmem_shortage_count") - } - Self::AUDIOOUT_CONNECTOR_TYPE => { - f.write_str("SCE_REGMGR_ENT_KEY_AUDIOOUT_connector_type") - } - Self::AUDIOOUT_CODEC => f.write_str("SCE_REGMGR_ENT_KEY_AUDIOOUT_codec"), - Self::NET_WIFI_FREQ_BAND => f.write_str("SCE_REGMGR_ENT_KEY_NET_WIFI_freq_band"), - Self::NP_DEBUG => f.write_str("SCE_REGMGR_ENT_KEY_NP_debug"), - Self::BROWSER_DEBUG_NOTIFICATION => { - f.write_str("SCE_REGMGR_ENT_KEY_BROWSER_DEBUG_notification") - } - Self::MORPHEUS_DEBUG_VR_CAPTURE => { - f.write_str("SCE_REGMGR_ENT_KEY_MORPHEUS_DEBUG_vr_capture") - } - Self::DEVENV_TOOL_BOOT_PARAM => { - f.write_str("SCE_REGMGR_ENT_KEY_DEVENV_TOOL_boot_param") - } - Self::DEVENV_TOOL_TRC_NOTIFY => { - f.write_str("SCE_REGMGR_ENT_KEY_DEVENV_TOOL_trc_notify") - } - Self::DEVENV_TOOL_USE_DEFAULT_LIB => { - f.write_str("SCE_REGMGR_ENT_KEY_DEVENV_TOOL_use_default_lib") - } - Self::DEVENV_TOOL_SYS_PRX_PRELOAD => { - f.write_str("SCE_REGMGR_ENT_KEY_DEVENV_TOOL_sys_prx_preload") - } - Self::DEVENV_TOOL_GAME_INTMEM_DBG => { - f.write_str("SCE_REGMGR_ENT_KEY_DEVENV_TOOL_game_intmem_dbg") - } - v => write!(f, "{:#x}", v.0), - } + "SCE_REGMGR_ENT_KEY_SYSTEM_LIBC_intmem_shortage_count" + } + Self::AUDIOOUT_CONNECTOR_TYPE => "SCE_REGMGR_ENT_KEY_AUDIOOUT_connector_type", + Self::AUDIOOUT_CODEC => "SCE_REGMGR_ENT_KEY_AUDIOOUT_codec", + Self::NET_WIFI_FREQ_BAND => "SCE_REGMGR_ENT_KEY_NET_WIFI_freq_band", + Self::NP_DEBUG => "SCE_REGMGR_ENT_KEY_NP_debug", + Self::BROWSER_DEBUG_NOTIFICATION => "SCE_REGMGR_ENT_KEY_BROWSER_DEBUG_notification", + Self::MORPHEUS_DEBUG_VR_CAPTURE => "SCE_REGMGR_ENT_KEY_MORPHEUS_DEBUG_vr_capture", + Self::DEVENV_TOOL_BOOT_PARAM => "SCE_REGMGR_ENT_KEY_DEVENV_TOOL_boot_param", + Self::DEVENV_TOOL_PRELOAD_CHK_OFF => "SCE_REGMGR_ENT_KEY_DEVENV_TOOL_preload_chk_off", + Self::DEVENV_TOOL_TRC_NOTIFY => "SCE_REGMGR_ENT_KEY_DEVENV_TOOL_trc_notify", + Self::DEVENV_TOOL_USE_DEFAULT_LIB => "SCE_REGMGR_ENT_KEY_DEVENV_TOOL_use_default_lib", + Self::DEVENV_TOOL_SYS_PRX_PRELOAD => "SCE_REGMGR_ENT_KEY_DEVENV_TOOL_sys_prx_preload", + Self::DEVENV_TOOL_GAME_INTMEM_DBG => "SCE_REGMGR_ENT_KEY_DEVENV_TOOL_game_intmem_dbg", + Self::DEVENV_TOOL_SCE_MODULE_DBG => "SCE_REGMGR_ENT_KEY_DEVENV_TOOL_sce_module_dbg", + v => return write!(f, "{:#x}", v.0), + }; + + f.write_str(name) } } diff --git a/src/kernel/src/rtld/mem.rs b/src/kernel/src/rtld/mem.rs index e128e6643..49d18f25f 100644 --- a/src/kernel/src/rtld/mem.rs +++ b/src/kernel/src/rtld/mem.rs @@ -165,7 +165,7 @@ impl Memory { let prot = seg.prot; if let Err(e) = proc.vm().mprotect(addr, len, prot) { - return Err(MapError::ProtectMemoryFailed(addr, len, prot, e)); + return Err(MapError::ProtectMemoryFailed(addr as _, len, prot, e)); } } @@ -293,7 +293,9 @@ impl Memory { let prot = Protections::CPU_READ | Protections::CPU_WRITE; if let Err(e) = self.proc.vm().mprotect(ptr, len, prot) { - return Err(UnprotectSegmentError::MprotectFailed(ptr, len, prot, e)); + return Err(UnprotectSegmentError::MprotectFailed( + ptr as _, len, prot, e, + )); } Ok(UnprotectedSegment { @@ -326,7 +328,7 @@ impl Memory { let prot = Protections::CPU_READ | Protections::CPU_WRITE; if let Err(e) = self.proc.vm().mprotect(self.ptr, end, prot) { - return Err(UnprotectError::MprotectFailed(self.ptr, end, prot, e)); + return Err(UnprotectError::MprotectFailed(self.ptr as _, end, prot, e)); } Ok(UnprotectedMemory { @@ -506,13 +508,13 @@ pub enum CodeWorkspaceError { /// Represents an error when [`Memory::unprotect_segment()`] is failed. #[derive(Debug, Error)] pub enum UnprotectSegmentError { - #[error("cannot protect {1:#018x} bytes starting at {0:p} with {2}")] - MprotectFailed(*const u8, usize, Protections, #[source] MemoryUpdateError), + #[error("cannot protect {1:#018x} bytes starting at {0:#x} with {2}")] + MprotectFailed(usize, usize, Protections, #[source] MemoryUpdateError), } /// Represents an error when [`Memory::unprotect()`] is failed. #[derive(Debug, Error)] pub enum UnprotectError { - #[error("cannot protect {1:#018x} bytes starting at {0:p} with {2}")] - MprotectFailed(*const u8, usize, Protections, #[source] MemoryUpdateError), + #[error("cannot protect {1:#018x} bytes starting at {0:#x} with {2}")] + MprotectFailed(usize, usize, Protections, #[source] MemoryUpdateError), } diff --git a/src/kernel/src/rtld/mod.rs b/src/kernel/src/rtld/mod.rs index 26c559117..b3c0c0c50 100644 --- a/src/kernel/src/rtld/mod.rs +++ b/src/kernel/src/rtld/mod.rs @@ -4,6 +4,7 @@ use self::resolver::{ResolveFlags, SymbolResolver}; use crate::budget::ProcType; use crate::ee::native::{NativeEngine, SetupModuleError}; use crate::errno::{Errno, EINVAL, ENOENT, ENOEXEC, ENOMEM, EPERM, ESRCH}; +use crate::fs::VFileFlags; use crate::fs::{Fs, OpenError, VPath, VPathBuf}; use crate::idt::Entry; use crate::imgact::orbis::{DynamicFlags, Elf, FileType, ReadProgramError, Relocation, Symbol}; @@ -91,7 +92,7 @@ impl RuntimeLinker { let path = path.as_ref(); let file = self .fs - .open(path, Some(td)) + .open(path, VFileFlags::READ, Some(td)) .map_err(ExecError::OpenExeFailed)?; let elf = Elf::open(path.as_str(), file).map_err(ExecError::ReadExeFailed)?; @@ -209,7 +210,7 @@ impl RuntimeLinker { } // Get file. - let file = match self.fs.open(path, Some(td)) { + let file = match self.fs.open(path, VFileFlags::READ, Some(td)) { Ok(v) => v, Err(e) => return Err(LoadError::OpenFileFailed(e)), }; @@ -255,7 +256,7 @@ impl RuntimeLinker { // Map file. let mut table = td.proc().objects_mut(); - let (entry, _) = table.alloc(|id| { + let (entry, _) = table.try_alloc_with(|id| { let name = path.file_name().unwrap(); let id: u32 = (id + 1).try_into().unwrap(); let mut md = match Module::map(&self.ee, td.proc(), elf, 0, name, id, names, tls) { @@ -1253,8 +1254,8 @@ pub enum MapError { #[error("cannot allocate {0} bytes")] MemoryAllocationFailed(usize, #[source] MmapError), - #[error("cannot protect {1:#018x} bytes starting at {0:p} with {2}")] - ProtectMemoryFailed(*const u8, usize, Protections, #[source] MemoryUpdateError), + #[error("cannot protect {1:#018x} bytes starting at {0:#x} with {2}")] + ProtectMemoryFailed(usize, usize, Protections, #[source] MemoryUpdateError), #[error("cannot unprotect segment {0}")] UnprotectSegmentFailed(usize, #[source] UnprotectSegmentError), diff --git a/src/kernel/src/shm/mod.rs b/src/kernel/src/shm/mod.rs index e0df17d4e..15a68e649 100644 --- a/src/kernel/src/shm/mod.rs +++ b/src/kernel/src/shm/mod.rs @@ -1,15 +1,15 @@ -use crate::{ - errno::{Errno, EINVAL}, - fs::{ - check_access, Access, AccessError, DefaultFileBackendError, FileBackend, IoCmd, Mode, - OpenFlags, PollEvents, Stat, TruncateLength, Uio, UioMut, VFile, VFileFlags, VPathBuf, - }, - process::VThread, - syscalls::{SysErr, SysIn, SysOut, Syscalls}, - ucred::{Gid, Ucred, Uid}, +use crate::errno::{Errno, EINVAL}; +use crate::fs::{ + check_access, Access, AccessError, DefaultFileBackendError, FileBackend, IoCmd, IoLen, IoVec, + IoVecMut, Mode, OpenFlags, PollEvents, Stat, TruncateLength, VFile, VFileFlags, VPathBuf, + Vnode, }; +use crate::process::VThread; +use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; +use crate::ucred::{Gid, Ucred, Uid}; use macros::Errno; -use std::{convert::Infallible, sync::Arc}; +use std::convert::Infallible; +use std::sync::Arc; use thiserror::Error; pub struct SharedMemoryManager {} @@ -29,18 +29,11 @@ impl SharedMemoryManager { let flags: OpenFlags = i.args[1].try_into().unwrap(); let mode: u32 = i.args[2].try_into().unwrap(); - if (flags & OpenFlags::O_ACCMODE != OpenFlags::O_RDONLY) - || (flags & OpenFlags::O_ACCMODE != OpenFlags::O_RDWR) - { + if (flags | OpenFlags::O_RDWR) & OpenFlags::O_ACCMODE != OpenFlags::O_RDWR { return Err(SysErr::Raw(EINVAL)); } - if !flags - .difference( - OpenFlags::O_ACCMODE | OpenFlags::O_CREAT | OpenFlags::O_EXCL | OpenFlags::O_TRUNC, - ) - .is_empty() - { + if !todo!() { return Err(SysErr::Raw(EINVAL)); } @@ -49,17 +42,14 @@ impl SharedMemoryManager { #[allow(unused_variables)] // TODO: remove when implementing. let mode = mode & filedesc.cmask() & 0o7777; - let fd = filedesc.alloc_without_budget::( - |_| match path { - ShmPath::Anon => { - todo!() - } - ShmPath::Path(_) => { - todo!() - } - }, - (flags & OpenFlags::O_ACCMODE).into_fflags(), - )?; + let fd = filedesc.alloc_without_budget::(|_| match path { + ShmPath::Anon => { + todo!() + } + ShmPath::Path(_) => { + todo!() + } + })?; Ok(fd.into()) } @@ -78,7 +68,7 @@ pub enum ShmPath { /// An implementation of the `shmfd` structure. #[derive(Debug)] #[allow(unused_variables)] // TODO: remove when used. -pub struct SharedMemory { +struct SharedMemory { uid: Uid, gid: Gid, mode: Mode, @@ -111,41 +101,42 @@ impl SharedMemory { } impl FileBackend for SharedMemory { + fn is_seekable(&self) -> bool { + todo!() + } + fn read( - self: &Arc, + &self, _: &VFile, - _: &mut UioMut, + _: u64, + _: &mut [IoVecMut], _: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { Err(Box::new(DefaultFileBackendError::OperationNotSupported)) } fn write( - self: &Arc, + &self, _: &VFile, - _: &mut Uio, + _: u64, + _: &[IoVec], _: Option<&VThread>, - ) -> Result<(), Box> { + ) -> Result> { Err(Box::new(DefaultFileBackendError::OperationNotSupported)) } #[allow(unused_variables)] // remove when implementing - fn ioctl( - self: &Arc, - file: &VFile, - cmd: IoCmd, - td: Option<&VThread>, - ) -> Result<(), Box> { + fn ioctl(&self, file: &VFile, cmd: IoCmd, td: Option<&VThread>) -> Result<(), Box> { todo!() } #[allow(unused_variables)] // TODO: remove when implementing - fn poll(self: &Arc, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents { + fn poll(&self, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents { todo!() } #[allow(unused_variables)] // remove when implementing - fn stat(self: &Arc, file: &VFile, td: Option<&VThread>) -> Result> { + fn stat(&self, file: &VFile, td: Option<&VThread>) -> Result> { let mut stat = Stat::zeroed(); stat.block_size = 0x4000; @@ -154,7 +145,7 @@ impl FileBackend for SharedMemory { } fn truncate( - self: &Arc, + &self, _: &VFile, length: TruncateLength, _: Option<&VThread>, @@ -163,6 +154,10 @@ impl FileBackend for SharedMemory { Ok(()) } + + fn vnode(&self) -> Option<&Arc> { + todo!() + } } #[derive(Debug, Error, Errno)] diff --git a/src/kernel/src/signal/mod.rs b/src/kernel/src/signal/mod.rs index 805962e66..2ac7e3625 100644 --- a/src/kernel/src/signal/mod.rs +++ b/src/kernel/src/signal/mod.rs @@ -1,12 +1,87 @@ pub use self::set::*; - +use crate::errno::EINVAL; +use crate::process::VThread; +use crate::syscalls::{SysArg, SysErr, SysIn, SysOut, Syscalls}; use bitflags::bitflags; use std::borrow::Cow; use std::fmt::{Display, Formatter}; use std::num::NonZeroI32; +use std::sync::Arc; mod set; +/// Manage process/thread signal. +pub struct SignalManager {} + +impl SignalManager { + pub fn new(sys: &mut Syscalls) -> Arc { + // Register syscalls. + let mgr = Arc::new(Self {}); + + sys.register(340, &mgr, Self::sys_sigprocmask); + + mgr + } + + fn sys_sigprocmask(self: &Arc, td: &VThread, i: &SysIn) -> Result { + // Get arguments. + let how: MaskOp = i.args[0].try_into()?; + let set: *const SignalSet = i.args[1].into(); + let oset: *mut SignalSet = i.args[2].into(); + + // Convert set to an option. + let set = if set.is_null() { + None + } else { + Some(unsafe { *set }) + }; + + // Keep the current mask for copying to the oset. We need to copy to the oset only when this + // function succees. + let mut mask = td.sigmask_mut(); + let prev = *mask; + + // Update the mask. + if let Some(mut set) = set { + match how { + MaskOp::Block => { + // Remove uncatchable signals. + set.remove(SIGKILL); + set.remove(SIGSTOP); + + // Update mask. + *mask |= set; + } + MaskOp::Unblock => { + // Update mask. + *mask &= !set; + + // TODO: Invoke signotify at the end. + } + MaskOp::Set => { + // Remove uncatchable signals. + set.remove(SIGKILL); + set.remove(SIGSTOP); + + // Replace mask. + *mask = set; + + // TODO: Invoke signotify at the end. + } + } + + // TODO: Check if we need to invoke reschedule_signals. + } + + // Copy output. + if !oset.is_null() { + unsafe { *oset = prev }; + } + + Ok(SysOut::ZERO) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Signal(NonZeroI32); @@ -85,14 +160,34 @@ pub fn strsignal(sig: Signal) -> Cow<'static, str> { } pub const SIG_MAXSIG: i32 = 128; -// List of sigprocmask operations. The value must be the same as PS4 kernel. -pub const SIG_BLOCK: i32 = 1; -pub const SIG_UNBLOCK: i32 = 2; -pub const SIG_SETMASK: i32 = 3; - pub const SIG_IGN: usize = 1; pub const SIG_DFL: usize = 0; +/// List of sigprocmask operations. The value must be the same as PS4 kernel. +#[repr(i32)] +#[derive(Debug, Clone, Copy)] +pub enum MaskOp { + Block = 1, // SIG_BLOCK + Unblock = 2, // SIG_UNBLOCK + Set = 3, // SIG_SETMASK +} + +impl TryFrom for MaskOp { + type Error = SysErr; + + fn try_from(value: SysArg) -> Result { + let raw: i32 = value.try_into().map_err(|_| SysErr::Raw(EINVAL))?; + let op = match raw { + v if v == Self::Block as i32 => Self::Block, + v if v == Self::Unblock as i32 => Self::Unblock, + v if v == Self::Set as i32 => Self::Set, + _ => return Err(SysErr::Raw(EINVAL)), + }; + + Ok(op) + } +} + /// An iterator over all possible signals pub struct SignalIter { current: i32, diff --git a/src/kernel/src/syscalls/input.rs b/src/kernel/src/syscalls/input.rs index 1daf88902..2fe6ec107 100644 --- a/src/kernel/src/syscalls/input.rs +++ b/src/kernel/src/syscalls/input.rs @@ -159,3 +159,9 @@ impl LowerHex for SysArg { LowerHex::fmt(&self.0, f) } } + +impl From for SysArg { + fn from(value: usize) -> Self { + SysArg(value) + } +} diff --git a/src/kernel/src/syscalls/output.rs b/src/kernel/src/syscalls/output.rs index 919196635..5df20cdda 100644 --- a/src/kernel/src/syscalls/output.rs +++ b/src/kernel/src/syscalls/output.rs @@ -21,6 +21,15 @@ impl From<*mut T> for SysOut { } } +impl From for SysOut { + fn from(value: bool) -> Self { + Self { + rax: value.into(), + rdx: 0, + } + } +} + impl From for SysOut { fn from(value: i32) -> Self { Self { diff --git a/src/kernel/src/sysctl/mod.rs b/src/kernel/src/sysctl/mod.rs index 05186077a..80241ba60 100644 --- a/src/kernel/src/sysctl/mod.rs +++ b/src/kernel/src/sysctl/mod.rs @@ -86,6 +86,7 @@ impl Sysctl { pub const KERN_PROC_APPINFO: i32 = 35; pub const KERN_PROC_SANITIZER: i32 = 41; pub const KERN_PROC_PTC: i32 = 43; + pub const KERN_PROC_TEXT_SEGMENT: i32 = 44; pub const MACHDEP_TSC_FREQ: i32 = 492; pub const VM_PS4DEV: i32 = 1; @@ -352,7 +353,7 @@ impl Sysctl { let td = VThread::current().unwrap(); - if arg1[0] != td.proc().id().get() { + if arg1[0] != td.proc().id() { return Err(SysErr::Raw(ESRCH)); } @@ -402,6 +403,16 @@ impl Sysctl { Ok(()) } + fn kern_proc_text_segment( + &self, + _: &'static Oid, + _: &Arg, + _: usize, + _: &mut SysctlReq, + ) -> Result<(), SysErr> { + todo!() + } + fn kern_usrstack( &self, _: &'static Oid, @@ -640,7 +651,8 @@ type Handler = fn(&Sysctl, &'static Oid, &Arg, usize, &mut SysctlReq) -> Result< // └─── ... // └─── (1.14.41) KERN_PROC_SANITIZER // └─── ... -// └─── (1.13.43) KERN_PROC_PTC +// └─── (1.14.43) KERN_PROC_PTC +// └─── (1.14.44) KERN_PROC_TEXT_SEGMENT // └─── ... // └─── (1.33) KERN_USRSTACK // └─── ... @@ -791,7 +803,7 @@ static KERN_PROC_SANITIZER: Oid = Oid { static KERN_PROC_PTC: Oid = Oid { parent: &KERN_PROC_CHILDREN, - link: None, // TODO: Implement this. + link: Some(&KERN_PROC_TEXT_SEGMENT), number: Sysctl::KERN_PROC_PTC, kind: Sysctl::CTLFLAG_RD | Sysctl::CTLFLAG_ANYBODY @@ -806,6 +818,20 @@ static KERN_PROC_PTC: Oid = Oid { enabled: true, }; +static KERN_PROC_TEXT_SEGMENT: Oid = Oid { + parent: &KERN_PROC_CHILDREN, + link: None, // TODO: Implement this. + number: Sysctl::KERN_PROC_TEXT_SEGMENT, + kind: Sysctl::CTLFLAG_RD | Sysctl::CTLFLAG_MPSAFE | Sysctl::CTLTYPE_NODE, + arg1: None, // TODO: This value on the PS4 is not null. + arg2: 0, + name: "kern_dynlib_get_libkernel_text_segment", + handler: Some(Sysctl::kern_proc_text_segment), + fmt: "N", + descr: "Sanitizing mode", + enabled: true, +}; + static KERN_USRSTACK: Oid = Oid { parent: &KERN_CHILDREN, link: Some(&KERN_ARANDOM), // TODO: Use a proper value. diff --git a/src/kernel/src/time/mod.rs b/src/kernel/src/time/mod.rs index 6dade86d7..a2d1473d8 100644 --- a/src/kernel/src/time/mod.rs +++ b/src/kernel/src/time/mod.rs @@ -1,4 +1,5 @@ -use crate::errno::Errno; +use crate::errno::{Errno, EINVAL}; +use crate::info; use crate::process::VThread; use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; use std::num::NonZeroI32; @@ -13,14 +14,17 @@ impl TimeManager { sys.register(116, &time, Self::sys_gettimeofday); sys.register(232, &time, Self::sys_clock_gettime); + sys.register(234, &time, Self::sys_clock_getres); time } - pub fn sys_gettimeofday(self: &Arc, _: &VThread, i: &SysIn) -> Result { + fn sys_gettimeofday(self: &Arc, _: &VThread, i: &SysIn) -> Result { let tv: *mut TimeVal = i.args[0].into(); let tz: *mut TimeZone = i.args[1].into(); + info!("Getting the time of day"); + if !tv.is_null() { unsafe { *tv = TimeVal::microtime()?; @@ -34,16 +38,186 @@ impl TimeManager { Ok(SysOut::ZERO) } - pub fn sys_clock_gettime(self: &Arc, _: &VThread, i: &SysIn) -> Result { - let clock: i32 = i.args[0].try_into().unwrap(); + fn sys_clock_gettime(self: &Arc, _: &VThread, i: &SysIn) -> Result { + let clock_id: ClockId = { + let clock: i32 = i.args[0].try_into().unwrap(); + + clock.try_into()? + }; + + let ts: *mut TimeSpec = i.args[1].into(); + + info!("Getting clock time with clock_id = {clock_id:?}"); + + unsafe { + *ts = match clock_id { + ClockId::REALTIME | ClockId::REALTIME_PRECISE => todo!(), + ClockId::VIRTUAL => todo!(), + ClockId::PROF => todo!(), + ClockId::UPTIME + | ClockId::UPTIME_PRECISE + | ClockId::MONOTONIC + | ClockId::MONOTONIC_PRECISE => Self::nanouptime()?, + ClockId::UPTIME_FAST | ClockId::MONOTONIC_FAST => todo!(), + ClockId::REALTIME_FAST => todo!(), + ClockId::SECOND => Self::time_second()?, + ClockId::THREAD_CPUTIME_ID => todo!(), + ClockId::PROC_TIME => todo!(), + ClockId::EXT_NETWORK => todo!(), + ClockId::EXT_DEBUG_NETWORK => todo!(), + ClockId::EXT_AD_NETWORK => todo!(), + ClockId::EXT_RAW_NETWORK => todo!(), + _ => todo!("clock_gettime with sclock_id = {clock_id:?}"), + } + } + + Ok(SysOut::ZERO) + } + + fn sys_clock_getres(self: &Arc, _: &VThread, i: &SysIn) -> Result { + let clock_id: ClockId = { + let clock: i32 = i.args[0].try_into().unwrap(); + + clock.try_into()? + }; + let ts: *mut TimeSpec = i.args[1].into(); + + info!("Getting clock resolution with clock_id = {clock_id:?}"); + + if !ts.is_null() { + unsafe { + *ts = match clock_id { + ClockId::REALTIME + | ClockId::MONOTONIC + | ClockId::UPTIME + | ClockId::UPTIME_PRECISE + | ClockId::UPTIME_FAST + | ClockId::REALTIME_PRECISE + | ClockId::REALTIME_FAST + | ClockId::MONOTONIC_PRECISE + | ClockId::MONOTONIC_FAST => todo!(), + ClockId::VIRTUAL | ClockId::PROF => todo!(), + ClockId::SECOND => TimeSpec { sec: 1, nsec: 0 }, + ClockId::THREAD_CPUTIME_ID => todo!(), + _ => return Ok(SysOut::ZERO), + } + } + } + + Ok(SysOut::ZERO) + } + + #[cfg(unix)] + fn nanouptime() -> Result { + use libc::{clock_gettime, CLOCK_MONOTONIC}; + use std::mem::MaybeUninit; + + let mut ts = MaybeUninit::uninit(); + + let res = unsafe { clock_gettime(CLOCK_MONOTONIC, ts.as_mut_ptr()) }; + + if res < 0 { + Err(std::io::Error::last_os_error())?; + } + + Ok(unsafe { ts.assume_init() }.into()) + } + + #[cfg(windows)] + pub fn nanouptime() -> Result { + use windows_sys::Win32::System::Performance::{ + QueryPerformanceCounter, QueryPerformanceFrequency, + }; + + let mut counter = 0; + let mut frequency = 0; + + unsafe { + if QueryPerformanceCounter(&mut counter) == 0 { + return Err(std::io::Error::last_os_error().into()); + } + if QueryPerformanceFrequency(&mut frequency) == 0 { + return Err(std::io::Error::last_os_error().into()); + } + } + + // Convert the counter to nanoseconds (Ensure no overflow using leftover ticks.) + let seconds = counter / frequency; + let leftover_ticks = counter % frequency; + let nanoseconds = (leftover_ticks * 1_000_000_000) / frequency; + + Ok(TimeSpec { + sec: seconds as i64, + nsec: nanoseconds as i64, + }) + } + + pub fn time_second() -> Result { + Ok(TimeSpec { + nsec: 0, + ..Self::nanouptime()? + }) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub struct ClockId(i32); + +impl ClockId { + pub const REALTIME: Self = Self(0); + pub const VIRTUAL: Self = Self(1); + pub const PROF: Self = Self(2); + pub const MONOTONIC: Self = Self(4); + pub const UPTIME: Self = Self(5); + pub const UPTIME_PRECISE: Self = Self(7); + pub const UPTIME_FAST: Self = Self(8); + pub const REALTIME_PRECISE: Self = Self(9); + pub const REALTIME_FAST: Self = Self(10); + pub const MONOTONIC_PRECISE: Self = Self(11); + pub const MONOTONIC_FAST: Self = Self(12); + pub const SECOND: Self = Self(13); + pub const THREAD_CPUTIME_ID: Self = Self(14); + pub const PROC_TIME: Self = Self(15); + pub const EXT_NETWORK: Self = Self(16); + pub const EXT_DEBUG_NETWORK: Self = Self(17); + pub const EXT_AD_NETWORK: Self = Self(18); + pub const EXT_RAW_NETWORK: Self = Self(19); +} + +impl TryFrom for ClockId { + type Error = SysErr; - todo!("clock_gettime with clock = {clock}") + fn try_from(value: i32) -> Result { + let clock_id = match value { + 0 => Self::REALTIME, + 1 => Self::VIRTUAL, + 2 => Self::PROF, + 4 => Self::MONOTONIC, + 5 => Self::UPTIME, + 7 => Self::UPTIME_PRECISE, + 8 => Self::UPTIME_FAST, + 9 => Self::REALTIME_PRECISE, + 10 => Self::REALTIME_FAST, + 11 => Self::MONOTONIC_PRECISE, + 12 => Self::MONOTONIC_FAST, + 13 => Self::SECOND, + 14 => Self::THREAD_CPUTIME_ID, + 15 => Self::PROC_TIME, + 16 => Self::EXT_NETWORK, + 17 => Self::EXT_DEBUG_NETWORK, + 18 => Self::EXT_AD_NETWORK, + 19 => Self::EXT_RAW_NETWORK, + ..=-1 => Self(value), + _ => return Err(SysErr::Raw(EINVAL)), + }; + + Ok(clock_id) } } /// An implementation of the `timespec` structure. -#[derive(Debug, Clone, Copy)] #[repr(C)] +#[derive(Debug, Clone, Copy)] pub struct TimeSpec { sec: i64, nsec: i64, @@ -55,6 +229,16 @@ impl TimeSpec { } } +#[cfg(unix)] +impl From for TimeSpec { + fn from(ts: libc::timespec) -> Self { + Self { + sec: ts.tv_sec, + nsec: ts.tv_nsec, + } + } +} + impl From for TimeSpec { fn from(tv: TimeVal) -> Self { Self { @@ -125,6 +309,20 @@ struct TimeZone { dsttime: i32, // tz_dsttime } +#[derive(Debug, Error)] +pub enum NanoUpTimeError { + #[error("Failed to get time")] + IoError(#[from] std::io::Error), +} + +impl Errno for NanoUpTimeError { + fn errno(&self) -> NonZeroI32 { + match self { + Self::IoError(_) => todo!(), + } + } +} + #[derive(Debug, Error)] pub enum MicroTimeError { #[error("Failed to get time")] diff --git a/src/kernel/src/ucred/auth.rs b/src/kernel/src/ucred/auth.rs index f47b4c918..257a47314 100644 --- a/src/kernel/src/ucred/auth.rs +++ b/src/kernel/src/ucred/auth.rs @@ -1,3 +1,5 @@ +use crate::rcmgr::RcMgr; + /// An implementation of `self_auth_info`. #[repr(C)] #[derive(Debug, Clone)] @@ -8,6 +10,7 @@ pub struct AuthInfo { pub unk: [u8; 0x40], } +#[allow(dead_code)] impl AuthInfo { pub const SYS_CORE: Self = Self { paid: AuthPaid::SYS_CORE, @@ -25,7 +28,9 @@ impl AuthInfo { ]), unk: [0; 0x40], }; +} +impl AuthInfo { pub fn from_title_id>(title_id: T) -> Option { // Skip CUSA. let id = title_id.as_ref().get(4..)?; @@ -79,6 +84,16 @@ impl AuthCaps { Self(raw) } + pub fn clear_non_type(&mut self) { + self.0[0] &= 0x7000000000000000; + self.0[1] = 0; + self.0[2] = 0; + self.0[3] = 0; + } +} + +#[allow(dead_code)] +impl AuthCaps { pub fn is_nongame(&self) -> bool { (self.0[0] & 0x1000000000000000) != 0 } @@ -87,23 +102,16 @@ impl AuthCaps { (self.0[0] & 0x2000000000000000) != 0 } - pub fn has_use_video_service_capability(&self) -> bool { - self.0[0] >> 0x39 & 1 != 0 - } - pub fn is_system(&self) -> bool { (self.0[0] & 0x4000000000000000) != 0 } - pub fn is_unk1(&self) -> bool { - (self.0[1] & 0x4000000000000000) != 0 + pub fn has_use_video_service(&self) -> bool { + (self.0[1] & 0x0200000000000000) != 0 } - pub fn clear_non_type(&mut self) { - self.0[0] &= 0x7000000000000000; - self.0[1] = 0; - self.0[2] = 0; - self.0[3] = 0; + pub fn is_unk1(&self) -> bool { + (self.0[1] & 0x4000000000000000) != 0 } } @@ -117,28 +125,29 @@ impl AuthAttrs { Self(raw) } - pub fn has_sce_program_attribute(&self) -> bool { - (self.0[0] & 0x80000000) != 0 + pub fn is_unk2(&self) -> bool { + (self.0[0] & 0x00400000) != 0 } - pub fn is_debuggable_process(&self) -> bool { - if self.0[0] & 0x1000000 == 0 { - if self.0[0] & 0x2000000 != 0 { - true - } else { - // TODO: properly implement this branch. - false + pub fn is_unk1(&self) -> bool { + (self.0[0] & 0x00800000) != 0 + } + + pub fn is_debuggable_process(&self, rc: &RcMgr) -> bool { + if (self.0[0] & 0x01000000) == 0 { + if (self.0[0] & 0x02000000) != 0 || rc.is_allow_ul_debugger() { + return true; } - } else { - todo!() + + // On the PS4 there is a same check as the first line of this function before it return + // 0, not sure what the purpose of this because the condition should always true here? + return false; } - } - pub fn is_unk1(&self) -> bool { - (self.0[0] & 0x800000) != 0 + rc.is_softwagner_qaf_for_acmgr() } - pub fn is_unk2(&self) -> bool { - (self.0[0] & 0x400000) != 0 + pub fn has_sce_program_attribute(&self) -> bool { + (self.0[0] & 0x80000000) != 0 } } diff --git a/src/kernel/src/ucred/mod.rs b/src/kernel/src/ucred/mod.rs index 343cbb012..c0f3ef143 100644 --- a/src/kernel/src/ucred/mod.rs +++ b/src/kernel/src/ucred/mod.rs @@ -2,6 +2,7 @@ pub use self::auth::*; pub use self::id::*; pub use self::privilege::*; use crate::errno::{Errno, EPERM}; +use crate::rcmgr::RcMgr; use macros::Errno; use thiserror::Error; @@ -116,7 +117,7 @@ impl Ucred { /// See `sceSblACMgrHasUseVideoServiceCapability` on the PS4 for a reference. pub fn has_use_video_service_capability(&self) -> bool { - self.auth.caps.has_use_video_service_capability() + self.auth.caps.has_use_video_service() } /// See `sceSblACMgrIsWebcoreProcess` on the PS4 for a reference. @@ -132,8 +133,8 @@ impl Ucred { } /// See `sceSblACMgrIsDebuggableProcess` on the PS4 for a reference. - pub fn is_debuggable_process(&self) -> bool { - self.auth.attrs.is_debuggable_process() + pub fn is_debuggable_process(&self, rc: &RcMgr) -> bool { + self.auth.attrs.is_debuggable_process(rc) } /// See `sceSblACMgrIsNongameUcred` on the PS4 for a reference. @@ -153,6 +154,13 @@ impl Ucred { self.auth.caps.is_unk1() && self.auth.attrs.is_unk2() } + pub fn unk_gc_check(&self) -> bool { + matches!( + self.auth.paid.get(), + 0x3800000000000009 | 0x380100000000002c + ) + } + /// See `priv_check_cred` on the PS4 for a reference. pub fn priv_check(&self, p: Privilege) -> Result<(), PrivilegeError> { // TODO: Check suser_enabled. diff --git a/src/kernel/src/vm/mod.rs b/src/kernel/src/vm/mod.rs index a160ef5bb..0bf1bf7b1 100644 --- a/src/kernel/src/vm/mod.rs +++ b/src/kernel/src/vm/mod.rs @@ -3,6 +3,7 @@ pub use self::stack::*; use self::iter::StartFromMut; use self::storage::Storage; +use crate::dev::DmemContainer; use crate::errno::{Errno, EINVAL, ENOMEM, EOPNOTSUPP}; use crate::process::VThread; use crate::syscalls::{SysArg, SysErr, SysIn, SysOut, Syscalls}; @@ -83,8 +84,11 @@ impl Vm { sys.register(69, &mm, Self::sys_sbrk); sys.register(70, &mm, Self::sys_sstk); + sys.register(73, &mm, Self::sys_munmap); sys.register(477, &mm, Self::sys_mmap); + sys.register(548, &mm, Self::sys_batch_map); sys.register(588, &mm, Self::sys_mname); + sys.register(628, &mm, Self::sys_mmap_dmem); Ok(mm) } @@ -390,7 +394,7 @@ impl Vm { // TODO: Check if PS4 requires contiguous allocations. if !prev.is_null() && info.addr != prev { - return Err(MemoryUpdateError::UnmappedAddr(prev)); + return Err(MemoryUpdateError::UnmappedAddr(prev as _)); } prev = info.end(); @@ -532,6 +536,19 @@ impl Vm { Err(SysErr::Raw(EOPNOTSUPP)) } + #[allow(unused_variables)] + fn sys_munmap(self: &Arc, _: &VThread, i: &SysIn) -> Result { + let addr: usize = i.args[0].into(); + let len: usize = i.args[1].into(); + + self.munmap_internal(addr, len) + } + + #[allow(unused_variables)] + fn munmap_internal(self: &Arc, addr: usize, len: usize) -> Result { + todo!() + } + fn sys_mmap(self: &Arc, _: &VThread, i: &SysIn) -> Result { // Get arguments. let addr: usize = i.args[0].into(); @@ -541,6 +558,18 @@ impl Vm { let fd: i32 = i.args[4].try_into().unwrap(); let pos: usize = i.args[5].into(); + self.mmap_internal(addr, len, prot, flags, fd, pos) + } + + fn mmap_internal( + self: &Arc, + addr: usize, + len: usize, + prot: Protections, + flags: MappingFlags, + fd: i32, + pos: usize, + ) -> Result { // Check if the request is a guard for main stack. if addr == self.stack.guard() { assert_eq!(len, Self::VIRTUAL_PAGE_SIZE); @@ -582,6 +611,84 @@ impl Vm { Ok(pages.into_raw().into()) } + fn sys_batch_map(self: &Arc, td: &VThread, i: &SysIn) -> Result { + let dmem_fd: i32 = i.args[0].try_into().unwrap(); + let flags: MappingFlags = i.args[1].try_into().unwrap(); + let operations: *const BatchMapArg = i.args[2].into(); + let num_of_ops: i32 = i.args[3].try_into().unwrap(); + let num_of_processed_ops: *mut i32 = i.args[4].into(); + + if flags.bits() & 0xe0bffb6f != 0 { + return Err(SysErr::Raw(EINVAL)); + } + + let slice_size = num_of_ops.try_into().ok().ok_or(SysErr::Raw(EINVAL))?; + let operations = unsafe { std::slice::from_raw_parts(operations, slice_size) }; + + let mut processed = 0; + + let result = operations.iter().try_for_each(|arg| { + match arg.op.try_into()? { + BatchMapOp::MapDirect => { + if *td.proc().dmem_container() != DmemContainer::One + /* || td.proc().unk4 & 2 != 0 */ + /* || td.proc().sdk_version < 0x2500000 */ + || flags.intersects(MappingFlags::MAP_STACK) + { + todo!() + } + + self.mmap_dmem_internal( + arg.addr, + arg.len, + arg.ty.try_into().unwrap(), + arg.prot.try_into().unwrap(), + flags, + arg.offset, + )?; + } + BatchMapOp::MapFlexible => { + if arg.addr & 0x3fff != 0 || arg.len & 0x3fff != 0 || arg.prot & 0xc8 != 0 { + return Err(SysErr::Raw(EINVAL)); + } + + self.mmap_internal( + arg.addr, + arg.len, + arg.prot.try_into().unwrap(), + flags.intersection(MappingFlags::MAP_ANON), + -1, + 0, + )?; + } + BatchMapOp::Protect => todo!(), + BatchMapOp::TypeProtect => todo!(), + BatchMapOp::Unmap => { + if arg.addr & 0x3fff != 0 || arg.len & 0x3fff != 0 { + return Err(SysErr::Raw(EINVAL)); + } + + self.munmap_internal(arg.addr, arg.len)?; + } + _ => todo!(), + } + + processed = processed + 1; + + Ok(()) + }); + + // TODO: invalidate TLB + + if !num_of_processed_ops.is_null() { + unsafe { + *num_of_processed_ops = processed; + } + } + + result.map(|_| SysOut::ZERO) + } + fn sys_mname(self: &Arc, _: &VThread, i: &SysIn) -> Result { let addr: usize = i.args[0].into(); let len: usize = i.args[1].into(); @@ -605,6 +712,31 @@ impl Vm { Ok(SysOut::ZERO) } + #[allow(unused_variables)] + fn sys_mmap_dmem(self: &Arc, _: &VThread, i: &SysIn) -> Result { + let start_addr: usize = i.args[0].into(); + let len: usize = i.args[1].into(); + let mem_type: MemoryType = i.args[2].try_into().unwrap(); + let prot: Protections = i.args[3].try_into().unwrap(); + let flags: MappingFlags = i.args[4].try_into().unwrap(); + let start_phys_addr: usize = i.args[5].into(); + + self.mmap_dmem_internal(start_addr, len, mem_type, prot, flags, start_phys_addr) + } + + #[allow(unused_variables)] + fn mmap_dmem_internal( + self: &Arc, + start_addr: usize, + len: usize, + mem_type: MemoryType, + prot: Protections, + flags: MappingFlags, + start_phys_addr: usize, + ) -> Result { + todo!() + } + fn align_virtual_page(ptr: *mut u8) -> *mut u8 { match (ptr as usize) % Self::VIRTUAL_PAGE_SIZE { 0 => ptr, @@ -729,6 +861,14 @@ impl TryFrom for Protections { } } +impl TryFrom for Protections { + type Error = TryFromIntError; + + fn try_from(value: u8) -> Result { + Ok(Self::from_bits_retain(value as u32)) + } +} + impl Display for Protections { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) @@ -823,6 +963,69 @@ pub enum MemoryUpdateError { #[error("invalid addr")] InvalidAddr, - #[error("address {0:p} is not mapped")] - UnmappedAddr(*const u8), + #[error("address {0:#x} is not mapped")] + UnmappedAddr(usize), +} + +#[repr(C)] +struct BatchMapArg { + addr: usize, + offset: usize, + len: usize, + prot: u8, + ty: u8, + op: i32, +} + +#[repr(i32)] +enum BatchMapOp { + MapDirect = 0, + Unmap = 1, + Protect = 2, + MapFlexible = 3, + TypeProtect = 4, +} + +impl TryFrom for BatchMapOp { + type Error = SysErr; + + fn try_from(raw: i32) -> Result { + match raw { + 0 => Ok(BatchMapOp::MapDirect), + 1 => Ok(BatchMapOp::Unmap), + 2 => Ok(BatchMapOp::Protect), + 3 => Ok(BatchMapOp::MapFlexible), + 4 => Ok(BatchMapOp::TypeProtect), + _ => Err(SysErr::Raw(EINVAL)), + } + } +} + +#[repr(i32)] +enum MemoryType { + WbOnion = 0, + WcGarlic = 3, + WbGarlic = 10, +} + +impl TryFrom for MemoryType { + type Error = TryFromIntError; + + fn try_from(value: SysArg) -> Result { + let val: u8 = value.try_into().unwrap(); + val.try_into() + } +} + +impl TryFrom for MemoryType { + type Error = TryFromIntError; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(MemoryType::WbOnion), + 3 => Ok(MemoryType::WcGarlic), + 10 => Ok(MemoryType::WbGarlic), + _ => unreachable!(), + } + } } diff --git a/src/main_window.cpp b/src/main_window.cpp index a501d590a..7efd786af 100644 --- a/src/main_window.cpp +++ b/src/main_window.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -106,10 +107,13 @@ MainWindow::MainWindow() : // Setup game list. m_games = new QListView(); - m_games->setViewMode(QListView::IconMode); - m_games->setWordWrap(true); m_games->setContextMenuPolicy(Qt::CustomContextMenu); + m_games->setLayoutMode(QListView::SinglePass); m_games->setModel(new GameListModel(this)); + m_games->setViewMode(QListView::IconMode); + m_games->setWordWrap(true); + m_games->verticalScrollBar()->setSingleStep(20); + connect(m_games, &QAbstractItemView::doubleClicked, this, &MainWindow::startGame); connect(m_games, &QWidget::customContextMenuRequested, this, &MainWindow::requestGamesContextMenu); diff --git a/src/param/src/lib.rs b/src/param/src/lib.rs index 1e9e7429f..9568dfa60 100644 --- a/src/param/src/lib.rs +++ b/src/param/src/lib.rs @@ -12,7 +12,7 @@ pub struct Param { content_id: Box, title: Option>, title_id: Box, - version: Box, + version: Option>, } impl Param { @@ -166,7 +166,7 @@ impl Param { content_id: content_id.ok_or(ReadError::MissingContentId)?, title, title_id: title_id.ok_or(ReadError::MissingTitleId)?, - version: version.ok_or(ReadError::MissingVersion)?, + version: version, }) } @@ -204,8 +204,8 @@ impl Param { } /// Fetches the value VERSION from given Param.SFO - pub fn version(&self) -> &str { - &self.version + pub fn version(&self) -> Option<&str> { + self.version.as_deref() } fn read_utf8( @@ -276,7 +276,4 @@ pub enum ReadError { #[error("TITLE_ID parameter not found")] MissingTitleId, - - #[error("VERSION parameter not found")] - MissingVersion, } diff --git a/src/resources.qrc b/src/resources.qrc index 4d7d0f472..938af5d5b 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -11,6 +11,7 @@ resources/lightmode/cog-outline.svg resources/lightmode/folder-open-outline.svg resources/lightmode/view-comfy.svg + resources/fallbackicon0.png resources/obliteration-icon.png diff --git a/src/resources/fallbackicon0.png b/src/resources/fallbackicon0.png new file mode 100644 index 000000000..0841196ea Binary files /dev/null and b/src/resources/fallbackicon0.png differ