Skip to main content

strat9_kernel/syscall/
volume.rs

1//! Volume / block device syscall handlers.
2//!
3//! Implements sector-level read/write and info queries for storage volumes
4//! accessed through capability handles.
5
6use super::error::SyscallError;
7use crate::{
8    capability::{CapId, CapPermissions, ResourceType},
9    hardware::storage::{
10        ahci,
11        virtio_block::{self, BlockDevice, SECTOR_SIZE},
12    },
13    memory::{UserSliceRead, UserSliceWrite},
14    process::current_task_clone,
15};
16
17const MAX_SECTORS_PER_CALL: u64 = 256;
18
19enum VolumeDeviceRef {
20    Virtio(&'static virtio_block::VirtioBlockDevice),
21    Ahci(&'static ahci::AhciController),
22}
23
24impl VolumeDeviceRef {
25    fn sector_count(&self) -> u64 {
26        match self {
27            VolumeDeviceRef::Virtio(dev) => BlockDevice::sector_count(*dev),
28            VolumeDeviceRef::Ahci(dev) => BlockDevice::sector_count(*dev),
29        }
30    }
31
32    fn read_sector(&self, sector: u64, buf: &mut [u8]) -> Result<(), SyscallError> {
33        match self {
34            VolumeDeviceRef::Virtio(dev) => {
35                BlockDevice::read_sector(*dev, sector, buf).map_err(SyscallError::from)
36            }
37            VolumeDeviceRef::Ahci(dev) => {
38                BlockDevice::read_sector(*dev, sector, buf).map_err(SyscallError::from)
39            }
40        }
41    }
42
43    fn write_sector(&self, sector: u64, buf: &[u8]) -> Result<(), SyscallError> {
44        match self {
45            VolumeDeviceRef::Virtio(dev) => {
46                BlockDevice::write_sector(*dev, sector, buf).map_err(SyscallError::from)
47            }
48            VolumeDeviceRef::Ahci(dev) => {
49                BlockDevice::write_sector(*dev, sector, buf).map_err(SyscallError::from)
50            }
51        }
52    }
53}
54
55fn resolve_volume_device(
56    handle: u64,
57    required: CapPermissions,
58) -> Result<VolumeDeviceRef, SyscallError> {
59    crate::silo::enforce_cap_for_current_task(handle)?;
60    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
61    let caps = unsafe { &*task.process.capabilities.get() };
62    let cap = caps
63        .get_with_permissions(CapId::from_raw(handle), required)
64        .ok_or(SyscallError::PermissionDenied)?;
65    if cap.resource_type != ResourceType::Volume {
66        return Err(SyscallError::BadHandle);
67    }
68
69    let ptr = cap.resource as *const ();
70    // Try Virtio first, then AHCI : compare stored pointer to the global device pointer.
71    if let Some(dev) = virtio_block::get_device() {
72        if dev as *const _ as *const () == ptr {
73            return Ok(VolumeDeviceRef::Virtio(dev));
74        }
75    }
76    if let Some(dev) = ahci::get_device() {
77        if dev as *const _ as *const () == ptr {
78            return Ok(VolumeDeviceRef::Ahci(dev));
79        }
80    }
81    Err(SyscallError::BadHandle)
82}
83
84/// SYS_VOLUME_READ: read sectors from a block device.
85pub fn sys_volume_read(
86    handle: u64,
87    sector: u64,
88    buf_ptr: u64,
89    sector_count: u64,
90) -> Result<u64, SyscallError> {
91    if sector_count == 0 || sector_count > MAX_SECTORS_PER_CALL {
92        return Err(SyscallError::InvalidArgument);
93    }
94
95    let required = CapPermissions {
96        read: true,
97        write: false,
98        execute: false,
99        grant: false,
100        revoke: false,
101    };
102    let device = resolve_volume_device(handle, required)?;
103    let total_sectors = device.sector_count();
104    if sector >= total_sectors || sector.saturating_add(sector_count) > total_sectors {
105        return Err(SyscallError::InvalidArgument);
106    }
107
108    let mut kbuf = [0u8; SECTOR_SIZE];
109    let probe_trace = sector == 0;
110
111    for i in 0..sector_count {
112        let cur_sector = sector.checked_add(i).ok_or(SyscallError::InvalidArgument)?;
113        device.read_sector(cur_sector, &mut kbuf)?;
114        let offset = (i as usize)
115            .checked_mul(SECTOR_SIZE)
116            .ok_or(SyscallError::InvalidArgument)?;
117        let ptr = buf_ptr
118            .checked_add(offset as u64)
119            .ok_or(SyscallError::Fault)?;
120        let user = UserSliceWrite::new(ptr, SECTOR_SIZE)?;
121        user.copy_from(&kbuf);
122        if probe_trace {
123            crate::serial_println!(
124                "[volume-read] copied to user handle={} sector={} ptr={:#x}",
125                handle,
126                cur_sector,
127                ptr
128            );
129        }
130    }
131
132    Ok(sector_count)
133}
134
135/// SYS_VOLUME_WRITE: write sectors to a block device.
136pub fn sys_volume_write(
137    handle: u64,
138    sector: u64,
139    buf_ptr: u64,
140    sector_count: u64,
141) -> Result<u64, SyscallError> {
142    if sector_count == 0 {
143        return Ok(0);
144    }
145    if sector_count > MAX_SECTORS_PER_CALL {
146        return Err(SyscallError::InvalidArgument);
147    }
148
149    let required = CapPermissions {
150        read: false,
151        write: true,
152        execute: false,
153        grant: false,
154        revoke: false,
155    };
156    let device = resolve_volume_device(handle, required)?;
157    let total_sectors = device.sector_count();
158    if sector >= total_sectors || sector.saturating_add(sector_count) > total_sectors {
159        return Err(SyscallError::InvalidArgument);
160    }
161
162    let mut kbuf = [0u8; SECTOR_SIZE];
163    for i in 0..sector_count {
164        let cur_sector = sector.checked_add(i).ok_or(SyscallError::InvalidArgument)?;
165        let offset = (i as usize)
166            .checked_mul(SECTOR_SIZE)
167            .ok_or(SyscallError::InvalidArgument)?;
168        let ptr = buf_ptr
169            .checked_add(offset as u64)
170            .ok_or(SyscallError::Fault)?;
171        let user = UserSliceRead::new(ptr, SECTOR_SIZE)?;
172        let data = user.read_to_vec();
173        if data.len() != SECTOR_SIZE {
174            return Err(SyscallError::InvalidArgument);
175        }
176        kbuf.copy_from_slice(&data);
177        device.write_sector(cur_sector, &kbuf)?;
178    }
179
180    Ok(sector_count)
181}
182
183/// SYS_VOLUME_INFO: query total sector count of a volume device.
184pub fn sys_volume_info(handle: u64) -> Result<u64, SyscallError> {
185    let required = CapPermissions {
186        read: true,
187        write: false,
188        execute: false,
189        grant: false,
190        revoke: false,
191    };
192    let device = resolve_volume_device(handle, required)?;
193    Ok(device.sector_count())
194}