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/<proto>/<host>/<path> subcommand
This commit is contained in:
Vincent Ollivier 2020-02-02 18:55:20 +01:00 committed by GitHub
parent d316381f13
commit 4fd7d68a63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1643 additions and 182 deletions

18
Cargo.lock generated
View File

@ -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"

View File

@ -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"] }

View File

@ -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:

103
doc/net.md Normal file
View File

@ -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

View File

@ -1,4 +1,5 @@
read /ini/banner.txt
# dhcp
login
shell
clear

View File

@ -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

View File

@ -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<usize> = 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 {

View File

@ -11,11 +11,19 @@ lazy_static! {
pub static ref RAW: Mutex<bool> = 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() {

109
src/kernel/idt.rs Normal file
View File

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

View File

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

View File

@ -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<Keyboard<layouts::Us104Key, ScancodeSet1>> = Mutex::new(
Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::MapLettersToUnicode)
);
}
/*
#[cfg(feature="dvorak")]
lazy_static! {
pub static ref KEYBOARD: Mutex<Keyboard<layouts::Dvorak104Key, ScancodeSet1>> = 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);
}
}
}

View File

@ -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<VirtAddr> = 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<PhysAddr> {
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> {

View File

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

View File

@ -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<Vec<DeviceConfig>> = Mutex::new(Vec::new());
}
pub fn find_device(vendor_id: u16, device_id: u16) -> Option<DeviceConfig> {
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<u32>,
addr_port: Port<u32>,
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);
}
}
}

7
src/kernel/pic.rs Normal file
View File

@ -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<ChainedPics> = Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });

View File

@ -6,3 +6,17 @@ pub fn rand64() -> Option<u64> {
None => None
}
}
pub fn rand32() -> Option<u32> {
match RdRand::new() {
Some(rand) => rand.get_u32(),
None => None
}
}
pub fn rand16() -> Option<u16> {
match RdRand::new() {
Some(rand) => rand.get_u16(),
None => None
}
}

470
src/kernel/rtl8139.rs Normal file
View File

@ -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<Option<EthernetInterface<'static, 'static, 'static, RTL8139>>> = Mutex::new(None);
}
pub struct Ports {
pub idr: [Port<u8>; 6], // ID Registers (IDR0 ... IDR5)
pub tx_cmds: [Port<u32>; TX_BUFFERS_COUNT], // Transmit Status of Descriptors (TSD0 .. TSD3)
pub tx_addrs: [Port<u32>; TX_BUFFERS_COUNT], // Transmit Start Address of Descriptor0 (TSAD0 .. TSAD3)
pub config1: Port<u8>, // Configuration Register 1 (CONFIG1)
pub rx_addr: Port<u32>, // Receive (Rx) Buffer Start Address (RBSTART)
pub capr: Port<u16>, // Current Address of Packet Read (CAPR)
pub cbr: Port<u16>, // Current Buffer Address (CBR)
pub cmd: Port<u8>, // Command Register (CR)
pub imr: Port<u16>, // Interrupt Mask Register (IMR)
pub isr: Port<u16>, // Interrupt Status Register (ISR)
pub tx_config: Port<u32>, // Transmit (Tx) Configuration Register (TCR)
pub rx_config: Port<u32>, // 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<State>,
ports: Ports,
eth_addr: Option<EthernetAddress>,
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<Self::TxToken> {
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<State>,
buffer: &'a mut [u8]
}
impl<'a> phy::RxToken for RxToken<'a> {
fn consume<R, F>(self, _timestamp: Instant, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
{
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<State>,
cmd_port: Port<u32>,
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<R, F>(mut self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
{
// 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
}
}
}

View File

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

33
src/kernel/time.rs Normal file
View File

@ -0,0 +1,33 @@
use crate::kernel;
use lazy_static::lazy_static;
use spin::Mutex;
lazy_static! {
pub static ref TICKS: Mutex<usize> = 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;
}

View File

@ -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,
}
}

View File

@ -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]

View File

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

92
src/user/dhcp.rs Normal file
View File

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

View File

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

View File

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

234
src/user/host.rs Normal file
View File

@ -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<u8>
}
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<IpAddress, ResponseCode> {
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::<UdpSocket>(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 <name>\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
}
}
}

184
src/user/http.rs Normal file
View File

@ -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<Self> {
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 <server> <path>\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::<TcpSocket>(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
}
}

18
src/user/ip.rs Normal file
View File

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

View File

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

View File

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

76
src/user/net.rs Normal file
View File

@ -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 <command>\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 <key> <value>\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
}

View File

@ -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/<host>/<path>\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());

17
src/user/route.rs Normal file
View File

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

View File

@ -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,
}
}

View File

@ -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::<f64>() {
kernel::sleep::sleep(duration);
kernel::time::sleep(duration);
return user::shell::ExitCode::CommandSuccessful;
}
}