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    fn read_sectors(&self, sector: u64, count: u16, buf: &mut [u8]) -> Result<(), SyscallError> {
55        match self {
56            VolumeDeviceRef::Virtio(dev) => {
57                BlockDevice::read_sectors(*dev, sector, count, buf).map_err(SyscallError::from)
58            }
59            VolumeDeviceRef::Ahci(dev) => {
60                BlockDevice::read_sectors(*dev, sector, count, buf).map_err(SyscallError::from)
61            }
62        }
63    }
64
65    fn write_sectors(&self, sector: u64, count: u16, buf: &[u8]) -> Result<(), SyscallError> {
66        match self {
67            VolumeDeviceRef::Virtio(dev) => {
68                BlockDevice::write_sectors(*dev, sector, count, buf).map_err(SyscallError::from)
69            }
70            VolumeDeviceRef::Ahci(dev) => {
71                BlockDevice::write_sectors(*dev, sector, count, buf).map_err(SyscallError::from)
72            }
73        }
74    }
75}
76
77fn resolve_volume_device(
78    handle: u64,
79    required: CapPermissions,
80) -> Result<VolumeDeviceRef, SyscallError> {
81    crate::silo::enforce_cap_for_current_task(handle)?;
82    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
83    let caps = unsafe { &*task.process.capabilities.get() };
84    let cap = caps
85        .get_with_permissions(CapId::from_raw(handle), required)
86        .ok_or(SyscallError::PermissionDenied)?;
87    if cap.resource_type != ResourceType::Volume {
88        return Err(SyscallError::BadHandle);
89    }
90
91    let ptr = cap.resource as *const ();
92    // Try Virtio first, then AHCI : compare stored pointer to the global device pointer.
93    if let Some(dev) = virtio_block::get_device() {
94        if dev as *const _ as *const () == ptr {
95            return Ok(VolumeDeviceRef::Virtio(dev));
96        }
97    }
98    if let Some(dev) = ahci::get_device() {
99        if dev as *const _ as *const () == ptr {
100            return Ok(VolumeDeviceRef::Ahci(dev));
101        }
102    }
103    Err(SyscallError::BadHandle)
104}
105
106/// SYS_VOLUME_READ: read sectors from a block device.
107pub fn sys_volume_read(
108    handle: u64,
109    sector: u64,
110    buf_ptr: u64,
111    sector_count: u64,
112) -> Result<u64, SyscallError> {
113    if sector_count == 0 || sector_count > MAX_SECTORS_PER_CALL {
114        return Err(SyscallError::InvalidArgument);
115    }
116
117    let required = CapPermissions {
118        read: true,
119        write: false,
120        execute: false,
121        grant: false,
122        revoke: false,
123    };
124    let device = resolve_volume_device(handle, required)?;
125    let total_sectors = device.sector_count();
126    if sector >= total_sectors || sector.saturating_add(sector_count) > total_sectors {
127        return Err(SyscallError::InvalidArgument);
128    }
129
130    let count = sector_count as u16;
131    let nbytes = (count as usize) * SECTOR_SIZE;
132
133    // Allocate a single kernel buffer for the full transfer, then copy to user
134    // in one shot. This eliminates the per-sector alloc/free overhead inside
135    // the driver (see virtio_block.rs do_request / ahci.rs submit_cmd).
136    let mut kbuf = alloc::vec![0u8; nbytes];
137    let buf_slice = &mut kbuf[..nbytes];
138    device.read_sectors(sector, count, buf_slice)?;
139
140    let user = UserSliceWrite::new(buf_ptr, nbytes)?;
141    user.copy_from(buf_slice);
142
143    if sector == 0 {
144        crate::serial_println!(
145            "[volume-read] bulk handle={} sector={} count={} ptr={:#x}",
146            handle,
147            sector,
148            sector_count,
149            buf_ptr
150        );
151    }
152
153    Ok(sector_count)
154}
155
156/// SYS_VOLUME_WRITE: write sectors to a block device.
157pub fn sys_volume_write(
158    handle: u64,
159    sector: u64,
160    buf_ptr: u64,
161    sector_count: u64,
162) -> Result<u64, SyscallError> {
163    if sector_count == 0 {
164        return Ok(0);
165    }
166    if sector_count > MAX_SECTORS_PER_CALL {
167        return Err(SyscallError::InvalidArgument);
168    }
169
170    let required = CapPermissions {
171        read: false,
172        write: true,
173        execute: false,
174        grant: false,
175        revoke: false,
176    };
177    let device = resolve_volume_device(handle, required)?;
178    let total_sectors = device.sector_count();
179    if sector >= total_sectors || sector.saturating_add(sector_count) > total_sectors {
180        return Err(SyscallError::InvalidArgument);
181    }
182
183    let count = sector_count as u16;
184    let nbytes = (count as usize) * SECTOR_SIZE;
185
186    // Read the full user buffer into a single kernel buffer, then issue
187    // a single multi-sector write. This eliminates the per-sector
188    // UserSliceRead + driver alloc/free overhead.
189    let user = UserSliceRead::new(buf_ptr, nbytes)?;
190    let kbuf = user.read_to_vec();
191    if kbuf.len() != nbytes {
192        return Err(SyscallError::InvalidArgument);
193    }
194    device.write_sectors(sector, count, &kbuf)?;
195
196    Ok(sector_count)
197}
198
199/// SYS_VOLUME_INFO: query total sector count of a volume device.
200pub fn sys_volume_info(handle: u64) -> Result<u64, SyscallError> {
201    let required = CapPermissions {
202        read: true,
203        write: false,
204        execute: false,
205        grant: false,
206        revoke: false,
207    };
208    let device = resolve_volume_device(handle, required)?;
209    Ok(device.sector_count())
210}