1use alloc::{string::String, vec::Vec};
23use core::sync::atomic::{AtomicU64, Ordering};
24
25use crate::{
26 hardware::storage::{
27 ahci,
28 virtio_block::{BlockDevice, BlockError, SECTOR_SIZE},
29 },
30 memory::{UserSliceRead, UserSliceWrite},
31 syscall::error::SyscallError,
32 vfs::scheme::{
33 finalize_pseudo_stat, AsyncSubmitResult, DirEntry, FileFlags, FileStat, OpenFlags,
34 OpenResult, Scheme, DEV_DEVFS, DT_BLK, DT_CHR,
35 },
36};
37
38const FID_ROOT: u64 = 0;
41const FID_SDA: u64 = 1;
42const FID_VDA: u64 = 2;
43const FID_NULL: u64 = 3;
44const FID_ZERO: u64 = 4;
45const FID_RANDOM: u64 = 5;
46const FID_URANDOM: u64 = 6;
47
48const RDEV_NULL: u64 = (1u64 << 8) | 3;
49const RDEV_ZERO: u64 = (1u64 << 8) | 5;
50const RDEV_RANDOM: u64 = (1u64 << 8) | 8;
51const RDEV_URANDOM: u64 = (1u64 << 8) | 9;
52const RDEV_SDA: u64 = (8u64 << 8) | 0;
53const RDEV_VDA: u64 = (254u64 << 8) | 0;
54
55static PRNG_STATE: AtomicU64 = AtomicU64::new(0xdeadbeef_cafebabe);
57
58fn prng_fill(buf: &mut [u8]) {
60 if crate::hardware::virtio::rng::is_available() {
61 let _ = crate::hardware::virtio::rng::read_entropy(buf);
62 return;
63 }
64 let ticks = crate::process::scheduler::ticks();
65 let mut state = PRNG_STATE.load(Ordering::Relaxed) ^ ticks;
66 for chunk in buf.chunks_mut(8) {
67 state ^= state << 13;
68 state ^= state >> 7;
69 state ^= state << 17;
70 let bytes = state.to_le_bytes();
71 let n = chunk.len();
72 chunk.copy_from_slice(&bytes[..n]);
73 }
74 PRNG_STATE.store(state, Ordering::Relaxed);
75}
76
77pub struct BlkDevScheme;
81
82fn try_direct_async_request(offset: u64, len: usize) -> Option<(u64, u32)> {
83 if len == 0 {
84 return Some((offset / SECTOR_SIZE as u64, 0));
85 }
86 if offset % SECTOR_SIZE as u64 != 0 || len % SECTOR_SIZE != 0 {
87 return None;
88 }
89 let lba = offset / SECTOR_SIZE as u64;
90 let byte_count = u32::try_from(len).ok()?;
91 Some((lba, byte_count))
92}
93
94impl BlkDevScheme {
95 pub fn new() -> Self {
97 BlkDevScheme
98 }
99}
100
101impl Scheme for BlkDevScheme {
102 fn open(&self, path: &str, _flags: OpenFlags) -> Result<OpenResult, SyscallError> {
106 match path.trim_start_matches('/') {
107 "" => {
108 Ok(OpenResult {
110 file_id: FID_ROOT,
111 size: None,
112 flags: FileFlags::DIRECTORY,
113 })
114 }
115 "sda" => {
116 let dev = ahci::get_device().ok_or(SyscallError::NotFound)?;
117 Ok(OpenResult {
118 file_id: FID_SDA,
119 size: Some(dev.sector_count() * SECTOR_SIZE as u64),
120 flags: FileFlags::DEVICE,
121 })
122 }
123 "vda" => {
124 let dev = crate::hardware::storage::virtio_block::get_device()
125 .ok_or(SyscallError::NotFound)?;
126 Ok(OpenResult {
127 file_id: FID_VDA,
128 size: Some(dev.sector_count() * SECTOR_SIZE as u64),
129 flags: FileFlags::DEVICE,
130 })
131 }
132 "null" => Ok(OpenResult {
133 file_id: FID_NULL,
134 size: Some(0),
135 flags: FileFlags::DEVICE,
136 }),
137 "zero" => Ok(OpenResult {
138 file_id: FID_ZERO,
139 size: None,
140 flags: FileFlags::DEVICE,
141 }),
142 "random" => Ok(OpenResult {
143 file_id: FID_RANDOM,
144 size: None,
145 flags: FileFlags::DEVICE,
146 }),
147 "urandom" => Ok(OpenResult {
148 file_id: FID_URANDOM,
149 size: None,
150 flags: FileFlags::DEVICE,
151 }),
152 _ => Err(SyscallError::NotFound),
153 }
154 }
155
156 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
160 match file_id {
161 FID_ROOT => {
162 let mut listing = String::new();
163 listing.push_str("null\nurandom\nzero\nrandom\n");
164 if ahci::get_device().is_some() {
165 listing.push_str("sda\n");
166 }
167 if crate::hardware::storage::virtio_block::get_device().is_some() {
168 listing.push_str("vda\n");
169 }
170 let bytes = listing.as_bytes();
171 let start = offset as usize;
172 if start >= bytes.len() {
173 return Ok(0);
174 }
175 let n = (bytes.len() - start).min(buf.len());
176 buf[..n].copy_from_slice(&bytes[start..start + n]);
177 Ok(n)
178 }
179 FID_NULL => Ok(0),
180 FID_ZERO => {
181 buf.fill(0);
182 Ok(buf.len())
183 }
184 FID_RANDOM | FID_URANDOM => {
185 prng_fill(buf);
186 Ok(buf.len())
187 }
188 FID_SDA => {
189 let dev = ahci::get_device().ok_or(SyscallError::BadHandle)?;
190 sector_read(dev, offset, buf).map_err(|_| SyscallError::IoError)
191 }
192 FID_VDA => {
193 let dev = crate::hardware::storage::virtio_block::get_device()
194 .ok_or(SyscallError::BadHandle)?;
195 sector_read(dev, offset, buf).map_err(|_| SyscallError::IoError)
196 }
197 _ => Err(SyscallError::BadHandle),
198 }
199 }
200
201 fn write(&self, file_id: u64, offset: u64, buf: &[u8]) -> Result<usize, SyscallError> {
205 match file_id {
206 FID_NULL => Ok(buf.len()),
207 FID_ZERO | FID_RANDOM | FID_URANDOM => Err(SyscallError::PermissionDenied),
208 FID_SDA => {
209 let dev = ahci::get_device().ok_or(SyscallError::BadHandle)?;
210 sector_write(dev, offset, buf).map_err(|_| SyscallError::IoError)
211 }
212 FID_VDA => {
213 let dev = crate::hardware::storage::virtio_block::get_device()
214 .ok_or(SyscallError::BadHandle)?;
215 sector_write(dev, offset, buf).map_err(|_| SyscallError::IoError)
216 }
217 _ => Err(SyscallError::PermissionDenied),
218 }
219 }
220
221 fn close(&self, _file_id: u64) -> Result<(), SyscallError> {
225 Ok(()) }
227
228 fn async_read(
229 &self,
230 file_id: u64,
231 offset: u64,
232 user_buf_vaddr: u64,
233 len: usize,
234 ring_id: u64,
235 user_data: u64,
236 ) -> Result<AsyncSubmitResult, SyscallError> {
237 if file_id == FID_SDA {
238 let dev = ahci::get_device().ok_or(SyscallError::BadHandle)?;
239 if let Some((lba, byte_count)) = try_direct_async_request(offset, len) {
240 let port_idx = dev.first_port_num().ok_or(SyscallError::BadHandle)?;
241 ahci::submit_async_storage_op(
242 port_idx,
243 lba,
244 byte_count,
245 user_buf_vaddr,
246 false,
247 ring_id,
248 user_data,
249 )
250 .map_err(|_| SyscallError::IoError)?;
251 return Ok(AsyncSubmitResult::InFlight);
252 }
253 }
254
255 const MAX_SYNC_FALLBACK_LEN: usize = 4 * 1024 * 1024; if len > MAX_SYNC_FALLBACK_LEN {
258 return Err(SyscallError::InvalidArgument);
259 }
260 let user_buf = UserSliceWrite::new(user_buf_vaddr, len)?;
261 let mut kernel_buf = alloc::vec![0u8; len];
262 let n = self.read(file_id, offset, &mut kernel_buf)?;
263 user_buf.copy_from(&kernel_buf[..n]);
264 Ok(AsyncSubmitResult::Completed(n as i32))
265 }
266
267 fn async_write(
268 &self,
269 file_id: u64,
270 offset: u64,
271 user_buf_vaddr: u64,
272 len: usize,
273 ring_id: u64,
274 user_data: u64,
275 ) -> Result<AsyncSubmitResult, SyscallError> {
276 if file_id == FID_SDA {
277 let dev = ahci::get_device().ok_or(SyscallError::BadHandle)?;
278 if let Some((lba, byte_count)) = try_direct_async_request(offset, len) {
279 let port_idx = dev.first_port_num().ok_or(SyscallError::BadHandle)?;
280 ahci::submit_async_storage_op(
281 port_idx,
282 lba,
283 byte_count,
284 user_buf_vaddr,
285 true,
286 ring_id,
287 user_data,
288 )
289 .map_err(|_| SyscallError::IoError)?;
290 return Ok(AsyncSubmitResult::InFlight);
291 }
292 }
293
294 const MAX_SYNC_FALLBACK_LEN: usize = 4 * 1024 * 1024; if len > MAX_SYNC_FALLBACK_LEN {
297 return Err(SyscallError::InvalidArgument);
298 }
299 let user_buf = UserSliceRead::new(user_buf_vaddr, len)?;
300 let kernel_buf = user_buf.read_to_vec();
301 let n = self.write(file_id, offset, &kernel_buf)?;
302 Ok(AsyncSubmitResult::Completed(n as i32))
303 }
304
305 fn size(&self, file_id: u64) -> Result<u64, SyscallError> {
309 if file_id == FID_SDA {
310 let dev = ahci::get_device().ok_or(SyscallError::BadHandle)?;
311 return Ok(dev.sector_count() * SECTOR_SIZE as u64);
312 }
313 if file_id == FID_VDA {
314 let dev = crate::hardware::storage::virtio_block::get_device()
315 .ok_or(SyscallError::BadHandle)?;
316 return Ok(dev.sector_count() * SECTOR_SIZE as u64);
317 }
318 Err(SyscallError::BadHandle)
319 }
320
321 fn stat(&self, file_id: u64) -> Result<FileStat, SyscallError> {
325 match file_id {
326 FID_ROOT => Ok(finalize_pseudo_stat(
327 FileStat {
328 st_ino: FID_ROOT,
329 st_mode: 0o040_755,
330 st_nlink: 2,
331 st_size: 0,
332 st_blksize: SECTOR_SIZE as u64,
333 st_blocks: 0,
334 ..FileStat::zeroed()
335 },
336 DEV_DEVFS,
337 0,
338 )),
339 FID_NULL => Ok(finalize_pseudo_stat(
340 FileStat {
341 st_ino: FID_NULL,
342 st_mode: 0o020_666,
343 st_nlink: 1,
344 st_size: 0,
345 st_blksize: 1,
346 st_blocks: 0,
347 ..FileStat::zeroed()
348 },
349 DEV_DEVFS,
350 RDEV_NULL,
351 )),
352 FID_ZERO => Ok(finalize_pseudo_stat(
353 FileStat {
354 st_ino: FID_ZERO,
355 st_mode: 0o020_444,
356 st_nlink: 1,
357 st_size: 0,
358 st_blksize: 1,
359 st_blocks: 0,
360 ..FileStat::zeroed()
361 },
362 DEV_DEVFS,
363 RDEV_ZERO,
364 )),
365 FID_RANDOM => Ok(finalize_pseudo_stat(
366 FileStat {
367 st_ino: FID_RANDOM,
368 st_mode: 0o020_444,
369 st_nlink: 1,
370 st_size: 0,
371 st_blksize: 1,
372 st_blocks: 0,
373 ..FileStat::zeroed()
374 },
375 DEV_DEVFS,
376 RDEV_RANDOM,
377 )),
378 FID_URANDOM => Ok(finalize_pseudo_stat(
379 FileStat {
380 st_ino: FID_URANDOM,
381 st_mode: 0o020_444,
382 st_nlink: 1,
383 st_size: 0,
384 st_blksize: 1,
385 st_blocks: 0,
386 ..FileStat::zeroed()
387 },
388 DEV_DEVFS,
389 RDEV_URANDOM,
390 )),
391 FID_SDA => {
392 let dev = ahci::get_device().ok_or(SyscallError::BadHandle)?;
393 let size = dev.sector_count() * SECTOR_SIZE as u64;
394 Ok(finalize_pseudo_stat(
395 FileStat {
396 st_ino: FID_SDA,
397 st_mode: 0o060_660, st_nlink: 1,
399 st_size: size,
400 st_blksize: SECTOR_SIZE as u64,
401 st_blocks: dev.sector_count(),
402 ..FileStat::zeroed()
403 },
404 DEV_DEVFS,
405 RDEV_SDA,
406 ))
407 }
408 FID_VDA => {
409 let dev = crate::hardware::storage::virtio_block::get_device()
410 .ok_or(SyscallError::BadHandle)?;
411 let size = dev.sector_count() * SECTOR_SIZE as u64;
412 Ok(finalize_pseudo_stat(
413 FileStat {
414 st_ino: FID_VDA,
415 st_mode: 0o060_660,
416 st_nlink: 1,
417 st_size: size,
418 st_blksize: SECTOR_SIZE as u64,
419 st_blocks: dev.sector_count(),
420 ..FileStat::zeroed()
421 },
422 DEV_DEVFS,
423 RDEV_VDA,
424 ))
425 }
426 _ => Err(SyscallError::BadHandle),
427 }
428 }
429
430 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
434 if file_id != FID_ROOT {
435 return Err(SyscallError::InvalidArgument);
436 }
437 let mut entries = Vec::new();
438 entries.push(DirEntry {
439 ino: FID_NULL,
440 file_type: DT_CHR,
441 name: String::from("null"),
442 });
443 entries.push(DirEntry {
444 ino: FID_ZERO,
445 file_type: DT_CHR,
446 name: String::from("zero"),
447 });
448 entries.push(DirEntry {
449 ino: FID_RANDOM,
450 file_type: DT_CHR,
451 name: String::from("random"),
452 });
453 entries.push(DirEntry {
454 ino: FID_URANDOM,
455 file_type: DT_CHR,
456 name: String::from("urandom"),
457 });
458 if ahci::get_device().is_some() {
459 entries.push(DirEntry {
460 ino: FID_SDA,
461 file_type: DT_BLK,
462 name: String::from("sda"),
463 });
464 }
465 if crate::hardware::storage::virtio_block::get_device().is_some() {
466 entries.push(DirEntry {
467 ino: FID_VDA,
468 file_type: DT_BLK,
469 name: String::from("vda"),
470 });
471 }
472 Ok(entries)
473 }
474}
475
476fn sector_read<D: BlockDevice>(dev: &D, offset: u64, buf: &mut [u8]) -> Result<usize, BlockError> {
483 let total = buf.len();
484 if total == 0 {
485 return Ok(0);
486 }
487
488 let disk_size = BlockDevice::sector_count(dev) * SECTOR_SIZE as u64;
489 if offset >= disk_size {
490 return Ok(0); }
492
493 let mut buf_pos: usize = 0;
494 let mut byte_off: u64 = offset;
495 let end = (offset + total as u64).min(disk_size);
497
498 while byte_off < end {
499 let sector = byte_off / SECTOR_SIZE as u64;
500 let sector_off = (byte_off % SECTOR_SIZE as u64) as usize;
501 let available = (SECTOR_SIZE - sector_off).min((end - byte_off) as usize);
502
503 let mut tmp = [0u8; SECTOR_SIZE];
504 dev.read_sector(sector, &mut tmp)?;
505
506 buf[buf_pos..buf_pos + available].copy_from_slice(&tmp[sector_off..sector_off + available]);
507
508 buf_pos += available;
509 byte_off += available as u64;
510 }
511
512 Ok(buf_pos)
513}
514
515fn sector_write<D: BlockDevice>(dev: &D, offset: u64, data: &[u8]) -> Result<usize, BlockError> {
520 let total = data.len();
521 if total == 0 {
522 return Ok(0);
523 }
524
525 let disk_size = BlockDevice::sector_count(dev) * SECTOR_SIZE as u64;
526 if offset >= disk_size {
527 return Err(BlockError::InvalidSector);
528 }
529 let end = (offset + total as u64).min(disk_size);
530
531 let mut data_pos: usize = 0;
532 let mut byte_off: u64 = offset;
533
534 while data_pos < total && byte_off < end {
535 let sector = byte_off / SECTOR_SIZE as u64;
536 let sector_off = (byte_off % SECTOR_SIZE as u64) as usize;
537 let remaining = (end - byte_off) as usize;
538 let to_write = (SECTOR_SIZE - sector_off).min(remaining);
539
540 let mut tmp = [0u8; SECTOR_SIZE];
542 if sector_off != 0 || to_write != SECTOR_SIZE {
543 dev.read_sector(sector, &mut tmp)?;
544 }
545
546 tmp[sector_off..sector_off + to_write]
547 .copy_from_slice(&data[data_pos..data_pos + to_write]);
548 dev.write_sector(sector, &tmp)?;
549
550 data_pos += to_write;
551 byte_off += to_write as u64;
552 }
553
554 Ok(data_pos)
555}
556
557#[cfg(test)]
558mod tests {
559 use super::try_direct_async_request;
560
561 #[test]
562 fn direct_async_request_requires_sector_alignment() {
563 assert_eq!(try_direct_async_request(0, 512), Some((0, 512)));
564 assert_eq!(try_direct_async_request(512, 1024), Some((1, 1024)));
565 assert_eq!(try_direct_async_request(1, 512), None);
566 assert_eq!(try_direct_async_request(0, 513), None);
567 }
568}