Skip to content

Commit

Permalink
Merge pull request sunriseos#206 from Orycterope/ahci
Browse files Browse the repository at this point in the history
AHCI driver
  • Loading branch information
Orycterope authored Mar 13, 2019
2 parents 1fd45da + 113833b commit 4ce9daf
Show file tree
Hide file tree
Showing 13 changed files with 2,051 additions and 22 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ target
Cargo.lock
isofiles/boot/kfs-*
os.iso
DISK.img
.idea
.gdbinit
.gdb_history
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 32 additions & 4 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -166,19 +166,47 @@ mkisofs-rs external/grub/isofiles isofiles -o os.iso -b boot/grub/i386-pc/eltori
'''
]

[tasks.disk]
workspace = false
description = "Creates an empty disk image."
command = "dd"
args = [ "if=/dev/zero", "of=DISK.img", "count=8M", "iflag=count_bytes" ]

# because we can't have a dependency on a file ರ_ರ
[tasks.create-disk-if-not-exist]
workspace = false
description = "Invokes the task `disk` if DISK.img does not already exist."
condition_script = [ "[ ! -e DISK.img ]" ]
run_task = "disk"

[tasks.qemu]
workspace = false
description = "Runs the bootable ISO in qemu."
dependencies = ["iso-release"]
dependencies = ["iso-release", "create-disk-if-not-exist"]
command = "qemu-system-i386"
args = ["-cdrom", "os.iso", "-serial", "stdio", "-vnc", "${VNC_PORT}", "-no-reboot", "-enable-kvm"]
args = [
"-cdrom", "os.iso",
"-serial", "stdio",
"-vnc", "${VNC_PORT}",
"-no-reboot",
"-enable-kvm",
"-drive", "id=diskA,file=DISK.img,format=raw,if=none", "-device", "ahci,id=ahci", "-device", "ide-drive,drive=diskA,bus=ahci.0",
]

[tasks.qemu-debug]
workspace = false
description = "Runs the bootable ISO in qemu with gdb support"
dependencies = ["iso"]
dependencies = ["iso", "create-disk-if-not-exist"]
command = "qemu-system-i386"
args = ["-cdrom", "os.iso", "-serial", "stdio", "-vnc", "${VNC_PORT}", "-no-reboot", "-gdb", "tcp::${GDB_PORT}", "-S", "-d", "cpu_reset"]
args = [
"-cdrom", "os.iso",
"-serial", "stdio",
"-vnc", "${VNC_PORT}",
"-no-reboot",
"-gdb", "tcp::${GDB_PORT}", "-S",
"-d", "cpu_reset",
"-drive", "id=diskA,file=DISK.img,format=raw,if=none", "-device", "ahci,id=ahci", "-device", "ide-drive,drive=diskA,bus=ahci.0",
]

[tasks.doc]
workspace = false
Expand Down
2 changes: 2 additions & 0 deletions ahci/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ kfs-libuser = { path = "../libuser" }
kfs-libutils = { path = "../libutils" }
spin = "0.4"
log = "0.4.6"
bitfield = "0.13.1"
static_assertions = "0.3.1"

[dependencies.lazy_static]
features = ["spin_no_std"]
Expand Down
225 changes: 225 additions & 0 deletions ahci/src/disk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
//! AHCI Disk

use alloc::sync::Arc;
use core::fmt::{self, Debug, Formatter};

use spin::Mutex;

use kfs_libuser::error::{Error, AhciError};
use kfs_libuser::types::SharedMemory;
use kfs_libuser::syscalls::MemoryPermissions;
use kfs_libuser::types::Handle;
use kfs_libuser::zero_box::ZeroBox;

use crate::hba::*;

/// An AHCI Disk
///
/// Manages an AHCI port, and provides functions to read and write sectors.
///
/// # Memory
///
/// A disk is responsible for all allocated memory in use by the port. When dropped, the port
/// is put to a stop, pointers to these regions are cleared from the hardware, and the regions
/// are eventually de-allocated.
///
/// # Lifetime
///
/// A Disk holds a reference to its `Port Control Registers`,
/// which is located in the root `HBA Memory Registers` mapping (the one found at `BAR5`).
///
/// As this root mapping will never be unmapped, the lifetime of this reference is `'static`.
pub struct Disk {

// memory zones

/// Pointer back to the corresponding Port Control Registers, found at `BAR5[100h]`-`BAR5[10FFh]`.
pub(super) px: &'static mut Px,
/// The allocated Received FIS memory zone that the port uses.
pub(super) rfis: ZeroBox<ReceivedFis>,
/// The allocated Command List memory zone that the port uses.
pub(super) cmd_list: ZeroBox<CmdHeaderArray>,
/// An allocated Command Table for each implemented Command List slot.
pub(super) cmd_tables: [Option<ZeroBox<CmdTable>>; 32],

// info obtained by the IDENTIFY command

/// Number of addressable sectors of this disk. Each sector is 512 octets.
pub(super) sectors: u64,
/// Indicates if the device supports 48 bit addresses.
pub(super) supports_48_bit: bool,
}

impl Disk {
/// Returns the number of addressable 512-octet sectors for this disk.
#[inline(never)]
fn sector_count(&self, ) -> Result<u64, Error> {
Ok(self.sectors)
}

/// Reads sectors from disk.
///
/// Reads `sector_count` sectors starting from `lba`.
#[inline(never)]
fn read_dma(&mut self, buffer: *mut u8, buffer_len: usize, lba: u64, sector_count: u64) -> Result<(), Error> {
if (buffer_len as u64) < sector_count * 512 {
return Err(AhciError::InvalidArg.into());
}
if lba.checked_add(sector_count).filter(|sum| *sum <= self.sectors).is_none() {
return Err(AhciError::InvalidArg.into());
}
// todo: AHCI: Read CI and figure out which slot to use
// body: For now AHCI driver is single-threaded and blocking,
// body: which means that the first slot is always available for use.
// body:
// body: If we want to make a multi-threaded implementation,
// body: we will have to implement some logic to choose the slot.
let command_slot_index = 0;
unsafe {
// safe: - we just mapped buffer, so it is valid memory,
// and buffer_len is its length
// otherwise mapping it would have failed.
// - buffer[0..buffer_len] falls in a single mapping,
// we just mapped it.
// - command_slot_index is 0, which is always implemented (spec),
// and we give the cmd_header and cmd_table of this index.
// - px is initialised.
Px::read_dma(
buffer,
buffer_len,
lba,
sector_count,
self.px,
&mut self.cmd_list.slots[command_slot_index],
self.cmd_tables[command_slot_index].as_mut().unwrap(),
command_slot_index,
self.supports_48_bit
)?
}
Ok(())
}

/// Writes sectors to disk.
///
/// Writes `sector_count` sectors starting from `lba`.
#[inline(never)]
fn write_dma(&mut self, buffer: *mut u8, buffer_len: usize, lba: u64, sector_count: u64) -> Result<(), Error> {
if (buffer_len as u64) < sector_count * 512 {
return Err(AhciError::InvalidArg.into());
}
if lba.checked_add(sector_count).filter(|sum| *sum <= self.sectors).is_none() {
return Err(AhciError::InvalidArg.into());
}
let command_slot_index = 0;
unsafe {
// safe: - we just mapped buffer, so it is valid memory,
// and buffer_len is its length
// otherwise mapping it would have failed.
// - buffer[0..buffer_len] falls in a single mapping,
// we just mapped it.
// - command_slot_index is 0, which is always implemented (spec),
// and we give the cmd_header and cmd_table of this index.
// - px is initialised.
Px::write_dma(
buffer,
buffer_len,
lba,
sector_count,
self.px,
&mut self.cmd_list.slots[command_slot_index],
self.cmd_tables[command_slot_index].as_mut().unwrap(),
command_slot_index,
self.supports_48_bit
)?
}
Ok(())
}
}

impl Drop for Disk {
/// Dropping a disk brings the port to a stop, and clears the pointers from the hardware.
fn drop(&mut self) {
self.px.stop();
self.px.clear_addresses();
}
}

impl Debug for Disk {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("Disk")
.field("sectors", &self.sectors)
.field("px", &self.px)
.finish()
}
}

/// Interface to a disk.
#[derive(Debug, Clone)]
pub struct IDisk(Arc<Mutex<Disk>>);

impl IDisk {
/// Creates an IDisk from the wrapped [Disk].
pub fn new(value: Arc<Mutex<Disk>>) -> Self {
Self(value)
}
}

object! {
impl IDisk {
/// Returns the number of addressable 512-octet sectors for this disk.
#[cmdid(0)]
fn sector_count(&mut self,) -> Result<(u64,), Error> {
Ok((self.0.lock().sectors,))
}

/// Reads sectors from disk.
///
/// Reads `sector_count` sectors starting from `lba`.
///
/// # Error
///
/// - InvalidArg:
/// - `mapping_size` does not reflect the passed handle's size, or mapping it failed,
/// - `lba`, `sector_count`, or `lba + sector_count` is higher than the number of
/// addressable sectors on this disk,
/// - `sector_count` == 0.
/// - BufferTooScattered:
/// - The passed handle points to memory that is so physically scattered it overflows
/// the PRDT. This can only happen for read/writes of 1985 sectors or more.
/// You should consider retrying with a smaller `sector_count`.
#[cmdid(1)]
fn read_dma(&mut self, handle: Handle<copy>, mapping_size: u64, lba: u64, sector_count: u64,) -> Result<(), Error> {
let sharedmem = SharedMemory(handle);
let addr = kfs_libuser::mem::find_free_address(mapping_size as _, 0x1000)?;
let mapped = sharedmem.map(addr, mapping_size as _, MemoryPermissions::empty())
// no need for permission, only the disk will dma to it.
.map_err(|_| AhciError::InvalidArg)?;
self.0.lock().read_dma(mapped.as_mut_ptr(), mapped.len(), lba, sector_count)
}

/// Writes sectors to disk.
///
/// Writes `sector_count` sectors starting from `lba`.
///
/// # Error
///
/// - InvalidArg:
/// - `mapping_size` does not reflect the passed handle's size, or mapping it failed,
/// - `lba`, `sector_count`, or `lba + sector_count` is higher than the number of
/// addressable sectors on this disk,
/// - `sector_count` == 0.
/// - BufferTooScattered:
/// - The passed handle points to memory that is so physically scattered it overflows
/// the PRDT. This can only happen for read/writes of 1985 sectors or more.
/// You should consider retrying with a smaller `sector_count`.
#[cmdid(2)]
fn write_dma(&mut self, handle: Handle<copy>, mapping_size: u64, lba: u64, sector_count: u64,) -> Result<(), Error> {
let sharedmem = SharedMemory(handle);
let addr = kfs_libuser::mem::find_free_address(mapping_size as _, 0x1000)?;
let mapped = sharedmem.map(addr, mapping_size as _, MemoryPermissions::empty())
// no need for permission, only the disk will dma to it.
.map_err(|_| AhciError::InvalidArg)?;
self.0.lock().write_dma(mapped.as_mut_ptr(), mapped.len(), lba, sector_count)
}
}
}
Loading

0 comments on commit 4ce9daf

Please sign in to comment.