From 4fd7d68a639ef48cd9d2bd380c1df2cfc461ca66 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Sun, 2 Feb 2020 18:55:20 +0100 Subject: [PATCH] Add network stack (#12) * Display MAC address * Add kernel::pci::find_device * Mask lower bits of 16-bit Memory Space BAR * Use array instead of vector for MAC address * Split interrupts module * Use IRQ constants instead of InterruptIndex enum * Replace kernel::sleep by kernel::time * Add kernel::idt::set_irq_handler * Add interrupt handler for RTL8139 * Enable bus mastering for RTL8139 * Setup NIC * Add features for vga/serial and qwerty/dvorak * Add smoltcp crate * Use EthernetAddress from smoltcp * Add RTL8139 struct to implement smoltcp Device * Save detected device * Add kernel::mem::translate_addr * Use physical address of rx_buffer * Add command to read raw network data * Parse packet header and length * Fix missing ascii on last line * Take CRC into account for packet length * Fix compilation error * Move buffer pointers after packet received * Use buffer slice instead of clone in RxToken * Add packet transmission and dhcp client * Configure network interface with DHCP client * Add debug mode to network interface * Clean dhcp command output * Add ip command * Clean up commands output * Count number of packets transmitted and received * Add route command * Add kernel::random::rand16 * Handle carriage return char * Add HTTP client * Improve http command output * Add DNS resolver command * Parse DNS responses to A IN queries * Resolve http host * Check if interface is ready before operations * Add timeout to polling loops * Fix sleep during polling * Add verbose arg to http command * Add State struct to Device struct * Add subcommand config and dump to net command * Add MTU to RX_BUFFER_LEN when using WRAP * Fix first transmission index * Refactor TxToken implementation * Add user agent to http requests * Add more comments to code * Add llvm-tools-preview component to readme * Add method to translate IRQ into system interrupt * Clear IRQ mask in set_irq_handler * Refactor driver code * Sleep less rather than more * Add rand32 * Disable RTL8139 interrupts * Use arrays instead of vectors for buffers * Add minimum sleep duration * Add phy_addr to dry init * Use CAPR and CBR to compute rx buffer offset * Add debug for alloc issue with continuous physical memory * Fix timeout in loops * Add unused buffer to push the rx buffer into contiguous memory * Add doc about network * Update readme * Add read /net/// subcommand --- Cargo.lock | 18 ++ Cargo.toml | 8 + README.md | 36 ++- doc/net.md | 103 +++++++++ dsk/ini/boot.sh | 1 + run/cool-retro-term.sh | 3 +- src/kernel/clock.rs | 15 +- src/kernel/console.rs | 14 +- src/kernel/idt.rs | 109 +++++++++ src/kernel/interrupts.rs | 74 ------ src/kernel/keyboard.rs | 28 ++- src/kernel/mem.rs | 28 ++- src/kernel/mod.rs | 6 +- src/kernel/pci.rs | 124 ++++++++--- src/kernel/pic.rs | 7 + src/kernel/random.rs | 14 ++ src/kernel/rtl8139.rs | 470 +++++++++++++++++++++++++++++++++++++++ src/kernel/sleep.rs | 12 - src/kernel/time.rs | 33 +++ src/kernel/vga.rs | 4 +- src/lib.rs | 12 +- src/main.rs | 3 +- src/user/dhcp.rs | 92 ++++++++ src/user/halt.rs | 2 +- src/user/hex.rs | 35 +-- src/user/host.rs | 234 +++++++++++++++++++ src/user/http.rs | 184 +++++++++++++++ src/user/ip.rs | 18 ++ src/user/login.rs | 4 +- src/user/mod.rs | 6 + src/user/net.rs | 76 +++++++ src/user/read.rs | 25 ++- src/user/route.rs | 17 ++ src/user/shell.rs | 8 +- src/user/sleep.rs | 2 +- 35 files changed, 1643 insertions(+), 182 deletions(-) create mode 100644 doc/net.md create mode 100644 src/kernel/idt.rs delete mode 100644 src/kernel/interrupts.rs create mode 100644 src/kernel/pic.rs create mode 100644 src/kernel/rtl8139.rs delete mode 100644 src/kernel/sleep.rs create mode 100644 src/kernel/time.rs create mode 100644 src/user/dhcp.rs create mode 100644 src/user/host.rs create mode 100644 src/user/http.rs create mode 100644 src/user/ip.rs create mode 100644 src/user/net.rs create mode 100644 src/user/route.rs diff --git a/Cargo.lock b/Cargo.lock index d854c1b..6c5a092 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,6 +155,12 @@ dependencies = [ "spin", ] +[[package]] +name = "managed" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" + [[package]] name = "moros" version = "0.1.0" @@ -170,6 +176,7 @@ dependencies = [ "pic8259_simple", "raw-cpuid", "sha2", + "smoltcp", "spin", "uart_16550", "volatile", @@ -259,6 +266,17 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "smoltcp" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" +dependencies = [ + "bitflags", + "byteorder", + "managed", +] + [[package]] name = "spin" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index 4738577..ab2206c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,13 @@ license = "MIT" repository = "https://github.com/vinc/moros" readme = "README.md" +[features] +default = ["vga", "qwerty"] +vga = [] +serial = [] +qwerty = [] +dvorak = [] + [dependencies] bootloader = { version = "0.8.3", features = ["map_physical_memory"]} linked_list_allocator = "0.6.4" @@ -24,3 +31,4 @@ base64 = { version = "0.11", default-features = false } pbkdf2 = { version = "0.3", default-features = false } sha2 = { version = "0.8", default-features = false } hmac = { version = "0.7", default-features = false } +smoltcp = { version = "0.6", default-features = false, features = ["alloc", "ethernet", "socket-tcp", "socket-udp", "proto-ipv4", "proto-dhcpv4"] } diff --git a/README.md b/README.md index fcabc72..7e1079c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,30 @@ 11 10 10 `1001' 00 01 `0110' `01101' ``` -MOROS is a toy operating system written in Rust. +MOROS is a toy operating system written in Rust for the x86 architecture. + +## Implemented + +- [x] Hardware interrupts +- [x] PS/2 Keyboard (qwerty and dvorak) +- [x] VGA Text mode +- [x] Paging +- [x] Heap allocation +- [x] RTC clock +- [x] PCI enumeration +- [x] ATA PIO mode +- [x] Random number generator +- [x] RTL8139 network card +- [x] IP/TCP/UDP/DHCP/DNS/HTTP protocols +- [x] Basic filesystem +- [x] Basic shell +- [x] Basic text editor +- [x] Basic file and network commands +- [x] A LOT OF SHORTCUTS TO GET EVERYTHING WORKING +- [x] HERE BE DRAGONS +- [ ] Processes +- [ ] Multitasking +- [ ] A real userspace ## Usage @@ -19,6 +42,7 @@ Install tools: rustup install nightly rustup default nightly rustup component add rust-src + rustup component add llvm-tools-preview cargo install cargo-xbuild bootimage Create disk: @@ -28,20 +52,20 @@ Create disk: Run with: cargo xrun --release -- \ - -cpu max \ + -cpu phenom \ -rtc base=localtime \ -nic model=rtl8139 \ -hdc disk.img Or with a serial console: - cargo xrun --release -- \ - -cpu max \ + cargo xrun --release --no-default-features --features serial,dvorak -- \ + -cpu phenom \ -rtc base=localtime \ -nic model=rtl8139 \ - -hdc disk.img \ + -serial stdio \ -display none \ - -serial stdio + -hdc disk.img Or with `cool-retro-term` for a retro console look: diff --git a/doc/net.md b/doc/net.md new file mode 100644 index 0000000..5c6a333 --- /dev/null +++ b/doc/net.md @@ -0,0 +1,103 @@ +# MOROS Net + +## NET + +The `net` command allows you to configure your network interface: + + > net config debug on + +And listen what is happening on the network: + + > net dump + ------------------------------------------------------------------ + [488.396667] NET RTL8139 receiving buffer: + + Command Register: 0x0C + Interrupt Status Register: 0x05 + CAPR: 584 + CBR: 720 + Header: 0x0001 + Length: 110 bytes + CRC: 0x746CF28C + RX Offset: 600 + + 00000000: 3333 0000 0001 5256 0000 0002 86DD 6000 33....RV......`. + 00000010: 0000 0038 3AFF FE80 0000 0000 0000 0000 ...8:........... + 00000020: 0000 0000 0002 FF02 0000 0000 0000 0000 ................ + 00000030: 0000 0000 0001 8600 155E 4000 0708 0000 .........^@..... + 00000040: 0000 0000 0000 0101 5256 0000 0002 0304 ........RV...... + 00000050: 40C0 0001 5180 0000 3840 0000 0000 FEC0 @...Q...8@...... + 00000060: 0000 0000 0000 0000 0000 0000 0000 .............. + ------------------------------------------------------------------ + [543.871322] NET RTL8139 receiving buffer: + + Command Register: 0x0C + Interrupt Status Register: 0x05 + CAPR: 704 + CBR: 788 + Header: 0x0001 + Length: 60 bytes + CRC: 0x921D3956 + RX Offset: 720 + + 00000000: 5254 0012 3456 5255 0A00 0202 0800 4500 RT..4VRU .....E. + 00000010: 002C 0001 0000 4006 62BB 0A00 0202 0A00 .,....@.b. ... . + 00000020: 020F A2E8 0016 0412 F801 0000 0000 6002 ..............`. + 00000030: 2238 BECB 0000 0204 05B4 0000 "8.......... + ------------------------------------------------------------------ + +## DHCP + +The `dhcp` command configures your network automatically: + + > dhcp + DHCP Discover transmitted + DHCP Offer received + Leased: 10.0.2.15/24 + Router: 10.0.2.2 + DNS: 10.0.2.3 + +## IP + +The `ip` command displays information about your IP address: + + > ip + Link: 52-54-00-12-34-56 + Addr: 10.0.2.15/24 + RX packets: 1 + TX packets: 1 + RX bytes: 590 + TX bytes: 299 + +NOTE: It will later allow you to set it. + +## ROUTE + +The `route` command displays the IP routing table: + + > route + Destination Gateway + 0.0.0.0/0 10.0.2.2 + +NOTE: It will later allow you to manipulate it. + +## HOST + +The `host` command performs DNS lookups: + + > host example.com + example.com has address 93.184.216.34 + +## HTTP + +Requesting a resource on a host: + + > http example.com /articles/index.html + +Is equivalent to: + + > read /net/http/example.com/articles + +And: + + > read /net/http/example.com:80/articles/index.html diff --git a/dsk/ini/boot.sh b/dsk/ini/boot.sh index 472c2e6..e429daa 100644 --- a/dsk/ini/boot.sh +++ b/dsk/ini/boot.sh @@ -1,4 +1,5 @@ read /ini/banner.txt +# dhcp login shell clear diff --git a/run/cool-retro-term.sh b/run/cool-retro-term.sh index 093e68c..7200569 100755 --- a/run/cool-retro-term.sh +++ b/run/cool-retro-term.sh @@ -4,7 +4,8 @@ set -e dir=$(dirname "$0") image="target/x86_64-moros/release/bootimage-moros.bin" -qemu="qemu-system-x86_64 -display curses -cpu max -nic model=rtl8139 -rtc base=localtime -hdc disk.img" +#qemu="qemu-system-x86_64 -display curses -cpu max -nic model=rtl8139 -rtc base=localtime -hdc disk.img" +qemu="qemu-system-x86_64 -display curses -cpu max -rtc base=localtime -hdc disk.img -netdev user,id=u1,hostfwd=tcp::2222-:22 -device rtl8139,netdev=u1 -object filter-dump,id=f1,netdev=u1,file=/tmp/qemu.pcap" # Build image if needed cd "$dir/.." && cargo bootimage --release diff --git a/src/kernel/clock.rs b/src/kernel/clock.rs index 643db78..4cbb484 100644 --- a/src/kernel/clock.rs +++ b/src/kernel/clock.rs @@ -1,23 +1,12 @@ +use crate::kernel; use crate::kernel::cmos::CMOS; -use lazy_static::lazy_static; -use spin::Mutex; const DAYS_BEFORE_MONTH: [u64; 13] = [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 ]; -lazy_static! { - pub static ref TICKS: Mutex = Mutex::new(0); -} - -pub fn tick() { - let mut ticks = TICKS.lock(); - *ticks += 1; -} - pub fn clock_monotonic() -> f64 { - let ticks = *TICKS.lock(); - 1.0 / (1.193182 * 1000000.0 / 65536.0) * ticks as f64 + 1.0 / (1.193182 * 1000000.0 / 65536.0) * kernel::time::ticks() as f64 } pub fn clock_realtime() -> f64 { diff --git a/src/kernel/console.rs b/src/kernel/console.rs index 77181b6..3ec7ea9 100644 --- a/src/kernel/console.rs +++ b/src/kernel/console.rs @@ -11,11 +11,19 @@ lazy_static! { pub static ref RAW: Mutex = Mutex::new(false); } +#[cfg(feature="vga")] #[macro_export] macro_rules! print { ($($arg:tt)*) => ({ $crate::kernel::vga::print_fmt(format_args!($($arg)*)); - //$crate::kernel::serial::print_fmt(format_args!($($arg)*)); + }); +} + +#[cfg(feature="serial")] +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ({ + $crate::kernel::serial::print_fmt(format_args!($($arg)*)); }); } @@ -84,7 +92,7 @@ pub fn get_char() -> char { kernel::console::disable_echo(); kernel::console::enable_raw(); loop { - kernel::sleep::halt(); + kernel::time::halt(); let res = interrupts::without_interrupts(|| { let mut stdin = STDIN.lock(); match stdin.chars().next_back() { @@ -107,7 +115,7 @@ pub fn get_char() -> char { pub fn get_line() -> String { loop { - kernel::sleep::halt(); + kernel::time::halt(); let res = interrupts::without_interrupts(|| { let mut stdin = STDIN.lock(); match stdin.chars().next_back() { diff --git a/src/kernel/idt.rs b/src/kernel/idt.rs new file mode 100644 index 0000000..526cb5f --- /dev/null +++ b/src/kernel/idt.rs @@ -0,0 +1,109 @@ +use crate::{print, kernel}; +use lazy_static::lazy_static; +use spin::Mutex; +use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; +use x86_64::instructions::interrupts; + +// Translate IRQ into system interrupt +fn interrupt_index(irq: u8) -> u8 { + kernel::pic::PIC_1_OFFSET + irq +} + +fn default_handler() { + return; +} + +macro_rules! irq_handler { + ($handler:ident, $irq:expr) => { + pub extern "x86-interrupt" fn $handler(_stack_frame: &mut InterruptStackFrame) { + let handlers = IRQ_HANDLERS.lock(); + handlers[$irq](); + unsafe { kernel::pic::PICS.lock().notify_end_of_interrupt(interrupt_index($irq)); } + } + }; +} + +irq_handler!(irq0_handler, 0); +irq_handler!(irq1_handler, 1); +irq_handler!(irq2_handler, 2); +irq_handler!(irq3_handler, 3); +irq_handler!(irq4_handler, 4); +irq_handler!(irq5_handler, 5); +irq_handler!(irq6_handler, 6); +irq_handler!(irq7_handler, 7); +irq_handler!(irq8_handler, 8); +irq_handler!(irq9_handler, 9); +irq_handler!(irq10_handler, 10); +irq_handler!(irq11_handler, 11); +irq_handler!(irq12_handler, 12); +irq_handler!(irq13_handler, 13); +irq_handler!(irq14_handler, 14); +irq_handler!(irq15_handler, 15); + +lazy_static! { + pub static ref IRQ_HANDLERS: Mutex<[fn(); 16]> = Mutex::new([default_handler; 16]); + + static ref IDT: InterruptDescriptorTable = { + let mut idt = InterruptDescriptorTable::new(); + idt.breakpoint.set_handler_fn(breakpoint_handler); + unsafe { + idt.double_fault.set_handler_fn(double_fault_handler).set_stack_index(kernel::gdt::DOUBLE_FAULT_IST_INDEX); + } + idt[interrupt_index(0) as usize].set_handler_fn(irq0_handler); + idt[interrupt_index(1) as usize].set_handler_fn(irq1_handler); + idt[interrupt_index(2) as usize].set_handler_fn(irq2_handler); + idt[interrupt_index(3) as usize].set_handler_fn(irq3_handler); + idt[interrupt_index(4) as usize].set_handler_fn(irq4_handler); + idt[interrupt_index(5) as usize].set_handler_fn(irq5_handler); + idt[interrupt_index(6) as usize].set_handler_fn(irq6_handler); + idt[interrupt_index(7) as usize].set_handler_fn(irq7_handler); + idt[interrupt_index(8) as usize].set_handler_fn(irq8_handler); + idt[interrupt_index(9) as usize].set_handler_fn(irq9_handler); + idt[interrupt_index(10) as usize].set_handler_fn(irq10_handler); + idt[interrupt_index(11) as usize].set_handler_fn(irq11_handler); + idt[interrupt_index(12) as usize].set_handler_fn(irq12_handler); + idt[interrupt_index(13) as usize].set_handler_fn(irq13_handler); + idt[interrupt_index(14) as usize].set_handler_fn(irq14_handler); + idt[interrupt_index(15) as usize].set_handler_fn(irq15_handler); + idt + }; +} + +pub fn init() { + IDT.load(); +} + +use x86_64::instructions::port::Port; + +pub fn set_irq_handler(irq: u8, handler: fn()) { + interrupts::without_interrupts(|| { + let mut handlers = IRQ_HANDLERS.lock(); + handlers[irq as usize] = handler; + + clear_irq_mask(irq); + }); +} + +pub fn set_irq_mask(irq: u8) { + let mut port: Port = Port::new(if irq < 8 { 0x21 } else { 0xA1 } ); + unsafe { + let value = port.read() & (1 << irq); + port.write(value); + } +} + +pub fn clear_irq_mask(irq: u8) { + let mut port: Port = Port::new(if irq < 8 { 0x21 } else { 0xA1 } ); + unsafe { + let value = port.read() & !(1 << irq); + port.write(value); + } +} + +extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFrame) { + print!("EXCEPTION: BREAKPOINT\n{:#?}\n", stack_frame); +} + +extern "x86-interrupt" fn double_fault_handler(stack_frame: &mut InterruptStackFrame, _error_code: u64) -> ! { + panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); +} diff --git a/src/kernel/interrupts.rs b/src/kernel/interrupts.rs deleted file mode 100644 index 3c64028..0000000 --- a/src/kernel/interrupts.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::{print, kernel}; -use lazy_static::lazy_static; -use pic8259_simple::ChainedPics; -use spin::Mutex; -use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; - -pub const PIC_1_OFFSET: u8 = 32; -pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; - -#[derive(Debug, Clone, Copy)] -#[repr(u8)] -pub enum InterruptIndex { - Timer = PIC_1_OFFSET, - Keyboard = PIC_1_OFFSET + 1 -} - -impl InterruptIndex { - fn as_u8(self) -> u8 { - self as u8 - } - - fn as_usize(self) -> usize { - usize::from(self.as_u8()) - } -} - -pub static PICS: Mutex = Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) }); - -lazy_static! { - static ref IDT: InterruptDescriptorTable = { - let mut idt = InterruptDescriptorTable::new(); - idt.breakpoint.set_handler_fn(breakpoint_handler); - unsafe { - idt.double_fault.set_handler_fn(double_fault_handler).set_stack_index(kernel::gdt::DOUBLE_FAULT_IST_INDEX); - } - idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler); - idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard_interrupt_handler); - idt - }; -} - -pub fn init_idt() { - IDT.load(); -} - -extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFrame) { - print!("EXCEPTION: BREAKPOINT\n{:#?}\n", stack_frame); -} - -extern "x86-interrupt" fn double_fault_handler(stack_frame: &mut InterruptStackFrame, _error_code: u64) -> ! { - panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); -} - -extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: &mut InterruptStackFrame) { - kernel::clock::tick(); - - unsafe { - PICS.lock().notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); - } -} - -extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut InterruptStackFrame) { - let mut keyboard = kernel::keyboard::KEYBOARD.lock(); - let scancode = kernel::keyboard::read_scancode(); - if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { - if let Some(key) = keyboard.process_keyevent(key_event) { - kernel::console::key_handle(key); - } - } - - unsafe { - PICS.lock().notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8()); - } -} diff --git a/src/kernel/keyboard.rs b/src/kernel/keyboard.rs index e45e607..340cba3 100644 --- a/src/kernel/keyboard.rs +++ b/src/kernel/keyboard.rs @@ -1,19 +1,26 @@ -//use crate::{print, kernel}; +use crate::kernel; use lazy_static::lazy_static; use pc_keyboard::{Keyboard, ScancodeSet1, HandleControl, layouts}; use spin::Mutex; use x86_64::instructions::port::Port; +// TODO: Support layout change from userspace +#[cfg(feature="qwerty")] lazy_static! { - // NOTE: Replace `Dvorak104Key` with `Us104Key` for Qwerty keyboards - // TODO: Support layout change from userspace pub static ref KEYBOARD: Mutex> = Mutex::new( Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::MapLettersToUnicode) ); } -/* +#[cfg(feature="dvorak")] +lazy_static! { + pub static ref KEYBOARD: Mutex> = Mutex::new( + Keyboard::new(layouts::Dvorak104Key, ScancodeSet1, HandleControl::MapLettersToUnicode) + ); +} + pub fn init() { + /* let mut port = Port::new(0x60); // Identify @@ -61,8 +68,9 @@ pub fn init() { return init(); } print!("[{:.6}] keyboard: switch to scancode set 2\n", kernel::clock::clock_monotonic()); + */ + kernel::idt::set_irq_handler(1, interrupt_handler); } -*/ pub fn read_scancode() -> u8 { let mut port = Port::new(0x60); @@ -70,3 +78,13 @@ pub fn read_scancode() -> u8 { port.read() } } + +fn interrupt_handler() { + let mut keyboard = KEYBOARD.lock(); + let scancode = read_scancode(); + if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { + if let Some(key) = keyboard.process_keyevent(key_event) { + kernel::console::key_handle(key); + } + } +} diff --git a/src/kernel/mem.rs b/src/kernel/mem.rs index c205dd0..0606283 100644 --- a/src/kernel/mem.rs +++ b/src/kernel/mem.rs @@ -1,12 +1,14 @@ use bootloader::bootinfo::{BootInfo, MemoryMap, MemoryRegionType}; use crate::{print, kernel}; -use x86_64::{ - structures::paging::{ - FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB, - UnusedPhysFrame, - }, - PhysAddr, VirtAddr, -}; +use lazy_static::lazy_static; +use spin::Mutex; +use x86_64::structures::paging::mapper::MapperAllSizes; +use x86_64::structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB, UnusedPhysFrame}; +use x86_64::{PhysAddr, VirtAddr}; + +lazy_static! { + static ref PHYS_MEM_OFFSET: Mutex = Mutex::new(VirtAddr::new(0)); +} pub fn init(boot_info: &'static BootInfo) { let mut mem_total = 0; @@ -19,9 +21,17 @@ pub fn init(boot_info: &'static BootInfo) { print!("[{:.6}] MEM {} KB\n", kernel::clock::clock_monotonic(), mem_total >> 10); let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset); - let mut mapper = unsafe { kernel::mem::mapper(phys_mem_offset) }; - let mut frame_allocator = unsafe { kernel::mem::BootInfoFrameAllocator::init(&boot_info.memory_map) }; + let mut mapper = unsafe { mapper(phys_mem_offset) }; + let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_map) }; kernel::allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed"); + + *PHYS_MEM_OFFSET.lock() = phys_mem_offset; +} + +pub fn translate_addr(addr: VirtAddr) -> Option { + let phys_mem_offset = *PHYS_MEM_OFFSET.lock(); + let mapper = unsafe { mapper(phys_mem_offset) }; + mapper.translate_addr(addr) } pub unsafe fn mapper(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> { diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index c6fbae4..0e3db43 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -7,11 +7,13 @@ pub mod console; pub mod cpu; pub mod fs; pub mod gdt; -pub mod interrupts; +pub mod idt; pub mod keyboard; pub mod mem; pub mod pci; +pub mod pic; pub mod random; +pub mod rtl8139; pub mod serial; -pub mod sleep; +pub mod time; pub mod vga; diff --git a/src/kernel/pci.rs b/src/kernel/pci.rs index 1331656..689a600 100644 --- a/src/kernel/pci.rs +++ b/src/kernel/pci.rs @@ -5,23 +5,74 @@ use lazy_static::lazy_static; use spin::Mutex; use x86_64::instructions::port::Port; -const CONFIG_ADDR: u16 = 0xCF8; -const CONFIG_DATA: u16 = 0xCFC; - -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct DeviceConfig { pub bus: u8, pub device: u8, pub function: u8, pub vendor_id: u16, pub device_id: u16, + pub status: u16, + pub command: u16, pub base_addresses: [u32; 6], + pub interrupt_pin: u8, + pub interrupt_line: u8, +} + +impl DeviceConfig { + pub fn new(bus: u8, device: u8, function: u8) -> Self { + let vendor_id = get_vendor_id(bus, device, function); + let device_id = get_device_id(bus, device, function); + + let mut register = ConfigRegister::new(bus, device, function, 0x04); + let data = register.read(); + let command = data.get_bits(0..16) as u16; + let status = data.get_bits(16..32) as u16; + + let mut register = ConfigRegister::new(bus, device, function, 0x3C); + let data = register.read(); + let interrupt_line = data.get_bits(0..8) as u8; + let interrupt_pin = data.get_bits(8..16) as u8; + + let mut base_addresses: [u32; 6] = [0; 6]; + for i in 0..6 { + let offset = 0x10 + ((i as u8) << 2); + let mut register = ConfigRegister::new(bus, device, function, offset); + base_addresses[i] = register.read(); + } + + Self { + bus, device, function, + + // Configuration Space registers + vendor_id, device_id, + status, command, + base_addresses, + interrupt_pin, interrupt_line, + } + } + + pub fn enable_bus_mastering(&mut self) { + let mut register = ConfigRegister::new(self.bus, self.device, self.function, 0x04); + let mut data = register.read(); + data.set_bit(2, true); + register.write(data); + } } lazy_static! { pub static ref PCI_DEVICES: Mutex> = Mutex::new(Vec::new()); } +pub fn find_device(vendor_id: u16, device_id: u16) -> Option { + for &device in PCI_DEVICES.lock().iter() { + if device.vendor_id == vendor_id && device.device_id == device_id { + return Some(device); + } + } + None +} + pub fn init() { for bus in 0..256 { check_bus(bus as u8); @@ -56,45 +107,60 @@ fn check_device(bus: u8, device: u8) { } fn add_device(bus: u8, device: u8, function: u8) { - let vendor_id = get_vendor_id(bus, device, function); - let device_id = get_device_id(bus, device, function); - - let mut base_addresses: [u32; 6] = [0; 6]; - for i in 0..6 { - let offset = 0x10 + ((i as u8) << 2); - base_addresses[i] = read_config(bus, device, function, offset); - } + let config = DeviceConfig::new(bus, device, function); + PCI_DEVICES.lock().push(config); let uptime = kernel::clock::clock_monotonic(); - print!("[{:.6}] PCI {:04}:{:02}:{:02} [{:04X}:{:04X}]\n", uptime, bus, device, function, vendor_id, device_id); - let device_config = DeviceConfig { bus, device, function, vendor_id, device_id, base_addresses }; - PCI_DEVICES.lock().push(device_config); + print!( + "[{:.6}] PCI {:04}:{:02}:{:02} [{:04X}:{:04X}]\n", + uptime, bus, device, function, config.vendor_id, config.device_id + ); } fn get_vendor_id(bus: u8, device: u8, function: u8) -> u16 { - read_config(bus, device, function, 0x00).get_bits(0..16) as u16 + let mut register = ConfigRegister::new(bus, device, function, 0x00); + register.read().get_bits(0..16) as u16 } fn get_device_id(bus: u8, device: u8, function: u8) -> u16 { - read_config(bus, device, function, 0x00).get_bits(16..32) as u16 + let mut register = ConfigRegister::new(bus, device, function, 0x00); + register.read().get_bits(16..32) as u16 } fn get_header_type(bus: u8, device: u8, function: u8) -> u8 { - read_config(bus, device, function, 0x0C).get_bits(16..24) as u8 + let mut register = ConfigRegister::new(bus, device, function, 0x0C); + register.read().get_bits(16..24) as u8 } -fn read_config(bus: u8, device: u8, function: u8, offset: u8) -> u32 { - let addr = 0x8000_0000 - | ((bus as u32) << 16) - | ((device as u32) << 11) - | ((function as u32) << 8) - | ((offset as u32) & 0xFC); +struct ConfigRegister { + data_port: Port, + addr_port: Port, + addr: u32, +} - let mut addr_port = Port::new(CONFIG_ADDR); - let mut data_port = Port::new(CONFIG_DATA); +impl ConfigRegister { + pub fn new(bus: u8, device: u8, function: u8, offset: u8) -> Self { + Self { + data_port: Port::new(0xCFC), + addr_port: Port::new(0xCF8), + addr: 0x8000_0000 | ((bus as u32) << 16) + | ((device as u32) << 11) + | ((function as u32) << 8) + | ((offset as u32) & 0xFC), + } + } - unsafe { - addr_port.write(addr); - data_port.read() + pub fn read(&mut self) -> u32 { + unsafe { + self.addr_port.write(self.addr); + self.data_port.read() + } + } + + pub fn write(&mut self, data: u32) { + unsafe { + self.addr_port.write(self.addr); + self.data_port.write(data); + } } } diff --git a/src/kernel/pic.rs b/src/kernel/pic.rs new file mode 100644 index 0000000..abe8bba --- /dev/null +++ b/src/kernel/pic.rs @@ -0,0 +1,7 @@ +use pic8259_simple::ChainedPics; +use spin::Mutex; + +pub const PIC_1_OFFSET: u8 = 32; +pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; + +pub static PICS: Mutex = Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) }); diff --git a/src/kernel/random.rs b/src/kernel/random.rs index 7eb4166..bc917b0 100644 --- a/src/kernel/random.rs +++ b/src/kernel/random.rs @@ -6,3 +6,17 @@ pub fn rand64() -> Option { None => None } } + +pub fn rand32() -> Option { + match RdRand::new() { + Some(rand) => rand.get_u32(), + None => None + } +} + +pub fn rand16() -> Option { + match RdRand::new() { + Some(rand) => rand.get_u16(), + None => None + } +} diff --git a/src/kernel/rtl8139.rs b/src/kernel/rtl8139.rs new file mode 100644 index 0000000..e77b344 --- /dev/null +++ b/src/kernel/rtl8139.rs @@ -0,0 +1,470 @@ +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use core::cell::RefCell; +use core::convert::TryInto; +use crate::{print, kernel}; +use crate::user; +use lazy_static::lazy_static; +use smoltcp::Result; +use smoltcp::iface::{EthernetInterface, EthernetInterfaceBuilder, NeighborCache, Routes}; +use smoltcp::phy::{Device, DeviceCapabilities}; +use smoltcp::phy; +use smoltcp::time::Instant; +use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address}; +use spin::Mutex; +use x86_64::instructions::port::Port; +use x86_64::VirtAddr; + +// 00 = 8K + 16 bytes +// 01 = 16K + 16 bytes +// 10 = 32K + 16 bytes +// 11 = 64K + 16 bytes +const RX_BUFFER_IDX: usize = 0; + +const MTU: usize = 1500; + +const RX_BUFFER_PAD: usize = 16; +const RX_BUFFER_LEN: usize = (8129 << RX_BUFFER_IDX) + RX_BUFFER_PAD; + +const TX_BUFFER_LEN: usize = 4096; +const TX_BUFFERS_COUNT: usize = 4; +const ROK: u16 = 0x01; + +const CR_RST: u8 = 1 << 4; // Reset +const CR_RE: u8 = 1 << 3; // Receiver Enable +const CR_TE: u8 = 1 << 2; // Transmitter Enable +const CR_BUFE: u8 = 1 << 0; // Buffer Empty + +// Rx Buffer Length +const RCR_RBLEN: u32 = (RX_BUFFER_IDX << 11) as u32; + +// When the WRAP bit is set, the nic will keep moving the rest +// of the packet data into the memory immediately after the +// end of the Rx buffer instead of going back to the begining +// of the buffer. So the buffer must have an additionnal 1500 bytes. +const RCR_WRAP: u32 = 1 << 7; + +const RCR_AB: u32 = 1 << 3; // Accept Broadcast packets +const RCR_AM: u32 = 1 << 2; // Accept Multicast packets +const RCR_APM: u32 = 1 << 1; // Accept Physical Match packets +const RCR_AAP: u32 = 1 << 0; // Accept All Packets + +// Interframe Gap Time +const TCR_IFG: u32 = 3 << 24; + +// Max DMA Burst Size per Tx DMA Burst +// 000 = 16 bytes +// 001 = 32 bytes +// 010 = 64 bytes +// 011 = 128 bytes +// 100 = 256 bytes +// 101 = 512 bytes +// 110 = 1024 bytes +// 111 = 2048 bytes +const TCR_MXDMA0: u32 = 1 << 8; +const TCR_MXDMA1: u32 = 1 << 9; +const TCR_MXDMA2: u32 = 1 << 10; + +// Interrupt Mask Register +const IMR_TOK: u16 = 1 << 2; // Transmit OK Interrupt +const IMR_ROK: u16 = 1 << 0; // Receive OK Interrupt + +lazy_static! { + pub static ref IFACE: Mutex>> = Mutex::new(None); +} + +pub struct Ports { + pub idr: [Port; 6], // ID Registers (IDR0 ... IDR5) + pub tx_cmds: [Port; TX_BUFFERS_COUNT], // Transmit Status of Descriptors (TSD0 .. TSD3) + pub tx_addrs: [Port; TX_BUFFERS_COUNT], // Transmit Start Address of Descriptor0 (TSAD0 .. TSAD3) + pub config1: Port, // Configuration Register 1 (CONFIG1) + pub rx_addr: Port, // Receive (Rx) Buffer Start Address (RBSTART) + pub capr: Port, // Current Address of Packet Read (CAPR) + pub cbr: Port, // Current Buffer Address (CBR) + pub cmd: Port, // Command Register (CR) + pub imr: Port, // Interrupt Mask Register (IMR) + pub isr: Port, // Interrupt Status Register (ISR) + pub tx_config: Port, // Transmit (Tx) Configuration Register (TCR) + pub rx_config: Port, // Receive (Rx) Configuration Register (RCR) +} + +impl Ports { + pub fn new(io_addr: u16) -> Self { + Self { + idr: [ + Port::new(io_addr + 0x00), + Port::new(io_addr + 0x01), + Port::new(io_addr + 0x02), + Port::new(io_addr + 0x03), + Port::new(io_addr + 0x04), + Port::new(io_addr + 0x05), + ], + tx_cmds: [ + Port::new(io_addr + 0x10), + Port::new(io_addr + 0x14), + Port::new(io_addr + 0x18), + Port::new(io_addr + 0x1C), + ], + tx_addrs: [ + Port::new(io_addr + 0x20), + Port::new(io_addr + 0x24), + Port::new(io_addr + 0x28), + Port::new(io_addr + 0x2C), + ], + config1: Port::new(io_addr + 0x52), + rx_addr: Port::new(io_addr + 0x30), + capr: Port::new(io_addr + 0x38), + cbr: Port::new(io_addr + 0x3A), + cmd: Port::new(io_addr + 0x37), + imr: Port::new(io_addr + 0x3C), + isr: Port::new(io_addr + 0x3E), + tx_config: Port::new(io_addr + 0x40), + rx_config: Port::new(io_addr + 0x44), + } + } +} + +pub struct State { + rx_bytes_count: u64, + tx_bytes_count: u64, + rx_packets_count: u64, + tx_packets_count: u64, +} + +pub struct RTL8139 { + state: RefCell, + ports: Ports, + eth_addr: Option, + xx_buffer: Box<[u8; 4 << 10]>, // TODO: Remove this + rx_buffer: Box<[u8; RX_BUFFER_LEN + MTU]>, + rx_offset: usize, + tx_buffers: [Box<[u8; TX_BUFFER_LEN]>; TX_BUFFERS_COUNT], // TODO: Remove this + tx_id: usize, + pub debug_mode: bool, +} + +impl RTL8139 { + pub fn new(io_addr: u16) -> Self { + let state = State { + rx_bytes_count: 0, + tx_bytes_count: 0, + rx_packets_count: 0, + tx_packets_count: 0, + }; + Self { + state: RefCell::new(state), + ports: Ports::new(io_addr), + eth_addr: None, + + xx_buffer: Box::new([0; 4 << 10]), // TODO: Remove this + // Add MTU to RX_BUFFER_LEN if RCR_WRAP is set + rx_buffer: Box::new([0; RX_BUFFER_LEN + MTU]), + + rx_offset: 0, + tx_buffers: [ + Box::new([0; TX_BUFFER_LEN]), + Box::new([0; TX_BUFFER_LEN]), + Box::new([0; TX_BUFFER_LEN]), + Box::new([0; TX_BUFFER_LEN]), + ], + + // Before a transmission begin the id is incremented, + // so the first transimission will start at 0. + tx_id: TX_BUFFERS_COUNT - 1, + + debug_mode: false, + } + } + + pub fn init(&mut self) { + // Power on + unsafe { self.ports.config1.write(0) } + + // Software reset + unsafe { + self.ports.cmd.write(CR_RST); + while self.ports.cmd.read() & CR_RST != 0 {} + } + + // Enable Receive and Transmitter + unsafe { self.ports.cmd.write(CR_RE | CR_TE) } + + // Read MAC addr + let mac = unsafe { + [ + self.ports.idr[0].read(), + self.ports.idr[1].read(), + self.ports.idr[2].read(), + self.ports.idr[3].read(), + self.ports.idr[4].read(), + self.ports.idr[5].read(), + ] + }; + self.eth_addr = Some(EthernetAddress::from_bytes(&mac)); + + // FIXME: The buffers must be in a contiguous physical memory + //print!("xx_buffer[0000]=0x{:08X}\n", phys_addr(&self.xx_buffer[0000])); + //print!("xx_buffer[1000]=0x{:08X}\n", phys_addr(&self.xx_buffer[1000])); + //print!("xx_buffer[2000]=0x{:08X}\n", phys_addr(&self.xx_buffer[2000])); + //print!("xx_buffer[3000]=0x{:08X}\n", phys_addr(&self.xx_buffer[3000])); + //print!("xx_buffer[4000]=0x{:08X}\n", phys_addr(&self.xx_buffer[4000])); + //print!("rx_buffer[0000]=0x{:08X}\n", phys_addr(&self.rx_buffer[0000])); + //print!("rx_buffer[1000]=0x{:08X}\n", phys_addr(&self.rx_buffer[1000])); + //print!("rx_buffer[2000]=0x{:08X}\n", phys_addr(&self.rx_buffer[2000])); + //print!("rx_buffer[3000]=0x{:08X}\n", phys_addr(&self.rx_buffer[3000])); + //print!("rx_buffer[4000]=0x{:08X}\n", phys_addr(&self.rx_buffer[4000])); + //print!("rx_buffer[5000]=0x{:08X}\n", phys_addr(&self.rx_buffer[5000])); + //print!("rx_buffer[6000]=0x{:08X}\n", phys_addr(&self.rx_buffer[6000])); + //print!("rx_buffer[7000]=0x{:08X}\n", phys_addr(&self.rx_buffer[7000])); + //print!("rx_buffer[8000]=0x{:08X}\n", phys_addr(&self.rx_buffer[8000])); + //print!("rx_buffer[9000]=0x{:08X}\n", phys_addr(&self.rx_buffer[9000])); + //let buffer_len = self.rx_buffer.len(); + //let memory_len = phys_addr(&self.rx_buffer[n - 1]) - phys_addr(&self.rx_buffer[0]); + //assert!(buffer_len == memory_len as usize, "{} != {}", buffer_len, memory_len); + + // Get physical address of rx_buffer + let rx_addr = phys_addr(&self.rx_buffer[0]); + + // Init Receive buffer + unsafe { self.ports.rx_addr.write(rx_addr as u32) } + + for i in 0..4 { + // Get physical address of each tx_buffer + let tx_addr = phys_addr(&self.tx_buffers[i][0]); + + // Init Transmit buffer + unsafe { self.ports.tx_addrs[i].write(tx_addr as u32) } + } + + // Set interrupts + unsafe { self.ports.imr.write(IMR_TOK | IMR_ROK) } + + // Configure receive buffer (RCR) + unsafe { self.ports.rx_config.write(RCR_RBLEN | RCR_WRAP | RCR_AB | RCR_AM | RCR_APM | RCR_AAP) } + + // Configure transmit buffer (TCR) + unsafe { self.ports.tx_config.write(TCR_IFG | TCR_MXDMA0 | TCR_MXDMA1 | TCR_MXDMA2); } + } + + pub fn rx_bytes_count(&self) -> u64 { + self.state.borrow().rx_bytes_count + } + pub fn tx_bytes_count(&self) -> u64 { + self.state.borrow().tx_bytes_count + } + pub fn rx_packets_count(&self) -> u64 { + self.state.borrow().rx_packets_count + } + pub fn tx_packets_count(&self) -> u64 { + self.state.borrow().tx_packets_count + } +} + +impl<'a> Device<'a> for RTL8139 { + type RxToken = RxToken<'a>; + type TxToken = TxToken<'a>; + + fn capabilities(&self) -> DeviceCapabilities { + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = MTU; + caps.max_burst_size = Some(1); + caps + } + + // RxToken buffer, when not empty, will contains: + // [header (2 bytes)] + // [length (2 bytes)] + // [packet (length - 4 bytes)] + // [crc (4 bytes)] + fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + let cmd = unsafe { self.ports.cmd.read() }; + if (cmd & CR_BUFE) == CR_BUFE { + return None; + } + + let isr = unsafe { self.ports.isr.read() }; + let capr = unsafe { self.ports.capr.read() }; + let cbr = unsafe { self.ports.cbr.read() }; + // CAPR starts at 65520 and with the pad it overflows to 0 + let offset = ((capr as usize) + RX_BUFFER_PAD) % (1 << 16); + + let header = u16::from_le_bytes(self.rx_buffer[(offset + 0)..(offset + 2)].try_into().unwrap()); + if self.debug_mode { + print!("------------------------------------------------------------------\n"); + let uptime = kernel::clock::clock_monotonic(); + print!("[{:.6}] NET RTL8139 receiving buffer:\n\n", uptime); + print!("Command Register: 0x{:02X}\n", cmd); + print!("Interrupt Status Register: 0x{:02X}\n", isr); + print!("CAPR: {}\n", capr); + print!("CBR: {}\n", cbr); + print!("Header: 0x{:04X}\n", header); + } + if header & ROK != ROK { + unsafe { self.ports.capr.write(cbr) }; + return None; + } + + let length = u16::from_le_bytes(self.rx_buffer[(offset + 2)..(offset + 4)].try_into().unwrap()); + let n = length as usize; + let crc = u32::from_le_bytes(self.rx_buffer[(offset + n)..(offset + n + 4)].try_into().unwrap()); + + if self.debug_mode { + print!("Length: {} bytes\n", n - 4); + print!("CRC: 0x{:08X}\n", crc); + print!("RX Offset: {}\n", offset); + user::hex::print_hex(&self.rx_buffer[(offset + 4)..(offset + n)]); + } + + // Update buffer read pointer + self.rx_offset = (offset + n + 4 + 3) & !3; + unsafe { self.ports.capr.write((self.rx_offset - RX_BUFFER_PAD) as u16); } + + let state = &self.state; + let rx = RxToken { state, buffer: &mut self.rx_buffer[(offset + 4)..(offset + n)] }; + let cmd_port = self.ports.tx_cmds[self.tx_id].clone(); + let buffer = &mut self.tx_buffers[self.tx_id][..]; + let debug_mode = self.debug_mode; + let tx = TxToken { state, cmd_port, buffer, debug_mode }; + + Some((rx, tx)) + } + + fn transmit(&'a mut self) -> Option { + let isr = unsafe { self.ports.isr.read() }; + self.tx_id = (self.tx_id + 1) % TX_BUFFERS_COUNT; + + if self.debug_mode { + print!("------------------------------------------------------------------\n"); + let uptime = kernel::clock::clock_monotonic(); + print!("[{:.6}] NET RTL8139 transmitting buffer:\n\n", uptime); + print!("TX Buffer: {}\n", self.tx_id); + print!("Interrupt Status Register: 0x{:02X}\n", isr); + } + + let state = &self.state; + let cmd_port = self.ports.tx_cmds[self.tx_id].clone(); + let buffer = &mut self.tx_buffers[self.tx_id][..]; + let debug_mode = self.debug_mode; + let tx = TxToken { state, cmd_port, buffer, debug_mode }; + + Some(tx) + } +} + +#[doc(hidden)] +pub struct RxToken<'a> { + state: &'a RefCell, + buffer: &'a mut [u8] +} + +impl<'a> phy::RxToken for RxToken<'a> { + fn consume(self, _timestamp: Instant, f: F) -> Result + where F: FnOnce(&mut [u8]) -> Result + { + self.state.borrow_mut().rx_packets_count += 1; + self.state.borrow_mut().rx_bytes_count += self.buffer.len() as u64; + f(self.buffer) + } +} + +#[doc(hidden)] +pub struct TxToken<'a> { + state: &'a RefCell, + cmd_port: Port, + buffer: &'a mut [u8], + debug_mode: bool, +} + +//const CRS: u32 = 1 << 31; // Carrier Sense Lost +//const TAB: u32 = 1 << 30; // Transmit Abort +//const OWC: u32 = 1 << 29; // Out of Window Collision +//const CDH: u32 = 1 << 28; // CD Heart Beat +const TOK: u32 = 1 << 15; // Transmit OK +//const TUN: u32 = 1 << 14; // Transmit FIFO Underrun +const OWN: u32 = 1 << 13; // DMA operation completed + +impl<'a> phy::TxToken for TxToken<'a> { + fn consume(mut self, _timestamp: Instant, len: usize, f: F) -> Result + where F: FnOnce(&mut [u8]) -> Result + { + // 1. Copy the packet to a physically contiguous buffer in memory. + let res = f(&mut self.buffer[0..len]); + + // 2. Fill in Start Address(physical address) of this buffer. + // NOTE: This has was done during init + + if res.is_ok() { + unsafe { + // 3. Fill in Transmit Status: the size of this packet, the + // early transmit threshold, and clear OWN bit in TSD (this + // starts the PCI operation). + // NOTE: The length of the packet use the first 13 bits (but + // should not exceed 1792 bytes), and a value of 0x000000 + // for the early transmit threshold means 8 bytes. So we + // just write the size of the packet. + self.cmd_port.write(0x1FFF & len as u32); + + // 4. When the whole packet is moved to FIFO, the OWN bit is + // set to 1. + while self.cmd_port.read() & OWN != OWN {} + // 5. When the whole packet is moved to line, the TOK bit is + // set to 1. + while self.cmd_port.read() & TOK != TOK {} + } + } + self.state.borrow_mut().tx_packets_count += 1; + self.state.borrow_mut().tx_bytes_count += len as u64; + if self.debug_mode { + user::hex::print_hex(&self.buffer[0..len]); + } + + res + } +} + +pub fn init() { + if let Some(mut pci_device) = kernel::pci::find_device(0x10EC, 0x8139) { + pci_device.enable_bus_mastering(); + + let io_addr = (pci_device.base_addresses[0] as u16) & 0xFFF0; + let mut rtl8139_device = RTL8139::new(io_addr); + + rtl8139_device.init(); + + if let Some(eth_addr) = rtl8139_device.eth_addr { + let uptime = kernel::clock::clock_monotonic(); + print!("[{:.6}] NET RTL8139 MAC {}\n", uptime, eth_addr); + + let neighbor_cache = NeighborCache::new(BTreeMap::new()); + let routes = Routes::new(BTreeMap::new()); + let ip_addrs = [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)]; + let iface = EthernetInterfaceBuilder::new(rtl8139_device). + ethernet_addr(eth_addr). + neighbor_cache(neighbor_cache). + ip_addrs(ip_addrs). + routes(routes). + finalize(); + + *IFACE.lock() = Some(iface); + } + + //let irq = pci_device.interrupt_line; + //kernel::idt::set_irq_handler(irq, interrupt_handler); + } +} + +fn phys_addr(ptr: &u8) -> u64 { + let rx_ptr = ptr as *const u8; + let virt_addr = VirtAddr::new(rx_ptr as u64); + let phys_addr = kernel::mem::translate_addr(virt_addr).unwrap(); + phys_addr.as_u64() +} + +pub fn interrupt_handler() { + print!("RTL8139 interrupt!\n"); + if let Some(mut guard) = IFACE.try_lock() { + if let Some(ref mut iface) = *guard { + unsafe { iface.device_mut().ports.isr.write(0xffff) } // Clear the interrupt + } + } +} diff --git a/src/kernel/sleep.rs b/src/kernel/sleep.rs deleted file mode 100644 index c234c14..0000000 --- a/src/kernel/sleep.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::kernel::clock; - -pub fn sleep(duration: f64) { - let start = clock::clock_monotonic(); - while clock::clock_monotonic() - start < duration { - halt(); - } -} - -pub fn halt() { - x86_64::instructions::hlt(); -} diff --git a/src/kernel/time.rs b/src/kernel/time.rs new file mode 100644 index 0000000..8c4e852 --- /dev/null +++ b/src/kernel/time.rs @@ -0,0 +1,33 @@ +use crate::kernel; +use lazy_static::lazy_static; +use spin::Mutex; + +lazy_static! { + pub static ref TICKS: Mutex = Mutex::new(0); +} + +pub fn ticks() -> usize { + *TICKS.lock() +} + +pub fn sleep(duration: f64) { + let interval = 1.0 / (1.193182 * 1000000.0 / 65536.0); + let start = kernel::clock::clock_monotonic(); + halt(); + while kernel::clock::clock_monotonic() - start < duration - interval { + halt(); + } +} + +pub fn halt() { + x86_64::instructions::hlt(); +} + +pub fn init() { + kernel::idt::set_irq_handler(0, interrupt_handler); +} + +pub fn interrupt_handler() { + let mut ticks = TICKS.lock(); + *ticks += 1; +} diff --git a/src/kernel/vga.rs b/src/kernel/vga.rs index d4aefce..3ba3d1a 100644 --- a/src/kernel/vga.rs +++ b/src/kernel/vga.rs @@ -137,6 +137,8 @@ impl Writer { 0x0A => { // Newline self.new_line(); }, + 0x0D => { // Carriage Return + }, 0x08 => { // Backspace if self.col_pos > 0 { self.col_pos -= 1; @@ -278,7 +280,7 @@ pub fn writer_position() -> (usize, usize) { // Printable ascii chars + backspace + newline pub fn is_printable(c: u8) -> bool { match c { - 0x20..=0x7E | 0x08 | 0x0A => true, + 0x20..=0x7E | 0x08 | 0x0A | 0x0D => true, _ => false, } } diff --git a/src/lib.rs b/src/lib.rs index 12e51ac..0447807 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,19 +10,21 @@ pub mod user; use bootloader::BootInfo; pub fn init(boot_info: &'static BootInfo) { - //kernel::keyboard::init(); kernel::gdt::init(); - kernel::interrupts::init_idt(); - unsafe { kernel::interrupts::PICS.lock().initialize() }; + kernel::idt::init(); + unsafe { kernel::pic::PICS.lock().initialize() }; x86_64::instructions::interrupts::enable(); print!("[{:.6}] MOROS version {}\n", kernel::clock::clock_monotonic(), env!("CARGO_PKG_VERSION")); + kernel::time::init(); + kernel::keyboard::init(); kernel::mem::init(boot_info); kernel::cpu::init(); - kernel::pci::init(); + kernel::pci::init(); // Require MEM + kernel::rtl8139::init(); // Require PCI kernel::ata::init(); - kernel::fs::init(); + kernel::fs::init(); // Require ATA } #[alloc_error_handler] diff --git a/src/main.rs b/src/main.rs index 718aa1a..338bdcf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ fn main(boot_info: &'static BootInfo) -> ! { kernel::fs::Dir::create("/dev"); // Devices kernel::fs::Dir::create("/ini"); // Initializers kernel::fs::Dir::create("/lib"); // Libraries + kernel::fs::Dir::create("/net"); // Network kernel::fs::Dir::create("/src"); // Sources kernel::fs::Dir::create("/tmp"); // Temporaries kernel::fs::Dir::create("/usr"); // User directories @@ -45,5 +46,5 @@ fn include_file(pathname: &str, contents: &str) { #[panic_handler] fn panic(info: &PanicInfo) -> ! { print!("{}\n", info); - loop { kernel::sleep::sleep(10.0) } + loop { kernel::time::sleep(10.0) } } diff --git a/src/user/dhcp.rs b/src/user/dhcp.rs new file mode 100644 index 0000000..3bfd1f9 --- /dev/null +++ b/src/user/dhcp.rs @@ -0,0 +1,92 @@ +use alloc::vec; +use alloc::vec::Vec; +use alloc::string::ToString; +use core::time::Duration; +use crate::{print, kernel, user}; +use smoltcp::dhcp::Dhcpv4Client; +use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata}; +use smoltcp::time::Instant; +use smoltcp::wire::{Ipv4Address, IpCidr, Ipv4Cidr}; + +pub fn main(_args: &[&str]) -> user::shell::ExitCode { + if let Some(ref mut iface) = *kernel::rtl8139::IFACE.lock() { + let mut iface = iface; + let mut sockets = SocketSet::new(vec![]); + let dhcp_rx_buffer = RawSocketBuffer::new( + [RawPacketMetadata::EMPTY; 1], + vec![0; 900] + ); + let dhcp_tx_buffer = RawSocketBuffer::new( + [RawPacketMetadata::EMPTY; 1], + vec![0; 600] + ); + + let timestamp = Instant::from_millis((kernel::clock::clock_monotonic() * 1000.0) as i64); + let mut dhcp = Dhcpv4Client::new(&mut sockets, dhcp_rx_buffer, dhcp_tx_buffer, timestamp); + + let mut prev_cidr = match iface.ip_addrs().first() { + Some(IpCidr::Ipv4(ip_addr)) => ip_addr.clone().into(), + _ => Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0), + }; + + print!("DHCP Discover transmitted\n"); + let time = kernel::clock::clock_monotonic(); + loop { + if kernel::clock::clock_monotonic() - time > 60.0 { + print!("Timeout reached\n"); + return user::shell::ExitCode::CommandError; + } + + let timestamp = Instant::from_millis((kernel::clock::clock_monotonic() * 1000.0) as i64); + iface.poll(&mut sockets, timestamp).map(|_| ()).unwrap_or_else(|e| print!("Error: {:?}\n", e)); + let config = dhcp.poll(&mut iface, &mut sockets, timestamp).unwrap_or_else(|e| { + print!("DHCP: {:?}\n", e); + None + }); + let mut success = false; + config.map(|config| { + print!("DHCP Offer received\n"); + //print!("DHCP config: {:?}\n", config); + match config.address { + Some(cidr) => if cidr != prev_cidr { + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().nth(0).map(|addr| { + *addr = IpCidr::Ipv4(cidr); + }); + }); + prev_cidr = cidr; + print!("Leased: {}\n", cidr); + } + _ => {} + } + + config.router.map(|router| { + iface.routes_mut().add_default_ipv4_route(router.into()).unwrap() + }); + iface.routes_mut().update(|routes_map| { + routes_map.get(&IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)).map(|default_route| { + print!("Router: {}\n", default_route.via_router); + }); + }); + + let dns_servers: Vec<_> = config.dns_servers.iter().filter_map(|s| *s).map(|s| s.to_string()).collect(); + if dns_servers.len() > 0 { + print!("DNS: {}\n", dns_servers.join(", ")); + } + + success = true; + }); + + if success { + return user::shell::ExitCode::CommandSuccessful; + } + + let mut timeout = dhcp.next_poll(timestamp); + iface.poll_delay(&sockets, timestamp).map(|sockets_timeout| timeout = sockets_timeout); + let wait_duration: Duration = timeout.into(); + kernel::time::sleep(wait_duration.as_secs_f64()); + } + } + + user::shell::ExitCode::CommandError +} diff --git a/src/user/halt.rs b/src/user/halt.rs index 9ac76a3..2c61917 100644 --- a/src/user/halt.rs +++ b/src/user/halt.rs @@ -2,7 +2,7 @@ use crate::{print, kernel, user}; pub fn main(_args: &[&str]) -> user::shell::ExitCode { print!("MOROS has reached its fate, the system is now halted.\n"); - kernel::sleep::sleep(3.0); + kernel::time::sleep(3.0); kernel::acpi::poweroff(); user::shell::ExitCode::CommandSuccessful } diff --git a/src/user/hex.rs b/src/user/hex.rs index a1f224c..abb72d2 100644 --- a/src/user/hex.rs +++ b/src/user/hex.rs @@ -1,21 +1,20 @@ use crate::{print, kernel, user}; -use alloc::vec::Vec; pub fn main(args: &[&str]) -> user::shell::ExitCode { if args.len() != 2 { return user::shell::ExitCode::CommandError; } - let path: Vec<_> = args[1].split("/").collect(); - if path.len() != 8 || path[1] != "dev" || path[2] != "ata" || path[4] != "dsk" || path[6] != "blk" { - return user::shell::ExitCode::CommandError; + + let pathname = args[1]; + + if let Some(file) = kernel::fs::File::open(pathname) { + let contents = file.read_to_string(); + print_hex(contents.as_bytes()); + user::shell::ExitCode::CommandSuccessful + } else { + print!("File not found '{}'\n", pathname); + user::shell::ExitCode::CommandError } - let bus = path[3].parse().unwrap(); - let dsk = path[5].parse().unwrap(); - let blk = path[7].parse().unwrap(); - let mut buf = [0u8; 512]; - kernel::ata::read(bus, dsk, blk, &mut buf); - print_hex(&buf); - user::shell::ExitCode::CommandSuccessful } pub fn print_hex(buf: &[u8]) { @@ -25,11 +24,17 @@ pub fn print_hex(buf: &[u8]) { print!("\n{:08X}: ", i * 2); } print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]); - if i % 8 == 7 { - for j in 0..16 { - let c = buf[(i * 2 + 1) - 15 + j] as char; - if c.is_ascii_graphic() || c.is_ascii_whitespace() { + 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!("."); } diff --git a/src/user/host.rs b/src/user/host.rs new file mode 100644 index 0000000..33ade2e --- /dev/null +++ b/src/user/host.rs @@ -0,0 +1,234 @@ +use alloc::vec::Vec; +use alloc::vec; +use bit_field::BitField; +use core::convert::TryInto; +use core::str; +use core::time::Duration; +use crate::{print, kernel, user}; +use smoltcp::socket::{SocketSet, UdpSocket, UdpSocketBuffer, UdpPacketMetadata}; +use smoltcp::time::Instant; +use smoltcp::wire::{IpEndpoint, IpAddress, Ipv4Address}; + +// See RFC 1035 for implementation details + +#[repr(u16)] +enum QueryType { + A = 1, + // NS = 2, + // MD = 3, + // MF = 4, + // CNAME = 5, + // SOA = 6, + // MX = 15, + // TXT = 16, +} + +#[repr(u16)] +enum QueryClass { + IN = 1, +} + +#[derive(Debug)] +#[repr(u16)] +pub enum ResponseCode { + NoError = 0, + FormatError = 1, + ServerFailure = 2, + NameError = 3, + NotImplemented = 4, + Refused = 5, + + UnknownError, + NetworkError, +} + +struct Message { + pub datagram: Vec +} + +const FLAG_RD: u16 = 0x0100; // Recursion desired + +impl Message { + pub fn from(datagram: &[u8]) -> Self { + Self { + datagram: Vec::from(datagram) + } + } + + pub fn query(qname: &str, qtype: QueryType, qclass: QueryClass) -> Self { + let mut datagram = Vec::new(); + + let id = kernel::random::rand16().expect("random id"); + for b in id.to_be_bytes().iter() { + datagram.push(*b); // Transaction ID + } + for b in FLAG_RD.to_be_bytes().iter() { + datagram.push(*b); // Flags + } + for b in (1 as u16).to_be_bytes().iter() { + datagram.push(*b); // Questions + } + for _ in 0..6 { + datagram.push(0); // Answer + Authority + Additional + } + for label in qname.split('.') { + datagram.push(label.len() as u8); // QNAME label length + for b in label.bytes() { + datagram.push(b); // QNAME label bytes + } + } + datagram.push(0); // Root null label + for b in (qtype as u16).to_be_bytes().iter() { + datagram.push(*b); // QTYPE + } + for b in (qclass as u16).to_be_bytes().iter() { + datagram.push(*b); // QCLASS + } + + Self { + datagram + } + } + + pub fn id(&self) -> u16 { + u16::from_be_bytes(self.datagram[0..2].try_into().unwrap()) + } + + pub fn header(&self) -> u16 { + u16::from_be_bytes(self.datagram[2..4].try_into().unwrap()) + } + + pub fn is_response(&self) -> bool { + self.header().get_bit(15) + } + + /* + pub fn is_query(&self) -> bool { + !self.is_response() + } + */ + + pub fn rcode(&self) -> ResponseCode { + match self.header().get_bits(11..15) { + 0 => ResponseCode::NoError, + 1 => ResponseCode::FormatError, + 2 => ResponseCode::ServerFailure, + 3 => ResponseCode::NameError, + 4 => ResponseCode::NotImplemented, + 5 => ResponseCode::Refused, + _ => ResponseCode::UnknownError, + } + } +} + +pub fn resolve(name: &str) -> Result { + let dns_address = IpAddress::v4(192, 168, 1, 3); + let dns_port = 53; + let server = IpEndpoint::new(dns_address, dns_port); + + let local_port = 49152 + kernel::random::rand16().expect("random port") % 16384; + let client = IpEndpoint::new(IpAddress::Unspecified, local_port); + + let qname = name; + let qtype = QueryType::A; + let qclass = QueryClass::IN; + let query = Message::query(qname, qtype, qclass); + + let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 512]); + let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 512]); + let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); + + let mut sockets = SocketSet::new(vec![]); + let udp_handle = sockets.add(udp_socket); + + enum State { Bind, Query, Response }; + let mut state = State::Bind; + if let Some(ref mut iface) = *kernel::rtl8139::IFACE.lock() { + match iface.ipv4_addr() { + None => { + return Err(ResponseCode::NetworkError); + } + Some(ip_addr) if ip_addr.is_unspecified() => { + return Err(ResponseCode::NetworkError); + } + _ => {} + } + + let time = kernel::clock::clock_monotonic(); + loop { + if kernel::clock::clock_monotonic() - time > 5.0 { + return Err(ResponseCode::NetworkError); + } + + let timestamp = Instant::from_millis((kernel::clock::clock_monotonic() * 1000.0) as i64); + match iface.poll(&mut sockets, timestamp) { + Ok(_) => {}, + Err(e) => { + print!("poll error: {}\n", e); + } + } + + { + let mut socket = sockets.get::(udp_handle); + + state = match state { + State::Bind if !socket.is_open() => { + socket.bind(client).unwrap(); + State::Query + } + State::Query if socket.can_send() => { + socket.send_slice(&query.datagram, server).expect("cannot send"); + State::Response + } + State::Response if socket.can_recv() => { + let (data, _) = socket.recv().expect("cannot receive"); + let message = Message::from(data); + if message.id() == query.id() && message.is_response() { + return match message.rcode() { + ResponseCode::NoError => { + // TODO: Parse the datagram instead of + // extracting the last 4 bytes. + //let rdata = message.answer().rdata(); + let n = message.datagram.len(); + let rdata = &message.datagram[(n - 4)..]; + + Ok(IpAddress::from(Ipv4Address::from_bytes(rdata))) + } + rcode => { + Err(rcode) + } + } + } + state + } + _ => state + } + } + + if let Some(wait_duration) = iface.poll_delay(&sockets, timestamp) { + let wait_duration: Duration = wait_duration.into(); + kernel::time::sleep(wait_duration.as_secs_f64()); + } + } + } else { + Err(ResponseCode::NetworkError) + } +} + +pub fn main(args: &[&str]) -> user::shell::ExitCode { + if args.len() != 2 { + print!("Usage: host \n"); + return user::shell::ExitCode::CommandError; + } + let name = args[1]; + match resolve(name) { + Ok(ip_addr) => { + print!("{} has address {}\n", name, ip_addr); + user::shell::ExitCode::CommandSuccessful + } + Err(e) => { + print!("Could not resolve host: {:?}\n", e); + user::shell::ExitCode::CommandError + } + } +} diff --git a/src/user/http.rs b/src/user/http.rs new file mode 100644 index 0000000..8eb9cbf --- /dev/null +++ b/src/user/http.rs @@ -0,0 +1,184 @@ +use alloc::borrow::ToOwned; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use alloc::vec; +use core::str::{self, FromStr}; +use core::time::Duration; +use crate::{print, kernel, user}; +use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; +use smoltcp::time::Instant; +use smoltcp::wire::IpAddress; + +#[derive(Debug)] +struct URL { + pub host: String, + pub port: u16, + pub path: String, +} + +impl URL { + pub fn parse(url: &str) -> Option { + if !url.starts_with("http://") { + return None; + } + let url = &url[7..]; + let (server, path) = match url.find('/') { + Some(i) => url.split_at(i), + None => (url, "/"), + }; + let (host, port) = match server.find(':') { + Some(i) => server.split_at(i), + None => (server, "80"), + }; + Some(Self { + host: host.into(), + port: port.parse().unwrap_or(80), + path: path.into(), + }) + } +} + +pub fn main(args: &[&str]) -> user::shell::ExitCode { + let mut is_verbose = false; + let args: Vec<_> = args.into_iter().filter(|arg| { + let arg = arg.to_string(); + if arg == "--verbose" { + is_verbose = true; + } + !arg.starts_with("--") + }).collect(); + if args.len() != 3 { + print!("Usage: http \n"); + return user::shell::ExitCode::CommandError; + } + + let url = "http://".to_owned() + args[1] + args[2]; + let url = URL::parse(&url).expect("invalid URL format"); + + let address = if url.host.ends_with(char::is_numeric) { + IpAddress::from_str(&url.host).expect("invalid address format") + } else { + match user::host::resolve(&url.host) { + Ok(ip_addr) => { + ip_addr + } + Err(e) => { + print!("Could not resolve host: {:?}\n", e); + return user::shell::ExitCode::CommandError; + } + } + }; + + let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 1024]); + let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 1024]); + let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); + + let mut sockets = SocketSet::new(vec![]); + let tcp_handle = sockets.add(tcp_socket); + + enum State { Connect, Request, Response }; + let mut state = State::Connect; + + if let Some(ref mut iface) = *kernel::rtl8139::IFACE.lock() { + match iface.ipv4_addr() { + None => { + print!("Interface not ready\n"); + return user::shell::ExitCode::CommandError; + } + Some(ip_addr) if ip_addr.is_unspecified() => { + print!("Interface not ready\n"); + return user::shell::ExitCode::CommandError; + } + _ => {} + } + + let time = kernel::clock::clock_monotonic(); + loop { + if kernel::clock::clock_monotonic() - time > 5.0 { + print!("Timeout reached\n"); + return user::shell::ExitCode::CommandError; + } + + let timestamp = Instant::from_millis((kernel::clock::clock_monotonic() * 1000.0) as i64); + match iface.poll(&mut sockets, timestamp) { + Ok(_) => {}, + Err(e) => { + print!("Interface polling error: {}\n", e); + return user::shell::ExitCode::CommandError; + } + } + + { + let mut socket = sockets.get::(tcp_handle); + + state = match state { + State::Connect if !socket.is_active() => { + let local_port = 49152 + kernel::random::rand16().expect("random port") % 16384; + if is_verbose { + print!("* Connecting to {}:{}\n", address, url.port); + } + if socket.connect((address, url.port), local_port).is_err() { + print!("Could not connect to {}:{}\n", address, url.port); + return user::shell::ExitCode::CommandError; + } + State::Request + } + State::Request if socket.may_send() => { + let http_get = "GET ".to_owned() + &url.path + " HTTP/1.1\r\n"; + let http_host = "Host: ".to_owned() + &url.host + "\r\n"; + let http_ua = "User-Agent: MOROS/".to_owned() + env!("CARGO_PKG_VERSION") + "\r\n"; + let http_connection = "Connection: close\r\n".to_owned(); + if is_verbose { + print!("> {}", http_get); + print!("> {}", http_host); + print!("> {}", http_ua); + print!("> {}", http_connection); + print!(">\n"); + } + socket.send_slice(http_get.as_ref()).expect("cannot send"); + socket.send_slice(http_host.as_ref()).expect("cannot send"); + socket.send_slice(http_ua.as_ref()).expect("cannot send"); + socket.send_slice(http_connection.as_ref()).expect("cannot send"); + socket.send_slice(b"\r\n").expect("cannot send"); + State::Response + } + State::Response if socket.can_recv() => { + socket.recv(|data| { + let contents = str::from_utf8(data).unwrap_or("invalid UTF-8"); + let mut is_header = true; + for line in contents.lines() { + if line.len() == 0 { + is_header = false; + if !is_verbose { + continue + } + } + if is_header { + if is_verbose { + print!("< {}\n", line); + } + } else { + print!("{}\n", line); + } + } + (data.len(), ()) + }).unwrap(); + State::Response + } + State::Response if !socket.may_recv() => { + break; + } + _ => state + } + } + + if let Some(wait_duration) = iface.poll_delay(&sockets, timestamp) { + let wait_duration: Duration = wait_duration.into(); + kernel::time::sleep(wait_duration.as_secs_f64()); + } + } + user::shell::ExitCode::CommandSuccessful + } else { + user::shell::ExitCode::CommandError + } +} diff --git a/src/user/ip.rs b/src/user/ip.rs new file mode 100644 index 0000000..c314a3f --- /dev/null +++ b/src/user/ip.rs @@ -0,0 +1,18 @@ +use crate::{print, kernel, user}; + +pub fn main(_args: &[&str]) -> user::shell::ExitCode { + if let Some(ref iface) = *kernel::rtl8139::IFACE.lock() { + print!("Link: {}\n", iface.ethernet_addr()); + for ip_cidr in iface.ip_addrs() { + print!("Addr: {}/{}\n", ip_cidr.address(), ip_cidr.prefix_len()); + } + print!("RX packets: {}\n", iface.device().rx_packets_count()); + print!("TX packets: {}\n", iface.device().tx_packets_count()); + print!("RX bytes: {}\n", iface.device().rx_bytes_count()); + print!("TX bytes: {}\n", iface.device().tx_bytes_count()); + user::shell::ExitCode::CommandSuccessful + } else { + print!("Could not find network interface\n"); + user::shell::ExitCode::CommandError + } +} diff --git a/src/user/login.rs b/src/user/login.rs index 00c94d6..e822965 100644 --- a/src/user/login.rs +++ b/src/user/login.rs @@ -34,7 +34,7 @@ pub fn login() -> user::shell::ExitCode { username.pop(); // Trim end of string match hashed_passwords.get(&username) { None => { - kernel::sleep::sleep(1.0); + kernel::time::sleep(1.0); return login(); }, Some(hashed_password) => { @@ -45,7 +45,7 @@ pub fn login() -> user::shell::ExitCode { print!("\n"); password.pop(); if !check(&password, hashed_password) { - kernel::sleep::sleep(1.0); + kernel::time::sleep(1.0); return login(); } } diff --git a/src/user/mod.rs b/src/user/mod.rs index 8a29ad8..01c558f 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -3,15 +3,21 @@ pub mod clear; pub mod copy; pub mod date; pub mod delete; +pub mod dhcp; pub mod editor; pub mod halt; pub mod help; pub mod hex; +pub mod http; +pub mod host; +pub mod ip; pub mod list; pub mod login; +pub mod net; pub mod print; pub mod r#move; pub mod read; +pub mod route; pub mod shell; pub mod sleep; pub mod uptime; diff --git a/src/user/net.rs b/src/user/net.rs new file mode 100644 index 0000000..1b5ea6d --- /dev/null +++ b/src/user/net.rs @@ -0,0 +1,76 @@ +use crate::{print, kernel, user}; +//use smoltcp::wire::Ipv4Address; +use smoltcp::time::Instant; +use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; + +pub fn main(args: &[&str]) -> user::shell::ExitCode { + if args.len() == 1 { + print!("Usage: net \n"); + return user::shell::ExitCode::CommandError; + } + + if let Some(ref mut iface) = *kernel::rtl8139::IFACE.lock() { + match args[1] { + "config" => { + if args.len() < 4 { + print!("Usage: net config \n"); + return user::shell::ExitCode::CommandError; + } + match args[2] { + "debug" => { + iface.device_mut().debug_mode = match args[3] { + "1" | "on" | "enable" => true, + "0" | "off" | "disable" => false, + _ => { + print!("Invalid config value\n"); + return user::shell::ExitCode::CommandError; + } + } + } + _ => { + print!("Invalid config key\n"); + return user::shell::ExitCode::CommandError; + } + } + } + "dump" => { + iface.device_mut().debug_mode = true; + + let mut server_rx_buffer = [0; 2048]; + let mut server_tx_buffer = [0; 2048]; + let server_socket = TcpSocket::new( + TcpSocketBuffer::new(&mut server_rx_buffer[..]), + TcpSocketBuffer::new(&mut server_tx_buffer[..]) + ); + + let mut sockets_storage = [None, None]; + let mut sockets = SocketSet::new(&mut sockets_storage[..]); + let _server_handle = sockets.add(server_socket); + + loop { + let now = kernel::clock::clock_monotonic(); + match iface.poll(&mut sockets, Instant::from_millis((now * 1000.0) as i64)) { + Ok(true) => { + print!("------------------------------------------------------------------\n"); + print!("Polling result: Ok(true)\n"); + }, + Ok(false) => { + //print!("------------------------------------------------------------------\n"); + //print!("Polling Result: Ok(false)\n\n"); + }, + Err(e) => { + print!("------------------------------------------------------------------\n"); + print!("polling result: err({})\n", e); + } + } + kernel::time::sleep(1.0); + } + } + _ => { + print!("Invalid command\n"); + return user::shell::ExitCode::CommandError; + } + } + } + user::shell::ExitCode::CommandSuccessful +} diff --git a/src/user/read.rs b/src/user/read.rs index bf8723c..db8175d 100644 --- a/src/user/read.rs +++ b/src/user/read.rs @@ -1,3 +1,5 @@ +use alloc::borrow::ToOwned; +use alloc::vec::Vec; use crate::{print, kernel, user}; pub fn main(args: &[&str]) -> user::shell::ExitCode { @@ -22,7 +24,28 @@ pub fn main(args: &[&str]) -> user::shell::ExitCode { user::shell::ExitCode::CommandSuccessful }, _ => { - if pathname.ends_with('/') { + if pathname.starts_with("/net/") { + // Examples: + // > read /net/http/example.com/articles + // > read /net/http/example.com:8080/articles/index.html + let parts: Vec<_> = pathname.split('/').collect(); + if parts.len() < 4 { + print!("Usage: read /net/http//\n"); + user::shell::ExitCode::CommandError + } else { + match parts[2] { + "http" => { + let host = parts[3]; + let path = "/".to_owned() + &parts[4..].join("/"); + user::http::main(&["http", host, &path]) + } + _ => { + print!("Error: unknown protocol '{}'\n", parts[2]); + user::shell::ExitCode::CommandError + } + } + } + } else if pathname.ends_with('/') { user::list::main(args) } else if let Some(file) = kernel::fs::File::open(pathname) { print!("{}", file.read_to_string()); diff --git a/src/user/route.rs b/src/user/route.rs new file mode 100644 index 0000000..e31030a --- /dev/null +++ b/src/user/route.rs @@ -0,0 +1,17 @@ +use alloc::string::ToString; +use crate::{print, kernel, user}; + +pub fn main(_args: &[&str]) -> user::shell::ExitCode { + if let Some(ref mut iface) = *kernel::rtl8139::IFACE.lock() { + print!("{:<19} {}\n", "Destination", "Gateway"); + iface.routes_mut().update(|storage| { + for (cidr, route) in storage.iter() { + print!("{:<19} {}\n", cidr.to_string(), route.via_router); + } + }); + user::shell::ExitCode::CommandSuccessful + } else { + print!("Could not find network interface\n"); + user::shell::ExitCode::CommandError + } +} diff --git a/src/user/shell.rs b/src/user/shell.rs index 4660002..1d34a86 100644 --- a/src/user/shell.rs +++ b/src/user/shell.rs @@ -282,7 +282,13 @@ impl Shell { "login" => user::login::main(&args), "base64" => user::base64::main(&args), "halt" => user::halt::main(&args), - "hex" => user::hex::main(&args), + "hex" => user::hex::main(&args), // TODO: Rename to `dump` + "net" => user::net::main(&args), + "route" => user::route::main(&args), + "dhcp" => user::dhcp::main(&args), + "http" => user::http::main(&args), + "host" => user::host::main(&args), + "ip" => user::ip::main(&args), _ => ExitCode::CommandUnknown, } } diff --git a/src/user/sleep.rs b/src/user/sleep.rs index 1019980..ac476dc 100644 --- a/src/user/sleep.rs +++ b/src/user/sleep.rs @@ -3,7 +3,7 @@ use crate::{kernel, user}; pub fn main(args: &[&str]) -> user::shell::ExitCode { if args.len() == 2 { if let Ok(duration) = args[1].parse::() { - kernel::sleep::sleep(duration); + kernel::time::sleep(duration); return user::shell::ExitCode::CommandSuccessful; } }