Quellcode durchsuchen

feat(BlockDevice): add write requests for block devices

greatbridf vor 7 Monaten
Ursprung
Commit
1e9ae1be41

+ 2 - 1
src/driver/ahci/mod.rs

@@ -13,6 +13,7 @@ use alloc::{format, sync::Arc};
 use control::AdapterControl;
 use defs::*;
 use eonix_mm::address::{AddrOps as _, PAddr};
+use eonix_runtime::task::Task;
 use eonix_sync::SpinIrq as _;
 use port::AdapterPort;
 
@@ -132,7 +133,7 @@ impl Device<'static> {
                     port,
                 )?;
 
-                port.partprobe()?;
+                Task::block_on(port.partprobe())?;
 
                 Ok(())
             })() {

+ 28 - 8
src/driver/ahci/port.rs

@@ -1,4 +1,4 @@
-use super::command::{Command, IdentifyCommand, ReadLBACommand};
+use super::command::{Command, IdentifyCommand, ReadLBACommand, WriteLBACommand};
 use super::slot::CommandSlot;
 use super::stats::AdapterPortStats;
 use super::{
@@ -275,13 +275,33 @@ impl BlockRequestQueue for AdapterPort<'_> {
     }
 
     fn submit(&self, req: BlockDeviceRequest) -> KResult<()> {
-        // TODO: check disk size limit using newtype
-        if req.count > 65535 {
-            return Err(EINVAL);
+        match req {
+            BlockDeviceRequest::Read {
+                sector,
+                count,
+                buffer,
+            } => {
+                if count > 65535 {
+                    return Err(EINVAL);
+                }
+
+                let command = ReadLBACommand::new(buffer, sector, count as u16)?;
+
+                self.send_command(&command)
+            }
+            BlockDeviceRequest::Write {
+                sector,
+                count,
+                buffer,
+            } => {
+                if count > 65535 {
+                    return Err(EINVAL);
+                }
+
+                let command = WriteLBACommand::new(buffer, sector, count as u16)?;
+
+                self.send_command(&command)
+            }
         }
-
-        let command = ReadLBACommand::new(req.buffer, req.sector, req.count as u16)?;
-
-        self.send_command(&command)
     }
 }

+ 2 - 2
src/driver/virtio.rs

@@ -15,6 +15,7 @@ use eonix_mm::{
     address::{Addr, PAddr, PhysAccess},
     paging::PFN,
 };
+use eonix_runtime::task::Task;
 use eonix_sync::Spin;
 use virtio_drivers::{
     device::blk::VirtIOBlk,
@@ -113,8 +114,7 @@ pub fn init_virtio_devices() {
                     )
                     .expect("Failed to register VirtIO Block device");
 
-                    block_device
-                        .partprobe()
+                    Task::block_on(block_device.partprobe())
                         .expect("Failed to probe partitions for VirtIO Block device");
 
                     disk_id += 1;

+ 18 - 9
src/driver/virtio/virtio_blk.rs

@@ -17,16 +17,25 @@ impl BlockRequestQueue for Spin<VirtIOBlk<HAL, MmioTransport<'_>>> {
     }
 
     fn submit(&self, req: BlockDeviceRequest) -> KResult<()> {
-        let mut dev = self.lock();
-        for ((start, len), buffer_page) in
-            Chunks::new(req.sector as usize, req.count as usize, 8).zip(req.buffer.iter())
-        {
-            let buffer = unsafe {
-                // SAFETY: Pages in `req.buffer` are guaranteed to be exclusively owned by us.
-                &mut buffer_page.as_memblk().as_bytes_mut()[..len as usize * 512]
-            };
+        match req {
+            BlockDeviceRequest::Write { .. } => todo!(),
+            BlockDeviceRequest::Read {
+                sector,
+                count,
+                buffer,
+            } => {
+                let mut dev = self.lock();
+                for ((start, len), buffer_page) in
+                    Chunks::new(sector as usize, count as usize, 8).zip(buffer.iter())
+                {
+                    let buffer = unsafe {
+                        // SAFETY: Pages in `req.buffer` are guaranteed to be exclusively owned by us.
+                        &mut buffer_page.as_memblk().as_bytes_mut()[..len as usize * 512]
+                    };
 
-            dev.read_blocks(start, buffer).map_err(|_| EIO)?;
+                    dev.read_blocks(start, buffer).map_err(|_| EIO)?;
+                }
+            }
         }
 
         Ok(())

+ 2 - 2
src/fs/fat32.rs

@@ -101,12 +101,12 @@ impl FatFs {
     fn read_cluster(&self, cluster: ClusterNo, buf: &Page) -> KResult<()> {
         let cluster = cluster - 2;
 
-        let rq = BlockDeviceRequest {
+        let rq = BlockDeviceRequest::Read {
             sector: self.data_start as u64 + cluster as u64 * self.sectors_per_cluster as u64,
             count: self.sectors_per_cluster as u64,
             buffer: core::slice::from_ref(buf),
         };
-        self.device.read_raw(rq)?;
+        self.device.commit_request(rq)?;
 
         Ok(())
     }

+ 98 - 89
src/kernel/block.rs

@@ -1,11 +1,13 @@
+mod mbr;
+
 use super::{
     constants::ENOENT,
     mem::{paging::Page, AsMemoryBlock as _},
     vfs::DevId,
 };
-use crate::kernel::constants::{EEXIST, EINVAL, EIO};
+use crate::kernel::constants::{EEXIST, EINVAL};
 use crate::{
-    io::{Buffer, FillResult, UninitBuffer},
+    io::{Buffer, FillResult},
     prelude::*,
 };
 use alloc::{
@@ -13,40 +15,44 @@ use alloc::{
     sync::Arc,
 };
 use core::cmp::Ordering;
+use mbr::MBRPartTable;
 
 pub fn make_device(major: u32, minor: u32) -> DevId {
     (major << 8) & 0xff00u32 | minor & 0xffu32
 }
 
-pub trait BlockRequestQueue: Send + Sync {
-    /// Maximum number of sectors that can be read in one request
-    ///
-    fn max_request_pages(&self) -> u64;
-
-    fn submit(&self, req: BlockDeviceRequest) -> KResult<()>;
+pub struct Partition {
+    pub lba_offset: u64,
+    pub sector_count: u64,
 }
 
-struct BlockDeviceDisk {
-    queue: Arc<dyn BlockRequestQueue>,
+pub trait PartTable {
+    fn partitions(&self) -> impl Iterator<Item = Partition> + use<'_, Self>;
 }
 
-#[allow(dead_code)]
-struct BlockDevicePartition {
-    disk_dev: DevId,
-    offset: u64,
+pub trait BlockRequestQueue: Send + Sync {
+    /// Maximum number of sectors that can be read in one request
+    fn max_request_pages(&self) -> u64;
 
-    queue: Arc<dyn BlockRequestQueue>,
+    fn submit(&self, req: BlockDeviceRequest) -> KResult<()>;
 }
 
 enum BlockDeviceType {
-    Disk(BlockDeviceDisk),
-    Partition(BlockDevicePartition),
+    Disk {
+        queue: Arc<dyn BlockRequestQueue>,
+    },
+    Partition {
+        disk_dev: DevId,
+        lba_offset: u64,
+        queue: Arc<dyn BlockRequestQueue>,
+    },
 }
 
 pub struct BlockDevice {
+    /// Unique device identifier, major and minor numbers
     devid: DevId,
-    size: u64,
-    max_pages: u64,
+    /// Total size of the device in sectors (512 bytes each)
+    sector_count: u64,
 
     dev_type: BlockDeviceType,
 }
@@ -73,37 +79,16 @@ impl Ord for BlockDevice {
 
 static BLOCK_DEVICE_LIST: Spin<BTreeMap<DevId, Arc<BlockDevice>>> = Spin::new(BTreeMap::new());
 
-#[derive(Debug, Clone, Copy)]
-#[repr(C)]
-struct MBREntry {
-    attr: u8,
-    chs_start: [u8; 3],
-    part_type: u8,
-    chs_end: [u8; 3],
-    lba_start: u32,
-    cnt: u32,
-}
-
-#[derive(Debug, Clone, Copy)]
-#[repr(C, packed)]
-struct MBR {
-    code: [u8; 446],
-    entries: [MBREntry; 4],
-    magic: [u8; 2],
-}
-
 impl BlockDevice {
     pub fn register_disk(
         devid: DevId,
         size: u64,
         queue: Arc<dyn BlockRequestQueue>,
     ) -> KResult<Arc<Self>> {
-        let max_pages = queue.max_request_pages();
         let device = Arc::new(Self {
             devid,
-            size,
-            max_pages,
-            dev_type: BlockDeviceType::Disk(BlockDeviceDisk { queue }),
+            sector_count: size,
+            dev_type: BlockDeviceType::Disk { queue },
         });
 
         match BLOCK_DEVICE_LIST.lock().entry(devid) {
@@ -122,21 +107,27 @@ impl BlockDevice {
         self.devid
     }
 
+    fn queue(&self) -> &Arc<dyn BlockRequestQueue> {
+        match &self.dev_type {
+            BlockDeviceType::Disk { queue } => queue,
+            BlockDeviceType::Partition { queue, .. } => queue,
+        }
+    }
+
     pub fn register_partition(&self, idx: u32, offset: u64, size: u64) -> KResult<Arc<Self>> {
-        let queue = match self.dev_type {
-            BlockDeviceType::Disk(ref disk) => disk.queue.clone(),
-            BlockDeviceType::Partition(_) => return Err(EINVAL),
+        let queue = match &self.dev_type {
+            BlockDeviceType::Disk { queue } => queue.clone(),
+            BlockDeviceType::Partition { .. } => return Err(EINVAL),
         };
 
         let device = Arc::new(BlockDevice {
             devid: make_device(self.devid >> 8, idx as u32),
-            size,
-            max_pages: self.max_pages,
-            dev_type: BlockDeviceType::Partition(BlockDevicePartition {
+            sector_count: size,
+            dev_type: BlockDeviceType::Partition {
                 disk_dev: self.devid,
-                offset,
+                lba_offset: offset,
                 queue,
-            }),
+            },
         });
 
         match BLOCK_DEVICE_LIST.lock().entry(device.devid()) {
@@ -145,29 +136,21 @@ impl BlockDevice {
         }
     }
 
-    pub fn partprobe(&self) -> KResult<()> {
+    pub async fn partprobe(&self) -> KResult<()> {
         match self.dev_type {
-            BlockDeviceType::Partition(_) => Err(EINVAL),
-            BlockDeviceType::Disk(_) => {
-                let mut mbr: UninitBuffer<MBR> = UninitBuffer::new();
-                self.read_some(0, &mut mbr)?.ok_or(EIO)?;
-                let mbr = mbr.assume_filled_ref()?;
-
-                if mbr.magic != [0x55, 0xaa] {
-                    return Ok(());
-                }
-
-                let entries = mbr.entries;
-
-                for (idx, entry) in entries.iter().enumerate() {
-                    if entry.part_type == 0 {
-                        continue;
-                    }
-
-                    let offset = entry.lba_start as u64;
-                    let size = entry.cnt as u64;
-
-                    self.register_partition(idx as u32 + 1, offset, size)?;
+            BlockDeviceType::Partition { .. } => Err(EINVAL),
+            BlockDeviceType::Disk { .. } => {
+                let mbr_table = MBRPartTable::from_disk(self).await?;
+
+                for (
+                    idx,
+                    Partition {
+                        lba_offset,
+                        sector_count,
+                    },
+                ) in mbr_table.partitions().enumerate()
+                {
+                    self.register_partition(idx as u32 + 1, lba_offset, sector_count)?;
                 }
 
                 Ok(())
@@ -183,19 +166,32 @@ impl BlockDevice {
     /// - `req.sector` must be within the disk size
     /// - `req.buffer` must be enough to hold the data
     ///
-    pub fn read_raw(&self, mut req: BlockDeviceRequest) -> KResult<()> {
-        // TODO: check disk size limit
-        if req.sector + req.count > self.size {
-            return Err(EINVAL);
-        }
+    pub fn commit_request(&self, mut req: BlockDeviceRequest) -> KResult<()> {
+        // Verify the request parameters.
+        match &mut req {
+            BlockDeviceRequest::Read { sector, count, .. } => {
+                if *sector + *count > self.sector_count {
+                    return Err(EINVAL);
+                }
 
-        match self.dev_type {
-            BlockDeviceType::Disk(ref disk) => disk.queue.submit(req),
-            BlockDeviceType::Partition(ref part) => {
-                req.sector += part.offset;
-                part.queue.submit(req)
+                if let BlockDeviceType::Partition { lba_offset, .. } = &self.dev_type {
+                    // Adjust the sector for partition offset.
+                    *sector += lba_offset;
+                }
+            }
+            BlockDeviceRequest::Write { sector, count, .. } => {
+                if *sector + *count > self.sector_count {
+                    return Err(EINVAL);
+                }
+
+                if let BlockDeviceType::Partition { lba_offset, .. } = &self.dev_type {
+                    // Adjust the sector for partition offset.
+                    *sector += lba_offset;
+                }
             }
         }
+
+        self.queue().submit(req)
     }
 
     /// Read some from the block device, may involve some copy and fragmentation
@@ -234,7 +230,7 @@ impl BlockDevice {
                     pages = core::slice::from_ref(page.as_ref().unwrap());
                 }
                 count => {
-                    nread = count.min(self.max_pages);
+                    nread = count.min(self.queue().max_request_pages());
 
                     let npages = (nread + 15) / 16;
                     let mut _page_vec = Vec::with_capacity(npages as usize);
@@ -246,13 +242,13 @@ impl BlockDevice {
                 }
             }
 
-            let req = BlockDeviceRequest {
+            let req = BlockDeviceRequest::Read {
                 sector: sector_start,
                 count: nread,
                 buffer: &pages,
             };
 
-            self.read_raw(req)?;
+            self.commit_request(req)?;
 
             for page in pages.iter() {
                 // SAFETY: We are the only owner of the page so no one could be mutating it.
@@ -283,8 +279,21 @@ impl BlockDevice {
     }
 }
 
-pub struct BlockDeviceRequest<'lt> {
-    pub sector: u64, // Sector to read from, in 512-byte blocks
-    pub count: u64,  // Number of sectors to read
-    pub buffer: &'lt [Page],
+pub enum BlockDeviceRequest<'lt> {
+    Read {
+        /// Sector to read from, in 512-byte blocks
+        sector: u64,
+        /// Number of sectors to read
+        count: u64,
+        /// Buffer pages to read into
+        buffer: &'lt [Page],
+    },
+    Write {
+        /// Sector to write to, in 512-byte blocks
+        sector: u64,
+        /// Number of sectors to write
+        count: u64,
+        /// Buffer pages to write from
+        buffer: &'lt [Page],
+    },
 }

+ 57 - 0
src/kernel/block/mbr.rs

@@ -0,0 +1,57 @@
+use super::{BlockDevice, PartTable, Partition};
+use crate::{
+    io::UninitBuffer,
+    kernel::constants::{EIO, ENODEV},
+    prelude::KResult,
+};
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+struct MBREntry {
+    attr: u8,
+    chs_start: [u8; 3],
+    part_type: u8,
+    chs_end: [u8; 3],
+    lba_start: u32,
+    cnt: u32,
+}
+
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct MBRData {
+    code: [u8; 446],
+    entries: [MBREntry; 4],
+    magic: [u8; 2],
+}
+
+pub struct MBRPartTable {
+    entries: [MBREntry; 4],
+}
+
+impl MBRPartTable {
+    pub async fn from_disk(disk: &BlockDevice) -> KResult<Self> {
+        let mut mbr: UninitBuffer<MBRData> = UninitBuffer::new();
+        disk.read_some(0, &mut mbr)?.ok_or(EIO)?;
+        let mbr = mbr.assume_init()?;
+
+        if mbr.magic != [0x55, 0xaa] {
+            Err(ENODEV)?;
+        }
+
+        Ok(Self {
+            entries: mbr.entries,
+        })
+    }
+}
+
+impl PartTable for MBRPartTable {
+    fn partitions(&self) -> impl Iterator<Item = Partition> + use<'_> {
+        self.entries
+            .iter()
+            .filter(|entry| entry.part_type != 0)
+            .map(|entry| Partition {
+                lba_offset: entry.lba_start as u64,
+                sector_count: entry.cnt as u64,
+            })
+    }
+}

+ 0 - 1
src/kernel/vfs/file.rs

@@ -283,7 +283,6 @@ impl InodeFile {
 
         let mut cursor = Task::block_on(self.cursor.lock());
 
-        // TODO!!!: use `UserBuffer`
         if self.append {
             let nwrote = self.dentry.write(stream, WriteOffset::End(&mut cursor))?;