adding a deadlock to master :>

This commit is contained in:
able 2022-07-31 01:54:01 -05:00
parent 25e87735e6
commit a4cc9cdf4f
23 changed files with 1122 additions and 176 deletions

60
Cargo.lock generated
View file

@ -29,9 +29,9 @@ dependencies = [
"ab_glyph", "ab_glyph",
"acpi", "acpi",
"axel", "axel",
"bitflags",
"bootloader", "bootloader",
"cpuio", "cpuio",
"embedded-graphics",
"ext2", "ext2",
"externc-libm", "externc-libm",
"facepalm", "facepalm",
@ -134,7 +134,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]] [[package]]
name = "axel" name = "axel"
version = "0.1.0" 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 = [ dependencies = [
"hashbrown 0.7.2", "hashbrown 0.7.2",
"log", "log",
@ -142,12 +142,6 @@ dependencies = [
"versioning", "versioning",
] ]
[[package]]
name = "az"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f771a5d1f5503f7f4279a30f3643d3421ba149848b89ecaaec0ea2acf04a5ac4"
[[package]] [[package]]
name = "bare-metal" name = "bare-metal"
version = "1.0.0" version = "1.0.0"
@ -178,12 +172,6 @@ version = "0.9.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de78decc37247c7cfac5dbf3495c7298c6ac97cb355161caa7e15969c6648e6c" checksum = "de78decc37247c7cfac5dbf3495c7298c6ac97cb355161caa7e15969c6648e6c"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -283,29 +271,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 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]] [[package]]
name = "ext2" name = "ext2"
version = "0.1.1" version = "0.1.1"
@ -331,15 +296,6 @@ dependencies = [
"log", "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]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -490,9 +446,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.16" version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
@ -533,12 +489,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882"
[[package]]
name = "micromath"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc4010833aea396656c2f91ee704d51a6f1329ec2ab56ffd00bfd56f7481ea94"
[[package]] [[package]]
name = "no-std-compat" name = "no-std-compat"
version = "0.4.1" version = "0.4.1"
@ -1029,7 +979,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "versioning" name = "versioning"
version = "0.1.2" 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 = [ dependencies = [
"serde", "serde",
] ]

Binary file not shown.

View file

@ -46,14 +46,13 @@ test-args = [
] ]
[dependencies] [dependencies]
lazy_static = { version = "*", features = ["spin_no_std"] } lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
qrcode = { path = "../qrcode-rust" } qrcode = { path = "../qrcode-rust" }
bitflags = "1.2.1"
linked_list_allocator = "0.9.0" linked_list_allocator = "0.9.0"
lliw = "0.2.0" lliw = "0.2.0"
spin = "0.9" spin = "0.9"
log = "*" log = "0.4.17"
pretty-hex = "0.2.1" pretty-hex = "0.2.1"
unicode-width = "0.1.7" unicode-width = "0.1.7"
picorand = "0.1.0" picorand = "0.1.0"
@ -63,8 +62,9 @@ rhai = "1.6.0"
libwasm = { git = "https://git.ablecorp.us:443/able/libwasm.git" } libwasm = { git = "https://git.ablecorp.us:443/able/libwasm.git" }
axel = { git = "https://git.ablecorp.us/able/aos_userland" } axel = { git = "https://git.ablecorp.us/able/aos_userland" }
versioning = { 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" pc-keyboard = "0.5"
[dependencies.logos] [dependencies.logos]
version = "0.12" version = "0.12"
default-features = false default-features = false
@ -138,7 +138,7 @@ cpuio = { git = "https://git.ablecorp.us/ondra05/cpuio.git" }
pic8259 = "0.10.1" pic8259 = "0.10.1"
uart_16550 = "0.2.0" uart_16550 = "0.2.0"
volatile = "0.2.6" volatile = "0.2.6"
x86_64 = "*" x86_64 = "0.14.8"
pc-beeper = { git = "https://github.com/AbleOS/pc-beeper" } pc-beeper = { git = "https://github.com/AbleOS/pc-beeper" }
vga = "*" vga = "0.2.7"
acpi = "4.1.0" acpi = "4.1.0"

View file

@ -1,5 +1,5 @@
pub mod allocator; pub mod allocator;
pub mod graphics; // pub mod graphics;
pub mod serial; pub mod serial;
pub mod sysinfo; pub mod sysinfo;
pub mod timer; pub mod timer;

View file

@ -1,22 +1,24 @@
// #![allow(clippy::print_literal)] // #![allow(clippy::print_literal)]
use super::{gdt, interrupts}; use super::{drivers::serial, gdt, interrupts};
use crate::{logger, serial_println}; use crate::{logger, serial_println};
/// x86_64 initialization /// x86_64 initialization
pub fn init() { 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(); // let mut log_socket_id = SimpleSock::new();
log_socket_id.register_protocol("Logger".to_string()); // log_socket_id.register_protocol("Logger".to_string());
let result = logger::init(); let result = logger::init();
match result { match result {
Ok(_) => { Ok(_) => {
serial_println!("Logger initialized"); info!("Logger initialized");
} }
Err(err) => error!("{}", err), Err(err) => serial_println!("{}", err),
} }
/*
*/
gdt::init(); gdt::init();
interrupts::init_idt(); interrupts::init_idt();

View file

@ -18,6 +18,8 @@ use spin::Lazy;
use vga::colors::Color16; use vga::colors::Color16;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
use super::sloop;
pub const PIC_1_OFFSET: u8 = 32; pub const PIC_1_OFFSET: u8 = 32;
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
@ -30,6 +32,9 @@ pub static PICS: spin::Mutex<ChainedPics> =
pub enum InterruptIndex { pub enum InterruptIndex {
Timer = PIC_1_OFFSET, Timer = PIC_1_OFFSET,
Keyboard, Keyboard,
/// Mouse offset
Mouse = 44,
// SecondInterrupt = PIC_2_OFFSET, // SecondInterrupt = PIC_2_OFFSET,
Cmos = 0x70, Cmos = 0x70,
} }
@ -56,11 +61,11 @@ static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler); idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler);
idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard_interrupt_handler); idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard_interrupt_handler);
idt[InterruptIndex::Mouse.as_usize()].set_handler_fn(crate::hardware::mouse_interrupt_handler);
idt[6].set_handler_fn(floppy_disk_interrupt_handler);
// run `a + b + l + e + o + s print;` in ablescript and its 54 thats why this seemingly arbitrary number was chosen // 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[54].set_handler_fn(software_int_handler);
idt idt
}); });
@ -78,7 +83,7 @@ extern "x86-interrupt" fn double_fault_handler(
_error_code: u64, _error_code: u64,
) -> ! { ) -> ! {
bsod(BSODSource::DoubleFault(&stack_frame)); bsod(BSODSource::DoubleFault(&stack_frame));
// panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
} }
#[naked] #[naked]
@ -87,6 +92,7 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFr
unsafe { unsafe {
// print!("."); // print!(".");
asm!( asm!(
// Kernel tick // Kernel tick
"call {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) // Switch to next task (interrupt'll be returned there)
"jmp {switch_to_next}", "jmp {switch_to_next}",
tick = sym kernel::tick, tick = sym crate::kmain::tick,
save = sym task_switcher::save_and_enqueue, save = sym task_switcher::save_and_enqueue,
switch_to_next = sym task_switcher::switch_to_next, switch_to_next = sym task_switcher::switch_to_next,
options(noreturn), options(noreturn),
@ -122,14 +128,16 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStac
.add_byte(unsafe { Port::new(0x60).read() }) .add_byte(unsafe { Port::new(0x60).read() })
.map(|x| x.and_then(|ev| keyboard.process_keyevent(ev))) .map(|x| x.and_then(|ev| keyboard.process_keyevent(ev)))
{ {
trace!("{key:?}");
match key { match key {
DecodedKey::Unicode(chr) => match chr { DecodedKey::Unicode(chr) => match chr {
// Backspace // Backspace
'\u{8}' => { '\u{8}' => {
// TODO: Fix this and apply to new term
WRITER.lock().backspace(); WRITER.lock().backspace();
KEYBUFF.lock().push(8.into()); KEYBUFF.lock().push(8.into());
} }
'^' => KERNEL_STATE.lock().shutdown(), // '^' => KERNEL_STATE.lock().shutdown(),
chr => { chr => {
KEYBUFF.lock().push(chr); KEYBUFF.lock().push(chr);
print!("{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() { pub fn init_idt() {
IDT.load(); IDT.load();
} }
@ -200,6 +204,8 @@ pub fn reset_pit_for_cpu() {
set_pit_3(1000); set_pit_3(1000);
} }
pub fn bsod(src: BSODSource) -> ! { pub fn bsod(src: BSODSource) -> ! {
trace!("{src:?}");
let mut mode = SCREEN_BUFFER.lock(); let mut mode = SCREEN_BUFFER.lock();
mode.force_redraw(); mode.force_redraw();
/* /*
@ -214,17 +220,20 @@ pub fn bsod(src: BSODSource) -> ! {
let mut y = 0; let mut y = 0;
let src1 = match src { let src1 = match src {
BSODSource::DoubleFault(_) => "DoubleFault", BSODSource::DoubleFault(_) => "DoubleFault".to_string(),
BSODSource::Panic(_) => "Panic", BSODSource::Panic(panic_info) => {
let strr = format!("PANIC: {}", panic_info);
strr
}
}; };
let st = format!( 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 src1
); );
for current in st.chars() { for current in st.chars() {
if current == '\n' { if current == '\n' || x == 40 {
y += 1; y += 1;
x = 1; x = 1;
} else { } else {
@ -239,16 +248,15 @@ pub fn bsod(src: BSODSource) -> ! {
} }
let mut x = 1; 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 sf = format!("https://git.ablecorp.us/able/ableos/wiki/Double-Faults");
let sd = match src { let sd = match src {
BSODSource::DoubleFault(_) => "https://git.ablecorp.us/able/ableos/wiki/Double-Faults", BSODSource::DoubleFault(_) => "https://git.ablecorp.us/able/ableos/wiki/Double-Faults",
BSODSource::Panic(_) => { BSODSource::Panic(_) => {
//
trace!("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(); mode.copy_to_buffer();
loop {} sloop();
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,4 +1,4 @@
use crate::driver_traits::graphics::Point; // use crate::driver_traits::graphics::Point;
pub type MenuBar = Vec<MenuOption>; pub type MenuBar = Vec<MenuOption>;
@ -8,16 +8,16 @@ pub struct MenuOption {
pub struct Window { pub struct Window {
title: String, title: String,
position: Point, // position: Point,
fullscreen: bool, fullscreen: bool,
} }
// all of these should return a result // all of these should return a result
impl Window { impl Window {
pub fn new(title: String, position: Point, fullscreen: bool) -> Self { pub fn new(title: String, /*position: Point,*/ fullscreen: bool) -> Self {
Self { Self {
title, title,
position, // position,
fullscreen, fullscreen,
} }
} }
@ -31,7 +31,7 @@ impl Window {
pub fn set_title(&mut self) { pub fn set_title(&mut self) {
todo!(); todo!();
} }
pub fn set_position(&mut self, pos: Point) { pub fn set_position(&mut self /*pos: Point*/) {
self.position = pos; // self.position = pos;
} }
} }

130
ableos/src/hardware/mod.rs Normal file
View file

@ -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<Mouse> = Mutex::new(Mouse::new());
pub static ref MOUSE: Mutex<OnScreenMouse> = 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);
}
}

View file

@ -1,6 +1,6 @@
#![allow(clippy::empty_loop)] #![allow(clippy::empty_loop)]
use core::sync::atomic::Ordering; use core::sync::atomic::{AtomicU64, Ordering};
use crate::arch::drivers::sysinfo::master; use crate::arch::drivers::sysinfo::master;
use crate::ipc::channel::ChannelMessage; use crate::ipc::channel::ChannelMessage;
@ -13,9 +13,9 @@ use crate::{
scratchpad, scratchpad,
}; };
use crate::{boot_conf::KernelConfig, systeminfo::RELEASE_TYPE}; 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 genfs::{Fs, OpenOptions};
use kernel::{KERNEL_VERSION, TICK}; use kernel::KERNEL_VERSION;
use libwasm::syscalls::time_calls::get_time; use libwasm::syscalls::time_calls::get_time;
use qrcode::render::unicode; use qrcode::render::unicode;
use qrcode::QrCode; use qrcode::QrCode;
@ -35,29 +35,43 @@ pub fn kernel_main() -> ! {
} else { } else {
log::set_max_level(log::LevelFilter::Off); 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(); let mut ipc_service = IPC.lock();
// create some channels or whatever here then drop it // create some channels or whatever here then drop it
let log_handle = ipc_service.create_channel("LOG", true); let log_handle = ipc_service.create_channel("LOG", true);
let log_sock_handle = ipc_service.create_socket("LOG", true); let log_sock_handle = ipc_service.create_socket("LOG", true);
for handle in ipc_service.examine_board() { for handle in ipc_service.examine_board() {
println!("Discovered: {}", handle); // println!("Discovered: {}", handle);
} }
drop(ipc_service); 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) // 1. Initialise ATA Subsystem. (Perform Once, on boot)
init().expect("Failed To Start ATA..."); // init().expect("Failed To Start ATA...");
let mut buffer: [u8; ATA_BLOCK_SIZE] = [0; ATA_BLOCK_SIZE]; // let mut buffer: [u8; ATA_BLOCK_SIZE] = [0; ATA_BLOCK_SIZE];
// FIXME: Calls to read panic the kernel // FIXME: Calls to read panic the kernel
// read(0, 0, 0, &mut buffer); // read(0, 0, 0, &mut buffer);
// for abc in list() { // for abc in list() {
// trace!("{:?}", abc); // trace!("{:?}", abc);
// } // }
log_version_data();
x86_64::instructions::interrupts::without_interrupts(|| { x86_64::instructions::interrupts::without_interrupts(|| {
let mut scheduler = SCHEDULER.lock(); let mut scheduler = SCHEDULER.lock();
// comment this out to resume normal use // comment this out to resume normal use
@ -116,9 +130,28 @@ pub fn cpu_socket_startup() {
} }
pub fn log_version_data() { pub fn log_version_data() {
info!("{} v{:?}", RELEASE_TYPE, KERNEL_VERSION); info!("{} v{}", RELEASE_TYPE, KERNEL_VERSION);
info!( info!(
"Brand String: {}", "Brand String: {}",
master().unwrap().brand_string().unwrap() 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");
});
}

View file

@ -53,6 +53,7 @@ pub mod keyboard;
pub mod kmain; pub mod kmain;
pub mod logger; pub mod logger;
pub mod prelude; pub mod prelude;
pub mod ps2_mouse;
pub mod relib; pub mod relib;
pub mod rhai_shell; pub mod rhai_shell;
pub mod scheduler; pub mod scheduler;
@ -69,10 +70,13 @@ pub mod allocator;
// pub use allocator as aalloc; // pub use allocator as aalloc;
pub mod channels; pub mod channels;
pub mod handle; pub mod handle;
pub mod hardware;
pub mod ipc; pub mod ipc;
pub mod panic; pub mod panic;
mod unicode_utils; mod unicode_utils;
pub mod vga_e; pub mod vga_e;
pub mod vgai;
pub mod vterm;
#[prelude_import] #[prelude_import]
pub use prelude::rust_2021::*; pub use prelude::rust_2021::*;

View file

@ -1,6 +1,7 @@
use crate::kmain::KERNEL_CONF; use crate::kmain::KERNEL_CONF;
use crate::network::socket::{SimpleSock, Socket}; use crate::network::socket::{SimpleSock, Socket};
use crate::time::fetch_time; use crate::time::fetch_time;
use alloc::borrow::ToOwned;
use lliw::{Fg, Reset}; use lliw::{Fg, Reset};
use log::{Level, Metadata, Record}; use log::{Level, Metadata, Record};
use log::{LevelFilter, SetLoggerError}; use log::{LevelFilter, SetLoggerError};
@ -17,6 +18,8 @@ impl log::Log for SimpleLogger {
fn log(&self, record: &Record) { fn log(&self, record: &Record) {
if self.enabled(record.metadata()) { if self.enabled(record.metadata()) {
let time_float = fetch_time(); let time_float = fetch_time();
use log::Level::*;
use Fg::*;
let color = match record.level() { let color = match record.level() {
log::Level::Error => (Fg::Red, "$RED$"), log::Level::Error => (Fg::Red, "$RED$"),
@ -26,6 +29,7 @@ impl log::Log for SimpleLogger {
log::Level::Trace => (Fg::Yellow, "$YELLOW$"), log::Level::Trace => (Fg::Yellow, "$YELLOW$"),
}; };
/*
let msg = format!( let msg = format!(
"[{}{}$RESET$][$GREEN${}$RESET$]{}\n", "[{}{}$RESET$][$GREEN${}$RESET$]{}\n",
color.1, color.1,
@ -33,29 +37,33 @@ impl log::Log for SimpleLogger {
time_float, time_float,
record.args() 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 { if KERNEL_CONF.logging.log_to_serial {
serial_println!( serial_println!(
"[{}{}{}][{}{}{}] {}", "[{}{}{}][{}{}{}][{}{}@{}{}] {}",
color.0, color.0,
record.level(), record.level(),
Fg::Reset, Fg::Reset,
Fg::Green, Fg::Green,
time_float, time_float,
Reset, Reset,
Fg::Blue,
mod_path,
line,
Reset,
record.args() 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 /// Clear the log buffer
@ -64,4 +72,9 @@ impl log::Log for SimpleLogger {
pub fn init() -> Result<(), SetLoggerError> { pub fn init() -> Result<(), SetLoggerError> {
log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Trace)) log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Trace))
// ?;
// trace!("Logger started");
// Ok(())
} }

View file

@ -5,4 +5,5 @@ pub use core::arch::asm;
pub use core::prelude::rust_2021::*; pub use core::prelude::rust_2021::*;
pub use core::prelude::v1::*; pub use core::prelude::v1::*;
pub use core::result::Result::*;
pub use log::{debug, info, trace, warn}; pub use log::{debug, info, trace, warn};

View file

@ -21,8 +21,15 @@ impl core::fmt::Write for Stdout {
} }
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
fn write_str(&mut self, s: &str) -> Result<(), Error> { fn write_str(&mut self, s: &str) -> Result<(), Error> {
use crate::kprint; use crate::TERM;
kprint!("{}", s);
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(()) Ok(())
} }
#[cfg(target_arch = "riscv64")] #[cfg(target_arch = "riscv64")]

300
ableos/src/ps2_mouse.rs Normal file
View file

@ -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<u8>,
data_port: Port<u8>,
current_packet: u8,
current_state: MouseState,
completed_state: MouseState,
on_complete: Option<fn(MouseState)>,
}
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<u8, &'static str> {
// 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")
}
}

View file

@ -10,8 +10,9 @@ use crate::handle::Handle;
use crate::ipc::IPC; use crate::ipc::IPC;
use crate::rhai_shell::shell; use crate::rhai_shell::shell;
use crate::rhai_shell::KEYBUFF; use crate::rhai_shell::KEYBUFF;
use crate::vterm::Term;
use crate::wasm_jumploader::run_program; use crate::wasm_jumploader::run_program;
use crate::SCREEN_BUFFER; use crate::{vgai, SCREEN_BUFFER};
use acpi::{AcpiTables, PlatformInfo}; use acpi::{AcpiTables, PlatformInfo};
use alloc::collections::{vec_deque, VecDeque}; use alloc::collections::{vec_deque, VecDeque};
use cpuio::inb; use cpuio::inb;
@ -24,6 +25,7 @@ use genfs::OpenOptions;
use genfs::{DirEntry, Fs}; use genfs::{DirEntry, Fs};
use kernel::proccess::PID; use kernel::proccess::PID;
use kernel::software_int; use kernel::software_int;
use spin::Lazy;
use vga::writers::GraphicsWriter; use vga::writers::GraphicsWriter;
// TODO: move to a better place // TODO: move to a better place
@ -43,6 +45,8 @@ impl acpi::AcpiHandler for AcpiStruct {
todo!("unmap_physical_region"); todo!("unmap_physical_region");
} }
} }
pub static TERM: Lazy<spin::Mutex<Term>> = Lazy::new(|| spin::Mutex::new(Term::new()));
#[derive(Debug)] #[derive(Debug)]
pub struct Path { pub struct Path {
@ -65,50 +69,8 @@ impl Path {
/// Experimental scratchpad for testing. /// Experimental scratchpad for testing.
pub fn scratchpad() { 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(); real_shell();
} }
use crate::graphics::VgaBuffer;
pub fn acpi() { pub fn acpi() {
let acpi_handler = AcpiStruct {}; let acpi_handler = AcpiStruct {};

View file

@ -1,5 +1,5 @@
use crate::kmain::TICK;
use core::sync::atomic::Ordering; use core::sync::atomic::Ordering;
use kernel::TICK;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
pub fn fetch_time() -> f64 { pub fn fetch_time() -> f64 {

64
ableos/src/vgai.rs Normal file
View file

@ -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);
}
}
}
}

100
ableos/src/vterm.rs Normal file
View file

@ -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");
}
}
}

View file

@ -136,7 +136,7 @@ host_externals! {
trace!("SYSCALL: get time"); trace!("SYSCALL: get time");
x86_64::instructions::interrupts::disable(); 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(); x86_64::instructions::interrupts::enable();
let ret: u32 = tick_time.try_into().unwrap(); let ret: u32 = tick_time.try_into().unwrap();

View file

@ -25,7 +25,7 @@ use core::{
use versioning::Version; use versioning::Version;
/// The number of ticks since the first CPU was started /// 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 /// Kernel's version
pub const KERNEL_VERSION: Version = Version { pub const KERNEL_VERSION: Version = Version {
@ -34,6 +34,7 @@ pub const KERNEL_VERSION: Version = Version {
patch: 2, patch: 2,
}; };
/*
/// called by arch specific timers to tick up all kernel related functions /// called by arch specific timers to tick up all kernel related functions
pub fn tick() { pub fn tick() {
let mut data = TICK.load(Relaxed); let mut data = TICK.load(Relaxed);
@ -41,7 +42,7 @@ pub fn tick() {
TICK.store(data, Relaxed) TICK.store(data, Relaxed)
} }
*/
/// Cause a software interrupt /// Cause a software interrupt
pub fn software_int() { pub fn software_int() {
unsafe { asm!("int 54") } unsafe { asm!("int 54") }

243
qmp.py Normal file
View file

@ -0,0 +1,243 @@
# QEMU Monitor Protocol Python class
#
# Copyright (C) 2009, 2010 Red Hat Inc.
#
# Authors:
# Luiz Capitulino <lcapitulino@redhat.com>
#
# 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

118
qprofiler.py Normal file
View file

@ -0,0 +1,118 @@
#!/usr/bin/python
#
# QProfiler is a QEMU profiler based on QMP
#
# Copyright (c) 2019-2022 Matias Vara <matiasevara@gmail.com>
# 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 <http://www.gnu.org/licenses/>.
#
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:]))

View file

@ -11,6 +11,8 @@ enum Command {
Run { Run {
#[clap(long, short)] #[clap(long, short)]
debug: bool, debug: bool,
#[clap(long, short)]
profile: bool,
#[clap(long, short, arg_enum)] #[clap(long, short, arg_enum)]
machine: Option<MachineType>, machine: Option<MachineType>,
@ -44,7 +46,12 @@ fn main() -> anyhow::Result<()> {
let args = Command::parse(); let args = Command::parse();
match args { match args {
Command::Run { debug, machine } => { Command::Run {
debug,
profile,
machine,
} => {
let _dir = xshell::pushd("./ableos"); let _dir = xshell::pushd("./ableos");
let _debug_log: &[&str] = match debug { let _debug_log: &[&str] = match debug {
@ -54,15 +61,18 @@ fn main() -> anyhow::Result<()> {
match machine.unwrap_or(MachineType::X86) { match machine.unwrap_or(MachineType::X86) {
MachineType::X86 => { MachineType::X86 => {
xshell::cmd!("cargo run --release").run()?; 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 => { MachineType::Arm => {
xshell::cmd!("cargo build --release --target=json_targets/aarch64-ableos.json") xshell::cmd!("cargo build --release --target=json_targets/aarch64-ableos.json")
.run()?; .run()?;
#[rustfmt::skip] #[rustfmt::skip]
xshell::cmd!( xshell::cmd!(
"qemu-system-aarch64 "qemu-system-aarch64
-machine virt -machine virt
-m 1024M -m 1024M
-cpu cortex-a53 -cpu cortex-a53
-kernel target/aarch64-ableos/release/ableos -kernel target/aarch64-ableos/release/ableos
-device virtio-keyboard -device virtio-keyboard
@ -75,15 +85,15 @@ fn main() -> anyhow::Result<()> {
#[rustfmt::skip] #[rustfmt::skip]
xshell::cmd!( xshell::cmd!(
"qemu-system-riscv64 "qemu-system-riscv64
-machine virt -machine virt
-cpu rv64 -cpu rv64
-smp 8 -smp 8
-m 128M -m 128M
-bios src/arch/riscv/firmwear/opensbi-riscv64-generic-fw_jump.bin -bios src/arch/riscv/firmwear/opensbi-riscv64-generic-fw_jump.bin
-kernel target/riscv64gc-unknown-none-elf/release/ableos -kernel target/riscv64gc-unknown-none-elf/release/ableos
" "
// -serial stdio // -serial stdio
).run()?; ).run()?;
} }
} }
} }