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