diff --git a/Cargo.lock b/Cargo.lock index cb13548e..f95929be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -230,6 +230,15 @@ dependencies = [ "digest", ] +[[package]] +name = "iced-x86" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd366a53278429c028367e0ba22a46cab6d565a57afb959f06e92c7a69e7828" +dependencies = [ + "lazy_static", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -315,6 +324,7 @@ dependencies = [ "base64", "bit_field", "bootloader", + "iced-x86", "lazy_static", "libm", "linked_list_allocator", diff --git a/Cargo.toml b/Cargo.toml index d1140c26..80064e3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ aml = "0.16.4" base64 = { version = "0.13.1", default-features = false } bit_field = "0.10.2" bootloader = { version = "0.9.23", features = ["map_physical_memory"] } +iced-x86 = { version = "1.20.0", default-features = false, features = ["no_std", "decoder", "encoder", "block_encoder", "code_asm", "nasm"] } lazy_static = { version = "1.4.0", features = ["spin_no_std"] } libm = "0.2.8" linked_list_allocator = "0.10.5" diff --git a/run/moros-fuse.py b/run/moros-fuse.py index b6faecb9..fa76e812 100644 --- a/run/moros-fuse.py +++ b/run/moros-fuse.py @@ -9,7 +9,7 @@ from threading import Lock BLOCK_SIZE = 512 -SUPERBLOCK_ADDR = 4096 * BLOCK_SIZE +SUPERBLOCK_ADDR = 2 * 4096 * BLOCK_SIZE BITMAP_ADDR = SUPERBLOCK_ADDR + 2 * BLOCK_SIZE ENTRY_SIZE = (1 + 4 + 4 + 8 + 1) diff --git a/src/lib.rs b/src/lib.rs index 3c26ba5f..fa0c9983 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ pub mod usr; use bootloader::BootInfo; -const KERNEL_SIZE: usize = 2 << 20; // 2 MB +const KERNEL_SIZE: usize = 4 << 20; // 2 MB pub fn init(boot_info: &'static BootInfo) { sys::vga::init(); diff --git a/src/sys/acpi.rs b/src/sys/acpi.rs index a65abd7d..3e0331bc 100644 --- a/src/sys/acpi.rs +++ b/src/sys/acpi.rs @@ -26,12 +26,12 @@ pub fn shutdown() { if let Ok(fadt) = acpi.find_table::() { if let Ok(block) = fadt.pm1a_control_block() { pm1a_control_block = block.address as u32; - //debug!("ACPI Found PM1a Control Block: {:#x}", pm1a_control_block); + //debug!("ACPI Found PM1a Control Block: {:#X}", pm1a_control_block); } } if let Ok(dsdt) = &acpi.dsdt() { let address = sys::mem::phys_to_virt(PhysAddr::new(dsdt.address as u64)); - //debug!("ACPI Found DSDT at {:#x} {:#x}", dsdt.address, address); + //debug!("ACPI Found DSDT at {:#X} {:#X}", dsdt.address, address); let table = unsafe { core::slice::from_raw_parts(address.as_ptr(), dsdt.length as usize) }; if aml.parse_table(table).is_ok() { let name = AmlName::from_str("\\_S5").unwrap(); @@ -69,7 +69,7 @@ pub struct MorosAcpiHandler; impl AcpiHandler for MorosAcpiHandler { unsafe fn map_physical_region(&self, physical_address: usize, size: usize) -> PhysicalMapping { let virtual_address = sys::mem::phys_to_virt(PhysAddr::new(physical_address as u64)); - //debug!("ACPI mapping phys {:#x} -> virt {:#x}", physical_address, virtual_address); + //debug!("ACPI mapping phys {:#X} -> virt {:#X}", physical_address, virtual_address); PhysicalMapping::new(physical_address, NonNull::new(virtual_address.as_mut_ptr()).unwrap(), size, size, Self) } diff --git a/src/sys/allocator.rs b/src/sys/allocator.rs index 7c006a61..018c8cc8 100644 --- a/src/sys/allocator.rs +++ b/src/sys/allocator.rs @@ -55,7 +55,7 @@ pub fn init_heap() -> Result<(), MapToError> { } pub fn alloc_pages(mapper: &mut OffsetPageTable, addr: u64, size: usize) -> Result<(), ()> { - //debug!("Alloc pages (addr={:#x}, size={})", addr, size); + //debug!("Alloc pages (addr={:#X}, size={})", addr, size); let mut frame_allocator = sys::mem::frame_allocator(); let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE; let pages = { diff --git a/src/sys/fs/block.rs b/src/sys/fs/block.rs index 8ec04e0e..ab4f5890 100644 --- a/src/sys/fs/block.rs +++ b/src/sys/fs/block.rs @@ -44,7 +44,7 @@ impl Block { let mut buf = [0; super::BLOCK_SIZE]; if let Some(ref mut block_device) = *super::block_device::BLOCK_DEVICE.lock() { if block_device.read(addr, &mut buf).is_err() { - debug!("MFS: could not read block {:#x}", addr); + debug!("MFS: could not read block {:#X}", addr); } } Self { addr, buf } @@ -53,7 +53,7 @@ impl Block { pub fn write(&self) { if let Some(ref mut block_device) = *super::block_device::BLOCK_DEVICE.lock() { if block_device.write(self.addr, &self.buf).is_err() { - debug!("MFS: could not write block {:#x}", self.addr); + debug!("MFS: could not write block {:#X}", self.addr); } } } diff --git a/src/sys/idt.rs b/src/sys/idt.rs index ab0eb50f..0342cdb3 100644 --- a/src/sys/idt.rs +++ b/src/sys/idt.rs @@ -120,7 +120,7 @@ extern "x86-interrupt" fn page_fault_handler(_stack_frame: InterruptStackFrame, if sys::allocator::alloc_pages(&mut mapper, addr, 1).is_err() { let csi_color = api::console::Style::color("LightRed"); let csi_reset = api::console::Style::reset(); - printk!("{}Error:{} Could not allocate address {:#x}\n", csi_color, csi_reset, addr); + printk!("{}Error:{} Could not allocate address {:#X}\n", csi_color, csi_reset, addr); if error_code.contains(PageFaultErrorCode::USER_MODE) { api::syscall::exit(ExitCode::PageFaultError); } else { diff --git a/src/sys/process.rs b/src/sys/process.rs index ba279d73..a347ad2a 100644 --- a/src/sys/process.rs +++ b/src/sys/process.rs @@ -341,8 +341,8 @@ impl Process { let proc_size = MAX_PROC_SIZE as u64; let code_addr = CODE_ADDR.fetch_add(proc_size, Ordering::SeqCst); let stack_addr = code_addr + proc_size - 4096; - //debug!("code_addr: {:#x}", code_addr); - //debug!("stack_addr: {:#x}", stack_addr); + //debug!("code_addr: {:#X}", code_addr); + //debug!("stack_addr: {:#X}", stack_addr); let mut entry_point_addr = 0; let code_ptr = code_addr as *mut u8; @@ -356,7 +356,7 @@ impl Process { let addr = segment.address() as usize; if let Ok(data) = segment.data() { for (i, b) in data.iter().enumerate() { - //debug!("code: {:#x}", unsafe { code_ptr.add(addr + i) as usize }); + //debug!("code: {:#X}", unsafe { code_ptr.add(addr + i) as usize }); unsafe { core::ptr::write(code_ptr.add(addr + i), *b) }; } } @@ -401,7 +401,7 @@ impl Process { let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) }; let heap_addr = self.code_addr + (self.stack_addr - self.code_addr) / 2; - //debug!("user-args: {:#016x}", heap_addr); + //debug!("user-args: {:#016X}", heap_addr); sys::allocator::alloc_pages(&mut mapper, heap_addr, 1).expect("proc heap alloc"); let args_ptr = ptr_from_addr(args_ptr as u64) as usize; @@ -429,7 +429,7 @@ impl Process { let heap_addr = addr; let heap_size = (self.stack_addr - heap_addr) / 2; - //debug!("user-heap: {:#016x}..{:#016x}", heap_addr, heap_addr + heap_size); + //debug!("user-heap: {:#016X}..{:#016X}", heap_addr, heap_addr + heap_size); unsafe { self.allocator.lock().init(heap_addr as *mut u8, heap_size as usize) }; set_id(self.id); // Change PID diff --git a/src/sys/syscall/service.rs b/src/sys/syscall/service.rs index f28bb1a3..6264c26b 100644 --- a/src/sys/syscall/service.rs +++ b/src/sys/syscall/service.rs @@ -90,7 +90,7 @@ pub fn close(handle: usize) { } pub fn spawn(path: &str, args_ptr: usize, args_len: usize) -> ExitCode { - //debug!("syscall::spawn(path={}, args_ptr={:#x}, args_len={})", path, args_ptr, args_len); + //debug!("syscall::spawn(path={}, args_ptr={:#X}, args_len={})", path, args_ptr, args_len); let path = match sys::fs::canonicalize(path) { Ok(path) => path, Err(_) => return ExitCode::OpenError, @@ -127,7 +127,7 @@ pub fn stop(code: usize) -> usize { sys::acpi::shutdown(); } _ => { - debug!("STOP SYSCALL: Invalid code '{:#x}' received", code); + debug!("STOP SYSCALL: Invalid code '{:#X}' received", code); } } 0 diff --git a/src/usr/assembler.rs b/src/usr/assembler.rs new file mode 100644 index 00000000..d8234399 --- /dev/null +++ b/src/usr/assembler.rs @@ -0,0 +1,245 @@ +use crate::api::fs; +use crate::api::console::Style; +use crate::api::process::ExitCode; +use crate::api::syscall; + +use alloc::collections::btree_map::BTreeMap; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec::Vec; +use alloc::vec; +use core::num::ParseIntError; +use core::iter; +use iced_x86::code_asm::*; +use nom::IResult; +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::character::complete::alpha1; +use nom::character::complete::alphanumeric1; +use nom::character::complete::multispace0; +use nom::combinator::recognize; +use nom::multi::separated_list0; +use nom::sequence::delimited; +use nom::sequence::preceded; +use nom::sequence::terminated; +use nom::sequence::tuple; + +#[derive(Clone, Debug)] +pub enum Exp { + Label(String), + Instr(Vec), +} + +pub fn main(args: &[&str]) -> Result<(), ExitCode> { + if args.len() != 2 { + help(); + return Err(ExitCode::UsageError); + } + if args[1] == "-h" || args[1] == "--help" { + help(); + return Ok(()); + } + let path = args[1]; + if let Ok(input) = fs::read_to_string(path) { + if let Ok(output) = assemble(&input) { + let mut buf = vec![0x7F, b'B', b'I', b'N']; + buf.extend_from_slice(&output); + syscall::write(1, &buf); + } + Ok(()) + } else { + error!("Could not find file '{}'", path); + Err(ExitCode::Failure) + } +} + +pub fn assemble(input: &str) -> Result, IcedError> { + let mut a = CodeAssembler::new(64)?; + let mut labels = BTreeMap::new(); + let mut buf = input; + loop { + match parse(buf) { + Ok((rem, exp)) => { + debug!("{:?}", exp); + match exp { + Exp::Label(name) => { + let label = a.create_label(); + labels.insert(name, label); + } + _ => {} + } + if rem.trim().is_empty() { + break; + } + buf = rem; + } + Err(err) => { + debug!("Error: {:#?}", err); + break; + } + } + } + let mut buf = input; + loop { + match parse(buf) { + Ok((rem, exp)) => { + match exp { + Exp::Label(name) => { + if let Some(mut label) = labels.get_mut(&name) { + a.set_label(&mut label)?; + } + } + Exp::Instr(args) => { + match args[0].as_str() { + "mov" => { + if let Ok(reg) = parse_r32(&args[1]) { + if let Ok(num) = parse_u32(&args[2]) { + a.mov(reg, num)?; + } else if let Some(label) = labels.get(&args[2]) { + a.lea(reg, ptr(*label))?; + } + } else if let Ok(reg) = parse_r64(&args[1]) { + if let Ok(num) = parse_u64(&args[2]) { + a.mov(reg, num)?; + } else if let Some(label) = labels.get(&args[2]) { + a.lea(reg, ptr(*label))?; + } + } + } + "int" => { + if let Ok(num) = parse_u32(&args[1]) { + a.int(num)?; + } + } + "db" => { + let mut buf = Vec::new(); + for arg in args[1..].iter() { + if let Ok(num) = parse_u8(arg) { + buf.push(num); + } + } + a.db(&buf)?; + } + _ => { + error!("Invalid instruction '{}'\n", args[0]); + break; + } + } + } + } + if rem.trim().is_empty() { + break; + } + buf = rem; + } + Err(err) => { + debug!("asm: {:#?}", err); + break; + } + } + } + a.assemble(0x200_000) +} + +// Parser + +fn parse(input: &str) -> IResult<&str, Exp> { + alt((parse_label, parse_instr))(input) +} + +fn parse_instr(input: &str) -> IResult<&str, Exp> { + let (input, (code, args)) = tuple(( + delimited(multispace0, alpha1, multispace0), + separated_list0( + terminated(tag(","), multispace0), + alt((alpha1, hex)) + ) + ))(input)?; + let instr = iter::once(code).chain(args.iter().copied()).map(|s| s.to_string()).collect(); + let exp = Exp::Instr(instr); + Ok((input, exp)) +} + +fn parse_label(input: &str) -> IResult<&str, Exp> { + let (input, label) = delimited(multispace0, terminated(alpha1, tag(":")), multispace0)(input)?; + Ok((input, Exp::Label(label.to_string()))) +} + +fn parse_u8(s: &str) -> Result { + if s.starts_with("0x") { + u8::from_str_radix(&s[2..], 16) + } else { + u8::from_str_radix(s, 10) + } +} + +fn parse_u32(s: &str) -> Result { + if s.starts_with("0x") { + u32::from_str_radix(&s[2..], 16) + } else { + u32::from_str_radix(s, 10) + } +} + +fn parse_u64(s: &str) -> Result { + if s.starts_with("0x") { + u64::from_str_radix(&s[2..], 16) + } else { + u64::from_str_radix(s, 10) + } +} + +fn parse_r32(name: &str) -> Result { + match name { + "eax" => Ok(eax), + "ebx" => Ok(ebx), + "ecx" => Ok(ecx), + "edx" => Ok(edx), + "edi" => Ok(edi), + "esi" => Ok(esi), + "ebp" => Ok(ebp), + "esp" => Ok(esp), + "r8d" => Ok(r8d), + "r9d" => Ok(r9d), + "r10d" => Ok(r10d), + "r11d" => Ok(r11d), + "r12d" => Ok(r12d), + "r13d" => Ok(r13d), + "r14d" => Ok(r14d), + "r15d" => Ok(r15d), + _ => Err(()), + } +} + +fn parse_r64(name: &str) -> Result { + match name { + "rax" => Ok(rax), + "rbx" => Ok(rbx), + "rcx" => Ok(rcx), + "rdx" => Ok(rdx), + "rdi" => Ok(rdi), + "rsi" => Ok(rsi), + "rbp" => Ok(rbp), + "rsp" => Ok(rsp), + "r8" => Ok(r8), + "r9" => Ok(r9), + "r10" => Ok(r10), + "r11" => Ok(r11), + "r12" => Ok(r12), + "r13" => Ok(r13), + "r14" => Ok(r14), + "r15" => Ok(r15), + _ => Err(()), + } +} + +fn hex(input: &str) -> IResult<&str, &str> { + recognize(preceded(alt((tag("0x"), tag("0X"))), alphanumeric1))(input) +} + +fn help() { + let csi_option = Style::color("LightCyan"); + let csi_title = Style::color("Yellow"); + let csi_reset = Style::reset(); + println!("{}Usage:{} asm {}{}", csi_title, csi_reset, csi_option, csi_reset); +} diff --git a/src/usr/elf.rs b/src/usr/elf.rs index b0bbf302..104fdef5 100644 --- a/src/usr/elf.rs +++ b/src/usr/elf.rs @@ -1,39 +1,91 @@ use crate::api::console::Style; use crate::api::fs; use crate::api::process::ExitCode; - use crate::usr; + +use alloc::string::String; +use iced_x86::{Decoder, DecoderOptions, Formatter, Instruction, NasmFormatter}; use object::{Object, ObjectSection}; pub fn main(args: &[&str]) -> Result<(), ExitCode> { - if args.len() != 2 { - help(); - return Err(ExitCode::UsageError); - } - if args[1] == "-h" || args[1] == "--help" { - help(); - return Ok(()); + let mut path = ""; + let mut disassemble = false; + let mut i = 1; + let n = args.len(); + while i < n { + match args[i] { + "-h" | "--help" => { + help(); + return Ok(()); + } + "-d" | "--disassemble" => { + disassemble = true; + } + _ => { + if args[i].starts_with('-') { + error!("Invalid option '{}'", args[i]); + return Err(ExitCode::UsageError); + } else if path.is_empty() { + path = args[i]; + } else { + error!("Too many arguments"); + return Err(ExitCode::UsageError); + } + } + } + i += 1; } let color = Style::color("Yellow"); let reset = Style::reset(); - let pathname = args[1]; - if let Ok(buf) = fs::read_to_bytes(pathname) { + if let Ok(buf) = fs::read_to_bytes(path) { let bin = buf.as_slice(); if let Ok(obj) = object::File::parse(bin) { - println!("ELF entry address: {:#x}", obj.entry()); + println!("ELF entry address: {:#X}", obj.entry()); for section in obj.sections() { if let Ok(name) = section.name() { if name.is_empty() { continue; } + if disassemble && section.kind() != object::SectionKind::Text { + continue; + } let addr = section.address() as usize; let size = section.size(); let align = section.align(); println!(); - println!("{}{}{} (addr: {:#x}, size: {}, align: {})", color, name, reset, addr, size, align); + + println!("{}{}{} (addr: {:#X}, size: {}, align: {})", color, name, reset, addr, size, align); if let Ok(data) = section.data() { - usr::hex::print_hex(data); + if disassemble { + let ip = addr as u64; + let mut decoder = Decoder::with_ip(64, data, ip, DecoderOptions::NONE); + let mut formatter = NasmFormatter::new(); + formatter.options_mut().set_hex_prefix("0x"); + formatter.options_mut().set_hex_suffix(""); + //formatter.options_mut().set_first_operand_char_index(10); + let mut output = String::new(); + let mut instruction = Instruction::default(); + while decoder.can_decode() { + decoder.decode_out(&mut instruction); + output.clear(); + formatter.format(&instruction, &mut output); + print!("{}{:016X}: ", Style::color("LightCyan"), instruction.ip()); + let start_index = (instruction.ip() - ip) as usize; + let instr_bytes = &data[start_index..start_index + instruction.len()]; + for b in instr_bytes.iter() { + print!("{}{:02X} {}", Style::color("Pink"), b, reset); + } + if instr_bytes.len() < 10 { + for _ in 0..10 - instr_bytes.len() { + print!(" "); + } + } + println!(" {}", output); + } + } else { + usr::hex::print_hex_at(data, addr); + } } } } @@ -43,7 +95,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> { Err(ExitCode::Failure) } } else { - error!("Could not find file '{}'", pathname); + error!("Could not find file '{}'", path); Err(ExitCode::Failure) } } @@ -53,4 +105,7 @@ fn help() { let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); println!("{}Usage:{} elf {}{}", csi_title, csi_reset, csi_option, csi_reset); + println!(); + println!("{}Options:{}", csi_title, csi_reset); + println!(" {0}-d{1}, {0}--disassemble{1} Display assembler contents of executable section", csi_option, csi_reset); } diff --git a/src/usr/hex.rs b/src/usr/hex.rs index 686c5fac..553096e9 100644 --- a/src/usr/hex.rs +++ b/src/usr/hex.rs @@ -2,6 +2,10 @@ use crate::api::fs; use crate::api::console::Style; use crate::api::process::ExitCode; +use alloc::format; +use alloc::string::String; +use alloc::vec::Vec; + // TODO: add `--skip` and `--length` params pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() != 2 { @@ -24,32 +28,32 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> { // TODO: move this to api::hex::print_hex pub fn print_hex(buf: &[u8]) { - let n = buf.len() / 2; - for i in 0..n { - print!("{}", Style::color("LightCyan")); - if i % 8 == 0 { - print!("{:08X}: ", i * 2); - } - print!("{}", Style::color("Pink")); - print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]); - print!("{}", Style::reset()); - if i % 8 == 7 || i == n - 1 { - for _ in 0..(7 - (i % 8)) { - print!(" "); - } - let m = ((i % 8) + 1) * 2; - for j in 0..m { - let c = buf[(i * 2 + 1) - (m - 1) + j] as char; - if c.is_ascii_graphic() { - print!("{}", c); - } else if c.is_ascii_whitespace() { - print!(" "); - } else { - print!("."); - } + print_hex_at(buf, 0) +} + +pub fn print_hex_at(buf: &[u8], offset: usize) { + let cyan = Style::color("LightCyan"); + let pink = Style::color("Pink"); + let reset = Style::reset(); + + for (index, chunk) in buf.chunks(16).enumerate() { + let addr = offset + index * 16; + + let hex = chunk.chunks(2).map(|pair| + pair.iter().map(|byte| + format!("{:02X}", byte) + ).collect::>().join("") + ).collect::>().join(" "); + + let ascii: String = chunk.iter().map(|byte| + if *byte >= 32 && *byte <= 126 { + *byte as char + } else { + '.' } - println!(); - } + ).collect(); + + println!("{}{:08X}: {}{:40}{}{}", cyan, addr, pink, hex, reset, ascii); } } diff --git a/src/usr/mod.rs b/src/usr/mod.rs index ea33e6ba..4356b82e 100644 --- a/src/usr/mod.rs +++ b/src/usr/mod.rs @@ -1,3 +1,4 @@ +pub mod assembler; pub mod base64; pub mod beep; pub mod calc; diff --git a/src/usr/shell.rs b/src/usr/shell.rs index d55185c1..96a8b68a 100644 --- a/src/usr/shell.rs +++ b/src/usr/shell.rs @@ -14,11 +14,11 @@ use alloc::vec::Vec; use alloc::string::{String, ToString}; // TODO: Scan /bin -const AUTOCOMPLETE_COMMANDS: [&str; 35] = [ - "2048", "base64", "calc", "copy", "date", "delete", "dhcp", "disk", "edit", "elf", "env", - "goto", "help", "hex", "host", "http", "httpd", "install", "keyboard", "life", "lisp", - "list", "memory", "move", "net", "pci", "quit", "read", "shell", "socket", "tcp", - "time", "user", "vga", "write" +const AUTOCOMPLETE_COMMANDS: [&str; 36] = [ + "2048", "asm", "base64", "calc", "copy", "date", "delete", "dhcp", "disk", "edit", "elf", + "env", "goto", "help", "hex", "host", "http", "httpd", "install", "keyboard", "life", "lisp", + "list", "memory", "move", "net", "pci", "quit", "read", "shell", "socket", "tcp", "time", + "user", "vga", "write" ]; struct Config { @@ -440,6 +440,7 @@ fn exec_with_config(cmd: &str, config: &mut Config) -> Result<(), ExitCode> { "" => Ok(()), "2048" => usr::pow::main(&args), "alias" => cmd_alias(&args, config), + "asm" => usr::assembler::main(&args), "base64" => usr::base64::main(&args), "beep" => usr::beep::main(&args), "calc" => usr::calc::main(&args),