From a4cc9cdf4f950e90bb1529d44cd8f958b9282839 Mon Sep 17 00:00:00 2001 From: able Date: Sun, 31 Jul 2022 01:54:01 -0500 Subject: [PATCH] adding a deadlock to master :> --- Cargo.lock | 60 +--- __pycache__/qmp.cpython-310.pyc | Bin 0 -> 8558 bytes ableos/Cargo.toml | 14 +- ableos/src/arch/x86_64/drivers/mod.rs | 2 +- ableos/src/arch/x86_64/init.rs | 14 +- ableos/src/arch/x86_64/interrupts.rs | 42 ++- ableos/src/experiments/y_compositor/window.rs | 12 +- ableos/src/hardware/mod.rs | 130 ++++++++ ableos/src/kmain.rs | 51 ++- ableos/src/lib.rs | 4 + ableos/src/logger.rs | 37 ++- ableos/src/prelude/rust_2021.rs | 1 + ableos/src/print.rs | 11 +- ableos/src/ps2_mouse.rs | 300 ++++++++++++++++++ ableos/src/scratchpad.rs | 48 +-- ableos/src/time.rs | 2 +- ableos/src/vgai.rs | 64 ++++ ableos/src/vterm.rs | 100 ++++++ ableos/src/wasm_jumploader/host_functions.rs | 2 +- kernel/src/lib.rs | 5 +- qmp.py | 243 ++++++++++++++ qprofiler.py | 118 +++++++ repbuild/src/main.rs | 38 ++- 23 files changed, 1122 insertions(+), 176 deletions(-) create mode 100644 __pycache__/qmp.cpython-310.pyc create mode 100644 ableos/src/hardware/mod.rs create mode 100644 ableos/src/ps2_mouse.rs create mode 100644 ableos/src/vgai.rs create mode 100644 ableos/src/vterm.rs create mode 100644 qmp.py create mode 100644 qprofiler.py diff --git a/Cargo.lock b/Cargo.lock index b55e00b..f418e30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,9 +29,9 @@ dependencies = [ "ab_glyph", "acpi", "axel", + "bitflags", "bootloader", "cpuio", - "embedded-graphics", "ext2", "externc-libm", "facepalm", @@ -134,7 +134,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axel" version = "0.1.0" -source = "git+https://git.ablecorp.us/able/aos_userland#a9721a7b034ec24c3bc2b6ffe6fa989b47b144ef" +source = "git+https://git.ablecorp.us/able/aos_userland#7f61266a9b6a5d0032ff84b3ee971eb7b14bd1fa" dependencies = [ "hashbrown 0.7.2", "log", @@ -142,12 +142,6 @@ dependencies = [ "versioning", ] -[[package]] -name = "az" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f771a5d1f5503f7f4279a30f3643d3421ba149848b89ecaaec0ea2acf04a5ac4" - [[package]] name = "bare-metal" version = "1.0.0" @@ -178,12 +172,6 @@ version = "0.9.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de78decc37247c7cfac5dbf3495c7298c6ac97cb355161caa7e15969c6648e6c" -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - [[package]] name = "cfg-if" version = "1.0.0" @@ -283,29 +271,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" -[[package]] -name = "embedded-graphics" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "750082c65094fbcc4baf9ba31583ce9a8bb7f52cadfb96f6164b1bc7f922f32b" -dependencies = [ - "az", - "byteorder", - "embedded-graphics-core", - "float-cmp", - "micromath", -] - -[[package]] -name = "embedded-graphics-core" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b1239db5f3eeb7e33e35bd10bd014e7b2537b17e071f726a09351431337cfa" -dependencies = [ - "az", - "byteorder", -] - [[package]] name = "ext2" version = "0.1.1" @@ -331,15 +296,6 @@ dependencies = [ "log", ] -[[package]] -name = "float-cmp" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" -dependencies = [ - "num-traits", -] - [[package]] name = "fnv" version = "1.0.7" @@ -490,9 +446,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] @@ -533,12 +489,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -[[package]] -name = "micromath" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc4010833aea396656c2f91ee704d51a6f1329ec2ab56ffd00bfd56f7481ea94" - [[package]] name = "no-std-compat" version = "0.4.1" @@ -1029,7 +979,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "versioning" version = "0.1.2" -source = "git+https://git.ablecorp.us/able/aos_userland#a9721a7b034ec24c3bc2b6ffe6fa989b47b144ef" +source = "git+https://git.ablecorp.us/able/aos_userland#7f61266a9b6a5d0032ff84b3ee971eb7b14bd1fa" dependencies = [ "serde", ] diff --git a/__pycache__/qmp.cpython-310.pyc b/__pycache__/qmp.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..000975ed0142eb0b5a0cdbf24e04a686f1be2bd4 GIT binary patch literal 8558 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 HEn5Et#P429 literal 0 HcmV?d00001 diff --git a/ableos/Cargo.toml b/ableos/Cargo.toml index f0d5c60..aafb66b 100644 --- a/ableos/Cargo.toml +++ b/ableos/Cargo.toml @@ -46,14 +46,13 @@ test-args = [ ] [dependencies] -lazy_static = { version = "*", features = ["spin_no_std"] } +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } qrcode = { path = "../qrcode-rust" } - - +bitflags = "1.2.1" linked_list_allocator = "0.9.0" lliw = "0.2.0" spin = "0.9" -log = "*" +log = "0.4.17" pretty-hex = "0.2.1" unicode-width = "0.1.7" picorand = "0.1.0" @@ -63,8 +62,9 @@ rhai = "1.6.0" libwasm = { git = "https://git.ablecorp.us:443/able/libwasm.git" } axel = { git = "https://git.ablecorp.us/able/aos_userland" } versioning = { git = "https://git.ablecorp.us/able/aos_userland" } -embedded-graphics = "*" +# embedded-graphics = "*" pc-keyboard = "0.5" + [dependencies.logos] version = "0.12" default-features = false @@ -138,7 +138,7 @@ cpuio = { git = "https://git.ablecorp.us/ondra05/cpuio.git" } pic8259 = "0.10.1" uart_16550 = "0.2.0" volatile = "0.2.6" -x86_64 = "*" +x86_64 = "0.14.8" pc-beeper = { git = "https://github.com/AbleOS/pc-beeper" } -vga = "*" +vga = "0.2.7" acpi = "4.1.0" diff --git a/ableos/src/arch/x86_64/drivers/mod.rs b/ableos/src/arch/x86_64/drivers/mod.rs index 89f292e..a16b28d 100644 --- a/ableos/src/arch/x86_64/drivers/mod.rs +++ b/ableos/src/arch/x86_64/drivers/mod.rs @@ -1,5 +1,5 @@ pub mod allocator; -pub mod graphics; +// pub mod graphics; pub mod serial; pub mod sysinfo; pub mod timer; diff --git a/ableos/src/arch/x86_64/init.rs b/ableos/src/arch/x86_64/init.rs index d7275d7..f8df89e 100644 --- a/ableos/src/arch/x86_64/init.rs +++ b/ableos/src/arch/x86_64/init.rs @@ -1,22 +1,24 @@ // #![allow(clippy::print_literal)] -use super::{gdt, interrupts}; +use super::{drivers::serial, gdt, interrupts}; use crate::{logger, serial_println}; /// x86_64 initialization pub fn init() { - use crate::{network::socket::SimpleSock, relib::network::socket::Socket}; + // use crate::{network::socket::SimpleSock, relib::network::socket::Socket}; - let mut log_socket_id = SimpleSock::new(); - log_socket_id.register_protocol("Logger".to_string()); + // let mut log_socket_id = SimpleSock::new(); + // log_socket_id.register_protocol("Logger".to_string()); let result = logger::init(); match result { Ok(_) => { - serial_println!("Logger initialized"); + info!("Logger initialized"); } - Err(err) => error!("{}", err), + Err(err) => serial_println!("{}", err), } + /* + */ gdt::init(); interrupts::init_idt(); diff --git a/ableos/src/arch/x86_64/interrupts.rs b/ableos/src/arch/x86_64/interrupts.rs index 30eb4dc..c6c9887 100644 --- a/ableos/src/arch/x86_64/interrupts.rs +++ b/ableos/src/arch/x86_64/interrupts.rs @@ -18,6 +18,8 @@ use spin::Lazy; use vga::colors::Color16; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; +use super::sloop; + pub const PIC_1_OFFSET: u8 = 32; pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; @@ -30,6 +32,9 @@ pub static PICS: spin::Mutex = pub enum InterruptIndex { Timer = PIC_1_OFFSET, Keyboard, + /// Mouse offset + Mouse = 44, + // SecondInterrupt = PIC_2_OFFSET, Cmos = 0x70, } @@ -56,11 +61,11 @@ static IDT: Lazy = Lazy::new(|| { idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler); idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard_interrupt_handler); - - idt[6].set_handler_fn(floppy_disk_interrupt_handler); + idt[InterruptIndex::Mouse.as_usize()].set_handler_fn(crate::hardware::mouse_interrupt_handler); // run `a + b + l + e + o + s print;` in ablescript and its 54 thats why this seemingly arbitrary number was chosen idt[54].set_handler_fn(software_int_handler); + idt }); @@ -78,7 +83,7 @@ extern "x86-interrupt" fn double_fault_handler( _error_code: u64, ) -> ! { bsod(BSODSource::DoubleFault(&stack_frame)); - // panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); + panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); } #[naked] @@ -87,6 +92,7 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFr unsafe { // print!("."); asm!( + // Kernel tick "call {tick}", @@ -99,7 +105,7 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFr // Switch to next task (interrupt'll be returned there) "jmp {switch_to_next}", - tick = sym kernel::tick, + tick = sym crate::kmain::tick, save = sym task_switcher::save_and_enqueue, switch_to_next = sym task_switcher::switch_to_next, options(noreturn), @@ -122,14 +128,16 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStac .add_byte(unsafe { Port::new(0x60).read() }) .map(|x| x.and_then(|ev| keyboard.process_keyevent(ev))) { + trace!("{key:?}"); match key { DecodedKey::Unicode(chr) => match chr { // Backspace '\u{8}' => { + // TODO: Fix this and apply to new term WRITER.lock().backspace(); KEYBUFF.lock().push(8.into()); } - '^' => KERNEL_STATE.lock().shutdown(), + // '^' => KERNEL_STATE.lock().shutdown(), chr => { KEYBUFF.lock().push(chr); print!("{chr}"); @@ -155,10 +163,6 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStac } } -extern "x86-interrupt" fn floppy_disk_interrupt_handler(_stack_frame: InterruptStackFrame) { - println!("EXCEPTION: FLOPPY DISK"); -} - pub fn init_idt() { IDT.load(); } @@ -200,6 +204,8 @@ pub fn reset_pit_for_cpu() { set_pit_3(1000); } pub fn bsod(src: BSODSource) -> ! { + trace!("{src:?}"); + let mut mode = SCREEN_BUFFER.lock(); mode.force_redraw(); /* @@ -214,17 +220,20 @@ pub fn bsod(src: BSODSource) -> ! { let mut y = 0; let src1 = match src { - BSODSource::DoubleFault(_) => "DoubleFault", - BSODSource::Panic(_) => "Panic", + BSODSource::DoubleFault(_) => "DoubleFault".to_string(), + BSODSource::Panic(panic_info) => { + let strr = format!("PANIC: {}", panic_info); + strr + } }; let st = format!( - "We fucked up ඞ : {}\nThe following qr code will link you to the\nwiki which hopefully solves your problems", + "We fucked up ඞ : \n{}\nThe following qr code will link you to the wiki which hopefully solves your problems", src1 ); for current in st.chars() { - if current == '\n' { + if current == '\n' || x == 40 { y += 1; x = 1; } else { @@ -239,16 +248,15 @@ pub fn bsod(src: BSODSource) -> ! { } let mut x = 1; - let mut y = 11; + let mut y = 34; // let sf = format!("https://git.ablecorp.us/able/ableos/wiki/Double-Faults"); let sd = match src { BSODSource::DoubleFault(_) => "https://git.ablecorp.us/able/ableos/wiki/Double-Faults", BSODSource::Panic(_) => { - // trace!("panic"); - "https://git.ablecorp.us/able/ableos/wiki/" + "https://git.ablecorp.us/able/ableos/wiki/Panic" } }; @@ -274,7 +282,7 @@ pub fn bsod(src: BSODSource) -> ! { mode.copy_to_buffer(); - loop {} + sloop(); } #[derive(Debug)] diff --git a/ableos/src/experiments/y_compositor/window.rs b/ableos/src/experiments/y_compositor/window.rs index 82301e3..1c8d56f 100644 --- a/ableos/src/experiments/y_compositor/window.rs +++ b/ableos/src/experiments/y_compositor/window.rs @@ -1,4 +1,4 @@ -use crate::driver_traits::graphics::Point; +// use crate::driver_traits::graphics::Point; pub type MenuBar = Vec; @@ -8,16 +8,16 @@ pub struct MenuOption { pub struct Window { title: String, - position: Point, + // position: Point, fullscreen: bool, } // all of these should return a result impl Window { - pub fn new(title: String, position: Point, fullscreen: bool) -> Self { + pub fn new(title: String, /*position: Point,*/ fullscreen: bool) -> Self { Self { title, - position, + // position, fullscreen, } } @@ -31,7 +31,7 @@ impl Window { pub fn set_title(&mut self) { todo!(); } - pub fn set_position(&mut self, pos: Point) { - self.position = pos; + pub fn set_position(&mut self /*pos: Point*/) { + // self.position = pos; } } diff --git a/ableos/src/hardware/mod.rs b/ableos/src/hardware/mod.rs new file mode 100644 index 0000000..9ef238d --- /dev/null +++ b/ableos/src/hardware/mod.rs @@ -0,0 +1,130 @@ +use crate::arch::interrupts::InterruptIndex; +use crate::arch::interrupts::PICS; +use crate::ps2_mouse::{Mouse, MouseState}; +use crate::vga_e::VGAE; +use lazy_static::lazy_static; +use log::info; +use spin::Mutex; +use vga::colors::Color16; +use vga::writers::GraphicsWriter; +use x86_64::instructions::port::PortReadOnly; +use x86_64::structures::idt::InterruptStackFrame; + +const MOUSE_MAX_X: u16 = 638; +const MOUSE_MAX_Y: u16 = 478; + +lazy_static! { + pub static ref _MOUSE: Mutex = Mutex::new(Mouse::new()); + pub static ref MOUSE: Mutex = Mutex::new(OnScreenMouse::default()); +} + +#[derive(Default)] +pub struct OnScreenMouse { + // absolute position on screen + x: u16, + // absolute position on screen + y: u16, +} + +impl OnScreenMouse { + pub fn get_x(&self) -> u16 { + return self.x; + } + + pub fn get_y(&self) -> u16 { + return self.y; + } + + pub fn change_x(&mut self, delta_x: i16) { + if delta_x.is_negative() { + self.x = self.x.saturating_sub((-delta_x) as u16) + } else { + self.x = self.x.saturating_add(delta_x as u16) + } + } + + pub fn change_y(&mut self, delta_y: i16) { + if delta_y.is_negative() { + self.y = self.y.saturating_add((-delta_y) as u16) + } else { + self.y = self.y.saturating_sub(delta_y as u16) + } + } + pub fn set_x(&mut self, x: u16) { + if x >= MOUSE_MAX_X { + self.x = MOUSE_MAX_X - 1; + } else { + self.x = x; + } + } + pub fn set_y(&mut self, y: u16) { + if y >= MOUSE_MAX_Y { + self.y = MOUSE_MAX_Y - 1; + } else { + self.y = y; + } + } +} + +// Initialize the mouse and set the on complete event. +pub fn init_mouse() { + let mut mouse = _MOUSE.lock(); + // debug!("Trying to initialize mouse"); + mouse.init().unwrap(); + mouse.set_on_complete(on_complete); + // info!("Mouse initialized"); +} + +// This will be fired when a packet is finished being processed. +fn on_complete(mouse_state: MouseState) { + // how much the cursor has moved + let delta_x = mouse_state.get_x(); + let delta_y = mouse_state.get_y(); + /* + trace!("MOUSE CHANGE: X{{{}}} Y{{{}}}", delta_x, delta_y); + trace!( + "MOUSE BUTTONS: LEFT{{{}}} RIGHT{{{}}}", + mouse_state._left_button_down(), + mouse_state._right_button_down() + ); + */ + + let mut mouse = MOUSE.lock(); + if mouse.get_x() >= MOUSE_MAX_X { + mouse.set_x(MOUSE_MAX_X - 1); + } else { + mouse.change_x(delta_x); + } + + if mouse.get_y() >= MOUSE_MAX_Y { + mouse.set_y(MOUSE_MAX_Y - 1); + } else { + mouse.change_y(delta_y); + } + // only move the cursor when delta_x is in some range + // i.e. if the cursor moves too fast, ignore it. + // if the mouse moves too fast the delta will overflow + mouse.change_y(delta_y); + + x86_64::instructions::interrupts::without_interrupts(|| { + use crate::TERM; + let mut term = TERM.lock(); + term.set_dirty(true); + }); + + // draw_mouse((mouse.get_x() as usize, mouse.get_y() as usize)); +} + +// An example interrupt based on https://os.phil-opp.com/hardware-interrupts/. +// The ps2 mouse is configured to fire +// interrupts at PIC offset 12. +pub extern "x86-interrupt" fn mouse_interrupt_handler(_stack_frame: InterruptStackFrame) { + let mut port = PortReadOnly::new(0x60); + let packet = unsafe { port.read() }; + _MOUSE.lock().process_packet(packet); + + unsafe { + PICS.lock() + .notify_end_of_interrupt(InterruptIndex::Mouse as u8); + } +} diff --git a/ableos/src/kmain.rs b/ableos/src/kmain.rs index 88d5ae9..fb78a46 100644 --- a/ableos/src/kmain.rs +++ b/ableos/src/kmain.rs @@ -1,6 +1,6 @@ #![allow(clippy::empty_loop)] -use core::sync::atomic::Ordering; +use core::sync::atomic::{AtomicU64, Ordering}; use crate::arch::drivers::sysinfo::master; use crate::ipc::channel::ChannelMessage; @@ -13,9 +13,9 @@ use crate::{ scratchpad, }; use crate::{boot_conf::KernelConfig, systeminfo::RELEASE_TYPE}; -use crate::{wasm_jumploader, SectionType}; +use crate::{hardware, wasm_jumploader, SectionType, TERM}; use genfs::{Fs, OpenOptions}; -use kernel::{KERNEL_VERSION, TICK}; +use kernel::KERNEL_VERSION; use libwasm::syscalls::time_calls::get_time; use qrcode::render::unicode; use qrcode::QrCode; @@ -35,29 +35,43 @@ pub fn kernel_main() -> ! { } else { log::set_max_level(log::LevelFilter::Off); } + let mut term = TERM.lock(); + term.initialize(); + term.set_dirty(true); + term.draw_term(); + drop(term); + /* + x86_64::instructions::interrupts::without_interrupts(|| { + hardware::init_mouse(); + }); + + // println!("abc"); let mut ipc_service = IPC.lock(); // create some channels or whatever here then drop it let log_handle = ipc_service.create_channel("LOG", true); let log_sock_handle = ipc_service.create_socket("LOG", true); for handle in ipc_service.examine_board() { - println!("Discovered: {}", handle); + // println!("Discovered: {}", handle); } drop(ipc_service); - use x86_ata::{init, list, read, ATA_BLOCK_SIZE}; + + */ + + // use x86_ata::{init, ATA_BLOCK_SIZE}; // 1. Initialise ATA Subsystem. (Perform Once, on boot) - init().expect("Failed To Start ATA..."); - let mut buffer: [u8; ATA_BLOCK_SIZE] = [0; ATA_BLOCK_SIZE]; + // init().expect("Failed To Start ATA..."); + // let mut buffer: [u8; ATA_BLOCK_SIZE] = [0; ATA_BLOCK_SIZE]; // FIXME: Calls to read panic the kernel // read(0, 0, 0, &mut buffer); // for abc in list() { // trace!("{:?}", abc); // } - + log_version_data(); x86_64::instructions::interrupts::without_interrupts(|| { let mut scheduler = SCHEDULER.lock(); // comment this out to resume normal use @@ -116,9 +130,28 @@ pub fn cpu_socket_startup() { } pub fn log_version_data() { - info!("{} v{:?}", RELEASE_TYPE, KERNEL_VERSION); + info!("{} v{}", RELEASE_TYPE, KERNEL_VERSION); info!( "Brand String: {}", master().unwrap().brand_string().unwrap() ); } + +pub static TICK: AtomicU64 = AtomicU64::new(0); + +pub fn tick() { + x86_64::instructions::interrupts::without_interrupts(|| { + trace!("ticking time"); + + let mut term = TERM.lock(); + + term.draw_term(); + use core::sync::atomic::Ordering::Relaxed; + + let mut data = TICK.load(Relaxed); + data = data.wrapping_add(1); + + TICK.store(data, Relaxed); + trace!("time ticked"); + }); +} diff --git a/ableos/src/lib.rs b/ableos/src/lib.rs index 67aa8a3..99600f0 100644 --- a/ableos/src/lib.rs +++ b/ableos/src/lib.rs @@ -53,6 +53,7 @@ pub mod keyboard; pub mod kmain; pub mod logger; pub mod prelude; +pub mod ps2_mouse; pub mod relib; pub mod rhai_shell; pub mod scheduler; @@ -69,10 +70,13 @@ pub mod allocator; // pub use allocator as aalloc; pub mod channels; pub mod handle; +pub mod hardware; pub mod ipc; pub mod panic; mod unicode_utils; pub mod vga_e; +pub mod vgai; +pub mod vterm; #[prelude_import] pub use prelude::rust_2021::*; diff --git a/ableos/src/logger.rs b/ableos/src/logger.rs index b85301e..cea2ce4 100644 --- a/ableos/src/logger.rs +++ b/ableos/src/logger.rs @@ -1,6 +1,7 @@ use crate::kmain::KERNEL_CONF; use crate::network::socket::{SimpleSock, Socket}; use crate::time::fetch_time; +use alloc::borrow::ToOwned; use lliw::{Fg, Reset}; use log::{Level, Metadata, Record}; use log::{LevelFilter, SetLoggerError}; @@ -17,6 +18,8 @@ impl log::Log for SimpleLogger { fn log(&self, record: &Record) { if self.enabled(record.metadata()) { let time_float = fetch_time(); + use log::Level::*; + use Fg::*; let color = match record.level() { log::Level::Error => (Fg::Red, "$RED$"), @@ -26,6 +29,7 @@ impl log::Log for SimpleLogger { log::Level::Trace => (Fg::Yellow, "$YELLOW$"), }; + /* let msg = format!( "[{}{}$RESET$][$GREEN${}$RESET$]{}\n", color.1, @@ -33,29 +37,33 @@ impl log::Log for SimpleLogger { time_float, record.args() ); - // kprint!("{}", msg); + */ + let mod_path = match record.module_path() { + Some(p) => p, + None => "unknown", + }; + + let line = match record.line() { + Some(line_number) => line_number.to_string(), + None => "??".to_string(), + }; + if KERNEL_CONF.logging.log_to_serial { serial_println!( - "[{}{}{}][{}{}{}] {}", + "[{}{}{}][{}{}{}][{}{}@{}{}] {}", color.0, record.level(), Fg::Reset, Fg::Green, time_float, Reset, + Fg::Blue, + mod_path, + line, + Reset, record.args() ); } - /* - TODO: Get a proper socket from IPC instead of this deprecated method - let log_socket_id = SimpleSock::grab_socket("Logger".to_string()); - match log_socket_id { - Some(mut log_socket_id) => { - log_socket_id.write(msg.as_bytes().to_vec()); - } - None => warn!("No socket found for Logger"), - } - */ } } /// Clear the log buffer @@ -64,4 +72,9 @@ impl log::Log for SimpleLogger { pub fn init() -> Result<(), SetLoggerError> { log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Trace)) + // ?; + + // trace!("Logger started"); + + // Ok(()) } diff --git a/ableos/src/prelude/rust_2021.rs b/ableos/src/prelude/rust_2021.rs index 114d92e..2e95f7d 100644 --- a/ableos/src/prelude/rust_2021.rs +++ b/ableos/src/prelude/rust_2021.rs @@ -5,4 +5,5 @@ pub use core::arch::asm; pub use core::prelude::rust_2021::*; pub use core::prelude::v1::*; +pub use core::result::Result::*; pub use log::{debug, info, trace, warn}; diff --git a/ableos/src/print.rs b/ableos/src/print.rs index b2fd0b0..5dba907 100644 --- a/ableos/src/print.rs +++ b/ableos/src/print.rs @@ -21,8 +21,15 @@ impl core::fmt::Write for Stdout { } #[cfg(target_arch = "x86_64")] fn write_str(&mut self, s: &str) -> Result<(), Error> { - use crate::kprint; - kprint!("{}", s); + use crate::TERM; + + trace!("printing"); + x86_64::instructions::interrupts::without_interrupts(|| { + let mut term = TERM.lock(); + term.set_dirty(true); + term.print(s.to_string()); + drop(term); + }); Ok(()) } #[cfg(target_arch = "riscv64")] diff --git a/ableos/src/ps2_mouse.rs b/ableos/src/ps2_mouse.rs new file mode 100644 index 0000000..aa61930 --- /dev/null +++ b/ableos/src/ps2_mouse.rs @@ -0,0 +1,300 @@ +use bitflags::bitflags; +use x86_64::instructions::port::Port; + +const ADDRESS_PORT_ADDRESS: u16 = 0x64; +const DATA_PORT_ADDRESS: u16 = 0x60; +const GET_STATUS_BYTE: u8 = 0x20; +const SET_STATUS_BYTE: u8 = 0x60; + +const DISABLE_FIRST: u8 = 0xAD; +const DISABLE_SECOND: u8 = 0xA7; +const ENABLE_FIRST: u8 = 0xAE; +const ENABLE_SECOND: u8 = 0xA8; + +bitflags! { + /// Represents the flags currently set for the mouse. + #[derive(Default)] + pub struct MouseFlags: u8 { + /// Whether or not the left mouse button is pressed. + const LEFT_BUTTON = 0b0000_0001; + + /// Whether or not the right mouse button is pressed. + const RIGHT_BUTTON = 0b0000_0010; + + /// Whether or not the middle mouse button is pressed. + const MIDDLE_BUTTON = 0b0000_0100; + + /// Whether or not the packet is valid or not. + const ALWAYS_ONE = 0b0000_1000; + + /// Whether or not the x delta is negative. + const X_SIGN = 0b0001_0000; + + /// Whether or not the y delta is negative. + const Y_SIGN = 0b0010_0000; + + /// Whether or not the x delta overflowed. + const X_OVERFLOW = 0b0100_0000; + + /// Whether or not the y delta overflowed. + const Y_OVERFLOW = 0b1000_0000; + } +} + +#[repr(u8)] +enum Command { + EnablePacketStreaming = 0xF4, + SetDefaults = 0xF6, +} + +/// A basic interface to interact with a PS2 mouse. +#[derive(Debug)] +pub struct Mouse { + command_port: Port, + data_port: Port, + current_packet: u8, + current_state: MouseState, + completed_state: MouseState, + on_complete: Option, +} + +impl Default for Mouse { + fn default() -> Mouse { + Mouse::new() + } +} + +/// A snapshot of the mouse flags, x delta and y delta. +#[derive(Debug, Copy, Clone, Default)] +pub struct MouseState { + flags: MouseFlags, + x: i16, + y: i16, +} + +impl MouseState { + /// Returns a new `MouseState`. + pub const fn new() -> MouseState { + MouseState { + flags: MouseFlags::empty(), + x: 0, + y: 0, + } + } + + /// Returns true if the left mouse button is currently down. + pub fn _left_button_down(&self) -> bool { + self.flags.contains(MouseFlags::LEFT_BUTTON) + } + + /// Returns true if the left mouse button is currently up. + pub fn _left_button_up(&self) -> bool { + !self.flags.contains(MouseFlags::LEFT_BUTTON) + } + + /// Returns true if the right mouse button is currently down. + pub fn _right_button_down(&self) -> bool { + self.flags.contains(MouseFlags::RIGHT_BUTTON) + } + + /// Returns true if the right mouse button is currently up. + pub fn _right_button_up(&self) -> bool { + !self.flags.contains(MouseFlags::RIGHT_BUTTON) + } + + /// Returns true if the x axis has moved. + pub fn _x_moved(&self) -> bool { + self.x != 0 + } + + /// Returns true if the y axis has moved. + pub fn _y_moved(&self) -> bool { + self.y != 0 + } + + /// Returns true if the x or y axis has moved. + pub fn _moved(&self) -> bool { + self._x_moved() || self._y_moved() + } + + /// Returns the x delta of the mouse state. + pub fn get_x(&self) -> i16 { + self.x + } + + /// Returns the y delta of the mouse state. + pub fn get_y(&self) -> i16 { + self.y + } +} + +impl Mouse { + /// Creates a new `Mouse`. + pub const fn new() -> Mouse { + Mouse { + command_port: Port::new(ADDRESS_PORT_ADDRESS), + data_port: Port::new(DATA_PORT_ADDRESS), + current_packet: 0, + current_state: MouseState::new(), + completed_state: MouseState::new(), + on_complete: None, + } + } + + /// Returns the last completed state of the mouse. + pub fn _get_state(&self) -> MouseState { + self.completed_state + } + + // super helpful resource, albeit in C + // https://github.com/29jm/SnowflakeOS/blob/master/kernel/src/devices/ps2.c#L18 + /// Attempts to initialize a `Mouse`. If successful, interrupts will be generated + /// as `PIC offset + 12`. + pub fn init(&mut self) -> Result<(), &'static str> { + // Disable both PS/2 device ports + // Even if only one is present, disabling the second is harmless + self.write_command_port(DISABLE_FIRST)?; + self.write_command_port(DISABLE_SECOND)?; + + // Flush output buffer: if the controller had anything to say, ignore it + unsafe { + self.data_port.read(); + } + + debug!("mouse driver: writing GET_STATUS to port..."); + self.write_command_port(GET_STATUS_BYTE)?; + debug!("mouse driver: reading status from port..."); + let status = self.read_data_port()? | 0x02; + + debug!("Got status {}", status); + + // self.write_command_port(0xa8)?; + + self.write_command_port(SET_STATUS_BYTE)?; + + self.write_data_port(status & 0xDF)?; + + self.send_command(Command::SetDefaults)?; + self.send_command(Command::EnablePacketStreaming)?; + + self.write_command_port(ENABLE_FIRST)?; + self.write_command_port(ENABLE_SECOND)?; + + // Some keyboards actually send a reply, flush it + unsafe { + self.data_port.read(); + } + + Ok(()) + } + + /// Attempts to process a packet. + pub fn process_packet(&mut self, packet: u8) { + match self.current_packet { + 0 => { + let flags = MouseFlags::from_bits_truncate(packet); + if !flags.contains(MouseFlags::ALWAYS_ONE) { + return; + } + self.current_state.flags = flags; + } + 1 => self.process_x_movement(packet), + 2 => { + self.process_y_movement(packet); + self.completed_state = self.current_state; + if let Some(on_complete) = self.on_complete { + on_complete(self.completed_state); + } + } + _ => unreachable!(), + } + self.current_packet = (self.current_packet + 1) % 3; + } + + /// Sets the `on_complete` function to be called when a packet is completed. + pub fn set_on_complete(&mut self, handler: fn(MouseState)) { + self.on_complete = Some(handler); + } + + fn process_x_movement(&mut self, packet: u8) { + if !self.current_state.flags.contains(MouseFlags::X_OVERFLOW) { + self.current_state.x = if self.current_state.flags.contains(MouseFlags::X_SIGN) { + self.sign_extend(packet) + } else { + packet as i16 + }; + } + } + + fn process_y_movement(&mut self, packet: u8) { + if !self.current_state.flags.contains(MouseFlags::Y_OVERFLOW) { + self.current_state.y = if self.current_state.flags.contains(MouseFlags::Y_SIGN) { + self.sign_extend(packet) + } else { + packet as i16 + }; + } + } + + fn read_data_port(&mut self) -> Result { + // INFO: What the fuck + debug!("owo"); + self.wait_for_read()?; + // HERESY: Stop + debug!("what's this"); + Ok(unsafe { self.data_port.read() }) + } + + fn send_command(&mut self, command: Command) -> Result<(), &'static str> { + self.write_command_port(0xD4)?; + self.write_data_port(command as u8)?; + if self.read_data_port()? != 0xFA { + return Err("mouse did not respond to the command"); + } + Ok(()) + } + + fn sign_extend(&self, packet: u8) -> i16 { + ((packet as u16) | 0xFF00) as i16 + } + + fn write_command_port(&mut self, value: u8) -> Result<(), &'static str> { + debug!("mouse driver: waiting for write"); + self.wait_for_write()?; + unsafe { + self.command_port.write(value); + } + debug!("mouse driver: command written"); + Ok(()) + } + + fn write_data_port(&mut self, value: u8) -> Result<(), &'static str> { + self.wait_for_write()?; + unsafe { + self.data_port.write(value); + } + Ok(()) + } + + fn wait_for_read(&mut self) -> Result<(), &'static str> { + let timeout = 100_000; + for _x in 0..timeout { + let value = unsafe { self.command_port.read() }; + if (value & 0x1) == 0x1 { + return Ok(()); + } + } + Err("wait for mouse read timeout") + } + + fn wait_for_write(&mut self) -> Result<(), &'static str> { + let timeout = 100_000; + for _ in 0..timeout { + let value = unsafe { self.command_port.read() }; + if (value & 0x2) == 0x0 { + return Ok(()); + } + } + Err("wait for mouse write timeout") + } +} diff --git a/ableos/src/scratchpad.rs b/ableos/src/scratchpad.rs index 4324d60..cf65c57 100644 --- a/ableos/src/scratchpad.rs +++ b/ableos/src/scratchpad.rs @@ -10,8 +10,9 @@ use crate::handle::Handle; use crate::ipc::IPC; use crate::rhai_shell::shell; use crate::rhai_shell::KEYBUFF; +use crate::vterm::Term; use crate::wasm_jumploader::run_program; -use crate::SCREEN_BUFFER; +use crate::{vgai, SCREEN_BUFFER}; use acpi::{AcpiTables, PlatformInfo}; use alloc::collections::{vec_deque, VecDeque}; use cpuio::inb; @@ -24,6 +25,7 @@ use genfs::OpenOptions; use genfs::{DirEntry, Fs}; use kernel::proccess::PID; use kernel::software_int; +use spin::Lazy; use vga::writers::GraphicsWriter; // TODO: move to a better place @@ -43,6 +45,8 @@ impl acpi::AcpiHandler for AcpiStruct { todo!("unmap_physical_region"); } } + +pub static TERM: Lazy> = Lazy::new(|| spin::Mutex::new(Term::new())); #[derive(Debug)] pub struct Path { @@ -65,50 +69,8 @@ impl Path { /// Experimental scratchpad for testing. pub fn scratchpad() { - let axel_raw = "kernel{ - vals= - time: 123 - fn| - print: (None) -> (None); - foo: (None) -> (Num); -}"; - - let axel = axel::parse(axel_raw.to_string()); - - // software_int(); - - // let xyz = pci::brute_force_scan(); - // for dev in xyz { - // trace!("{:?}", dev); - // dev.bars.iter().for_each(|bar| { - // trace!("{:?}", bar); - // }); - // } - - for node in axel { - info!("{:?}", node); - } - /* - use crate::devices::pci::brute_force_scan; - let infos = brute_force_scan(); - for device in infos { - match device.vendor_id { - 0x1af4 => { - info!("Found virtio device"); - use crate::virtio::device_handler; - device_handler(device); - } - _ => { - info!("Found unknown device"); - } - } - } - - */ - real_shell(); } -use crate::graphics::VgaBuffer; pub fn acpi() { let acpi_handler = AcpiStruct {}; diff --git a/ableos/src/time.rs b/ableos/src/time.rs index 7184a64..49462d2 100644 --- a/ableos/src/time.rs +++ b/ableos/src/time.rs @@ -1,5 +1,5 @@ +use crate::kmain::TICK; use core::sync::atomic::Ordering; -use kernel::TICK; #[cfg(target_arch = "x86_64")] pub fn fetch_time() -> f64 { diff --git a/ableos/src/vgai.rs b/ableos/src/vgai.rs new file mode 100644 index 0000000..8abc91a --- /dev/null +++ b/ableos/src/vgai.rs @@ -0,0 +1,64 @@ +use vga::writers::{Graphics640x480x16, GraphicsWriter}; + +pub enum Color { + /// Represents the color `Black (0x0)`. + Black = 0x0, + /// Represents the color `Blue (0x1)`. + Blue = 0x1, + /// Represents the color `Green (0x2)`. + Green = 0x2, + /// Represents the color `Cyan (0x3)`. + Cyan = 0x3, + /// Represents the color `Red (0x4)`. + Red = 0x4, + /// Represents the color `Magenta (0x5)`. + Magenta = 0x5, + /// Represents the color `Brown (0x6)`. + Brown = 0x6, + /// Represents the color `LightGrey (0x7)`. + LightGrey = 0x7, + /// Represents the color `DarkGrey (0x8)`. + DarkGrey = 0x8, + /// Represents the color `LightBlue (0x9)`. + LightBlue = 0x9, + /// Represents the color `LightGreen (0xA)`. + LightGreen = 0xA, + /// Represents the color `LightCyan (0xB)`. + LightCyan = 0xB, + /// Represents the color `LightRed (0xC)`. + LightRed = 0xC, + /// Represents the color `Pink (0xD)`. + Pink = 0xD, + /// Represents the color `Yellow (0xE)`. + Yellow = 0xE, + /// Represents the color `White (0xF)`. + White = 0xF, +} + +const VGA_BUFFER_SIZE: usize = 480; + +pub struct ScreenBuffer {} +impl ScreenBuffer { + pub fn initialise() {} + + pub fn flip_buffer() { + /* + before doing anything past here we need to tell the VGA about some things + */ + let setup = false; + if setup { + use Color::*; + let dualpixel: u8 = Red as u8; + + let buff: [u8; VGA_BUFFER_SIZE] = [dualpixel; VGA_BUFFER_SIZE]; + + let buff_ptr = buff.as_ptr(); + let vga_buffer = 0xb8000 as *mut u8; + + use core::ptr; + unsafe { + ptr::copy(buff_ptr, vga_buffer, VGA_BUFFER_SIZE); + } + } + } +} diff --git a/ableos/src/vterm.rs b/ableos/src/vterm.rs new file mode 100644 index 0000000..8fd6139 --- /dev/null +++ b/ableos/src/vterm.rs @@ -0,0 +1,100 @@ +use vga::{colors::Color16, writers::GraphicsWriter}; + +use crate::{ + hardware::{MOUSE, _MOUSE}, + vga_e::VGAE, +}; +const TERM_MINUS_ONE_LINE: usize = 4720; +const CURSOR_COLOR: Color16 = Color16::Cyan; + +#[derive(Debug)] +pub struct Term { + dirty: bool, + term: [char; 80 * 60], + x: usize, +} +impl Term { + pub fn new() -> Self { + Self { + dirty: false, + x: 0, + term: ['\0'; 80 * 60], + } + } + pub fn is_dirty(&self) -> bool { + self.dirty + } + + pub fn set_dirty(&mut self, dirty: bool) { + self.dirty = dirty + } + + pub fn print(&mut self, data: String) { + for c in data.chars() { + if self.x == 79 || c == '\n' { + self.move_up(); + self.x = 0; + } else { + self.term[TERM_MINUS_ONE_LINE + self.x] = c; + self.x += 1; + } + } + } + pub fn move_up(&mut self) { + self.term.rotate_left(80); + for x in 0..80 { + self.term[TERM_MINUS_ONE_LINE + x] = '\0'; + } + } + pub fn initialize(&self) { + let mode = VGAE.lock(); + mode.set_mode(); + drop(mode); + } + + pub fn draw_term(&mut self) { + if self.is_dirty() { + trace!("Redrawing"); + let mode = VGAE.lock(); + mode.clear_screen(Color16::Black); + + let mouse_coord = x86_64::instructions::interrupts::without_interrupts(|| { + let cursor = MOUSE.lock(); + + (cursor.get_x() as usize, cursor.get_y() as usize) + }); + + mode.draw_line( + (mouse_coord.0 as isize + 0, mouse_coord.1 as isize + 0), + (mouse_coord.0 as isize + 10, mouse_coord.1 as isize + 10), + CURSOR_COLOR, + ); + mode.draw_line( + (mouse_coord.0 as isize + 0, mouse_coord.1 as isize + 0), + (mouse_coord.0 as isize + 5, mouse_coord.1 as isize + 0), + CURSOR_COLOR, + ); + mode.draw_line( + (mouse_coord.0 as isize + 0, mouse_coord.1 as isize + 0), + (mouse_coord.0 as isize + 0, mouse_coord.1 as isize + 5), + CURSOR_COLOR, + ); + + let mut x = 0; + let mut y = 0; + + for c in self.term { + mode.draw_character(x * 8, y * 8, c, Color16::White); + + if x == 79 { + y += 1; + x = 0; + } else { + x += 1 + } + } + self.set_dirty(true); + trace!("Finished drawing"); + } + } +} diff --git a/ableos/src/wasm_jumploader/host_functions.rs b/ableos/src/wasm_jumploader/host_functions.rs index 2e6157b..04b956f 100644 --- a/ableos/src/wasm_jumploader/host_functions.rs +++ b/ableos/src/wasm_jumploader/host_functions.rs @@ -136,7 +136,7 @@ host_externals! { trace!("SYSCALL: get time"); x86_64::instructions::interrupts::disable(); - let tick_time = kernel::TICK.load(Relaxed); + let tick_time = crate::kmain::TICK.load(Relaxed); x86_64::instructions::interrupts::enable(); let ret: u32 = tick_time.try_into().unwrap(); diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index d400ccc..b5e2840 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -25,7 +25,7 @@ use core::{ use versioning::Version; /// The number of ticks since the first CPU was started -pub static TICK: AtomicU64 = AtomicU64::new(0); +// pub static TICK: AtomicU64 = AtomicU64::new(0); /// Kernel's version pub const KERNEL_VERSION: Version = Version { @@ -34,6 +34,7 @@ pub const KERNEL_VERSION: Version = Version { patch: 2, }; +/* /// called by arch specific timers to tick up all kernel related functions pub fn tick() { let mut data = TICK.load(Relaxed); @@ -41,7 +42,7 @@ pub fn tick() { TICK.store(data, Relaxed) } - +*/ /// Cause a software interrupt pub fn software_int() { unsafe { asm!("int 54") } diff --git a/qmp.py b/qmp.py new file mode 100644 index 0000000..9210da5 --- /dev/null +++ b/qmp.py @@ -0,0 +1,243 @@ +# QEMU Monitor Protocol Python class +# +# Copyright (C) 2009, 2010 Red Hat Inc. +# +# Authors: +# Luiz Capitulino +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. + +import json +import errno +import socket +import logging + + +class QMPError(Exception): + pass + + +class QMPConnectError(QMPError): + pass + + +class QMPCapabilitiesError(QMPError): + pass + + +class QMPTimeoutError(QMPError): + pass + + +class QEMUMonitorProtocol(object): + + #: Logger object for debugging messages + logger = logging.getLogger('QMP') + #: Socket's error class + error = socket.error + #: Socket's timeout + timeout = socket.timeout + + def __init__(self, address, server=False): + """ + Create a QEMUMonitorProtocol class. + @param address: QEMU address, can be either a unix socket path (string) + or a tuple in the form ( address, port ) for a TCP + connection + @param server: server mode listens on the socket (bool) + @raise socket.error on socket connection errors + @note No connection is established, this is done by the connect() or + accept() methods + """ + self.__events = [] + self.__address = address + self.__sock = self.__get_sock() + self.__sockfile = None + if server: + self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.__sock.bind(self.__address) + self.__sock.listen(1) + + def __get_sock(self): + if isinstance(self.__address, tuple): + family = socket.AF_INET + else: + family = socket.AF_UNIX + return socket.socket(family, socket.SOCK_STREAM) + + def __negotiate_capabilities(self): + greeting = self.__json_read() + if greeting is None or "QMP" not in greeting: + raise QMPConnectError + # Greeting seems ok, negotiate capabilities + resp = self.cmd('qmp_capabilities') + if "return" in resp: + return greeting + raise QMPCapabilitiesError + + def __json_read(self, only_event=False): + while True: + data = self.__sockfile.readline() + if not data: + return + resp = json.loads(data) + if 'event' in resp: + self.logger.debug("<<< %s", resp) + self.__events.append(resp) + if not only_event: + continue + return resp + + def __get_events(self, wait=False): + """ + Check for new events in the stream and cache them in __events. + @param wait (bool): block until an event is available. + @param wait (float): If wait is a float, treat it as a timeout value. + @raise QMPTimeoutError: If a timeout float is provided and the timeout + period elapses. + @raise QMPConnectError: If wait is True but no events could be + retrieved or if some other error occurred. + """ + + # Check for new events regardless and pull them into the cache: + self.__sock.setblocking(0) + try: + self.__json_read() + except socket.error as err: + if err[0] == errno.EAGAIN: + # No data available + pass + self.__sock.setblocking(1) + + # Wait for new events, if needed. + # if wait is 0.0, this means "no wait" and is also implicitly false. + if not self.__events and wait: + if isinstance(wait, float): + self.__sock.settimeout(wait) + try: + ret = self.__json_read(only_event=True) + except socket.timeout: + raise QMPTimeoutError("Timeout waiting for event") + except: + raise QMPConnectError("Error while reading from socket") + if ret is None: + raise QMPConnectError("Error while reading from socket") + self.__sock.settimeout(None) + + def connect(self, negotiate=True): + """ + Connect to the QMP Monitor and perform capabilities negotiation. + @return QMP greeting dict + @raise socket.error on socket connection errors + @raise QMPConnectError if the greeting is not received + @raise QMPCapabilitiesError if fails to negotiate capabilities + """ + self.__sock.connect(self.__address) + self.__sockfile = self.__sock.makefile() + if negotiate: + return self.__negotiate_capabilities() + + def accept(self): + """ + Await connection from QMP Monitor and perform capabilities negotiation. + @return QMP greeting dict + @raise socket.error on socket connection errors + @raise QMPConnectError if the greeting is not received + @raise QMPCapabilitiesError if fails to negotiate capabilities + """ + self.__sock.settimeout(15) + self.__sock, _ = self.__sock.accept() + self.__sockfile = self.__sock.makefile() + return self.__negotiate_capabilities() + + def cmd_obj(self, qmp_cmd): + """ + Send a QMP command to the QMP Monitor. + @param qmp_cmd: QMP command to be sent as a Python dict + @return QMP response as a Python dict or None if the connection has + been closed + """ + self.logger.debug(">>> %s", qmp_cmd) + try: + self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8')) + except socket.error as err: + if err[0] == errno.EPIPE: + return + raise socket.error(err) + resp = self.__json_read() + self.logger.debug("<<< %s", resp) + return resp + + def cmd(self, name, args=None, cmd_id=None): + """ + Build a QMP command and send it to the QMP Monitor. + @param name: command name (string) + @param args: command arguments (dict) + @param cmd_id: command id (dict, list, string or int) + """ + qmp_cmd = {'execute': name} + if args: + qmp_cmd['arguments'] = args + if cmd_id: + qmp_cmd['id'] = cmd_id + return self.cmd_obj(qmp_cmd) + + def command(self, cmd, **kwds): + """ + Build and send a QMP command to the monitor, report errors if any + """ + ret = self.cmd(cmd, kwds) + if "error" in ret: + raise Exception(ret['error']['desc']) + return ret['return'] + + def pull_event(self, wait=False): + """ + Pulls a single event. + @param wait (bool): block until an event is available. + @param wait (float): If wait is a float, treat it as a timeout value. + @raise QMPTimeoutError: If a timeout float is provided and the timeout + period elapses. + @raise QMPConnectError: If wait is True but no events could be + retrieved or if some other error occurred. + @return The first available QMP event, or None. + """ + self.__get_events(wait) + + if self.__events: + return self.__events.pop(0) + return None + + def get_events(self, wait=False): + """ + Get a list of available QMP events. + @param wait (bool): block until an event is available. + @param wait (float): If wait is a float, treat it as a timeout value. + @raise QMPTimeoutError: If a timeout float is provided and the timeout + period elapses. + @raise QMPConnectError: If wait is True but no events could be + retrieved or if some other error occurred. + @return The list of available QMP events. + """ + self.__get_events(wait) + return self.__events + + def clear_events(self): + """ + Clear current list of pending events. + """ + self.__events = [] + + def close(self): + self.__sock.close() + self.__sockfile.close() + + def settimeout(self, timeout): + self.__sock.settimeout(timeout) + + def get_sock_fd(self): + return self.__sock.fileno() + + def is_scm_available(self): + return self.__sock.family == socket.AF_UNIX \ No newline at end of file diff --git a/qprofiler.py b/qprofiler.py new file mode 100644 index 0000000..1170b67 --- /dev/null +++ b/qprofiler.py @@ -0,0 +1,118 @@ +#!/usr/bin/python +# +# QProfiler is a QEMU profiler based on QMP +# +# Copyright (c) 2019-2022 Matias Vara +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +from __future__ import print_function +import sys, os, re +from qmp import QEMUMonitorProtocol +from time import sleep +import subprocess + +def main(args): + path = None + filename = None + + # duration of the test in seconds + duration = 5 + + # sampling frequency in seconds + frequency = 0.05 + + while len(args): + arg = args[0] + + if arg.startswith('--'): + arg = arg[2:] + if arg.find('=') == -1: + value = True + else: + arg, value = arg.split('=', 1) + + if arg in ['path']: + if type(value) == str: + path = value + elif arg in ['duration']: + duration = int(value) + elif arg in ['frequency']: + frequency = float(value) + elif arg in ['filename']: + filename = value + else: + print('Unknown argument "%s"' % arg) + return 1 + + args = args[1:] + else: + break + + if not path: + print("Path isn't set, use --path=qmp-monitor-address") + return 1 + + def do_command(srv, cmd, **kwds): + rsp = srv.cmd(cmd, kwds) + if 'error' in rsp: + raise Exception(rsp['error']['desc']) + return rsp['return'] + + srv = QEMUMonitorProtocol(path) + srv.connect() + + arguments = {} + command = 'human-monitor-command' + + r = int(duration // frequency) + + rip_hash = {} + + for i in range(r): + arguments['command-line'] = 'info registers' + rsp = do_command(srv, command, **arguments) + + regs = re.search(r'RIP=([\w]+)\s', rsp) + rip = regs.group(1) + + if rip in rip_hash: + rip_hash[rip] += 1 + else: + rip_hash[rip] = 1 + + sleep(frequency) + + srv.close() + rip_hash_name = {} + + for i in rip_hash: + with open(os.devnull, 'w') as devnull: + # pass + tmp = subprocess.check_output("addr2line --demangle -p -s -f -e " + + filename + + " " + + i , shell=True, stderr=devnull).rstrip() + + if tmp in rip_hash_name: + rip_hash_name[tmp] += rip_hash[i] + else: + rip_hash_name[tmp] = rip_hash[i] + + for i in rip_hash_name: + print('{:>8} {}'.format(rip_hash_name[i], i)) + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) \ No newline at end of file diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index 875400d..895e016 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -11,6 +11,8 @@ enum Command { Run { #[clap(long, short)] debug: bool, + #[clap(long, short)] + profile: bool, #[clap(long, short, arg_enum)] machine: Option, @@ -44,7 +46,12 @@ fn main() -> anyhow::Result<()> { let args = Command::parse(); match args { - Command::Run { debug, machine } => { + Command::Run { + debug, + profile, + + machine, + } => { let _dir = xshell::pushd("./ableos"); let _debug_log: &[&str] = match debug { @@ -54,15 +61,18 @@ fn main() -> anyhow::Result<()> { match machine.unwrap_or(MachineType::X86) { MachineType::X86 => { xshell::cmd!("cargo run --release").run()?; + if profile { + xshell::cmd!("python qprofiler.py --path=qmp-sock --filename=target/x86_64-ableos/release/ableos").run()?; + } } MachineType::Arm => { xshell::cmd!("cargo build --release --target=json_targets/aarch64-ableos.json") .run()?; #[rustfmt::skip] - xshell::cmd!( - "qemu-system-aarch64 - -machine virt - -m 1024M + xshell::cmd!( + "qemu-system-aarch64 + -machine virt + -m 1024M -cpu cortex-a53 -kernel target/aarch64-ableos/release/ableos -device virtio-keyboard @@ -75,15 +85,15 @@ fn main() -> anyhow::Result<()> { #[rustfmt::skip] xshell::cmd!( "qemu-system-riscv64 - -machine virt - -cpu rv64 - -smp 8 - -m 128M - -bios src/arch/riscv/firmwear/opensbi-riscv64-generic-fw_jump.bin - -kernel target/riscv64gc-unknown-none-elf/release/ableos - " - // -serial stdio - ).run()?; + -machine virt + -cpu rv64 + -smp 8 + -m 128M + -bios src/arch/riscv/firmwear/opensbi-riscv64-generic-fw_jump.bin + -kernel target/riscv64gc-unknown-none-elf/release/ableos + " + // -serial stdio + ).run()?; } } }