mirror of https://github.com/vinc/moros.git
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:
parent
d316381f13
commit
4fd7d68a63
|
@ -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"
|
||||
|
|
|
@ -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"] }
|
||||
|
|
36
README.md
36
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:
|
||||
|
||||
|
|
|
@ -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
|
|
@ -1,4 +1,5 @@
|
|||
read /ini/banner.txt
|
||||
# dhcp
|
||||
login
|
||||
shell
|
||||
clear
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) });
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
12
src/lib.rs
12
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]
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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!(".");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue