mirror of https://github.com/vinc/moros.git
Merge branch 'trunk' into feature/virtio-net
This commit is contained in:
commit
40891894b0
|
@ -1,6 +1,11 @@
|
|||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
- Add e1000 driver (#337)
|
||||
- Reduce DHCP sleep time (#610)
|
||||
- Allow copying file to dir (#607)
|
||||
- Fix HTTP server (#609)
|
||||
- Fix HTTP server content type (#608)
|
||||
- Improve RNG (#602)
|
||||
- Fix invalid VGA palette reset (#605)
|
||||
- Improve VGA palette parser (#604)
|
||||
|
|
16
Makefile
16
Makefile
|
@ -13,10 +13,12 @@ keyboard = qwerty# qwerty, azerty, dvorak
|
|||
mode = release
|
||||
|
||||
# Emulation options
|
||||
nic = rtl8139# rtl8139, pcnet, virtio-net
|
||||
nic = rtl8139# rtl8139, pcnet, e1000, virtio-net
|
||||
audio = sdl# sdl, coreaudio
|
||||
signal = off# on
|
||||
kvm = false
|
||||
pcap = false
|
||||
trace = false# e1000
|
||||
monitor = false
|
||||
|
||||
export MOROS_VERSION = $(shell git describe --tags | sed "s/^v//")
|
||||
|
@ -65,7 +67,7 @@ qemu-opts = -m $(memory) -drive file=$(img),format=raw \
|
|||
ifeq ($(kvm),true)
|
||||
qemu-opts += -cpu host -accel kvm
|
||||
else
|
||||
qemu-opts += -cpu max
|
||||
qemu-opts += -cpu core2duo
|
||||
endif
|
||||
|
||||
ifeq ($(pcap),true)
|
||||
|
@ -77,13 +79,18 @@ ifeq ($(monitor),true)
|
|||
endif
|
||||
|
||||
ifeq ($(output),serial)
|
||||
qemu-opts += -display none -chardev stdio,id=s0,signal=off -serial chardev:s0
|
||||
qemu-opts += -display none
|
||||
qemu-opts += -chardev stdio,id=s0,signal=$(signal) -serial chardev:s0
|
||||
endif
|
||||
|
||||
ifeq ($(mode),debug)
|
||||
qemu-opts += -s -S
|
||||
endif
|
||||
|
||||
ifeq ($(trace),e1000)
|
||||
qemu-opts += -trace 'e1000*'
|
||||
endif
|
||||
|
||||
# In debug mode, open another terminal with the following command
|
||||
# and type `continue` to start the boot process:
|
||||
# > gdb target/x86_64-moros/debug/moros -ex "target remote :1234"
|
||||
|
@ -93,7 +100,8 @@ qemu:
|
|||
|
||||
test:
|
||||
cargo test --release --lib --no-default-features --features serial -- \
|
||||
-m $(memory) -display none -serial stdio -device isa-debug-exit,iobase=0xF4,iosize=0x04
|
||||
-m $(memory) -display none -serial stdio \
|
||||
-device isa-debug-exit,iobase=0xF4,iosize=0x04
|
||||
|
||||
website:
|
||||
cd www && sh build.sh
|
||||
|
|
|
@ -29,8 +29,7 @@ This project started from the [seventh post][1] of the second edition of
|
|||
- PCI devices
|
||||
- ATA PIO mode
|
||||
- Random number generator (using [rand_hc][12])
|
||||
- RTL8139 network card
|
||||
- AMD PCNET network card
|
||||
- Intel PRO/1000, RTL8139, and AMD PCNET network cards
|
||||
- DHCP/IP/TCP/UDP/DNS/HTTP network protocols (using [smoltcp][13])
|
||||
- Basic [filesystem](doc/filesystem.md)
|
||||
- Basic [shell](doc/shell.md)
|
||||
|
|
|
@ -4,10 +4,15 @@
|
|||
|
||||
- [x] QEMU
|
||||
- [x] CPU: Intel Core 2 Duo T7700
|
||||
- [x] NIC: Realtek RTL8139
|
||||
- [x] NIC: Intel PRO/1000 MT Desktop
|
||||
- [x] NIC: Realtek RTL8139C
|
||||
- [x] NIC: AMD PCnet-FAST III
|
||||
|
||||
- [x] VirtualBox
|
||||
- [x] NIC: PCnet-FAST III
|
||||
- [x] NIC: Intel PRO/1000 MT Desktop (82540EM)
|
||||
- [x] NIC: Intel PRO/1000 MT Server (82545EM)
|
||||
- [x] NIC: Intel PRO/1000 T Server (82543GC)
|
||||
- [x] NIC: AMD PCnet-FAST III
|
||||
|
||||
- [x] Bochs
|
||||
|
||||
|
@ -18,12 +23,19 @@
|
|||
- [x] Homebuilt (2007)
|
||||
- [x] MB: Asus P5K
|
||||
- [x] CPU: Intel Core 2 Duo E6850
|
||||
- [x] NIC: Intel PRO/1000 GT Desktop
|
||||
- [x] NIC: Realtek RTL8139B
|
||||
- [x] NIC: Realtek RTL8139C
|
||||
- [x] NIC: Realtek RTL8139D
|
||||
|
||||
- [x] HP ProLiant MicroServer N40L (2012)
|
||||
- [x] CPU: AMD Athlon II Dual Core
|
||||
- [ ] NIC: HP NC107i
|
||||
|
||||
- [x] Lenovo ThinkCentre M83 SFF (2014)
|
||||
- [x] CPU: Intel Pentium G3220
|
||||
- [x] NIC: Intel I217-LM
|
||||
|
||||
- [x] Intel NUC 5CPYH (2015)
|
||||
- [x] CPU: Intel Celeron N3050
|
||||
- [ ] NIC: Realtek RTL8111HN
|
||||
|
@ -32,15 +44,15 @@
|
|||
|
||||
- [x] Dell Latitude E6400 (2008)
|
||||
- [x] CPU: Intel Core 2 Duo P8600
|
||||
- [ ] NIC: Intel PRO/1000
|
||||
- [x] NIC: Intel 82567LM
|
||||
|
||||
- [x] Lenovo ThinkPad X200 (2008)
|
||||
- [x] CPU: Intel Core 2 Duo P8600
|
||||
- [ ] NIC: Intel 82567-LM
|
||||
- [x] NIC: Intel 82567LM
|
||||
|
||||
- [x] Lenovo ThinkPad T440p (2013)
|
||||
- [x] CPU: Intel Core i5-4300M
|
||||
- [ ] NIC: Intel I217-LM
|
||||
- [x] NIC: Intel I217-LM
|
||||
|
||||
- [x] ASUS EeeBook E202SA (2017)
|
||||
- [x] CPU: Intel Pentium N3710
|
||||
|
|
|
@ -6,4 +6,4 @@ set -e
|
|||
make image output=video keyboard=dvorak nic=pcnet
|
||||
qemu-img convert -f raw -O vdi disk.img disk.vdi -o size=32M
|
||||
VBoxManage internalcommands sethduuid disk.vdi dbbfad68-c3d1-4c9a-828f-7e4db4e9488e
|
||||
VBoxManage startvm Moros
|
||||
VBoxManage startvm MOROS
|
||||
|
|
|
@ -224,7 +224,14 @@ pub fn read_dir(path: &str) -> Result<Vec<FileInfo>, ()> {
|
|||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_file() {
|
||||
fn test_filename() {
|
||||
assert_eq!(filename("/path/to/file.txt"), "file.txt");
|
||||
assert_eq!(filename("/file.txt"), "file.txt");
|
||||
assert_eq!(filename("file.txt"), "file.txt");
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_fs() {
|
||||
use crate::sys::fs::{dismount, format_mem, mount_mem};
|
||||
mount_mem();
|
||||
format_mem();
|
||||
|
|
|
@ -128,9 +128,9 @@ impl PhysBuf {
|
|||
|
||||
// Realloc vec until it uses a chunk of contiguous physical memory
|
||||
fn from(vec: Vec<u8>) -> Self {
|
||||
let buffer_len = vec.len() - 1;
|
||||
let memory_len = phys_addr(&vec[buffer_len]) - phys_addr(&vec[0]);
|
||||
if buffer_len == memory_len as usize {
|
||||
let buffer_end = vec.len() - 1;
|
||||
let memory_end = phys_addr(&vec[buffer_end]) - phys_addr(&vec[0]);
|
||||
if buffer_end == memory_end as usize {
|
||||
Self {
|
||||
buf: Arc::new(Mutex::new(vec)),
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ mod nic;
|
|||
pub mod socket;
|
||||
|
||||
use crate::{sys, usr};
|
||||
use crate::sys::pci::DeviceConfig;
|
||||
|
||||
use alloc::format;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
|
@ -33,8 +35,8 @@ fn time() -> Instant {
|
|||
pub enum EthernetDevice {
|
||||
RTL8139(nic::rtl8139::Device),
|
||||
PCNET(nic::pcnet::Device),
|
||||
E1000(nic::e1000::Device),
|
||||
VirtIO(nic::virtio::Device),
|
||||
//E2000,
|
||||
}
|
||||
|
||||
pub trait EthernetDeviceIO {
|
||||
|
@ -50,6 +52,7 @@ impl EthernetDeviceIO for EthernetDevice {
|
|||
match self {
|
||||
EthernetDevice::RTL8139(dev) => dev.config(),
|
||||
EthernetDevice::PCNET(dev) => dev.config(),
|
||||
EthernetDevice::E1000(dev) => dev.config(),
|
||||
EthernetDevice::VirtIO(dev) => dev.config(),
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +61,7 @@ impl EthernetDeviceIO for EthernetDevice {
|
|||
match self {
|
||||
EthernetDevice::RTL8139(dev) => dev.stats(),
|
||||
EthernetDevice::PCNET(dev) => dev.stats(),
|
||||
EthernetDevice::E1000(dev) => dev.stats(),
|
||||
EthernetDevice::VirtIO(dev) => dev.stats(),
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +70,7 @@ impl EthernetDeviceIO for EthernetDevice {
|
|||
match self {
|
||||
EthernetDevice::RTL8139(dev) => dev.receive_packet(),
|
||||
EthernetDevice::PCNET(dev) => dev.receive_packet(),
|
||||
EthernetDevice::E1000(dev) => dev.receive_packet(),
|
||||
EthernetDevice::VirtIO(dev) => dev.receive_packet(),
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +79,7 @@ impl EthernetDeviceIO for EthernetDevice {
|
|||
match self {
|
||||
EthernetDevice::RTL8139(dev) => dev.transmit_packet(len),
|
||||
EthernetDevice::PCNET(dev) => dev.transmit_packet(len),
|
||||
EthernetDevice::E1000(dev) => dev.transmit_packet(len),
|
||||
EthernetDevice::VirtIO(dev) => dev.transmit_packet(len),
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +88,7 @@ impl EthernetDeviceIO for EthernetDevice {
|
|||
match self {
|
||||
EthernetDevice::RTL8139(dev) => dev.next_tx_buffer(len),
|
||||
EthernetDevice::PCNET(dev) => dev.next_tx_buffer(len),
|
||||
EthernetDevice::E1000(dev) => dev.next_tx_buffer(len),
|
||||
EthernetDevice::VirtIO(dev) => dev.next_tx_buffer(len),
|
||||
}
|
||||
}
|
||||
|
@ -154,11 +161,11 @@ impl smoltcp::phy::TxToken for TxToken {
|
|||
{
|
||||
let config = self.device.config();
|
||||
let buf = self.device.next_tx_buffer(len);
|
||||
let res = f(buf);
|
||||
if config.is_debug_enabled() {
|
||||
debug!("NET Packet Transmitted");
|
||||
usr::hex::print_hex(buf);
|
||||
}
|
||||
let res = f(buf);
|
||||
self.device.transmit_packet(len);
|
||||
self.device.stats().tx_add(len as u64);
|
||||
res
|
||||
|
@ -243,20 +250,31 @@ impl Stats {
|
|||
}
|
||||
}
|
||||
|
||||
fn find_pci_io_base(vendor_id: u16, device_id: u16) -> Option<u16> {
|
||||
if let Some(mut pci_device) = sys::pci::find_device(vendor_id, device_id) {
|
||||
pci_device.enable_bus_mastering();
|
||||
let io_base = (pci_device.base_addresses[0] as u16) & 0xFFF0;
|
||||
Some(io_base)
|
||||
fn find_device(vendor_id: u16, device_id: u16) -> Option<DeviceConfig> {
|
||||
if let Some(mut dev) = sys::pci::find_device(vendor_id, device_id) {
|
||||
dev.enable_bus_mastering();
|
||||
Some(dev)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
const E1000_DEVICES: [u16; 8] = [
|
||||
0x1004, // 82543GC (Intel PRO/1000 T)
|
||||
0x100C, // 82544GC (Intel PRO/1000 T)
|
||||
0x100E, // 82540EM (Intel PRO/1000 MT)
|
||||
0x100F, // 82545EM (Intel PRO/1000 MT)
|
||||
0x107C, // 82541PI (Intel PRO/1000 GT)
|
||||
0x10D3, // 82574L
|
||||
0x10F5, // 82567LM
|
||||
0x153A, // I217-LM
|
||||
];
|
||||
|
||||
pub fn init() {
|
||||
let add = |mut device: EthernetDevice, name| {
|
||||
if let Some(mac) = device.config().mac() {
|
||||
log!("NET {} MAC {}", name, mac);
|
||||
let addr = format!("{}", mac).to_uppercase();
|
||||
log!("NET {} MAC {}", name, addr);
|
||||
|
||||
let config = smoltcp::iface::Config::new(mac.into());
|
||||
let iface = Interface::new(config, &mut device, time());
|
||||
|
@ -264,13 +282,28 @@ pub fn init() {
|
|||
*NET.lock() = Some((iface, device));
|
||||
}
|
||||
};
|
||||
if let Some(io) = find_pci_io_base(0x10EC, 0x8139) {
|
||||
add(EthernetDevice::RTL8139(nic::rtl8139::Device::new(io)), "RTL8139");
|
||||
if let Some(dev) = find_device(0x10EC, 0x8139) {
|
||||
let io = dev.io_base();
|
||||
let nic = nic::rtl8139::Device::new(io);
|
||||
add(EthernetDevice::RTL8139(nic), "RTL8139");
|
||||
}
|
||||
if let Some(io) = find_pci_io_base(0x1022, 0x2000) {
|
||||
add(EthernetDevice::PCNET(nic::pcnet::Device::new(io)), "PCNET");
|
||||
if let Some(dev) = find_device(0x1022, 0x2000) {
|
||||
let io = dev.io_base();
|
||||
let nic = nic::pcnet::Device::new(io);
|
||||
add(EthernetDevice::PCNET(nic), "PCNET");
|
||||
}
|
||||
if let Some(io) = find_pci_io_base(0x1AF4, 0x1000) {
|
||||
add(EthernetDevice::VirtIO(nic::virtio::Device::new(io)), "VirtIO");
|
||||
if let Some(dev) = find_device(0x1AF4, 0x1000) {
|
||||
let io = dev.io_base();
|
||||
let nic = nic::virtio::Device::new(io);
|
||||
add(EthernetDevice::VirtIO(nic), "VirtIO");
|
||||
}
|
||||
for id in E1000_DEVICES {
|
||||
if let Some(dev) = find_device(0x8086, id) {
|
||||
let io = dev.io_base();
|
||||
let mem = dev.mem_base();
|
||||
let bar = dev.bar_type();
|
||||
let nic = nic::e1000::Device::new(io, mem, bar);
|
||||
add(EthernetDevice::E1000(nic), "E1000");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,450 @@
|
|||
use crate::sys;
|
||||
use crate::sys::allocator::PhysBuf;
|
||||
use crate::sys::net::{EthernetDeviceIO, Config, Stats};
|
||||
use spin::Mutex;
|
||||
|
||||
use alloc::slice;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
use bit_field::BitField;
|
||||
use core::ptr;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use smoltcp::wire::EthernetAddress;
|
||||
use x86_64::instructions::port::Port;
|
||||
use x86_64::PhysAddr;
|
||||
|
||||
// https://pdos.csail.mit.edu/6.828/2019/readings/hardware/8254x_GBe_SDM.pdf
|
||||
|
||||
// Registers
|
||||
const REG_CTRL: u16 = 0x0000; // Device Control Register
|
||||
const REG_STATUS: u16 = 0x0008; // Device Status Register
|
||||
const REG_EECD: u16 = 0x0014; // EEPROM/Flash Control & Data Register
|
||||
const REG_ICR: u16 = 0x00C0; // Interrupt Cause Read Register
|
||||
const REG_IMS: u16 = 0x00D0; // Interrupt Mask Set/Read Register
|
||||
const REG_IMC: u16 = 0x00D8; // Interrupt Mask Clear Register
|
||||
const REG_RCTL: u16 = 0x0100; // Receive Control Register
|
||||
const REG_RDBAL: u16 = 0x2800; // Receive Descriptor Base Address Low
|
||||
const REG_RDBAH: u16 = 0x2804; // Receive Descriptor Base Address High
|
||||
const REG_RDLEN: u16 = 0x2808; // Receive Descriptor Length
|
||||
const REG_RDH: u16 = 0x2810; // Receive Descriptor Head
|
||||
const REG_RDT: u16 = 0x2818; // Receive Descriptor Tail
|
||||
const REG_TCTL: u16 = 0x0400; // Transmit Control Register
|
||||
const REG_TIPG: u16 = 0x0410; // Transmit IPG Register
|
||||
const REG_TDBAL: u16 = 0x3800; // Transmit Descriptor Base Address Low
|
||||
const REG_TDBAH: u16 = 0x3804; // Transmit Descriptor Base Address High
|
||||
const REG_TDLEN: u16 = 0x3808; // Transmit Descriptor Length
|
||||
const REG_TDH: u16 = 0x3810; // Transmit Descriptor Head
|
||||
const REG_TDT: u16 = 0x3818; // Transmit Descriptor Tail
|
||||
const REG_MTA: u16 = 0x5200; // Multicast Table Array
|
||||
|
||||
const CTRL_LRST: u32 = 1 << 3; // Link Reset
|
||||
const CTRL_ASDE: u32 = 1 << 5; // Auto-Speed Detection Enable
|
||||
const CTRL_SLU: u32 = 1 << 6; // Set Link Up
|
||||
const CTRL_RST: u32 = 1 << 26; // Reset
|
||||
|
||||
const ICR_LSC: u32 = 1 << 2; // Link Status Change
|
||||
const ICR_RXDMT0: u32 = 1 << 4; // Receive Descriptor Minimum Threshold Reached
|
||||
const ICR_RXT0: u32 = 1 << 7; // Receiver Timer Interrupt
|
||||
|
||||
const RCTL_EN: u32 = 1 << 1; // Receiver Enable
|
||||
const RCTL_BAM: u32 = 1 << 15; // Broadcast Accept Mode
|
||||
const RCTL_SECRC: u32 = 1 << 26; // Strip Ethernet CRC
|
||||
|
||||
// Buffer Sizes
|
||||
// const RCTL_BSIZE_256: u32 = 3 << 16;
|
||||
// const RCTL_BSIZE_512: u32 = 2 << 16;
|
||||
// const RCTL_BSIZE_1024: u32 = 1 << 16;
|
||||
// const RCTL_BSIZE_2048: u32 = 0 << 16;
|
||||
// const RCTL_BSIZE_4096: u32 = (3 << 16) | (1 << 25);
|
||||
// const RCTL_BSIZE_16384: u32 = (1 << 16) | (1 << 25);
|
||||
const RCTL_BSIZE_8192: u32 = (2 << 16) | (1 << 25);
|
||||
|
||||
const CMD_EOP: u8 = 1 << 0; // End of Packet
|
||||
const CMD_IFCS: u8 = 1 << 1; // Insert FCS
|
||||
const CMD_RS: u8 = 1 << 3; // Report Status
|
||||
|
||||
const TCTL_EN: u32 = 1 << 1; // Transmit Enable
|
||||
const TCTL_PSP: u32 = 1 << 3; // Pad Short Packets
|
||||
const TCTL_MULR: u32 = 1 << 28; // Multiple Request Support
|
||||
const TCTL_CT_SHIFT: u32 = 4; // Collision Threshold
|
||||
const TCTL_COLD_SHIFT: u32 = 12; // Collision Distance
|
||||
|
||||
// Transmit Descriptor Status Field
|
||||
const TSTA_DD: u8 = 1 << 0; // Descriptor Done
|
||||
|
||||
// Receive Descriptor Status Field
|
||||
const RSTA_DD: u8 = 1 << 0; // Descriptor Done
|
||||
const RSTA_EOP: u8 = 1 << 1; // End of Packet
|
||||
|
||||
// Device Status Register
|
||||
const DSTA_LU: u32 = 1 << 1; // Link Up Indication
|
||||
|
||||
// Transmit IPG Register
|
||||
const TIPG_IPGT: u32 = 10; // IPG Transmit Time
|
||||
const TIPG_IPGR1: u32 = 8; // IPG Receive Time 1
|
||||
const TIPG_IPGR2: u32 = 6; // IPG Receive Time 2
|
||||
|
||||
const IO_ADDR: u16 = 0x00;
|
||||
const IO_DATA: u16 = 0x04;
|
||||
|
||||
// NOTE: Must be a multiple of 8
|
||||
const RX_BUFFERS_COUNT: usize = 64;
|
||||
const TX_BUFFERS_COUNT: usize = 8;
|
||||
|
||||
// NOTE: Must be equals
|
||||
const BUFFER_SIZE: usize = 8192;
|
||||
const RCTL_BSIZE: u32 = RCTL_BSIZE_8192;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[repr(C, align(16))]
|
||||
struct RxDesc {
|
||||
addr: u64,
|
||||
len: u16,
|
||||
checksum: u16,
|
||||
status: u8,
|
||||
errors: u8,
|
||||
special: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[repr(C, align(16))]
|
||||
struct TxDesc {
|
||||
addr: u64,
|
||||
len: u16,
|
||||
cso: u8,
|
||||
cmd: u8,
|
||||
status: u8,
|
||||
css: u8,
|
||||
special: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Device {
|
||||
mem_base: PhysAddr,
|
||||
io_base: u16,
|
||||
bar_type: u16,
|
||||
has_eeprom: bool,
|
||||
config: Arc<Config>,
|
||||
stats: Arc<Stats>,
|
||||
rx_buffers: [PhysBuf; RX_BUFFERS_COUNT],
|
||||
tx_buffers: [PhysBuf; TX_BUFFERS_COUNT],
|
||||
rx_descs: Arc<Mutex<[RxDesc; RX_BUFFERS_COUNT]>>,
|
||||
tx_descs: Arc<Mutex<[TxDesc; TX_BUFFERS_COUNT]>>,
|
||||
rx_id: Arc<AtomicUsize>,
|
||||
tx_id: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn new(io_base: u16, mem_base: PhysAddr, bar_type: u16) -> Self {
|
||||
const RX: usize = RX_BUFFERS_COUNT;
|
||||
const TX: usize = TX_BUFFERS_COUNT;
|
||||
|
||||
let mut device = Self {
|
||||
bar_type: bar_type,
|
||||
io_base: io_base,
|
||||
mem_base: mem_base,
|
||||
has_eeprom: false,
|
||||
config: Arc::new(Config::new()),
|
||||
stats: Arc::new(Stats::new()),
|
||||
rx_buffers: [(); RX].map(|_| PhysBuf::new(BUFFER_SIZE)),
|
||||
tx_buffers: [(); TX].map(|_| PhysBuf::new(BUFFER_SIZE)),
|
||||
rx_descs: Arc::new(Mutex::new([(); RX].map(|_| RxDesc::default()))),
|
||||
tx_descs: Arc::new(Mutex::new([(); TX].map(|_| TxDesc::default()))),
|
||||
rx_id: Arc::new(AtomicUsize::new(0)),
|
||||
|
||||
// Before a transmission begin the id is incremented,
|
||||
// so the first transimission will start at 0.
|
||||
tx_id: Arc::new(AtomicUsize::new(TX - 1)),
|
||||
};
|
||||
device.reset();
|
||||
device.init();
|
||||
device
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
// Disable interrupts
|
||||
self.write(REG_IMC, 0xFFFF);
|
||||
|
||||
// Reset device
|
||||
let ctrl = self.read(REG_CTRL);
|
||||
self.write(REG_CTRL, ctrl | CTRL_RST); // Reset
|
||||
sys::time::nanowait(500); // TODO: How long should we wait?
|
||||
|
||||
// Disable interrupts again
|
||||
self.write(REG_IMC, 0xFFFF);
|
||||
|
||||
// Reset link
|
||||
let ctrl = self.read(REG_CTRL) & !CTRL_LRST;
|
||||
self.write(REG_CTRL, ctrl);
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
self.detect_eeprom();
|
||||
self.config.update_mac(self.read_mac());
|
||||
|
||||
self.init_rx();
|
||||
self.init_tx();
|
||||
self.link_up();
|
||||
|
||||
// TODO: Enable interrupts
|
||||
//self.write(REG_IMS, ICR_LSC | ICR_RXDMT0 | ICR_RXT0);
|
||||
self.write(REG_IMS, 0);
|
||||
|
||||
// Clear interrupts
|
||||
self.read(REG_ICR);
|
||||
}
|
||||
|
||||
fn init_rx(&mut self) {
|
||||
// Multicast Table Array
|
||||
for i in 0..128 {
|
||||
self.write(REG_MTA + i * 4, 0);
|
||||
}
|
||||
|
||||
// Descriptors
|
||||
let mut rx_descs = self.rx_descs.lock();
|
||||
let n = RX_BUFFERS_COUNT;
|
||||
for i in 0..n {
|
||||
rx_descs[i].addr = self.rx_buffers[i].addr();
|
||||
rx_descs[i].status = 0;
|
||||
}
|
||||
|
||||
let ptr = ptr::addr_of!(rx_descs[0]) as *const u8;
|
||||
let phys_addr = sys::allocator::phys_addr(ptr);
|
||||
|
||||
// Ring address and length
|
||||
self.write(REG_RDBAL, phys_addr.get_bits(0..32) as u32);
|
||||
self.write(REG_RDBAH, phys_addr.get_bits(32..64) as u32);
|
||||
self.write(REG_RDLEN, (n * 16) as u32);
|
||||
|
||||
// Ring head and tail
|
||||
self.write(REG_RDH, 0);
|
||||
self.write(REG_RDT, (n - 1) as u32);
|
||||
|
||||
// Control Register
|
||||
self.write(REG_RCTL, RCTL_EN | RCTL_BAM | RCTL_SECRC | RCTL_BSIZE);
|
||||
}
|
||||
|
||||
fn init_tx(&mut self) {
|
||||
// Descriptors
|
||||
let mut tx_descs = self.tx_descs.lock();
|
||||
let n = TX_BUFFERS_COUNT;
|
||||
for i in 0..n {
|
||||
tx_descs[i].addr = self.tx_buffers[i].addr();
|
||||
tx_descs[i].cmd = 0;
|
||||
tx_descs[i].status = TSTA_DD as u8;
|
||||
}
|
||||
|
||||
let ptr = ptr::addr_of!(tx_descs[0]) as *const _;
|
||||
let phys_addr = sys::allocator::phys_addr(ptr);
|
||||
|
||||
// Ring address and length
|
||||
self.write(REG_TDBAL, phys_addr.get_bits(0..32) as u32);
|
||||
self.write(REG_TDBAH, phys_addr.get_bits(32..64) as u32);
|
||||
self.write(REG_TDLEN, (n as u32) * 16);
|
||||
|
||||
// Ring head and tail
|
||||
self.write(REG_TDH, 0);
|
||||
self.write(REG_TDT, 0);
|
||||
|
||||
// Control Register
|
||||
// NOTE: MULR is only needed for Intel I217-LM
|
||||
self.write(REG_TCTL, TCTL_EN // Transmit Enable
|
||||
| TCTL_PSP // Pad Short Packets
|
||||
| (0x0F << TCTL_CT_SHIFT) // Collision Threshold
|
||||
| (0x3F << TCTL_COLD_SHIFT) // Collision Distance
|
||||
| TCTL_MULR); // Multiple Request Support
|
||||
|
||||
// Inter Packet Gap (3 x 10 bits)
|
||||
self.write(REG_TIPG, TIPG_IPGT // IPG Transmit Time
|
||||
| (TIPG_IPGR1 << 10) // IPG Receive Time 1
|
||||
| (TIPG_IPGR2 << 20)); // IPG Receive Time 2
|
||||
}
|
||||
|
||||
fn read_mac(&self) -> EthernetAddress {
|
||||
let mut mac = [0; 6];
|
||||
if self.has_eeprom {
|
||||
let mut tmp;
|
||||
tmp = self.read_eeprom(0);
|
||||
mac[0] = (tmp &0xff) as u8;
|
||||
mac[1] = (tmp >> 8) as u8;
|
||||
tmp = self.read_eeprom(1);
|
||||
mac[2] = (tmp &0xff) as u8;
|
||||
mac[3] = (tmp >> 8) as u8;
|
||||
tmp = self.read_eeprom(2);
|
||||
mac[4] = (tmp &0xff) as u8;
|
||||
mac[5] = (tmp >> 8) as u8;
|
||||
} else {
|
||||
unsafe {
|
||||
let phys = self.mem_base + 0x5400;
|
||||
let addr = sys::mem::phys_to_virt(phys).as_u64();
|
||||
let mac_32 = core::ptr::read_volatile(addr as *const u32);
|
||||
if mac_32 != 0 {
|
||||
let mac_8 = slice::from_raw_parts(addr as *const u8, 6);
|
||||
mac[..].clone_from_slice(mac_8);
|
||||
}
|
||||
}
|
||||
}
|
||||
EthernetAddress::from_bytes(&mac[..])
|
||||
}
|
||||
|
||||
fn link_up(&self) {
|
||||
let ctrl = self.read(REG_CTRL);
|
||||
self.write(REG_CTRL, ctrl | CTRL_SLU | CTRL_ASDE & !CTRL_LRST);
|
||||
}
|
||||
|
||||
fn write(&self, addr: u16, data: u32) {
|
||||
unsafe {
|
||||
if self.bar_type == 0 {
|
||||
let phys = self.mem_base + addr as u64;
|
||||
let addr = sys::mem::phys_to_virt(phys).as_u64() as *mut u32;
|
||||
core::ptr::write_volatile(addr, data);
|
||||
} else {
|
||||
Port::new(self.io_base + IO_ADDR).write(addr);
|
||||
Port::new(self.io_base + IO_DATA).write(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&self, addr: u16) -> u32 {
|
||||
unsafe {
|
||||
if self.bar_type == 0 {
|
||||
let phys = self.mem_base + addr as u64;
|
||||
let addr = sys::mem::phys_to_virt(phys).as_u64() as *mut u32;
|
||||
core::ptr::read_volatile(addr)
|
||||
} else {
|
||||
Port::new(self.io_base + IO_ADDR).write(addr);
|
||||
Port::new(self.io_base + IO_DATA).read()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_eeprom(&mut self) {
|
||||
self.write(REG_EECD, 1);
|
||||
let mut i = 0;
|
||||
while !self.has_eeprom && i < 1000 {
|
||||
self.has_eeprom = self.read(REG_EECD) & 0x10 > 0;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn read_eeprom(&self, addr: u16) -> u32 {
|
||||
let e = if self.has_eeprom { 4 } else { 0 };
|
||||
self.write(REG_EECD, 1 | ((addr as u32) << 2 * e));
|
||||
let mut res = 0;
|
||||
while res & (1 << 1 * e) == 0 {
|
||||
res = self.read(REG_EECD);
|
||||
}
|
||||
(res >> 16) & 0xFFFF
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn debug(&self) {
|
||||
// Registers
|
||||
debug!("NET E1000: ICR: {:#034b}", self.read(REG_ICR));
|
||||
debug!("NET E1000: CTRL: {:#034b}", self.read(REG_CTRL));
|
||||
debug!("NET E1000: STATUS: {:#034b}", self.read(REG_STATUS));
|
||||
debug!("NET E1000: RDH -> {}", self.read(REG_RDH));
|
||||
debug!("NET E1000: RDT -> {}", self.read(REG_RDT));
|
||||
debug!("NET E1000: TDH -> {}", self.read(REG_TDH));
|
||||
debug!("NET E1000: TDT -> {}", self.read(REG_TDT));
|
||||
|
||||
// Receive descriptors
|
||||
let rx_descs = self.rx_descs.lock();
|
||||
for i in 0..RX_BUFFERS_COUNT {
|
||||
let ptr = ptr::addr_of!(rx_descs[i]) as *const u8;
|
||||
let phy = sys::allocator::phys_addr(ptr);
|
||||
debug!(
|
||||
"NET E1000: [{}] {:?} ({:#X} -> {:#X})",
|
||||
i, rx_descs[i], ptr as u64, phy
|
||||
);
|
||||
}
|
||||
|
||||
// Transmit descriptors
|
||||
let tx_descs = self.tx_descs.lock();
|
||||
for i in 0..TX_BUFFERS_COUNT {
|
||||
let ptr = ptr::addr_of!(tx_descs[i]) as *const u8;
|
||||
let phy = sys::allocator::phys_addr(ptr);
|
||||
debug!(
|
||||
"NET E1000: [{}] {:?} ({:#X} -> {:#X})",
|
||||
i, tx_descs[i], ptr as u64, phy
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EthernetDeviceIO for Device {
|
||||
fn config(&self) -> Arc<Config> {
|
||||
self.config.clone()
|
||||
}
|
||||
|
||||
fn stats(&self) -> Arc<Stats> {
|
||||
self.stats.clone()
|
||||
}
|
||||
|
||||
fn receive_packet(&mut self) -> Option<Vec<u8>> {
|
||||
let icr = self.read(REG_ICR);
|
||||
self.write(REG_ICR, icr);
|
||||
|
||||
// Link Status Change
|
||||
if icr & ICR_LSC > 0 {
|
||||
if self.read(REG_STATUS) & DSTA_LU == 0 {
|
||||
self.link_up();
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// Receive Descriptor Minimum Threshold
|
||||
if icr & ICR_RXDMT0 > 0 {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// Receiver Timer Interrupt
|
||||
if icr & ICR_RXT0 > 0 {
|
||||
// TODO
|
||||
}
|
||||
|
||||
let rx_id = self.rx_id.load(Ordering::SeqCst);
|
||||
let mut rx_descs = self.rx_descs.lock();
|
||||
|
||||
// If hardware is done with the current descriptor
|
||||
if rx_descs[rx_id].status & RSTA_DD > 0 {
|
||||
if rx_descs[rx_id].status & RSTA_EOP == 0 {
|
||||
// FIXME: this is not the last descriptor for the packet
|
||||
}
|
||||
self.rx_id.store((rx_id + 1) % RX_BUFFERS_COUNT, Ordering::SeqCst);
|
||||
let n = rx_descs[rx_id].len as usize;
|
||||
let buf = self.rx_buffers[rx_id][0..n].to_vec();
|
||||
rx_descs[rx_id].status = 0; // Driver is done
|
||||
self.write(REG_RDT, rx_id as u32);
|
||||
return Some(buf);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn transmit_packet(&mut self, len: usize) {
|
||||
let tx_id = self.tx_id.load(Ordering::SeqCst);
|
||||
let mut tx_descs = self.tx_descs.lock();
|
||||
debug_assert_eq!(tx_descs[tx_id].addr, self.tx_buffers[tx_id].addr());
|
||||
|
||||
// Setup descriptor
|
||||
tx_descs[tx_id].len = len as u16;
|
||||
tx_descs[tx_id].cmd = CMD_EOP | CMD_IFCS | CMD_RS;
|
||||
tx_descs[tx_id].status = 0; // Driver is done
|
||||
|
||||
// Let the hardware handle the descriptor
|
||||
self.write(REG_TDT, ((tx_id + 1) % TX_BUFFERS_COUNT) as u32);
|
||||
}
|
||||
|
||||
fn next_tx_buffer(&mut self, len: usize) -> &mut [u8] {
|
||||
let tx_id = (self.tx_id.load(Ordering::SeqCst) + 1) % TX_BUFFERS_COUNT;
|
||||
self.tx_id.store(tx_id, Ordering::SeqCst);
|
||||
&mut self.tx_buffers[tx_id][0..len]
|
||||
}
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_driver() {
|
||||
assert_eq!(core::mem::size_of::<RxDesc>(), 16);
|
||||
assert_eq!(core::mem::size_of::<TxDesc>(), 16);
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod e1000;
|
||||
pub mod pcnet;
|
||||
pub mod rtl8139;
|
||||
pub mod virtio;
|
||||
|
|
|
@ -304,7 +304,7 @@ impl EthernetDeviceIO for Device {
|
|||
self.rx_des[rx_id * DE_LEN + 7].set_bit(DE_OWN, true);
|
||||
|
||||
rx_id = (rx_id + 1) % RX_BUFFERS_COUNT;
|
||||
self.rx_id.store(rx_id, Ordering::Relaxed);
|
||||
self.rx_id.store(rx_id, Ordering::SeqCst);
|
||||
|
||||
if end_of_packet {
|
||||
break;
|
||||
|
@ -333,7 +333,7 @@ impl EthernetDeviceIO for Device {
|
|||
// Give back ownership to the card
|
||||
self.tx_des[tx_id * DE_LEN + 7].set_bit(DE_OWN, true);
|
||||
|
||||
self.tx_id.store((tx_id + 1) % TX_BUFFERS_COUNT, Ordering::Relaxed);
|
||||
self.tx_id.store((tx_id + 1) % TX_BUFFERS_COUNT, Ordering::SeqCst);
|
||||
|
||||
if !is_buffer_owner(&self.tx_des, tx_id) {
|
||||
self.ports.write_csr_32(0, 1 << CSR0_TDMD); // Send all buffers
|
||||
|
|
|
@ -317,7 +317,7 @@ impl EthernetDeviceIO for Device {
|
|||
|
||||
fn next_tx_buffer(&mut self, len: usize) -> &mut [u8] {
|
||||
let tx_id = (self.tx_id.load(Ordering::SeqCst) + 1) % TX_BUFFERS_COUNT;
|
||||
self.tx_id.store(tx_id, Ordering::Relaxed);
|
||||
self.tx_id.store(tx_id, Ordering::SeqCst);
|
||||
&mut self.tx_buffers[tx_id][0..len]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use bit_field::BitField;
|
|||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
use x86_64::instructions::port::Port;
|
||||
use x86_64::PhysAddr;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct DeviceConfig {
|
||||
|
@ -82,6 +83,38 @@ impl DeviceConfig {
|
|||
data.set_bit(2, true);
|
||||
register.write(data);
|
||||
}
|
||||
|
||||
pub fn bar_type(&self) -> u16 {
|
||||
self.base_addresses[0].get_bits(1..3) as u16
|
||||
}
|
||||
|
||||
pub fn mem_base(&self) -> PhysAddr {
|
||||
debug_assert!(self.base_addresses[0].get_bit(0) == false);
|
||||
let bar0 = self.base_addresses[0];
|
||||
let bar1 = self.base_addresses[1];
|
||||
let addr = match bar0.get_bits(1..3) {
|
||||
0 => { // 32 bits
|
||||
(bar0 & 0xFFFFFFF0) as u64
|
||||
}
|
||||
1 => { // 16 bits
|
||||
(bar0 & 0x0000FFF0) as u64
|
||||
}
|
||||
2 => { // 64 bits
|
||||
let l = (bar0 & 0xFFFFFFF0) as u64;
|
||||
let h = (bar1 & 0xFFFFFFF0) as u64;
|
||||
l + (h << 32)
|
||||
}
|
||||
_ => { // TODO
|
||||
panic!("Unknown base address size");
|
||||
}
|
||||
};
|
||||
PhysAddr::new(addr)
|
||||
}
|
||||
|
||||
pub fn io_base(&self) -> u16 {
|
||||
debug_assert!(self.base_addresses[0].get_bit(0) == true);
|
||||
(self.base_addresses[0] as u16) & 0xFFF0
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
|
|
|
@ -2,12 +2,11 @@ use crate::api::console::Style;
|
|||
use crate::api::fs;
|
||||
use crate::api::process::ExitCode;
|
||||
|
||||
use alloc::format;
|
||||
use alloc::string::{String, ToString};
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
let n = args.len();
|
||||
if n != 3 {
|
||||
help();
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
for i in 1..n {
|
||||
match args[i] {
|
||||
"-h" | "--help" => {
|
||||
|
@ -17,12 +16,26 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
_ => continue,
|
||||
}
|
||||
}
|
||||
if n != 3 {
|
||||
help();
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
|
||||
if args[2].is_empty() {
|
||||
error!("Could not write to ''");
|
||||
return Err(ExitCode::Failure);
|
||||
}
|
||||
|
||||
let source = args[1];
|
||||
let dest = args[2];
|
||||
let dest = destination(args[1], args[2]);
|
||||
|
||||
if fs::is_dir(source) {
|
||||
error!("Could not copy directory '{}'", source);
|
||||
return Err(ExitCode::Failure);
|
||||
}
|
||||
|
||||
if let Ok(contents) = fs::read_to_bytes(source) {
|
||||
if fs::write(dest, &contents).is_ok() {
|
||||
if fs::write(&dest, &contents).is_ok() {
|
||||
Ok(())
|
||||
} else {
|
||||
error!("Could not write to '{}'", dest);
|
||||
|
@ -34,6 +47,16 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
}
|
||||
|
||||
fn destination(source: &str, dest: &str) -> String {
|
||||
debug_assert!(!dest.is_empty());
|
||||
let mut dest = dest.trim_end_matches('/').to_string();
|
||||
if dest.is_empty() || fs::is_dir(&dest) {
|
||||
let file = fs::filename(source);
|
||||
dest = format!("{}/{}", dest, file);
|
||||
}
|
||||
dest
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
|
@ -43,3 +66,24 @@ fn help() {
|
|||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_destination() {
|
||||
use crate::{usr, sys};
|
||||
|
||||
sys::fs::mount_mem();
|
||||
sys::fs::format_mem();
|
||||
usr::install::copy_files(false);
|
||||
|
||||
assert_eq!(destination("foo.txt", "bar.txt"), "bar.txt");
|
||||
|
||||
assert_eq!(destination("foo.txt", "/"), "/foo.txt");
|
||||
assert_eq!(destination("foo.txt", "/tmp"), "/tmp/foo.txt");
|
||||
assert_eq!(destination("foo.txt", "/tmp/"), "/tmp/foo.txt");
|
||||
|
||||
assert_eq!(destination("/usr/vinc/foo.txt", "/"), "/foo.txt");
|
||||
assert_eq!(destination("/usr/vinc/foo.txt", "/tmp"), "/tmp/foo.txt");
|
||||
assert_eq!(destination("/usr/vinc/foo.txt", "/tmp/"), "/tmp/foo.txt");
|
||||
|
||||
sys::fs::dismount();
|
||||
}
|
||||
|
|
|
@ -64,8 +64,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
Some(dhcpv4::Event::Deconfigured) => {}
|
||||
}
|
||||
|
||||
if let Some(d) = iface.poll_delay(time, &sockets) {
|
||||
syscall::sleep((d.total_micros() as f64) / 1000000.0);
|
||||
if let Some(delay) = iface.poll_delay(time, &sockets) {
|
||||
let d = (delay.total_micros() as f64) / 1000000.0;
|
||||
syscall::sleep(d.min(0.1)); // Don't sleep longer than 0.1s
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -152,7 +152,7 @@ pub fn resolve(name: &str) -> Result<IpAddress, ResponseCode> {
|
|||
loop {
|
||||
let mut data = vec![0; buf_len];
|
||||
if let Some(bytes) = syscall::read(handle, &mut data) {
|
||||
if bytes == 0 {
|
||||
if bytes < 28 {
|
||||
break;
|
||||
}
|
||||
data.resize(bytes, 0);
|
||||
|
|
125
src/usr/httpd.rs
125
src/usr/httpd.rs
|
@ -23,6 +23,7 @@ use smoltcp::wire::IpAddress;
|
|||
|
||||
const MAX_CONNECTIONS: usize = 32;
|
||||
const POLL_DELAY_DIV: usize = 128;
|
||||
const INDEX: [&str; 4] = ["", "/index.html", "/index.htm", "/index.txt"];
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Request {
|
||||
|
@ -212,16 +213,14 @@ fn get(req: &Request, res: &mut Response) {
|
|||
res.body.extend_from_slice(b"<h1>Moved Permanently</h1>\r\n");
|
||||
} else {
|
||||
let mut not_found = true;
|
||||
for autocomplete in
|
||||
&["", "/index.html", "/index.htm", "/index.txt"]
|
||||
{
|
||||
let real_path = format!("{}{}", res.real_path, autocomplete);
|
||||
for index in INDEX {
|
||||
let real_path = format!("{}{}", res.real_path, index);
|
||||
if fs::is_dir(&real_path) {
|
||||
continue;
|
||||
}
|
||||
if let Ok(buf) = fs::read_to_bytes(&real_path) {
|
||||
res.code = 200;
|
||||
res.mime = content_type(&res.real_path);
|
||||
res.mime = content_type(&real_path);
|
||||
let tmp;
|
||||
res.body.extend_from_slice(
|
||||
if res.mime.starts_with("text/") {
|
||||
|
@ -338,6 +337,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
i += 1;
|
||||
}
|
||||
|
||||
// NOTE: This specific format is needed by `join_path`
|
||||
let dir = format!("/{}", fs::realpath(&dir).trim_matches('/'));
|
||||
|
||||
if let Some((ref mut iface, ref mut device)) = *sys::net::NET.lock() {
|
||||
let mut sockets = SocketSet::new(vec![]);
|
||||
|
||||
|
@ -381,45 +383,57 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
None => continue,
|
||||
};
|
||||
if socket.may_recv() {
|
||||
let res = socket.recv(|buf| {
|
||||
if let Some(req) = Request::from(endpoint.addr, buf) {
|
||||
let mut res = Response::new(req.clone());
|
||||
let sep = if req.path == "/" { "" } else { "/" };
|
||||
res.real_path = format!(
|
||||
"{}{}{}",
|
||||
dir,
|
||||
sep,
|
||||
req.path.strip_suffix('/').unwrap_or(&req.path)
|
||||
).replace("//", "/");
|
||||
|
||||
match req.verb.as_str() {
|
||||
"GET" => {
|
||||
get(&req, &mut res)
|
||||
}
|
||||
"PUT" if !read_only => {
|
||||
put(&req, &mut res)
|
||||
}
|
||||
"DELETE" if !read_only => {
|
||||
delete(&req, &mut res)
|
||||
}
|
||||
_ => {
|
||||
let s = b"<h1>Bad Request</h1>\r\n";
|
||||
res.body.extend_from_slice(s);
|
||||
res.code = 400;
|
||||
res.mime = "text/html".to_string();
|
||||
}
|
||||
// The amount of octets queued in the receive buffer may be
|
||||
// larger than the contiguous slice returned by `recv` so
|
||||
// we need to loop over chunks of it until it is empty.
|
||||
let recv_queue = socket.recv_queue();
|
||||
let mut receiving = true;
|
||||
let mut buf = vec![];
|
||||
while receiving {
|
||||
let res = socket.recv(|chunk| {
|
||||
buf.extend_from_slice(chunk);
|
||||
if buf.len() < recv_queue {
|
||||
return (chunk.len(), None);
|
||||
}
|
||||
res.end();
|
||||
println!("{}", res);
|
||||
(buf.len(), Some(res))
|
||||
} else {
|
||||
(0, None)
|
||||
receiving = false;
|
||||
|
||||
let addr = endpoint.addr;
|
||||
if let Some(req) = Request::from(addr, &buf) {
|
||||
let mut res = Response::new(req.clone());
|
||||
res.real_path = join_path(&dir, &req.path);
|
||||
|
||||
match req.verb.as_str() {
|
||||
"GET" => {
|
||||
get(&req, &mut res)
|
||||
}
|
||||
"PUT" if !read_only => {
|
||||
put(&req, &mut res)
|
||||
}
|
||||
"DELETE" if !read_only => {
|
||||
delete(&req, &mut res)
|
||||
}
|
||||
_ => {
|
||||
let s = b"<h1>Bad Request</h1>\r\n";
|
||||
res.body.extend_from_slice(s);
|
||||
res.code = 400;
|
||||
res.mime = "text/html".to_string();
|
||||
}
|
||||
}
|
||||
res.end();
|
||||
println!("{}", res);
|
||||
(chunk.len(), Some(res))
|
||||
} else {
|
||||
(0, None)
|
||||
}
|
||||
});
|
||||
if receiving {
|
||||
continue;
|
||||
}
|
||||
});
|
||||
if let Ok(Some(res)) = res {
|
||||
*keep_alive = res.is_persistent();
|
||||
for chunk in res.buf.chunks(buf_len) {
|
||||
send_queue.push_back(chunk.to_vec());
|
||||
if let Ok(Some(res)) = res {
|
||||
*keep_alive = res.is_persistent();
|
||||
for chunk in res.buf.chunks(buf_len) {
|
||||
send_queue.push_back(chunk.to_vec());
|
||||
}
|
||||
}
|
||||
}
|
||||
if socket.can_send() {
|
||||
|
@ -437,10 +451,10 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
send_queue.clear();
|
||||
}
|
||||
}
|
||||
if let Some(wait_duration) = iface.poll_delay(time, &sockets) {
|
||||
let t = wait_duration.total_micros() / POLL_DELAY_DIV as u64;
|
||||
if t > 0 {
|
||||
syscall::sleep((t as f64) / 1000000.0);
|
||||
if let Some(delay) = iface.poll_delay(time, &sockets) {
|
||||
let d = delay.total_micros() / POLL_DELAY_DIV as u64;
|
||||
if d > 0 {
|
||||
syscall::sleep((d as f64) / 1000000.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -468,6 +482,15 @@ fn content_type(path: &str) -> String {
|
|||
}.to_string()
|
||||
}
|
||||
|
||||
// Join the requested file path to the root dir of the server
|
||||
fn join_path(dir: &str, path: &str) -> String {
|
||||
debug_assert!(dir.starts_with('/'));
|
||||
debug_assert!(path.starts_with('/'));
|
||||
let path = path.trim_matches('/');
|
||||
let sep = if dir == "/" || path == "" { "" } else { "/" };
|
||||
format!("{}{}{}", dir, sep, path)
|
||||
}
|
||||
|
||||
fn usage() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
|
@ -491,3 +514,13 @@ fn usage() {
|
|||
csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_join_path() {
|
||||
assert_eq!(join_path("/foo", "/bar/"), "/foo/bar");
|
||||
assert_eq!(join_path("/foo", "/bar"), "/foo/bar");
|
||||
assert_eq!(join_path("/foo", "/"), "/foo");
|
||||
assert_eq!(join_path("/", "/bar/"), "/bar");
|
||||
assert_eq!(join_path("/", "/bar"), "/bar");
|
||||
assert_eq!(join_path("/", "/"), "/");
|
||||
}
|
||||
|
|
|
@ -19,9 +19,10 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
|
||||
// TODO: Avoid doing copy+delete
|
||||
match usr::copy::main(args) {
|
||||
Ok(()) => usr::delete::main(&args[0..2]),
|
||||
_ => Err(ExitCode::Failure),
|
||||
if usr::copy::main(args).is_ok() {
|
||||
usr::delete::main(&args[0..2])
|
||||
} else {
|
||||
Err(ExitCode::Failure)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,12 +15,17 @@
|
|||
|
||||
<ul>
|
||||
<li>[x] CPU: Intel Core 2 Duo T7700</li>
|
||||
<li>[x] NIC: Realtek RTL8139</li>
|
||||
<li>[x] NIC: Intel PRO/1000 MT Desktop</li>
|
||||
<li>[x] NIC: Realtek RTL8139C</li>
|
||||
<li>[x] NIC: AMD PCnet-FAST III</li>
|
||||
</ul></li>
|
||||
<li><p>[x] VirtualBox</p>
|
||||
|
||||
<ul>
|
||||
<li>[x] NIC: PCnet-FAST III</li>
|
||||
<li>[x] NIC: Intel PRO/1000 MT Desktop (82540EM)</li>
|
||||
<li>[x] NIC: Intel PRO/1000 MT Server (82545EM)</li>
|
||||
<li>[x] NIC: Intel PRO/1000 T Server (82543GC)</li>
|
||||
<li>[x] NIC: AMD PCnet-FAST III</li>
|
||||
</ul></li>
|
||||
<li><p>[x] Bochs</p></li>
|
||||
</ul>
|
||||
|
@ -35,6 +40,9 @@
|
|||
<ul>
|
||||
<li>[x] MB: Asus P5K</li>
|
||||
<li>[x] CPU: Intel Core 2 Duo E6850</li>
|
||||
<li>[x] NIC: Intel PRO/1000 GT Desktop</li>
|
||||
<li>[x] NIC: Realtek RTL8139B</li>
|
||||
<li>[x] NIC: Realtek RTL8139C</li>
|
||||
<li>[x] NIC: Realtek RTL8139D</li>
|
||||
</ul></li>
|
||||
<li><p>[x] HP ProLiant MicroServer N40L (2012)</p>
|
||||
|
@ -43,6 +51,12 @@
|
|||
<li>[x] CPU: AMD Athlon II Dual Core</li>
|
||||
<li>[ ] NIC: HP NC107i</li>
|
||||
</ul></li>
|
||||
<li><p>[x] Lenovo ThinkCentre M83 SFF (2014)</p>
|
||||
|
||||
<ul>
|
||||
<li>[x] CPU: Intel Pentium G3220</li>
|
||||
<li>[x] NIC: Intel I217-LM</li>
|
||||
</ul></li>
|
||||
<li><p>[x] Intel NUC 5CPYH (2015)</p>
|
||||
|
||||
<ul>
|
||||
|
@ -58,19 +72,19 @@
|
|||
|
||||
<ul>
|
||||
<li>[x] CPU: Intel Core 2 Duo P8600</li>
|
||||
<li>[ ] NIC: Intel PRO/1000</li>
|
||||
<li>[x] NIC: Intel 82567LM</li>
|
||||
</ul></li>
|
||||
<li><p>[x] Lenovo ThinkPad X200 (2008)</p>
|
||||
|
||||
<ul>
|
||||
<li>[x] CPU: Intel Core 2 Duo P8600</li>
|
||||
<li>[ ] NIC: Intel 82567-LM</li>
|
||||
<li>[x] NIC: Intel 82567LM</li>
|
||||
</ul></li>
|
||||
<li><p>[x] Lenovo ThinkPad T440p (2013)</p>
|
||||
|
||||
<ul>
|
||||
<li>[x] CPU: Intel Core i5-4300M</li>
|
||||
<li>[ ] NIC: Intel I217-LM</li>
|
||||
<li>[x] NIC: Intel I217-LM</li>
|
||||
</ul></li>
|
||||
<li><p>[x] ASUS EeeBook E202SA (2017)</p>
|
||||
|
||||
|
|
Loading…
Reference in New Issue