().unwrap();
21 |
22 | match back.protocol_specific() {
23 | Some(ProtocolSpecific::Wayland(_)) => {} // Wayland doesn't allow windows to position themselves.
24 | Some(ProtocolSpecific::X11(x11)) => unsafe {
25 | self::x11::set_center(&x11, self)?;
26 | },
27 | None => unimplemented!(),
28 | };
29 |
30 | Ok(())
31 | }
32 |
33 | fn set_modal(self, parent: &P) -> Result, PlatformError>
34 | where
35 | P: DesktopWindow,
36 | Self: Sized,
37 | {
38 | let back = wae::global::().unwrap();
39 |
40 | let wayland = match back.protocol_specific() {
41 | Some(ProtocolSpecific::Wayland(wayland)) => unsafe {
42 | self::wayland::set_modal(wayland, &self, parent).map(Some)?
43 | },
44 | Some(ProtocolSpecific::X11(x11)) => unsafe {
45 | self::x11::set_modal(&x11, &self, parent)?;
46 |
47 | None
48 | },
49 | None => None,
50 | };
51 |
52 | Ok(Modal::new(self, parent, wayland))
53 | }
54 | }
55 |
56 | /// Linux-specific error for [`DesktopExt`].
57 | #[derive(Debug, Error)]
58 | pub enum PlatformError {
59 | #[error("couldn't create xdg_dialog_v1")]
60 | CreateXdgDialogV1(#[source] wayland_client::DispatchError),
61 |
62 | #[error("couldn't set window modal")]
63 | SetModal(#[source] wayland_client::DispatchError),
64 |
65 | #[error("couldn't set window type")]
66 | XcbSetWindowType(#[source] xcb::ProtocolError),
67 |
68 | #[error("couldn't set window wm state")]
69 | XcbSetWmState(#[source] xcb::ProtocolError),
70 |
71 | #[error("couldn't set window parent")]
72 | XcbSetParent(#[source] xcb::ProtocolError),
73 |
74 | #[error("couldn't get window geometry")]
75 | XcbGetGeometry(#[source] xcb::Error),
76 |
77 | #[error("couldn't center window")]
78 | XcbCenterWindow(#[source] xcb::ProtocolError),
79 |
80 | #[error("couldn't get window attributes")]
81 | XlibGetWindowAttributes,
82 | }
83 |
--------------------------------------------------------------------------------
/gui/src/ui/linux/modal.rs:
--------------------------------------------------------------------------------
1 | use crate::ui::DesktopWindow;
2 | use std::ops::Deref;
3 | use wae::Blocker;
4 | use wayland_protocols::xdg::dialog::v1::client::xdg_dialog_v1::XdgDialogV1;
5 |
6 | /// Encapsulates a modal window and its parent.
7 | ///
8 | /// This struct forces the modal window to be dropped before its parent.
9 | pub struct Modal<'a, W, P: DesktopWindow> {
10 | window: W,
11 | wayland: Option,
12 | #[allow(dead_code)]
13 | blocker: Blocker<'a, P>,
14 | }
15 |
16 | impl<'a, W, P: DesktopWindow> Modal<'a, W, P> {
17 | pub fn new(window: W, parent: &'a P, wayland: Option) -> Self {
18 | Self {
19 | window,
20 | wayland,
21 | blocker: wae::block(parent),
22 | }
23 | }
24 | }
25 |
26 | impl<'a, W, P: DesktopWindow> Drop for Modal<'a, W, P> {
27 | fn drop(&mut self) {
28 | if let Some(v) = self.wayland.take() {
29 | v.destroy();
30 | }
31 | }
32 | }
33 |
34 | impl<'a, W, P: DesktopWindow> Deref for Modal<'a, W, P> {
35 | type Target = W;
36 |
37 | fn deref(&self) -> &Self::Target {
38 | &self.window
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/gui/src/ui/linux/wayland.rs:
--------------------------------------------------------------------------------
1 | use super::PlatformError;
2 | use crate::ui::DesktopWindow;
3 | use crate::ui::backend::Wayland;
4 | use std::ptr::null_mut;
5 | use wayland_backend::sys::client::ObjectId;
6 | use wayland_client::Proxy;
7 | use wayland_protocols::xdg::dialog::v1::client::xdg_dialog_v1::XdgDialogV1;
8 | use wayland_protocols::xdg::shell::client::xdg_toplevel::XdgToplevel;
9 |
10 | /// # Safety
11 | /// `parent` must outlive `target`.
12 | pub unsafe fn set_modal(
13 | wayland: &Wayland,
14 | target: &impl DesktopWindow,
15 | parent: &impl DesktopWindow,
16 | ) -> Result {
17 | // Get xdg_toplevel for parent.
18 | let mut queue = wayland.queue().borrow_mut();
19 | let mut state = wayland.state().borrow_mut();
20 | let qh = queue.handle();
21 | let parent = unsafe { get_xdg_toplevel(wayland, parent) };
22 |
23 | // Get xdg_dialog_v1.
24 | let target = unsafe { get_xdg_toplevel(wayland, target) };
25 | let dialog = state.xdg_dialog().get_xdg_dialog(&target, &qh, ());
26 |
27 | queue
28 | .roundtrip(&mut state)
29 | .map_err(PlatformError::CreateXdgDialogV1)?;
30 |
31 | // Set modal.
32 | target.set_parent(Some(&parent));
33 | dialog.set_modal();
34 |
35 | queue
36 | .roundtrip(&mut state)
37 | .map_err(PlatformError::SetModal)?;
38 |
39 | Ok(dialog)
40 | }
41 |
42 | /// # Safety
43 | /// `win` must outlive the returned [`XdgToplevel`].
44 | unsafe fn get_xdg_toplevel(wayland: &Wayland, win: &impl DesktopWindow) -> XdgToplevel {
45 | let obj = win.xdg_toplevel().map(|v| v.as_ptr()).unwrap_or(null_mut());
46 | let obj = unsafe { ObjectId::from_ptr(XdgToplevel::interface(), obj.cast()).unwrap() };
47 |
48 | XdgToplevel::from_id(wayland.connection(), obj).unwrap()
49 | }
50 |
--------------------------------------------------------------------------------
/gui/src/ui/macos/mod.rs:
--------------------------------------------------------------------------------
1 | pub use self::dialogs::*;
2 |
3 | use self::modal::Modal;
4 | use self::view::get_window;
5 | use super::{DesktopExt, DesktopWindow};
6 | use block2::RcBlock;
7 | use objc2::msg_send;
8 | use std::ffi::c_long;
9 | use std::ops::Deref;
10 | use thiserror::Error;
11 |
12 | mod dialogs;
13 | mod modal;
14 | mod view;
15 |
16 | impl DesktopExt for T {
17 | type Modal<'a, P>
18 | = Modal<'a, Self, P>
19 | where
20 | P: DesktopWindow + 'a;
21 |
22 | fn set_center(&self) -> Result<(), PlatformError> {
23 | let win = get_window(self.handle());
24 | let _: () = unsafe { msg_send![win, center] };
25 | Ok(())
26 | }
27 |
28 | fn set_modal(self, parent: &P) -> Result, PlatformError>
29 | where
30 | P: DesktopWindow,
31 | Self: Sized,
32 | {
33 | // Setup completionHandler.
34 | let cb = RcBlock::new(move |_: c_long| {});
35 |
36 | // Show the sheet.
37 | let w = get_window(self.handle());
38 | let p = get_window(parent.handle());
39 | let _: () = unsafe { msg_send![p, beginSheet:w, completionHandler:cb.deref()] };
40 |
41 | Ok(Modal::new(self, parent))
42 | }
43 | }
44 |
45 | /// macOS-specific error for [`DesktopExt`].
46 | #[derive(Debug, Error)]
47 | pub enum PlatformError {}
48 |
--------------------------------------------------------------------------------
/gui/src/ui/macos/modal.rs:
--------------------------------------------------------------------------------
1 | use super::view::get_window;
2 | use crate::ui::DesktopWindow;
3 | use objc2::msg_send;
4 | use std::ops::Deref;
5 | use wae::Blocker;
6 |
7 | /// Encapsulates a modal window and its parent.
8 | ///
9 | /// This struct forces the modal window to be dropped before its parent.
10 | pub struct Modal<'a, W, P>
11 | where
12 | W: DesktopWindow,
13 | P: DesktopWindow,
14 | {
15 | window: W,
16 | parent: &'a P,
17 | #[allow(dead_code)]
18 | blocker: Blocker<'a, P>,
19 | }
20 |
21 | impl<'a, W, P> Modal<'a, W, P>
22 | where
23 | W: DesktopWindow,
24 | P: DesktopWindow,
25 | {
26 | pub(super) fn new(window: W, parent: &'a P) -> Self {
27 | Self {
28 | window,
29 | parent,
30 | blocker: wae::block(parent),
31 | }
32 | }
33 | }
34 |
35 | impl<'a, W, P> Drop for Modal<'a, W, P>
36 | where
37 | W: DesktopWindow,
38 | P: DesktopWindow,
39 | {
40 | fn drop(&mut self) {
41 | let w = get_window(self.window.handle());
42 | let p = get_window(self.parent.handle());
43 | let _: () = unsafe { msg_send![p, endSheet:w] };
44 | }
45 | }
46 |
47 | impl<'a, W, P> Deref for Modal<'a, W, P>
48 | where
49 | W: DesktopWindow,
50 | P: DesktopWindow,
51 | {
52 | type Target = W;
53 |
54 | fn deref(&self) -> &Self::Target {
55 | &self.window
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/gui/src/ui/macos/view.rs:
--------------------------------------------------------------------------------
1 | use objc2::msg_send;
2 | use objc2::runtime::NSObject;
3 | use raw_window_handle::{HasWindowHandle, RawWindowHandle};
4 |
5 | /// The returned `NSWindow` will be valid while `win` still alive.
6 | pub fn get_window(win: impl HasWindowHandle) -> *mut NSObject {
7 | let win = get_view(win);
8 |
9 | unsafe { msg_send![win, window] }
10 | }
11 |
12 | /// The returned `NSView` will be valid while `win` still alive.
13 | pub fn get_view(win: impl HasWindowHandle) -> *mut NSObject {
14 | let win = win.window_handle().unwrap();
15 |
16 | match win.as_ref() {
17 | RawWindowHandle::AppKit(v) => v.ns_view.as_ptr().cast(),
18 | _ => unreachable!(),
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/gui/src/ui/windows/modal.rs:
--------------------------------------------------------------------------------
1 | use crate::ui::DesktopWindow;
2 | use std::ops::Deref;
3 | use wae::Blocker;
4 |
5 | /// Encapsulates a modal window and its parent.
6 | ///
7 | /// This struct forces the modal window to be dropped before its parent.
8 | pub struct Modal<'a, W, P: DesktopWindow> {
9 | window: W,
10 | #[allow(dead_code)]
11 | blocker: Blocker<'a, P>,
12 | }
13 |
14 | impl<'a, W, P: DesktopWindow> Modal<'a, W, P> {
15 | pub(super) fn new(window: W, parent: &'a P) -> Self {
16 | Self {
17 | window,
18 | blocker: wae::block(parent),
19 | }
20 | }
21 | }
22 |
23 | impl<'a, W, P: DesktopWindow> Deref for Modal<'a, W, P> {
24 | type Target = W;
25 |
26 | fn deref(&self) -> &Self::Target {
27 | &self.window
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/gui/src/util/mod.rs:
--------------------------------------------------------------------------------
1 | pub use self::channel::*;
2 |
3 | mod channel;
4 |
--------------------------------------------------------------------------------
/gui/src/vfs/mod.rs:
--------------------------------------------------------------------------------
1 | use num_enum::{IntoPrimitive, TryFromPrimitive};
2 | use redb::{TableDefinition, TypeName};
3 |
4 | pub const FS_TYPE: TableDefinition<(), FsType> = TableDefinition::new("fs_type");
5 |
6 | /// Filesystem type.
7 | #[repr(u16)]
8 | #[derive(Debug, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
9 | pub enum FsType {
10 | ExFat = 1,
11 | }
12 |
13 | impl redb::Value for FsType {
14 | type SelfType<'a> = Self;
15 | type AsBytes<'a> = [u8; 2];
16 |
17 | fn fixed_width() -> Option {
18 | Some(size_of::())
19 | }
20 |
21 | fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
22 | where
23 | Self: 'a,
24 | {
25 | u16::from_le_bytes(data.try_into().unwrap())
26 | .try_into()
27 | .unwrap()
28 | }
29 |
30 | fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
31 | where
32 | Self: 'a,
33 | Self: 'b,
34 | {
35 | u16::from(*value).to_le_bytes()
36 | }
37 |
38 | fn type_name() -> TypeName {
39 | TypeName::new("obliteration::FsType")
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/gui/src/vmm/cpu/mod.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 | use crate::hw::DeviceContext;
3 | use hv::Cpu;
4 | use std::collections::BTreeMap;
5 | use std::num::NonZero;
6 | use thiserror::Error;
7 |
8 | pub mod debug;
9 |
10 | /// Contains instantiated device context for a CPU.
11 | pub struct Device<'a, C: Cpu> {
12 | pub context: Box + 'a>,
13 | pub end: NonZero,
14 | pub name: &'a str,
15 | }
16 |
17 | impl<'a, C: Cpu> Device<'a, C> {
18 | pub fn insert(
19 | tree: &mut BTreeMap,
20 | dev: &'a T,
21 | f: impl FnOnce(&'a T) -> Box + 'a>,
22 | ) {
23 | let addr = dev.addr();
24 | let dev = Self {
25 | context: f(dev),
26 | end: dev.len().checked_add(addr).unwrap(),
27 | name: dev.name(),
28 | };
29 |
30 | assert!(tree.insert(addr, dev).is_none());
31 | }
32 | }
33 |
34 | /// Implementation of [`gdbstub::target::Target::Error`].
35 | #[derive(Debug, Error)]
36 | pub enum GdbError {
37 | #[error("the main CPU exited")]
38 | MainCpuExited,
39 |
40 | #[error("CPU not found")]
41 | CpuNotFound,
42 | }
43 |
--------------------------------------------------------------------------------
/gui/src/vmm/kernel/note.rs:
--------------------------------------------------------------------------------
1 | use bytes::{Buf, Bytes};
2 | use std::cmp::min;
3 | use thiserror::Error;
4 |
5 | /// Iterator to enumerate ELF notes.
6 | pub struct Notes(Bytes);
7 |
8 | impl Notes {
9 | pub(super) fn new(data: impl Into) -> Self {
10 | Self(data.into())
11 | }
12 | }
13 |
14 | impl Iterator for Notes {
15 | type Item = Result;
16 |
17 | fn next(&mut self) -> Option {
18 | // Check remaining data.
19 | let hdr = 4 * 3;
20 |
21 | if self.0.is_empty() {
22 | return None;
23 | } else if self.0.len() < hdr {
24 | return Some(Err(NoteError::InvalidHeader));
25 | }
26 |
27 | // Parse header.
28 | let mut hdr = self.0.split_to(hdr);
29 | let nlen: usize = hdr.get_u32_ne().try_into().unwrap();
30 | let dlen: usize = hdr.get_u32_ne().try_into().unwrap();
31 | let ty = hdr.get_u32_ne();
32 |
33 | if nlen == 0 {
34 | // Name is null-terminated so it should have at least 1 byte.
35 | return Some(Err(NoteError::InvalidName));
36 | } else if nlen > self.0.len() {
37 | return Some(Err(NoteError::InvalidHeader));
38 | }
39 |
40 | // Get name.
41 | let mut name = self.0.split_to(nlen);
42 | let len = nlen - 1;
43 |
44 | if name.iter().position(|&b| b == 0) != Some(len) {
45 | return Some(Err(NoteError::InvalidName));
46 | }
47 |
48 | name.truncate(len);
49 |
50 | // Skip alignment.
51 | let skip = nlen.next_multiple_of(4) - nlen;
52 |
53 | self.0.advance(min(skip, self.0.len()));
54 |
55 | if dlen > self.0.len() {
56 | return Some(Err(NoteError::InvalidHeader));
57 | }
58 |
59 | // Get description.
60 | let desc = self.0.split_to(dlen);
61 | let skip = dlen.next_multiple_of(4) - dlen;
62 |
63 | self.0.advance(min(skip, self.0.len()));
64 |
65 | Some(Ok(Note { name, desc, ty }))
66 | }
67 | }
68 |
69 | /// Parsed ELF program header.
70 | pub struct Note {
71 | pub name: Bytes,
72 | pub desc: Bytes,
73 | pub ty: u32,
74 | }
75 |
76 | /// Represents an error when [`Notes`] fails to enumerate next ELF note.
77 | #[derive(Debug, Error)]
78 | pub enum NoteError {
79 | #[error("invalid header")]
80 | InvalidHeader,
81 |
82 | #[error("invalid name")]
83 | InvalidName,
84 | }
85 |
--------------------------------------------------------------------------------
/gui/src/vmm/kernel/segment.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT OR Apache-2.0
2 | use std::fs::File;
3 | use std::io::Read;
4 | use std::iter::FusedIterator;
5 | use thiserror::Error;
6 |
7 | pub const PT_LOAD: u32 = 1;
8 | pub const PT_DYNAMIC: u32 = 2;
9 | pub const PT_NOTE: u32 = 4;
10 | pub const PT_PHDR: u32 = 6;
11 | pub const PT_GNU_EH_FRAME: u32 = 0x6474e550;
12 | pub const PT_GNU_STACK: u32 = 0x6474e551;
13 | pub const PT_GNU_RELRO: u32 = 0x6474e552;
14 |
15 | /// Iterator to enumerate ELF program headers.
16 | pub struct ProgramHeaders<'a> {
17 | file: &'a mut File,
18 | start: u64,
19 | count: u64,
20 | parsed: u64,
21 | }
22 |
23 | impl<'a> ProgramHeaders<'a> {
24 | pub(super) fn new(file: &'a mut File, start: u64, count: u64) -> Self {
25 | Self {
26 | file,
27 | start,
28 | count,
29 | parsed: 0,
30 | }
31 | }
32 | }
33 |
34 | impl Iterator for ProgramHeaders<'_> {
35 | type Item = Result;
36 |
37 | fn next(&mut self) -> Option {
38 | // Check remaining.
39 | if self.parsed == self.count {
40 | return None;
41 | }
42 |
43 | // Read data.
44 | let mut data = [0u8; 56];
45 |
46 | if let Err(e) = self.file.read_exact(&mut data) {
47 | return Some(Err(ProgramHeaderError::ReadFailed(
48 | self.start + self.parsed * 56,
49 | e,
50 | )));
51 | }
52 |
53 | // Parse data.
54 | let p_type = u32::from_ne_bytes(data[..4].try_into().unwrap());
55 | let p_offset = u64::from_ne_bytes(data[8..16].try_into().unwrap());
56 | let p_vaddr = usize::from_ne_bytes(data[16..24].try_into().unwrap());
57 | let p_filesz = usize::from_ne_bytes(data[32..40].try_into().unwrap());
58 | let p_memsz = usize::from_ne_bytes(data[40..48].try_into().unwrap());
59 |
60 | self.parsed += 1;
61 |
62 | Some(Ok(ProgramHeader {
63 | p_type,
64 | p_offset,
65 | p_vaddr,
66 | p_filesz,
67 | p_memsz,
68 | }))
69 | }
70 | }
71 |
72 | impl FusedIterator for ProgramHeaders<'_> {}
73 |
74 | /// Parsed ELF program header.
75 | pub struct ProgramHeader {
76 | pub p_type: u32,
77 | pub p_offset: u64,
78 | pub p_vaddr: usize,
79 | pub p_filesz: usize,
80 | pub p_memsz: usize,
81 | }
82 |
83 | /// Represents an error when [`ProgramHeaders`] fails to enumerate an ELF header.
84 | #[derive(Debug, Error)]
85 | pub enum ProgramHeaderError {
86 | #[error("couldn't read 56 bytes at offset {0}")]
87 | ReadFailed(u64, #[source] std::io::Error),
88 | }
89 |
--------------------------------------------------------------------------------
/gui/ui/about.slint:
--------------------------------------------------------------------------------
1 | import { AboutSlint, VerticalBox, StandardButton } from "std-widgets.slint";
2 | import { TabBar, TabContainer } from "@root/widgets/tab.slint";
3 |
4 | component Obliteration {
5 | VerticalBox {
6 | Image {
7 | source: @image-url("@root/assets/logo.png");
8 | }
9 |
10 | Text {
11 | text: "Obliteration is a free and open-source software to run PlayStation 4 system software on PC.";
12 | wrap: word-wrap;
13 | }
14 | }
15 | }
16 |
17 | component Slint {
18 | AboutSlint { }
19 | }
20 |
21 | export component AboutWindow inherits Dialog {
22 | title: "About";
23 | width: 500px;
24 | height: 245px;
25 |
26 | TabContainer {
27 | tab := TabBar {
28 | tabs: [
29 | { text: "Obliteration", icon: @image-url("@root/assets/icon.png") },
30 | {
31 | text: "Slint",
32 | icon: @image-url("@root/assets/slint-logo-small-dark.svg"),
33 | colorize-icon: true
34 | }
35 | ];
36 | }
37 |
38 | if tab.current-page == 0: Obliteration { }
39 |
40 | if tab.current-page == 1: Slint { }
41 | }
42 |
43 | StandardButton {
44 | kind: close;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/gui/ui/assets/cpu-64-bit.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/ui/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/obhq/obliteration/5842d0ff44ef5ddf6848cf01f239ef754b74102e/gui/ui/assets/icon.png
--------------------------------------------------------------------------------
/gui/ui/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/obhq/obliteration/5842d0ff44ef5ddf6848cf01f239ef754b74102e/gui/ui/assets/logo.png
--------------------------------------------------------------------------------
/gui/ui/assets/monitor.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/ui/assets/slint-logo-small-dark.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/gui/ui/assets/sony-playstation.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/ui/close-octagon-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/ui/debug.slint:
--------------------------------------------------------------------------------
1 | import { VerticalBox } from "std-widgets.slint";
2 |
3 | export component WaitForDebugger inherits Window {
4 | in property address;
5 |
6 | title: "Obliteration";
7 | icon: @image-url("@root/assets/icon.png");
8 | width: 400px;
9 | height: 50px;
10 |
11 | VerticalBox {
12 | alignment: center;
13 |
14 | Text {
15 | text: "Waiting for debugger at \{address}.";
16 | horizontal-alignment: center;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/gui/ui/error.slint:
--------------------------------------------------------------------------------
1 | import { Palette, HorizontalBox, StandardButton } from "std-widgets.slint";
2 |
3 | component Content inherits Rectangle {
4 | in property message;
5 |
6 | background: Palette.background;
7 |
8 | HorizontalBox {
9 | VerticalLayout {
10 | alignment: start;
11 |
12 | Image {
13 | source: @image-url("close-octagon-outline.svg");
14 | colorize: Palette.foreground;
15 | width: 50px;
16 | height: 50px;
17 | }
18 | }
19 |
20 | Text {
21 | text: message;
22 | wrap: word-wrap;
23 | }
24 | }
25 | }
26 |
27 | component ActionBar inherits Rectangle {
28 | callback close();
29 |
30 | background: Palette.alternate-background;
31 |
32 | HorizontalBox {
33 | alignment: end;
34 |
35 | StandardButton {
36 | kind: StandardButtonKind.close;
37 | clicked => {
38 | close();
39 | }
40 | }
41 | }
42 | }
43 |
44 | export component ErrorWindow inherits Window {
45 | in property message;
46 |
47 | pure callback close();
48 |
49 | title: "Obliteration";
50 | icon: @image-url("@root/assets/icon.png");
51 | min-width: 400px;
52 | preferred-width: 400px; // Force word-wrap instead of expand the window.
53 |
54 | VerticalLayout {
55 | Content {
56 | message: message;
57 | vertical-stretch: 1;
58 | }
59 |
60 | ActionBar {
61 | close => {
62 | close();
63 | }
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/gui/ui/lib.slint:
--------------------------------------------------------------------------------
1 | export { AboutWindow } from "about.slint";
2 | export { WaitForDebugger } from "debug.slint";
3 | export { ErrorWindow } from "error.slint";
4 | export { MainWindow } from "main.slint";
5 | export { NewProfile } from "new-profile.slint";
6 | export { SettingsWindow } from "settings.slint";
7 | export { InstallFirmware, SetupWizard } from "setup.slint";
8 |
--------------------------------------------------------------------------------
/gui/ui/main/display.slint:
--------------------------------------------------------------------------------
1 | import { ComboBox, HorizontalBox, VerticalBox, GroupBox } from "std-widgets.slint";
2 |
3 | export component DisplayTab {
4 | in property <[string]> devices;
5 | in-out property selected-device;
6 | in property <[string]> resolutions;
7 | in-out property selected-resolution;
8 |
9 | VerticalBox {
10 | padding-top: 0;
11 | alignment: start;
12 |
13 | HorizontalBox {
14 | padding: 0;
15 |
16 | GroupBox {
17 | title: "Device";
18 | width: 50%;
19 | ComboBox {
20 | model: root.devices;
21 | current-index <=> root.selected-device;
22 | }
23 | }
24 |
25 | GroupBox {
26 | title: "Resolution";
27 | ComboBox {
28 | model: root.resolutions;
29 | current-index <=> root.selected-resolution;
30 | }
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/gui/ui/main/idps.slint:
--------------------------------------------------------------------------------
1 | export component IdpsTab { }
2 |
--------------------------------------------------------------------------------
/gui/ui/main/save.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/ui/main/start.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/ui/new-profile.slint:
--------------------------------------------------------------------------------
1 | import { StandardButton, GroupBox, LineEdit } from "std-widgets.slint";
2 |
3 | export component NewProfile inherits Dialog {
4 | out property name;
5 |
6 | title: "New Profile";
7 | min-width: 400px;
8 |
9 | VerticalLayout {
10 | alignment: start;
11 |
12 | GroupBox {
13 | title: "Name";
14 |
15 | LineEdit {
16 | text <=> root.name;
17 | }
18 | }
19 | }
20 |
21 | StandardButton {
22 | kind: ok;
23 | }
24 |
25 | StandardButton {
26 | kind: cancel;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/gui/ui/settings.slint:
--------------------------------------------------------------------------------
1 | import { StandardButton } from "std-widgets.slint";
2 | import { TabBar, TabContainer } from "@root/widgets/tab.slint";
3 | import { GraphicsTab } from "settings/graphics.slint";
4 |
5 | export component SettingsWindow inherits Dialog {
6 | in property graphics-debug-layer-name: "VK_LAYER_KHRONOS_validation";
7 | in-out property graphics-debug-layer-checked;
8 |
9 | title: "Settings";
10 |
11 | TabContainer {
12 | tab := TabBar {
13 | tabs: [
14 | { text: "Graphics", icon: @image-url("@root/assets/monitor.svg"), colorize-icon: true }
15 | ];
16 | }
17 |
18 | if tab.current-page == 0: GraphicsTab {
19 | debug-layer-name: root.graphics-debug-layer-name;
20 | debug-layer-checked <=> root.graphics-debug-layer-checked;
21 | }
22 | }
23 |
24 | StandardButton {
25 | kind: ok;
26 | }
27 |
28 | StandardButton {
29 | kind: cancel;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/gui/ui/settings/graphics.slint:
--------------------------------------------------------------------------------
1 | import { CheckBox, VerticalBox, GroupBox } from "std-widgets.slint";
2 |
3 | export component GraphicsTab {
4 | in property debug-layer-name;
5 | in-out property debug-layer-checked;
6 |
7 | VerticalBox {
8 | padding-top: 0;
9 | padding-bottom: 0;
10 |
11 | GroupBox {
12 | title: "Debug";
13 |
14 | VerticalLayout {
15 | alignment: start;
16 |
17 | CheckBox {
18 | text: "Enable \{debug-layer-name} (restart required)";
19 | checked <=> root.debug-layer-checked;
20 | }
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/gui/ui/setup/conclusion.slint:
--------------------------------------------------------------------------------
1 | import { VerticalBox } from "std-widgets.slint";
2 | import { Header } from "header.slint";
3 |
4 | export component Conclusion {
5 | VerticalBox {
6 | Header {
7 | title: "Setup complete";
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/gui/ui/setup/firmware.slint:
--------------------------------------------------------------------------------
1 | import { VerticalBox, HorizontalBox, LineEdit, Button, Palette, ProgressIndicator } from "std-widgets.slint";
2 | import { Header } from "header.slint";
3 |
4 | export component Firmware {
5 | in-out property firmware-dump <=> input.text;
6 |
7 | pure callback browse();
8 |
9 | VerticalBox {
10 | Header {
11 | title: "Install Firmware";
12 | }
13 |
14 | Text {
15 | text: "Select a firmware dump that you got from Firmware Dumper.";
16 | }
17 |
18 | HorizontalBox {
19 | padding: 0;
20 |
21 | input := LineEdit {
22 | placeholder-text: "Path to a firmware dump";
23 | }
24 |
25 | Button {
26 | text: "...";
27 | clicked => {
28 | browse();
29 | }
30 | }
31 | }
32 |
33 | Rectangle { }
34 | }
35 | }
36 |
37 | export component InstallFirmware inherits Window {
38 | in property status;
39 | in property progress;
40 |
41 | title: "Installing Firmware";
42 | icon: @image-url("@root/assets/icon.png");
43 | min-width: 400px;
44 | preferred-width: 400px;
45 | min-height: 100px;
46 | preferred-height: 100px;
47 |
48 | VerticalBox {
49 | alignment: center;
50 |
51 | Text {
52 | text: "Installing firmware, please wait.";
53 | horizontal-alignment: center;
54 | }
55 |
56 | ProgressIndicator {
57 | progress: progress;
58 | }
59 |
60 | Text {
61 | text: status;
62 | horizontal-alignment: center;
63 | wrap: no-wrap;
64 | overflow: elide;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/gui/ui/setup/header.slint:
--------------------------------------------------------------------------------
1 | import { VerticalBox } from "std-widgets.slint";
2 |
3 | export component Header {
4 | in property title;
5 |
6 | VerticalBox {
7 | padding: 0;
8 |
9 | Text {
10 | text: root.title;
11 | font-size: 30px;
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/gui/ui/setup/intro.slint:
--------------------------------------------------------------------------------
1 | import { VerticalBox, Button } from "std-widgets.slint";
2 | import { Header } from "header.slint";
3 |
4 | export component Intro {
5 | pure callback get-dumper();
6 |
7 | VerticalBox {
8 | Header {
9 | title: "Introduction";
10 | }
11 |
12 | Text {
13 | text: "This wizard will help you setup Obliteration. To ensure you're ready, make sure you have a firmware dumped from your PlayStation 4 using the Firmware Dumper.";
14 | wrap: word-wrap;
15 | }
16 |
17 | VerticalLayout {
18 | vertical-stretch: 1;
19 | alignment: center;
20 |
21 | HorizontalLayout {
22 | alignment: center;
23 |
24 | Button {
25 | text: "Get Firmware Dumper";
26 | clicked => {
27 | get-dumper();
28 | }
29 | }
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/gui/ui/setup/nav.slint:
--------------------------------------------------------------------------------
1 | import { HorizontalBox, Button, StandardButton, Palette } from "std-widgets.slint";
2 |
3 | export component NavBar inherits Rectangle {
4 | in property back-text;
5 | in property back-enabled;
6 | in property next-text;
7 |
8 | callback back-clicked <=> back.clicked;
9 | callback next-clicked <=> next.clicked;
10 | pure callback cancel();
11 |
12 | background: Palette.alternate-background;
13 |
14 | HorizontalBox {
15 | alignment: end;
16 |
17 | back := Button {
18 | text: root.back-text;
19 | enabled: root.back-enabled;
20 | }
21 |
22 | next := Button {
23 | text: root.next-text;
24 | }
25 |
26 | StandardButton {
27 | kind: StandardButtonKind.cancel;
28 | clicked => {
29 | cancel();
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/gui/ui/setup/root.slint:
--------------------------------------------------------------------------------
1 | import { VerticalBox, HorizontalBox, LineEdit, Button } from "std-widgets.slint";
2 | import { Header } from "header.slint";
3 |
4 | export component DataRoot {
5 | in-out property path <=> input.text;
6 |
7 | callback browse();
8 |
9 | VerticalBox {
10 | Header {
11 | title: "Data Location";
12 | }
13 |
14 | Text {
15 | text: "Select a directory to store Obliteration data. This directory will be used to store everything, including firmware and games. If you have data from the previous usage you can reuse those data.";
16 | wrap: word-wrap;
17 | }
18 |
19 | HorizontalBox {
20 | padding: 0;
21 |
22 | input := LineEdit {
23 | placeholder-text: "Path to a directory";
24 | }
25 |
26 | Button {
27 | text: "...";
28 | clicked => {
29 | browse();
30 | }
31 | }
32 | }
33 |
34 | Rectangle { }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/gui/ui/widgets/tab.slint:
--------------------------------------------------------------------------------
1 | import { HorizontalBox, Palette } from "std-widgets.slint";
2 |
3 | component Tab {
4 | in property text;
5 | in property icon;
6 | in property colorize-icon;
7 | in property selected;
8 |
9 | callback clicked <=> touch.clicked;
10 |
11 | max-height: l.preferred-height;
12 |
13 | states [
14 | pressed when touch.pressed && !root.selected: {
15 | state.opacity: 0.8;
16 | }
17 | hover when touch.has-hover && !root.selected: {
18 | state.opacity: 0.6;
19 | }
20 | selected when root.selected: {
21 | state.opacity: 1;
22 | }
23 | ]
24 |
25 | state := Rectangle {
26 | opacity: 0;
27 | background: Palette.background.darker(0.1);
28 | border-top-left-radius: 4px;
29 | border-top-right-radius: 4px;
30 |
31 | animate opacity { duration: 150ms; }
32 | }
33 |
34 | l := HorizontalBox {
35 | alignment: center;
36 |
37 | Image {
38 | source: root.icon;
39 | width: 15px;
40 | colorize: root.colorize-icon ? Palette.control-foreground : transparent;
41 | }
42 |
43 | Text {
44 | text: root.text;
45 | }
46 | }
47 |
48 | touch := TouchArea {
49 | width: 100%;
50 | height: 100%;
51 | }
52 | }
53 |
54 | export component TabContainer {
55 | Rectangle {
56 | background: Palette.background.darker(0.1);
57 | border-width: 1px;
58 | border-color: Palette.border;
59 | border-radius: 4px;
60 |
61 | VerticalLayout {
62 | padding: 1px;
63 |
64 | @children
65 | }
66 | }
67 | }
68 |
69 | export component TabBar {
70 | in property <[{text: string, icon: image, colorize-icon: bool}]> tabs;
71 | out property current-page;
72 |
73 | Rectangle {
74 | background: Palette.border;
75 | border-top-left-radius: 4px;
76 | border-top-right-radius: 4px;
77 | }
78 |
79 | HorizontalLayout {
80 | for tab[index] in root.tabs: Tab {
81 | text: tab.text;
82 | icon: tab.icon;
83 | colorize-icon: tab.colorize-icon;
84 | selected: index == root.current-page;
85 | clicked => {
86 | root.current-page = index;
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/kernel/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [target.aarch64-unknown-none-softfloat]
2 | rustflags = ["-C", "relocation-model=pic"]
3 |
--------------------------------------------------------------------------------
/kernel/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "obkrnl"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [dependencies]
7 | bitfield-struct = "0.10.1"
8 | bitflag = { path = "../lib/bitflag" }
9 | config = { path = "../config" }
10 | hashbrown = "0.14.5"
11 | humansize = { version = "2.1.3", features = ["no_alloc"] }
12 | krt = { path = "../lib/krt" }
13 | macros = { path = "../macros" }
14 | talc = { version = "4.4.1", default-features = false }
15 | thiserror = { version = "2.0.12", default-features = false }
16 |
17 | [target.'cfg(target_arch = "x86_64")'.dependencies]
18 | x86-64 = { path = "../lib/x86-64" }
19 |
--------------------------------------------------------------------------------
/kernel/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | Permission is hereby granted, free of charge, to any person obtaining a copy
2 | of this software and associated documentation files (the "Software"), to deal
3 | in the Software without restriction, including without limitation the rights
4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5 | copies of the Software, and to permit persons to whom the Software is
6 | furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all
9 | copies or substantial portions of the Software.
10 |
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 | SOFTWARE.
18 |
--------------------------------------------------------------------------------
/kernel/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | let target = std::env::var("TARGET").unwrap();
3 |
4 | match target.as_str() {
5 | "aarch64-unknown-none-softfloat" => {
6 | println!("cargo::rustc-link-arg-bins=--pie");
7 | println!("cargo::rustc-link-arg-bins=-zmax-page-size=0x4000");
8 | }
9 | "x86_64-unknown-none" => {
10 | println!("cargo::rustc-link-arg-bins=-zmax-page-size=0x1000");
11 | }
12 | _ => {}
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/kernel/src/aarch64.rs:
--------------------------------------------------------------------------------
1 | use alloc::string::String;
2 | use alloc::sync::Arc;
3 |
4 | pub fn identify_cpu() -> CpuInfo {
5 | todo!()
6 | }
7 |
8 | pub unsafe fn setup_main_cpu(cpu: CpuInfo) -> Arc {
9 | todo!()
10 | }
11 |
12 | /// Contains information for CPU on current machine.
13 | pub struct CpuInfo {
14 | pub cpu_vendor: String,
15 | }
16 |
17 | /// Contains architecture-specific configurations obtained from [`setup_main_cpu()`].
18 | pub struct ArchConfig {
19 | pub secondary_start: &'static [u8],
20 | }
21 |
--------------------------------------------------------------------------------
/kernel/src/config/aarch64.rs:
--------------------------------------------------------------------------------
1 | pub const PAGE_SHIFT: usize = 14; // 16K
2 |
--------------------------------------------------------------------------------
/kernel/src/config/dipsw.rs:
--------------------------------------------------------------------------------
1 | /// Identifier of a Dipsw.
2 | #[derive(Clone, Copy, PartialEq, Eq)]
3 | pub enum Dipsw {
4 | Unk0 = 0,
5 | DisabledKaslr = 12,
6 | Unk16 = 16,
7 | Unk17 = 17,
8 | Unk24 = 24,
9 | Unk97 = 97,
10 | Unk140 = 140,
11 | Unk146 = 146,
12 | }
13 |
--------------------------------------------------------------------------------
/kernel/src/config/x86_64.rs:
--------------------------------------------------------------------------------
1 | // We use 4K 4-Level Paging here. You may wonder about this because it seems like page size on the
2 | // PS4 is 16K. The truth is the PS4 emulate the 16K page size with 4K pages. You can check this by
3 | // yourself by looking at acpi_install_wakeup_handler() function on the PS4 kernel and compare it
4 | // with FreeBSD version. No idea why the PS4 choose to emulate 16K page.
5 | pub const PAGE_SHIFT: usize = 12;
6 |
--------------------------------------------------------------------------------
/kernel/src/context/aarch64.rs:
--------------------------------------------------------------------------------
1 | use super::Base;
2 | use crate::arch::ArchConfig;
3 | use crate::proc::Thread;
4 | use core::marker::PhantomPinned;
5 | use core::pin::Pin;
6 |
7 | /// Extended [Base] for AArch64.
8 | #[repr(C)]
9 | pub(super) struct Context {
10 | pub base: Base, // Must be first field.
11 | phantom: PhantomPinned,
12 | }
13 |
14 | impl Context {
15 | pub fn new(base: Base, arch: &ArchConfig) -> Self {
16 | Self {
17 | base,
18 | phantom: PhantomPinned,
19 | }
20 | }
21 |
22 | pub unsafe fn activate(self: Pin<&mut Self>) {
23 | todo!();
24 | }
25 |
26 | pub unsafe fn load_static_ptr() -> *const T {
27 | todo!()
28 | }
29 |
30 | pub unsafe fn load_ptr() -> *const T {
31 | todo!()
32 | }
33 |
34 | pub unsafe fn load_volatile_usize() -> usize {
35 | todo!()
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/kernel/src/context/arc.rs:
--------------------------------------------------------------------------------
1 | use alloc::sync::Arc;
2 | use core::ops::Deref;
3 |
4 | /// Provides an access to the value of [Arc](alloc::sync::Arc) on [Context](super::Context) without
5 | /// cloning a reference.
6 | ///
7 | /// We need this type because return a reference from [Context](super::Context) require static
8 | /// lifetime, which allow the caller to store it at a global level. Once the value is destroyed that
9 | /// reference will be invalid. We can solve this by return a cloned [Arc](alloc::sync::Arc) but most
10 | /// of the time the caller just want a temporary access to the value, which mean the increment and
11 | /// decrement of a reference is not needed so we invent this type to solve this problem.
12 | ///
13 | /// This type work by making itself not [Send] and [Sync], which prevent the caller from storing it
14 | /// at a global level.
15 | #[repr(transparent)]
16 | pub struct BorrowedArc(*const T); // A pointer make this type automatically !Send and !Sync.
17 |
18 | impl BorrowedArc {
19 | /// # Safety
20 | /// `v` must be owned by [Arc](alloc::sync::Arc) if not null.
21 | pub(super) const unsafe fn new(v: *const T) -> Option {
22 | if v.is_null() { None } else { Some(Self(v)) }
23 | }
24 |
25 | /// # Safety
26 | /// `v` must be owned by [Arc](alloc::sync::Arc).
27 | pub(super) const unsafe fn from_non_null(v: *const T) -> Self {
28 | Self(v)
29 | }
30 |
31 | /// Note that this is an associated function, which means that you have to call it as
32 | /// `BorrowedArc::as_ptr(v)` instead of `v.as_ptr()`. This is so that there is no conflict with
33 | /// a method on the inner type.
34 | pub fn as_ptr(this: &Self) -> *const T {
35 | this.0
36 | }
37 |
38 | pub fn into_owned(self) -> Arc {
39 | // SAFETY: This is safe because the requirement of new() and from_non_null().
40 | unsafe { Arc::increment_strong_count(self.0) };
41 | unsafe { Arc::from_raw(self.0) }
42 | }
43 | }
44 |
45 | impl Clone for BorrowedArc {
46 | fn clone(&self) -> Self {
47 | *self
48 | }
49 | }
50 |
51 | impl Copy for BorrowedArc {}
52 |
53 | impl Deref for BorrowedArc {
54 | type Target = T;
55 |
56 | fn deref(&self) -> &Self::Target {
57 | unsafe { &*self.0 }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/kernel/src/context/local.rs:
--------------------------------------------------------------------------------
1 | use super::{PinnedContext, config, pin_cpu};
2 | use alloc::vec::Vec;
3 | use core::ops::Deref;
4 |
5 | /// Encapsulates per-CPU value.
6 | ///
7 | /// `T` need to implement [Send] for this type to implement [Send] and [Sync] because its value
8 | /// created by one thread then access from another thread.
9 | ///
10 | /// Use [RefCell](core::cell::RefCell) if you need interior mutability but it will make that value
11 | /// not safe to access from any interrupt handler. You can't use mutex here because once the thread
12 | /// is pinned it cannot go to sleep.
13 | pub struct CpuLocal(Vec);
14 |
15 | impl CpuLocal {
16 | pub fn new(mut f: impl FnMut(usize) -> T) -> Self {
17 | let len = config().max_cpu().get();
18 | let mut vec = Vec::with_capacity(len);
19 |
20 | for i in 0..len {
21 | vec.push(f(i));
22 | }
23 |
24 | Self(vec)
25 | }
26 |
27 | /// The calling thread cannot go to sleep until the returned [`CpuLock`] is dropped. Attempt to
28 | /// call any function that can put the thread to sleep will be panic.
29 | pub fn lock(&self) -> CpuLock {
30 | let pin = pin_cpu();
31 | let val = &self.0[unsafe { pin.cpu() }];
32 |
33 | CpuLock { val, pin }
34 | }
35 | }
36 |
37 | unsafe impl Send for CpuLocal {}
38 | unsafe impl Sync for CpuLocal {}
39 |
40 | /// RAII struct to access per-CPU value in [`CpuLocal`].
41 | pub struct CpuLock<'a, T> {
42 | val: &'a T,
43 | #[allow(dead_code)]
44 | pin: PinnedContext, // Must be dropped last.
45 | }
46 |
47 | impl Deref for CpuLock<'_, T> {
48 | type Target = T;
49 |
50 | fn deref(&self) -> &Self::Target {
51 | self.val
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/kernel/src/event/mod.rs:
--------------------------------------------------------------------------------
1 | pub use self::ty::*;
2 |
3 | use crate::lock::{Mutex, MutexGuard};
4 | use alloc::collections::btree_map::BTreeMap;
5 |
6 | mod ty;
7 |
8 | /// Encapsulate a set of [`Event`].
9 | ///
10 | /// Usually there are only one [`EventSet`] per subsystem. The purpose of this struct is to prevent
11 | /// race condition during subscribing and triggering multiple events. In other words, this struct
12 | /// provide atomicity for subscription to multiple events in the set.
13 | pub struct EventSet(Mutex); // TODO: Change to RwLock.
14 |
15 | impl EventSet {
16 | pub fn trigger(&self) -> EventTrigger {
17 | EventTrigger(self.0.lock())
18 | }
19 | }
20 |
21 | impl Default for EventSet {
22 | fn default() -> Self {
23 | Self(Mutex::default())
24 | }
25 | }
26 |
27 | /// Implementation of `eventhandler_list` structure.
28 | ///
29 | /// Our implementation is different from PS4 version to make it idomatic to Rust.
30 | pub struct Event {
31 | subscribers: BTreeMap<(u32, u64), T::Wrapper>, // el_entries
32 | }
33 |
34 | impl Default for Event {
35 | fn default() -> Self {
36 | Self {
37 | subscribers: BTreeMap::new(),
38 | }
39 | }
40 | }
41 |
42 | /// Struct to trigger one or more events.
43 | ///
44 | /// It is guarantee that no other handler in the current set will get registered until this struct
45 | /// has been dropped.
46 | pub struct EventTrigger<'a, S>(MutexGuard<'a, S>);
47 |
48 | impl EventTrigger<'_, S> {
49 | pub fn select(&mut self, event: E) -> impl Iterator-
50 | where
51 | E: FnOnce(&S) -> &Event,
52 | T: EventType,
53 | {
54 | event(&self.0).subscribers.values()
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/kernel/src/event/ty.rs:
--------------------------------------------------------------------------------
1 | use crate::subsystem::Subsystem;
2 | use alloc::boxed::Box;
3 | use alloc::sync::Arc;
4 |
5 | /// Type of an event.
6 | pub trait EventType: 'static {
7 | type Handler;
8 | type Wrapper: Send + Sync + 'static;
9 | }
10 |
11 | impl EventType for fn(&A) {
12 | type Handler = fn(&Arc
, &A);
13 | type Wrapper = Box;
14 | }
15 |
16 | impl EventType for fn(&mut A) {
17 | type Handler = fn(&Arc, &mut A);
18 | type Wrapper = Box;
19 | }
20 |
--------------------------------------------------------------------------------
/kernel/src/imgact/mod.rs:
--------------------------------------------------------------------------------
1 | pub use self::ps4::*;
2 |
3 | mod ps4;
4 |
--------------------------------------------------------------------------------
/kernel/src/imgact/ps4/abi.rs:
--------------------------------------------------------------------------------
1 | use crate::proc::ProcAbi;
2 |
3 | /// Implementation of [`ProcAbi`] for PS4 processes.
4 | pub struct Ps4Abi;
5 |
6 | impl ProcAbi for Ps4Abi {
7 | fn syscall_handler(&self) {
8 | todo!()
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/kernel/src/imgact/ps4/mod.rs:
--------------------------------------------------------------------------------
1 | pub use self::abi::*;
2 |
3 | mod abi;
4 |
--------------------------------------------------------------------------------
/kernel/src/imgfmt/elf/mod.rs:
--------------------------------------------------------------------------------
1 | use core::ops::Deref;
2 |
3 | /// Single ELF note.
4 | #[repr(C)]
5 | pub struct Note {
6 | hdr: NoteHdr,
7 | name: [u8; N],
8 | desc: NoteDesc,
9 | }
10 |
11 | impl Note {
12 | /// # Safety
13 | /// `name` must contains NUL as a last element.
14 | pub const unsafe fn new(name: [u8; N], ty: u32, desc: [u8; D]) -> Self {
15 | Self {
16 | hdr: NoteHdr {
17 | name_len: N as _,
18 | desc_len: D as _,
19 | ty,
20 | },
21 | name,
22 | desc: NoteDesc(desc),
23 | }
24 | }
25 | }
26 |
27 | /// Implementation of `Elf64_Nhdr` and `Elf32_Nhdr` structure.
28 | #[repr(C)]
29 | pub struct NoteHdr {
30 | /// n_namesz.
31 | pub name_len: u32,
32 | /// n_descsz.
33 | pub desc_len: u32,
34 | /// n_type.
35 | pub ty: u32,
36 | }
37 |
38 | /// Note description.
39 | #[repr(C, align(4))]
40 | pub struct NoteDesc([u8; L]);
41 |
42 | impl Deref for NoteDesc {
43 | type Target = [u8; L];
44 |
45 | fn deref(&self) -> &Self::Target {
46 | &self.0
47 | }
48 | }
49 |
50 | #[cfg(test)]
51 | mod tests {
52 | use super::*;
53 | use core::mem::offset_of;
54 |
55 | #[test]
56 | fn note() {
57 | assert_eq!(offset_of!(Note::<3, 1>, name), 12);
58 | assert_eq!(offset_of!(Note::<3, 1>, desc), 16);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/kernel/src/imgfmt/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod elf;
2 |
--------------------------------------------------------------------------------
/kernel/src/lock/gutex/guard.rs:
--------------------------------------------------------------------------------
1 | use super::GroupGuard;
2 | use core::fmt::{Display, Formatter};
3 | use core::ops::{Deref, DerefMut};
4 |
5 | /// RAII structure used to release the shared read access of a lock when dropped.
6 | pub struct GutexRead<'a, T> {
7 | #[allow(dead_code)] // active and value fields is protected by this lock.
8 | lock: GroupGuard<'a>,
9 | active: *mut usize,
10 | value: *const T,
11 | }
12 |
13 | impl<'a, T> GutexRead<'a, T> {
14 | /// # Safety
15 | /// `active` and `value` must be protected by `lock`.
16 | pub(super) fn new(lock: GroupGuard<'a>, active: *mut usize, value: *const T) -> Self {
17 | Self {
18 | lock,
19 | active,
20 | value,
21 | }
22 | }
23 | }
24 |
25 | impl Drop for GutexRead<'_, T> {
26 | fn drop(&mut self) {
27 | unsafe { *self.active -= 1 };
28 | }
29 | }
30 |
31 | impl Deref for GutexRead<'_, T> {
32 | type Target = T;
33 |
34 | fn deref(&self) -> &Self::Target {
35 | unsafe { &*self.value }
36 | }
37 | }
38 |
39 | impl Display for GutexRead<'_, T> {
40 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
41 | self.deref().fmt(f)
42 | }
43 | }
44 |
45 | unsafe impl Sync for GutexRead<'_, T> {}
46 |
47 | /// RAII structure used to release the exclusive write access of a lock when dropped.
48 | pub struct GutexWrite<'a, T> {
49 | #[allow(dead_code)] // active and value fields is protected by this lock.
50 | lock: GroupGuard<'a>,
51 | active: *mut usize,
52 | value: *mut T,
53 | }
54 |
55 | impl<'a, T> GutexWrite<'a, T> {
56 | /// # Safety
57 | /// `active` and `value` must be protected by `lock`.
58 | pub(super) unsafe fn new(lock: GroupGuard<'a>, active: *mut usize, value: *mut T) -> Self {
59 | Self {
60 | active,
61 | value,
62 | lock,
63 | }
64 | }
65 | }
66 |
67 | impl Drop for GutexWrite<'_, T> {
68 | fn drop(&mut self) {
69 | unsafe { *self.active = 0 };
70 | }
71 | }
72 |
73 | impl Deref for GutexWrite<'_, T> {
74 | type Target = T;
75 |
76 | fn deref(&self) -> &Self::Target {
77 | unsafe { &*self.value }
78 | }
79 | }
80 |
81 | impl DerefMut for GutexWrite<'_, T> {
82 | fn deref_mut(&mut self) -> &mut Self::Target {
83 | unsafe { &mut *self.value }
84 | }
85 | }
86 |
87 | impl Display for GutexWrite<'_, T> {
88 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
89 | self.deref().fmt(f)
90 | }
91 | }
92 |
93 | unsafe impl Sync for GutexWrite<'_, T> {}
94 |
--------------------------------------------------------------------------------
/kernel/src/lock/mod.rs:
--------------------------------------------------------------------------------
1 | pub use self::gutex::*;
2 | pub use self::mutex::*;
3 |
4 | mod gutex;
5 | mod mutex;
6 |
7 | const MTX_UNOWNED: usize = 4;
8 |
--------------------------------------------------------------------------------
/kernel/src/proc/abi.rs:
--------------------------------------------------------------------------------
1 | /// Implementation of `sysentvec` structure.
2 | pub trait ProcAbi: Send + Sync {
3 | fn syscall_handler(&self);
4 | }
5 |
--------------------------------------------------------------------------------
/kernel/src/proc/cell.rs:
--------------------------------------------------------------------------------
1 | use super::Thread;
2 | use crate::context::{BorrowedArc, current_thread};
3 | use core::cell::Cell;
4 |
5 | /// Encapsulates a field of [Thread] that can only be accessed by the CPU that currently executing
6 | /// the thread.
7 | ///
8 | /// # Context safety
9 | /// [`Default`] implementation of this type does not require a CPU context as long as implementation
10 | /// on `T` does not.
11 | #[derive(Default)]
12 | pub struct PrivateCell(T);
13 |
14 | impl PrivateCell {
15 | fn validate(&self, owner: &Thread) {
16 | // This check will optimized out for most of the time due to the implementation of
17 | // current_thread() use "pure" + "nomem" on inline assembly.
18 | let current = current_thread();
19 |
20 | if !core::ptr::eq(BorrowedArc::as_ptr(¤t), owner) {
21 | panic!("accessing a private cell from the other thread is not supported");
22 | }
23 | }
24 | }
25 |
26 | impl PrivateCell> {
27 | /// See [set] for a safe wrapper.
28 | ///
29 | /// # Safety
30 | /// `owner` must be an owner of this field.
31 | ///
32 | /// # Panics
33 | /// If `owner` is not the current thread.
34 | pub unsafe fn set(&self, owner: &Thread, v: T) {
35 | self.validate(owner);
36 | self.0.set(v);
37 | }
38 | }
39 |
40 | impl PrivateCell> {
41 | /// See [get] for a safe wrapper.
42 | ///
43 | /// # Safety
44 | /// `owner` must be an owner of this field.
45 | ///
46 | /// # Panics
47 | /// If `owner` is not the current thread.
48 | pub unsafe fn get(&self, owner: &Thread) -> T {
49 | self.validate(owner);
50 | self.0.get()
51 | }
52 | }
53 |
54 | unsafe impl Sync for PrivateCell {}
55 |
56 | /// Safe wrapper of [PrivateCell::set()].
57 | macro_rules! set {
58 | ($t:ident, $f:ident, $v:expr) => {
59 | // SAFETY: $t is an owner of $f.
60 | unsafe { $t.$f.set($t, $v) }
61 | };
62 | }
63 |
64 | /// Safe wrapper of [PrivateCell::get()].
65 | macro_rules! get {
66 | ($t:ident, $f:ident) => {
67 | // SAFETY: $t is an owner of $f.
68 | unsafe { $t.$f.get($t) }
69 | };
70 | }
71 |
72 | pub(super) use get;
73 | pub(super) use set;
74 |
--------------------------------------------------------------------------------
/kernel/src/proc/pid.rs:
--------------------------------------------------------------------------------
1 | use core::borrow::Borrow;
2 | use core::ffi::c_int;
3 |
4 | /// Unique identifier of a process.
5 | #[repr(transparent)]
6 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7 | pub struct Pid(c_int);
8 |
9 | impl Pid {
10 | pub const KERNEL: Self = Self(0);
11 | pub const IDLE: Self = Self(10);
12 |
13 | /// Returns [`None`] if `v` is negative.
14 | pub const fn new(v: c_int) -> Option {
15 | if v >= 0 { Some(Self(v)) } else { None }
16 | }
17 | }
18 |
19 | impl Borrow for Pid {
20 | fn borrow(&self) -> &c_int {
21 | &self.0
22 | }
23 | }
24 |
25 | impl PartialEq for Pid {
26 | fn eq(&self, other: &c_int) -> bool {
27 | self.0 == *other
28 | }
29 | }
30 |
31 | impl PartialEq for c_int {
32 | fn eq(&self, other: &Pid) -> bool {
33 | *self == other.0
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/kernel/src/proc/process.rs:
--------------------------------------------------------------------------------
1 | use super::{ProcAbi, ProcEvents};
2 | use crate::event::EventSet;
3 | use alloc::sync::Arc;
4 | use core::sync::atomic::{AtomicUsize, Ordering};
5 |
6 | /// Implementation of `proc` structure.
7 | pub struct Proc {
8 | abi: Arc, // p_sysent
9 | pager: AtomicUsize,
10 | }
11 |
12 | impl Proc {
13 | /// See `proc_init` and `proc_ctor` on the Orbis for a reference.
14 | ///
15 | /// # Reference offsets
16 | /// | Version | Offset |
17 | /// |---------|---------------------|
18 | /// |PS4 11.00|0x375970 and 0x3755D0|
19 | pub fn new(abi: Arc, events: &Arc>) -> Arc {
20 | let mut proc = Self {
21 | abi,
22 | pager: AtomicUsize::new(0),
23 | };
24 |
25 | // Trigger process_init event.
26 | let mut et = events.trigger();
27 |
28 | for h in et.select(|s| &s.process_init) {
29 | h(&mut proc);
30 | }
31 |
32 | // Trigger process_ctor event.
33 | let proc = Arc::new(proc);
34 | let weak = Arc::downgrade(&proc);
35 |
36 | for h in et.select(|s| &s.process_ctor) {
37 | h(&weak);
38 | }
39 |
40 | drop(et);
41 |
42 | todo!()
43 | }
44 |
45 | /// This function does not do anything except initialize the struct memory. It is the caller
46 | /// responsibility to configure the process after this so it have a proper states and trigger
47 | /// necessary events.
48 | ///
49 | /// # Context safety
50 | /// This function does not require a CPU context.
51 | pub fn new_bare(abi: Arc) -> Self {
52 | Self {
53 | abi,
54 | pager: AtomicUsize::new(0),
55 | }
56 | }
57 |
58 | pub fn abi(&self) -> &Arc {
59 | &self.abi
60 | }
61 |
62 | pub fn pager(&self) -> usize {
63 | self.pager.load(Ordering::Relaxed)
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/kernel/src/sched/mod.rs:
--------------------------------------------------------------------------------
1 | pub use self::sleep::*;
2 |
3 | mod sleep;
4 |
--------------------------------------------------------------------------------
/kernel/src/sched/sleep.rs:
--------------------------------------------------------------------------------
1 | use crate::context::current_thread;
2 |
3 | /// See `_sleep` on the PS4 for a reference.
4 | pub fn sleep() {
5 | // Remove current thread from sleep queue.
6 | let td = current_thread();
7 | let addr = td.sleeping_mut();
8 |
9 | if *addr != 0 {
10 | todo!()
11 | }
12 |
13 | todo!()
14 | }
15 |
--------------------------------------------------------------------------------
/kernel/src/signal/mod.rs:
--------------------------------------------------------------------------------
1 | /// Value of *nix signal.
2 | #[derive(Debug, Clone, Copy)]
3 | pub struct Signal(u8);
4 |
5 | impl Signal {
6 | const MAX: u8 = 128; // _SIG_MAXSIG
7 |
8 | /// # Panics
9 | /// If `v` is not a valid signal number.
10 | pub const fn from_bits(v: u8) -> Self {
11 | assert!(v <= Self::MAX);
12 | Self(v)
13 | }
14 |
15 | pub const fn into_bits(self) -> u8 {
16 | self.0
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/kernel/src/subsystem/mod.rs:
--------------------------------------------------------------------------------
1 | /// Subsystem of the kernel.
2 | ///
3 | /// There should be only one instance for each subsystem. Normally it will live forever until the
4 | /// machine is shutdown. That means it is okay to to leak it.
5 | pub trait Subsystem: Send + Sync + 'static {}
6 |
--------------------------------------------------------------------------------
/kernel/src/trap/aarch64.rs:
--------------------------------------------------------------------------------
1 | /// Main entry point for interrupt.
2 | ///
3 | /// This will be called by an inline assembly.
4 | pub extern "C" fn interrupt_handler(_: &mut TrapFrame) {
5 | todo!()
6 | }
7 |
8 | /// Contains states of the interupted program.
9 | #[repr(C)]
10 | pub struct TrapFrame {}
11 |
--------------------------------------------------------------------------------
/kernel/src/trap/mod.rs:
--------------------------------------------------------------------------------
1 | pub use self::arch::*;
2 |
3 | #[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
4 | #[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
5 | mod arch;
6 | mod vm;
7 |
--------------------------------------------------------------------------------
/kernel/src/trap/vm.rs:
--------------------------------------------------------------------------------
1 | use super::TrapFrame;
2 | use config::Vm;
3 |
4 | /// # Interupt safety
5 | /// This function can be called from interupt handler.
6 | pub fn interrupt_handler(_: &Vm, _: &mut TrapFrame) {
7 | // TODO: Implement a virtual device with GDB stub.
8 | todo!()
9 | }
10 |
--------------------------------------------------------------------------------
/kernel/src/trap/x86_64.rs:
--------------------------------------------------------------------------------
1 | use crate::context::current_thread;
2 | use config::BootEnv;
3 | use core::sync::atomic::Ordering;
4 | use krt::boot_env;
5 |
6 | /// Main entry point for interrupt.
7 | ///
8 | /// This will be called by an inline assembly.
9 | ///
10 | /// See `trap` function on the PS4 for a reference.
11 | pub extern "C" fn interrupt_handler(frame: &mut TrapFrame) {
12 | let td = current_thread();
13 |
14 | unsafe { td.active_interrupts().fetch_add(1, Ordering::Relaxed) };
15 |
16 | match frame.num {
17 | TrapNo::Breakpoint => match boot_env() {
18 | BootEnv::Vm(vm) => super::vm::interrupt_handler(vm, frame),
19 | },
20 | }
21 |
22 | unsafe { td.active_interrupts().fetch_sub(1, Ordering::Relaxed) };
23 | }
24 |
25 | /// Main entry point for `syscall` instruction.
26 | ///
27 | /// This will be called by an inline assembly.
28 | ///
29 | /// See `amd64_syscall` function on the PS4 for a reference.
30 | pub extern "C" fn syscall_handler() {
31 | // TODO: Implement pc_cnt.v_syscall increment.
32 | let td = current_thread();
33 | let p = td.proc();
34 |
35 | td.set_profiling_ticks(0);
36 |
37 | // We merge sv_fetch_syscall_args and the code to invoke each syscall handler together.
38 | p.abi().syscall_handler();
39 |
40 | todo!()
41 | }
42 |
43 | /// Predefined interrupt vector number.
44 | #[allow(dead_code)] // Used by inline assembly.
45 | #[repr(u32)]
46 | #[derive(Clone, Copy, PartialEq, Eq)]
47 | pub enum TrapNo {
48 | Breakpoint = 3, // T_BPTFLT
49 | }
50 |
51 | /// Contains states of the interupted program.
52 | #[repr(C)]
53 | pub struct TrapFrame {
54 | pub rdi: usize, // tf_rdi
55 | pub rsi: usize, // tf_rsi
56 | pub rdx: usize, // tf_rdx
57 | pub rcx: usize, // tf_rcx
58 | pub r8: usize, // tf_r8
59 | pub r9: usize, // tf_r9
60 | pub rax: usize, // tf_rax
61 | pub rbx: usize, // tf_rbx
62 | pub rbp: usize, // tf_rbp
63 | pub r10: usize, // tf_r10
64 | pub r11: usize, // tf_r11
65 | pub r12: usize, // tf_r12
66 | pub r13: usize, // tf_r13
67 | pub r14: usize, // tf_r14
68 | pub r15: usize, // tf_r15
69 | pub num: TrapNo, // tf_trapno
70 | pub fs: u16, // tf_fs
71 | pub gs: u16, // tf_gs
72 | }
73 |
--------------------------------------------------------------------------------
/kernel/src/uma/aarch64.rs:
--------------------------------------------------------------------------------
1 | use super::Alloc;
2 | use crate::vm::Vm;
3 |
4 | pub fn small_alloc(vm: &Vm, flags: Alloc) {
5 | todo!()
6 | }
7 |
--------------------------------------------------------------------------------
/kernel/src/uma/boxed.rs:
--------------------------------------------------------------------------------
1 | use core::ops::Deref;
2 |
3 | /// Encapsulates an object allocated from a UMA zone.
4 | pub struct UmaBox(*mut T);
5 |
6 | impl Deref for UmaBox {
7 | type Target = T;
8 |
9 | fn deref(&self) -> &Self::Target {
10 | unsafe { &*self.0 }
11 | }
12 | }
13 |
14 | unsafe impl Send for UmaBox {}
15 | unsafe impl Sync for UmaBox {}
16 |
--------------------------------------------------------------------------------
/kernel/src/uma/bucket.rs:
--------------------------------------------------------------------------------
1 | /// Implementation of `uma_bucket` structure.
2 | #[repr(C)]
3 | pub struct UmaBucket {
4 | len: usize, // ub_cnt
5 | items: I, // ub_bucket
6 | }
7 |
8 | impl UmaBucket {
9 | pub fn len(&self) -> usize {
10 | self.len
11 | }
12 | }
13 |
14 | /// Each item in the [`UmaBucket::items`].
15 | pub struct BucketItem {}
16 |
--------------------------------------------------------------------------------
/kernel/src/uma/slab.rs:
--------------------------------------------------------------------------------
1 | /// Implementation of `uma_slab_head`, `uma_slab` and `uma_slab_refcnt`.
2 | ///
3 | /// We use slightly different mechanism here but has the same memory layout.
4 | #[repr(C)]
5 | pub struct Slab {
6 | free: I, // us_freelist
7 | }
8 |
9 | /// Item in the slab to represents `uma_slab` structure.
10 | #[repr(C)]
11 | pub struct Free {}
12 |
13 | /// Item in the slab to represents `uma_slab_refcnt` structure.
14 | #[repr(C)]
15 | pub struct RcFree {}
16 |
--------------------------------------------------------------------------------
/kernel/src/uma/x86_64.rs:
--------------------------------------------------------------------------------
1 | use super::Alloc;
2 | use crate::vm::Vm;
3 |
4 | /// See `uma_small_alloc` on the Orbis for a reference.
5 | ///
6 | /// # Reference offsets
7 | /// | Version | Offset |
8 | /// |---------|--------|
9 | /// |PS4 11.00|0x22FD70|
10 | pub fn small_alloc(vm: &Vm, flags: Alloc) {
11 | // TODO: There are an increment on an unknown variable on the Orbis.
12 | vm.alloc_page(
13 | None,
14 | // TODO: Refactor this for readability.
15 | ((((u32::from(flags) & 0x100) >> 2) - (u32::from((u32::from(flags) & 0x401) == 1)) + 0x22)
16 | | 0x100)
17 | .into(),
18 | );
19 |
20 | todo!()
21 | }
22 |
--------------------------------------------------------------------------------
/kernel/src/vm/object.rs:
--------------------------------------------------------------------------------
1 | /// Implementation of `vm_object` structure.
2 | pub struct VmObject {
3 | vm: usize,
4 | }
5 |
6 | impl VmObject {
7 | pub fn vm(&self) -> usize {
8 | self.vm
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/kernel/src/vm/page.rs:
--------------------------------------------------------------------------------
1 | /// Implementation of `vm_page` structure.
2 | pub struct VmPage {}
3 |
--------------------------------------------------------------------------------
/kernel/src/vm/stats.rs:
--------------------------------------------------------------------------------
1 | use crate::lock::Gutex;
2 |
3 | /// Contains statistics for a VM.
4 | ///
5 | /// This is a subset of `vmmeter` structure.
6 | pub struct VmStats {
7 | pub free_reserved: usize, // v_free_reserved
8 | pub cache_count: Gutex, // v_cache_count
9 | pub free_count: Gutex, // v_free_count
10 | pub interrupt_free_min: Gutex, // v_interrupt_free_min
11 | }
12 |
--------------------------------------------------------------------------------
/legacy/src/arch/mod.rs:
--------------------------------------------------------------------------------
1 | use crate::errno::EINVAL;
2 | use crate::info;
3 | use crate::process::{PcbFlags, VThread};
4 | use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls};
5 | use std::sync::atomic::AtomicU64;
6 | use std::sync::Arc;
7 |
8 | /// An implementation of machine-dependent services.
9 | pub struct MachDep {
10 | tsc_freq: AtomicU64,
11 | }
12 |
13 | impl MachDep {
14 | const I386_GET_IOPERM: u32 = 3;
15 | const I386_SET_IOPERM: u32 = 4;
16 | const AMD64_SET_FSBASE: u32 = 129;
17 |
18 | // PS4 / PS4 Slim
19 | const TSC_FREQ: u64 = 1_600_000_000;
20 |
21 | // PS4 PRO (Neo) TODO
22 | // const TSC_FREQ: u64 = 2_130_000_000;
23 |
24 | pub fn new(sys: &mut Syscalls) -> Arc {
25 | let mach = Arc::new(Self {
26 | tsc_freq: Self::init_tsc(),
27 | });
28 |
29 | sys.register(165, &mach, Self::sysarch);
30 |
31 | mach
32 | }
33 |
34 | fn sysarch(self: &Arc, td: &Arc, i: &SysIn) -> Result {
35 | let op: u32 = i.args[0].try_into().unwrap();
36 | let parms: *mut u8 = i.args[1].into();
37 | let mut pcb = td.pcb_mut();
38 |
39 | if op < 2 {
40 | return Err(SysErr::Raw(EINVAL));
41 | }
42 |
43 | match op {
44 | Self::I386_GET_IOPERM | Self::I386_SET_IOPERM => todo!("sysarch with op = 3 | 4"),
45 | _ => {}
46 | }
47 |
48 | match op {
49 | Self::AMD64_SET_FSBASE => {
50 | // We can't check if the value within the user space because we are not a real
51 | // kernel.
52 | let v = unsafe { std::ptr::read_unaligned(parms as _) };
53 |
54 | pcb.fsbase = v;
55 | pcb.flags |= PcbFlags::PCB_FULL_IRET;
56 |
57 | info!("FS segment has been changed to {v:#x}.");
58 | }
59 | v => todo!("sysarch with op = {v}"),
60 | }
61 |
62 | Ok(SysOut::ZERO)
63 | }
64 |
65 | pub fn tsc_freq(&self) -> &AtomicU64 {
66 | &self.tsc_freq
67 | }
68 |
69 | fn init_tsc() -> AtomicU64 {
70 | AtomicU64::new(Self::TSC_FREQ)
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/legacy/src/arnd.rs:
--------------------------------------------------------------------------------
1 | use std::sync::Mutex;
2 |
3 | /// An implementation of `arc4rand` on the PS4.
4 | /// TODO: Implement reseed.
5 | pub fn rand_bytes(buf: &mut [u8]) {
6 | ARND.lock().unwrap().rand_bytes(buf)
7 | }
8 |
9 | static ARND: Mutex = Mutex::new(State::new());
10 |
11 | /// Random number generator based on
12 | /// https://github.com/freebsd/freebsd-src/blob/release/9.1.0/sys/libkern/arc4random.c.
13 | #[derive(Debug)]
14 | struct State {
15 | i: u8,
16 | j: u8,
17 | sbox: [u8; 256],
18 | }
19 |
20 | impl State {
21 | const fn new() -> Self {
22 | Self {
23 | i: 0,
24 | j: 0,
25 | sbox: sbox_init(),
26 | }
27 | }
28 |
29 | fn rand_bytes(&mut self, buf: &mut [u8]) {
30 | buf.iter_mut().for_each(|b| *b = self.rand_byte());
31 | }
32 |
33 | fn rand_byte(&mut self) -> u8 {
34 | let s = self;
35 |
36 | s.i = s.i.wrapping_add(1);
37 | s.j = s.j.wrapping_add(s.sbox[s.i as usize]);
38 | s.sbox.swap(s.i as usize, s.j as usize);
39 | s.sbox[s.sbox[s.i as usize].wrapping_add(s.sbox[s.j as usize]) as usize]
40 | }
41 | }
42 |
43 | const fn sbox_init() -> [u8; 256] {
44 | let mut sbox: [u8; 256] = [0; 256];
45 |
46 | let mut i = 0;
47 |
48 | while i < 256 {
49 | sbox[i] = i as u8;
50 | i += 1;
51 | }
52 |
53 | sbox
54 | }
55 |
--------------------------------------------------------------------------------
/legacy/src/dev/camera.rs:
--------------------------------------------------------------------------------
1 | use crate::errno::Errno;
2 | use crate::fs::{
3 | make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, IoLen, IoVec, IoVecMut,
4 | MakeDevError, MakeDevFlags, Mode, OpenFlags,
5 | };
6 | use crate::process::VThread;
7 | use crate::ucred::{Gid, Uid};
8 | use std::sync::Arc;
9 | use thiserror::Error;
10 |
11 | #[derive(Debug)]
12 | struct Camera {}
13 |
14 | impl Camera {
15 | fn new() -> Self {
16 | Self {}
17 | }
18 | }
19 |
20 | impl DeviceDriver for Camera {
21 | #[allow(unused_variables)] // TODO: remove when implementing
22 | fn open(
23 | &self,
24 | dev: &Arc,
25 | mode: OpenFlags,
26 | devtype: i32,
27 | td: Option<&VThread>,
28 | ) -> Result<(), Box> {
29 | todo!()
30 | }
31 |
32 | #[allow(unused_variables)] // TODO: remove when implementing
33 | fn read(
34 | &self,
35 | dev: &Arc,
36 | off: Option,
37 | buf: &mut [IoVecMut],
38 | td: Option<&VThread>,
39 | ) -> Result> {
40 | todo!()
41 | }
42 |
43 | #[allow(unused_variables)] // TODO: remove when implementing
44 | fn write(
45 | &self,
46 | dev: &Arc,
47 | off: Option,
48 | buf: &[IoVec],
49 | td: Option<&VThread>,
50 | ) -> Result> {
51 | todo!()
52 | }
53 |
54 | #[allow(unused_variables)] // TODO: remove when implementing
55 | fn ioctl(
56 | &self,
57 | dev: &Arc,
58 | cmd: IoCmd,
59 | td: Option<&VThread>,
60 | ) -> Result<(), Box> {
61 | todo!()
62 | }
63 | }
64 |
65 | pub struct CameraManager {
66 | camera: Arc,
67 | }
68 |
69 | impl CameraManager {
70 | pub fn new() -> Result, CameraInitError> {
71 | let camera = make_dev(
72 | Camera::new(),
73 | DriverFlags::from_bits_retain(0x80000004),
74 | 0,
75 | "camera",
76 | Uid::ROOT,
77 | Gid::ROOT,
78 | Mode::new(0o666).unwrap(),
79 | None,
80 | MakeDevFlags::ETERNAL,
81 | )?;
82 |
83 | Ok(Arc::new(Self { camera }))
84 | }
85 | }
86 |
87 | /// Represents an error when [`CameraManager`] fails to initialize.
88 | #[derive(Debug, Error)]
89 | pub enum CameraInitError {
90 | #[error("cannot create camera device")]
91 | CreateGcFailed(#[from] MakeDevError),
92 | }
93 |
--------------------------------------------------------------------------------
/legacy/src/dev/dipsw.rs:
--------------------------------------------------------------------------------
1 | use thiserror::Error;
2 |
3 | use crate::{
4 | errno::Errno,
5 | fs::{
6 | make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, MakeDevError, MakeDevFlags,
7 | Mode,
8 | },
9 | process::VThread,
10 | ucred::{Gid, Uid},
11 | };
12 | use std::sync::Arc;
13 |
14 | #[derive(Debug)]
15 | struct Dipsw {}
16 |
17 | impl Dipsw {
18 | fn new() -> Self {
19 | Self {}
20 | }
21 | }
22 |
23 | impl DeviceDriver for Dipsw {
24 | #[allow(unused_variables)]
25 | fn ioctl(
26 | &self,
27 | dev: &Arc,
28 | cmd: IoCmd,
29 | td: Option<&VThread>,
30 | ) -> Result<(), Box> {
31 | let td = td.unwrap();
32 |
33 | if !td.cred().is_system() {
34 | match cmd {
35 | // TODO: properly implement this
36 | IoCmd::DIPSWCHECK2(val) | IoCmd::DIPSWUNK(val) => *val = false as i32,
37 | _ => todo!(),
38 | }
39 | } else {
40 | todo!()
41 | }
42 |
43 | Ok(())
44 | }
45 | }
46 |
47 | pub struct DipswManager {
48 | dipsw: Arc,
49 | }
50 |
51 | impl DipswManager {
52 | pub fn new() -> Result, DipswInitError> {
53 | let dipsw = make_dev(
54 | Dipsw::new(),
55 | DriverFlags::from_bits_retain(0x80000004),
56 | 0,
57 | "dipsw",
58 | Uid::ROOT,
59 | Gid::ROOT,
60 | Mode::new(0o644).unwrap(),
61 | None,
62 | MakeDevFlags::ETERNAL,
63 | )?;
64 |
65 | Ok(Arc::new(Self { dipsw }))
66 | }
67 | }
68 |
69 | /// Represents an error when [`DipswManager`] fails to initialize.
70 | #[derive(Debug, Error)]
71 | pub enum DipswInitError {
72 | #[error("cannot create dipsw device")]
73 | CreateDipswFailed(#[from] MakeDevError),
74 | }
75 |
--------------------------------------------------------------------------------
/legacy/src/dev/hid.rs:
--------------------------------------------------------------------------------
1 | use crate::errno::Errno;
2 | use crate::fs::{CharacterDevice, DeviceDriver, IoCmd, IoLen, IoVecMut};
3 | use crate::process::VThread;
4 | use std::sync::Arc;
5 |
6 | #[derive(Debug)]
7 | struct Hid {}
8 |
9 | impl DeviceDriver for Hid {
10 | #[allow(unused_variables)] // TODO: remove when implementing
11 | fn read(
12 | &self,
13 | dev: &Arc,
14 | off: Option,
15 | buf: &mut [IoVecMut],
16 | td: Option<&VThread>,
17 | ) -> Result> {
18 | todo!()
19 | }
20 |
21 | #[allow(unused_variables)] // TODO: remove when implementing
22 | fn ioctl(
23 | &self,
24 | dev: &Arc,
25 | cmd: IoCmd,
26 | td: Option<&VThread>,
27 | ) -> Result<(), Box> {
28 | todo!()
29 | }
30 | }
31 |
32 | #[repr(C)]
33 | #[derive(Debug)]
34 | pub struct OpenPortArgs {
35 | user_id: u32,
36 | ty: u32,
37 | index: u32,
38 | }
39 |
--------------------------------------------------------------------------------
/legacy/src/dev/mod.rs:
--------------------------------------------------------------------------------
1 | pub use camera::*;
2 | pub use dce::*;
3 | pub use deci::*;
4 | pub use dipsw::*;
5 | pub use dmem::*;
6 | pub use gc::*;
7 | pub use hid::*;
8 | pub use hmd::*;
9 | pub use random::*;
10 | pub use rng::*;
11 | pub use sbl_srv::*;
12 | pub use ttyconsole::*;
13 |
14 | mod camera;
15 | mod dce;
16 | mod deci;
17 | mod dipsw;
18 | mod dmem;
19 | mod gc;
20 | mod hid;
21 | mod hmd;
22 | mod random;
23 | mod rng;
24 | mod sbl_srv;
25 | mod ttyconsole;
26 |
--------------------------------------------------------------------------------
/legacy/src/dev/random.rs:
--------------------------------------------------------------------------------
1 | use crate::errno::Errno;
2 | use crate::fs::{CharacterDevice, DefaultDeviceError, DeviceDriver, IoCmd, IoLen, IoVec, IoVecMut};
3 | use crate::process::VThread;
4 | use std::sync::Arc;
5 |
6 | #[derive(Debug)]
7 | struct Random {}
8 |
9 | impl DeviceDriver for Random {
10 | #[allow(unused_variables)] // TODO: remove when implementing
11 | fn read(
12 | &self,
13 | dev: &Arc,
14 | off: Option,
15 | buf: &mut [IoVecMut],
16 | td: Option<&VThread>,
17 | ) -> Result> {
18 | todo!()
19 | }
20 |
21 | #[allow(unused_variables)] // TODO: remove when implementing
22 | fn write(
23 | &self,
24 | dev: &Arc,
25 | off: Option,
26 | buf: &[IoVec],
27 | td: Option<&VThread>,
28 | ) -> Result> {
29 | todo!()
30 | }
31 |
32 | fn ioctl(
33 | &self,
34 | _: &Arc,
35 | cmd: IoCmd,
36 | _: Option<&VThread>,
37 | ) -> Result<(), Box> {
38 | match cmd {
39 | IoCmd::FIOASYNC(_) | IoCmd::FIONBIO(_) => Ok(()),
40 | _ => Err(Box::new(DefaultDeviceError::CommandNotSupported)),
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/legacy/src/dev/rng.rs:
--------------------------------------------------------------------------------
1 | use crate::{
2 | arnd,
3 | errno::Errno,
4 | fs::{
5 | make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, MakeDevError, MakeDevFlags,
6 | Mode,
7 | },
8 | process::VThread,
9 | ucred::{Gid, Uid},
10 | };
11 | use std::sync::Arc;
12 | use thiserror::Error;
13 |
14 | #[derive(Debug)]
15 | struct Rng {}
16 |
17 | impl Rng {
18 | fn new() -> Self {
19 | Self {}
20 | }
21 | }
22 |
23 | impl DeviceDriver for Rng {
24 | fn ioctl(
25 | &self,
26 | _: &Arc,
27 | cmd: IoCmd,
28 | _: Option<&VThread>,
29 | ) -> Result<(), Box> {
30 | match cmd {
31 | // TODO: these are separate algorithms, and should be implemented as such,
32 | // however arc4rand seems sufficient for now
33 | IoCmd::RNGGETGENUINE(input) | IoCmd::RNGFIPS(input) => {
34 | input.error = 0;
35 |
36 | arnd::rand_bytes(&mut input.data);
37 |
38 | Ok(())
39 | }
40 | _ => todo!(), // ENOIOCTL,
41 | }
42 | }
43 | }
44 |
45 | #[repr(C)]
46 | #[derive(Debug)]
47 | pub struct RngInput {
48 | /// This field seems to be treated as an error
49 | error: i32,
50 | data: [u8; 64],
51 | }
52 |
53 | pub struct RngManager {
54 | rng: Arc,
55 | }
56 |
57 | impl RngManager {
58 | pub fn new() -> Result, RngInitError> {
59 | let rng = make_dev(
60 | Rng::new(),
61 | DriverFlags::from_bits_retain(0x80000004),
62 | 0,
63 | "rng",
64 | Uid::ROOT,
65 | Gid::ROOT,
66 | Mode::new(0o444).unwrap(),
67 | None,
68 | MakeDevFlags::ETERNAL,
69 | )?;
70 |
71 | Ok(Arc::new(Self { rng }))
72 | }
73 | }
74 |
75 | /// Represents an error when [`RngManager`] fails to initialize.
76 | #[derive(Debug, Error)]
77 | pub enum RngInitError {
78 | #[error("cannot create rng device")]
79 | CreateRngFailed(#[from] MakeDevError),
80 | }
81 |
--------------------------------------------------------------------------------
/legacy/src/dev/sbl_srv.rs:
--------------------------------------------------------------------------------
1 | use crate::{
2 | errno::Errno,
3 | fs::{
4 | make_dev, CharacterDevice, DeviceDriver, DriverFlags, IoCmd, MakeDevError, MakeDevFlags,
5 | Mode,
6 | },
7 | process::VThread,
8 | ucred::{Gid, Uid},
9 | };
10 | use std::sync::Arc;
11 | use thiserror::Error;
12 |
13 | #[derive(Debug)]
14 | struct SblSrv {}
15 |
16 | impl SblSrv {
17 | fn new() -> Self {
18 | Self {}
19 | }
20 | }
21 |
22 | impl DeviceDriver for SblSrv {
23 | #[allow(unused_variables)] // TODO: remove when implementing
24 | fn ioctl(
25 | &self,
26 | dev: &Arc,
27 | cmd: IoCmd,
28 | td: Option<&VThread>,
29 | ) -> Result<(), Box> {
30 | todo!()
31 | }
32 | }
33 |
34 | pub struct SblSrvManager {
35 | sbl: Arc,
36 | }
37 |
38 | impl SblSrvManager {
39 | pub fn new() -> Result, SblSrvInitError> {
40 | let sbl = make_dev(
41 | SblSrv {},
42 | DriverFlags::INIT,
43 | 0,
44 | "sbl_srv",
45 | Uid::ROOT,
46 | Gid::ROOT,
47 | Mode::new(0o600).unwrap(),
48 | None,
49 | MakeDevFlags::empty(),
50 | )
51 | .map_err(SblSrvInitError::CreateSblSrvFailed)?;
52 |
53 | Ok(Arc::new(Self { sbl }))
54 | }
55 | }
56 |
57 | /// Represents an error when [`SblSrvManager`] fails to initialize.
58 | #[derive(Debug, Error)]
59 | pub enum SblSrvInitError {
60 | #[error("cannot create sbl_srv device")]
61 | CreateSblSrvFailed(#[source] MakeDevError),
62 | }
63 |
--------------------------------------------------------------------------------
/legacy/src/dmem/blockpool.rs:
--------------------------------------------------------------------------------
1 | use crate::errno::Errno;
2 | use crate::fs::{DefaultFileBackendError, FileBackend, IoCmd, PollEvents, Stat, VFile, Vnode};
3 | use crate::process::VThread;
4 | use std::sync::Arc;
5 |
6 | #[derive(Debug)]
7 | pub struct BlockPool {}
8 |
9 | impl BlockPool {
10 | pub fn new() -> Self {
11 | Self {}
12 | }
13 | }
14 |
15 | impl FileBackend for BlockPool {
16 | fn is_seekable(&self) -> bool {
17 | todo!()
18 | }
19 |
20 | #[allow(unused_variables)] // TODO: remove when implementing
21 | fn ioctl(&self, file: &VFile, cmd: IoCmd, td: Option<&VThread>) -> Result<(), Box> {
22 | match cmd {
23 | IoCmd::BPOOLEXPAND(args) => todo!(),
24 | IoCmd::BPOOLSTATS(out) => todo!(),
25 | _ => Err(Box::new(DefaultFileBackendError::IoctlNotSupported)),
26 | }
27 | }
28 |
29 | #[allow(unused_variables)] // TODO: remove when implementing
30 | fn poll(&self, file: &VFile, events: PollEvents, td: &VThread) -> PollEvents {
31 | todo!()
32 | }
33 |
34 | fn stat(&self, _: &VFile, _: Option<&VThread>) -> Result> {
35 | let mut stat = Stat::zeroed();
36 |
37 | stat.block_size = 0x10000;
38 | stat.mode = 0o130000;
39 |
40 | todo!()
41 | }
42 |
43 | fn vnode(&self) -> Option<&Arc> {
44 | None
45 | }
46 | }
47 |
48 | #[repr(C)]
49 | #[derive(Debug)]
50 | pub struct BlockpoolExpandArgs {
51 | len: usize,
52 | search_start: usize,
53 | search_end: usize,
54 | alignment: usize,
55 | }
56 |
57 | #[repr(C)]
58 | #[derive(Debug)]
59 | pub struct BlockpoolStats {
60 | avail_flushed: i32,
61 | avail_cached: i32,
62 | allocated_flushed: i32,
63 | allocated_cached: i32,
64 | }
65 |
--------------------------------------------------------------------------------
/legacy/src/event/mod.rs:
--------------------------------------------------------------------------------
1 | use crate::subsystem::Subsystem;
2 | use std::collections::BTreeMap;
3 | use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
4 |
5 | pub use self::ty::*;
6 |
7 | mod ty;
8 |
9 | pub struct Event {
10 | next_id: u64,
11 | }
12 |
13 | impl Event {
14 | /// See `eventhandler_register` on the PS4 for a reference.
15 | ///
16 | /// `handler` must not call into any function that is going to trigger any event in the same
17 | /// [`EventSet`] because it can cause a deadlock. That means what `handler` can do is limited.
18 | pub fn subscribe(
19 | &mut self,
20 | subsys: &Arc,
21 | handler: T::Handler,
22 | priority: u32,
23 | ) {
24 | let subsys = subsys.clone();
25 | let id = self.next_id;
26 |
27 | assert!(self
28 | .subscribers
29 | .insert((priority, id), T::wrap_handler(subsys, handler))
30 | .is_none());
31 |
32 | self.next_id += 1;
33 | }
34 | }
35 |
36 | impl Default for Event {
37 | fn default() -> Self {
38 | Self { next_id: 0 }
39 | }
40 | }
41 |
42 | impl EventSet {
43 | pub fn lock(&self) -> RwLockWriteGuard {
44 | self.0.write().unwrap()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/legacy/src/event/ty.rs:
--------------------------------------------------------------------------------
1 | use std::sync::Arc;
2 |
3 | /// Type of an event.
4 | pub trait EventType: 'static {
5 | fn wrap_handler(s: Arc, h: Self::Handler) -> Self::Wrapper
6 | where
7 | S: Send + Sync + 'static;
8 | }
9 |
10 | impl EventType for fn(A) {
11 | type Handler | |