├── .gitmodules ├── .gitignore ├── rust-toolchain.toml ├── src ├── sync.rs ├── prelude.rs ├── macros.rs ├── sync │ ├── once_cell.rs │ ├── spin_mutex.rs │ ├── oneshot.rs │ ├── mpsc.rs │ └── mutex.rs ├── fat.rs ├── graphics │ ├── frame_buffer.rs │ ├── color.rs │ ├── font.rs │ ├── traits.rs │ └── geometry.rs ├── serial.rs ├── co_task.rs ├── co_task │ ├── traits.rs │ └── executor.rs ├── graphics.rs ├── desktop.rs ├── fmt.rs ├── fat │ ├── cluster_chain.rs │ ├── fat_entry.rs │ ├── directory.rs │ └── directory_entry.rs ├── emergency_console.rs ├── gdt.rs ├── paging.rs ├── log.rs ├── cxx_support.rs ├── text_window.rs ├── interrupt.rs ├── error.rs ├── mouse.rs ├── xhc.rs ├── keyboard.rs ├── acpi.rs ├── memory.rs ├── main.rs ├── framed_window.rs ├── window.rs └── timer.rs ├── mikanos_usb ├── cxx_src │ ├── usb │ │ ├── classdriver │ │ │ ├── base.cpp │ │ │ ├── keyboard.hpp │ │ │ ├── base.hpp │ │ │ ├── mouse.hpp │ │ │ ├── hid.hpp │ │ │ ├── mouse.cpp │ │ │ ├── keyboard.cpp │ │ │ └── hid.cpp │ │ ├── xhci │ │ │ ├── speed.hpp │ │ │ ├── registers.cpp │ │ │ ├── devmgr.hpp │ │ │ ├── port.cpp │ │ │ ├── port.hpp │ │ │ ├── xhci.hpp │ │ │ ├── device.hpp │ │ │ ├── ring.hpp │ │ │ ├── devmgr.cpp │ │ │ ├── trb.cpp │ │ │ ├── ring.cpp │ │ │ └── context.hpp │ │ ├── arraymap.hpp │ │ ├── memory.cpp │ │ ├── endpoint.hpp │ │ ├── memory.hpp │ │ ├── setupdata.hpp │ │ ├── device.hpp │ │ └── descriptor.hpp │ ├── cxx_support.h │ ├── logger.cpp │ ├── logger.hpp │ ├── error.hpp │ ├── sabios_support.cpp │ └── register.hpp ├── Cargo.toml ├── build.rs ├── src │ └── lib.rs └── .clang-format ├── boot ├── Cargo.toml └── src │ └── main.rs ├── .cargo └── config.toml ├── x86_64-sabios.json ├── LICENSE-MIT ├── Cargo.toml └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.cache/ 2 | /.gdb_history 3 | /.vscode/ 4 | /compile_commands.json 5 | /target 6 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | components = ["rustfmt", "clippy", "rust-src", "llvm-tools-preview"] 4 | -------------------------------------------------------------------------------- /src/sync.rs: -------------------------------------------------------------------------------- 1 | pub(crate) use self::{mutex::*, once_cell::*, spin_mutex::*}; 2 | 3 | pub(crate) mod mpsc; 4 | mod mutex; 5 | mod once_cell; 6 | pub(crate) mod oneshot; 7 | mod spin_mutex; 8 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/classdriver/base.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/classdriver/base.hpp" 2 | 3 | namespace usb { 4 | ClassDriver::ClassDriver(Device *dev) : dev_{dev} {} 5 | 6 | ClassDriver::~ClassDriver() {} 7 | } // namespace usb 8 | -------------------------------------------------------------------------------- /boot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "boot" 3 | version = "0.1.0" 4 | edition = "2018" 5 | resolver = "2" 6 | 7 | [dependencies] 8 | bootloader-locator = "0.0.4" 9 | locate-cargo-manifest = "0.2.2" 10 | runner-utils = "0.0.2" 11 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | 3 | pub(crate) use crate::{ 4 | bail, 5 | co_task::TryFutureExt as _, 6 | debug, error, 7 | error::{Error, ErrorKind, Result}, 8 | info, log, trace, warn, 9 | }; 10 | pub(crate) use futures_util::{FutureExt as _, StreamExt as _, TryFutureExt as _}; 11 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_os = "none")'] 2 | runner = "cargo run --package boot --" 3 | 4 | [alias] 5 | kbuild = "build --target x86_64-sabios.json -Z build-std=core,alloc" 6 | krun = "run --target x86_64-sabios.json -Z build-std=core,alloc" 7 | ktest = "test --target x86_64-sabios.json -Z build-std=core,alloc" 8 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/xhci/speed.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/xhci/speed.hpp 3 | * 4 | * Protocol Speed ID のデフォルト定義.PSIC == 0 のときのみ有効. 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace usb::xhci { 10 | const int kFullSpeed = 1; 11 | const int kLowSpeed = 2; 12 | const int kHighSpeed = 3; 13 | const int kSuperSpeed = 4; 14 | const int kSuperSpeedPlus = 5; 15 | } // namespace usb::xhci 16 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! byte_getter { 3 | ($vis:vis $field:ident : [u8; $n:expr]) => { 4 | $vis fn $field(&self) -> [u8; $n] { 5 | self.$field 6 | } 7 | }; 8 | ($vis:vis $field:ident : $ty:ident) => { 9 | $vis fn $field(&self) -> $ty { 10 | $ty::from_le_bytes(self.$field) 11 | } 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/cxx_support.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | #include 5 | #include 6 | extern "C" { 7 | #else 8 | #include 9 | #include 10 | #endif 11 | 12 | int32_t sabios_log(int32_t level, const char *file, size_t file_len, uint32_t line, const char *msg, 13 | size_t msg_len, bool cont_line); 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | -------------------------------------------------------------------------------- /mikanos_usb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mikanos_usb" 3 | version = "0.1.0" 4 | edition = "2018" 5 | build = "build.rs" 6 | links = "mikanos_usb" 7 | resolver = "2" 8 | 9 | [build-dependencies] 10 | cc = "1.0.68" 11 | flate2 = "1.0.20" 12 | glob = "0.3.0" 13 | reqwest = "0.11.4" 14 | tar = "0.4.35" 15 | tokio = { version = "1.7.1", features = ["macros", "rt-multi-thread"] } 16 | 17 | [dependencies] 18 | x86_64 = "0.14.3" 19 | -------------------------------------------------------------------------------- /x86_64-sabios.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 4 | "arch": "x86_64", 5 | "target-endian": "little", 6 | "target-pointer-width": "64", 7 | "target-c-int-width": "32", 8 | "os": "none", 9 | "executables": true, 10 | "linker-flavor": "ld.lld", 11 | "linker": "rust-lld", 12 | "panic-strategy": "abort", 13 | "disable-redzone": true, 14 | "features": "-mmx,-sse,+soft-float" 15 | } 16 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/logger.cpp: -------------------------------------------------------------------------------- 1 | #include "logger.hpp" 2 | 3 | #include "cxx_support.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | int _Log(enum LogLevel level, const char *file, uint32_t line, bool cont_line, const char *format, 10 | ...) { 11 | char buf[1024]; 12 | va_list ap; 13 | 14 | va_start(ap, format); 15 | int res = vsnprintf(buf, sizeof(buf) - 1, format, ap); 16 | va_end(ap); 17 | 18 | sabios_log(level, file, strlen(file), line, buf, strlen(buf), cont_line); 19 | 20 | return res; 21 | } 22 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/logger.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file logger.hpp 3 | * 4 | * カーネルロガーの実装. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | enum LogLevel { 12 | kError = 3, 13 | kWarn = 4, 14 | kInfo = 6, 15 | kDebug = 7, 16 | kTrace = 8, 17 | }; 18 | 19 | #define Log(level, ...) _Log((level), __FILE__, __LINE__, false, ##__VA_ARGS__) 20 | 21 | /** @brief ログを指定された優先度で記録する. 22 | * 23 | * 指定された優先度がしきい値以上ならば記録する. 24 | * 優先度がしきい値未満ならログは捨てられる. 25 | * 26 | * @param level ログの優先度.しきい値以上の優先度のログのみが記録される. 27 | * @param format 書式文字列.printk と互換. 28 | */ 29 | int _Log(enum LogLevel level, const char *file, uint32_t line, bool cont_line, const char *format, 30 | ...) __attribute__((format(printf, 5, 6))); 31 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/xhci/registers.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/xhci/registers.hpp" 2 | 3 | namespace { 4 | template Ptr AddOrNull(Ptr p, Disp d) { return d == 0 ? nullptr : p + d; } 5 | } // namespace 6 | 7 | namespace usb::xhci { 8 | ExtendedRegisterList::Iterator &ExtendedRegisterList::Iterator::operator++() { 9 | if (reg_) { 10 | reg_ = AddOrNull(reg_, reg_->Read().bits.next_pointer); 11 | static_assert(sizeof(*reg_) == 4); 12 | } 13 | return *this; 14 | } 15 | 16 | ExtendedRegisterList::ExtendedRegisterList(uint64_t mmio_base, HCCPARAMS1_Bitmap hccp) 17 | : first_{AddOrNull(reinterpret_cast(mmio_base), 18 | hccp.bits.xhci_extended_capabilities_pointer)} {} 19 | } // namespace usb::xhci 20 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/classdriver/keyboard.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/classdriver/keyboard.hpp 3 | * 4 | * HID keyboard class driver. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "usb/classdriver/hid.hpp" 10 | 11 | #include 12 | 13 | namespace usb { 14 | class HIDKeyboardDriver : public HIDBaseDriver { 15 | public: 16 | HIDKeyboardDriver(Device *dev, int interface_index); 17 | 18 | void *operator new(size_t size); 19 | void operator delete(void *ptr) noexcept; 20 | 21 | Error OnDataReceived() override; 22 | 23 | using ObserverType = void(uint8_t modifier, uint8_t keycode); 24 | void SubscribeKeyPush(std::function observer); 25 | static std::function default_observer; 26 | 27 | private: 28 | std::array, 4> observers_; 29 | int num_observers_ = 0; 30 | 31 | void NotifyKeyPush(uint8_t modifier, uint8_t keycode); 32 | }; 33 | } // namespace usb 34 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/classdriver/base.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/classdriver/base.hpp 3 | * 4 | * USB デバイスクラス用のドライバのベースクラス. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "error.hpp" 10 | #include "usb/endpoint.hpp" 11 | #include "usb/setupdata.hpp" 12 | 13 | namespace usb { 14 | class Device; 15 | 16 | class ClassDriver { 17 | public: 18 | ClassDriver(Device *dev); 19 | virtual ~ClassDriver(); 20 | 21 | virtual Error Initialize() = 0; 22 | virtual Error SetEndpoint(const EndpointConfig &config) = 0; 23 | virtual Error OnEndpointsConfigured() = 0; 24 | virtual Error OnControlCompleted(EndpointID ep_id, SetupData setup_data, const void *buf, 25 | int len) = 0; 26 | virtual Error OnInterruptCompleted(EndpointID ep_id, const void *buf, int len) = 0; 27 | 28 | /** このクラスドライバを保持する USB デバイスを返す. */ 29 | Device *ParentDevice() const { return dev_; } 30 | 31 | private: 32 | Device *dev_; 33 | }; 34 | } // namespace usb 35 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/classdriver/mouse.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/classdriver/mouse.hpp 3 | * 4 | * HID mouse class driver. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "usb/classdriver/hid.hpp" 10 | 11 | #include 12 | 13 | namespace usb { 14 | class HIDMouseDriver : public HIDBaseDriver { 15 | public: 16 | HIDMouseDriver(Device *dev, int interface_index); 17 | 18 | void *operator new(size_t size); 19 | void operator delete(void *ptr) noexcept; 20 | 21 | Error OnDataReceived() override; 22 | 23 | using ObserverType = void(uint8_t buttons, int8_t displacement_x, int8_t displacement_y); 24 | void SubscribeMouseMove(std::function observer); 25 | static std::function default_observer; 26 | 27 | private: 28 | std::array, 4> observers_; 29 | int num_observers_ = 0; 30 | 31 | void NotifyMouseMove(uint8_t buttons, int8_t displacement_x, int8_t displacement_y); 32 | }; 33 | } // namespace usb 34 | -------------------------------------------------------------------------------- /src/sync/once_cell.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use conquer_once::noblock; 3 | 4 | /// A wrapper around `noblock::OnceCell` which panics immediately when error detected. 5 | #[derive(Debug)] 6 | pub(crate) struct OnceCell(noblock::OnceCell); 7 | 8 | impl OnceCell { 9 | pub(crate) const fn uninit() -> Self { 10 | Self(noblock::OnceCell::uninit()) 11 | } 12 | 13 | #[track_caller] 14 | pub(crate) fn init_once(&self, f: impl FnOnce() -> T) { 15 | #[allow(clippy::unwrap_used)] 16 | self.try_init_once(f).unwrap() 17 | } 18 | 19 | #[track_caller] 20 | pub(crate) fn try_init_once(&self, f: impl FnOnce() -> T) -> Result<()> { 21 | Ok(self.0.try_init_once(f)?) 22 | } 23 | 24 | #[track_caller] 25 | pub(crate) fn get(&self) -> &T { 26 | #[allow(clippy::unwrap_used)] 27 | self.try_get().unwrap() 28 | } 29 | 30 | #[track_caller] 31 | pub(crate) fn try_get(&self) -> Result<&T> { 32 | Ok(self.0.try_get()?) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/fat.rs: -------------------------------------------------------------------------------- 1 | pub(crate) use self::{bpb::*, cluster_chain::*, directory::*, directory_entry::*, fat_entry::*}; 2 | use crate::{ 3 | prelude::*, 4 | sync::{Mutex, MutexGuard, OnceCell}, 5 | }; 6 | 7 | mod bpb; 8 | mod cluster_chain; 9 | mod directory; 10 | mod directory_entry; 11 | mod fat_entry; 12 | 13 | #[derive(Debug)] 14 | pub(crate) enum FatType { 15 | Fat12, 16 | Fat16, 17 | Fat32, 18 | } 19 | 20 | extern "C" { 21 | static mut _binary_fs_fat_start: u8; 22 | static _binary_fs_fat_size: usize; 23 | } 24 | 25 | static FILESYSTEM: OnceCell> = OnceCell::uninit(); 26 | 27 | pub(crate) fn init() { 28 | let filesystem = unsafe { bpb::get(&mut _binary_fs_fat_start) }; 29 | info!("file system type: {:?}", filesystem.fat_type()); 30 | info!("{:?}", filesystem); 31 | FILESYSTEM.init_once(move || Mutex::new(filesystem)); 32 | } 33 | 34 | pub(crate) fn lock() -> MutexGuard<'static, &'static mut dyn BiosParameterBlock> { 35 | FILESYSTEM.get().lock() 36 | } 37 | -------------------------------------------------------------------------------- /src/graphics/frame_buffer.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | desktop, 3 | graphics::{Draw, FrameBufferDrawer, ScreenInfo}, 4 | prelude::*, 5 | sync::{OnceCell, SpinMutex, SpinMutexGuard}, 6 | }; 7 | use bootloader::boot_info::FrameBuffer; 8 | 9 | static DRAWER: OnceCell> = OnceCell::uninit(); 10 | 11 | pub(super) fn init(frame_buffer: FrameBuffer) -> Result { 12 | let mut drawer = FrameBufferDrawer::new_frame_buffer(frame_buffer)?; 13 | let info = drawer.info(); 14 | drawer.fill_rect(info.area(), desktop::BG_COLOR); 15 | 16 | DRAWER.init_once(|| SpinMutex::new(drawer)); 17 | 18 | Ok(info) 19 | } 20 | 21 | pub(crate) fn lock_drawer() -> SpinMutexGuard<'static, FrameBufferDrawer> { 22 | DRAWER.get().lock() 23 | } 24 | 25 | pub(crate) unsafe fn emergency_lock_drawer() -> SpinMutexGuard<'static, FrameBufferDrawer> { 26 | let drawer = DRAWER.get(); 27 | if let Ok(drawer) = drawer.try_lock() { 28 | return drawer; 29 | } 30 | unsafe { drawer.force_unlock() }; 31 | drawer.lock() 32 | } 33 | -------------------------------------------------------------------------------- /src/sync/spin_mutex.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | 3 | /// A wrapper around `spin::Mutex` which panics immediately when deadlock detected. 4 | #[derive(Debug, Default)] 5 | pub(crate) struct SpinMutex(spin::Mutex); 6 | 7 | pub(crate) type SpinMutexGuard<'a, T> = spin::MutexGuard<'a, T>; 8 | 9 | impl SpinMutex { 10 | pub(crate) const fn new(value: T) -> Self { 11 | Self(spin::Mutex::new(value)) 12 | } 13 | } 14 | 15 | impl SpinMutex 16 | where 17 | T: ?Sized, 18 | { 19 | #[track_caller] 20 | pub(crate) fn lock(&self) -> SpinMutexGuard<'_, T> { 21 | #[allow(clippy::unwrap_used)] 22 | self.try_lock().unwrap() 23 | } 24 | 25 | #[track_caller] 26 | pub(crate) fn try_lock(&self) -> Result> { 27 | Ok(self.0.try_lock().ok_or(ErrorKind::Deadlock)?) 28 | } 29 | 30 | pub(crate) unsafe fn force_unlock(&self) { 31 | unsafe { self.0.force_unlock() } 32 | } 33 | 34 | #[track_caller] 35 | pub(crate) fn with_lock(&self, f: impl FnOnce(&mut T) -> R) -> R { 36 | f(&mut *self.lock()) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/serial.rs: -------------------------------------------------------------------------------- 1 | use spin::{Lazy, Mutex}; 2 | use uart_16550::SerialPort; 3 | 4 | pub static SERIAL1: Lazy> = Lazy::new(|| { 5 | let mut serial_port = unsafe { SerialPort::new(0x3F8) }; 6 | serial_port.init(); 7 | Mutex::new(serial_port) 8 | }); 9 | 10 | #[doc(hidden)] 11 | pub fn _print(args: ::core::fmt::Arguments) { 12 | use core::fmt::Write; 13 | use x86_64::instructions::interrupts; 14 | 15 | #[allow(clippy::expect_used)] 16 | interrupts::without_interrupts(|| { 17 | SERIAL1 18 | .lock() 19 | .write_fmt(args) 20 | .expect("Printing to serial failed"); 21 | }); 22 | } 23 | 24 | /// Prints to the host through the serial interface. 25 | #[macro_export] 26 | macro_rules! serial_print { 27 | ($($arg:tt)*) => { 28 | $crate::serial::_print(format_args!($($arg)*)); 29 | }; 30 | } 31 | 32 | /// Prints to the host through the serial interface, appending a newline. 33 | #[macro_export] 34 | macro_rules! serial_println { 35 | () => ($crate::serial_print!("\n")); 36 | ($($arg:tt)*) => ($crate::serial_print!("{}\n", format_args!($($arg)*))); 37 | } 38 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/xhci/devmgr.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/xhci/devmgr.hpp 3 | * 4 | * USB デバイスの管理機能. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "error.hpp" 10 | #include "usb/xhci/context.hpp" 11 | #include "usb/xhci/device.hpp" 12 | 13 | #include 14 | #include 15 | 16 | namespace usb::xhci { 17 | class DeviceManager { 18 | 19 | public: 20 | Error Initialize(size_t max_slots); 21 | DeviceContext **DeviceContexts() const; 22 | Device *FindByPort(uint8_t port_num, uint32_t route_string) const; 23 | Device *FindByState(enum Device::State state) const; 24 | Device *FindBySlot(uint8_t slot_id) const; 25 | // WithError Get(uint8_t device_id) const; 26 | Error AllocDevice(uint8_t slot_id, DoorbellRegister *dbreg); 27 | Error LoadDCBAA(uint8_t slot_id); 28 | Error Remove(uint8_t slot_id); 29 | 30 | private: 31 | // device_context_pointers_ can be used as DCBAAP's value. 32 | // The number of elements is max_slots_ + 1. 33 | DeviceContext **device_context_pointers_; 34 | size_t max_slots_; 35 | 36 | // The number of elements is max_slots_ + 1. 37 | Device **devices_; 38 | }; 39 | } // namespace usb::xhci 40 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 gifnksm 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/co_task.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use core::{ 3 | future::Future, 4 | pin::Pin, 5 | sync::atomic::{AtomicU64, Ordering}, 6 | task::{Context, Poll}, 7 | }; 8 | use custom_debug_derive::Debug as CustomDebug; 9 | 10 | pub(crate) use self::{executor::*, traits::*}; 11 | 12 | mod executor; 13 | mod traits; 14 | 15 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 16 | pub(crate) struct CoTaskId(u64); 17 | 18 | impl CoTaskId { 19 | fn new() -> Self { 20 | static NEXT_ID: AtomicU64 = AtomicU64::new(0); 21 | Self(NEXT_ID.fetch_add(1, Ordering::Relaxed)) 22 | } 23 | } 24 | 25 | /// Cooperative Task 26 | #[derive(CustomDebug)] 27 | pub(crate) struct CoTask { 28 | id: CoTaskId, 29 | #[debug(skip)] 30 | future: Pin + Send>>, 31 | } 32 | 33 | impl CoTask { 34 | pub(crate) fn new(future: impl Future + Send + 'static) -> Self { 35 | Self { 36 | id: CoTaskId::new(), 37 | future: Box::pin(future), 38 | } 39 | } 40 | 41 | fn poll(&mut self, cx: &mut Context) -> Poll<()> { 42 | self.future.as_mut().poll(cx) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/xhci/port.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/xhci/port.hpp" 2 | 3 | #include "usb/xhci/registers.hpp" 4 | #include "usb/xhci/xhci.hpp" 5 | 6 | namespace usb::xhci { 7 | uint8_t Port::Number() const { return port_num_; } 8 | 9 | bool Port::IsConnected() const { return port_reg_set_.PORTSC.Read().bits.current_connect_status; } 10 | 11 | bool Port::IsEnabled() const { return port_reg_set_.PORTSC.Read().bits.port_enabled_disabled; } 12 | 13 | bool Port::IsConnectStatusChanged() const { 14 | return port_reg_set_.PORTSC.Read().bits.connect_status_change; 15 | } 16 | 17 | bool Port::IsPortResetChanged() const { return port_reg_set_.PORTSC.Read().bits.port_reset_change; } 18 | 19 | int Port::Speed() const { return port_reg_set_.PORTSC.Read().bits.port_speed; } 20 | 21 | Error Port::Reset() { 22 | auto portsc = port_reg_set_.PORTSC.Read(); 23 | portsc.data[0] &= 0x0e00c3e0u; 24 | portsc.data[0] |= 0x00020010u; // Write 1 to PR and CSC 25 | port_reg_set_.PORTSC.Write(portsc); 26 | while (port_reg_set_.PORTSC.Read().bits.port_reset) 27 | ; 28 | return MAKE_ERROR(Error::kSuccess); 29 | } 30 | 31 | Device *Port::Initialize() { return nullptr; } 32 | } // namespace usb::xhci 33 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/arraymap.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/arraymap.hpp 3 | * 4 | * 固定長配列を用いた簡易なマップ実装. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace usb { 13 | template class ArrayMap { 14 | public: 15 | std::optional Get(const K &key) const { 16 | for (int i = 0; i < table_.size(); ++i) { 17 | if (auto opt_k = table_[i].first; opt_k && opt_k.value() == key) { 18 | return table_[i].second; 19 | } 20 | } 21 | return std::nullopt; 22 | } 23 | 24 | void Put(const K &key, const V &value) { 25 | for (int i = 0; i < table_.size(); ++i) { 26 | if (!table_[i].first) { 27 | table_[i].first = key; 28 | table_[i].second = value; 29 | break; 30 | } 31 | } 32 | } 33 | 34 | void Delete(const K &key) { 35 | for (int i = 0; i < table_.size(); ++i) { 36 | if (auto opt_k = table_[i].first; opt_k && opt_k.value() == key) { 37 | table_[i].first = std::nullopt; 38 | break; 39 | } 40 | } 41 | } 42 | 43 | private: 44 | std::array, V>, N> table_{}; 45 | }; 46 | } // namespace usb 47 | -------------------------------------------------------------------------------- /src/co_task/traits.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt::Debug, 3 | future::Future, 4 | panic::Location, 5 | pin::Pin, 6 | task::{Context, Poll}, 7 | }; 8 | use futures_util::TryFuture; 9 | use pin_project::pin_project; 10 | 11 | impl TryFutureExt for Fut {} 12 | 13 | pub(crate) trait TryFutureExt: TryFuture { 14 | #[track_caller] 15 | fn unwrap(self) -> Unwrap 16 | where 17 | Self: Sized, 18 | { 19 | Unwrap(self, Location::caller()) 20 | } 21 | } 22 | 23 | #[pin_project] 24 | #[derive(Debug)] 25 | pub(crate) struct Unwrap(#[pin] Fut, &'static Location<'static>); 26 | 27 | impl Future for Unwrap 28 | where 29 | Fut: TryFuture, 30 | Fut::Error: Debug, 31 | { 32 | type Output = Fut::Ok; 33 | 34 | #[track_caller] 35 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 36 | let this = self.project(); 37 | match this.0.try_poll(cx) { 38 | Poll::Pending => Poll::Pending, 39 | Poll::Ready(Ok(value)) => Poll::Ready(value), 40 | Poll::Ready(Err(err)) => panic!("panic at {}: {:?}", this.1, err), 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/graphics.rs: -------------------------------------------------------------------------------- 1 | use crate::{prelude::*, sync::OnceCell}; 2 | use bootloader::boot_info::{FrameBuffer, PixelFormat}; 3 | 4 | pub(crate) use self::{buffer_drawer::*, color::*, geometry::*, traits::*}; 5 | 6 | mod buffer_drawer; 7 | mod color; 8 | pub(crate) mod font; 9 | pub(crate) mod frame_buffer; 10 | mod geometry; 11 | mod traits; 12 | 13 | static SCREEN_INFO: OnceCell = OnceCell::uninit(); 14 | 15 | pub(crate) fn init(frame_buffer: FrameBuffer) -> Result<()> { 16 | let screen_info = frame_buffer::init(frame_buffer)?; 17 | info!( 18 | "screen: size={}, bytes_per_pixel={}, pixel_format={:?}", 19 | screen_info.size, screen_info.bytes_per_pixel, screen_info.pixel_format, 20 | ); 21 | 22 | SCREEN_INFO.init_once(|| screen_info); 23 | 24 | Ok(()) 25 | } 26 | 27 | #[derive(Debug, Clone, Copy)] 28 | pub(crate) struct ScreenInfo { 29 | pub(crate) size: Size, 30 | pub(crate) bytes_per_pixel: i32, 31 | pub(crate) pixel_format: PixelFormat, 32 | } 33 | 34 | impl ScreenInfo { 35 | pub(crate) fn get() -> ScreenInfo { 36 | *SCREEN_INFO.get() 37 | } 38 | 39 | pub(crate) fn area(&self) -> Rectangle { 40 | Rectangle::new(Point::new(0, 0), self.size) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/classdriver/hid.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/classdriver/hid.hpp 3 | * 4 | * HID base class driver. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "usb/classdriver/base.hpp" 10 | 11 | namespace usb { 12 | class HIDBaseDriver : public ClassDriver { 13 | public: 14 | HIDBaseDriver(Device *dev, int interface_index, int in_packet_size); 15 | Error Initialize() override; 16 | Error SetEndpoint(const EndpointConfig &config) override; 17 | Error OnEndpointsConfigured() override; 18 | Error OnControlCompleted(EndpointID ep_id, SetupData setup_data, const void *buf, 19 | int len) override; 20 | Error OnInterruptCompleted(EndpointID ep_id, const void *buf, int len) override; 21 | 22 | virtual Error OnDataReceived() = 0; 23 | const static size_t kBufferSize = 1024; 24 | const std::array &Buffer() const { return buf_; } 25 | const std::array &PreviousBuffer() const { return previous_buf_; } 26 | 27 | private: 28 | EndpointID ep_interrupt_in_; 29 | EndpointID ep_interrupt_out_; 30 | const int interface_index_; 31 | int in_packet_size_; 32 | int initialize_phase_{0}; 33 | 34 | std::array buf_{}, previous_buf_{}; 35 | }; 36 | } // namespace usb 37 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/memory.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/memory.hpp" 2 | 3 | #include 4 | 5 | namespace { 6 | template T Ceil(T value, unsigned int alignment) { 7 | return (value + alignment - 1) & ~static_cast(alignment - 1); 8 | } 9 | 10 | template T MaskBits(T value, U mask) { 11 | return value & ~static_cast(mask - 1); 12 | } 13 | } // namespace 14 | 15 | namespace usb { 16 | size_t memory_pool_size = 0; 17 | uintptr_t pool_base_ptr = 0; 18 | uintptr_t alloc_ptr = 0; 19 | 20 | void SetMemoryPool(uintptr_t pool_ptr, size_t pool_size) { 21 | pool_base_ptr = alloc_ptr = pool_ptr; 22 | memory_pool_size = pool_size; 23 | } 24 | 25 | void *AllocMem(size_t size, unsigned int alignment, unsigned int boundary) { 26 | if (alignment > 0) { 27 | alloc_ptr = Ceil(alloc_ptr, alignment); 28 | } 29 | if (boundary > 0) { 30 | auto next_boundary = Ceil(alloc_ptr, boundary); 31 | if (next_boundary < alloc_ptr + size) { 32 | alloc_ptr = next_boundary; 33 | } 34 | } 35 | 36 | if (pool_base_ptr + memory_pool_size < alloc_ptr + size) { 37 | return nullptr; 38 | } 39 | 40 | auto p = alloc_ptr; 41 | alloc_ptr += size; 42 | return reinterpret_cast(p); 43 | } 44 | 45 | void FreeMem(void *p) {} 46 | } // namespace usb 47 | -------------------------------------------------------------------------------- /src/desktop.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | graphics::{Color, Draw, Point, Rectangle, ScreenInfo, Size}, 3 | layer, 4 | prelude::*, 5 | window::Window, 6 | }; 7 | 8 | pub(crate) const BG_COLOR: Color = Color::new(45, 118, 237); 9 | pub(crate) const FG_COLOR: Color = Color::WHITE; 10 | 11 | fn draw(drawer: &mut dyn Draw, size: Size) { 12 | drawer.fill_rect( 13 | Rectangle::new(Point::new(0, 0), Size::new(size.x, size.y - 50)), 14 | BG_COLOR, 15 | ); 16 | drawer.fill_rect( 17 | Rectangle::new(Point::new(0, size.y - 50), Size::new(size.x, 50)), 18 | Color::new(1, 8, 17), 19 | ); 20 | drawer.fill_rect( 21 | Rectangle::new(Point::new(0, size.y - 50), Size::new(size.x / 5, 50)), 22 | Color::new(80, 80, 80), 23 | ); 24 | drawer.draw_rect( 25 | Rectangle::new(Point::new(10, size.y - 40), Size::new(30, 30)), 26 | Color::new(160, 160, 160), 27 | ); 28 | } 29 | 30 | pub(crate) async fn handler_task() -> Result<()> { 31 | let screen_info = ScreenInfo::get(); 32 | let mut window = Window::builder() 33 | .size(screen_info.size) 34 | .height(layer::DESKTOP_HEIGHT) 35 | .build()?; 36 | 37 | draw(&mut window, screen_info.size); 38 | window.flush().await?; 39 | 40 | Ok(()) 41 | } 42 | -------------------------------------------------------------------------------- /src/fmt.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | ascii, 3 | fmt::{self, LowerHex, Write}, 4 | }; 5 | 6 | pub(crate) struct LowerHexDebug(pub(crate) T); 7 | 8 | impl fmt::Debug for LowerHexDebug 9 | where 10 | T: LowerHex, 11 | { 12 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 13 | write!(f, "{:x}", self.0) 14 | } 15 | } 16 | 17 | pub(crate) struct ByteArray<'a>(pub(crate) &'a [u8]); 18 | 19 | impl fmt::Debug for ByteArray<'_> { 20 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 21 | f.debug_list() 22 | .entries(self.0.iter().map(LowerHexDebug)) 23 | .finish() 24 | } 25 | } 26 | 27 | pub(crate) struct ByteString<'a>(pub(crate) &'a [u8]); 28 | 29 | impl fmt::Debug for ByteString<'_> { 30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 | write!(f, "\"")?; 32 | for byte in self.0.iter().flat_map(|&b| ascii::escape_default(b)) { 33 | f.write_char(byte as char)?; 34 | } 35 | write!(f, "\"")?; 36 | Ok(()) 37 | } 38 | } 39 | 40 | impl fmt::Display for ByteString<'_> { 41 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 42 | for byte in self.0.iter().flat_map(|&b| ascii::escape_default(b)) { 43 | f.write_char(byte as char)?; 44 | } 45 | Ok(()) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/fat/cluster_chain.rs: -------------------------------------------------------------------------------- 1 | use super::{BiosParameterBlock, FatEntry}; 2 | 3 | #[derive(Debug, Clone)] 4 | pub(crate) struct ClusterChain<'a> { 5 | bpb: &'a dyn BiosParameterBlock, 6 | next_entry: Option, 7 | } 8 | 9 | impl<'a> ClusterChain<'a> { 10 | pub(super) fn new(bpb: &'a dyn BiosParameterBlock, cluster: u32) -> Self { 11 | Self { 12 | bpb, 13 | next_entry: Some(FatEntry::Used(cluster)), 14 | } 15 | } 16 | 17 | pub(super) fn bpb(&'a self) -> &'a dyn BiosParameterBlock { 18 | self.bpb 19 | } 20 | } 21 | 22 | impl Iterator for ClusterChain<'_> { 23 | type Item = Result; 24 | 25 | fn next(&mut self) -> Option { 26 | let next_entry = self.next_entry?; 27 | let res = match next_entry { 28 | FatEntry::Used(next_cluster) => { 29 | self.next_entry = Some(self.bpb.fat_entry(next_cluster)); 30 | Ok(next_cluster) 31 | } 32 | FatEntry::UsedEof(next_cluster) => { 33 | self.next_entry = None; 34 | Ok(next_cluster) 35 | } 36 | FatEntry::Unused | FatEntry::Reserved | FatEntry::Bad => { 37 | self.next_entry = None; 38 | Err(next_entry) 39 | } 40 | }; 41 | Some(res) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/classdriver/mouse.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/classdriver/mouse.hpp" 2 | 3 | #include "logger.hpp" 4 | #include "usb/device.hpp" 5 | #include "usb/memory.hpp" 6 | 7 | #include 8 | 9 | namespace usb { 10 | HIDMouseDriver::HIDMouseDriver(Device *dev, int interface_index) 11 | : HIDBaseDriver{dev, interface_index, 3} {} 12 | 13 | Error HIDMouseDriver::OnDataReceived() { 14 | uint8_t buttons = Buffer()[0]; 15 | int8_t displacement_x = Buffer()[1]; 16 | int8_t displacement_y = Buffer()[2]; 17 | NotifyMouseMove(buttons, displacement_x, displacement_y); 18 | Log(kTrace, "%02x,(%3d,%3d)\n", buttons, displacement_x, displacement_y); 19 | return MAKE_ERROR(Error::kSuccess); 20 | } 21 | 22 | void *HIDMouseDriver::operator new(size_t size) { return AllocMem(sizeof(HIDMouseDriver), 0, 0); } 23 | 24 | void HIDMouseDriver::operator delete(void *ptr) noexcept { FreeMem(ptr); } 25 | 26 | void HIDMouseDriver::SubscribeMouseMove(std::function observer) { 27 | observers_[num_observers_++] = observer; 28 | } 29 | 30 | std::function HIDMouseDriver::default_observer; 31 | 32 | void HIDMouseDriver::NotifyMouseMove(uint8_t buttons, int8_t displacement_x, 33 | int8_t displacement_y) { 34 | for (int i = 0; i < num_observers_; ++i) { 35 | observers_[i](buttons, displacement_x, displacement_y); 36 | } 37 | } 38 | } // namespace usb 39 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["boot", "mikanos_usb"] 3 | 4 | [package] 5 | name = "sabios" 6 | version = "0.1.0" 7 | authors = ["gifnksm "] 8 | edition = "2018" 9 | build = "build.rs" 10 | resolver = "2" 11 | 12 | # https://github.com/rust-lang/cargo/issues/7359 13 | # [profile.dev] 14 | # panic = "abort" 15 | 16 | [profile.release] 17 | # panic = "abort" 18 | debug = true 19 | 20 | [dependencies] 21 | arrayvec = { version = "0.7.1", default-features = false } 22 | bit_field = "0.10.1" 23 | bootloader = "0.10.7" 24 | conquer-once = { version = "0.3.2", default-features = false } 25 | crossbeam-queue = { version = "0.3.2", default-features = false, features = ["alloc"] } 26 | custom_debug_derive = "0.5.0" 27 | derivative = { version = "2.2.0", features = ["use_core"] } 28 | enumflags2 = "0.7.1" 29 | futures-util = { version = "0.3.16", default-features = false, features = ["alloc", "async-await-macro"] } 30 | linked_list_allocator = "0.9.0" 31 | mikanos_usb = { path = "./mikanos_usb" } 32 | num-traits = { version = "0.2.14", default-features = false } 33 | pin-project = "1.0.8" 34 | spin = "0.9.2" 35 | static_assertions = "1.1.0" 36 | uart_16550 = "0.2.15" 37 | volatile = "0.4.4" 38 | x86_64 = "0.14.4" 39 | 40 | [build-dependencies] 41 | color-eyre = "0.5.11" 42 | fatfs = "0.3.5" 43 | fscommon = "0.1.1" 44 | llvm-tools = "0.1.1" 45 | 46 | [package.metadata.bootloader] 47 | map-physical-memory = true 48 | 49 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/classdriver/keyboard.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/classdriver/keyboard.hpp" 2 | 3 | #include "usb/device.hpp" 4 | #include "usb/memory.hpp" 5 | 6 | #include 7 | 8 | namespace usb { 9 | HIDKeyboardDriver::HIDKeyboardDriver(Device *dev, int interface_index) 10 | : HIDBaseDriver{dev, interface_index, 8} {} 11 | 12 | Error HIDKeyboardDriver::OnDataReceived() { 13 | for (int i = 2; i < 8; ++i) { 14 | const uint8_t key = Buffer()[i]; 15 | if (key == 0) { 16 | continue; 17 | } 18 | const auto &prev_buf = PreviousBuffer(); 19 | if (std::find(prev_buf.begin() + 2, prev_buf.end(), key) != prev_buf.end()) { 20 | continue; 21 | } 22 | NotifyKeyPush(Buffer()[0], key); 23 | } 24 | return MAKE_ERROR(Error::kSuccess); 25 | } 26 | 27 | void *HIDKeyboardDriver::operator new(size_t size) { 28 | return AllocMem(sizeof(HIDKeyboardDriver), 0, 0); 29 | } 30 | 31 | void HIDKeyboardDriver::operator delete(void *ptr) noexcept { FreeMem(ptr); } 32 | 33 | void HIDKeyboardDriver::SubscribeKeyPush(std::function observer) { 34 | observers_[num_observers_++] = observer; 35 | } 36 | 37 | std::function HIDKeyboardDriver::default_observer; 38 | 39 | void HIDKeyboardDriver::NotifyKeyPush(uint8_t modifier, uint8_t keycode) { 40 | for (int i = 0; i < num_observers_; ++i) { 41 | observers_[i](modifier, keycode); 42 | } 43 | } 44 | } // namespace usb 45 | -------------------------------------------------------------------------------- /src/graphics/color.rs: -------------------------------------------------------------------------------- 1 | use core::{convert::TryFrom, fmt}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 4 | pub(crate) struct Color { 5 | pub(crate) r: u8, 6 | pub(crate) g: u8, 7 | pub(crate) b: u8, 8 | } 9 | 10 | #[allow(dead_code)] 11 | impl Color { 12 | pub(crate) const RED: Self = Color::new(255, 0, 0); 13 | pub(crate) const GREEN: Self = Color::new(0, 255, 0); 14 | pub(crate) const BLUE: Self = Color::new(0, 0, 255); 15 | pub(crate) const BLACK: Self = Color::new(0, 0, 0); 16 | pub(crate) const WHITE: Self = Color::new(255, 255, 255); 17 | } 18 | 19 | impl Color { 20 | pub(crate) const fn new(r: u8, g: u8, b: u8) -> Self { 21 | Color { r, g, b } 22 | } 23 | 24 | pub(crate) const fn from_code(code: u32) -> Self { 25 | Self { 26 | r: ((code >> 16) & 0xff) as u8, 27 | g: ((code >> 8) & 0xff) as u8, 28 | b: (code & 0xff) as u8, 29 | } 30 | } 31 | 32 | pub(crate) const fn from_grayscale(v: u8) -> Self { 33 | Color::new(v, v, v) 34 | } 35 | 36 | pub(crate) fn to_grayscale(self) -> u8 { 37 | #[allow(clippy::unwrap_used)] // this never panics 38 | u8::try_from((u16::from(self.r) + u16::from(self.g) + u16::from(self.b)) / 3).unwrap() 39 | } 40 | } 41 | 42 | impl fmt::Display for Color { 43 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 44 | write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/endpoint.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/endpoint.hpp 3 | * 4 | * エンドポイント設定に関する機能. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "error.hpp" 10 | 11 | namespace usb { 12 | enum class EndpointType { 13 | kControl = 0, 14 | kIsochronous = 1, 15 | kBulk = 2, 16 | kInterrupt = 3, 17 | }; 18 | 19 | class EndpointID { 20 | public: 21 | constexpr EndpointID() : addr_{0} {} 22 | constexpr EndpointID(const EndpointID &ep_id) : addr_{ep_id.addr_} {} 23 | explicit constexpr EndpointID(int addr) : addr_{addr} {} 24 | 25 | /** エンドポイント番号と入出力方向から ID を構成する. 26 | * 27 | * ep_num は 0..15 の整数. 28 | * dir_in は Control エンドポイントでは常に true にしなければならない. 29 | */ 30 | constexpr EndpointID(int ep_num, bool dir_in) : addr_{ep_num << 1 | dir_in} {} 31 | 32 | EndpointID &operator=(const EndpointID &rhs) { 33 | addr_ = rhs.addr_; 34 | return *this; 35 | } 36 | 37 | /** エンドポイントアドレス(0..31) */ 38 | int Address() const { return addr_; } 39 | 40 | /** エンドポイント番号(0..15) */ 41 | int Number() const { return addr_ >> 1; } 42 | 43 | /** 入出力方向.Control エンドポイントは true */ 44 | bool IsIn() const { return addr_ & 1; } 45 | 46 | private: 47 | int addr_; 48 | }; 49 | 50 | constexpr EndpointID kDefaultControlPipeID{0, true}; 51 | 52 | struct EndpointConfig { 53 | /** エンドポイント ID */ 54 | EndpointID ep_id; 55 | 56 | /** このエンドポイントの種別 */ 57 | EndpointType ep_type; 58 | 59 | /** このエンドポイントの最大パケットサイズ(バイト) */ 60 | int max_packet_size; 61 | 62 | /** このエンドポイントの制御周期(125*2^(interval-1) マイクロ秒) */ 63 | int interval; 64 | }; 65 | } // namespace usb 66 | -------------------------------------------------------------------------------- /src/emergency_console.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | graphics::{font, frame_buffer, Color, Draw, FrameBufferDrawer, Point, Rectangle, ScreenInfo}, 3 | serial_print, 4 | }; 5 | use core::fmt; 6 | 7 | pub(crate) fn with_console(f: impl FnOnce(&mut EmergencyConsole<'_>)) -> ! { 8 | let screen_info = ScreenInfo::get(); 9 | let mut drawer = unsafe { frame_buffer::emergency_lock_drawer() }; 10 | let mut console = EmergencyConsole { 11 | screen_info, 12 | pos: Point::new(0, 0), 13 | drawer: &mut *drawer, 14 | }; 15 | 16 | f(&mut console); 17 | 18 | crate::hlt_loop(); 19 | } 20 | 21 | pub(crate) struct EmergencyConsole<'a> { 22 | screen_info: ScreenInfo, 23 | pos: Point, 24 | drawer: &'a mut FrameBufferDrawer, 25 | } 26 | 27 | impl fmt::Write for EmergencyConsole<'_> { 28 | fn write_str(&mut self, s: &str) -> fmt::Result { 29 | serial_print!("{}", s); 30 | 31 | for ch in s.chars() { 32 | if ch != '\n' { 33 | self.drawer.fill_rect( 34 | Rectangle::new(self.pos, font::FONT_PIXEL_SIZE), 35 | Color::WHITE, 36 | ); 37 | self.drawer.draw_char(self.pos, ch, Color::RED); 38 | self.pos.x += font::FONT_PIXEL_SIZE.x; 39 | } 40 | 41 | if ch == '\n' || self.pos.x + font::FONT_PIXEL_SIZE.x > self.screen_info.size.x { 42 | self.pos.y += font::FONT_PIXEL_SIZE.y; 43 | self.pos.x = 0; 44 | } 45 | } 46 | Ok(()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/gdt.rs: -------------------------------------------------------------------------------- 1 | use crate::sync::OnceCell; 2 | use x86_64::{ 3 | instructions::segmentation, 4 | structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}, 5 | }; 6 | 7 | #[derive(Debug)] 8 | pub(crate) struct Selectors { 9 | pub(crate) kernel_code_selector: SegmentSelector, 10 | pub(crate) kernel_stack_selector: SegmentSelector, 11 | } 12 | 13 | static GDT: OnceCell = OnceCell::uninit(); 14 | static SELECTORS: OnceCell = OnceCell::uninit(); 15 | 16 | pub(crate) fn init() { 17 | let null_segment = SegmentSelector(0); 18 | let mut selectors = Selectors { 19 | kernel_code_selector: null_segment, 20 | kernel_stack_selector: null_segment, 21 | }; 22 | GDT.init_once(|| { 23 | let mut gdt = GlobalDescriptorTable::new(); 24 | selectors.kernel_code_selector = gdt.add_entry(Descriptor::kernel_code_segment()); 25 | selectors.kernel_stack_selector = gdt.add_entry(Descriptor::kernel_data_segment()); 26 | gdt 27 | }); 28 | GDT.get().load(); 29 | 30 | unsafe { 31 | segmentation::load_ds(null_segment); 32 | segmentation::load_es(null_segment); 33 | segmentation::load_fs(null_segment); 34 | segmentation::load_gs(null_segment); 35 | } 36 | 37 | unsafe { segmentation::load_ss(selectors.kernel_stack_selector) }; 38 | unsafe { segmentation::set_cs(selectors.kernel_code_selector) }; 39 | 40 | SELECTORS.init_once(|| selectors); 41 | } 42 | 43 | pub(crate) fn selectors() -> &'static Selectors { 44 | SELECTORS.get() 45 | } 46 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/xhci/port.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/xhci/port.hpp 3 | * 4 | * xHCI の各ポートを表すクラスと周辺機能. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "error.hpp" 10 | #include "usb/xhci/registers.hpp" 11 | 12 | #include 13 | 14 | #define CLEAR_STATUS_BIT(bitname) \ 15 | [this]() { \ 16 | PORTSC_Bitmap portsc = port_reg_set_.PORTSC.Read(); \ 17 | portsc.data[0] &= 0x0e01c3e0u; \ 18 | portsc.bits.bitname = 1; \ 19 | port_reg_set_.PORTSC.Write(portsc); \ 20 | }() 21 | 22 | namespace usb::xhci { 23 | class Controller; 24 | struct PortRegisterSet; 25 | class Device; 26 | 27 | class Port { 28 | public: 29 | Port(uint8_t port_num, PortRegisterSet &port_reg_set) 30 | : port_num_{port_num}, port_reg_set_{port_reg_set} {} 31 | 32 | uint8_t Number() const; 33 | bool IsConnected() const; 34 | bool IsEnabled() const; 35 | bool IsConnectStatusChanged() const; 36 | bool IsPortResetChanged() const; 37 | int Speed() const; 38 | Error Reset(); 39 | Device *Initialize(); 40 | 41 | void ClearConnectStatusChanged() const { CLEAR_STATUS_BIT(connect_status_change); } 42 | void ClearPortResetChange() const { CLEAR_STATUS_BIT(port_reset_change); } 43 | 44 | private: 45 | const uint8_t port_num_; 46 | PortRegisterSet &port_reg_set_; 47 | }; 48 | } // namespace usb::xhci 49 | -------------------------------------------------------------------------------- /src/sync/oneshot.rs: -------------------------------------------------------------------------------- 1 | use super::Mutex; 2 | use alloc::sync::Arc; 3 | use core::{ 4 | future::Future, 5 | pin::Pin, 6 | task::{Context, Poll}, 7 | }; 8 | use futures_util::task::AtomicWaker; 9 | 10 | pub(crate) fn channel() -> (Sender, Receiver) { 11 | let inner = Arc::new(Inner::new()); 12 | let tx = Sender { 13 | inner: inner.clone(), 14 | }; 15 | let rx = Receiver { inner }; 16 | (tx, rx) 17 | } 18 | 19 | #[derive(Debug)] 20 | pub(crate) struct Sender { 21 | inner: Arc>, 22 | } 23 | 24 | impl Sender { 25 | pub(crate) fn send(self, value: T) { 26 | *self.inner.value.lock() = Some(value); 27 | self.inner.waker.wake(); 28 | } 29 | } 30 | 31 | #[derive(Debug)] 32 | pub(crate) struct Receiver { 33 | inner: Arc>, 34 | } 35 | 36 | impl Future for Receiver { 37 | type Output = T; 38 | 39 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 40 | // fast path 41 | if let Some(value) = self.inner.value.lock().take() { 42 | return Poll::Ready(value); 43 | } 44 | 45 | self.inner.waker.register(cx.waker()); 46 | if let Some(value) = self.inner.value.lock().take() { 47 | self.inner.waker.take(); 48 | Poll::Ready(value) 49 | } else { 50 | Poll::Pending 51 | } 52 | } 53 | } 54 | 55 | #[derive(Debug)] 56 | struct Inner { 57 | value: Mutex>, 58 | waker: AtomicWaker, 59 | } 60 | 61 | impl Inner { 62 | fn new() -> Self { 63 | Self { 64 | value: Mutex::new(None), 65 | waker: AtomicWaker::new(), 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/memory.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/memory.hpp 3 | * 4 | * USB ドライバ用の動的メモリ管理機能 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace usb { 13 | void SetMemoryPool(uintptr_t pool_ptr, size_t pool_size); 14 | 15 | /** @brief 指定されたバイト数のメモリ領域を確保して先頭ポインタを返す. 16 | * 17 | * 先頭アドレスが alignment に揃ったメモリ領域を確保する. 18 | * size <= boundary ならメモリ領域が boundary を跨がないことを保証する. 19 | * boundary は典型的にはページ境界を跨がないように 4096 を指定する. 20 | * 21 | * @param size 確保するメモリ領域のサイズ(バイト単位) 22 | * @param alignment メモリ領域のアライメント制約.0 なら制約しない. 23 | * @param boundary 確保したメモリ領域が跨いではいけない境界.0 なら制約しない. 24 | * @return 確保できなかった場合は nullptr 25 | */ 26 | void *AllocMem(size_t size, unsigned int alignment, unsigned int boundary); 27 | 28 | template T *AllocArray(size_t num_obj, unsigned int alignment, unsigned int boundary) { 29 | return reinterpret_cast(AllocMem(sizeof(T) * num_obj, alignment, boundary)); 30 | } 31 | 32 | /** @brief 指定されたメモリ領域を解放する.本当に解放することは保証されない. */ 33 | void FreeMem(void *p); 34 | 35 | /** @brief 標準コンテナ用のメモリアロケータ */ 36 | template class Allocator { 37 | public: 38 | using size_type = size_t; 39 | using pointer = T *; 40 | using value_type = T; 41 | 42 | Allocator() noexcept = default; 43 | Allocator(const Allocator &) noexcept = default; 44 | template Allocator(const Allocator &) noexcept {} 45 | ~Allocator() noexcept = default; 46 | Allocator &operator=(const Allocator &) = default; 47 | 48 | pointer allocate(size_type n) { return AllocArray(n, Alignment, Boundary); } 49 | 50 | void deallocate(pointer p, size_type num) { FreeMem(p); } 51 | }; 52 | } // namespace usb 53 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/error.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class Error { 7 | public: 8 | enum Code { 9 | kSuccess, 10 | kNoEnoughMemory, 11 | kInvalidSlotID, 12 | kInvalidEndpointNumber, 13 | kTransferRingNotSet, 14 | kAlreadyAllocated, 15 | kNotImplemented, 16 | kInvalidDescriptor, 17 | kBufferTooSmall, 18 | kUnknownDevice, 19 | kNoCorrespondingSetupStage, 20 | kTransferFailed, 21 | kInvalidPhase, 22 | kUnknownXHCISpeedID, 23 | kNoWaiter, 24 | kEndpointNotInCharge, 25 | kLastOfCode, // この列挙子は常に最後に配置する 26 | }; 27 | 28 | private: 29 | static constexpr std::array code_names_{ 30 | "kSuccess", 31 | "kNoEnoughMemory", 32 | "kInvalidSlotID", 33 | "kInvalidEndpointNumber", 34 | "kTransferRingNotSet", 35 | "kAlreadyAllocated", 36 | "kNotImplemented", 37 | "kInvalidDescriptor", 38 | "kBufferTooSmall", 39 | "kUnknownDevice", 40 | "kNoCorrespondingSetupStage", 41 | "kTransferFailed", 42 | "kInvalidPhase", 43 | "kUnknownXHCISpeedID", 44 | "kNoWaiter", 45 | "kEndpointNotInCharge", 46 | }; 47 | static_assert(Error::Code::kLastOfCode == code_names_.size()); 48 | 49 | public: 50 | Error(Code code, const char *file, int line) : code_{code}, line_{line}, file_{file} {} 51 | 52 | Code Cause() const { return this->code_; } 53 | 54 | operator bool() const { return this->code_ != kSuccess; } 55 | 56 | const char *Name() const { return code_names_[static_cast(this->code_)]; } 57 | 58 | const char *File() const { return this->file_; } 59 | 60 | int Line() const { return this->line_; } 61 | 62 | private: 63 | Code code_; 64 | int line_; 65 | const char *file_; 66 | }; 67 | 68 | #define MAKE_ERROR(code) Error((code), __FILE__, __LINE__) 69 | 70 | template struct WithError { 71 | T value; 72 | Error error; 73 | }; 74 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/xhci/xhci.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/xhci/xhci.hpp 3 | * 4 | * xHCI ホストコントローラ制御用クラス. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "error.hpp" 10 | #include "usb/xhci/context.hpp" 11 | #include "usb/xhci/devmgr.hpp" 12 | #include "usb/xhci/port.hpp" 13 | #include "usb/xhci/registers.hpp" 14 | #include "usb/xhci/ring.hpp" 15 | 16 | namespace usb::xhci { 17 | class Controller { 18 | public: 19 | Controller(uintptr_t mmio_base); 20 | Error Initialize(); 21 | Error Run(); 22 | Ring *CommandRing() { return &cr_; } 23 | EventRing *PrimaryEventRing() { return &er_; } 24 | DoorbellRegister *DoorbellRegisterAt(uint8_t index); 25 | Port PortAt(uint8_t port_num) { return Port{port_num, PortRegisterSets()[port_num - 1]}; } 26 | uint8_t MaxPorts() const { return max_ports_; } 27 | DeviceManager *DeviceManager() { return &devmgr_; } 28 | 29 | private: 30 | static const size_t kDeviceSize = 8; 31 | 32 | const uintptr_t mmio_base_; 33 | CapabilityRegisters *const cap_; 34 | OperationalRegisters *const op_; 35 | const uint8_t max_ports_; 36 | 37 | class DeviceManager devmgr_; 38 | Ring cr_; 39 | EventRing er_; 40 | 41 | InterrupterRegisterSetArray InterrupterRegisterSets() const { 42 | return {mmio_base_ + cap_->RTSOFF.Read().Offset() + 0x20u, 1024}; 43 | } 44 | 45 | PortRegisterSetArray PortRegisterSets() const { 46 | return {reinterpret_cast(op_) + 0x400u, max_ports_}; 47 | } 48 | 49 | DoorbellRegisterArray DoorbellRegisters() const { 50 | return {mmio_base_ + cap_->DBOFF.Read().Offset(), 256}; 51 | } 52 | }; 53 | 54 | Error ConfigurePort(Controller &xhc, Port &port); 55 | Error ConfigureEndpoints(Controller &xhc, Device &dev); 56 | 57 | /** @brief イベントリングに登録されたイベントを高々1つ処理する. 58 | * 59 | * xhc のプライマリイベントリングの先頭のイベントを処理する. 60 | * イベントが無ければ即座に Error::kSuccess を返す. 61 | * 62 | * @return イベントを正常に処理できたら Error::kSuccess 63 | */ 64 | Error ProcessEvent(Controller &xhc); 65 | } // namespace usb::xhci 66 | -------------------------------------------------------------------------------- /src/sync/mpsc.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use alloc::sync::Arc; 3 | use core::{ 4 | pin::Pin, 5 | task::{Context, Poll}, 6 | }; 7 | use crossbeam_queue::ArrayQueue; 8 | use futures_util::{task::AtomicWaker, Stream}; 9 | 10 | pub(crate) fn channel(buffer: usize) -> (Sender, Receiver) { 11 | let inner = Arc::new(Inner::new(buffer)); 12 | let tx = Sender { 13 | inner: inner.clone(), 14 | }; 15 | let rx = Receiver { inner }; 16 | (tx, rx) 17 | } 18 | 19 | #[derive(Debug)] 20 | pub(crate) struct Sender { 21 | inner: Arc>, 22 | } 23 | 24 | impl Sender { 25 | pub(crate) fn send(&self, value: T) -> Result<()> { 26 | self.inner.queue.push(value).map_err(|_| ErrorKind::Full)?; 27 | self.inner.waker.wake(); 28 | Ok(()) 29 | } 30 | } 31 | 32 | impl Clone for Sender { 33 | fn clone(&self) -> Self { 34 | Self { 35 | inner: self.inner.clone(), 36 | } 37 | } 38 | } 39 | 40 | #[derive(Debug)] 41 | pub(crate) struct Receiver { 42 | inner: Arc>, 43 | } 44 | 45 | impl Stream for Receiver { 46 | type Item = T; 47 | 48 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 49 | // fast path 50 | if let Some(value) = self.inner.queue.pop() { 51 | return Poll::Ready(Some(value)); 52 | } 53 | 54 | self.inner.waker.register(cx.waker()); 55 | if let Some(value) = self.inner.queue.pop() { 56 | self.inner.waker.take(); 57 | Poll::Ready(Some(value)) 58 | } else { 59 | Poll::Pending 60 | } 61 | } 62 | } 63 | 64 | #[derive(Debug)] 65 | struct Inner { 66 | queue: ArrayQueue, 67 | waker: AtomicWaker, 68 | } 69 | 70 | impl Inner { 71 | fn new(buffer: usize) -> Self { 72 | Self { 73 | queue: ArrayQueue::new(buffer), 74 | waker: AtomicWaker::new(), 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/sabios_support.cpp: -------------------------------------------------------------------------------- 1 | #include "logger.hpp" 2 | #include "usb/classdriver/keyboard.hpp" 3 | #include "usb/classdriver/mouse.hpp" 4 | #include "usb/memory.hpp" 5 | #include "usb/xhci/xhci.hpp" 6 | 7 | #include 8 | 9 | extern "C" usb::xhci::Controller *cxx_xhci_controller_new(uint64_t xhc_mmio_base) { 10 | static usb::xhci::Controller xhc{xhc_mmio_base}; 11 | return &xhc; 12 | } 13 | 14 | extern "C" void cxx_xhci_controller_initialize(usb::xhci::Controller *xhc) { xhc->Initialize(); } 15 | 16 | extern "C" int32_t cxx_xhci_controller_run(usb::xhci::Controller *xhc) { 17 | auto err = xhc->Run(); 18 | return err.Cause(); 19 | } 20 | 21 | extern "C" void cxx_xhci_controller_configure_connected_ports(usb::xhci::Controller *xhc) { 22 | for (int i = 1; i <= xhc->MaxPorts(); i++) { 23 | auto port = xhc->PortAt(i); 24 | Log(kDebug, "Port %d: IsConnected=%d\n", i, port.IsConnected()); 25 | 26 | if (port.IsConnected()) { 27 | if (auto err = ConfigurePort(*xhc, port)) { 28 | Log(kError, "failed to configure port: %s at %s:%d\n", err.Name(), err.File(), err.Line()); 29 | continue; 30 | } 31 | } 32 | } 33 | } 34 | 35 | extern "C" int32_t cxx_xhci_controller_process_event(usb::xhci::Controller *xhc) { 36 | auto err = ProcessEvent(*xhc); 37 | return err.Cause(); 38 | } 39 | 40 | extern "C" bool cxx_xhci_controller_has_event(usb::xhci::Controller *xhc) { 41 | return xhc->PrimaryEventRing()->HasFront(); 42 | } 43 | 44 | extern "C" typedef void (*MouseObserverType)(uint8_t buttons, int8_t displacement_x, 45 | int8_t displacement_y); 46 | 47 | extern "C" void cxx_xhci_hid_mouse_driver_set_default_observer(MouseObserverType observer) { 48 | usb::HIDMouseDriver::default_observer = observer; 49 | } 50 | 51 | extern "C" typedef void (*KeyboardObserverType)(uint8_t modifier, uint8_t keycode); 52 | 53 | extern "C" void cxx_xhci_hid_keyboard_driver_set_default_observer(KeyboardObserverType observer) { 54 | usb::HIDKeyboardDriver::default_observer = observer; 55 | } 56 | 57 | extern "C" void cxx_set_memory_pool(uintptr_t pool_ptr, size_t pool_size) { 58 | usb::SetMemoryPool(pool_ptr, pool_size); 59 | } 60 | -------------------------------------------------------------------------------- /mikanos_usb/build.rs: -------------------------------------------------------------------------------- 1 | use flate2::read::GzDecoder; 2 | use std::{env, fs, os::unix, path::PathBuf}; 3 | use tar::Archive as TarArchive; 4 | 5 | type Error = Box; 6 | type Result = std::result::Result; 7 | 8 | async fn build_lib() -> Result<()> { 9 | let out_dir = PathBuf::from(env::var("OUT_DIR")?); 10 | let unpacked_dir = out_dir.join("x86_64-elf"); 11 | 12 | if unpacked_dir.exists() { 13 | fs::remove_dir_all(&unpacked_dir)?; 14 | } 15 | 16 | let resp = reqwest::get( 17 | "https://github.com/uchan-nos/mikanos-build/releases/download/v2.0/x86_64-elf.tar.gz", 18 | ) 19 | .await? 20 | .bytes() 21 | .await?; 22 | 23 | let tar = GzDecoder::new(&*resp); 24 | let mut archive = TarArchive::new(tar); 25 | archive.unpack(&out_dir)?; 26 | 27 | env::set_var("CC", "clang"); 28 | env::set_var("CXX", "clang++"); 29 | 30 | let files = glob::glob("./cxx_src/**/*.cpp")?.collect::, _>>()?; 31 | 32 | cc::Build::new() 33 | .cpp(true) 34 | .include(unpacked_dir.join("include")) 35 | .include(unpacked_dir.join("include/c++/v1")) 36 | .include("./cxx_src/") 37 | .files(files) 38 | .define("__ELF__", None) 39 | .define("_LDBL_EQ_DBL", None) 40 | .define("_GNU_SOURCE", None) 41 | .define("_POSIX_TIMERS", None) 42 | .flag("-nostdlibinc") 43 | .flag("-ffreestanding") 44 | .flag("-mno-red-zone") 45 | .flag("-fno-exceptions") 46 | .flag("-fno-rtti") 47 | .flag("-std=c++17") 48 | .extra_warnings(false) 49 | .cpp_link_stdlib(None) 50 | .target("x86_64-elf") 51 | .compile("mikanos_usb"); 52 | 53 | for lib in &["c", "c++", "c++abi"] { 54 | let filename = format!("lib{}.a", lib); 55 | let dest = out_dir.join(&filename); 56 | let src = unpacked_dir.join(format!("lib/{}", filename)); 57 | if dest.exists() { 58 | fs::remove_file(&dest)?; 59 | } 60 | unix::fs::symlink(&src, &dest)?; 61 | println!("cargo:rustc-link-lib=static={}", lib); 62 | } 63 | 64 | Ok(()) 65 | } 66 | 67 | #[tokio::main] 68 | async fn main() { 69 | build_lib().await.unwrap(); 70 | } 71 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/xhci/device.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/xhci/device.hpp 3 | * 4 | * USB デバイスを表すクラスと関連機能. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "error.hpp" 10 | #include "usb/arraymap.hpp" 11 | #include "usb/device.hpp" 12 | #include "usb/xhci/context.hpp" 13 | #include "usb/xhci/registers.hpp" 14 | #include "usb/xhci/trb.hpp" 15 | 16 | #include 17 | #include 18 | 19 | namespace usb::xhci { 20 | class Device : public usb::Device { 21 | public: 22 | enum class State { kInvalid, kBlank, kSlotAssigning, kSlotAssigned }; 23 | 24 | using OnTransferredCallbackType = void(Device *dev, DeviceContextIndex dci, int completion_code, 25 | int trb_transfer_length, TRB *issue_trb); 26 | 27 | Device(uint8_t slot_id, DoorbellRegister *dbreg); 28 | 29 | Error Initialize(); 30 | 31 | DeviceContext *DeviceContext() { return &ctx_; } 32 | InputContext *InputContext() { return &input_ctx_; } 33 | // usb::Device* USBDevice() { return usb_device_; } 34 | // void SetUSBDevice(usb::Device* value) { usb_device_ = value; } 35 | 36 | State State() const { return state_; } 37 | uint8_t SlotID() const { return slot_id_; } 38 | 39 | void SelectForSlotAssignment(); 40 | Ring *AllocTransferRing(DeviceContextIndex index, size_t buf_size); 41 | 42 | Error ControlIn(EndpointID ep_id, SetupData setup_data, void *buf, int len, 43 | ClassDriver *issuer) override; 44 | Error ControlOut(EndpointID ep_id, SetupData setup_data, const void *buf, int len, 45 | ClassDriver *issuer) override; 46 | Error InterruptIn(EndpointID ep_id, void *buf, int len) override; 47 | Error InterruptOut(EndpointID ep_id, void *buf, int len) override; 48 | 49 | Error OnTransferEventReceived(const TransferEventTRB &trb); 50 | 51 | private: 52 | alignas(64) struct DeviceContext ctx_; 53 | alignas(64) struct InputContext input_ctx_; 54 | 55 | const uint8_t slot_id_; 56 | DoorbellRegister *const dbreg_; 57 | 58 | enum State state_; 59 | std::array transfer_rings_; // index = dci - 1 60 | 61 | /** コントロール転送が完了した際に DataStageTRB や StatusStageTRB 62 | * から対応する SetupStageTRB を検索するためのマップ. 63 | */ 64 | ArrayMap setup_stage_map_{}; 65 | 66 | // usb::Device* usb_device_; 67 | }; 68 | } // namespace usb::xhci 69 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/classdriver/hid.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/classdriver/hid.hpp" 2 | 3 | #include "logger.hpp" 4 | #include "usb/device.hpp" 5 | 6 | #include 7 | 8 | namespace usb { 9 | HIDBaseDriver::HIDBaseDriver(Device *dev, int interface_index, int in_packet_size) 10 | : ClassDriver{dev}, interface_index_{interface_index}, in_packet_size_{in_packet_size} {} 11 | 12 | Error HIDBaseDriver::Initialize() { return MAKE_ERROR(Error::kNotImplemented); } 13 | 14 | Error HIDBaseDriver::SetEndpoint(const EndpointConfig &config) { 15 | if (config.ep_type == EndpointType::kInterrupt && config.ep_id.IsIn()) { 16 | ep_interrupt_in_ = config.ep_id; 17 | } else if (config.ep_type == EndpointType::kInterrupt && !config.ep_id.IsIn()) { 18 | ep_interrupt_out_ = config.ep_id; 19 | } 20 | return MAKE_ERROR(Error::kSuccess); 21 | } 22 | 23 | Error HIDBaseDriver::OnEndpointsConfigured() { 24 | SetupData setup_data{}; 25 | setup_data.request_type.bits.direction = request_type::kOut; 26 | setup_data.request_type.bits.type = request_type::kClass; 27 | setup_data.request_type.bits.recipient = request_type::kInterface; 28 | setup_data.request = request::kSetProtocol; 29 | setup_data.value = 0; // boot protocol 30 | setup_data.index = interface_index_; 31 | setup_data.length = 0; 32 | 33 | initialize_phase_ = 1; 34 | return ParentDevice()->ControlOut(kDefaultControlPipeID, setup_data, nullptr, 0, this); 35 | } 36 | 37 | Error HIDBaseDriver::OnControlCompleted(EndpointID ep_id, SetupData setup_data, const void *buf, 38 | int len) { 39 | Log(kTrace, "HIDBaseDriver::OnControlCompleted: dev %08lx, phase = %d, len = %d\n", 40 | reinterpret_cast(this), initialize_phase_, len); 41 | if (initialize_phase_ == 1) { 42 | initialize_phase_ = 2; 43 | return ParentDevice()->InterruptIn(ep_interrupt_in_, buf_.data(), in_packet_size_); 44 | } 45 | 46 | return MAKE_ERROR(Error::kNotImplemented); 47 | } 48 | 49 | Error HIDBaseDriver::OnInterruptCompleted(EndpointID ep_id, const void *buf, int len) { 50 | if (ep_id.IsIn()) { 51 | OnDataReceived(); 52 | std::copy_n(buf_.begin(), len, previous_buf_.begin()); 53 | return ParentDevice()->InterruptIn(ep_interrupt_in_, buf_.data(), in_packet_size_); 54 | } 55 | 56 | return MAKE_ERROR(Error::kNotImplemented); 57 | } 58 | } // namespace usb 59 | -------------------------------------------------------------------------------- /src/paging.rs: -------------------------------------------------------------------------------- 1 | use crate::{memory::BitmapMemoryManager, prelude::*}; 2 | use x86_64::{ 3 | structures::paging::{Mapper, OffsetPageTable, Page, PageTable, PhysFrame}, 4 | PhysAddr, VirtAddr, 5 | }; 6 | 7 | /// Initialize a new OffsetPageTable. 8 | /// 9 | /// # Safety 10 | /// 11 | /// This function is unsafe because the caller must guarantee that the 12 | /// complete physical memory is mapped to virtual memory at the passed 13 | /// `physical_memory_offset`. Also, this function must be only called once 14 | /// to avoid aliasing `&mut` references (which is undefined behavior). 15 | pub(crate) unsafe fn init(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> { 16 | let level_4_table = unsafe { active_level_4_table(physical_memory_offset) }; 17 | unsafe { OffsetPageTable::new(level_4_table, physical_memory_offset) } 18 | } 19 | 20 | /// Returns a mutable reference to the active level 4 table. 21 | /// 22 | /// This function is unsafe because the caller must guarantee that the 23 | /// complete physical memory is mapped to virtual memory at the passed 24 | /// `physical_memory_offset`. Also, this function must be only called once 25 | /// to avoid aliasing `&mut` references (which is undefined behavior). 26 | pub(crate) unsafe fn active_level_4_table( 27 | physical_memory_offset: VirtAddr, 28 | ) -> &'static mut PageTable { 29 | use x86_64::registers::control::Cr3; 30 | 31 | let (level_4_table_frame, _) = Cr3::read(); 32 | 33 | let phys = level_4_table_frame.start_address(); 34 | let virt = physical_memory_offset + phys.as_u64(); 35 | let page_table_ptr: *mut PageTable = virt.as_mut_ptr(); 36 | 37 | unsafe { &mut *page_table_ptr } 38 | } 39 | 40 | pub(crate) fn make_identity_mapping( 41 | mapper: &mut OffsetPageTable, 42 | allocator: &mut BitmapMemoryManager, 43 | base_addr: u64, 44 | num_pages: usize, 45 | ) -> Result<()> { 46 | use x86_64::structures::paging::PageTableFlags as Flags; 47 | let base_page = Page::from_start_address(VirtAddr::new(base_addr))?; 48 | let base_frame = PhysFrame::from_start_address(PhysAddr::new(base_addr))?; 49 | let flags = Flags::PRESENT | Flags::WRITABLE; 50 | for i in 0..num_pages { 51 | let page = base_page + i as u64; 52 | let frame = base_frame + i as u64; 53 | unsafe { mapper.map_to(page, frame, flags, &mut *allocator) }?.flush(); 54 | } 55 | Ok(()) 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sabios - 錆OS 2 | 3 | "sabios" is a Toy OS written in Rust. 4 | 5 | * [My blog post series (Japanese).][my-blog] 6 | 7 | [my-blog]: http://gifnksm.hatenablog.jp/archive/category/%E3%82%BC%E3%83%AD%E3%81%8B%E3%82%89%E3%81%AEOS%E8%87%AA%E4%BD%9C%E5%85%A5%E9%96%80 8 | 9 | Inspired by following great pioneers: 10 | 11 | * [Writing an OS in Rust][blog-os] by [@phil-opp] 12 | * [ゼロからのOS自作入門][zero-os] and [MikanOS] by [@uchan-nos] 13 | 14 | [blog-os]: https://os.phil-opp.com/ 15 | [@phil-opp]: https://github.com/phil-opp 16 | [zero-os]: https://zero.osdev.jp/ 17 | [MikanOS]: https://github.com/uchan-nos/mikanos 18 | [@uchan-nos]: https://github.com/uchan-nos 19 | 20 | ## Instructions 21 | 22 | ```console 23 | # Boot sabios with UEFI bootloader 24 | $ cargo krun --release 25 | ``` 26 | 27 | ## Requirements 28 | 29 | Following tools are required: 30 | 31 | * [rustup] 32 | * [QEMU] 33 | * OVMF (for Arch Linux users, install [edk2-ovmf] package) 34 | * [clang] (for compiling C++ USB drive stack) 35 | 36 | [rustup]: https://rustup.rs/ 37 | [QEMU]: https://www.qemu.org/ 38 | [edk2-ovmf]: https://archlinux.org/packages/extra/any/edk2-ovmf/ 39 | [clang]: https://clang.llvm.org/ 40 | 41 | [`boot` crate] assumes that OVMF is installed in `/usr/share/OVMF/x64/OVMF.fd`. 42 | 43 | [`boot` crate]: boot 44 | 45 | ## References 46 | 47 | * FAT 48 | * [Microsoft Extensible Firmware Initiative FAT32 File System Specification][MS-FAT32] 49 | * [Japanese Translation][MS-FAT32-JA] 50 | * [FATファイルシステムのしくみと操作法][ELM-FAT] 51 | 52 | [MS-FAT32]: https://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/fatgen103.doc 53 | [MS-FAT32-JA]: https://docs.google.com/document/d/1ba8Jyfm4GmNgADNscqOgS9gZ9CPUgvCZSt1xRDuFV24/edit?usp=sharing 54 | [ELM-FAT]: http://elm-chan.org/docs/fat.html 55 | 56 | ## License 57 | 58 | Licensed under either of 59 | 60 | * Apache License, Version 2.0 61 | ([LICENSE-APACHE] or ) 62 | * MIT license 63 | ([LICENSE-MIT] or ) 64 | 65 | [LICENSE-APACHE]: LICENSE-APACHE 66 | [LICENSE-MIT]: LICENSE-MIT 67 | 68 | at your option. 69 | 70 | ## Contribution 71 | 72 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 73 | -------------------------------------------------------------------------------- /src/fat/fat_entry.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 2 | pub(crate) enum FatEntry { 3 | Unused, 4 | Reserved, 5 | Used(u32), 6 | UsedEof(u32), 7 | Bad, 8 | } 9 | 10 | impl FatEntry { 11 | pub(super) fn from_fat12(value: u16) -> Self { 12 | match value { 13 | 0x000 => Self::Unused, 14 | 0x001 => Self::Reserved, 15 | 0x002..=0xff6 => Self::Used(u32::from(value)), 16 | 0xff7 => Self::Bad, 17 | 0xff8..=0xfff => Self::UsedEof(u32::from(value)), 18 | _ => Self::Bad, 19 | } 20 | } 21 | 22 | // fn to_fat12(&self) -> u16 { 23 | // match self { 24 | // FatEntry::Unused => 0x000, 25 | // FatEntry::Reserved => 0x001, 26 | // FatEntry::Used(value) => *value as u16, 27 | // FatEntry::UsedEof(value) => *value as u16, 28 | // FatEntry::Bad => 0xff7, 29 | // } 30 | // } 31 | 32 | pub(super) fn from_fat16(value: u16) -> Self { 33 | match value { 34 | 0x0000 => Self::Unused, 35 | 0x0001 => Self::Reserved, 36 | 0x0002..=0xfff6 => Self::Used(u32::from(value)), 37 | 0xfff7 => Self::Bad, 38 | 0xfff8..=0xffff => Self::UsedEof(u32::from(value)), 39 | } 40 | } 41 | 42 | // fn to_fat16(&self) -> u16 { 43 | // match self { 44 | // FatEntry::Unused => 0x0000, 45 | // FatEntry::Reserved => 0x0001, 46 | // FatEntry::Used(value) => *value as u16, 47 | // FatEntry::UsedEof(value) => *value as u16, 48 | // FatEntry::Bad => 0xfff7, 49 | // } 50 | // } 51 | 52 | pub(super) fn from_fat32(value: u32) -> Self { 53 | match value { 54 | 0x0000_0000 => Self::Unused, 55 | 0x0000_0001 => Self::Reserved, 56 | 0x0000_0002..=0x0fff_fff6 => Self::Used(value), 57 | 0x0fff_fff7 => Self::Bad, 58 | 0x0fff_fff8..=0x0fff_ffff => Self::UsedEof(value), 59 | _ => Self::Bad, 60 | } 61 | } 62 | 63 | // fn to_fat32(&self) -> u32 { 64 | // match self { 65 | // FatEntry::Unused => 0x0000_0000, 66 | // FatEntry::Reserved => 0x0000_0001, 67 | // FatEntry::Used(value) => *value, 68 | // FatEntry::UsedEof(value) => *value, 69 | // FatEntry::Bad => 0xffff_fff7, 70 | // } 71 | // } 72 | } 73 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/setupdata.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace usb { 4 | namespace request_type { 5 | // bmRequestType recipient 6 | const int kDevice = 0; 7 | const int kInterface = 1; 8 | const int kEndpoint = 2; 9 | const int kOther = 3; 10 | 11 | // bmRequestType type 12 | const int kStandard = 0; 13 | const int kClass = 1; 14 | const int kVendor = 2; 15 | 16 | // bmRequestType direction 17 | const int kOut = 0; 18 | const int kIn = 1; 19 | } // namespace request_type 20 | 21 | namespace request { 22 | const int kGetStatus = 0; 23 | const int kClearFeature = 1; 24 | const int kSetFeature = 3; 25 | const int kSetAddress = 5; 26 | const int kGetDescriptor = 6; 27 | const int kSetDescriptor = 7; 28 | const int kGetConfiguration = 8; 29 | const int kSetConfiguration = 9; 30 | const int kGetInterface = 10; 31 | const int kSetInterface = 11; 32 | const int kSynchFrame = 12; 33 | const int kSetEncryption = 13; 34 | const int kGetEncryption = 14; 35 | const int kSetHandshake = 15; 36 | const int kGetHandshake = 16; 37 | const int kSetConnection = 17; 38 | const int kSetSecurityData = 18; 39 | const int kGetSecurityData = 19; 40 | const int kSetWUSBData = 20; 41 | const int kLoopbackDataWrite = 21; 42 | const int kLoopbackDataRead = 22; 43 | const int kSetInterfaceDS = 23; 44 | const int kSetSel = 48; 45 | const int kSetIsochDelay = 49; 46 | 47 | // HID class specific report values 48 | const int kGetReport = 1; 49 | const int kSetProtocol = 11; 50 | } // namespace request 51 | 52 | namespace descriptor_type { 53 | const int kDevice = 1; 54 | const int kConfiguration = 2; 55 | const int kString = 3; 56 | const int kInterface = 4; 57 | const int kEndpoint = 5; 58 | const int kInterfacePower = 8; 59 | const int kOTG = 9; 60 | const int kDebug = 10; 61 | const int kInterfaceAssociation = 11; 62 | const int kBOS = 15; 63 | const int kDeviceCapability = 16; 64 | const int kHID = 33; 65 | const int kSuperspeedUSBEndpointCompanion = 48; 66 | const int kSuperspeedPlusIsochronousEndpointCompanion = 49; 67 | } // namespace descriptor_type 68 | 69 | struct SetupData { 70 | union { 71 | uint8_t data; 72 | struct { 73 | uint8_t recipient : 5; 74 | uint8_t type : 2; 75 | uint8_t direction : 1; 76 | } bits; 77 | } request_type; 78 | uint8_t request; 79 | uint16_t value; 80 | uint16_t index; 81 | uint16_t length; 82 | } __attribute__((packed)); 83 | 84 | inline bool operator==(SetupData lhs, SetupData rhs) { 85 | return lhs.request_type.data == rhs.request_type.data && lhs.request == rhs.request && 86 | lhs.value == rhs.value && lhs.index == rhs.index && lhs.length == rhs.length; 87 | } 88 | } // namespace usb 89 | -------------------------------------------------------------------------------- /src/graphics/font.rs: -------------------------------------------------------------------------------- 1 | use crate::graphics::{Color, Draw, Point, Rectangle, Size}; 2 | use core::convert::TryFrom; 3 | 4 | pub(crate) const FONT_PIXEL_SIZE: Size = Size::new(8, 16); 5 | 6 | include!(concat!(env!("OUT_DIR"), "/ascii_font.rs")); 7 | 8 | type Font = [u8; 16]; 9 | 10 | fn get_ascii_font(ch: u8) -> &'static Font { 11 | static_assertions::const_assert_eq!(ASCII_FONT.len(), 256); 12 | &ASCII_FONT[usize::from(ch)] 13 | } 14 | 15 | pub(super) fn draw_byte_char( 16 | drawer: &mut D, 17 | pos: Point, 18 | byte: u8, 19 | color: Color, 20 | ) -> Rectangle 21 | where 22 | D: Draw, 23 | { 24 | let font = get_ascii_font(byte); 25 | let draw_rect = Rectangle { 26 | pos, 27 | size: FONT_PIXEL_SIZE, 28 | }; 29 | 30 | for (font_y, draw_y) in draw_rect.y_range().enumerate() { 31 | for (font_x, draw_x) in draw_rect.x_range().enumerate() { 32 | if (font[font_y] << font_x) & 0x80 != 0 { 33 | drawer.draw(Point::new(draw_x, draw_y), color); 34 | } 35 | } 36 | } 37 | 38 | draw_rect 39 | } 40 | 41 | pub(super) fn draw_byte_str( 42 | drawer: &mut D, 43 | pos: Point, 44 | bytes: &[u8], 45 | color: Color, 46 | ) -> Rectangle 47 | where 48 | D: Draw, 49 | { 50 | let start_pos = pos; 51 | let mut end_pos = start_pos; 52 | let mut pos = start_pos; 53 | for byte in bytes { 54 | let rect = draw_byte_char(drawer, pos, *byte, color); 55 | pos.x = rect.x_end(); 56 | end_pos = Point::elem_max(end_pos, rect.end_pos()); 57 | } 58 | let size = end_pos - start_pos; 59 | Rectangle::new(start_pos, size) 60 | } 61 | 62 | pub(crate) fn char_to_byte(ch: char) -> u8 { 63 | let codepoint = u32::from(ch); 64 | u8::try_from(codepoint).unwrap_or(b'?') 65 | } 66 | 67 | pub(super) fn draw_char( 68 | drawer: &mut D, 69 | pos: Point, 70 | ch: char, 71 | color: Color, 72 | ) -> Rectangle 73 | where 74 | D: Draw, 75 | { 76 | let byte = char_to_byte(ch); 77 | draw_byte_char(drawer, pos, byte, color) 78 | } 79 | 80 | pub(super) fn draw_str(drawer: &mut D, pos: Point, s: &str, color: Color) -> Rectangle 81 | where 82 | D: Draw, 83 | { 84 | let start_pos = pos; 85 | let mut end_pos = start_pos; 86 | let mut pos = start_pos; 87 | for ch in s.chars() { 88 | let rect = draw_char(drawer, pos, ch, color); 89 | pos.x = rect.x_end(); 90 | end_pos = Point::elem_max(end_pos, rect.end_pos()); 91 | } 92 | let size = end_pos - start_pos; 93 | Rectangle::new(start_pos, size) 94 | } 95 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/xhci/ring.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/xhci/ring.hpp 3 | * 4 | * Event Ring, Command Ring, Transfer Ring のクラスや関連機能. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "error.hpp" 10 | #include "usb/memory.hpp" 11 | #include "usb/xhci/registers.hpp" 12 | #include "usb/xhci/trb.hpp" 13 | 14 | #include 15 | #include 16 | 17 | namespace usb::xhci { 18 | /** @brief Command/Transfer Ring を表すクラス. */ 19 | class Ring { 20 | public: 21 | Ring() = default; 22 | Ring(const Ring &) = delete; 23 | ~Ring(); 24 | Ring &operator=(const Ring &) = delete; 25 | 26 | /** @brief リングのメモリ領域を割り当て,メンバを初期化する. */ 27 | Error Initialize(size_t buf_size); 28 | 29 | /** @brief TRB に cycle bit を設定した上でリング末尾に追加する. 30 | * 31 | * @return 追加された(リング上の)TRB を指すポインタ. 32 | */ 33 | template TRB *Push(const TRBType &trb) { return Push(trb.data); } 34 | 35 | TRB *Buffer() const { return buf_; } 36 | 37 | private: 38 | TRB *buf_ = nullptr; 39 | size_t buf_size_ = 0; 40 | 41 | /** @brief プロデューサ・サイクル・ステートを表すビット */ 42 | bool cycle_bit_; 43 | /** @brief リング上で次に書き込む位置 */ 44 | size_t write_index_; 45 | 46 | /** @brief TRB に cycle bit を設定した上でリング末尾に書き込む. 47 | * 48 | * write_index_ は変化させない. 49 | */ 50 | void CopyToLast(const std::array &data); 51 | 52 | /** @brief TRB に cycle bit を設定した上でリング末尾に追加する. 53 | * 54 | * write_index_ をインクリメントする.その結果 write_index_ がリング末尾 55 | * に達したら LinkTRB を適切に配置して write_index_ を 0 に戻し, 56 | * cycle bit を反転させる. 57 | * 58 | * @return 追加された(リング上の)TRB を指すポインタ. 59 | */ 60 | TRB *Push(const std::array &data); 61 | }; 62 | 63 | union EventRingSegmentTableEntry { 64 | std::array data; 65 | struct { 66 | uint64_t ring_segment_base_address; // 64 バイトアライメント 67 | 68 | uint32_t ring_segment_size : 16; 69 | uint32_t : 16; 70 | 71 | uint32_t : 32; 72 | } __attribute__((packed)) bits; 73 | }; 74 | 75 | class EventRing { 76 | public: 77 | Error Initialize(size_t buf_size, InterrupterRegisterSet *interrupter); 78 | 79 | TRB *ReadDequeuePointer() const { 80 | return reinterpret_cast(interrupter_->ERDP.Read().Pointer()); 81 | } 82 | 83 | void WriteDequeuePointer(TRB *p); 84 | 85 | bool HasFront() const { return Front()->bits.cycle_bit == cycle_bit_; } 86 | 87 | TRB *Front() const { return ReadDequeuePointer(); } 88 | 89 | void Pop(); 90 | 91 | private: 92 | TRB *buf_; 93 | size_t buf_size_; 94 | 95 | bool cycle_bit_; 96 | EventRingSegmentTableEntry *erst_; 97 | InterrupterRegisterSet *interrupter_; 98 | }; 99 | } // namespace usb::xhci 100 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/register.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file register.hpp 3 | * 4 | * メモリマップトレジスタを読み書きする機能を提供する. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | template struct ArrayLength {}; 13 | 14 | template struct ArrayLength { static const size_t value = N; }; 15 | 16 | /** 17 | * MemMapRegister is a wrapper for a memory mapped register. 18 | * 19 | * MemMapRegister forces user program to read/write the underlying register 20 | * with specific bit width. The bit width will be deduced from the type of 21 | * T::data. T is the template parameter. T::data should be an array. 22 | */ 23 | template class MemMapRegister { 24 | public: 25 | T Read() const { 26 | T tmp; 27 | for (size_t i = 0; i < len_; ++i) { 28 | tmp.data[i] = value_.data[i]; 29 | } 30 | return tmp; 31 | } 32 | 33 | void Write(const T &value) { 34 | for (size_t i = 0; i < len_; ++i) { 35 | value_.data[i] = value.data[i]; 36 | } 37 | } 38 | 39 | private: 40 | volatile T value_; 41 | static const size_t len_ = ArrayLength::value; 42 | }; 43 | 44 | template struct DefaultBitmap { 45 | T data[1]; 46 | 47 | DefaultBitmap &operator=(const T &value) { data[0] = value; } 48 | operator T() const { return data[0]; } 49 | }; 50 | 51 | /* 52 | * Design: container-like classes. 53 | * 54 | * Container-like classes, such as PortArray and DeviceContextArray, 55 | * should have Size() method and Iterator type. 56 | * Size() should return the number of elements, and iterators 57 | * of that type should iterate all elements. 58 | * 59 | * Each element may have a flag indicating availableness of the element. 60 | * For example each port has "Port Enabled/Disabled" bit. 61 | * Size() and iterators should not skip disabled elements. 62 | */ 63 | 64 | template class ArrayWrapper { 65 | public: 66 | using ValueType = T; 67 | using Iterator = ValueType *; 68 | using ConstIterator = const ValueType *; 69 | 70 | ArrayWrapper(uintptr_t array_base_addr, size_t size) 71 | : array_(reinterpret_cast(array_base_addr)), size_(size) {} 72 | 73 | size_t Size() const { return size_; } 74 | 75 | // begin, end, cbegin, cend must be lower case names 76 | // to be used in rage-based for statements. 77 | Iterator begin() { return array_; } 78 | Iterator end() { return array_ + size_; } 79 | ConstIterator cbegin() const { return array_; } 80 | ConstIterator cend() const { return array_ + size_; } 81 | 82 | ValueType &operator[](size_t index) { return array_[index]; } 83 | 84 | private: 85 | ValueType *const array_; 86 | const size_t size_; 87 | }; 88 | -------------------------------------------------------------------------------- /src/fat/directory.rs: -------------------------------------------------------------------------------- 1 | use super::{ClusterChain, DirectoryEntry, FatEntry}; 2 | use crate::fat::FileAttribute; 3 | use core::{mem, slice}; 4 | 5 | #[derive(Debug)] 6 | pub(crate) enum Directory<'a> { 7 | RootDir(&'a [DirectoryEntry]), 8 | ClusterChain(ClusterChain<'a>), 9 | } 10 | 11 | impl<'a> Directory<'a> { 12 | pub(super) fn new_root_dir(sectors: &'a [DirectoryEntry]) -> Self { 13 | Self::RootDir(sectors) 14 | } 15 | 16 | pub(super) fn new_cluster_chain(chain: ClusterChain<'a>) -> Self { 17 | Self::ClusterChain(chain) 18 | } 19 | 20 | pub(crate) fn entries(&self) -> DirectoryEntries<'a> { 21 | match self { 22 | Directory::RootDir(entries) => DirectoryEntries { 23 | iter: entries.iter(), 24 | chain: None, 25 | }, 26 | Directory::ClusterChain(chain) => DirectoryEntries { 27 | iter: [].iter(), 28 | chain: Some(chain.clone()), 29 | }, 30 | } 31 | } 32 | } 33 | 34 | #[derive(Debug)] 35 | pub(crate) struct DirectoryEntries<'a> { 36 | iter: slice::Iter<'a, DirectoryEntry>, 37 | chain: Option>, 38 | } 39 | 40 | impl<'a> Iterator for DirectoryEntries<'a> { 41 | type Item = Result<&'a DirectoryEntry, FatEntry>; 42 | 43 | fn next(&mut self) -> Option { 44 | loop { 45 | for entry in &mut self.iter { 46 | if entry.name()[0] == 0x00 { 47 | // stop iteration 48 | self.iter = [].iter(); 49 | return None; 50 | } 51 | if entry.name()[0] == 0xe5 { 52 | continue; 53 | } 54 | if entry.attr() == FileAttribute::LFN { 55 | continue; 56 | } 57 | return Some(Ok(entry)); 58 | } 59 | let chain = self.chain.as_mut()?; 60 | let cluster = match chain.next()? { 61 | Ok(cluster) => cluster, 62 | Err(err) => return Some(Err(err)), 63 | }; 64 | let bpb = chain.bpb(); 65 | let sectors_per_cluster = usize::from(bpb.sectors_per_cluster()); 66 | let bytes_per_sector = usize::from(bpb.bytes_per_sector()); 67 | let entry_size = mem::size_of::(); 68 | 69 | let sector = bpb.cluster_sector(cluster); 70 | let data = bpb.sector_ptr(sector).cast(); 71 | let len = (sectors_per_cluster * bytes_per_sector + entry_size - 1) / entry_size; 72 | 73 | self.iter = unsafe { slice::from_raw_parts(data, len) }.iter(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/device.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/device.hpp 3 | * 4 | * USB デバイスを表すクラスと関連機能. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "error.hpp" 10 | #include "usb/arraymap.hpp" 11 | #include "usb/endpoint.hpp" 12 | #include "usb/setupdata.hpp" 13 | 14 | #include 15 | 16 | namespace usb { 17 | class ClassDriver; 18 | 19 | class Device { 20 | public: 21 | virtual ~Device(); 22 | virtual Error ControlIn(EndpointID ep_id, SetupData setup_data, void *buf, int len, 23 | ClassDriver *issuer); 24 | virtual Error ControlOut(EndpointID ep_id, SetupData setup_data, const void *buf, int len, 25 | ClassDriver *issuer); 26 | virtual Error InterruptIn(EndpointID ep_id, void *buf, int len); 27 | virtual Error InterruptOut(EndpointID ep_id, void *buf, int len); 28 | 29 | Error StartInitialize(); 30 | bool IsInitialized() { return is_initialized_; } 31 | EndpointConfig *EndpointConfigs() { return ep_configs_.data(); } 32 | int NumEndpointConfigs() { return num_ep_configs_; } 33 | Error OnEndpointsConfigured(); 34 | 35 | uint8_t *Buffer() { return buf_.data(); } 36 | 37 | protected: 38 | Error OnControlCompleted(EndpointID ep_id, SetupData setup_data, const void *buf, int len); 39 | Error OnInterruptCompleted(EndpointID ep_id, const void *buf, int len); 40 | 41 | private: 42 | /** @brief エンドポイントに割り当て済みのクラスドライバ. 43 | * 44 | * 添字はエンドポイント番号(0 - 15). 45 | * 添字 0 はどのクラスドライバからも使われないため,常に未使用. 46 | */ 47 | std::array class_drivers_{}; 48 | 49 | std::array buf_{}; 50 | 51 | // following fields are used during initialization 52 | uint8_t num_configurations_; 53 | uint8_t config_index_; 54 | 55 | Error OnDeviceDescriptorReceived(const uint8_t *buf, int len); 56 | Error OnConfigurationDescriptorReceived(const uint8_t *buf, int len); 57 | Error OnSetConfigurationCompleted(uint8_t config_value); 58 | 59 | bool is_initialized_ = false; 60 | int initialize_phase_ = 0; 61 | std::array ep_configs_; 62 | int num_ep_configs_; 63 | Error InitializePhase1(const uint8_t *buf, int len); 64 | Error InitializePhase2(const uint8_t *buf, int len); 65 | Error InitializePhase3(uint8_t config_value); 66 | Error InitializePhase4(); 67 | 68 | /** OnControlCompleted の中で要求の発行元を特定するためのマップ構造. 69 | * ControlOut または ControlIn を発行したときに発行元が登録される. 70 | */ 71 | ArrayMap event_waiters_{}; 72 | }; 73 | 74 | Error GetDescriptor(Device &dev, EndpointID ep_id, uint8_t desc_type, uint8_t desc_index, void *buf, 75 | int len, bool debug = false); 76 | Error SetConfiguration(Device &dev, EndpointID ep_id, uint8_t config_value, bool debug = false); 77 | } // namespace usb 78 | -------------------------------------------------------------------------------- /src/log.rs: -------------------------------------------------------------------------------- 1 | use crate::{print, println, serial_print, serial_println}; 2 | use core::fmt; 3 | 4 | static CONSOLE_LOG_LEVEL: spin::RwLock = spin::RwLock::new(Level::Warn); 5 | static SERIAL_LOG_LEVEL: spin::RwLock = spin::RwLock::new(Level::Info); 6 | 7 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 8 | pub(crate) enum Level { 9 | Error, 10 | Warn, 11 | Info, 12 | Debug, 13 | Trace, 14 | } 15 | 16 | impl fmt::Display for Level { 17 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 18 | let s = match self { 19 | Level::Error => "ERROR", 20 | Level::Warn => "WARN", 21 | Level::Info => "INFO", 22 | Level::Debug => "DEBUG", 23 | Level::Trace => "TRACE", 24 | }; 25 | write!(f, "{}", s) 26 | } 27 | } 28 | 29 | pub(crate) fn set_level(console_level: Level, serial_level: Level) { 30 | *CONSOLE_LOG_LEVEL.write() = console_level; 31 | *SERIAL_LOG_LEVEL.write() = serial_level; 32 | } 33 | 34 | #[doc(hidden)] 35 | pub(crate) fn _log( 36 | level: Level, 37 | args: fmt::Arguments, 38 | file: &str, 39 | line: u32, 40 | cont_line: bool, 41 | newline: bool, 42 | ) { 43 | if level <= *SERIAL_LOG_LEVEL.read() { 44 | match (cont_line, newline) { 45 | (true, true) => serial_println!("{}", args), 46 | (true, false) => serial_print!("{}", args), 47 | (false, true) => serial_println!("[{}] {}:{} {}", level, file, line, args), 48 | (false, false) => serial_print!("[{}] {}:{} {}", level, file, line, args), 49 | } 50 | } 51 | if level <= *CONSOLE_LOG_LEVEL.read() { 52 | match (cont_line, newline) { 53 | (true, true) => println!("{}", args), 54 | (true, false) => print!("{}", args), 55 | (false, true) => println!("[{}] {}", level, args), 56 | (false, false) => print!("[{}] {}", level, args), 57 | } 58 | } 59 | } 60 | 61 | #[macro_export] 62 | macro_rules! log { 63 | ($level:expr, $($arg:tt)*) => { 64 | $crate::log::_log($level, format_args!($($arg)*), file!(), line!(), false, true); 65 | } 66 | } 67 | 68 | #[macro_export] 69 | macro_rules! error { 70 | ($($arg:tt)*) => ($crate::log!($crate::log::Level::Error, $($arg)*)); 71 | } 72 | #[macro_export] 73 | macro_rules! warn { 74 | ($($arg:tt)*) => ($crate::log!($crate::log::Level::Warn, $($arg)*)); 75 | } 76 | #[macro_export] 77 | macro_rules! info { 78 | ($($arg:tt)*) => ($crate::log!($crate::log::Level::Info, $($arg)*)); 79 | } 80 | #[macro_export] 81 | macro_rules! debug { 82 | ($($arg:tt)*) => ($crate::log!($crate::log::Level::Debug, $($arg)*)); 83 | } 84 | #[macro_export] 85 | macro_rules! trace { 86 | ($($arg:tt)*) => ($crate::log!($crate::log::Level::Trace, $($arg)*)); 87 | } 88 | -------------------------------------------------------------------------------- /src/cxx_support.rs: -------------------------------------------------------------------------------- 1 | use crate::log::{self, Level}; 2 | use core::{ptr, slice, str}; 3 | 4 | #[no_mangle] 5 | extern "C" fn sabios_log( 6 | level: i32, 7 | file: *const u8, 8 | file_len: usize, 9 | line: u32, 10 | msg: *const u8, 11 | msg_len: usize, 12 | cont_line: bool, 13 | ) -> i32 { 14 | let level = match level { 15 | 3 => Level::Error, 16 | 4 => Level::Warn, 17 | 7 => Level::Debug, 18 | 8 => Level::Trace, 19 | _ => Level::Info, 20 | }; 21 | 22 | unsafe { 23 | let msg = slice::from_raw_parts(msg, msg_len); 24 | let msg = str::from_utf8_unchecked(msg); 25 | let file = slice::from_raw_parts(file, file_len); 26 | let file = str::from_utf8_unchecked(file); 27 | let newline = msg.ends_with('\n'); 28 | log::_log( 29 | level, 30 | format_args!("{}", msg.trim_end()), 31 | file, 32 | line, 33 | cont_line, 34 | newline, 35 | ); 36 | } 37 | 38 | msg_len as i32 39 | } 40 | 41 | extern "C" { 42 | fn __errno() -> *mut i32; 43 | } 44 | 45 | #[allow(non_camel_case_types)] 46 | type pid_t = i32; 47 | const EBADF: i32 = 9; 48 | const ENOMEM: i32 = 12; 49 | const EINVAL: i32 = 22; 50 | 51 | #[no_mangle] 52 | extern "C" fn sbrk(_increment: isize) -> *const u8 { 53 | ptr::null() 54 | } 55 | 56 | #[no_mangle] 57 | extern "C" fn _exit() -> ! { 58 | loop { 59 | x86_64::instructions::hlt(); 60 | } 61 | } 62 | 63 | #[no_mangle] 64 | extern "C" fn kill(_pid: pid_t, _sig: i32) -> i32 { 65 | unsafe { 66 | *__errno() = EINVAL; 67 | } 68 | -1 69 | } 70 | 71 | #[no_mangle] 72 | extern "C" fn getpid() -> pid_t { 73 | unsafe { 74 | *__errno() = EINVAL; 75 | } 76 | -1 77 | } 78 | 79 | #[no_mangle] 80 | extern "C" fn close() -> i32 { 81 | unsafe { 82 | *__errno() = EBADF; 83 | } 84 | -1 85 | } 86 | 87 | #[no_mangle] 88 | extern "C" fn read(_fd: i32, _buf: *mut u8, _count: usize) -> isize { 89 | unsafe { 90 | *__errno() = EBADF; 91 | } 92 | -1 93 | } 94 | 95 | #[no_mangle] 96 | extern "C" fn write(_fd: i32, _buf: *const u8, _count: usize) -> isize { 97 | unsafe { 98 | *__errno() = EBADF; 99 | } 100 | -1 101 | } 102 | 103 | #[no_mangle] 104 | extern "C" fn lseek(_fd: i32, _offset: isize, _whence: i32) -> isize { 105 | unsafe { 106 | *__errno() = EBADF; 107 | } 108 | -1 109 | } 110 | 111 | #[no_mangle] 112 | extern "C" fn fstat(_fd: i32, _buf: *mut u8) -> i32 { 113 | unsafe { 114 | *__errno() = EBADF; 115 | } 116 | -1 117 | } 118 | 119 | #[no_mangle] 120 | extern "C" fn isatty(_fd: i32) -> i32 { 121 | unsafe { 122 | *__errno() = EBADF; 123 | } 124 | -1 125 | } 126 | 127 | #[no_mangle] 128 | extern "C" fn posix_memalign(_memptr: *mut *mut u8, _alignment: usize, _size: usize) -> i32 { 129 | ENOMEM 130 | } 131 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/xhci/devmgr.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/xhci/devmgr.hpp" 2 | 3 | #include "usb/memory.hpp" 4 | 5 | namespace usb::xhci { 6 | Error DeviceManager::Initialize(size_t max_slots) { 7 | max_slots_ = max_slots; 8 | 9 | devices_ = AllocArray(max_slots_ + 1, 0, 0); 10 | if (devices_ == nullptr) { 11 | return MAKE_ERROR(Error::kNoEnoughMemory); 12 | } 13 | 14 | device_context_pointers_ = AllocArray(max_slots_ + 1, 64, 4096); 15 | if (device_context_pointers_ == nullptr) { 16 | FreeMem(devices_); 17 | return MAKE_ERROR(Error::kNoEnoughMemory); 18 | } 19 | 20 | for (size_t i = 0; i <= max_slots_; ++i) { 21 | devices_[i] = nullptr; 22 | device_context_pointers_[i] = nullptr; 23 | } 24 | 25 | return MAKE_ERROR(Error::kSuccess); 26 | } 27 | 28 | DeviceContext **DeviceManager::DeviceContexts() const { return device_context_pointers_; } 29 | 30 | Device *DeviceManager::FindByPort(uint8_t port_num, uint32_t route_string) const { 31 | for (size_t i = 1; i <= max_slots_; ++i) { 32 | auto dev = devices_[i]; 33 | if (dev == nullptr) 34 | continue; 35 | if (dev->DeviceContext()->slot_context.bits.root_hub_port_num == port_num) { 36 | return dev; 37 | } 38 | } 39 | return nullptr; 40 | } 41 | 42 | Device *DeviceManager::FindByState(enum Device::State state) const { 43 | for (size_t i = 1; i <= max_slots_; ++i) { 44 | auto dev = devices_[i]; 45 | if (dev == nullptr) 46 | continue; 47 | if (dev->State() == state) { 48 | return dev; 49 | } 50 | } 51 | return nullptr; 52 | } 53 | 54 | Device *DeviceManager::FindBySlot(uint8_t slot_id) const { 55 | if (slot_id > max_slots_) { 56 | return nullptr; 57 | } 58 | return devices_[slot_id]; 59 | } 60 | 61 | /* 62 | WithError DeviceManager::Get(uint8_t device_id) const { 63 | if (device_id >= num_devices_) { 64 | return {nullptr, Error::kInvalidDeviceId}; 65 | } 66 | return {&devices_[device_id], Error::kSuccess}; 67 | } 68 | */ 69 | 70 | Error DeviceManager::AllocDevice(uint8_t slot_id, DoorbellRegister *dbreg) { 71 | if (slot_id > max_slots_) { 72 | return MAKE_ERROR(Error::kInvalidSlotID); 73 | } 74 | 75 | if (devices_[slot_id] != nullptr) { 76 | return MAKE_ERROR(Error::kAlreadyAllocated); 77 | } 78 | 79 | devices_[slot_id] = AllocArray(1, 64, 4096); 80 | new (devices_[slot_id]) Device(slot_id, dbreg); 81 | return MAKE_ERROR(Error::kSuccess); 82 | } 83 | 84 | Error DeviceManager::LoadDCBAA(uint8_t slot_id) { 85 | if (slot_id > max_slots_) { 86 | return MAKE_ERROR(Error::kInvalidSlotID); 87 | } 88 | 89 | auto dev = devices_[slot_id]; 90 | device_context_pointers_[slot_id] = dev->DeviceContext(); 91 | return MAKE_ERROR(Error::kSuccess); 92 | } 93 | 94 | Error DeviceManager::Remove(uint8_t slot_id) { 95 | device_context_pointers_[slot_id] = nullptr; 96 | FreeMem(devices_[slot_id]); 97 | devices_[slot_id] = nullptr; 98 | return MAKE_ERROR(Error::kSuccess); 99 | } 100 | } // namespace usb::xhci 101 | -------------------------------------------------------------------------------- /mikanos_usb/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(unsafe_op_in_unsafe_fn)] 2 | #![warn(clippy::unwrap_used)] 3 | #![warn(clippy::expect_used)] 4 | #![no_std] 5 | 6 | type MouseObserverType = extern "C" fn(buttons: u8, displacement_x: i8, displacement_y: i8); 7 | type KeyboardObserverType = extern "C" fn(modifier: u8, keycode: u8); 8 | 9 | extern "C" { 10 | fn cxx_xhci_controller_new(xhc_mmio_base: u64) -> *mut xhci::Controller; 11 | fn cxx_xhci_controller_initialize(xhc: *mut xhci::Controller); 12 | fn cxx_xhci_controller_run(xhc: *mut xhci::Controller) -> i32; 13 | fn cxx_xhci_controller_configure_connected_ports(xhc: *mut xhci::Controller); 14 | fn cxx_xhci_controller_process_event(xhc: *mut xhci::Controller) -> i32; 15 | fn cxx_xhci_controller_has_event(xhc: *mut xhci::Controller) -> bool; 16 | fn cxx_xhci_hid_mouse_driver_set_default_observer(observer: MouseObserverType); 17 | fn cxx_xhci_hid_keyboard_driver_set_default_observer(observer: KeyboardObserverType); 18 | fn cxx_set_memory_pool(pool_ptr: u64, pool_size: usize); 19 | } 20 | 21 | pub struct CxxError(pub i32); 22 | 23 | pub mod xhci { 24 | use super::*; 25 | 26 | // opaque type 27 | pub enum Controller {} 28 | 29 | impl Controller { 30 | pub unsafe fn new(xhc_mmio_base: u64) -> &'static mut Controller { 31 | unsafe { &mut *cxx_xhci_controller_new(xhc_mmio_base) } 32 | } 33 | 34 | pub fn init(&mut self) { 35 | unsafe { cxx_xhci_controller_initialize(self) } 36 | } 37 | 38 | pub fn run(&mut self) -> Result<(), CxxError> { 39 | let res = unsafe { cxx_xhci_controller_run(self) }; 40 | convert_res(res) 41 | } 42 | 43 | pub fn configure_connected_ports(&mut self) { 44 | unsafe { cxx_xhci_controller_configure_connected_ports(self) } 45 | } 46 | 47 | pub fn process_event(&mut self) -> Result<(), CxxError> { 48 | let res = unsafe { cxx_xhci_controller_process_event(self) }; 49 | convert_res(res) 50 | } 51 | 52 | pub fn has_event(&mut self) -> bool { 53 | unsafe { cxx_xhci_controller_has_event(self) } 54 | } 55 | } 56 | } 57 | 58 | // opaque type 59 | pub enum HidMouseDriver {} 60 | 61 | pub type HidMouseObserver = extern "C" fn(buttons: u8, displacement_x: i8, displacement_y: i8); 62 | 63 | impl HidMouseDriver { 64 | pub fn set_default_observer(observer: HidMouseObserver) { 65 | unsafe { cxx_xhci_hid_mouse_driver_set_default_observer(observer) } 66 | } 67 | } 68 | 69 | // opaque type 70 | pub enum HidKeyboardDriver {} 71 | 72 | pub type HidKeyboardObserver = extern "C" fn(modifier: u8, keycode: u8); 73 | 74 | impl HidKeyboardDriver { 75 | pub fn set_default_observer(observer: HidKeyboardObserver) { 76 | unsafe { cxx_xhci_hid_keyboard_driver_set_default_observer(observer) } 77 | } 78 | } 79 | 80 | pub unsafe fn set_memory_pool(pool_ptr: u64, pool_size: usize) { 81 | unsafe { 82 | cxx_set_memory_pool(pool_ptr, pool_size); 83 | } 84 | } 85 | 86 | #[track_caller] 87 | fn convert_res(res: i32) -> Result<(), CxxError> { 88 | match res { 89 | 0 => Ok(()), 90 | n => Err(CxxError(n)), 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/xhci/trb.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/xhci/trb.hpp" 2 | 3 | namespace usb::xhci { 4 | const std::array kTRBCompletionCodeToName{ 5 | "Invalid", 6 | "Success", 7 | "Data Buffer Error", 8 | "Babble Detected Error", 9 | "USB Transaction Error", 10 | "TRB Error", 11 | "Stall Error", 12 | "Resource Error", 13 | "Bandwidth Error", 14 | "No Slots Available Error", 15 | "Invalid Stream Type Error", 16 | "Slot Not Enabled Error", 17 | "Endpoint Not Enabled Error", 18 | "Short Packet", 19 | "Ring Underrun", 20 | "Ring Overrun", 21 | "VF Event Ring Full Error", 22 | "Parameter Error", 23 | "Bandwidth Overrun Error", 24 | "Context State Error", 25 | "No ping Response Error", 26 | "Event Ring Full Error", 27 | "Incompatible Device Error", 28 | "Missed Service Error", 29 | "Command Ring Stopped", 30 | "Command Aborted", 31 | "Stopped", 32 | "Stopped - Length Invalid", 33 | "Stopped - Short Packet", 34 | "Max Exit Latency Too Large Error", 35 | "Reserved", 36 | "Isoch Buffer Overrun", 37 | "Event Lost Error", 38 | "Undefined Error", 39 | "Invalid Stream ID Error", 40 | "Secondary Bandwidth Error", 41 | "Split Transaction Error", 42 | }; 43 | 44 | const std::array kTRBTypeToName{ 45 | "Reserved", // 0 46 | "Normal", 47 | "Setup Stage", 48 | "Data Stage", 49 | "Status Stage", 50 | "Isoch", 51 | "Link", 52 | "EventData", 53 | "No-Op", // 8 54 | "Enable Slot Command", 55 | "Disable Slot Command", 56 | "Address Device Command", 57 | "Configure Endpoint Command", 58 | "Evaluate Context Command", 59 | "Reset Endpoint Command", 60 | "Stop Endpoint Command", 61 | "Set TR Dequeue Pointer Command", // 16 62 | "Reset Device Command", 63 | "Force Event Command", 64 | "Negotiate Bandwidth Command", 65 | "Set Latency Tolerance Value Command", 66 | "Get Port Bandwidth Command", 67 | "Force Header Command", 68 | "No Op Command", 69 | "Reserved", // 24 70 | "Reserved", 71 | "Reserved", 72 | "Reserved", 73 | "Reserved", 74 | "Reserved", 75 | "Reserved", 76 | "Reserved", 77 | "Transfer Event", // 32 78 | "Command Completion Event", 79 | "Port Status Change Event", 80 | "Bandwidth Request Event", 81 | "Doorbell Event", 82 | "Host Controller Event", 83 | "Device Notification Event", 84 | "MFINDEX Wrap Event", 85 | "Reserved", // 40 86 | "Reserved", 87 | "Reserved", 88 | "Reserved", 89 | "Reserved", 90 | "Reserved", 91 | "Reserved", 92 | "Reserved", 93 | "Vendor Defined", // 48 94 | "Vendor Defined", 95 | "Vendor Defined", 96 | "Vendor Defined", 97 | "Vendor Defined", 98 | "Vendor Defined", 99 | "Vendor Defined", 100 | "Vendor Defined", 101 | "Vendor Defined", // 56 102 | "Vendor Defined", 103 | "Vendor Defined", 104 | "Vendor Defined", 105 | "Vendor Defined", 106 | "Vendor Defined", 107 | "Vendor Defined", 108 | "Vendor Defined", 109 | }; 110 | } // namespace usb::xhci 111 | -------------------------------------------------------------------------------- /src/graphics/traits.rs: -------------------------------------------------------------------------------- 1 | use super::{font, Color, Offset, Point, Rectangle, Size}; 2 | 3 | pub(crate) trait Draw { 4 | fn size(&self) -> Size; 5 | fn draw(&mut self, p: Point, c: Color); 6 | fn move_area(&mut self, offset: Point, src: Rectangle); 7 | 8 | fn area(&self) -> Rectangle { 9 | Rectangle::new(Point::new(0, 0), self.size()) 10 | } 11 | 12 | fn fill_rect(&mut self, rect: Rectangle, c: Color) { 13 | for p in rect.points() { 14 | self.draw(p, c); 15 | } 16 | } 17 | 18 | fn draw_rect(&mut self, rect: Rectangle, c: Color) { 19 | if rect.size.x == 0 || rect.size.y == 0 { 20 | return; 21 | } 22 | 23 | for x in rect.x_range() { 24 | self.draw(Point::new(x, rect.y_start()), c); 25 | self.draw(Point::new(x, rect.y_end() - 1), c); 26 | } 27 | for y in rect.y_range() { 28 | self.draw(Point::new(rect.x_start(), y), c); 29 | self.draw(Point::new(rect.x_end() - 1, y), c); 30 | } 31 | } 32 | 33 | fn draw_byte_char(&mut self, pos: Point, byte: u8, color: Color) -> Rectangle 34 | where 35 | Self: Sized, 36 | { 37 | font::draw_byte_char(self, pos, byte, color) 38 | } 39 | 40 | fn draw_byte_str(&mut self, pos: Point, bytes: &[u8], color: Color) -> Rectangle 41 | where 42 | Self: Sized, 43 | { 44 | font::draw_byte_str(self, pos, bytes, color) 45 | } 46 | 47 | fn draw_char(&mut self, pos: Point, ch: char, color: Color) -> Rectangle 48 | where 49 | Self: Sized, 50 | { 51 | font::draw_char(self, pos, ch, color) 52 | } 53 | 54 | fn draw_str(&mut self, pos: Point, s: &str, color: Color) -> Rectangle 55 | where 56 | Self: Sized, 57 | { 58 | font::draw_str(self, pos, s, color) 59 | } 60 | 61 | fn draw_box( 62 | &mut self, 63 | area: Rectangle, 64 | background: Color, 65 | border_top_left: Color, 66 | border_bottom_right: Color, 67 | ) { 68 | // fill main box 69 | self.fill_rect( 70 | Rectangle::new(area.pos + Offset::new(1, 1), area.size - Offset::new(2, 2)), 71 | background, 72 | ); 73 | 74 | // draw border lines 75 | self.fill_rect( 76 | Rectangle::new(area.pos, Size::new(area.size.x, 1)), 77 | border_top_left, 78 | ); 79 | self.fill_rect( 80 | Rectangle::new(area.pos, Size::new(1, area.size.y)), 81 | border_top_left, 82 | ); 83 | self.fill_rect( 84 | Rectangle::new( 85 | area.pos + Offset::new(0, area.size.y), 86 | Size::new(area.size.x, 1), 87 | ), 88 | border_bottom_right, 89 | ); 90 | self.fill_rect( 91 | Rectangle::new( 92 | area.pos + Offset::new(area.size.x, 0), 93 | Size::new(1, area.size.y), 94 | ), 95 | border_bottom_right, 96 | ); 97 | } 98 | } 99 | static_assertions::assert_obj_safe!(Draw); 100 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/xhci/ring.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/xhci/ring.hpp" 2 | 3 | #include "usb/memory.hpp" 4 | 5 | namespace usb::xhci { 6 | Ring::~Ring() { 7 | if (buf_ != nullptr) { 8 | FreeMem(buf_); 9 | } 10 | } 11 | 12 | Error Ring::Initialize(size_t buf_size) { 13 | if (buf_ != nullptr) { 14 | FreeMem(buf_); 15 | } 16 | 17 | cycle_bit_ = true; 18 | write_index_ = 0; 19 | buf_size_ = buf_size; 20 | 21 | buf_ = AllocArray(buf_size_, 64, 64 * 1024); 22 | if (buf_ == nullptr) { 23 | return MAKE_ERROR(Error::kNoEnoughMemory); 24 | } 25 | memset(buf_, 0, buf_size_ * sizeof(TRB)); 26 | 27 | return MAKE_ERROR(Error::kSuccess); 28 | } 29 | 30 | void Ring::CopyToLast(const std::array &data) { 31 | for (int i = 0; i < 3; ++i) { 32 | // data[0..2] must be written prior to data[3]. 33 | buf_[write_index_].data[i] = data[i]; 34 | } 35 | buf_[write_index_].data[3] = (data[3] & 0xfffffffeu) | static_cast(cycle_bit_); 36 | } 37 | 38 | TRB *Ring::Push(const std::array &data) { 39 | auto trb_ptr = &buf_[write_index_]; 40 | CopyToLast(data); 41 | 42 | ++write_index_; 43 | if (write_index_ == buf_size_ - 1) { 44 | LinkTRB link{buf_}; 45 | link.bits.toggle_cycle = true; 46 | CopyToLast(link.data); 47 | 48 | write_index_ = 0; 49 | cycle_bit_ = !cycle_bit_; 50 | } 51 | 52 | return trb_ptr; 53 | } 54 | 55 | Error EventRing::Initialize(size_t buf_size, InterrupterRegisterSet *interrupter) { 56 | if (buf_ != nullptr) { 57 | FreeMem(buf_); 58 | } 59 | 60 | cycle_bit_ = true; 61 | buf_size_ = buf_size; 62 | interrupter_ = interrupter; 63 | 64 | buf_ = AllocArray(buf_size_, 64, 64 * 1024); 65 | if (buf_ == nullptr) { 66 | return MAKE_ERROR(Error::kNoEnoughMemory); 67 | } 68 | memset(buf_, 0, buf_size_ * sizeof(TRB)); 69 | 70 | erst_ = AllocArray(1, 64, 64 * 1024); 71 | if (erst_ == nullptr) { 72 | FreeMem(buf_); 73 | return MAKE_ERROR(Error::kNoEnoughMemory); 74 | } 75 | memset(erst_, 0, 1 * sizeof(EventRingSegmentTableEntry)); 76 | 77 | erst_[0].bits.ring_segment_base_address = reinterpret_cast(buf_); 78 | erst_[0].bits.ring_segment_size = buf_size_; 79 | 80 | ERSTSZ_Bitmap erstsz = interrupter_->ERSTSZ.Read(); 81 | erstsz.SetSize(1); 82 | interrupter_->ERSTSZ.Write(erstsz); 83 | 84 | WriteDequeuePointer(&buf_[0]); 85 | 86 | ERSTBA_Bitmap erstba = interrupter_->ERSTBA.Read(); 87 | erstba.SetPointer(reinterpret_cast(erst_)); 88 | interrupter_->ERSTBA.Write(erstba); 89 | 90 | return MAKE_ERROR(Error::kSuccess); 91 | } 92 | 93 | void EventRing::WriteDequeuePointer(TRB *p) { 94 | auto erdp = interrupter_->ERDP.Read(); 95 | erdp.SetPointer(reinterpret_cast(p)); 96 | interrupter_->ERDP.Write(erdp); 97 | } 98 | 99 | void EventRing::Pop() { 100 | auto p = ReadDequeuePointer() + 1; 101 | 102 | TRB *segment_begin = reinterpret_cast(erst_[0].bits.ring_segment_base_address); 103 | TRB *segment_end = segment_begin + erst_[0].bits.ring_segment_size; 104 | 105 | if (p == segment_end) { 106 | p = segment_begin; 107 | cycle_bit_ = !cycle_bit_; 108 | } 109 | 110 | WriteDequeuePointer(p); 111 | } 112 | } // namespace usb::xhci 113 | -------------------------------------------------------------------------------- /src/fat/directory_entry.rs: -------------------------------------------------------------------------------- 1 | use crate::byte_getter; 2 | use core::{fmt, mem}; 3 | use enumflags2::{bitflags, make_bitflags, BitFlags}; 4 | 5 | #[bitflags] 6 | #[repr(u8)] 7 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 8 | pub(crate) enum FileAttribute { 9 | ReadOnly = 0x01, 10 | Hidden = 0x02, 11 | System = 0x04, 12 | VolumeId = 0x08, 13 | Directory = 0x10, 14 | Archive = 0x20, 15 | } 16 | impl FileAttribute { 17 | pub(crate) const LFN: BitFlags = 18 | make_bitflags!(FileAttribute::{ReadOnly | Hidden | System | VolumeId}); 19 | } 20 | 21 | #[repr(C)] 22 | pub(crate) struct DirectoryEntry { 23 | name: [u8; 11], // offset: 0 ([u8; 11]) 24 | attr: [u8; 1], // offset: 11 (u8) 25 | nt_res: [u8; 1], // offset: 12 (u8) 26 | create_time_tenth: [u8; 1], // offset: 13 (u8) 27 | create_time: [u8; 2], // offset: 14 (u16) 28 | create_date: [u8; 2], // offset: 16 (u16) 29 | last_access_date: [u8; 2], // offset: 18 (u16) 30 | first_cluster_high: [u8; 2], // offset: 20 (u16) 31 | write_time: [u8; 2], // offset: 22 (u16) 32 | write_date: [u8; 2], // offset: 24 (u16) 33 | first_cluster_low: [u8; 2], // offset: 26 (u16) 34 | file_size: [u8; 4], // offset: 28 (u32) 35 | } 36 | static_assertions::const_assert_eq!(mem::size_of::(), 32); 37 | static_assertions::const_assert_eq!(mem::align_of::(), 1); 38 | 39 | impl DirectoryEntry { 40 | byte_getter!(pub(super) name: [u8; 11]); 41 | pub(crate) fn attr(&self) -> BitFlags { 42 | BitFlags::from_bits_truncate(u8::from_le_bytes(self.attr)) 43 | } 44 | byte_getter!(nt_res: u8); 45 | byte_getter!(create_time_tenth: u8); 46 | byte_getter!(create_time: u16); 47 | byte_getter!(create_date: u16); 48 | byte_getter!(last_access_date: u16); 49 | byte_getter!(first_cluster_high: u16); 50 | byte_getter!(write_time: u16); 51 | byte_getter!(write_date: u16); 52 | byte_getter!(first_cluster_low: u16); 53 | byte_getter!(file_size: u32); 54 | } 55 | 56 | impl fmt::Debug for DirectoryEntry { 57 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 58 | f.debug_struct("DirectoryEntry") 59 | .field("name", &self.name()) 60 | .field("attr", &self.attr()) 61 | .field("nt_res", &self.nt_res()) 62 | .field("create_time_tenth", &self.create_time_tenth()) 63 | .field("create_time", &self.create_time()) 64 | .field("create_date", &self.create_date()) 65 | .field("last_access_date", &self.last_access_date()) 66 | .field("first_cluster_high", &self.first_cluster_high()) 67 | .field("write_time", &self.write_time()) 68 | .field("write_date", &self.write_date()) 69 | .field("first_cluster_low", &self.first_cluster_low()) 70 | .field("file_size", &self.file_size()) 71 | .finish() 72 | } 73 | } 74 | 75 | fn trim_trailing(bytes: &[u8], byte: u8) -> &[u8] { 76 | let mut bytes = bytes; 77 | while let Some(stripped) = bytes.strip_suffix(&[byte]) { 78 | bytes = stripped; 79 | } 80 | bytes 81 | } 82 | 83 | impl DirectoryEntry { 84 | pub(crate) fn basename(&self) -> &[u8] { 85 | trim_trailing(&self.name[..8], 0x20) 86 | } 87 | 88 | pub(crate) fn extension(&self) -> &[u8] { 89 | trim_trailing(&self.name[8..], 0x20) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/xhci/context.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "usb/endpoint.hpp" 4 | 5 | namespace usb::xhci { 6 | class Ring; 7 | union TRB; 8 | 9 | union SlotContext { 10 | uint32_t dwords[8]; 11 | struct { 12 | uint32_t route_string : 20; 13 | uint32_t speed : 4; 14 | uint32_t : 1; // reserved 15 | uint32_t mtt : 1; 16 | uint32_t hub : 1; 17 | uint32_t context_entries : 5; 18 | 19 | uint32_t max_exit_latency : 16; 20 | uint32_t root_hub_port_num : 8; 21 | uint32_t num_ports : 8; 22 | 23 | // TT : Transaction Translator 24 | uint32_t tt_hub_slot_id : 8; 25 | uint32_t tt_port_num : 8; 26 | uint32_t ttt : 2; 27 | uint32_t : 4; // reserved 28 | uint32_t interrupter_target : 10; 29 | 30 | uint32_t usb_device_address : 8; 31 | uint32_t : 19; 32 | uint32_t slot_state : 5; 33 | } __attribute__((packed)) bits; 34 | } __attribute__((packed)); 35 | 36 | union EndpointContext { 37 | uint32_t dwords[8]; 38 | struct { 39 | uint32_t ep_state : 3; 40 | uint32_t : 5; 41 | uint32_t mult : 2; 42 | uint32_t max_primary_streams : 5; 43 | uint32_t linear_stream_array : 1; 44 | uint32_t interval : 8; 45 | uint32_t max_esit_payload_hi : 8; 46 | 47 | uint32_t : 1; 48 | uint32_t error_count : 2; 49 | uint32_t ep_type : 3; 50 | uint32_t : 1; 51 | uint32_t host_initiate_disable : 1; 52 | uint32_t max_burst_size : 8; 53 | uint32_t max_packet_size : 16; 54 | 55 | uint32_t dequeue_cycle_state : 1; 56 | uint32_t : 3; 57 | uint64_t tr_dequeue_pointer : 60; 58 | 59 | uint32_t average_trb_length : 16; 60 | uint32_t max_esit_payload_lo : 16; 61 | } __attribute__((packed)) bits; 62 | 63 | TRB *TransferRingBuffer() const { return reinterpret_cast(bits.tr_dequeue_pointer << 4); } 64 | 65 | void SetTransferRingBuffer(TRB *buffer) { 66 | bits.tr_dequeue_pointer = reinterpret_cast(buffer) >> 4; 67 | } 68 | } __attribute__((packed)); 69 | 70 | struct DeviceContextIndex { 71 | int value; 72 | 73 | explicit DeviceContextIndex(int dci) : value{dci} {} 74 | DeviceContextIndex(EndpointID ep_id) : value{ep_id.Address()} {} 75 | 76 | DeviceContextIndex(int ep_num, bool dir_in) : value{2 * ep_num + (ep_num == 0 ? 1 : dir_in)} {} 77 | 78 | DeviceContextIndex(const DeviceContextIndex &rhs) = default; 79 | DeviceContextIndex &operator=(const DeviceContextIndex &rhs) = default; 80 | }; 81 | 82 | struct DeviceContext { 83 | SlotContext slot_context; 84 | EndpointContext ep_contexts[31]; 85 | } __attribute__((packed)); 86 | 87 | struct InputControlContext { 88 | uint32_t drop_context_flags; 89 | uint32_t add_context_flags; 90 | uint32_t reserved1[5]; 91 | uint8_t configuration_value; 92 | uint8_t interface_number; 93 | uint8_t alternate_setting; 94 | uint8_t reserved2; 95 | } __attribute__((packed)); 96 | 97 | struct InputContext { 98 | InputControlContext input_control_context; 99 | SlotContext slot_context; 100 | EndpointContext ep_contexts[31]; 101 | 102 | /** @brief Enable the slot context. 103 | * 104 | * @return Pointer to the slot context enabled. 105 | */ 106 | SlotContext *EnableSlotContext() { 107 | input_control_context.add_context_flags |= 1; 108 | return &slot_context; 109 | } 110 | 111 | /** @brief Enable an endpoint. 112 | * 113 | * @param dci Device Context Index (1 .. 31) 114 | * @return Pointer to the endpoint context enabled. 115 | */ 116 | EndpointContext *EnableEndpoint(DeviceContextIndex dci) { 117 | input_control_context.add_context_flags |= 1u << dci.value; 118 | return &ep_contexts[dci.value - 1]; 119 | } 120 | } __attribute__((packed)); 121 | } // namespace usb::xhci 122 | -------------------------------------------------------------------------------- /boot/src/main.rs: -------------------------------------------------------------------------------- 1 | use bootloader_locator::locate_bootloader; 2 | use locate_cargo_manifest::locate_manifest; 3 | use std::{ 4 | env, 5 | path::{Path, PathBuf}, 6 | process::{self, Command, ExitStatus}, 7 | time::Duration, 8 | }; 9 | 10 | const RUN_ARGS: &[&str] = &[ 11 | "-m", 12 | "1G", 13 | "-serial", 14 | "stdio", 15 | "-device", 16 | "nec-usb-xhci,id=xhci", 17 | "-device", 18 | "usb-mouse", 19 | "-device", 20 | "usb-kbd", 21 | "-gdb", 22 | "tcp::1234", 23 | "-no-reboot", 24 | ]; 25 | const TEST_ARGS: &[&str] = &[ 26 | "-m", 27 | "1G", 28 | "-serial", 29 | "stdio", 30 | "-device", 31 | "nec-usb-xhci,id=xhci", 32 | "-device", 33 | "usb-mouse", 34 | "-device", 35 | "usb-kbd", 36 | "-gdb", 37 | "tcp::1234", 38 | "-device", 39 | "isa-debug-exit,iobase=0xf4,iosize=0x04", 40 | "-display", 41 | "none", 42 | "-no-reboot", 43 | ]; 44 | const TEST_TIMEOUT_SECS: u64 = 30; 45 | const OVMF_PATH: &str = "/usr/share/OVMF/x64/OVMF.fd"; 46 | 47 | fn main() { 48 | let mut args = env::args().skip(1); // skip executable name 49 | 50 | let kernel_binary_path = { 51 | let path = PathBuf::from(args.next().unwrap()); 52 | path.canonicalize().unwrap() 53 | }; 54 | 55 | println!("use kernel executable: {}", kernel_binary_path.display()); 56 | let image = create_disk_image(&kernel_binary_path); 57 | 58 | let mut run_cmd = Command::new("qemu-system-x86_64"); 59 | run_cmd 60 | .arg("-drive") 61 | .arg(format!("format=raw,file={}", image.display())) 62 | .arg("-bios") 63 | .arg(OVMF_PATH); 64 | 65 | let binary_kind = runner_utils::binary_kind(&kernel_binary_path); 66 | if binary_kind.is_test() { 67 | run_cmd.args(TEST_ARGS); 68 | 69 | let exit_status = run_test_command(run_cmd); 70 | match exit_status.code() { 71 | Some(33) => {} // success 72 | other => panic!("Test failed (exit code: {:?})", other), 73 | } 74 | } else { 75 | run_cmd.args(RUN_ARGS); 76 | let exit_status = run_cmd.status().unwrap(); 77 | if !exit_status.success() { 78 | process::exit(exit_status.code().unwrap_or(1)); 79 | } 80 | } 81 | } 82 | 83 | fn run_test_command(mut cmd: Command) -> ExitStatus { 84 | runner_utils::run_with_timeout(&mut cmd, Duration::from_secs(TEST_TIMEOUT_SECS)).unwrap() 85 | } 86 | 87 | fn create_disk_image(kernel_binary_path: &Path) -> PathBuf { 88 | let bootloader_manifest_path = locate_bootloader("bootloader").unwrap(); 89 | let kernel_manifest_path = locate_manifest().unwrap(); 90 | 91 | let mut build_cmd = Command::new(env!("CARGO")); 92 | build_cmd.current_dir(bootloader_manifest_path.parent().unwrap()); 93 | build_cmd.arg("builder"); 94 | build_cmd.arg("--firmware").arg("uefi"); 95 | build_cmd 96 | .arg("--kernel-manifest") 97 | .arg(&kernel_manifest_path); 98 | build_cmd.arg("--kernel-binary").arg(&kernel_binary_path); 99 | build_cmd 100 | .arg("--target-dir") 101 | .arg(kernel_manifest_path.parent().unwrap().join("target")); 102 | build_cmd 103 | .arg("--out-dir") 104 | .arg(kernel_binary_path.parent().unwrap()); 105 | 106 | if !build_cmd.status().unwrap().success() { 107 | panic!("bootloader build failed"); 108 | } 109 | 110 | let kernel_binary_name = kernel_binary_path.file_name().unwrap().to_str().unwrap(); 111 | let disk_image = kernel_binary_path 112 | .parent() 113 | .unwrap() 114 | .join(format!("boot-uefi-{}.img", kernel_binary_name)); 115 | if !disk_image.exists() { 116 | panic!( 117 | "Disk image does not exist at {} after bootloader build", 118 | disk_image.display() 119 | ); 120 | } 121 | disk_image 122 | } 123 | -------------------------------------------------------------------------------- /src/text_window.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | framed_window::{FramedWindow, FramedWindowEvent}, 3 | graphics::{font, Color, Draw, Point, Rectangle, Size}, 4 | prelude::*, 5 | timer, 6 | }; 7 | use alloc::string::String; 8 | use futures_util::select_biased; 9 | 10 | const BACKGROUND: Color = Color::WHITE; 11 | const BORDER_DARK: Color = Color::from_code(0x848484); 12 | const BORDER_LIGHT: Color = Color::from_code(0xc6c6c6); 13 | 14 | #[derive(Debug)] 15 | pub(crate) struct TextWindow { 16 | window: FramedWindow, 17 | index: i32, 18 | max_chars: i32, 19 | cursor_visible: bool, 20 | } 21 | 22 | impl TextWindow { 23 | pub(crate) fn new(title: String, pos: Point) -> Result { 24 | let font_size = font::FONT_PIXEL_SIZE; 25 | let window_size = Size::new(160, font_size.y + 8); 26 | let window = FramedWindow::builder(title) 27 | .size(window_size) 28 | .pos(pos) 29 | .build()?; 30 | Ok(Self { 31 | window, 32 | index: 0, 33 | max_chars: (window_size.x - 8) / font_size.x - 1, 34 | cursor_visible: true, 35 | }) 36 | } 37 | 38 | fn insert_pos(&self) -> Point { 39 | let font_size = font::FONT_PIXEL_SIZE; 40 | Point::new(4 + font_size.x * self.index, 6) 41 | } 42 | 43 | fn draw_text_box(&mut self) { 44 | let area = self.window.area(); 45 | self.window 46 | .draw_box(area, BACKGROUND, BORDER_DARK, BORDER_LIGHT); 47 | } 48 | 49 | fn draw_cursor(&mut self, visible: bool) { 50 | let font_size = font::FONT_PIXEL_SIZE; 51 | let color = if visible { Color::BLACK } else { Color::WHITE }; 52 | let pos = self.insert_pos(); 53 | self.window 54 | .fill_rect(Rectangle::new(pos, font_size - Size::new(1, 1)), color); 55 | } 56 | 57 | fn handle_event(&mut self, event: FramedWindowEvent) { 58 | match event { 59 | FramedWindowEvent::Keyboard(event) => { 60 | if event.ascii == '\0' { 61 | return; 62 | } 63 | 64 | if event.ascii == '\x08' && self.index > 0 { 65 | self.draw_cursor(false); 66 | self.index -= 1; 67 | self.window.fill_rect( 68 | Rectangle::new(self.insert_pos(), Size::new(8, 16)), 69 | Color::WHITE, 70 | ); 71 | self.draw_cursor(self.cursor_visible); 72 | } else if event.ascii >= ' ' && self.index < self.max_chars { 73 | self.draw_cursor(false); 74 | let pos = self.insert_pos(); 75 | self.window.draw_char(pos, event.ascii, Color::BLACK); 76 | self.index += 1; 77 | self.draw_cursor(self.cursor_visible); 78 | } 79 | } 80 | } 81 | } 82 | 83 | fn handle_timeout(&mut self) { 84 | self.cursor_visible = !self.cursor_visible; 85 | self.draw_cursor(self.cursor_visible); 86 | } 87 | 88 | pub(crate) async fn run(mut self) -> Result<()> { 89 | self.draw_text_box(); 90 | self.window.flush().await?; 91 | 92 | let mut interval = timer::lapic::interval(0, 50)?; 93 | loop { 94 | select_biased! { 95 | event = self.window.recv_event().fuse() => { 96 | let event = match event { 97 | Some(event) => event?, 98 | None => return Ok(()), 99 | }; 100 | self.handle_event(event); 101 | } 102 | timeout = interval.next().fuse() => { 103 | let _timeout = match timeout { 104 | Some(event) => event?, 105 | _ => return Ok(()), 106 | }; 107 | self.handle_timeout(); 108 | } 109 | } 110 | self.window.flush().await?; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/co_task/executor.rs: -------------------------------------------------------------------------------- 1 | use super::{CoTask, CoTaskId}; 2 | use crate::task::{self, TaskId}; 3 | use alloc::{collections::BTreeMap, sync::Arc, task::Wake}; 4 | use core::task::{Context, Poll, Waker}; 5 | use crossbeam_queue::ArrayQueue; 6 | use x86_64::instructions::interrupts; 7 | 8 | #[derive(Debug)] 9 | enum Event { 10 | Spawn(CoTask), 11 | Wake(CoTaskId), 12 | } 13 | 14 | #[derive(Debug)] 15 | pub(crate) struct Executor { 16 | task_id: TaskId, 17 | tasks: BTreeMap, 18 | task_queue: Arc>, 19 | waker_cache: BTreeMap, 20 | } 21 | 22 | impl Executor { 23 | pub(crate) fn new(task_id: TaskId) -> Self { 24 | Self { 25 | task_id, 26 | tasks: BTreeMap::new(), 27 | task_queue: Arc::new(ArrayQueue::new(100)), 28 | waker_cache: BTreeMap::new(), 29 | } 30 | } 31 | 32 | pub(crate) fn handle(&self) -> Handle { 33 | Handle { 34 | task_queue: self.task_queue.clone(), 35 | } 36 | } 37 | 38 | pub(crate) fn spawn(&mut self, task: CoTask) { 39 | let task_id = task.id; 40 | if self.tasks.insert(task.id, task).is_some() { 41 | panic!("task with same ID already in tasks"); 42 | } 43 | #[allow(clippy::expect_used)] 44 | self.task_queue 45 | .push(Event::Wake(task_id)) 46 | .expect("queue full"); 47 | } 48 | 49 | pub(crate) fn run(&mut self) -> ! { 50 | loop { 51 | self.run_ready_tasks(); 52 | self.sleep_if_idle(); 53 | } 54 | } 55 | 56 | fn wake(&mut self, co_task_id: CoTaskId) { 57 | // destructure `self` to avoid borrow checker errors 58 | let Self { 59 | task_id, 60 | tasks, 61 | task_queue, 62 | waker_cache, 63 | } = self; 64 | 65 | let task = match tasks.get_mut(&co_task_id) { 66 | Some(task) => task, 67 | None => return, // task no longer exists 68 | }; 69 | 70 | let waker = waker_cache 71 | .entry(co_task_id) 72 | .or_insert_with(|| CoTaskWaker::waker(*task_id, co_task_id, task_queue.clone())); 73 | let mut context = Context::from_waker(waker); 74 | if let Poll::Ready(()) = task.poll(&mut context) { 75 | // task done -> remove it and its cached waker 76 | tasks.remove(&co_task_id); 77 | waker_cache.remove(&co_task_id); 78 | } 79 | } 80 | 81 | fn run_ready_tasks(&mut self) { 82 | while let Some(event) = self.task_queue.pop() { 83 | match event { 84 | Event::Spawn(task) => self.spawn(task), 85 | Event::Wake(task_id) => self.wake(task_id), 86 | } 87 | } 88 | } 89 | 90 | fn sleep_if_idle(&self) { 91 | interrupts::disable(); 92 | if self.task_queue.is_empty() { 93 | task::sleep(self.task_id); 94 | } 95 | interrupts::enable(); 96 | } 97 | } 98 | 99 | struct CoTaskWaker { 100 | task_id: TaskId, 101 | co_task_id: CoTaskId, 102 | task_queue: Arc>, 103 | } 104 | 105 | impl CoTaskWaker { 106 | fn waker(task_id: TaskId, co_task_id: CoTaskId, task_queue: Arc>) -> Waker { 107 | Waker::from(Arc::new(CoTaskWaker { 108 | task_id, 109 | co_task_id, 110 | task_queue, 111 | })) 112 | } 113 | 114 | fn wake_task(&self) { 115 | interrupts::without_interrupts(|| { 116 | #[allow(clippy::expect_used)] 117 | self.task_queue 118 | .push(Event::Wake(self.co_task_id)) 119 | .expect("task_queue full"); 120 | task::wake(self.task_id); 121 | }) 122 | } 123 | } 124 | 125 | impl Wake for CoTaskWaker { 126 | fn wake(self: Arc) { 127 | self.wake_task() 128 | } 129 | 130 | fn wake_by_ref(self: &Arc) { 131 | self.wake_task() 132 | } 133 | } 134 | 135 | #[derive(Debug, Clone)] 136 | pub(crate) struct Handle { 137 | task_queue: Arc>, 138 | } 139 | 140 | impl Handle { 141 | pub(crate) fn spawn(&self, task: CoTask) { 142 | #[allow(clippy::expect_used)] 143 | self.task_queue 144 | .push(Event::Spawn(task)) 145 | .expect("queue full"); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /mikanos_usb/cxx_src/usb/descriptor.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/descriptor.hpp 3 | * 4 | * USB Descriptor の定義集. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace usb { 13 | struct DeviceDescriptor { 14 | static const uint8_t kType = 1; 15 | 16 | uint8_t length; // offset 0 17 | uint8_t descriptor_type; // offset 1 18 | uint16_t usb_release; // offset 2 19 | uint8_t device_class; // offset 4 20 | uint8_t device_sub_class; // offset 5 21 | uint8_t device_protocol; // offset 6 22 | uint8_t max_packet_size; // offset 7 23 | uint16_t vendor_id; // offset 8 24 | uint16_t product_id; // offset 10 25 | uint16_t device_release; // offset 12 26 | uint8_t manufacturer; // offset 14 27 | uint8_t product; // offset 15 28 | uint8_t serial_number; // offset 16 29 | uint8_t num_configurations; // offset 17 30 | } __attribute__((packed)); 31 | 32 | struct ConfigurationDescriptor { 33 | static const uint8_t kType = 2; 34 | 35 | uint8_t length; // offset 0 36 | uint8_t descriptor_type; // offset 1 37 | uint16_t total_length; // offset 2 38 | uint8_t num_interfaces; // offset 4 39 | uint8_t configuration_value; // offset 5 40 | uint8_t configuration_id; // offset 6 41 | uint8_t attributes; // offset 7 42 | uint8_t max_power; // offset 8 43 | } __attribute__((packed)); 44 | 45 | struct InterfaceDescriptor { 46 | static const uint8_t kType = 4; 47 | 48 | uint8_t length; // offset 0 49 | uint8_t descriptor_type; // offset 1 50 | uint8_t interface_number; // offset 2 51 | uint8_t alternate_setting; // offset 3 52 | uint8_t num_endpoints; // offset 4 53 | uint8_t interface_class; // offset 5 54 | uint8_t interface_sub_class; // offset 6 55 | uint8_t interface_protocol; // offset 7 56 | uint8_t interface_id; // offset 8 57 | } __attribute__((packed)); 58 | 59 | struct EndpointDescriptor { 60 | static const uint8_t kType = 5; 61 | 62 | uint8_t length; // offset 0 63 | uint8_t descriptor_type; // offset 1 64 | union { 65 | uint8_t data; 66 | struct { 67 | uint8_t number : 4; 68 | uint8_t : 3; 69 | uint8_t dir_in : 1; 70 | } __attribute__((packed)) bits; 71 | } endpoint_address; // offset 2 72 | union { 73 | uint8_t data; 74 | struct { 75 | uint8_t transfer_type : 2; 76 | uint8_t sync_type : 2; 77 | uint8_t usage_type : 2; 78 | uint8_t : 2; 79 | } __attribute__((packed)) bits; 80 | } attributes; // offset 3 81 | uint16_t max_packet_size; // offset 4 82 | uint8_t interval; // offset 6 83 | } __attribute__((packed)); 84 | 85 | struct HIDDescriptor { 86 | static const uint8_t kType = 33; 87 | 88 | uint8_t length; // offset 0 89 | uint8_t descriptor_type; // offset 1 90 | uint16_t hid_release; // offset 2 91 | uint8_t country_code; // offset 4 92 | uint8_t num_descriptors; // offset 5 93 | 94 | struct ClassDescriptor { 95 | /** @brief クラス特有ディスクリプタのタイプ値. */ 96 | uint8_t descriptor_type; 97 | /** @brief クラス特有ディスクリプタのバイト数. */ 98 | uint16_t descriptor_length; 99 | } __attribute__((packed)); 100 | 101 | /** @brief HID 特有のディスクリプタに関する情報を得る. 102 | * 103 | * HID はクラス特有(class-specific)のディスクリプタを 1 つ以上持つ. 104 | * その数は num_descriptors に記載されている. 105 | * Report ディスクリプタ(type = 34)は HID デバイスであれば必ず存在するため, 106 | * num_descriptors は必ず 1 以上となる. 107 | * 108 | * @param index 取得するディスクリプタの番号.0 <= index < num_descriptors. 109 | * @return index で指定されたディスクリプタの情報.index が範囲外なら nullptr. 110 | */ 111 | ClassDescriptor *GetClassDescriptor(size_t index) const { 112 | if (index >= num_descriptors) { 113 | return nullptr; 114 | } 115 | const auto end_of_struct = reinterpret_cast(this) + sizeof(HIDDescriptor); 116 | return reinterpret_cast(end_of_struct) + index; 117 | } 118 | } __attribute__((packed)); 119 | 120 | template T *DescriptorDynamicCast(uint8_t *desc_data) { 121 | if (desc_data[1] == T::kType) { 122 | return reinterpret_cast(desc_data); 123 | } 124 | return nullptr; 125 | } 126 | 127 | template const T *DescriptorDynamicCast(const uint8_t *desc_data) { 128 | if (desc_data[1] == T::kType) { 129 | return reinterpret_cast(desc_data); 130 | } 131 | return nullptr; 132 | } 133 | } // namespace usb 134 | -------------------------------------------------------------------------------- /src/interrupt.rs: -------------------------------------------------------------------------------- 1 | use crate::{emergency_console, println, sync::OnceCell, timer, xhc}; 2 | use core::{ 3 | fmt::Write as _, 4 | sync::atomic::{AtomicBool, Ordering}, 5 | }; 6 | use volatile::Volatile; 7 | use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; 8 | 9 | #[derive(Debug, Clone, Copy)] 10 | #[repr(u8)] 11 | pub(crate) enum InterruptIndex { 12 | Xhci = 0x40, 13 | Timer = 0x41, 14 | } 15 | 16 | impl InterruptIndex { 17 | pub(crate) fn as_u8(self) -> u8 { 18 | self as u8 19 | } 20 | 21 | pub(crate) fn as_u32(self) -> u32 { 22 | u32::from(self.as_u8()) 23 | } 24 | 25 | pub(crate) fn as_usize(self) -> usize { 26 | usize::from(self.as_u8()) 27 | } 28 | } 29 | 30 | static IDT: OnceCell = OnceCell::uninit(); 31 | 32 | pub(crate) fn init() { 33 | IDT.init_once(|| { 34 | let mut idt = InterruptDescriptorTable::new(); 35 | idt.breakpoint.set_handler_fn(breakpoint_handler); 36 | idt.page_fault.set_handler_fn(page_fault_handler); 37 | idt.general_protection_fault 38 | .set_handler_fn(general_protection_fault_handler); 39 | idt.segment_not_present 40 | .set_handler_fn(segment_not_present_handler); 41 | idt.double_fault.set_handler_fn(double_fault_handler); 42 | idt[InterruptIndex::Xhci.as_usize()].set_handler_fn(xhc::interrupt_handler); 43 | idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer::lapic::interrupt_handler); 44 | idt 45 | }); 46 | IDT.get().load(); 47 | } 48 | 49 | static INTERRUPT_CONTEXT: AtomicBool = AtomicBool::new(false); 50 | 51 | pub(crate) fn is_interrupt_context() -> bool { 52 | INTERRUPT_CONTEXT.load(Ordering::Relaxed) 53 | } 54 | 55 | pub(crate) struct InterruptContextGuard {} 56 | 57 | impl InterruptContextGuard { 58 | pub(crate) fn new() -> Self { 59 | let old_value = INTERRUPT_CONTEXT.swap(true, Ordering::Relaxed); 60 | assert!(!old_value); 61 | Self {} 62 | } 63 | } 64 | 65 | impl Drop for InterruptContextGuard { 66 | fn drop(&mut self) { 67 | let old_value = INTERRUPT_CONTEXT.swap(false, Ordering::Relaxed); 68 | assert!(old_value); 69 | } 70 | } 71 | 72 | extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { 73 | let _guard = InterruptContextGuard::new(); 74 | println!("EXCEPTION: BREAKPOINT"); 75 | println!("{:#?}", stack_frame); 76 | } 77 | 78 | extern "x86-interrupt" fn page_fault_handler( 79 | stack_frame: InterruptStackFrame, 80 | error_code: PageFaultErrorCode, 81 | ) { 82 | use x86_64::registers::control::Cr2; 83 | 84 | let _guard = InterruptContextGuard::new(); 85 | emergency_console::with_console(|console| { 86 | let _ = writeln!(console, "EXCEPTION: PAGE FAULT"); 87 | let _ = writeln!(console, "Accessed Address: {:?}", Cr2::read()); 88 | let _ = writeln!(console, "Error Code: {:x}", error_code); 89 | let _ = writeln!(console, "{:#?}", stack_frame); 90 | }); 91 | } 92 | 93 | extern "x86-interrupt" fn general_protection_fault_handler( 94 | stack_frame: InterruptStackFrame, 95 | error_code: u64, 96 | ) { 97 | let _guard = InterruptContextGuard::new(); 98 | emergency_console::with_console(|console| { 99 | let _ = writeln!(console, "EXCEPTION: GENERAL PROTECTION FAULT"); 100 | let _ = writeln!(console, "Error Code: {:x}", error_code); 101 | let _ = writeln!(console, "{:#?}", stack_frame); 102 | }); 103 | } 104 | 105 | extern "x86-interrupt" fn segment_not_present_handler( 106 | stack_frame: InterruptStackFrame, 107 | error_code: u64, 108 | ) { 109 | let _guard = InterruptContextGuard::new(); 110 | emergency_console::with_console(|console| { 111 | let _ = writeln!(console, "EXCEPTION: STACK NOT PRESENT"); 112 | let _ = writeln!(console, "Error Code: {:x}", error_code); 113 | let _ = writeln!(console, "{:#?}", stack_frame); 114 | }); 115 | } 116 | 117 | extern "x86-interrupt" fn double_fault_handler( 118 | stack_frame: InterruptStackFrame, 119 | error_code: u64, 120 | ) -> ! { 121 | let _guard = InterruptContextGuard::new(); 122 | emergency_console::with_console(|console| { 123 | let _ = writeln!(console, "EXCEPTION: DOUBLE FAULT",); 124 | let _ = writeln!(console, "Error Code: {:x}", error_code); 125 | let _ = writeln!(console, "{:#?}", stack_frame); 126 | }); 127 | } 128 | 129 | pub(crate) fn notify_end_of_interrupt() { 130 | assert!(is_interrupt_context()); 131 | 132 | #[allow(clippy::unwrap_used)] 133 | let mut memory = Volatile::new(unsafe { (0xfee000b0 as *mut u32).as_mut().unwrap() }); 134 | memory.write(0); 135 | } 136 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use bootloader::boot_info::PixelFormat; 2 | use conquer_once::{TryGetError, TryInitError}; 3 | use core::{fmt, num::TryFromIntError, panic::Location}; 4 | use mikanos_usb::CxxError; 5 | use x86_64::structures::paging::{mapper::MapToError, page::AddressNotAligned, Size4KiB}; 6 | 7 | pub(crate) type Result = core::result::Result; 8 | 9 | #[derive(Debug)] 10 | pub(crate) struct Error { 11 | kind: ErrorKind, 12 | location: &'static Location<'static>, 13 | } 14 | 15 | impl Error { 16 | #[track_caller] 17 | pub(crate) fn new(kind: ErrorKind) -> Self { 18 | let location = Location::caller(); 19 | Self { kind, location } 20 | } 21 | } 22 | 23 | impl From for Error { 24 | #[track_caller] 25 | fn from(err: ErrorKind) -> Self { 26 | Error::new(err) 27 | } 28 | } 29 | 30 | impl fmt::Display for Error { 31 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 32 | write!( 33 | f, 34 | "{:?}: {}, {}:{}:{}", 35 | self.kind, 36 | self.kind, 37 | self.location.file(), 38 | self.location.line(), 39 | self.location.column() 40 | )?; 41 | Ok(()) 42 | } 43 | } 44 | 45 | #[derive(Debug)] 46 | pub(crate) enum ErrorKind { 47 | AddressNotAligned(AddressNotAligned), 48 | MapTo(MapToError), 49 | TryInit(TryInitError), 50 | TryGet(TryGetError), 51 | TryFromInt(TryFromIntError), 52 | FrameBufferNotSupported, 53 | PhysicalMemoryNotMapped, 54 | RsdpNotMapped, 55 | InvalidRsdp, 56 | InvalidXsdt, 57 | FadtNotFound, 58 | UnsupportedPixelFormat(PixelFormat), 59 | Deadlock, 60 | Full, 61 | NoEnoughMemory, 62 | XhcNotFound, 63 | IndexOutOfRange, 64 | InvalidSlotID, 65 | InvalidEndpointNumber, 66 | TransferRingNotSet, 67 | AlreadyAllocated, 68 | NotImplemented, 69 | InvalidDescriptor, 70 | BufferTooSmall, 71 | UnknownDevice, 72 | NoCorrespondingSetupStage, 73 | TransferFailed, 74 | InvalidPhase, 75 | UnknownXHCISpeedID, 76 | NoWaiter, 77 | EndpointNotInCharge, 78 | NoPciMsi, 79 | Unknown, 80 | } 81 | 82 | impl fmt::Display for ErrorKind { 83 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 84 | match self { 85 | ErrorKind::AddressNotAligned(err) => write!(f, "{}", err), 86 | ErrorKind::MapTo(err) => write!(f, "{:?}", err), 87 | ErrorKind::TryInit(err) => write!(f, "{}", err), 88 | ErrorKind::TryGet(err) => write!(f, "{}", err), 89 | ErrorKind::UnsupportedPixelFormat(pixel_format) => { 90 | write!(f, "unsupported pixel format: {:?}", pixel_format) 91 | } 92 | ErrorKind::Full => write!(f, "buffer full"), 93 | _ => write!(f, "{:?}", self), 94 | } 95 | } 96 | } 97 | 98 | impl From for Error { 99 | #[track_caller] 100 | fn from(err: AddressNotAligned) -> Self { 101 | Error::from(ErrorKind::AddressNotAligned(err)) 102 | } 103 | } 104 | 105 | impl From> for Error { 106 | #[track_caller] 107 | fn from(err: MapToError) -> Self { 108 | Error::from(ErrorKind::MapTo(err)) 109 | } 110 | } 111 | 112 | impl From for Error { 113 | #[track_caller] 114 | fn from(err: TryInitError) -> Self { 115 | Error::from(ErrorKind::TryInit(err)) 116 | } 117 | } 118 | 119 | impl From for Error { 120 | #[track_caller] 121 | fn from(err: TryGetError) -> Self { 122 | Error::from(ErrorKind::TryGet(err)) 123 | } 124 | } 125 | 126 | impl From for Error { 127 | #[track_caller] 128 | fn from(err: TryFromIntError) -> Self { 129 | Error::from(ErrorKind::TryFromInt(err)) 130 | } 131 | } 132 | 133 | impl From for Error { 134 | #[track_caller] 135 | fn from(err: CxxError) -> Self { 136 | use ErrorKind::*; 137 | let kind = match err.0 { 138 | 1 => NoEnoughMemory, 139 | 2 => InvalidSlotID, 140 | 3 => InvalidEndpointNumber, 141 | 4 => TransferRingNotSet, 142 | 5 => AlreadyAllocated, 143 | 6 => NotImplemented, 144 | 7 => InvalidDescriptor, 145 | 8 => BufferTooSmall, 146 | 9 => UnknownDevice, 147 | 10 => NoCorrespondingSetupStage, 148 | 11 => TransferFailed, 149 | 12 => InvalidPhase, 150 | 13 => UnknownXHCISpeedID, 151 | 14 => NoWaiter, 152 | 15 => EndpointNotInCharge, 153 | _ => Unknown, 154 | }; 155 | Error::from(kind) 156 | } 157 | } 158 | 159 | #[macro_export] 160 | macro_rules! bail { 161 | ($err:expr) => { 162 | return Err($crate::error::Error::from($err)) 163 | }; 164 | } 165 | -------------------------------------------------------------------------------- /mikanos_usb/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveMacros: false 7 | AlignConsecutiveAssignments: false 8 | AlignConsecutiveBitFields: false 9 | AlignConsecutiveDeclarations: false 10 | AlignEscapedNewlines: Right 11 | AlignOperands: Align 12 | AlignTrailingComments: true 13 | AllowAllArgumentsOnNextLine: true 14 | AllowAllConstructorInitializersOnNextLine: true 15 | AllowAllParametersOfDeclarationOnNextLine: true 16 | AllowShortEnumsOnASingleLine: true 17 | AllowShortBlocksOnASingleLine: Never 18 | AllowShortCaseLabelsOnASingleLine: false 19 | AllowShortFunctionsOnASingleLine: All 20 | AllowShortLambdasOnASingleLine: All 21 | AllowShortIfStatementsOnASingleLine: Never 22 | AllowShortLoopsOnASingleLine: false 23 | AlwaysBreakAfterDefinitionReturnType: None 24 | AlwaysBreakAfterReturnType: None 25 | AlwaysBreakBeforeMultilineStrings: false 26 | AlwaysBreakTemplateDeclarations: MultiLine 27 | BinPackArguments: true 28 | BinPackParameters: true 29 | BraceWrapping: 30 | AfterCaseLabel: false 31 | AfterClass: false 32 | AfterControlStatement: Never 33 | AfterEnum: false 34 | AfterFunction: false 35 | AfterNamespace: false 36 | AfterObjCDeclaration: false 37 | AfterStruct: false 38 | AfterUnion: false 39 | AfterExternBlock: false 40 | BeforeCatch: false 41 | BeforeElse: false 42 | BeforeLambdaBody: false 43 | BeforeWhile: false 44 | IndentBraces: false 45 | SplitEmptyFunction: true 46 | SplitEmptyRecord: true 47 | SplitEmptyNamespace: true 48 | BreakBeforeBinaryOperators: None 49 | BreakBeforeBraces: Attach 50 | BreakBeforeInheritanceComma: false 51 | BreakInheritanceList: BeforeColon 52 | BreakBeforeTernaryOperators: true 53 | BreakConstructorInitializersBeforeComma: false 54 | BreakConstructorInitializers: BeforeColon 55 | BreakAfterJavaFieldAnnotations: false 56 | BreakStringLiterals: true 57 | ColumnLimit: 100 58 | CommentPragmas: '^ IWYU pragma:' 59 | CompactNamespaces: false 60 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 61 | ConstructorInitializerIndentWidth: 4 62 | ContinuationIndentWidth: 4 63 | Cpp11BracedListStyle: true 64 | DeriveLineEnding: true 65 | DerivePointerAlignment: false 66 | DisableFormat: false 67 | ExperimentalAutoDetectBinPacking: false 68 | FixNamespaceComments: true 69 | ForEachMacros: 70 | - foreach 71 | - Q_FOREACH 72 | - BOOST_FOREACH 73 | IncludeBlocks: Regroup 74 | IncludeCategories: 75 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 76 | Priority: 2 77 | SortPriority: 0 78 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 79 | Priority: 3 80 | SortPriority: 0 81 | - Regex: '.*' 82 | Priority: 1 83 | SortPriority: 0 84 | IncludeIsMainRegex: '(Test)?$' 85 | IncludeIsMainSourceRegex: '' 86 | IndentCaseLabels: false 87 | IndentCaseBlocks: false 88 | IndentGotoLabels: true 89 | IndentPPDirectives: None 90 | IndentExternBlock: AfterExternBlock 91 | IndentWidth: 2 92 | IndentWrappedFunctionNames: false 93 | InsertTrailingCommas: None 94 | JavaScriptQuotes: Leave 95 | JavaScriptWrapImports: true 96 | KeepEmptyLinesAtTheStartOfBlocks: true 97 | MacroBlockBegin: '' 98 | MacroBlockEnd: '' 99 | MaxEmptyLinesToKeep: 1 100 | NamespaceIndentation: None 101 | ObjCBinPackProtocolList: Auto 102 | ObjCBlockIndentWidth: 2 103 | ObjCBreakBeforeNestedBlockParam: true 104 | ObjCSpaceAfterProperty: false 105 | ObjCSpaceBeforeProtocolList: true 106 | PenaltyBreakAssignment: 2 107 | PenaltyBreakBeforeFirstCallParameter: 19 108 | PenaltyBreakComment: 300 109 | PenaltyBreakFirstLessLess: 120 110 | PenaltyBreakString: 1000 111 | PenaltyBreakTemplateDeclaration: 10 112 | PenaltyExcessCharacter: 1000000 113 | PenaltyReturnTypeOnItsOwnLine: 60 114 | PointerAlignment: Right 115 | ReflowComments: true 116 | SortIncludes: true 117 | SortUsingDeclarations: true 118 | SpaceAfterCStyleCast: false 119 | SpaceAfterLogicalNot: false 120 | SpaceAfterTemplateKeyword: true 121 | SpaceBeforeAssignmentOperators: true 122 | SpaceBeforeCpp11BracedList: false 123 | SpaceBeforeCtorInitializerColon: true 124 | SpaceBeforeInheritanceColon: true 125 | SpaceBeforeParens: ControlStatements 126 | SpaceBeforeRangeBasedForLoopColon: true 127 | SpaceInEmptyBlock: false 128 | SpaceInEmptyParentheses: false 129 | SpacesBeforeTrailingComments: 1 130 | SpacesInAngles: false 131 | SpacesInConditionalStatement: false 132 | SpacesInContainerLiterals: true 133 | SpacesInCStyleCastParentheses: false 134 | SpacesInParentheses: false 135 | SpacesInSquareBrackets: false 136 | SpaceBeforeSquareBrackets: false 137 | Standard: Latest 138 | StatementMacros: 139 | - Q_UNUSED 140 | - QT_REQUIRE_VERSION 141 | TabWidth: 8 142 | UseCRLF: false 143 | UseTab: Never 144 | WhitespaceSensitiveMacros: 145 | - STRINGIZE 146 | - PP_STRINGIZE 147 | - BOOST_PP_STRINGIZE 148 | ... 149 | 150 | -------------------------------------------------------------------------------- /src/sync/mutex.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | prelude::*, 3 | task::{self, TaskId}, 4 | }; 5 | use core::{ 6 | cell::UnsafeCell, 7 | fmt, 8 | ops::{Deref, DerefMut}, 9 | sync::atomic::{AtomicBool, Ordering}, 10 | }; 11 | use crossbeam_queue::SegQueue; 12 | use x86_64::instructions::interrupts; 13 | 14 | pub(crate) struct Mutex { 15 | lock: AtomicBool, 16 | queue: SegQueue, 17 | data: UnsafeCell, 18 | } 19 | 20 | pub(crate) struct MutexGuard<'a, T: ?Sized + 'a> { 21 | lock: &'a AtomicBool, 22 | queue: &'a SegQueue, 23 | data: &'a mut T, 24 | } 25 | 26 | unsafe impl Sync for Mutex {} 27 | unsafe impl Send for Mutex {} 28 | 29 | impl Mutex { 30 | #[inline(always)] 31 | pub(crate) const fn new(data: T) -> Self { 32 | Self { 33 | lock: AtomicBool::new(false), 34 | queue: SegQueue::new(), 35 | data: UnsafeCell::new(data), 36 | } 37 | } 38 | } 39 | 40 | impl Mutex 41 | where 42 | T: ?Sized, 43 | { 44 | #[inline(always)] 45 | pub(crate) fn is_locked(&self) -> bool { 46 | self.lock.load(Ordering::Relaxed) 47 | } 48 | 49 | #[inline(always)] 50 | #[track_caller] 51 | pub(crate) fn try_lock(&self) -> Result> { 52 | // The reason for using a strong compare_exchange is explained here: 53 | // https://github.com/Amanieu/parking_lot/pull/207#issuecomment-575869107 54 | if self 55 | .lock 56 | .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) 57 | .is_ok() 58 | { 59 | Ok(MutexGuard { 60 | lock: &self.lock, 61 | queue: &self.queue, 62 | data: unsafe { &mut *self.data.get() }, 63 | }) 64 | } else { 65 | bail!(ErrorKind::Deadlock) 66 | } 67 | } 68 | 69 | #[inline(always)] 70 | #[track_caller] 71 | pub(crate) fn lock(&self) -> MutexGuard { 72 | let task_id = interrupts::without_interrupts(|| task::current().id()); 73 | 74 | // Can fail to lock even if the lock is not locked. May be more efficient than `try_lock` 75 | // when called in a loop. 76 | while self 77 | .lock 78 | .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed) 79 | .is_err() 80 | { 81 | while self.is_locked() { 82 | assert!(interrupts::are_enabled()); 83 | interrupts::without_interrupts(|| { 84 | if self.is_locked() { 85 | self.queue.push(task_id); 86 | task::sleep(task_id); 87 | } 88 | }); 89 | } 90 | } 91 | 92 | MutexGuard { 93 | lock: &self.lock, 94 | queue: &self.queue, 95 | data: unsafe { &mut *self.data.get() }, 96 | } 97 | } 98 | } 99 | 100 | impl fmt::Debug for Mutex 101 | where 102 | T: ?Sized + fmt::Debug, 103 | { 104 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 105 | match self.try_lock() { 106 | Ok(guard) => write!(f, "Mutex {{ data: {:?} }}", guard), 107 | Err(_) => write!(f, "Mutex {{ }}"), 108 | } 109 | } 110 | } 111 | 112 | impl Default for Mutex 113 | where 114 | T: ?Sized + Default, 115 | { 116 | fn default() -> Self { 117 | Self::new(Default::default()) 118 | } 119 | } 120 | 121 | impl From for Mutex { 122 | fn from(data: T) -> Self { 123 | Self::new(data) 124 | } 125 | } 126 | 127 | impl fmt::Debug for MutexGuard<'_, T> 128 | where 129 | T: ?Sized + fmt::Debug, 130 | { 131 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 132 | fmt::Debug::fmt(&**self, f) 133 | } 134 | } 135 | 136 | impl fmt::Display for MutexGuard<'_, T> 137 | where 138 | T: ?Sized + fmt::Display, 139 | { 140 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 141 | fmt::Display::fmt(&**self, f) 142 | } 143 | } 144 | 145 | impl Deref for MutexGuard<'_, T> 146 | where 147 | T: ?Sized, 148 | { 149 | type Target = T; 150 | 151 | fn deref(&self) -> &T { 152 | self.data 153 | } 154 | } 155 | 156 | impl DerefMut for MutexGuard<'_, T> 157 | where 158 | T: ?Sized, 159 | { 160 | fn deref_mut(&mut self) -> &mut T { 161 | self.data 162 | } 163 | } 164 | 165 | impl Drop for MutexGuard<'_, T> 166 | where 167 | T: ?Sized, 168 | { 169 | fn drop(&mut self) { 170 | self.lock.store(false, Ordering::Release); 171 | 172 | let len = self.queue.len(); 173 | let mut count = 0; 174 | while let Some(task_id) = self.queue.pop() { 175 | interrupts::without_interrupts(|| task::wake(task_id)); 176 | count += 1; 177 | if count >= len { 178 | break; 179 | } 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/mouse.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | graphics::{Color, Draw, Offset, Point, ScreenInfo}, 3 | layer, 4 | prelude::*, 5 | sync::{mpsc, OnceCell}, 6 | window::Window, 7 | }; 8 | use core::future::Future; 9 | use enumflags2::{bitflags, BitFlags}; 10 | 11 | const TRANSPARENT_COLOR: Color = Color::RED; 12 | const MOUSE_CURSOR_WIDTH: usize = 15; 13 | const MOUSE_CURSOR_HEIGHT: usize = 24; 14 | const MOUSE_CURSOR_SIZE: Point = 15 | Point::new(MOUSE_CURSOR_WIDTH as i32, MOUSE_CURSOR_HEIGHT as i32); 16 | 17 | const MOUSE_CURSOR_SHAPE: [[u8; MOUSE_CURSOR_WIDTH]; MOUSE_CURSOR_HEIGHT] = [ 18 | *b"@ ", 19 | *b"@@ ", 20 | *b"@.@ ", 21 | *b"@..@ ", 22 | *b"@...@ ", 23 | *b"@....@ ", 24 | *b"@.....@ ", 25 | *b"@......@ ", 26 | *b"@.......@ ", 27 | *b"@........@ ", 28 | *b"@.........@ ", 29 | *b"@..........@ ", 30 | *b"@...........@ ", 31 | *b"@............@ ", 32 | *b"@......@@@@@@@@", 33 | *b"@......@ ", 34 | *b"@....@@.@ ", 35 | *b"@...@ @.@ ", 36 | *b"@..@ @.@ ", 37 | *b"@.@ @.@ ", 38 | *b"@@ @.@ ", 39 | *b"@ @.@ ", 40 | *b" @.@ ", 41 | *b" @@@ ", 42 | ]; 43 | 44 | #[bitflags] 45 | #[repr(u8)] 46 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 47 | pub(crate) enum MouseButton { 48 | Left = 0b001, 49 | Right = 0b010, 50 | Middle = 0b100, 51 | } 52 | 53 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 54 | struct RawMouseEvent { 55 | buttons: BitFlags, 56 | displacement: Offset, 57 | } 58 | 59 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 60 | pub(crate) struct MouseEvent { 61 | pub(crate) down: BitFlags, 62 | pub(crate) up: BitFlags, 63 | pub(crate) pos: Point, 64 | pub(crate) pos_diff: Offset, 65 | } 66 | 67 | static MOUSE_EVENT_TX: OnceCell> = OnceCell::uninit(); 68 | 69 | pub(crate) extern "C" fn observer(buttons: u8, displacement_x: i8, displacement_y: i8) { 70 | let buttons = BitFlags::::from_bits_truncate(buttons); 71 | let event = RawMouseEvent { 72 | buttons, 73 | displacement: Offset::new(i32::from(displacement_x), i32::from(displacement_y)), 74 | }; 75 | 76 | let res = MOUSE_EVENT_TX.try_get().and_then(|tx| tx.send(event)); 77 | 78 | if let Err(err) = res { 79 | error!("failed to enqueue to the queue: {}", err); 80 | } 81 | } 82 | 83 | fn draw(drawer: &mut dyn Draw) { 84 | for (dy, row) in (0..).zip(MOUSE_CURSOR_SHAPE) { 85 | for (dx, ch) in (0..).zip(row) { 86 | let p = Point::new(dx, dy); 87 | match ch { 88 | b'@' => drawer.draw(p, Color::BLACK), 89 | b'.' => drawer.draw(p, Color::WHITE), 90 | b' ' => drawer.draw(p, TRANSPARENT_COLOR), 91 | _ => {} 92 | } 93 | } 94 | } 95 | } 96 | 97 | pub(crate) fn handler_task() -> impl Future> { 98 | // Initialize MOUSE_EVENT_TX before co-task starts 99 | let (tx, mut rx) = mpsc::channel(100); 100 | MOUSE_EVENT_TX.init_once(|| tx); 101 | 102 | async move { 103 | let mut cursor_pos = Point::new(300, 200); 104 | let screen_info = ScreenInfo::get(); 105 | 106 | let mut window = Window::builder() 107 | .pos(cursor_pos) 108 | .size(MOUSE_CURSOR_SIZE) 109 | .transparent_color(Some(TRANSPARENT_COLOR)) 110 | .height(usize::MAX) 111 | .build()?; 112 | 113 | let cursor_layer_id = window.layer_id(); 114 | draw(&mut window); 115 | window.flush().await?; 116 | 117 | let tx = layer::event_tx(); 118 | 119 | // send dummy mouse event to notify cursor_layer_id 120 | tx.mouse_event( 121 | cursor_layer_id, 122 | MouseEvent { 123 | down: BitFlags::empty(), 124 | up: BitFlags::empty(), 125 | pos: cursor_pos, 126 | pos_diff: Offset::new(0, 0), 127 | }, 128 | ) 129 | .await?; 130 | 131 | let mut buttons = BitFlags::empty(); 132 | while let Some(event) = rx.next().await { 133 | let prev_cursor_pos = cursor_pos; 134 | let prev_buttons = buttons; 135 | 136 | if let Some(pos) = (cursor_pos + event.displacement).clamp(screen_info.area()) { 137 | cursor_pos = pos; 138 | } 139 | buttons = event.buttons; 140 | 141 | let down = buttons & !prev_buttons; 142 | let up = prev_buttons & !buttons; 143 | let pos_diff = cursor_pos - prev_cursor_pos; 144 | 145 | if prev_cursor_pos != cursor_pos { 146 | window.move_to(cursor_pos).await?; 147 | } 148 | tx.mouse_event( 149 | cursor_layer_id, 150 | MouseEvent { 151 | down, 152 | up, 153 | pos: cursor_pos, 154 | pos_diff, 155 | }, 156 | ) 157 | .await?; 158 | } 159 | 160 | Ok(()) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/xhc.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | interrupt::{self, InterruptContextGuard, InterruptIndex}, 3 | keyboard, memory, mouse, paging, 4 | pci::{self, Device, MsiDeliveryMode, MsiTriggerMode}, 5 | prelude::*, 6 | sync::{OnceCell, SpinMutex}, 7 | }; 8 | use core::{ 9 | pin::Pin, 10 | sync::atomic::{AtomicBool, Ordering}, 11 | task::{Context, Poll}, 12 | }; 13 | use futures_util::{task::AtomicWaker, Stream}; 14 | use mikanos_usb as usb; 15 | use x86_64::structures::{idt::InterruptStackFrame, paging::OffsetPageTable}; 16 | 17 | static XHC: OnceCell> = OnceCell::uninit(); 18 | 19 | pub(crate) fn init(devices: &[Device], mapper: &mut OffsetPageTable) -> Result<()> { 20 | let mut xhc_dev = None; 21 | for dev in devices { 22 | // is the device is xHC? 23 | if dev.class_code.test3(0x0c, 0x03, 0x30) { 24 | xhc_dev = Some(dev); 25 | 26 | if dev.vendor_id == 0x8086 { 27 | // prefer Intel's xHC 28 | break; 29 | } 30 | } 31 | } 32 | 33 | let xhc_dev = xhc_dev.ok_or(ErrorKind::XhcNotFound)?; 34 | info!("xHC has been found: {}", xhc_dev); 35 | 36 | let bsp_local_apic_id = unsafe { *(0xfee00020 as *const u32) } >> 24; 37 | pci::configure_msi_fixed_destination( 38 | xhc_dev, 39 | bsp_local_apic_id, 40 | MsiTriggerMode::Level, 41 | MsiDeliveryMode::Fixed, 42 | InterruptIndex::Xhci, 43 | 0, 44 | )?; 45 | 46 | let xhc_bar = pci::read_bar(xhc_dev, 0)?; 47 | debug!("xHC BAR0 = {:08x}", xhc_bar); 48 | let xhc_mmio_base = xhc_bar & !0xf; 49 | debug!("xHC mmio_base = {:08x}", xhc_mmio_base); 50 | 51 | map_xhc_mmio(mapper, xhc_mmio_base)?; 52 | alloc_memory_pool(mapper)?; 53 | 54 | let xhc = unsafe { usb::xhci::Controller::new(xhc_mmio_base) }; 55 | 56 | if xhc_dev.vendor_id == 0x8086 { 57 | switch_ehci_to_xhci(devices, xhc_dev); 58 | } 59 | 60 | xhc.init(); 61 | debug!("xhc starting"); 62 | xhc.run()?; 63 | 64 | usb::HidMouseDriver::set_default_observer(mouse::observer); 65 | usb::HidKeyboardDriver::set_default_observer(keyboard::observer); 66 | 67 | xhc.configure_connected_ports(); 68 | 69 | XHC.init_once(move || SpinMutex::new(xhc)); 70 | 71 | Ok(()) 72 | } 73 | 74 | fn map_xhc_mmio(mapper: &mut OffsetPageTable, xhc_mmio_base: u64) -> Result<()> { 75 | // Map [xhc_mmio_base..(xhc_mmio_base+64kib)] as identity map 76 | let mut allocator = memory::lock_memory_manager(); 77 | paging::make_identity_mapping(mapper, &mut *allocator, xhc_mmio_base, 16) 78 | } 79 | 80 | fn alloc_memory_pool(mapper: &mut OffsetPageTable) -> Result<()> { 81 | let num_frames = 32; 82 | let mut allocator = memory::lock_memory_manager(); 83 | let frame_range = allocator.allocate(num_frames)?; 84 | let base_addr = frame_range.start.start_address().as_u64(); 85 | paging::make_identity_mapping(mapper, &mut *allocator, base_addr, num_frames)?; 86 | unsafe { usb::set_memory_pool(base_addr, num_frames * (memory::BYTES_PER_FRAME as usize)) }; 87 | Ok(()) 88 | } 89 | 90 | fn switch_ehci_to_xhci(devices: &[Device], xhc_dev: &Device) { 91 | let intel_ehc_exists = devices.iter().any(|dev| { 92 | dev.class_code.test3(0x0c, 0x03, 0x20) && // EHCI 93 | dev.vendor_id == 0x8086 // Intel 94 | }); 95 | if !intel_ehc_exists { 96 | return; 97 | } 98 | 99 | let superspeed_ports = pci::read_conf_reg(xhc_dev, 0xdc); // USB3PRM 100 | pci::write_conf_reg(xhc_dev, 0xf8, superspeed_ports); // USB3_PSSEN 101 | let ehci2xhci_ports = pci::read_conf_reg(xhc_dev, 0xd4); // XUSB2PRM 102 | pci::write_conf_reg(xhc_dev, 0xd0, ehci2xhci_ports); // XUSB2PR 103 | debug!( 104 | "switch_ehci_to_xhci: SS={:2x}, xHCI={:2x}", 105 | superspeed_ports, ehci2xhci_ports 106 | ); 107 | } 108 | 109 | static INTERRUPTED_FLAG: AtomicBool = AtomicBool::new(false); 110 | static WAKER: AtomicWaker = AtomicWaker::new(); 111 | 112 | #[derive(Debug)] 113 | struct InterruptStream { 114 | _private: (), 115 | } 116 | 117 | impl InterruptStream { 118 | fn new() -> Self { 119 | Self { _private: () } 120 | } 121 | } 122 | 123 | impl Stream for InterruptStream { 124 | type Item = (); 125 | 126 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 127 | // fast path 128 | if INTERRUPTED_FLAG.swap(false, Ordering::Relaxed) { 129 | return Poll::Ready(Some(())); 130 | } 131 | 132 | WAKER.register(cx.waker()); 133 | if INTERRUPTED_FLAG.swap(false, Ordering::Relaxed) { 134 | WAKER.take(); 135 | Poll::Ready(Some(())) 136 | } else { 137 | Poll::Pending 138 | } 139 | } 140 | } 141 | 142 | pub(crate) extern "x86-interrupt" fn interrupt_handler(_stack_frame: InterruptStackFrame) { 143 | let _guard = InterruptContextGuard::new(); 144 | INTERRUPTED_FLAG.store(true, Ordering::Relaxed); 145 | WAKER.wake(); 146 | interrupt::notify_end_of_interrupt(); 147 | } 148 | 149 | pub(crate) async fn handler_task() { 150 | let mut interrupts = InterruptStream::new(); 151 | while let Some(()) = interrupts.next().await { 152 | let mut xhc = XHC.get().lock(); 153 | while xhc.has_event() { 154 | if let Err(err) = xhc.process_event().map_err(Error::from) { 155 | error!("error while process_event: {}", err); 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/keyboard.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | layer, 3 | prelude::*, 4 | sync::{mpsc, OnceCell}, 5 | }; 6 | use core::future::Future; 7 | use enumflags2::{bitflags, BitFlags}; 8 | 9 | const KEYCODE_MAP: [char; 256] = [ 10 | '\0', '\0', '\0', '\0', 'a', 'b', 'c', 'd', // 0 11 | 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', // 8 12 | 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', // 16 13 | 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', // 24 14 | '3', '4', '5', '6', '7', '8', '9', '0', // 32 15 | '\n', '\x08', '\x08', '\t', ' ', '-', '=', '[', // 40 16 | ']', '\\', '#', ';', '\'', '`', ',', '.', // 48 17 | '/', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 56 18 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 64 19 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 72 20 | '\0', '\0', '\0', '\0', '/', '*', '-', '+', // 80 21 | '\n', '1', '2', '3', '4', '5', '6', '7', // 88 22 | '8', '9', '0', '.', '\\', '\0', '\0', '=', // 96 23 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 104 24 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 112 25 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 120 26 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 128 27 | '\0', '\\', '\0', '\0', '\0', '\0', '\0', '\0', // 136 28 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 144 29 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 152 30 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 160 31 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 168 32 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 176 33 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 184 34 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 192 35 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 200 36 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 208 37 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 216 38 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 224 39 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 232 40 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 240 41 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 248 42 | ]; 43 | 44 | const KEYCODE_MAP_SHIFT: [char; 256] = [ 45 | '\0', '\0', '\0', '\0', 'A', 'B', 'C', 'D', // 0 46 | 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', // 8 47 | 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', // 16 48 | 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', // 24 49 | '#', '$', '%', '^', '&', '*', '(', ')', // 32 50 | '\n', '\x08', '\x08', '\t', ' ', '_', '+', '{', // 40 51 | '}', '|', '~', ':', '"', '~', '<', '>', // 48 52 | '?', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 56 53 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 64 54 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 72 55 | '\0', '\0', '\0', '\0', '/', '*', '-', '+', // 80 56 | '\n', '1', '2', '3', '4', '5', '6', '7', // 88 57 | '8', '9', '0', '.', '\\', '\0', '\0', '=', // 96 58 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 104 59 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 112 60 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 120 61 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 128 62 | '\0', '|', '\0', '\0', '\0', '\0', '\0', '\0', // 136 63 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 144 64 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 152 65 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 160 66 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 168 67 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 176 68 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 184 69 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 192 70 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 200 71 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 208 72 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 216 73 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 224 74 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 232 75 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 240 76 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', // 248 77 | ]; 78 | 79 | #[bitflags] 80 | #[repr(u8)] 81 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 82 | pub(crate) enum Modifier { 83 | LControl = 0b00000001, 84 | LShift = 0b00000010, 85 | LAlt = 0b00000100, 86 | LGui = 0b00001000, 87 | RControl = 0b00010000, 88 | RShift = 0b00100000, 89 | RAlt = 0b01000000, 90 | RGui = 0b10000000, 91 | } 92 | 93 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 94 | struct RawKeyboardEvent { 95 | modifier: BitFlags, 96 | keycode: u8, 97 | } 98 | 99 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 100 | pub(crate) struct KeyboardEvent { 101 | pub(crate) modifier: BitFlags, 102 | pub(crate) keycode: u8, 103 | pub(crate) ascii: char, 104 | } 105 | 106 | static KEYBOARD_EVENT_TX: OnceCell> = OnceCell::uninit(); 107 | 108 | pub(crate) extern "C" fn observer(modifier: u8, keycode: u8) { 109 | let modifier = BitFlags::::from_bits_truncate(modifier); 110 | let event = RawKeyboardEvent { modifier, keycode }; 111 | let res = KEYBOARD_EVENT_TX.try_get().and_then(|tx| tx.send(event)); 112 | 113 | if let Err(err) = res { 114 | error!("failed to enqueue to the queue: {}", err); 115 | } 116 | } 117 | 118 | pub(crate) fn handler_task() -> impl Future> { 119 | // Initialize KEYBOARD_EVENT_TX before co-task starts 120 | let (tx, mut rx) = mpsc::channel(100); 121 | KEYBOARD_EVENT_TX.init_once(|| tx); 122 | 123 | async move { 124 | let tx = layer::event_tx(); 125 | 126 | while let Some(event) = rx.next().await { 127 | let ascii = if event 128 | .modifier 129 | .intersects(Modifier::LShift | Modifier::RShift) 130 | { 131 | KEYCODE_MAP_SHIFT[usize::from(event.keycode)] 132 | } else { 133 | KEYCODE_MAP[usize::from(event.keycode)] 134 | }; 135 | let event = KeyboardEvent { 136 | modifier: event.modifier, 137 | keycode: event.keycode, 138 | ascii, 139 | }; 140 | tx.keyboard_event(event).await?; 141 | } 142 | Ok(()) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/acpi.rs: -------------------------------------------------------------------------------- 1 | use crate::{memory, paging, prelude::*, sync::OnceCell}; 2 | use core::{mem, slice}; 3 | use x86_64::{instructions::port::PortReadOnly, structures::paging::OffsetPageTable, VirtAddr}; 4 | 5 | /// Root System Description Pointer 6 | #[derive(Debug)] 7 | #[repr(C)] 8 | pub(crate) struct Rsdp { 9 | signature: [u8; 8], 10 | checksum: u8, 11 | oem_id: [u8; 6], 12 | revision: u8, 13 | rsdt_address: u32, 14 | length: u32, 15 | xsdt_address: u64, 16 | extended_checksum: u8, 17 | reserved: [u8; 3], 18 | } 19 | 20 | impl Rsdp { 21 | fn is_valid(&self) -> bool { 22 | if self.signature != *b"RSD PTR " { 23 | warn!("invalid signature: {:?}", self.signature); 24 | return false; 25 | } 26 | if self.revision != 2 { 27 | warn!("ACPI revision must be 2: {}", self.revision); 28 | return false; 29 | } 30 | let sum = unsafe { sum_bytes(self, 20) }; 31 | if sum != 0 { 32 | warn!("sum of 20 bytes must be 0: {}", sum); 33 | return false; 34 | } 35 | let sum = unsafe { sum_bytes(self, 36) }; 36 | if sum != 0 { 37 | warn!("sum of 36 bytes must be 0: {}", sum); 38 | return false; 39 | } 40 | true 41 | } 42 | } 43 | 44 | #[derive(Debug)] 45 | #[repr(C)] 46 | struct DescriptionHeader { 47 | signature: [u8; 4], 48 | length: u32, 49 | revision: u8, 50 | checksum: u8, 51 | oem_id: [u8; 6], 52 | oem_table_id: [u8; 8], 53 | oem_revision: u32, 54 | creator_id: u32, 55 | creator_revision: u32, 56 | } 57 | static_assertions::const_assert_eq!(mem::size_of::(), 36); 58 | 59 | /// Extended System Descriptor Table 60 | #[derive(Debug)] 61 | #[repr(C)] 62 | struct Xsdt { 63 | header: DescriptionHeader, 64 | } 65 | 66 | impl DescriptionHeader { 67 | fn is_valid(&self, expected_signature: &[u8]) -> bool { 68 | if self.signature != expected_signature { 69 | warn!("invalid signature: {:?}", self.signature); 70 | return false; 71 | } 72 | let sum = unsafe { sum_bytes(self, self.len()) }; 73 | if sum != 0 { 74 | warn!("sum of {} bytes must be 0: {}", self.length, sum); 75 | return false; 76 | } 77 | true 78 | } 79 | 80 | fn len(&self) -> usize { 81 | self.length as usize 82 | } 83 | } 84 | 85 | impl Xsdt { 86 | fn len(&self) -> usize { 87 | (self.header.len() - mem::size_of::()) / mem::size_of::() 88 | } 89 | 90 | fn entries(&self) -> impl Iterator { 91 | // `array_head` is not 8-byte aligned, so we cannot treat it as normal `*const u64`. 92 | // For example, `slice::from_raw_parts(array_head, len)` panics in debug build. 93 | let array_head = 94 | unsafe { (&self.header as *const DescriptionHeader).add(1) as *const [u8; 8] }; 95 | (0..self.len()).map(move |idx| { 96 | let bytes = unsafe { array_head.add(idx).read() }; 97 | u64::from_le_bytes(bytes) 98 | }) 99 | } 100 | } 101 | 102 | /// Extended System Descriptor Table 103 | #[derive(Debug)] 104 | #[repr(C)] 105 | struct Fadt { 106 | header: DescriptionHeader, 107 | reserved: [u8; 76 - mem::size_of::()], 108 | pm_tmr_blk: u32, 109 | reserved2: [u8; 112 - 80], 110 | flags: u32, 111 | reserved3: [u8; 276 - 116], 112 | } 113 | 114 | static FADT: OnceCell<&Fadt> = OnceCell::uninit(); 115 | 116 | /// # Safety 117 | /// 118 | /// This function is unsafe because the caller must guarantee that the 119 | /// data has size larger than or equal to `bytes`. 120 | unsafe fn sum_bytes(data: &T, bytes: usize) -> u8 { 121 | let data = data as *const T as *const u8; 122 | let data = unsafe { slice::from_raw_parts(data, bytes) }; 123 | data.iter().copied().fold(0, u8::wrapping_add) 124 | } 125 | 126 | /// # Safety 127 | /// 128 | /// This function is unsafe because the caller must guarantee that the 129 | /// complete RSDP is mapped to virtual memory at the passed `rsdp`. 130 | pub(crate) unsafe fn init(mapper: &mut OffsetPageTable, rsdp: VirtAddr) -> Result<()> { 131 | debug!("RSDP: {:x}", rsdp.as_u64()); 132 | map_page(mapper, rsdp)?; 133 | 134 | #[allow(clippy::unwrap_used)] 135 | let rsdp = unsafe { rsdp.as_ptr::().as_ref() }.unwrap(); 136 | if !rsdp.is_valid() { 137 | bail!(ErrorKind::InvalidRsdp); 138 | } 139 | 140 | let xsdt = VirtAddr::new(rsdp.xsdt_address); 141 | debug!("XSDT: {:x}", xsdt.as_u64()); 142 | map_page(mapper, xsdt)?; 143 | 144 | #[allow(clippy::unwrap_used)] 145 | let xsdt = unsafe { xsdt.as_ptr::().as_ref() }.unwrap(); 146 | if !xsdt.header.is_valid(b"XSDT") { 147 | bail!(ErrorKind::InvalidXsdt); 148 | } 149 | 150 | #[allow(clippy::unwrap_used)] 151 | let fadt = xsdt 152 | .entries() 153 | .filter_map(|entry| { 154 | debug!("entry: {:x}", entry); 155 | map_page(mapper, VirtAddr::new(entry)).unwrap(); 156 | unsafe { (entry as *const DescriptionHeader).as_ref() } 157 | }) 158 | // FACP is the signature of FADT 159 | .find(|&entry| entry.is_valid(b"FACP")) 160 | .and_then(|entry| unsafe { (entry as *const DescriptionHeader as *const Fadt).as_ref() }) 161 | .ok_or(ErrorKind::FadtNotFound)?; 162 | 163 | FADT.init_once(|| fadt); 164 | 165 | Ok(()) 166 | } 167 | 168 | pub(crate) const PM_TIMER_FREQ: u32 = 3579545; 169 | 170 | pub(crate) fn wait_milliseconds(msec: u32) { 171 | let fadt = FADT.get(); 172 | let pm_timer_32 = ((fadt.flags >> 8) & 1) != 0; 173 | 174 | let mut port = PortReadOnly::::new(fadt.pm_tmr_blk as u16); 175 | let start = unsafe { port.read() }; 176 | let mut end = start + PM_TIMER_FREQ * msec / 1000; 177 | if !pm_timer_32 { 178 | end &= 0x00ffffff; 179 | } 180 | 181 | if end < start { 182 | while unsafe { port.read() } >= start {} 183 | } 184 | while unsafe { port.read() } < end {} 185 | } 186 | 187 | fn map_page(mapper: &mut OffsetPageTable, addr: VirtAddr) -> Result<()> { 188 | let mut allocator = memory::lock_memory_manager(); 189 | paging::make_identity_mapping( 190 | mapper, 191 | &mut *allocator, 192 | addr.align_down(4096u64).as_u64(), 193 | 1, 194 | )?; 195 | Ok(()) 196 | } 197 | -------------------------------------------------------------------------------- /src/memory.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | prelude::*, 3 | sync::{SpinMutex, SpinMutexGuard}, 4 | }; 5 | use bootloader::boot_info::{MemoryRegion, MemoryRegionKind}; 6 | use core::cmp; 7 | use x86_64::{ 8 | structures::paging::{frame::PhysFrameRange, FrameAllocator, PhysFrame, Size4KiB}, 9 | PhysAddr, 10 | }; 11 | 12 | const fn kib(kib: u64) -> u64 { 13 | kib * 1024 14 | } 15 | const fn mib(mib: u64) -> u64 { 16 | mib * kib(1024) 17 | } 18 | const fn gib(gib: u64) -> u64 { 19 | gib * mib(1024) 20 | } 21 | 22 | pub(crate) const BYTES_PER_FRAME: u64 = kib(4); 23 | const MAX_PHYSICAL_MEMORY_BYTE: u64 = gib(128); 24 | const FRAME_COUNT: u64 = MAX_PHYSICAL_MEMORY_BYTE / BYTES_PER_FRAME; 25 | 26 | type MapLine = u64; 27 | const BITS_PER_MAP_LINE: u64 = MapLine::BITS as u64; 28 | const ALLOC_MAP_LEN: usize = (FRAME_COUNT / (BITS_PER_MAP_LINE as u64)) as usize; 29 | 30 | pub(crate) struct BitmapMemoryManager { 31 | alloc_map: [MapLine; ALLOC_MAP_LEN], 32 | range: PhysFrameRange, 33 | } 34 | 35 | static MEMORY_MANAGER: SpinMutex = SpinMutex::new(BitmapMemoryManager { 36 | alloc_map: [0; ALLOC_MAP_LEN], 37 | range: PhysFrameRange { 38 | start: unsafe { 39 | PhysFrame::from_start_address_unchecked(PhysAddr::new_truncate( 40 | MAX_PHYSICAL_MEMORY_BYTE, 41 | )) 42 | }, 43 | end: unsafe { PhysFrame::from_start_address_unchecked(PhysAddr::new_truncate(0)) }, 44 | }, 45 | }); 46 | 47 | pub(crate) fn lock_memory_manager() -> SpinMutexGuard<'static, BitmapMemoryManager> { 48 | MEMORY_MANAGER.lock() 49 | } 50 | 51 | impl BitmapMemoryManager { 52 | pub(crate) fn init(&mut self, regions: &[MemoryRegion]) -> Result<()> { 53 | let regions = MergedMemoryRegion::new(regions); 54 | let frame_size = 4096u64; 55 | 56 | let mut available_start = self.range.start; 57 | let mut available_end = self.range.end; 58 | for region in regions { 59 | let usable = region.kind == MemoryRegionKind::Usable; 60 | 61 | let start = PhysAddr::new(region.start); 62 | let end = PhysAddr::new(region.end); 63 | let (start, end) = if usable { 64 | (start.align_up(frame_size), end.align_down(frame_size)) 65 | } else { 66 | (start.align_down(frame_size), end.align_up(frame_size)) 67 | }; 68 | if start >= end { 69 | continue; 70 | } 71 | 72 | let start = PhysFrame::from_start_address(start)?; 73 | let end = PhysFrame::from_start_address(end)?; 74 | 75 | if available_end < start { 76 | self.mark_allocated(PhysFrame::range(available_end, start)); 77 | } 78 | 79 | if usable { 80 | available_start = cmp::min(available_start, start); 81 | available_end = cmp::max(available_end, end); 82 | } else { 83 | self.mark_allocated(PhysFrame::range(start, end)); 84 | } 85 | } 86 | 87 | self.range = PhysFrame::range(available_start, available_end); 88 | Ok(()) 89 | } 90 | 91 | pub(crate) fn mark_allocated(&mut self, range: PhysFrameRange) { 92 | for frame in range { 93 | self.set_bit(frame, true); 94 | } 95 | // update range for faster allocation 96 | if self.range.start == range.start { 97 | self.range.start = range.end; 98 | while self.range.start < self.range.end && self.get_bit(self.range.start) { 99 | self.range.start += 1; 100 | } 101 | } 102 | } 103 | 104 | // fn mark_freed(&mut self, range: PhysFrameRange) { 105 | // for frame in range { 106 | // self.set_bit(frame, false) 107 | // } 108 | // // update range if needed 109 | // if self.range.start <= range.end { 110 | // self.range.start = range.start; 111 | // } 112 | // } 113 | 114 | pub(crate) fn allocate(&mut self, num_frames: usize) -> Result { 115 | let mut start_frame = self.range.start; 116 | loop { 117 | let end_frame = start_frame + num_frames as u64; 118 | if end_frame > self.range.end { 119 | bail!(ErrorKind::NoEnoughMemory); 120 | } 121 | 122 | let range = PhysFrame::range(start_frame, end_frame); 123 | if let Some(allocated) = range.clone().find(|frame| self.get_bit(*frame)) { 124 | start_frame = allocated + 1; 125 | continue; 126 | } 127 | 128 | self.mark_allocated(range); 129 | return Ok(range); 130 | } 131 | } 132 | 133 | // pub(crate) fn free(&mut self, range: PhysFrameRange) { 134 | // for frame in range { 135 | // self.set_bit(frame, false) 136 | // } 137 | // } 138 | 139 | fn get_bit(&self, frame: PhysFrame) -> bool { 140 | let frame_index = frame.start_address().as_u64() / BYTES_PER_FRAME; 141 | let line_index = (frame_index / BITS_PER_MAP_LINE) as usize; 142 | let bit_index = frame_index % BITS_PER_MAP_LINE; 143 | 144 | (self.alloc_map[line_index] & (1 << bit_index)) != 0 145 | } 146 | 147 | fn set_bit(&mut self, frame: PhysFrame, allocated: bool) { 148 | let frame_index = frame.start_address().as_u64() / BYTES_PER_FRAME; 149 | let line_index = (frame_index / BITS_PER_MAP_LINE) as usize; 150 | let bit_index = frame_index % BITS_PER_MAP_LINE; 151 | 152 | if allocated { 153 | self.alloc_map[line_index] |= 1 << bit_index; 154 | } else { 155 | self.alloc_map[line_index] &= !(1 << bit_index); 156 | } 157 | } 158 | } 159 | 160 | unsafe impl FrameAllocator for BitmapMemoryManager { 161 | fn allocate_frame(&mut self) -> Option> { 162 | self.allocate(1).map(|range| range.start).ok() 163 | } 164 | } 165 | 166 | #[derive(Debug)] 167 | struct MergedMemoryRegion<'a> { 168 | regions: core::slice::Iter<'a, MemoryRegion>, 169 | } 170 | 171 | impl<'a> MergedMemoryRegion<'a> { 172 | fn new(regions: &'a [MemoryRegion]) -> Self { 173 | let regions = regions.iter(); 174 | Self { regions } 175 | } 176 | } 177 | 178 | impl<'a> Iterator for MergedMemoryRegion<'a> { 179 | type Item = MemoryRegion; 180 | 181 | fn next(&mut self) -> Option { 182 | let mut current = *self.regions.next()?; 183 | loop { 184 | #[allow(clippy::suspicious_operation_groupings)] 185 | match self.regions.as_slice().get(0) { 186 | Some(next) if current.kind == next.kind && current.end == next.start => { 187 | current.end = next.end; 188 | let _ = self.regions.next(); 189 | continue; 190 | } 191 | _ => return Some(current), 192 | } 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![warn(unsafe_op_in_unsafe_fn)] 2 | #![warn(clippy::unwrap_used)] 3 | #![warn(clippy::expect_used)] 4 | #![feature(asm)] 5 | #![feature(abi_x86_interrupt)] 6 | #![feature(alloc_error_handler)] 7 | #![feature(const_mut_refs)] 8 | #![feature(custom_test_frameworks)] 9 | #![feature(lang_items)] 10 | #![feature(naked_functions)] 11 | #![no_std] 12 | #![no_main] 13 | #![test_runner(test_runner)] 14 | #![reexport_test_harness_main = "test_main"] 15 | 16 | extern crate alloc; 17 | 18 | use self::{ 19 | co_task::{CoTask, Executor}, 20 | graphics::{Point, Size}, 21 | prelude::*, 22 | task::Task, 23 | terminal::Terminal, 24 | text_window::TextWindow, 25 | }; 26 | use bootloader::{ 27 | boot_info::{FrameBuffer, Optional}, 28 | entry_point, BootInfo, 29 | }; 30 | use core::{mem, panic::PanicInfo}; 31 | use x86_64::VirtAddr; 32 | 33 | mod acpi; 34 | mod allocator; 35 | mod co_task; 36 | mod console; 37 | mod cxx_support; 38 | mod desktop; 39 | mod emergency_console; 40 | mod error; 41 | mod fat; 42 | mod fmt; 43 | mod framed_window; 44 | mod gdt; 45 | mod graphics; 46 | mod interrupt; 47 | mod keyboard; 48 | mod layer; 49 | mod log; 50 | mod macros; 51 | mod memory; 52 | mod mouse; 53 | mod paging; 54 | mod pci; 55 | mod prelude; 56 | mod serial; 57 | mod sync; 58 | mod task; 59 | mod terminal; 60 | mod text_window; 61 | mod timer; 62 | mod triple_buffer; 63 | mod window; 64 | mod xhc; 65 | 66 | entry_point!(kernel_main); 67 | 68 | #[allow(clippy::expect_used)] 69 | fn kernel_main(boot_info: &'static mut BootInfo) -> ! { 70 | log::set_level(log::Level::Warn, log::Level::Debug); 71 | 72 | init(boot_info).expect("failed to initialize kernel"); 73 | 74 | #[cfg(test)] 75 | test_main(); 76 | 77 | start_window(); 78 | } 79 | 80 | fn init(boot_info: &'static mut BootInfo) -> Result<()> { 81 | let (frame_buffer, physical_memory_offset, rsdp) = extract_boot_info(boot_info)?; 82 | 83 | // Initialize graphics for boot log 84 | graphics::init(frame_buffer)?; 85 | 86 | // Initialize memory mapping / frame allocator / heap 87 | let mut mapper = unsafe { paging::init(physical_memory_offset) }; 88 | { 89 | let mut allocator = memory::lock_memory_manager(); 90 | 91 | allocator.init(&*boot_info.memory_regions)?; 92 | 93 | // Map CPU register addresses as identity mapping 94 | paging::make_identity_mapping(&mut mapper, &mut *allocator, 0xfee00000, 1)?; 95 | 96 | allocator::init_heap(&mut mapper, &mut *allocator)?; 97 | } 98 | 99 | // Initialize GDT/IDT 100 | gdt::init(); 101 | interrupt::init(); 102 | 103 | // Initialize PCI devices 104 | let devices = pci::scan_all_bus()?; 105 | xhc::init(&devices, &mut mapper)?; 106 | 107 | // Initialize LAPIC timer 108 | unsafe { acpi::init(&mut mapper, rsdp) }?; 109 | timer::lapic::init(); 110 | 111 | // Initialize file system 112 | fat::init(); 113 | 114 | task::init(); 115 | 116 | info!("Initialization completed"); 117 | 118 | Ok(()) 119 | } 120 | 121 | #[allow(clippy::expect_used)] 122 | fn start_window() -> ! { 123 | let layer_task = layer::handler_task().unwrap(); 124 | let console_param = console::start_window_mode().expect("failed to start console window mode"); 125 | 126 | let task_id = task::current().id(); 127 | 128 | // Initialize executor & co-tasks 129 | let mut executor = Executor::new(task_id); 130 | executor.spawn(CoTask::new(xhc::handler_task())); 131 | executor.spawn(CoTask::new(timer::lapic::handler_task())); 132 | executor.spawn(CoTask::new(mouse::handler_task().unwrap())); 133 | executor.spawn(CoTask::new(keyboard::handler_task().unwrap())); 134 | executor.spawn(CoTask::new(desktop::handler_task().unwrap())); 135 | executor.spawn(CoTask::new(console::handler_task(console_param).unwrap())); 136 | executor.spawn(CoTask::new(layer_task)); 137 | 138 | #[allow(clippy::unwrap_used)] 139 | task::spawn(Task::new( 140 | TextWindow::new("Text Box test".into(), Point::new(500, 100)) 141 | .unwrap() 142 | .run() 143 | .unwrap(), 144 | )); 145 | #[allow(clippy::unwrap_used)] 146 | task::spawn(Task::new( 147 | Terminal::new( 148 | "sabios Terminal".into(), 149 | Point::new(100, 200), 150 | Size::new(60, 15), 151 | ) 152 | .unwrap() 153 | .run() 154 | .unwrap(), 155 | )); 156 | 157 | x86_64::instructions::interrupts::enable(); 158 | 159 | // Start running 160 | println!("Welcome to sabios!"); 161 | 162 | executor.run(); 163 | } 164 | 165 | fn extract_boot_info(boot_info: &mut BootInfo) -> Result<(FrameBuffer, VirtAddr, VirtAddr)> { 166 | let frame_buffer = mem::replace(&mut boot_info.framebuffer, Optional::None) 167 | .into_option() 168 | .ok_or(ErrorKind::FrameBufferNotSupported)?; 169 | 170 | let physical_memory_offset = boot_info 171 | .physical_memory_offset 172 | .as_ref() 173 | .copied() 174 | .ok_or(ErrorKind::PhysicalMemoryNotMapped)?; 175 | let physical_memory_offset = VirtAddr::new(physical_memory_offset); 176 | 177 | let rsdp = boot_info 178 | .rsdp_addr 179 | .as_ref() 180 | .copied() 181 | .ok_or(ErrorKind::RsdpNotMapped)?; 182 | let rsdp = VirtAddr::new(rsdp); 183 | 184 | Ok((frame_buffer, physical_memory_offset, rsdp)) 185 | } 186 | 187 | fn hlt_loop() -> ! { 188 | loop { 189 | x86_64::instructions::hlt(); 190 | } 191 | } 192 | 193 | #[lang = "eh_personality"] 194 | extern "C" fn eh_personality() {} 195 | 196 | #[cfg(not(test))] 197 | #[panic_handler] 198 | fn panic(info: &PanicInfo) -> ! { 199 | use core::fmt::Write as _; 200 | emergency_console::with_console(|console| { 201 | let _ = write!(console, "{}", info); 202 | }); 203 | } 204 | 205 | #[cfg(test)] 206 | #[panic_handler] 207 | fn panic(info: &PanicInfo) -> ! { 208 | serial_println!("[failed]\n"); 209 | serial_println!("Error: {}\n", info); 210 | exit_qemu(QemuExitCode::Failed); 211 | } 212 | 213 | #[cfg(test)] 214 | fn test_runner(tests: &[&dyn Testable]) { 215 | serial_println!("Running {} tests", tests.len()); 216 | for test in tests { 217 | test.run(); 218 | } 219 | exit_qemu(QemuExitCode::Success); 220 | } 221 | 222 | trait Testable { 223 | fn run(&self); 224 | } 225 | 226 | impl Testable for T 227 | where 228 | T: Fn(), 229 | { 230 | fn run(&self) { 231 | serial_print!("{}...\t", core::any::type_name::()); 232 | self(); 233 | serial_println!("[ok]"); 234 | } 235 | } 236 | 237 | #[cfg(test)] 238 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 239 | #[repr(u32)] 240 | enum QemuExitCode { 241 | Success = 0x10, 242 | Failed = 0x11, 243 | } 244 | 245 | #[cfg(test)] 246 | fn exit_qemu(exit_code: QemuExitCode) -> ! { 247 | use x86_64::instructions::port::Port; 248 | 249 | unsafe { 250 | let mut port = Port::new(0xf4); 251 | port.write(exit_code as u32); 252 | } 253 | 254 | hlt_loop(); 255 | } 256 | -------------------------------------------------------------------------------- /src/framed_window.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | graphics::{Color, Draw, Point, Rectangle, Size}, 3 | keyboard::KeyboardEvent, 4 | prelude::*, 5 | window::WindowEvent, 6 | window::{self, Window}, 7 | }; 8 | use alloc::string::String; 9 | 10 | const PADDING_TOP: i32 = 24; 11 | const PADDING_BOTTOM: i32 = 4; 12 | const PADDING_LEFT: i32 = 4; 13 | const PADDING_RIGHT: i32 = 4; 14 | const PADDING_POS: Point = Point::new(PADDING_LEFT, PADDING_TOP); 15 | const PADDING_SIZE: Size = 16 | Size::new(PADDING_LEFT + PADDING_RIGHT, PADDING_TOP + PADDING_BOTTOM); 17 | 18 | #[derive(Debug, Clone)] 19 | pub(crate) struct Builder { 20 | title: String, 21 | inner: window::Builder, 22 | } 23 | 24 | impl Builder { 25 | pub(crate) fn new(title: String) -> Self { 26 | let mut inner = window::Builder::new(); 27 | inner.draggable(true); 28 | inner.height(usize::MAX); 29 | Self { title, inner } 30 | } 31 | 32 | pub(crate) fn pos(mut self, pos: Point) -> Self { 33 | self.inner.pos(pos); 34 | self 35 | } 36 | 37 | pub(crate) fn size(mut self, size: Size) -> Self { 38 | self.inner.size(size + PADDING_SIZE); 39 | self 40 | } 41 | 42 | pub(crate) fn build(mut self) -> Result { 43 | let window = self.inner.build()?; 44 | let mut window = FramedWindow { 45 | title: self.title, 46 | active: false, 47 | window, 48 | }; 49 | window.draw_frame(); 50 | Ok(window) 51 | } 52 | } 53 | 54 | #[derive(Debug)] 55 | pub(crate) enum FramedWindowEvent { 56 | Keyboard(KeyboardEvent), 57 | } 58 | 59 | #[derive(Debug)] 60 | pub(crate) struct FramedWindow { 61 | title: String, 62 | active: bool, 63 | window: Window, 64 | } 65 | 66 | impl Draw for FramedWindow { 67 | fn size(&self) -> Size { 68 | self.window.size() - PADDING_SIZE 69 | } 70 | 71 | fn draw(&mut self, p: Point, c: Color) { 72 | if self.area().contains(&p) { 73 | self.window.draw(p + PADDING_POS, c); 74 | } 75 | } 76 | 77 | fn move_area(&mut self, offset: Point, src: Rectangle) { 78 | if offset.x == 0 && offset.y == 0 { 79 | return; 80 | } 81 | 82 | (|| { 83 | let dst = (((src & self.area())? + offset) & self.area())?; 84 | let src = dst - offset; 85 | 86 | self.window 87 | .move_area(offset, Rectangle::new(src.pos + PADDING_POS, src.size)); 88 | 89 | Some(()) 90 | })(); 91 | } 92 | } 93 | 94 | impl FramedWindow { 95 | pub(crate) async fn recv_event(&mut self) -> Option> { 96 | while let Some(event) = self.window.recv_event().await { 97 | match event { 98 | WindowEvent::Activated => { 99 | if let Err(err) = self.activate().await { 100 | return Some(Err(err)); 101 | } 102 | continue; 103 | } 104 | WindowEvent::Deactivated => { 105 | if let Err(err) = self.deactivate().await { 106 | return Some(Err(err)); 107 | } 108 | continue; 109 | } 110 | WindowEvent::Keyboard(event) => { 111 | return Some(Ok(FramedWindowEvent::Keyboard(event))) 112 | } 113 | } 114 | } 115 | None 116 | } 117 | 118 | async fn activate(&mut self) -> Result<()> { 119 | if !self.active { 120 | self.draw_title_bar(true); 121 | self.active = true; 122 | self.flush().await?; 123 | } 124 | Ok(()) 125 | } 126 | 127 | async fn deactivate(&mut self) -> Result<()> { 128 | if self.active { 129 | self.draw_title_bar(false); 130 | self.active = false; 131 | self.flush().await?; 132 | } 133 | Ok(()) 134 | } 135 | } 136 | 137 | const CLOSE_BUTTON_WIDTH: usize = 16; 138 | const CLOSE_BUTTON_HEIGHT: usize = 14; 139 | const CLOSE_BUTTON: [[u8; CLOSE_BUTTON_WIDTH]; CLOSE_BUTTON_HEIGHT] = [ 140 | *b"...............@", 141 | *b".:::::::::::::$@", 142 | *b".:::::::::::::$@", 143 | *b".:::@@::::@@::$@", 144 | *b".::::@@::@@:::$@", 145 | *b".:::::@@@@::::$@", 146 | *b".::::::@@:::::$@", 147 | *b".:::::@@@@::::$@", 148 | *b".::::@@::@@:::$@", 149 | *b".:::@@::::@@::$@", 150 | *b".:::::::::::::$@", 151 | *b".:::::::::::::$@", 152 | *b".$$$$$$$$$$$$$$@", 153 | *b"@@@@@@@@@@@@@@@@", 154 | ]; 155 | 156 | const EDGE_DARK: Color = Color::from_code(0x848484); 157 | const EDGE_LIGHT: Color = Color::from_code(0xc6c6c6); 158 | const ACTIVE_BACKGROUND: Color = Color::from_code(0x000084); 159 | const INACTIVE_BACKGROUND: Color = Color::from_code(0x848484); 160 | 161 | impl FramedWindow { 162 | pub(crate) fn builder(title: String) -> Builder { 163 | Builder::new(title) 164 | } 165 | 166 | pub(crate) async fn flush(&mut self) -> Result<()> { 167 | self.window.flush().await 168 | } 169 | 170 | fn draw_frame(&mut self) { 171 | let win_size = self.window.size(); 172 | let (wx, wy) = (win_size.x, win_size.y); 173 | 174 | let data = &[ 175 | ((0, 0), (wx, 1), EDGE_LIGHT), 176 | ((1, 1), (wx - 2, 1), Color::WHITE), 177 | ((0, 0), (1, wy), EDGE_LIGHT), 178 | ((1, 1), (1, wy - 2), Color::WHITE), 179 | ((wx - 2, 1), (1, wy - 2), EDGE_DARK), 180 | ((wx - 1, 0), (1, wy), Color::BLACK), 181 | ((2, 2), (wx - 4, wy - 4), EDGE_LIGHT), 182 | ((1, wy - 2), (wx - 2, 1), EDGE_DARK), 183 | ((0, wy - 1), (wx, 1), Color::BLACK), 184 | ]; 185 | 186 | for (pos, size, color) in data { 187 | self.window.fill_rect( 188 | Rectangle::new(Point::new(pos.0, pos.1), Size::new(size.0, size.1)), 189 | *color, 190 | ); 191 | } 192 | 193 | self.draw_title_bar(false); 194 | } 195 | 196 | fn draw_title_bar(&mut self, active: bool) { 197 | let win_size = self.window.size(); 198 | let (wx, _wy) = (win_size.x, win_size.y); 199 | 200 | let background = if active { 201 | ACTIVE_BACKGROUND 202 | } else { 203 | INACTIVE_BACKGROUND 204 | }; 205 | 206 | self.window.fill_rect( 207 | Rectangle::new(Point::new(3, 3), Size::new(wx - 6, 18)), 208 | background, 209 | ); 210 | self.window 211 | .draw_str(Point::new(24, 4), &self.title, Color::WHITE); 212 | 213 | for (y, row) in (0..).zip(CLOSE_BUTTON) { 214 | for (x, ch) in (0..).zip(row) { 215 | let c = match ch { 216 | b'@' => Color::BLACK, 217 | b'$' => EDGE_DARK, 218 | b':' => EDGE_LIGHT, 219 | b'.' => Color::WHITE, 220 | _ => panic!("invalid char: {}", ch), 221 | }; 222 | self.window 223 | .draw(Point::new(wx - 5 - CLOSE_BUTTON_WIDTH as i32 + x, 5 + y), c); 224 | } 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/window.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | graphics::{Color, Draw, Point, Rectangle, ScreenInfo, Size}, 3 | keyboard::KeyboardEvent, 4 | layer::{self, EventSender, Layer, LayerBuffer, LayerId}, 5 | prelude::*, 6 | sync::mpsc, 7 | triple_buffer::{self, Producer}, 8 | }; 9 | 10 | #[derive(Debug)] 11 | pub(crate) enum WindowEvent { 12 | Activated, 13 | Deactivated, 14 | Keyboard(KeyboardEvent), 15 | } 16 | 17 | #[derive(Debug, Clone)] 18 | pub(crate) struct Builder { 19 | pos: Option>, 20 | size: Size, 21 | transparent_color: Option, 22 | height: Option, 23 | draggable: Option, 24 | } 25 | 26 | impl Builder { 27 | pub(crate) fn new() -> Self { 28 | Self { 29 | pos: None, 30 | size: Size::new(0, 0), 31 | transparent_color: None, 32 | height: None, 33 | draggable: None, 34 | } 35 | } 36 | 37 | pub(crate) fn pos(&mut self, pos: Point) -> &mut Self { 38 | self.pos = Some(pos); 39 | self 40 | } 41 | 42 | pub(crate) fn size(&mut self, size: Size) -> &mut Self { 43 | self.size = size; 44 | self 45 | } 46 | 47 | pub(crate) fn transparent_color(&mut self, tc: Option) -> &mut Self { 48 | self.transparent_color = tc; 49 | self 50 | } 51 | 52 | pub(crate) fn height(&mut self, height: usize) -> &mut Self { 53 | self.height = Some(height); 54 | self 55 | } 56 | 57 | pub(crate) fn draggable(&mut self, draggable: bool) -> &mut Self { 58 | self.draggable = Some(draggable); 59 | self 60 | } 61 | 62 | pub(crate) fn build(&mut self) -> Result { 63 | let screen_info = ScreenInfo::get(); 64 | let mut buffer = LayerBuffer::new(self.size, screen_info)?; 65 | buffer.set_transparent_color(self.transparent_color); 66 | 67 | let (producer, consumer) = triple_buffer::new(buffer.clone()); 68 | let (tx, rx) = mpsc::channel(100); 69 | let mut layer = Layer::new(consumer, tx); 70 | let layer_id = layer.id(); 71 | let event_tx = layer::event_tx(); 72 | 73 | if let Some(pos) = self.pos { 74 | layer.move_to(pos); 75 | } 76 | 77 | if let Some(draggable) = self.draggable { 78 | layer.set_draggable(draggable); 79 | } 80 | 81 | event_tx.register(layer)?; 82 | 83 | if let Some(height) = self.height { 84 | event_tx.set_height(layer_id, height)?; 85 | } 86 | 87 | Ok(Window { 88 | layer_id, 89 | event_tx, 90 | buffer, 91 | producer, 92 | rx, 93 | redraw_area: RedrawArea::new(self.size), 94 | }) 95 | } 96 | } 97 | 98 | #[derive(Debug)] 99 | pub(crate) struct Window { 100 | layer_id: LayerId, 101 | event_tx: EventSender, 102 | buffer: LayerBuffer, 103 | producer: Producer, 104 | rx: mpsc::Receiver, 105 | redraw_area: RedrawArea, 106 | } 107 | 108 | impl Window { 109 | pub(crate) fn builder() -> Builder { 110 | Builder::new() 111 | } 112 | 113 | pub(crate) fn layer_id(&self) -> LayerId { 114 | self.layer_id 115 | } 116 | 117 | pub(crate) async fn move_to(&self, pos: Point) -> Result<()> { 118 | self.event_tx.move_to(self.layer_id, pos).await 119 | } 120 | 121 | pub(crate) async fn flush(&mut self) -> Result<()> { 122 | if let Some(redraw_area) = self.redraw_area.take() { 123 | self.producer.with_buffer(|buffer| { 124 | buffer.clone_from(&self.buffer); 125 | }); 126 | self.producer.store(); 127 | self.event_tx.draw_layer(self.layer_id, redraw_area).await?; 128 | } 129 | Ok(()) 130 | } 131 | 132 | pub(crate) async fn recv_event(&mut self) -> Option { 133 | self.rx.next().await 134 | } 135 | } 136 | 137 | impl Draw for Window { 138 | fn size(&self) -> Size { 139 | self.buffer.size() 140 | } 141 | 142 | fn draw(&mut self, p: Point, c: Color) { 143 | self.redraw_area.add_point(p); 144 | self.buffer.draw(p, c); 145 | } 146 | 147 | fn move_area(&mut self, offset: Point, src: Rectangle) { 148 | self.redraw_area.add_rect(src + offset); 149 | self.buffer.move_area(offset, src); 150 | } 151 | 152 | // implement some default methods for faster redraw area computation 153 | fn fill_rect(&mut self, rect: Rectangle, c: Color) { 154 | self.redraw_area.add_rect(rect); 155 | self.buffer.fill_rect(rect, c) 156 | } 157 | 158 | fn draw_rect(&mut self, rect: Rectangle, c: Color) { 159 | self.redraw_area.add_rect(rect); 160 | self.buffer.draw_rect(rect, c) 161 | } 162 | 163 | fn draw_byte_char(&mut self, pos: Point, byte: u8, color: Color) -> Rectangle 164 | where 165 | Self: Sized, 166 | { 167 | let rect = self.buffer.draw_byte_char(pos, byte, color); 168 | self.redraw_area.add_rect(rect); 169 | rect 170 | } 171 | 172 | fn draw_byte_str(&mut self, pos: Point, bytes: &[u8], color: Color) -> Rectangle 173 | where 174 | Self: Sized, 175 | { 176 | let rect = self.buffer.draw_byte_str(pos, bytes, color); 177 | self.redraw_area.add_rect(rect); 178 | rect 179 | } 180 | 181 | fn draw_char(&mut self, pos: Point, ch: char, color: Color) -> Rectangle 182 | where 183 | Self: Sized, 184 | { 185 | let rect = self.buffer.draw_char(pos, ch, color); 186 | self.redraw_area.add_rect(rect); 187 | rect 188 | } 189 | 190 | fn draw_str(&mut self, pos: Point, s: &str, color: Color) -> Rectangle 191 | where 192 | Self: Sized, 193 | { 194 | let rect = self.buffer.draw_str(pos, s, color); 195 | self.redraw_area.add_rect(rect); 196 | rect 197 | } 198 | 199 | fn draw_box( 200 | &mut self, 201 | area: Rectangle, 202 | background: Color, 203 | border_top_left: Color, 204 | border_bottom_right: Color, 205 | ) { 206 | self.redraw_area.add_rect(area); 207 | self.buffer 208 | .draw_box(area, background, border_top_left, border_bottom_right); 209 | } 210 | } 211 | 212 | #[derive(Debug)] 213 | struct RedrawArea { 214 | redraw_area: Option>, 215 | draw_area: Rectangle, 216 | } 217 | 218 | impl RedrawArea { 219 | fn new(size: Size) -> Self { 220 | Self { 221 | redraw_area: None, 222 | draw_area: Rectangle::new(Point::new(0, 0), size), 223 | } 224 | } 225 | 226 | fn take(&mut self) -> Option> { 227 | self.redraw_area.take() 228 | } 229 | 230 | fn add_rect(&mut self, area: Rectangle) { 231 | if let Some(area) = self.draw_area & area { 232 | match &mut self.redraw_area { 233 | Some(redraw_area) => *redraw_area = *redraw_area | area, 234 | None => self.redraw_area = Some(area), 235 | } 236 | } 237 | } 238 | 239 | fn add_point(&mut self, p: Point) { 240 | self.add_rect(Rectangle::new(p, Size::new(1, 1))); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod lapic { 2 | use crate::{ 3 | acpi, 4 | interrupt::{self, InterruptContextGuard, InterruptIndex}, 5 | prelude::*, 6 | sync::{mpsc, oneshot, OnceCell}, 7 | task, 8 | }; 9 | use alloc::collections::BinaryHeap; 10 | use core::{ 11 | cmp, 12 | pin::Pin, 13 | sync::atomic::{AtomicU64, Ordering}, 14 | task::{Context, Poll}, 15 | }; 16 | use futures_util::{select_biased, task::AtomicWaker, Future, Stream}; 17 | use volatile::Volatile; 18 | use x86_64::structures::idt::InterruptStackFrame; 19 | 20 | const COUNT_MAX: u32 = u32::MAX; 21 | 22 | fn lvt_timer() -> Volatile<&'static mut u32> { 23 | #[allow(clippy::unwrap_used)] 24 | unsafe { 25 | Volatile::new((0xfee00320u64 as *mut u32).as_mut().unwrap()) 26 | } 27 | } 28 | fn initial_count() -> Volatile<&'static mut u32> { 29 | #[allow(clippy::unwrap_used)] 30 | unsafe { 31 | Volatile::new((0xfee00380u64 as *mut u32).as_mut().unwrap()) 32 | } 33 | } 34 | fn current_count() -> Volatile<&'static mut u32> { 35 | #[allow(clippy::unwrap_used)] 36 | unsafe { 37 | Volatile::new((0xfee00390u64 as *mut u32).as_mut().unwrap()) 38 | } 39 | } 40 | fn divide_config() -> Volatile<&'static mut u32> { 41 | #[allow(clippy::unwrap_used)] 42 | unsafe { 43 | Volatile::new((0xfee003e0u64 as *mut u32).as_mut().unwrap()) 44 | } 45 | } 46 | 47 | pub(crate) fn init() { 48 | divide_config().write(0b1011); // divide 1:1 49 | lvt_timer().write(0b001 << 16); // masked, one-shot 50 | 51 | start(); 52 | acpi::wait_milliseconds(100); 53 | let elapsed = elapsed(); 54 | stop(); 55 | 56 | divide_config().write(0b1011); // divide 1:1 57 | lvt_timer().write((0b010 << 16) | (InterruptIndex::Timer as u32)); // not-masked, periodic 58 | initial_count().write(((elapsed as f64) / 10.0) as u32); // interval : 10 ms 59 | } 60 | 61 | fn start() { 62 | initial_count().write(COUNT_MAX); 63 | } 64 | 65 | fn elapsed() -> u32 { 66 | COUNT_MAX - current_count().read() 67 | } 68 | 69 | fn stop() { 70 | initial_count().write(0); 71 | } 72 | 73 | pub(crate) fn oneshot(timeout: u64) -> Result> { 74 | let (tx, rx) = oneshot::channel(); 75 | let timer = Timer { timeout, tx }; 76 | TIMER_TX.get().send(timer)?; 77 | Ok(rx) 78 | } 79 | 80 | #[derive(Debug)] 81 | pub(crate) struct Interval { 82 | interval: u64, 83 | next: Option>, 84 | } 85 | 86 | impl Stream for Interval { 87 | type Item = Result; 88 | 89 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 90 | let mut next = match self.next.take() { 91 | Some(next) => next, 92 | None => return Poll::Ready(None), 93 | }; 94 | match Pin::new(&mut next).poll(cx) { 95 | Poll::Pending => { 96 | self.next = Some(next); 97 | Poll::Pending 98 | } 99 | Poll::Ready(timeout) => match oneshot(timeout + self.interval) { 100 | Ok(next) => { 101 | self.next = Some(next); 102 | Poll::Ready(Some(Ok(timeout))) 103 | } 104 | Err(err) => Poll::Ready(Some(Err(err))), 105 | }, 106 | } 107 | } 108 | } 109 | 110 | pub(crate) fn interval(start: u64, interval: u64) -> Result { 111 | let start = oneshot(start)?; 112 | Ok(Interval { 113 | interval, 114 | next: Some(start), 115 | }) 116 | } 117 | 118 | #[derive(Debug)] 119 | struct Timer { 120 | timeout: u64, 121 | tx: oneshot::Sender, 122 | } 123 | 124 | impl PartialEq for Timer { 125 | fn eq(&self, other: &Self) -> bool { 126 | self.timeout == other.timeout 127 | } 128 | } 129 | 130 | impl Eq for Timer {} 131 | 132 | impl PartialOrd for Timer { 133 | fn partial_cmp(&self, other: &Self) -> Option { 134 | Some(self.cmp(other)) 135 | } 136 | } 137 | 138 | impl Ord for Timer { 139 | fn cmp(&self, other: &Self) -> cmp::Ordering { 140 | self.timeout.cmp(&other.timeout).reverse() 141 | } 142 | } 143 | 144 | #[derive(Debug)] 145 | struct TimerManager { 146 | tick: u64, 147 | timers: BinaryHeap, 148 | } 149 | 150 | impl TimerManager { 151 | fn new() -> Self { 152 | Self { 153 | tick: 0, 154 | timers: BinaryHeap::new(), 155 | } 156 | } 157 | 158 | fn register(&mut self, timer: Timer) { 159 | self.timers.push(timer); 160 | self.fire_timers(); 161 | } 162 | 163 | fn tick(&mut self, count: u64) { 164 | self.tick += count; 165 | self.fire_timers(); 166 | } 167 | 168 | fn fire_timers(&mut self) { 169 | while let Some(timer) = self.timers.peek() { 170 | if timer.timeout > self.tick { 171 | break; 172 | } 173 | #[allow(clippy::unwrap_used)] 174 | let timer = self.timers.pop().unwrap(); 175 | timer.tx.send(timer.timeout); 176 | } 177 | } 178 | } 179 | 180 | static INTERRUPTED_COUNT: AtomicU64 = AtomicU64::new(0); 181 | static TOTAL_INTERRUPTED_COUNT: AtomicU64 = AtomicU64::new(0); 182 | static WAKER: AtomicWaker = AtomicWaker::new(); 183 | static TIMER_TX: OnceCell> = OnceCell::uninit(); 184 | 185 | #[derive(Debug)] 186 | struct InterruptStream { 187 | _private: (), 188 | } 189 | 190 | impl InterruptStream { 191 | fn new() -> Self { 192 | Self { _private: () } 193 | } 194 | } 195 | 196 | impl Stream for InterruptStream { 197 | type Item = u64; 198 | 199 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 200 | // fast path 201 | let count = INTERRUPTED_COUNT.swap(0, Ordering::Relaxed); 202 | if count > 0 { 203 | return Poll::Ready(Some(count)); 204 | } 205 | 206 | WAKER.register(cx.waker()); 207 | let count = INTERRUPTED_COUNT.swap(0, Ordering::Relaxed); 208 | if count > 0 { 209 | WAKER.take(); 210 | Poll::Ready(Some(count)) 211 | } else { 212 | Poll::Pending 213 | } 214 | } 215 | } 216 | 217 | pub(crate) extern "x86-interrupt" fn interrupt_handler(_stack_frame: InterruptStackFrame) { 218 | let guard = InterruptContextGuard::new(); 219 | INTERRUPTED_COUNT.fetch_add(1, Ordering::Relaxed); 220 | let current_count = TOTAL_INTERRUPTED_COUNT.fetch_add(1, Ordering::Relaxed); 221 | WAKER.wake(); 222 | interrupt::notify_end_of_interrupt(); 223 | 224 | if current_count % 2 == 0 { 225 | task::on_interrupt(guard); 226 | } 227 | } 228 | 229 | pub(crate) fn handler_task() -> impl Future { 230 | // Initialize TIMER_TX before co-task starts 231 | let (tx, mut rx) = mpsc::channel(100); 232 | TIMER_TX.init_once(|| tx); 233 | 234 | async move { 235 | let mut timer_manager = TimerManager::new(); 236 | let mut interrupts = InterruptStream::new(); 237 | loop { 238 | select_biased! { 239 | count = interrupts.next().fuse() => { 240 | #[allow(clippy::unwrap_used)] 241 | timer_manager.tick(count.unwrap()); 242 | }, 243 | timer = rx.next().fuse() => { 244 | #[allow(clippy::unwrap_used)] 245 | timer_manager.register(timer.unwrap()); 246 | } 247 | } 248 | } 249 | } 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/graphics/geometry.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt, iter, 3 | ops::{Add, AddAssign, BitAnd, BitOr, Mul, Neg, Range, Sub, SubAssign}, 4 | }; 5 | use num_traits::One; 6 | 7 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 8 | pub(crate) struct Vector2d { 9 | pub(crate) x: T, 10 | pub(crate) y: T, 11 | } 12 | 13 | impl Vector2d { 14 | pub(crate) const fn new(x: T, y: T) -> Self { 15 | Self { x, y } 16 | } 17 | } 18 | 19 | impl Vector2d 20 | where 21 | T: Copy + Ord, 22 | { 23 | pub(crate) fn elem_min(self, other: Self) -> Self { 24 | Self { 25 | x: T::min(self.x, other.x), 26 | y: T::min(self.y, other.y), 27 | } 28 | } 29 | 30 | pub(crate) fn elem_max(self, other: Self) -> Self { 31 | Self { 32 | x: T::max(self.x, other.x), 33 | y: T::max(self.y, other.y), 34 | } 35 | } 36 | } 37 | 38 | impl Vector2d 39 | where 40 | T: Copy + Ord + Add + Sub + One, 41 | { 42 | pub(crate) fn clamp(&self, rect: Rectangle) -> Option { 43 | Some(Self { 44 | x: T::clamp(self.x, rect.x_start(), rect.x_max()?), 45 | y: T::clamp(self.y, rect.y_start(), rect.y_max()?), 46 | }) 47 | } 48 | } 49 | 50 | impl fmt::Display for Vector2d 51 | where 52 | T: fmt::Display, 53 | { 54 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 55 | write!(f, "({}, {})", self.x, self.y) 56 | } 57 | } 58 | 59 | impl Neg for Vector2d 60 | where 61 | T: Neg, 62 | { 63 | type Output = Vector2d; 64 | 65 | fn neg(self) -> Self::Output { 66 | Vector2d { 67 | x: -self.x, 68 | y: -self.y, 69 | } 70 | } 71 | } 72 | 73 | impl Add> for Vector2d 74 | where 75 | T: Add, 76 | { 77 | type Output = Vector2d; 78 | 79 | fn add(self, rhs: Vector2d) -> Self::Output { 80 | Vector2d { 81 | x: self.x + rhs.x, 82 | y: self.y + rhs.y, 83 | } 84 | } 85 | } 86 | 87 | impl AddAssign> for Vector2d 88 | where 89 | T: AddAssign, 90 | { 91 | fn add_assign(&mut self, rhs: Vector2d) { 92 | self.x += rhs.x; 93 | self.y += rhs.y; 94 | } 95 | } 96 | 97 | impl Sub> for Vector2d 98 | where 99 | T: Sub, 100 | { 101 | type Output = Vector2d; 102 | 103 | fn sub(self, rhs: Vector2d) -> Self::Output { 104 | Vector2d { 105 | x: self.x - rhs.x, 106 | y: self.y - rhs.y, 107 | } 108 | } 109 | } 110 | 111 | impl SubAssign> for Vector2d 112 | where 113 | T: SubAssign, 114 | { 115 | fn sub_assign(&mut self, rhs: Vector2d) { 116 | self.x -= rhs.x; 117 | self.y -= rhs.y; 118 | } 119 | } 120 | 121 | impl Mul> for Vector2d 122 | where 123 | T: Mul, 124 | { 125 | type Output = Vector2d; 126 | 127 | fn mul(self, rhs: Vector2d) -> Self::Output { 128 | Vector2d { 129 | x: self.x * rhs.x, 130 | y: self.y * rhs.y, 131 | } 132 | } 133 | } 134 | 135 | pub(crate) type Point = Vector2d; 136 | pub(crate) type Size = Vector2d; 137 | pub(crate) type Offset = Vector2d; 138 | 139 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 140 | pub(crate) struct Rectangle { 141 | pub(crate) pos: Point, 142 | pub(crate) size: Size, 143 | } 144 | 145 | impl Rectangle { 146 | pub(crate) const fn new(pos: Point, size: Size) -> Self { 147 | Self { pos, size } 148 | } 149 | } 150 | 151 | impl Rectangle 152 | where 153 | T: Copy + Ord + Sub, 154 | { 155 | pub(crate) fn from_points(start: Point, end: Point) -> Option { 156 | let size = Size::new( 157 | (end.x > start.x).then(|| end.x - start.x)?, 158 | (end.y > start.y).then(|| end.y - start.y)?, 159 | ); 160 | Some(Rectangle { pos: start, size }) 161 | } 162 | } 163 | 164 | impl Rectangle 165 | where 166 | T: Copy + Add, 167 | { 168 | pub(crate) fn end_pos(&self) -> Point { 169 | self.pos + self.size 170 | } 171 | } 172 | 173 | impl fmt::Display for Rectangle 174 | where 175 | T: fmt::Display, 176 | { 177 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 178 | write!(f, "({}:{})", self.pos, self.size) 179 | } 180 | } 181 | 182 | impl BitAnd> for Rectangle 183 | where 184 | T: Copy + Ord + Add + Sub, 185 | { 186 | type Output = Option; 187 | 188 | fn bitand(self, rhs: Rectangle) -> Self::Output { 189 | let start = Point::::elem_max(self.pos, rhs.pos); 190 | let end = Point::::elem_min(self.end_pos(), rhs.end_pos()); 191 | Rectangle::from_points(start, end) 192 | } 193 | } 194 | 195 | impl BitOr> for Rectangle 196 | where 197 | T: Copy + Ord + Add + Sub, 198 | { 199 | type Output = Self; 200 | 201 | fn bitor(self, rhs: Rectangle) -> Self::Output { 202 | let start = Point::::elem_min(self.pos, rhs.pos); 203 | let end = Point::::elem_max(self.end_pos(), rhs.end_pos()); 204 | #[allow(clippy::unwrap_used)] 205 | Rectangle::from_points(start, end).unwrap() 206 | } 207 | } 208 | 209 | impl Add> for Rectangle 210 | where 211 | T: Add, 212 | { 213 | type Output = Self; 214 | 215 | fn add(self, rhs: Vector2d) -> Self::Output { 216 | Self { 217 | pos: self.pos + rhs, 218 | size: self.size, 219 | } 220 | } 221 | } 222 | 223 | impl AddAssign> for Rectangle 224 | where 225 | T: AddAssign, 226 | { 227 | fn add_assign(&mut self, rhs: Vector2d) { 228 | self.pos += rhs; 229 | } 230 | } 231 | 232 | impl Sub> for Rectangle 233 | where 234 | T: Sub, 235 | { 236 | type Output = Self; 237 | 238 | fn sub(self, rhs: Vector2d) -> Self::Output { 239 | Self { 240 | pos: self.pos - rhs, 241 | size: self.size, 242 | } 243 | } 244 | } 245 | 246 | impl SubAssign> for Rectangle 247 | where 248 | T: SubAssign, 249 | { 250 | fn sub_assign(&mut self, rhs: Vector2d) { 251 | self.pos -= rhs; 252 | } 253 | } 254 | 255 | impl Rectangle 256 | where 257 | T: Copy + Add, 258 | { 259 | pub(crate) fn x_start(&self) -> T { 260 | self.pos.x 261 | } 262 | 263 | pub(crate) fn y_start(&self) -> T { 264 | self.pos.y 265 | } 266 | 267 | pub(crate) fn x_end(&self) -> T { 268 | self.pos.x + self.size.x 269 | } 270 | 271 | pub(crate) fn y_end(&self) -> T { 272 | self.pos.y + self.size.y 273 | } 274 | 275 | pub(crate) fn x_range(&self) -> Range { 276 | self.x_start()..self.x_end() 277 | } 278 | 279 | pub(crate) fn y_range(&self) -> Range { 280 | self.y_start()..self.y_end() 281 | } 282 | } 283 | 284 | impl Rectangle 285 | where 286 | T: Copy + Ord + Add + Sub + One, 287 | { 288 | pub(crate) fn x_max(&self) -> Option { 289 | (self.x_end() > T::one()).then(|| self.x_end() - T::one()) 290 | } 291 | 292 | pub(crate) fn y_max(&self) -> Option { 293 | (self.y_end() > T::one()).then(|| self.y_end() - T::one()) 294 | } 295 | } 296 | 297 | impl Rectangle 298 | where 299 | T: Copy + Add + PartialOrd, 300 | { 301 | pub(crate) fn contains(&self, p: &Point) -> bool { 302 | self.x_range().contains(&p.x) && self.y_range().contains(&p.y) 303 | } 304 | } 305 | 306 | impl Rectangle 307 | where 308 | T: Copy + Add + Sub + Ord + One, 309 | { 310 | pub(crate) fn extend_to_contain(&self, p: Point) -> Rectangle { 311 | let x_start = T::min(p.x, self.x_start()); 312 | let y_start = T::min(p.y, self.y_start()); 313 | let x_end = T::max(p.x + T::one(), self.x_end()); 314 | let y_end = T::max(p.y + T::one(), self.y_end()); 315 | Rectangle { 316 | pos: Point::new(x_start, y_start), 317 | size: Size::new(x_end - x_start, y_end - y_start), 318 | } 319 | } 320 | } 321 | 322 | impl Rectangle 323 | where 324 | T: Copy + Add, 325 | Range: Iterator, 326 | { 327 | pub(crate) fn points(self) -> impl Iterator> { 328 | self.x_range() 329 | .flat_map(move |x| iter::repeat(x).zip(self.y_range())) 330 | .map(|(x, y)| Point::new(x, y)) 331 | } 332 | } 333 | --------------------------------------------------------------------------------