Merge pull request #8 from redox-os/pci_drivers
Create a PCI driver daemon, which starts up AHCI driver
This commit is contained in:
commit
b527fc3c99
6
Makefile
6
Makefile
|
@ -24,6 +24,7 @@ all: $(KBUILD)/harddrive.bin
|
|||
clean:
|
||||
cargo clean
|
||||
cargo clean --manifest-path libstd/Cargo.toml
|
||||
cargo clean --manifest-path drivers/ahcid/Cargo.toml
|
||||
cargo clean --manifest-path drivers/ps2d/Cargo.toml
|
||||
cargo clean --manifest-path drivers/pcid/Cargo.toml
|
||||
cargo clean --manifest-path drivers/vesad/Cargo.toml
|
||||
|
@ -39,7 +40,7 @@ FORCE:
|
|||
|
||||
# Emulation
|
||||
QEMU=qemu-system-$(ARCH)
|
||||
QEMUFLAGS=-serial mon:stdio -d guest_errors
|
||||
QEMUFLAGS=-serial mon:stdio -d cpu_reset -d guest_errors
|
||||
ifeq ($(ARCH),arm)
|
||||
LD=$(ARCH)-none-eabi-ld
|
||||
QEMUFLAGS+=-cpu arm1176 -machine integratorcp
|
||||
|
@ -55,7 +56,7 @@ qemu: $(KBUILD)/harddrive.bin
|
|||
$(QEMU) $(QEMUFLAGS) -kernel $<
|
||||
else
|
||||
LD=ld
|
||||
QEMUFLAGS+=-machine q35 -smp 4
|
||||
QEMUFLAGS+=-machine q35 -smp 4 -m 256
|
||||
ifeq ($(kvm),yes)
|
||||
QEMUFLAGS+=-enable-kvm -cpu host
|
||||
endif
|
||||
|
@ -168,6 +169,7 @@ coreutils: \
|
|||
initfs/bin/realpath
|
||||
|
||||
$(BUILD)/initfs.rs: \
|
||||
initfs/bin/ahcid \
|
||||
initfs/bin/pcid \
|
||||
initfs/bin/ps2d \
|
||||
initfs/bin/vesad \
|
||||
|
|
|
@ -43,11 +43,14 @@ impl AreaFrameAllocator {
|
|||
}
|
||||
|
||||
impl FrameAllocator for AreaFrameAllocator {
|
||||
fn allocate_frame(&mut self) -> Option<Frame> {
|
||||
if let Some(area) = self.current_area {
|
||||
fn allocate_frames(&mut self, count: usize) -> Option<Frame> {
|
||||
if count == 0 {
|
||||
None
|
||||
} else if let Some(area) = self.current_area {
|
||||
// "Clone" the frame to return it if it's free. Frame doesn't
|
||||
// implement Clone, but we can construct an identical frame.
|
||||
let frame = Frame{ number: self.next_free_frame.number };
|
||||
let start_frame = Frame{ number: self.next_free_frame.number };
|
||||
let end_frame = Frame { number: self.next_free_frame.number + (count - 1) };
|
||||
|
||||
// the last frame of the current area
|
||||
let current_area_last_frame = {
|
||||
|
@ -55,27 +58,28 @@ impl FrameAllocator for AreaFrameAllocator {
|
|||
Frame::containing_address(PhysicalAddress::new(address as usize))
|
||||
};
|
||||
|
||||
if frame > current_area_last_frame {
|
||||
if end_frame > current_area_last_frame {
|
||||
// all frames of current area are used, switch to next area
|
||||
self.choose_next_area();
|
||||
} else if frame >= self.kernel_start && frame <= self.kernel_end {
|
||||
} else if (start_frame >= self.kernel_start && start_frame <= self.kernel_end)
|
||||
|| (end_frame >= self.kernel_start && end_frame <= self.kernel_end) {
|
||||
// `frame` is used by the kernel
|
||||
self.next_free_frame = Frame {
|
||||
number: self.kernel_end.number + 1
|
||||
};
|
||||
} else {
|
||||
// frame is unused, increment `next_free_frame` and return it
|
||||
self.next_free_frame.number += 1;
|
||||
return Some(frame);
|
||||
self.next_free_frame.number += count;
|
||||
return Some(start_frame);
|
||||
}
|
||||
// `frame` was not valid, try it again with the updated `next_free_frame`
|
||||
self.allocate_frame()
|
||||
self.allocate_frames(count)
|
||||
} else {
|
||||
None // no free frames left
|
||||
}
|
||||
}
|
||||
|
||||
fn deallocate_frame(&mut self, frame: Frame) {
|
||||
fn deallocate_frames(&mut self, frame: Frame, count: usize) {
|
||||
//panic!("AreaFrameAllocator::deallocate_frame: not supported: {:?}", frame);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,17 +69,27 @@ pub unsafe fn init(kernel_start: usize, kernel_end: usize) {
|
|||
|
||||
/// Allocate a frame
|
||||
pub fn allocate_frame() -> Option<Frame> {
|
||||
allocate_frames(1)
|
||||
}
|
||||
|
||||
/// Deallocate a frame
|
||||
pub fn deallocate_frame(frame: Frame) {
|
||||
deallocate_frames(frame, 1)
|
||||
}
|
||||
|
||||
/// Allocate a range of frames
|
||||
pub fn allocate_frames(count: usize) -> Option<Frame> {
|
||||
if let Some(ref mut allocator) = *ALLOCATOR.lock() {
|
||||
allocator.allocate_frame()
|
||||
allocator.allocate_frames(count)
|
||||
} else {
|
||||
panic!("frame allocator not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
/// Deallocate a frame
|
||||
pub fn deallocate_frame(frame: Frame) {
|
||||
/// Deallocate a range of frames frame
|
||||
pub fn deallocate_frames(frame: Frame, count: usize) {
|
||||
if let Some(ref mut allocator) = *ALLOCATOR.lock() {
|
||||
allocator.deallocate_frame(frame)
|
||||
allocator.deallocate_frames(frame, count)
|
||||
} else {
|
||||
panic!("frame allocator not initialized");
|
||||
}
|
||||
|
@ -151,6 +161,6 @@ impl Iterator for FrameIter {
|
|||
}
|
||||
|
||||
pub trait FrameAllocator {
|
||||
fn allocate_frame(&mut self) -> Option<Frame>;
|
||||
fn deallocate_frame(&mut self, frame: Frame);
|
||||
fn allocate_frames(&mut self, size: usize) -> Option<Frame>;
|
||||
fn deallocate_frames(&mut self, frame: Frame, size: usize);
|
||||
}
|
||||
|
|
9
drivers/ahcid/Cargo.toml
Normal file
9
drivers/ahcid/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "ahcid"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "*"
|
||||
io = { path = "../io/" }
|
||||
spin = "*"
|
||||
syscall = { path = "../../syscall/" }
|
116
drivers/ahcid/src/ahci/disk.rs
Normal file
116
drivers/ahcid/src/ahci/disk.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
use std::ptr;
|
||||
|
||||
use syscall::error::{Error, EIO, Result};
|
||||
|
||||
use super::dma::Dma;
|
||||
use super::hba::{HbaPort, HbaCmdTable, HbaCmdHeader};
|
||||
|
||||
pub struct Disk {
|
||||
id: usize,
|
||||
port: &'static mut HbaPort,
|
||||
size: u64,
|
||||
clb: Dma<[HbaCmdHeader; 32]>,
|
||||
ctbas: [Dma<HbaCmdTable>; 32],
|
||||
fb: Dma<[u8; 256]>,
|
||||
buf: Dma<[u8; 256 * 512]>
|
||||
}
|
||||
|
||||
impl Disk {
|
||||
pub fn new(id: usize, port: &'static mut HbaPort) -> Result<Self> {
|
||||
let mut clb = Dma::zeroed()?;
|
||||
let mut ctbas = [
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
];
|
||||
let mut fb = Dma::zeroed()?;
|
||||
let buf = Dma::zeroed()?;
|
||||
|
||||
port.init(&mut clb, &mut ctbas, &mut fb);
|
||||
|
||||
let size = unsafe { port.identify(&mut clb, &mut ctbas).unwrap_or(0) };
|
||||
|
||||
Ok(Disk {
|
||||
id: id,
|
||||
port: port,
|
||||
size: size,
|
||||
clb: clb,
|
||||
ctbas: ctbas,
|
||||
fb: fb,
|
||||
buf: buf
|
||||
})
|
||||
}
|
||||
|
||||
pub fn id(&self) -> usize {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u64 {
|
||||
self.size
|
||||
}
|
||||
|
||||
pub fn read(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||
let sectors = buffer.len()/512;
|
||||
if sectors > 0 {
|
||||
let mut sector: usize = 0;
|
||||
while sectors - sector >= 255 {
|
||||
if let Err(err) = self.port.ata_dma(block + sector as u64, 255, false, &mut self.clb, &mut self.ctbas, &mut self.buf) {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
unsafe { ptr::copy(self.buf.as_ptr(), buffer.as_mut_ptr().offset(sector as isize * 512), 255 * 512); }
|
||||
|
||||
sector += 255;
|
||||
}
|
||||
if sector < sectors {
|
||||
if let Err(err) = self.port.ata_dma(block + sector as u64, sectors - sector, false, &mut self.clb, &mut self.ctbas, &mut self.buf) {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
unsafe { ptr::copy(self.buf.as_ptr(), buffer.as_mut_ptr().offset(sector as isize * 512), (sectors - sector) * 512); }
|
||||
|
||||
sector += sectors - sector;
|
||||
}
|
||||
|
||||
Ok(sector * 512)
|
||||
} else {
|
||||
println!("Invalid request");
|
||||
Err(Error::new(EIO))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
|
||||
let sectors = (buffer.len() + 511)/512;
|
||||
if sectors > 0 {
|
||||
let mut sector: usize = 0;
|
||||
while sectors - sector >= 255 {
|
||||
unsafe { ptr::copy(buffer.as_ptr().offset(sector as isize * 512), self.buf.as_mut_ptr(), 255 * 512); }
|
||||
|
||||
if let Err(err) = self.port.ata_dma(block + sector as u64, 255, true, &mut self.clb, &mut self.ctbas, &mut self.buf) {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
sector += 255;
|
||||
}
|
||||
if sector < sectors {
|
||||
unsafe { ptr::copy(buffer.as_ptr().offset(sector as isize * 512), self.buf.as_mut_ptr(), (sectors - sector) * 512); }
|
||||
|
||||
if let Err(err) = self.port.ata_dma(block + sector as u64, sectors - sector, true, &mut self.clb, &mut self.ctbas, &mut self.buf) {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
sector += sectors - sector;
|
||||
}
|
||||
|
||||
Ok(sector * 512)
|
||||
} else {
|
||||
println!("Invalid request");
|
||||
Err(Error::new(EIO))
|
||||
}
|
||||
}
|
||||
}
|
76
drivers/ahcid/src/ahci/dma.rs
Normal file
76
drivers/ahcid/src/ahci/dma.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
use std::{mem, ptr};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use syscall::{self, Result};
|
||||
|
||||
struct PhysBox {
|
||||
address: usize,
|
||||
size: usize
|
||||
}
|
||||
|
||||
impl PhysBox {
|
||||
fn new(size: usize) -> Result<PhysBox> {
|
||||
let address = unsafe { syscall::physalloc(size)? };
|
||||
Ok(PhysBox {
|
||||
address: address,
|
||||
size: size
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PhysBox {
|
||||
fn drop(&mut self) {
|
||||
let _ = unsafe { syscall::physfree(self.address, self.size) };
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dma<T> {
|
||||
phys: PhysBox,
|
||||
virt: *mut T
|
||||
}
|
||||
|
||||
impl<T> Dma<T> {
|
||||
pub fn new(value: T) -> Result<Dma<T>> {
|
||||
let phys = PhysBox::new(mem::size_of::<T>())?;
|
||||
let virt = unsafe { syscall::physmap(phys.address, phys.size, syscall::MAP_WRITE)? } as *mut T;
|
||||
unsafe { ptr::write(virt, value); }
|
||||
Ok(Dma {
|
||||
phys: phys,
|
||||
virt: virt
|
||||
})
|
||||
}
|
||||
|
||||
pub fn zeroed() -> Result<Dma<T>> {
|
||||
let phys = PhysBox::new(mem::size_of::<T>())?;
|
||||
let virt = unsafe { syscall::physmap(phys.address, phys.size, syscall::MAP_WRITE)? } as *mut T;
|
||||
unsafe { ptr::write_bytes(virt as *mut u8, 0, phys.size); }
|
||||
Ok(Dma {
|
||||
phys: phys,
|
||||
virt: virt
|
||||
})
|
||||
}
|
||||
|
||||
pub fn physical(&self) -> usize {
|
||||
self.phys.address
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Dma<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.virt }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Dma<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.virt }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Dma<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { drop(ptr::read(self.virt)); }
|
||||
let _ = unsafe { syscall::physunmap(self.virt as usize) };
|
||||
}
|
||||
}
|
155
drivers/ahcid/src/ahci/fis.rs
Normal file
155
drivers/ahcid/src/ahci/fis.rs
Normal file
|
@ -0,0 +1,155 @@
|
|||
use io::Mmio;
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum FisType {
|
||||
/// Register FIS - host to device
|
||||
RegH2D = 0x27,
|
||||
/// Register FIS - device to host
|
||||
RegD2H = 0x34,
|
||||
/// DMA activate FIS - device to host
|
||||
DmaAct = 0x39,
|
||||
/// DMA setup FIS - bidirectional
|
||||
DmaSetup = 0x41,
|
||||
/// Data FIS - bidirectional
|
||||
Data = 0x46,
|
||||
/// BIST activate FIS - bidirectional
|
||||
Bist = 0x58,
|
||||
/// PIO setup FIS - device to host
|
||||
PioSetup = 0x5F,
|
||||
/// Set device bits FIS - device to host
|
||||
DevBits = 0xA1
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct FisRegH2D {
|
||||
// DWORD 0
|
||||
pub fis_type: Mmio<u8>, // FIS_TYPE_REG_H2D
|
||||
|
||||
pub pm: Mmio<u8>, // Port multiplier, 1: Command, 0: Control
|
||||
|
||||
pub command: Mmio<u8>, // Command register
|
||||
pub featurel: Mmio<u8>, // Feature register, 7:0
|
||||
|
||||
// DWORD 1
|
||||
pub lba0: Mmio<u8>, // LBA low register, 7:0
|
||||
pub lba1: Mmio<u8>, // LBA mid register, 15:8
|
||||
pub lba2: Mmio<u8>, // LBA high register, 23:16
|
||||
pub device: Mmio<u8>, // Device register
|
||||
|
||||
// DWORD 2
|
||||
pub lba3: Mmio<u8>, // LBA register, 31:24
|
||||
pub lba4: Mmio<u8>, // LBA register, 39:32
|
||||
pub lba5: Mmio<u8>, // LBA register, 47:40
|
||||
pub featureh: Mmio<u8>, // Feature register, 15:8
|
||||
|
||||
// DWORD 3
|
||||
pub countl: Mmio<u8>, // Count register, 7:0
|
||||
pub counth: Mmio<u8>, // Count register, 15:8
|
||||
pub icc: Mmio<u8>, // Isochronous command completion
|
||||
pub control: Mmio<u8>, // Control register
|
||||
|
||||
// DWORD 4
|
||||
pub rsv1: [Mmio<u8>; 4], // Reserved
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct FisRegD2H {
|
||||
// DWORD 0
|
||||
pub fis_type: Mmio<u8>, // FIS_TYPE_REG_D2H
|
||||
|
||||
pub pm: Mmio<u8>, // Port multiplier, Interrupt bit: 2
|
||||
|
||||
pub status: Mmio<u8>, // Status register
|
||||
pub error: Mmio<u8>, // Error register
|
||||
|
||||
// DWORD 1
|
||||
pub lba0: Mmio<u8>, // LBA low register, 7:0
|
||||
pub lba1: Mmio<u8>, // LBA mid register, 15:8
|
||||
pub lba2: Mmio<u8>, // LBA high register, 23:16
|
||||
pub device: Mmio<u8>, // Device register
|
||||
|
||||
// DWORD 2
|
||||
pub lba3: Mmio<u8>, // LBA register, 31:24
|
||||
pub lba4: Mmio<u8>, // LBA register, 39:32
|
||||
pub lba5: Mmio<u8>, // LBA register, 47:40
|
||||
pub rsv2: Mmio<u8>, // Reserved
|
||||
|
||||
// DWORD 3
|
||||
pub countl: Mmio<u8>, // Count register, 7:0
|
||||
pub counth: Mmio<u8>, // Count register, 15:8
|
||||
pub rsv3: [Mmio<u8>; 2], // Reserved
|
||||
|
||||
// DWORD 4
|
||||
pub rsv4: [Mmio<u8>; 4], // Reserved
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct FisData {
|
||||
// DWORD 0
|
||||
pub fis_type: Mmio<u8>, // FIS_TYPE_DATA
|
||||
|
||||
pub pm: Mmio<u8>, // Port multiplier
|
||||
|
||||
pub rsv1: [Mmio<u8>; 2], // Reserved
|
||||
|
||||
// DWORD 1 ~ N
|
||||
pub data: [Mmio<u8>; 252], // Payload
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct FisPioSetup {
|
||||
// DWORD 0
|
||||
pub fis_type: Mmio<u8>, // FIS_TYPE_PIO_SETUP
|
||||
|
||||
pub pm: Mmio<u8>, // Port multiplier, direction: 4 - device to host, interrupt: 2
|
||||
|
||||
pub status: Mmio<u8>, // Status register
|
||||
pub error: Mmio<u8>, // Error register
|
||||
|
||||
// DWORD 1
|
||||
pub lba0: Mmio<u8>, // LBA low register, 7:0
|
||||
pub lba1: Mmio<u8>, // LBA mid register, 15:8
|
||||
pub lba2: Mmio<u8>, // LBA high register, 23:16
|
||||
pub device: Mmio<u8>, // Device register
|
||||
|
||||
// DWORD 2
|
||||
pub lba3: Mmio<u8>, // LBA register, 31:24
|
||||
pub lba4: Mmio<u8>, // LBA register, 39:32
|
||||
pub lba5: Mmio<u8>, // LBA register, 47:40
|
||||
pub rsv2: Mmio<u8>, // Reserved
|
||||
|
||||
// DWORD 3
|
||||
pub countl: Mmio<u8>, // Count register, 7:0
|
||||
pub counth: Mmio<u8>, // Count register, 15:8
|
||||
pub rsv3: Mmio<u8>, // Reserved
|
||||
pub e_status: Mmio<u8>, // New value of status register
|
||||
|
||||
// DWORD 4
|
||||
pub tc: Mmio<u16>, // Transfer count
|
||||
pub rsv4: [Mmio<u8>; 2], // Reserved
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct FisDmaSetup {
|
||||
// DWORD 0
|
||||
pub fis_type: Mmio<u8>, // FIS_TYPE_DMA_SETUP
|
||||
|
||||
pub pm: Mmio<u8>, // Port multiplier, direction: 4 - device to host, interrupt: 2, auto-activate: 1
|
||||
|
||||
pub rsv1: [Mmio<u8>; 2], // Reserved
|
||||
|
||||
// DWORD 1&2
|
||||
pub dma_buffer_id: Mmio<u64>, /* DMA Buffer Identifier. Used to Identify DMA buffer in host memory. SATA Spec says host specific and not in Spec. Trying AHCI spec might work. */
|
||||
|
||||
// DWORD 3
|
||||
pub rsv3: Mmio<u32>, // More reserved
|
||||
|
||||
// DWORD 4
|
||||
pub dma_buffer_offset: Mmio<u32>, // Byte offset into buffer. First 2 bits must be 0
|
||||
|
||||
// DWORD 5
|
||||
pub transfer_count: Mmio<u32>, // Number of bytes to transfer. Bit 0 must be 0
|
||||
|
||||
// DWORD 6
|
||||
pub rsv6: Mmio<u32>, // Reserved
|
||||
}
|
351
drivers/ahcid/src/ahci/hba.rs
Normal file
351
drivers/ahcid/src/ahci/hba.rs
Normal file
|
@ -0,0 +1,351 @@
|
|||
use io::{Io, Mmio};
|
||||
|
||||
use std::mem::size_of;
|
||||
use std::ops::DerefMut;
|
||||
use std::{ptr, u32};
|
||||
|
||||
use syscall::error::{Error, Result, EIO};
|
||||
|
||||
use super::dma::Dma;
|
||||
use super::fis::{FisType, FisRegH2D};
|
||||
|
||||
const ATA_CMD_READ_DMA_EXT: u8 = 0x25;
|
||||
const ATA_CMD_WRITE_DMA_EXT: u8 = 0x35;
|
||||
const ATA_CMD_IDENTIFY: u8 = 0xEC;
|
||||
const ATA_DEV_BUSY: u8 = 0x80;
|
||||
const ATA_DEV_DRQ: u8 = 0x08;
|
||||
|
||||
const HBA_PORT_CMD_CR: u32 = 1 << 15;
|
||||
const HBA_PORT_CMD_FR: u32 = 1 << 14;
|
||||
const HBA_PORT_CMD_FRE: u32 = 1 << 4;
|
||||
const HBA_PORT_CMD_ST: u32 = 1;
|
||||
const HBA_PORT_IS_TFES: u32 = 1 << 30;
|
||||
const HBA_SSTS_PRESENT: u32 = 0x3;
|
||||
const HBA_SIG_ATA: u32 = 0x00000101;
|
||||
const HBA_SIG_ATAPI: u32 = 0xEB140101;
|
||||
const HBA_SIG_PM: u32 = 0x96690101;
|
||||
const HBA_SIG_SEMB: u32 = 0xC33C0101;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum HbaPortType {
|
||||
None,
|
||||
Unknown(u32),
|
||||
SATA,
|
||||
SATAPI,
|
||||
PM,
|
||||
SEMB,
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct HbaPort {
|
||||
pub clb: Mmio<u64>, // 0x00, command list base address, 1K-byte aligned
|
||||
pub fb: Mmio<u64>, // 0x08, FIS base address, 256-byte aligned
|
||||
pub is: Mmio<u32>, // 0x10, interrupt status
|
||||
pub ie: Mmio<u32>, // 0x14, interrupt enable
|
||||
pub cmd: Mmio<u32>, // 0x18, command and status
|
||||
pub rsv0: Mmio<u32>, // 0x1C, Reserved
|
||||
pub tfd: Mmio<u32>, // 0x20, task file data
|
||||
pub sig: Mmio<u32>, // 0x24, signature
|
||||
pub ssts: Mmio<u32>, // 0x28, SATA status (SCR0:SStatus)
|
||||
pub sctl: Mmio<u32>, // 0x2C, SATA control (SCR2:SControl)
|
||||
pub serr: Mmio<u32>, // 0x30, SATA error (SCR1:SError)
|
||||
pub sact: Mmio<u32>, // 0x34, SATA active (SCR3:SActive)
|
||||
pub ci: Mmio<u32>, // 0x38, command issue
|
||||
pub sntf: Mmio<u32>, // 0x3C, SATA notification (SCR4:SNotification)
|
||||
pub fbs: Mmio<u32>, // 0x40, FIS-based switch control
|
||||
pub rsv1: [Mmio<u32>; 11], // 0x44 ~ 0x6F, Reserved
|
||||
pub vendor: [Mmio<u32>; 4], // 0x70 ~ 0x7F, vendor specific
|
||||
}
|
||||
|
||||
impl HbaPort {
|
||||
pub fn probe(&self) -> HbaPortType {
|
||||
if self.ssts.readf(HBA_SSTS_PRESENT) {
|
||||
let sig = self.sig.read();
|
||||
match sig {
|
||||
HBA_SIG_ATA => HbaPortType::SATA,
|
||||
HBA_SIG_ATAPI => HbaPortType::SATAPI,
|
||||
HBA_SIG_PM => HbaPortType::PM,
|
||||
HBA_SIG_SEMB => HbaPortType::SEMB,
|
||||
_ => HbaPortType::Unknown(sig),
|
||||
}
|
||||
} else {
|
||||
HbaPortType::None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&mut self, clb: &mut Dma<[HbaCmdHeader; 32]>, ctbas: &mut [Dma<HbaCmdTable>; 32], fb: &mut Dma<[u8; 256]>) {
|
||||
self.stop();
|
||||
|
||||
self.clb.write(clb.physical() as u64);
|
||||
self.fb.write(fb.physical() as u64);
|
||||
|
||||
for i in 0..32 {
|
||||
let cmdheader = &mut clb[i];
|
||||
cmdheader.ctba.write(ctbas[i].physical() as u64);
|
||||
cmdheader.prdtl.write(0);
|
||||
}
|
||||
|
||||
self.start();
|
||||
}
|
||||
|
||||
pub unsafe fn identify(&mut self, clb: &mut Dma<[HbaCmdHeader; 32]>, ctbas: &mut [Dma<HbaCmdTable>; 32]) -> Option<u64> {
|
||||
self.is.write(u32::MAX);
|
||||
|
||||
let dest: Dma<[u16; 256]> = Dma::new([0; 256]).unwrap();
|
||||
|
||||
if let Some(slot) = self.slot() {
|
||||
let cmdheader = &mut clb[slot as usize];
|
||||
cmdheader.cfl.write(((size_of::<FisRegH2D>() / size_of::<u32>()) as u8));
|
||||
cmdheader.prdtl.write(1);
|
||||
|
||||
{
|
||||
let cmdtbl = &mut ctbas[slot as usize];
|
||||
ptr::write_bytes(cmdtbl.deref_mut() as *mut HbaCmdTable as *mut u8, 0, size_of::<HbaCmdTable>());
|
||||
|
||||
let prdt_entry = &mut cmdtbl.prdt_entry[0];
|
||||
prdt_entry.dba.write(dest.physical() as u64);
|
||||
prdt_entry.dbc.write(512 | 1);
|
||||
}
|
||||
|
||||
{
|
||||
let cmdfis = &mut *(ctbas[slot as usize].cfis.as_mut_ptr() as *mut FisRegH2D);
|
||||
|
||||
cmdfis.fis_type.write(FisType::RegH2D as u8);
|
||||
cmdfis.pm.write(1 << 7);
|
||||
cmdfis.command.write(ATA_CMD_IDENTIFY);
|
||||
cmdfis.device.write(0);
|
||||
cmdfis.countl.write(1);
|
||||
cmdfis.counth.write(0);
|
||||
}
|
||||
|
||||
while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) {}
|
||||
|
||||
self.ci.writef(1 << slot, true);
|
||||
|
||||
while self.ci.readf(1 << slot) {
|
||||
if self.is.readf(HBA_PORT_IS_TFES) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if self.is.readf(HBA_PORT_IS_TFES) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut serial = String::new();
|
||||
for word in 10..20 {
|
||||
let d = dest[word];
|
||||
let a = ((d >> 8) as u8) as char;
|
||||
if a != '\0' {
|
||||
serial.push(a);
|
||||
}
|
||||
let b = (d as u8) as char;
|
||||
if b != '\0' {
|
||||
serial.push(b);
|
||||
}
|
||||
}
|
||||
|
||||
let mut firmware = String::new();
|
||||
for word in 23..27 {
|
||||
let d = dest[word];
|
||||
let a = ((d >> 8) as u8) as char;
|
||||
if a != '\0' {
|
||||
firmware.push(a);
|
||||
}
|
||||
let b = (d as u8) as char;
|
||||
if b != '\0' {
|
||||
firmware.push(b);
|
||||
}
|
||||
}
|
||||
|
||||
let mut model = String::new();
|
||||
for word in 27..47 {
|
||||
let d = dest[word];
|
||||
let a = ((d >> 8) as u8) as char;
|
||||
if a != '\0' {
|
||||
model.push(a);
|
||||
}
|
||||
let b = (d as u8) as char;
|
||||
if b != '\0' {
|
||||
model.push(b);
|
||||
}
|
||||
}
|
||||
|
||||
let mut sectors = (dest[100] as u64) |
|
||||
((dest[101] as u64) << 16) |
|
||||
((dest[102] as u64) << 32) |
|
||||
((dest[103] as u64) << 48);
|
||||
|
||||
let lba_bits = if sectors == 0 {
|
||||
sectors = (dest[60] as u64) | ((dest[61] as u64) << 16);
|
||||
28
|
||||
} else {
|
||||
48
|
||||
};
|
||||
|
||||
println!(" + Serial: {} Firmware: {} Model: {} {}-bit LBA Size: {} MB",
|
||||
serial.trim(), firmware.trim(), model.trim(), lba_bits, sectors / 2048);
|
||||
|
||||
Some(sectors * 512)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self) {
|
||||
while self.cmd.readf(HBA_PORT_CMD_CR) {}
|
||||
|
||||
self.cmd.writef(HBA_PORT_CMD_FRE, true);
|
||||
self.cmd.writef(HBA_PORT_CMD_ST, true);
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.cmd.writef(HBA_PORT_CMD_ST, false);
|
||||
|
||||
while self.cmd.readf(HBA_PORT_CMD_FR | HBA_PORT_CMD_CR) {}
|
||||
|
||||
self.cmd.writef(HBA_PORT_CMD_FRE, false);
|
||||
}
|
||||
|
||||
pub fn slot(&self) -> Option<u32> {
|
||||
let slots = self.sact.read() | self.ci.read();
|
||||
for i in 0..32 {
|
||||
if slots & 1 << i == 0 {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn ata_dma(&mut self, block: u64, sectors: usize, write: bool, clb: &mut Dma<[HbaCmdHeader; 32]>, ctbas: &mut [Dma<HbaCmdTable>; 32], buf: &mut Dma<[u8; 256 * 512]>) -> Result<usize> {
|
||||
println!("AHCI {:X} DMA BLOCK: {:X} SECTORS: {} WRITE: {}", (self as *mut HbaPort) as usize, block, sectors, write);
|
||||
|
||||
assert!(sectors > 0 && sectors < 256);
|
||||
|
||||
self.is.write(u32::MAX);
|
||||
|
||||
if let Some(slot) = self.slot() {
|
||||
println!("Slot {}", slot);
|
||||
|
||||
let cmdheader = &mut clb[slot as usize];
|
||||
|
||||
cmdheader.cfl.write(((size_of::<FisRegH2D>() / size_of::<u32>()) as u8));
|
||||
cmdheader.cfl.writef(1 << 6, write);
|
||||
|
||||
cmdheader.prdtl.write(1);
|
||||
|
||||
{
|
||||
let cmdtbl = &mut ctbas[slot as usize];
|
||||
unsafe { ptr::write_bytes(cmdtbl.deref_mut() as *mut HbaCmdTable as *mut u8, 0, size_of::<HbaCmdTable>()) };
|
||||
|
||||
let prdt_entry = &mut cmdtbl.prdt_entry[0];
|
||||
prdt_entry.dba.write(buf.physical() as u64);
|
||||
prdt_entry.dbc.write(((sectors * 512) as u32) | 1);
|
||||
}
|
||||
|
||||
{
|
||||
let cmdfis = unsafe { &mut *(ctbas[slot as usize].cfis.as_mut_ptr() as *mut FisRegH2D) };
|
||||
|
||||
cmdfis.fis_type.write(FisType::RegH2D as u8);
|
||||
cmdfis.pm.write(1 << 7);
|
||||
if write {
|
||||
cmdfis.command.write(ATA_CMD_WRITE_DMA_EXT);
|
||||
} else {
|
||||
cmdfis.command.write(ATA_CMD_READ_DMA_EXT);
|
||||
}
|
||||
|
||||
cmdfis.lba0.write(block as u8);
|
||||
cmdfis.lba1.write((block >> 8) as u8);
|
||||
cmdfis.lba2.write((block >> 16) as u8);
|
||||
|
||||
cmdfis.device.write(1 << 6);
|
||||
|
||||
cmdfis.lba3.write((block >> 24) as u8);
|
||||
cmdfis.lba4.write((block >> 32) as u8);
|
||||
cmdfis.lba5.write((block >> 40) as u8);
|
||||
|
||||
cmdfis.countl.write(sectors as u8);
|
||||
cmdfis.counth.write((sectors >> 8) as u8);
|
||||
}
|
||||
|
||||
println!("Busy Wait");
|
||||
while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) {}
|
||||
|
||||
self.ci.writef(1 << slot, true);
|
||||
|
||||
println!("Completion Wait");
|
||||
while self.ci.readf(1 << slot) {
|
||||
if self.is.readf(HBA_PORT_IS_TFES) {
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
}
|
||||
|
||||
if self.is.readf(HBA_PORT_IS_TFES) {
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
|
||||
Ok(sectors * 512)
|
||||
} else {
|
||||
println!("No Command Slots");
|
||||
Err(Error::new(EIO))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct HbaMem {
|
||||
pub cap: Mmio<u32>, // 0x00, Host capability
|
||||
pub ghc: Mmio<u32>, // 0x04, Global host control
|
||||
pub is: Mmio<u32>, // 0x08, Interrupt status
|
||||
pub pi: Mmio<u32>, // 0x0C, Port implemented
|
||||
pub vs: Mmio<u32>, // 0x10, Version
|
||||
pub ccc_ctl: Mmio<u32>, // 0x14, Command completion coalescing control
|
||||
pub ccc_pts: Mmio<u32>, // 0x18, Command completion coalescing ports
|
||||
pub em_loc: Mmio<u32>, // 0x1C, Enclosure management location
|
||||
pub em_ctl: Mmio<u32>, // 0x20, Enclosure management control
|
||||
pub cap2: Mmio<u32>, // 0x24, Host capabilities extended
|
||||
pub bohc: Mmio<u32>, // 0x28, BIOS/OS handoff control and status
|
||||
pub rsv: [Mmio<u8>; 116], // 0x2C - 0x9F, Reserved
|
||||
pub vendor: [Mmio<u8>; 96], // 0xA0 - 0xFF, Vendor specific registers
|
||||
pub ports: [HbaPort; 32], // 0x100 - 0x10FF, Port control registers
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct HbaPrdtEntry {
|
||||
dba: Mmio<u64>, // Data base address
|
||||
rsv0: Mmio<u32>, // Reserved
|
||||
dbc: Mmio<u32>, // Byte count, 4M max, interrupt = 1
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct HbaCmdTable {
|
||||
// 0x00
|
||||
cfis: [Mmio<u8>; 64], // Command FIS
|
||||
|
||||
// 0x40
|
||||
acmd: [Mmio<u8>; 16], // ATAPI command, 12 or 16 bytes
|
||||
|
||||
// 0x50
|
||||
rsv: [Mmio<u8>; 48], // Reserved
|
||||
|
||||
// 0x80
|
||||
prdt_entry: [HbaPrdtEntry; 65536], // Physical region descriptor table entries, 0 ~ 65535
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct HbaCmdHeader {
|
||||
// DW0
|
||||
cfl: Mmio<u8>, /* Command FIS length in DWORDS, 2 ~ 16, atapi: 4, write - host to device: 2, prefetchable: 1 */
|
||||
pm: Mmio<u8>, // Reset - 0x80, bist: 0x40, clear busy on ok: 0x20, port multiplier
|
||||
|
||||
prdtl: Mmio<u16>, // Physical region descriptor table length in entries
|
||||
|
||||
// DW1
|
||||
prdbc: Mmio<u32>, // Physical region descriptor byte count transferred
|
||||
|
||||
// DW2, 3
|
||||
ctba: Mmio<u64>, // Command table descriptor base address
|
||||
|
||||
// DW4 - 7
|
||||
rsv1: [Mmio<u32>; 4], // Reserved
|
||||
}
|
37
drivers/ahcid/src/ahci/mod.rs
Normal file
37
drivers/ahcid/src/ahci/mod.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use io::Io;
|
||||
|
||||
use self::disk::Disk;
|
||||
use self::hba::{HbaMem, HbaPortType};
|
||||
|
||||
pub mod disk;
|
||||
pub mod dma;
|
||||
pub mod fis;
|
||||
pub mod hba;
|
||||
|
||||
pub fn disks(base: usize, irq: u8) -> Vec<Disk> {
|
||||
println!(" + AHCI on: {:X} IRQ: {}", base as usize, irq);
|
||||
|
||||
let pi = unsafe { &mut *(base as *mut HbaMem) }.pi.read();
|
||||
let ret: Vec<Disk> = (0..32)
|
||||
.filter(|&i| pi & 1 << i as i32 == 1 << i as i32)
|
||||
.filter_map(|i| {
|
||||
let port = &mut unsafe { &mut *(base as *mut HbaMem) }.ports[i];
|
||||
let port_type = port.probe();
|
||||
println!("{}: {:?}", i, port_type);
|
||||
match port_type {
|
||||
HbaPortType::SATA => {
|
||||
match Disk::new(i, port) {
|
||||
Ok(disk) => Some(disk),
|
||||
Err(err) => {
|
||||
println!("{}: {}", i, err);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
ret
|
||||
}
|
48
drivers/ahcid/src/main.rs
Normal file
48
drivers/ahcid/src/main.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
#![feature(asm)]
|
||||
#![feature(question_mark)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate io;
|
||||
extern crate spin;
|
||||
extern crate syscall;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::{env, thread, usize};
|
||||
use syscall::{iopl, physmap, physunmap, MAP_WRITE, Packet, Scheme};
|
||||
|
||||
use scheme::DiskScheme;
|
||||
|
||||
pub mod ahci;
|
||||
pub mod scheme;
|
||||
|
||||
fn main() {
|
||||
let mut args = env::args().skip(1);
|
||||
|
||||
let bar_str = args.next().expect("ahcid: no address provided");
|
||||
let bar = usize::from_str_radix(&bar_str, 16).expect("ahcid: failed to parse address");
|
||||
|
||||
let irq_str = args.next().expect("ahcid: no irq provided");
|
||||
let irq = irq_str.parse::<u8>().expect("ahcid: failed to parse irq");
|
||||
|
||||
thread::spawn(move || {
|
||||
unsafe {
|
||||
iopl(3).expect("ahcid: failed to get I/O permission");
|
||||
asm!("cli" :::: "intel", "volatile");
|
||||
}
|
||||
|
||||
let address = unsafe { physmap(bar, 4096, MAP_WRITE).expect("ahcid: failed to map address") };
|
||||
{
|
||||
let mut socket = File::create(":disk").expect("ahcid: failed to create disk scheme");
|
||||
let scheme = DiskScheme::new(ahci::disks(address, irq));
|
||||
loop {
|
||||
let mut packet = Packet::default();
|
||||
socket.read(&mut packet).expect("ahcid: failed to read disk scheme");
|
||||
scheme.handle(&mut packet);
|
||||
socket.write(&mut packet).expect("ahcid: failed to read disk scheme");
|
||||
}
|
||||
}
|
||||
unsafe { let _ = physunmap(address); }
|
||||
});
|
||||
}
|
85
drivers/ahcid/src/scheme.rs
Normal file
85
drivers/ahcid/src/scheme.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::{cmp, str};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::Mutex;
|
||||
use syscall::{Error, EBADF, EINVAL, ENOENT, Result, Scheme, SEEK_CUR, SEEK_END, SEEK_SET};
|
||||
|
||||
use ahci::disk::Disk;
|
||||
|
||||
pub struct DiskScheme {
|
||||
disks: Box<[Arc<Mutex<Disk>>]>,
|
||||
handles: Mutex<BTreeMap<usize, (Arc<Mutex<Disk>>, usize)>>,
|
||||
next_id: AtomicUsize
|
||||
}
|
||||
|
||||
impl DiskScheme {
|
||||
pub fn new(disks: Vec<Disk>) -> DiskScheme {
|
||||
let mut disk_arcs = vec![];
|
||||
for disk in disks {
|
||||
disk_arcs.push(Arc::new(Mutex::new(disk)));
|
||||
}
|
||||
|
||||
DiskScheme {
|
||||
disks: disk_arcs.into_boxed_slice(),
|
||||
handles: Mutex::new(BTreeMap::new()),
|
||||
next_id: AtomicUsize::new(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for DiskScheme {
|
||||
fn open(&self, path: &[u8], _flags: usize) -> Result<usize> {
|
||||
let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
let i = path_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
if let Some(disk) = self.disks.get(i) {
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.lock().insert(id, (disk.clone(), 0));
|
||||
Ok(id)
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let mut handles = self.handles.lock();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut disk = handle.0.lock();
|
||||
let count = disk.read((handle.1 as u64)/512, buf)?;
|
||||
handle.1 += count;
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
|
||||
let mut handles = self.handles.lock();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut disk = handle.0.lock();
|
||||
let count = disk.write((handle.1 as u64)/512, buf)?;
|
||||
handle.1 += count;
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
|
||||
let mut handles = self.handles.lock();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let len = handle.0.lock().size() as usize;
|
||||
handle.1 = match whence {
|
||||
SEEK_SET => cmp::min(len, pos),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(len as isize, handle.1 as isize + pos as isize)) as usize,
|
||||
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
Ok(handle.1)
|
||||
}
|
||||
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
let mut handles = self.handles.lock();
|
||||
handles.remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
|
@ -2,5 +2,12 @@
|
|||
name = "pcid"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
rustc-serialize = { git = "https://github.com/redox-os/rustc-serialize.git" }
|
||||
toml = "*"
|
||||
|
||||
[dependencies.syscall]
|
||||
path = "../../syscall/"
|
||||
|
||||
[replace]
|
||||
"rustc-serialize:0.3.19" = { git = "https://github.com/redox-os/rustc-serialize.git" }
|
||||
|
|
14
drivers/pcid/src/config.rs
Normal file
14
drivers/pcid/src/config.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
#[derive(Debug, Default, RustcDecodable)]
|
||||
pub struct Config {
|
||||
pub drivers: Vec<DriverConfig>
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, RustcDecodable)]
|
||||
pub struct DriverConfig {
|
||||
pub name: Option<String>,
|
||||
pub class: Option<u8>,
|
||||
pub subclass: Option<u8>,
|
||||
pub vendor: Option<u16>,
|
||||
pub device: Option<u16>,
|
||||
pub command: Option<Vec<String>>
|
||||
}
|
|
@ -1,79 +1,152 @@
|
|||
#![feature(asm)]
|
||||
|
||||
extern crate rustc_serialize;
|
||||
extern crate syscall;
|
||||
extern crate toml;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::process::Command;
|
||||
use std::thread;
|
||||
use syscall::iopl;
|
||||
|
||||
use config::Config;
|
||||
use pci::{Pci, PciBar, PciClass};
|
||||
|
||||
mod config;
|
||||
mod pci;
|
||||
|
||||
fn enumerate_pci() {
|
||||
println!("PCI BS/DV/FN VEND:DEVI CL.SC.IN.RV");
|
||||
fn main() {
|
||||
thread::spawn(|| {
|
||||
let mut config = Config::default();
|
||||
|
||||
let pci = Pci::new();
|
||||
for bus in pci.buses() {
|
||||
for dev in bus.devs() {
|
||||
for func in dev.funcs() {
|
||||
if let Some(header) = func.header() {
|
||||
print!("PCI {:>02X}/{:>02X}/{:>02X} {:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X}",
|
||||
bus.num, dev.num, func.num,
|
||||
header.vendor_id, header.device_id,
|
||||
header.class, header.subclass, header.interface, header.revision);
|
||||
let mut args = env::args().skip(1);
|
||||
if let Some(config_path) = args.next() {
|
||||
if let Ok(mut config_file) = File::open(&config_path) {
|
||||
let mut config_data = String::new();
|
||||
if let Ok(_) = config_file.read_to_string(&mut config_data) {
|
||||
config = toml::decode_str(&config_data).unwrap_or(Config::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let pci_class = PciClass::from(header.class);
|
||||
print!(" {:?}", pci_class);
|
||||
match pci_class {
|
||||
PciClass::Storage => match header.subclass {
|
||||
0x01 => {
|
||||
print!(" IDE");
|
||||
println!("{:?}", config);
|
||||
|
||||
unsafe { iopl(3).unwrap() };
|
||||
|
||||
println!("PCI BS/DV/FN VEND:DEVI CL.SC.IN.RV");
|
||||
|
||||
let pci = Pci::new();
|
||||
for bus in pci.buses() {
|
||||
for dev in bus.devs() {
|
||||
for func in dev.funcs() {
|
||||
if let Some(header) = func.header() {
|
||||
print!("PCI {:>02X}/{:>02X}/{:>02X} {:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X}",
|
||||
bus.num, dev.num, func.num,
|
||||
header.vendor_id, header.device_id,
|
||||
header.class, header.subclass, header.interface, header.revision);
|
||||
|
||||
let pci_class = PciClass::from(header.class);
|
||||
print!(" {:?}", pci_class);
|
||||
match pci_class {
|
||||
PciClass::Storage => match header.subclass {
|
||||
0x01 => {
|
||||
print!(" IDE");
|
||||
},
|
||||
0x06 => {
|
||||
print!(" SATA");
|
||||
},
|
||||
_ => ()
|
||||
},
|
||||
0x06 => {
|
||||
print!(" SATA");
|
||||
},
|
||||
_ => ()
|
||||
},
|
||||
PciClass::SerialBus => match header.subclass {
|
||||
0x03 => match header.interface {
|
||||
0x00 => {
|
||||
print!(" UHCI");
|
||||
},
|
||||
0x10 => {
|
||||
print!(" OHCI");
|
||||
},
|
||||
0x20 => {
|
||||
print!(" EHCI");
|
||||
},
|
||||
0x30 => {
|
||||
print!(" XHCI");
|
||||
PciClass::SerialBus => match header.subclass {
|
||||
0x03 => match header.interface {
|
||||
0x00 => {
|
||||
print!(" UHCI");
|
||||
},
|
||||
0x10 => {
|
||||
print!(" OHCI");
|
||||
},
|
||||
0x20 => {
|
||||
print!(" EHCI");
|
||||
},
|
||||
0x30 => {
|
||||
print!(" XHCI");
|
||||
},
|
||||
_ => ()
|
||||
},
|
||||
_ => ()
|
||||
},
|
||||
_ => ()
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..header.bars.len() {
|
||||
match PciBar::from(header.bars[i]) {
|
||||
PciBar::None => (),
|
||||
PciBar::Memory(address) => print!(" {}={:>08X}", i, address),
|
||||
PciBar::Port(address) => print!(" {}={:>04X}", i, address)
|
||||
for i in 0..header.bars.len() {
|
||||
match PciBar::from(header.bars[i]) {
|
||||
PciBar::None => (),
|
||||
PciBar::Memory(address) => print!(" {}={:>08X}", i, address),
|
||||
PciBar::Port(address) => print!(" {}={:>04X}", i, address)
|
||||
}
|
||||
}
|
||||
|
||||
print!("\n");
|
||||
|
||||
for driver in config.drivers.iter() {
|
||||
if let Some(class) = driver.class {
|
||||
if class != header.class { continue; }
|
||||
}
|
||||
|
||||
if let Some(subclass) = driver.subclass {
|
||||
if subclass != header.subclass { continue; }
|
||||
}
|
||||
|
||||
if let Some(vendor) = driver.vendor {
|
||||
if vendor != header.vendor_id { continue; }
|
||||
}
|
||||
|
||||
if let Some(device) = driver.device {
|
||||
if device != header.device_id { continue; }
|
||||
}
|
||||
|
||||
if let Some(ref args) = driver.command {
|
||||
let mut args = args.iter();
|
||||
if let Some(program) = args.next() {
|
||||
let mut command = Command::new(program);
|
||||
for arg in args {
|
||||
let bar_arg = |i| -> String {
|
||||
match PciBar::from(header.bars[i]) {
|
||||
PciBar::None => String::new(),
|
||||
PciBar::Memory(address) => format!("{:>08X}", address),
|
||||
PciBar::Port(address) => format!("{:>04X}", address)
|
||||
}
|
||||
};
|
||||
let arg = match arg.as_str() {
|
||||
"$BAR0" => bar_arg(0),
|
||||
"$BAR1" => bar_arg(1),
|
||||
"$BAR2" => bar_arg(2),
|
||||
"$BAR3" => bar_arg(3),
|
||||
"$BAR4" => bar_arg(4),
|
||||
"$BAR5" => bar_arg(5),
|
||||
"$IRQ" => format!("{}", header.interrupt_line),
|
||||
_ => arg.clone()
|
||||
};
|
||||
command.arg(&arg);
|
||||
}
|
||||
|
||||
match command.spawn() {
|
||||
Ok(mut child) => match child.wait() {
|
||||
Ok(_status) => (), //println!("pcid: waited for {}: {:?}", line, status.code()),
|
||||
Err(err) => println!("pcid: failed to wait for {:?}: {}", command, err)
|
||||
},
|
||||
Err(err) => println!("pcid: failed to execute {:?}: {}", command, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Driver: {:?}", driver);
|
||||
}
|
||||
}
|
||||
|
||||
print!("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
thread::spawn(||{
|
||||
unsafe { iopl(3).unwrap() };
|
||||
|
||||
enumerate_pci();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use std::fs::File;
|
|||
use std::io::{Read, Write};
|
||||
use std::{slice, thread};
|
||||
use ransid::{Console, Event};
|
||||
use syscall::{physmap, physunmap, Packet, Result, Scheme, MAP_WRITE, MAP_WRITE_COMBINE};
|
||||
use syscall::{physmap, physunmap, Packet, Result, Scheme, EVENT_READ, MAP_WRITE, MAP_WRITE_COMBINE};
|
||||
|
||||
use display::Display;
|
||||
use mode_info::VBEModeInfo;
|
||||
|
@ -26,7 +26,8 @@ pub mod primitive;
|
|||
struct DisplayScheme {
|
||||
console: RefCell<Console>,
|
||||
display: RefCell<Display>,
|
||||
input: RefCell<VecDeque<u8>>
|
||||
input: RefCell<VecDeque<u8>>,
|
||||
requested: RefCell<usize>
|
||||
}
|
||||
|
||||
impl Scheme for DisplayScheme {
|
||||
|
@ -42,6 +43,12 @@ impl Scheme for DisplayScheme {
|
|||
Ok(id)
|
||||
}
|
||||
|
||||
fn fevent(&self, _id: usize, flags: usize) -> Result<usize> {
|
||||
*self.requested.borrow_mut() = flags;
|
||||
println!("fevent {:X}", flags);
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fsync(&self, _id: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
@ -121,7 +128,8 @@ fn main() {
|
|||
unsafe { slice::from_raw_parts_mut(onscreen as *mut u32, size) },
|
||||
unsafe { slice::from_raw_parts_mut(offscreen as *mut u32, size) }
|
||||
)),
|
||||
input: RefCell::new(VecDeque::new())
|
||||
input: RefCell::new(VecDeque::new()),
|
||||
requested: RefCell::new(0)
|
||||
};
|
||||
|
||||
let mut blocked = VecDeque::new();
|
||||
|
@ -129,18 +137,36 @@ fn main() {
|
|||
let mut packet = Packet::default();
|
||||
socket.read(&mut packet).expect("vesad: failed to read display scheme");
|
||||
//println!("vesad: {:?}", packet);
|
||||
|
||||
// If it is a read packet, and there is no data, block it. Otherwise, handle packet
|
||||
if packet.a == syscall::number::SYS_READ && packet.d > 0 && scheme.input.borrow().is_empty() {
|
||||
blocked.push_back(packet);
|
||||
} else {
|
||||
scheme.handle(&mut packet);
|
||||
socket.write(&packet).expect("vesad: failed to write display scheme");
|
||||
}
|
||||
|
||||
// If there are blocked readers, and data is available, handle them
|
||||
while ! scheme.input.borrow().is_empty() {
|
||||
if let Some(mut packet) = blocked.pop_front() {
|
||||
scheme.handle(&mut packet);
|
||||
socket.write(&packet).expect("vesad: failed to write display scheme");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are requested events, and data is available, send a notification
|
||||
if ! scheme.input.borrow().is_empty() && *scheme.requested.borrow() & EVENT_READ == EVENT_READ {
|
||||
let event_packet = Packet {
|
||||
id: 0,
|
||||
a: syscall::number::SYS_FEVENT,
|
||||
b: 0,
|
||||
c: EVENT_READ,
|
||||
d: scheme.input.borrow().len()
|
||||
};
|
||||
socket.write(&event_packet).expect("vesad: failed to write display scheme");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
initfs:bin/vesad
|
||||
initfs:bin/ps2d
|
||||
#initfs:bin/pcid
|
||||
initfs:bin/pcid initfs:etc/pcid.toml
|
||||
#initfs:bin/example
|
||||
initfs:bin/login display: initfs:bin/ion
|
||||
|
|
5
initfs/etc/pcid.toml
Normal file
5
initfs/etc/pcid.toml
Normal file
|
@ -0,0 +1,5 @@
|
|||
[[drivers]]
|
||||
name = "AHCI storage"
|
||||
class = 1
|
||||
subclass = 6
|
||||
command = ["initfs:bin/ahcid", "$BAR5", "$IRQ"]
|
|
@ -1,9 +1,10 @@
|
|||
use alloc::arc::Arc;
|
||||
use alloc::boxed::Box;
|
||||
use collections::{BTreeMap, Vec};
|
||||
use collections::{BTreeMap, Vec, VecDeque};
|
||||
use spin::Mutex;
|
||||
|
||||
use arch;
|
||||
use syscall::data::Event;
|
||||
use super::file::File;
|
||||
use super::memory::{Grant, Memory, SharedMemory};
|
||||
|
||||
|
@ -39,6 +40,8 @@ pub struct Context {
|
|||
pub grants: Arc<Mutex<Vec<Grant>>>,
|
||||
/// The current working directory
|
||||
pub cwd: Arc<Mutex<Vec<u8>>>,
|
||||
/// Kernel events
|
||||
pub events: Arc<Mutex<VecDeque<Event>>>,
|
||||
/// The process environment
|
||||
pub env: Arc<Mutex<BTreeMap<Box<[u8]>, Arc<Mutex<Vec<u8>>>>>>,
|
||||
/// The open files in the scheme
|
||||
|
@ -60,6 +63,7 @@ impl Context {
|
|||
stack: None,
|
||||
grants: Arc::new(Mutex::new(Vec::new())),
|
||||
cwd: Arc::new(Mutex::new(Vec::new())),
|
||||
events: Arc::new(Mutex::new(VecDeque::new())),
|
||||
env: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
files: Arc::new(Mutex::new(Vec::new()))
|
||||
}
|
||||
|
|
80
kernel/context/event.rs
Normal file
80
kernel/context/event.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
use alloc::arc::{Arc, Weak};
|
||||
use collections::{BTreeMap, VecDeque};
|
||||
use spin::{Mutex, Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use context;
|
||||
use syscall::data::Event;
|
||||
|
||||
type EventList = Weak<Mutex<VecDeque<Event>>>;
|
||||
|
||||
type Registry = BTreeMap<(usize, usize), BTreeMap<(usize, usize), EventList>>;
|
||||
|
||||
static REGISTRY: Once<RwLock<Registry>> = Once::new();
|
||||
|
||||
/// Initialize registry, called if needed
|
||||
fn init_registry() -> RwLock<Registry> {
|
||||
RwLock::new(Registry::new())
|
||||
}
|
||||
|
||||
/// Get the global schemes list, const
|
||||
fn registry() -> RwLockReadGuard<'static, Registry> {
|
||||
REGISTRY.call_once(init_registry).read()
|
||||
}
|
||||
|
||||
/// Get the global schemes list, mutable
|
||||
pub fn registry_mut() -> RwLockWriteGuard<'static, Registry> {
|
||||
REGISTRY.call_once(init_registry).write()
|
||||
}
|
||||
|
||||
pub fn register(fd: usize, scheme_id: usize, id: usize) -> bool {
|
||||
let (context_id, events) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().expect("event::register: No context");
|
||||
let context = context_lock.read();
|
||||
(context.id, Arc::downgrade(&context.events))
|
||||
};
|
||||
|
||||
let mut registry = registry_mut();
|
||||
let entry = registry.entry((scheme_id, id)).or_insert_with(|| {
|
||||
BTreeMap::new()
|
||||
});
|
||||
if entry.contains_key(&(context_id, fd)) {
|
||||
false
|
||||
} else {
|
||||
entry.insert((context_id, fd), events);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unregister(fd: usize, scheme_id: usize, id: usize) {
|
||||
let mut registry = registry_mut();
|
||||
|
||||
let mut remove = false;
|
||||
if let Some(entry) = registry.get_mut(&(scheme_id, id)) {
|
||||
entry.remove(&(context::context_id(), fd));
|
||||
|
||||
if entry.is_empty() {
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
|
||||
if remove {
|
||||
registry.remove(&(scheme_id, id));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trigger(scheme_id: usize, id: usize, flags: usize, data: usize) {
|
||||
let registry = registry();
|
||||
if let Some(event_lists) = registry.get(&(scheme_id, id)) {
|
||||
for entry in event_lists.iter() {
|
||||
if let Some(event_list_lock) = entry.1.upgrade() {
|
||||
let mut event_list = event_list_lock.lock();
|
||||
event_list.push_back(Event {
|
||||
id: (entry.0).1,
|
||||
flags: flags,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,9 @@ mod list;
|
|||
/// Context switch function
|
||||
mod switch;
|
||||
|
||||
/// Event handling
|
||||
pub mod event;
|
||||
|
||||
/// File struct - defines a scheme and a file number
|
||||
pub mod file;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use super::{contexts, Context, Status, CONTEXT_ID};
|
|||
/// # Safety
|
||||
///
|
||||
/// Do not call this while holding locks!
|
||||
pub unsafe fn switch() {
|
||||
pub unsafe fn switch() -> bool {
|
||||
use core::ops::DerefMut;
|
||||
|
||||
// Set the global lock to avoid the unsafe operations below from causing issues
|
||||
|
@ -48,10 +48,9 @@ pub unsafe fn switch() {
|
|||
}
|
||||
|
||||
if to_ptr as usize == 0 {
|
||||
// TODO: Sleep, wait for interrupt
|
||||
// Unset global lock if no context found
|
||||
arch::context::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
//println!("Switch {} to {}", (&*from_ptr).id, (&*to_ptr).id);
|
||||
|
@ -64,4 +63,6 @@ pub unsafe fn switch() {
|
|||
CONTEXT_ID.store((&mut *to_ptr).id, Ordering::SeqCst);
|
||||
|
||||
(&mut *from_ptr).arch.switch_to(&mut (&mut *to_ptr).arch);
|
||||
|
||||
true
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@ pub fn cpu_id() -> usize {
|
|||
}
|
||||
|
||||
pub extern fn userspace_init() {
|
||||
assert_eq!(syscall::chdir(b"initfs:bin/"), Ok(0));
|
||||
assert_eq!(syscall::chdir(b"initfs:bin"), Ok(0));
|
||||
|
||||
assert_eq!(syscall::open(b"debug:", 0), Ok(0));
|
||||
assert_eq!(syscall::open(b"debug:", 0), Ok(1));
|
||||
|
@ -162,8 +162,11 @@ pub extern fn kmain() {
|
|||
loop {
|
||||
unsafe {
|
||||
interrupt::disable();
|
||||
context::switch();
|
||||
interrupt::enable_and_nop();
|
||||
if context::switch() {
|
||||
interrupt::enable_and_nop();
|
||||
} else {
|
||||
interrupt::enable_and_halt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
use collections::VecDeque;
|
||||
use core::str;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
|
||||
use spin::{Mutex, Once};
|
||||
|
||||
use context;
|
||||
use syscall::error::*;
|
||||
use syscall::flag::EVENT_READ;
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
pub static DEBUG_SCHEME_ID: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
/// Input
|
||||
static INPUT: Once<Mutex<VecDeque<u8>>> = Once::new();
|
||||
|
||||
|
@ -17,7 +21,13 @@ fn init_input() -> Mutex<VecDeque<u8>> {
|
|||
/// Get the global schemes list, const
|
||||
#[no_mangle]
|
||||
pub extern fn debug_input(b: u8) {
|
||||
INPUT.call_once(init_input).lock().push_back(b)
|
||||
let len = {
|
||||
let mut input = INPUT.call_once(init_input).lock();
|
||||
input.push_back(b);
|
||||
input.len()
|
||||
};
|
||||
|
||||
context::event::trigger(DEBUG_SCHEME_ID.load(Ordering::SeqCst), 0, EVENT_READ, len);
|
||||
}
|
||||
|
||||
pub struct DebugScheme;
|
||||
|
@ -48,7 +58,7 @@ impl Scheme for DebugScheme {
|
|||
if i > 0 {
|
||||
return Ok(i);
|
||||
} else {
|
||||
unsafe { context::switch(); }
|
||||
unsafe { context::switch(); } //TODO: Block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +72,10 @@ impl Scheme for DebugScheme {
|
|||
Ok(buffer.len())
|
||||
}
|
||||
|
||||
fn fevent(&self, _file: usize, _flags: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fsync(&self, _file: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
|
94
kernel/scheme/event.rs
Normal file
94
kernel/scheme/event.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
use alloc::arc::{Arc, Weak};
|
||||
use collections::{BTreeMap, VecDeque};
|
||||
use core::mem;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::{Mutex, RwLock};
|
||||
|
||||
use context;
|
||||
use syscall::data::Event;
|
||||
use syscall::error::*;
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
pub struct EventScheme {
|
||||
next_id: AtomicUsize,
|
||||
handles: RwLock<BTreeMap<usize, Weak<Mutex<VecDeque<Event>>>>>
|
||||
}
|
||||
|
||||
impl EventScheme {
|
||||
pub fn new() -> EventScheme {
|
||||
EventScheme {
|
||||
next_id: AtomicUsize::new(0),
|
||||
handles: RwLock::new(BTreeMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for EventScheme {
|
||||
fn open(&self, _path: &[u8], _flags: usize) -> Result<usize> {
|
||||
let handle = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
context.events.clone()
|
||||
};
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Arc::downgrade(&handle));
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn dup(&self, id: usize) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
let handle_weak = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
handle_weak.upgrade().ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let new_id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(new_id, Arc::downgrade(&handle));
|
||||
Ok(new_id)
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
let handle_weak = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
handle_weak.upgrade().ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let event_size = mem::size_of::<Event>();
|
||||
let len = buf.len()/event_size;
|
||||
if len > 0 {
|
||||
loop {
|
||||
let mut i = 0;
|
||||
{
|
||||
let mut events = handle.lock();
|
||||
while ! events.is_empty() && i < len {
|
||||
let event = events.pop_front().unwrap();
|
||||
unsafe { *(buf.as_mut_ptr() as *mut Event).offset(i as isize) = event; }
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
return Ok(i * event_size);
|
||||
} else {
|
||||
unsafe { context::switch(); } //TODO: Block
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn fsync(&self, id: usize) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle_weak = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
handle_weak.upgrade().ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
|
@ -8,15 +8,15 @@
|
|||
|
||||
use alloc::arc::Arc;
|
||||
use alloc::boxed::Box;
|
||||
|
||||
use collections::BTreeMap;
|
||||
|
||||
use core::sync::atomic::Ordering;
|
||||
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use syscall::error::*;
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
use self::debug::DebugScheme;
|
||||
use self::debug::{DEBUG_SCHEME_ID, DebugScheme};
|
||||
use self::event::EventScheme;
|
||||
use self::env::EnvScheme;
|
||||
use self::initfs::InitFsScheme;
|
||||
use self::irq::IrqScheme;
|
||||
|
@ -25,6 +25,9 @@ use self::root::RootScheme;
|
|||
/// Debug scheme
|
||||
pub mod debug;
|
||||
|
||||
/// Kernel events
|
||||
pub mod event;
|
||||
|
||||
/// Environmental variables
|
||||
pub mod env;
|
||||
|
||||
|
@ -74,7 +77,7 @@ impl SchemeList {
|
|||
}
|
||||
|
||||
/// Create a new scheme.
|
||||
pub fn insert(&mut self, name: Box<[u8]>, scheme: Arc<Box<Scheme + Send + Sync>>) -> Result<&Arc<Box<Scheme + Send + Sync>>> {
|
||||
pub fn insert(&mut self, name: Box<[u8]>, scheme: Arc<Box<Scheme + Send + Sync>>) -> Result<usize> {
|
||||
if self.names.contains_key(&name) {
|
||||
return Err(Error::new(EEXIST));
|
||||
}
|
||||
|
@ -97,7 +100,7 @@ impl SchemeList {
|
|||
assert!(self.map.insert(id, scheme).is_none());
|
||||
assert!(self.names.insert(name, id).is_none());
|
||||
|
||||
Ok(self.map.get(&id).expect("Failed to insert new scheme. ID is out of bounds."))
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,7 +111,8 @@ static SCHEMES: Once<RwLock<SchemeList>> = Once::new();
|
|||
fn init_schemes() -> RwLock<SchemeList> {
|
||||
let mut list: SchemeList = SchemeList::new();
|
||||
list.insert(Box::new(*b""), Arc::new(Box::new(RootScheme::new()))).expect("failed to insert root scheme");
|
||||
list.insert(Box::new(*b"debug"), Arc::new(Box::new(DebugScheme))).expect("failed to insert debug scheme");
|
||||
DEBUG_SCHEME_ID.store(list.insert(Box::new(*b"debug"), Arc::new(Box::new(DebugScheme))).expect("failed to insert debug scheme"), Ordering::SeqCst);
|
||||
list.insert(Box::new(*b"event"), Arc::new(Box::new(EventScheme::new()))).expect("failed to insert event scheme");
|
||||
list.insert(Box::new(*b"env"), Arc::new(Box::new(EnvScheme::new()))).expect("failed to insert env scheme");
|
||||
list.insert(Box::new(*b"initfs"), Arc::new(Box::new(InitFsScheme::new()))).expect("failed to insert initfs scheme");
|
||||
list.insert(Box::new(*b"irq"), Arc::new(Box::new(IrqScheme))).expect("failed to insert irq scheme");
|
||||
|
|
|
@ -38,7 +38,8 @@ impl Scheme for RootScheme {
|
|||
return Err(Error::new(EEXIST));
|
||||
}
|
||||
let inner = Arc::new(UserInner::new(context));
|
||||
schemes.insert(path.to_vec().into_boxed_slice(), Arc::new(Box::new(UserScheme::new(Arc::downgrade(&inner))))).expect("failed to insert user scheme");
|
||||
let id = schemes.insert(path.to_vec().into_boxed_slice(), Arc::new(Box::new(UserScheme::new(Arc::downgrade(&inner))))).expect("failed to insert user scheme");
|
||||
inner.scheme_id.store(id, Ordering::SeqCst);
|
||||
inner
|
||||
};
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ use syscall::number::*;
|
|||
use syscall::scheme::Scheme;
|
||||
|
||||
pub struct UserInner {
|
||||
pub scheme_id: AtomicUsize,
|
||||
next_id: AtomicUsize,
|
||||
context: Weak<RwLock<Context>>,
|
||||
todo: Mutex<VecDeque<Packet>>,
|
||||
|
@ -24,7 +25,8 @@ pub struct UserInner {
|
|||
impl UserInner {
|
||||
pub fn new(context: Weak<RwLock<Context>>) -> UserInner {
|
||||
UserInner {
|
||||
next_id: AtomicUsize::new(0),
|
||||
scheme_id: AtomicUsize::new(0),
|
||||
next_id: AtomicUsize::new(1),
|
||||
context: context,
|
||||
todo: Mutex::new(VecDeque::new()),
|
||||
done: Mutex::new(BTreeMap::new())
|
||||
|
@ -52,7 +54,7 @@ impl UserInner {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe { context::switch(); }
|
||||
unsafe { context::switch(); } //TODO: Block
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +165,7 @@ impl UserInner {
|
|||
if i > 0 {
|
||||
return Ok(i * packet_size);
|
||||
} else {
|
||||
unsafe { context::switch(); }
|
||||
unsafe { context::switch(); } //TODO: Block
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -177,7 +179,14 @@ impl UserInner {
|
|||
let mut i = 0;
|
||||
while i < len {
|
||||
let packet = unsafe { *(buf.as_ptr() as *const Packet).offset(i as isize) };
|
||||
self.done.lock().insert(packet.id, packet.a);
|
||||
if packet.id == 0 {
|
||||
match packet.a {
|
||||
SYS_FEVENT => context::event::trigger(self.scheme_id.load(Ordering::SeqCst), packet.b, packet.c, packet.d),
|
||||
_ => println!("Unknown scheme -> kernel message {}", packet.a)
|
||||
}
|
||||
} else {
|
||||
self.done.lock().insert(packet.id, packet.a);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
@ -230,7 +239,12 @@ impl Scheme for UserScheme {
|
|||
|
||||
fn seek(&self, file: usize, position: usize, whence: usize) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
inner.call(SYS_FSYNC, file, position, whence)
|
||||
inner.call(SYS_LSEEK, file, position, whence)
|
||||
}
|
||||
|
||||
fn fevent(&self, file: usize, flags: usize) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
inner.call(SYS_FEVENT, file, flags, 0)
|
||||
}
|
||||
|
||||
fn fstat(&self, file: usize, stat: &mut Stat) -> Result<usize> {
|
||||
|
|
|
@ -72,6 +72,8 @@ pub fn close(fd: usize) -> Result<usize> {
|
|||
file
|
||||
};
|
||||
|
||||
context::event::unregister(fd, file.scheme, file.number);
|
||||
|
||||
let scheme = {
|
||||
let schemes = scheme::schemes();
|
||||
let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
|
||||
|
@ -98,6 +100,26 @@ pub fn dup(fd: usize) -> Result<usize> {
|
|||
scheme.dup(file.number)
|
||||
}
|
||||
|
||||
/// Register events for file
|
||||
pub fn fevent(fd: usize, flags: usize) -> Result<usize> {
|
||||
let file = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
|
||||
file
|
||||
};
|
||||
|
||||
let scheme = {
|
||||
let schemes = scheme::schemes();
|
||||
let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
|
||||
scheme.clone()
|
||||
};
|
||||
scheme.fevent(file.number, flags)?;
|
||||
context::event::register(fd, file.scheme, file.number);
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Get the canonical path of the file
|
||||
pub fn fpath(fd: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let file = {
|
||||
|
|
|
@ -45,9 +45,13 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize
|
|||
SYS_CLONE => clone(b, stack),
|
||||
SYS_YIELD => sched_yield(),
|
||||
SYS_GETCWD => getcwd(validate_slice_mut(b as *mut u8, c)?),
|
||||
SYS_FEVENT => fevent(b, c),
|
||||
SYS_FPATH => fpath(b, validate_slice_mut(c as *mut u8, d)?),
|
||||
SYS_PHYSALLOC => physalloc(b),
|
||||
SYS_PHYSFREE => physfree(b, c),
|
||||
SYS_PHYSMAP => physmap(b, c, d),
|
||||
SYS_PHYSUNMAP => physunmap(b),
|
||||
SYS_VIRTTOPHYS => virttophys(b),
|
||||
_ => {
|
||||
println!("Unknown syscall {}", a);
|
||||
Err(Error::new(ENOSYS))
|
||||
|
|
|
@ -8,7 +8,7 @@ use spin::Mutex;
|
|||
|
||||
use arch;
|
||||
use arch::externs::memcpy;
|
||||
use arch::memory::allocate_frame;
|
||||
use arch::memory::{allocate_frame, allocate_frames, deallocate_frames, Frame};
|
||||
use arch::paging::{ActivePageTable, InactivePageTable, Page, PhysicalAddress, VirtualAddress, entry};
|
||||
use arch::paging::temporary_page::TemporaryPage;
|
||||
use arch::start::usermode;
|
||||
|
@ -535,6 +535,16 @@ pub fn iopl(_level: usize) -> Result<usize> {
|
|||
Ok(0)
|
||||
}
|
||||
|
||||
pub fn physalloc(size: usize) -> Result<usize> {
|
||||
allocate_frames((size + 4095)/4096).ok_or(Error::new(ENOMEM)).map(|frame| frame.start_address().get())
|
||||
}
|
||||
|
||||
pub fn physfree(physical_address: usize, size: usize) -> Result<usize> {
|
||||
deallocate_frames(Frame::containing_address(PhysicalAddress::new(physical_address)), (size + 4095)/4096);
|
||||
//TODO: Check that no double free occured
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
//TODO: verify exlusive access to physical memory
|
||||
pub fn physmap(physical_address: usize, size: usize, flags: usize) -> Result<usize> {
|
||||
if size == 0 {
|
||||
|
@ -617,8 +627,15 @@ pub fn sched_yield() -> Result<usize> {
|
|||
Ok(0)
|
||||
}
|
||||
|
||||
pub fn virttophys(virtual_address: usize) -> Result<usize> {
|
||||
let active_table = unsafe { ActivePageTable::new() };
|
||||
match active_table.translate(VirtualAddress::new(virtual_address)) {
|
||||
Some(physical_address) => Ok(physical_address.get()),
|
||||
None => Err(Error::new(EFAULT))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result<usize> {
|
||||
//TODO: Implement status_ptr and options
|
||||
loop {
|
||||
{
|
||||
let mut exited = false;
|
||||
|
@ -644,6 +661,6 @@ pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result<usize> {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe { context::switch(); }
|
||||
unsafe { context::switch(); } //TODO: Block
|
||||
}
|
||||
}
|
||||
|
|
2
libstd
2
libstd
|
@ -1 +1 @@
|
|||
Subproject commit 9f7687ec7cf09945b8d832e4777b6c521f408754
|
||||
Subproject commit 29a6cab11d6e84c422917724ef0eca2bcc2c6589
|
|
@ -27,8 +27,8 @@ pub fn main() {
|
|||
}
|
||||
|
||||
command.env("HOME", "initfs:");
|
||||
command.env("PWD", "initfs:bin/");
|
||||
command.env("PATH", "initfs:bin/");
|
||||
command.env("PWD", "initfs:bin");
|
||||
command.env("PATH", "initfs:bin");
|
||||
command.env("COLUMNS", "80");
|
||||
command.env("LINES", "30");
|
||||
command.env("TTY", &tty);
|
||||
|
|
|
@ -25,6 +25,27 @@ impl Scheme for ExampleScheme {
|
|||
}
|
||||
|
||||
fn main(){
|
||||
{
|
||||
let events = syscall::open("event:", 0).unwrap();
|
||||
|
||||
let a = syscall::open("display:", 0).unwrap();
|
||||
syscall::fevent(a, syscall::EVENT_READ).unwrap();
|
||||
let b = syscall::open("debug:", 0).unwrap();
|
||||
syscall::fevent(b, syscall::EVENT_READ).unwrap();
|
||||
|
||||
loop {
|
||||
let mut event = syscall::Event::default();
|
||||
syscall::read(events, &mut event).unwrap();
|
||||
println!("{:?}", event);
|
||||
|
||||
let mut buf = vec![0; event.data];
|
||||
syscall::read(event.id, &mut buf).unwrap();
|
||||
println!("{}", unsafe { ::std::str::from_utf8_unchecked(&buf) });
|
||||
}
|
||||
|
||||
let _ = syscall::close(events);
|
||||
}
|
||||
|
||||
thread::spawn(move || {
|
||||
let mut socket = File::create(":example").expect("example: failed to create example scheme");
|
||||
let scheme = ExampleScheme;
|
||||
|
|
|
@ -1,6 +1,30 @@
|
|||
use core::ops::{Deref, DerefMut};
|
||||
use core::{mem, slice};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Event {
|
||||
pub id: usize,
|
||||
pub flags: usize,
|
||||
pub data: usize
|
||||
}
|
||||
|
||||
impl Deref for Event {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts(self as *const Event as *const u8, mem::size_of::<Event>()) as &[u8]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Event {
|
||||
fn deref_mut(&mut self) -> &mut [u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(self as *mut Event as *mut u8, mem::size_of::<Event>()) as &mut [u8]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
pub struct Packet {
|
||||
|
|
|
@ -14,6 +14,10 @@ pub const CLONE_SUPERVISE: usize = 0x400000;
|
|||
pub const CLOCK_REALTIME: usize = 1;
|
||||
pub const CLOCK_MONOTONIC: usize = 4;
|
||||
|
||||
pub const EVENT_NONE: usize = 0;
|
||||
pub const EVENT_READ: usize = 1;
|
||||
pub const EVENT_WRITE: usize = 2;
|
||||
|
||||
pub const FUTEX_WAIT: usize = 0;
|
||||
pub const FUTEX_WAKE: usize = 1;
|
||||
pub const FUTEX_REQUEUE: usize = 2;
|
||||
|
|
|
@ -58,6 +58,10 @@ pub fn exit(status: usize) -> Result<usize> {
|
|||
unsafe { syscall1(SYS_EXIT, status) }
|
||||
}
|
||||
|
||||
pub fn fevent(fd: usize, flags: usize) -> Result<usize> {
|
||||
unsafe { syscall2(SYS_FEVENT, fd, flags) }
|
||||
}
|
||||
|
||||
pub fn fpath(fd: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
unsafe { syscall3(SYS_FPATH, fd, buf.as_mut_ptr() as usize, buf.len()) }
|
||||
}
|
||||
|
@ -110,6 +114,14 @@ pub fn open(path: &str, flags: usize) -> Result<usize> {
|
|||
unsafe { syscall3(SYS_OPEN, path.as_ptr() as usize, path.len(), flags) }
|
||||
}
|
||||
|
||||
pub unsafe fn physalloc(size: usize) -> Result<usize> {
|
||||
syscall1(SYS_PHYSALLOC, size)
|
||||
}
|
||||
|
||||
pub unsafe fn physfree(physical_address: usize, size: usize) -> Result<usize> {
|
||||
syscall2(SYS_PHYSFREE, physical_address, size)
|
||||
}
|
||||
|
||||
pub unsafe fn physmap(physical_address: usize, size: usize, flags: usize) -> Result<usize> {
|
||||
syscall3(SYS_PHYSMAP, physical_address, size, flags)
|
||||
}
|
||||
|
@ -134,6 +146,10 @@ pub fn unlink(path: &str) -> Result<usize> {
|
|||
unsafe { syscall2(SYS_UNLINK, path.as_ptr() as usize, path.len()) }
|
||||
}
|
||||
|
||||
pub unsafe fn virttophys(virtual_address: usize) -> Result<usize> {
|
||||
syscall1(SYS_VIRTTOPHYS, virtual_address)
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: usize, status: &mut usize, options: usize) -> Result<usize> {
|
||||
unsafe { syscall3(SYS_WAITPID, pid, status as *mut usize as usize, options) }
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ pub const SYS_CLOCK_GETTIME: usize = 265;
|
|||
pub const SYS_DUP: usize = 41;
|
||||
pub const SYS_EXECVE: usize = 11;
|
||||
pub const SYS_EXIT: usize = 1;
|
||||
pub const SYS_FEVENT: usize = 927;
|
||||
pub const SYS_FPATH: usize = 928;
|
||||
pub const SYS_FSTAT: usize = 28;
|
||||
pub const SYS_FSYNC: usize = 118;
|
||||
|
@ -19,8 +20,11 @@ pub const SYS_LSEEK: usize = 19;
|
|||
pub const SYS_MKDIR: usize = 39;
|
||||
pub const SYS_NANOSLEEP: usize = 162;
|
||||
pub const SYS_OPEN: usize = 5;
|
||||
pub const SYS_PHYSMAP: usize = 945;
|
||||
pub const SYS_PHYSUNMAP: usize = 946;
|
||||
pub const SYS_PHYSALLOC: usize = 945;
|
||||
pub const SYS_PHYSFREE: usize = 946;
|
||||
pub const SYS_PHYSMAP: usize = 947;
|
||||
pub const SYS_PHYSUNMAP: usize = 948;
|
||||
pub const SYS_VIRTTOPHYS: usize = 949;
|
||||
pub const SYS_PIPE2: usize = 331;
|
||||
pub const SYS_READ: usize = 3;
|
||||
pub const SYS_RMDIR: usize = 84;
|
||||
|
|
|
@ -14,6 +14,7 @@ pub trait Scheme {
|
|||
SYS_READ => self.read(packet.b, unsafe { slice::from_raw_parts_mut(packet.c as *mut u8, packet.d) }),
|
||||
SYS_WRITE => self.write(packet.b, unsafe { slice::from_raw_parts(packet.c as *const u8, packet.d) }),
|
||||
SYS_LSEEK => self.seek(packet.b, packet.c, packet.d),
|
||||
SYS_FEVENT => self.fevent(packet.b, packet.c),
|
||||
SYS_FPATH => self.fpath(packet.b, unsafe { slice::from_raw_parts_mut(packet.c as *mut u8, packet.d) }),
|
||||
SYS_FSTAT => self.fstat(packet.b, unsafe { &mut *(packet.c as *mut Stat) }),
|
||||
SYS_FSYNC => self.fsync(packet.b),
|
||||
|
@ -67,6 +68,11 @@ pub trait Scheme {
|
|||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn fevent(&self, id: usize, flags: usize) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
|
|
Loading…
Reference in a new issue