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 syscall::error::SyscallError,
31 vfs::scheme::{
32 finalize_pseudo_stat, DirEntry, FileFlags, FileStat, OpenFlags, OpenResult, Scheme,
33 DEV_DEVFS, DT_BLK, DT_CHR,
34 },
35};
36
37const FID_ROOT: u64 = 0;
40const FID_SDA: u64 = 1;
41const FID_VDA: u64 = 2;
42const FID_NULL: u64 = 3;
43const FID_ZERO: u64 = 4;
44const FID_RANDOM: u64 = 5;
45const FID_URANDOM: u64 = 6;
46
47const RDEV_NULL: u64 = (1u64 << 8) | 3;
48const RDEV_ZERO: u64 = (1u64 << 8) | 5;
49const RDEV_RANDOM: u64 = (1u64 << 8) | 8;
50const RDEV_URANDOM: u64 = (1u64 << 8) | 9;
51const RDEV_SDA: u64 = (8u64 << 8) | 0;
52const RDEV_VDA: u64 = (254u64 << 8) | 0;
53
54static PRNG_STATE: AtomicU64 = AtomicU64::new(0xdeadbeef_cafebabe);
56
57fn prng_fill(buf: &mut [u8]) {
59 if crate::hardware::virtio::rng::is_available() {
60 let _ = crate::hardware::virtio::rng::read_entropy(buf);
61 return;
62 }
63 let ticks = crate::process::scheduler::ticks();
64 let mut state = PRNG_STATE.load(Ordering::Relaxed) ^ ticks;
65 for chunk in buf.chunks_mut(8) {
66 state ^= state << 13;
67 state ^= state >> 7;
68 state ^= state << 17;
69 let bytes = state.to_le_bytes();
70 let n = chunk.len();
71 chunk.copy_from_slice(&bytes[..n]);
72 }
73 PRNG_STATE.store(state, Ordering::Relaxed);
74}
75
76pub struct BlkDevScheme;
80
81impl BlkDevScheme {
82 pub fn new() -> Self {
84 BlkDevScheme
85 }
86}
87
88impl Scheme for BlkDevScheme {
89 fn open(&self, path: &str, _flags: OpenFlags) -> Result<OpenResult, SyscallError> {
93 match path.trim_start_matches('/') {
94 "" => {
95 Ok(OpenResult {
97 file_id: FID_ROOT,
98 size: None,
99 flags: FileFlags::DIRECTORY,
100 })
101 }
102 "sda" => {
103 let dev = ahci::get_device().ok_or(SyscallError::NotFound)?;
104 Ok(OpenResult {
105 file_id: FID_SDA,
106 size: Some(dev.sector_count() * SECTOR_SIZE as u64),
107 flags: FileFlags::DEVICE,
108 })
109 }
110 "vda" => {
111 let dev = crate::hardware::storage::virtio_block::get_device()
112 .ok_or(SyscallError::NotFound)?;
113 Ok(OpenResult {
114 file_id: FID_VDA,
115 size: Some(dev.sector_count() * SECTOR_SIZE as u64),
116 flags: FileFlags::DEVICE,
117 })
118 }
119 "null" => Ok(OpenResult {
120 file_id: FID_NULL,
121 size: Some(0),
122 flags: FileFlags::DEVICE,
123 }),
124 "zero" => Ok(OpenResult {
125 file_id: FID_ZERO,
126 size: None,
127 flags: FileFlags::DEVICE,
128 }),
129 "random" => Ok(OpenResult {
130 file_id: FID_RANDOM,
131 size: None,
132 flags: FileFlags::DEVICE,
133 }),
134 "urandom" => Ok(OpenResult {
135 file_id: FID_URANDOM,
136 size: None,
137 flags: FileFlags::DEVICE,
138 }),
139 _ => Err(SyscallError::NotFound),
140 }
141 }
142
143 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
147 match file_id {
148 FID_ROOT => {
149 let mut listing = String::new();
150 listing.push_str("null\nurandom\nzero\nrandom\n");
151 if ahci::get_device().is_some() {
152 listing.push_str("sda\n");
153 }
154 if crate::hardware::storage::virtio_block::get_device().is_some() {
155 listing.push_str("vda\n");
156 }
157 let bytes = listing.as_bytes();
158 let start = offset as usize;
159 if start >= bytes.len() {
160 return Ok(0);
161 }
162 let n = (bytes.len() - start).min(buf.len());
163 buf[..n].copy_from_slice(&bytes[start..start + n]);
164 Ok(n)
165 }
166 FID_NULL => Ok(0),
167 FID_ZERO => {
168 buf.fill(0);
169 Ok(buf.len())
170 }
171 FID_RANDOM | FID_URANDOM => {
172 prng_fill(buf);
173 Ok(buf.len())
174 }
175 FID_SDA => {
176 let dev = ahci::get_device().ok_or(SyscallError::BadHandle)?;
177 sector_read(dev, offset, buf).map_err(|_| SyscallError::IoError)
178 }
179 FID_VDA => {
180 let dev = crate::hardware::storage::virtio_block::get_device()
181 .ok_or(SyscallError::BadHandle)?;
182 sector_read(dev, offset, buf).map_err(|_| SyscallError::IoError)
183 }
184 _ => Err(SyscallError::BadHandle),
185 }
186 }
187
188 fn write(&self, file_id: u64, offset: u64, buf: &[u8]) -> Result<usize, SyscallError> {
192 match file_id {
193 FID_NULL => Ok(buf.len()),
194 FID_ZERO | FID_RANDOM | FID_URANDOM => Err(SyscallError::PermissionDenied),
195 FID_SDA => {
196 let dev = ahci::get_device().ok_or(SyscallError::BadHandle)?;
197 sector_write(dev, offset, buf).map_err(|_| SyscallError::IoError)
198 }
199 FID_VDA => {
200 let dev = crate::hardware::storage::virtio_block::get_device()
201 .ok_or(SyscallError::BadHandle)?;
202 sector_write(dev, offset, buf).map_err(|_| SyscallError::IoError)
203 }
204 _ => Err(SyscallError::PermissionDenied),
205 }
206 }
207
208 fn close(&self, _file_id: u64) -> Result<(), SyscallError> {
212 Ok(()) }
214
215 fn size(&self, file_id: u64) -> Result<u64, SyscallError> {
219 if file_id == FID_SDA {
220 let dev = ahci::get_device().ok_or(SyscallError::BadHandle)?;
221 return Ok(dev.sector_count() * SECTOR_SIZE as u64);
222 }
223 if file_id == FID_VDA {
224 let dev = crate::hardware::storage::virtio_block::get_device()
225 .ok_or(SyscallError::BadHandle)?;
226 return Ok(dev.sector_count() * SECTOR_SIZE as u64);
227 }
228 Err(SyscallError::BadHandle)
229 }
230
231 fn stat(&self, file_id: u64) -> Result<FileStat, SyscallError> {
235 match file_id {
236 FID_ROOT => Ok(finalize_pseudo_stat(
237 FileStat {
238 st_ino: FID_ROOT,
239 st_mode: 0o040_755,
240 st_nlink: 2,
241 st_size: 0,
242 st_blksize: SECTOR_SIZE as u64,
243 st_blocks: 0,
244 ..FileStat::zeroed()
245 },
246 DEV_DEVFS,
247 0,
248 )),
249 FID_NULL => Ok(finalize_pseudo_stat(
250 FileStat {
251 st_ino: FID_NULL,
252 st_mode: 0o020_666,
253 st_nlink: 1,
254 st_size: 0,
255 st_blksize: 1,
256 st_blocks: 0,
257 ..FileStat::zeroed()
258 },
259 DEV_DEVFS,
260 RDEV_NULL,
261 )),
262 FID_ZERO => Ok(finalize_pseudo_stat(
263 FileStat {
264 st_ino: FID_ZERO,
265 st_mode: 0o020_444,
266 st_nlink: 1,
267 st_size: 0,
268 st_blksize: 1,
269 st_blocks: 0,
270 ..FileStat::zeroed()
271 },
272 DEV_DEVFS,
273 RDEV_ZERO,
274 )),
275 FID_RANDOM => Ok(finalize_pseudo_stat(
276 FileStat {
277 st_ino: FID_RANDOM,
278 st_mode: 0o020_444,
279 st_nlink: 1,
280 st_size: 0,
281 st_blksize: 1,
282 st_blocks: 0,
283 ..FileStat::zeroed()
284 },
285 DEV_DEVFS,
286 RDEV_RANDOM,
287 )),
288 FID_URANDOM => Ok(finalize_pseudo_stat(
289 FileStat {
290 st_ino: FID_URANDOM,
291 st_mode: 0o020_444,
292 st_nlink: 1,
293 st_size: 0,
294 st_blksize: 1,
295 st_blocks: 0,
296 ..FileStat::zeroed()
297 },
298 DEV_DEVFS,
299 RDEV_URANDOM,
300 )),
301 FID_SDA => {
302 let dev = ahci::get_device().ok_or(SyscallError::BadHandle)?;
303 let size = dev.sector_count() * SECTOR_SIZE as u64;
304 Ok(finalize_pseudo_stat(
305 FileStat {
306 st_ino: FID_SDA,
307 st_mode: 0o060_660, st_nlink: 1,
309 st_size: size,
310 st_blksize: SECTOR_SIZE as u64,
311 st_blocks: dev.sector_count(),
312 ..FileStat::zeroed()
313 },
314 DEV_DEVFS,
315 RDEV_SDA,
316 ))
317 }
318 FID_VDA => {
319 let dev = crate::hardware::storage::virtio_block::get_device()
320 .ok_or(SyscallError::BadHandle)?;
321 let size = dev.sector_count() * SECTOR_SIZE as u64;
322 Ok(finalize_pseudo_stat(
323 FileStat {
324 st_ino: FID_VDA,
325 st_mode: 0o060_660,
326 st_nlink: 1,
327 st_size: size,
328 st_blksize: SECTOR_SIZE as u64,
329 st_blocks: dev.sector_count(),
330 ..FileStat::zeroed()
331 },
332 DEV_DEVFS,
333 RDEV_VDA,
334 ))
335 }
336 _ => Err(SyscallError::BadHandle),
337 }
338 }
339
340 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
344 if file_id != FID_ROOT {
345 return Err(SyscallError::InvalidArgument);
346 }
347 let mut entries = Vec::new();
348 entries.push(DirEntry {
349 ino: FID_NULL,
350 file_type: DT_CHR,
351 name: String::from("null"),
352 });
353 entries.push(DirEntry {
354 ino: FID_ZERO,
355 file_type: DT_CHR,
356 name: String::from("zero"),
357 });
358 entries.push(DirEntry {
359 ino: FID_RANDOM,
360 file_type: DT_CHR,
361 name: String::from("random"),
362 });
363 entries.push(DirEntry {
364 ino: FID_URANDOM,
365 file_type: DT_CHR,
366 name: String::from("urandom"),
367 });
368 if ahci::get_device().is_some() {
369 entries.push(DirEntry {
370 ino: FID_SDA,
371 file_type: DT_BLK,
372 name: String::from("sda"),
373 });
374 }
375 if crate::hardware::storage::virtio_block::get_device().is_some() {
376 entries.push(DirEntry {
377 ino: FID_VDA,
378 file_type: DT_BLK,
379 name: String::from("vda"),
380 });
381 }
382 Ok(entries)
383 }
384}
385
386fn sector_read<D: BlockDevice>(dev: &D, offset: u64, buf: &mut [u8]) -> Result<usize, BlockError> {
393 let total = buf.len();
394 if total == 0 {
395 return Ok(0);
396 }
397
398 let disk_size = BlockDevice::sector_count(dev) * SECTOR_SIZE as u64;
399 if offset >= disk_size {
400 return Ok(0); }
402
403 let mut buf_pos: usize = 0;
404 let mut byte_off: u64 = offset;
405 let end = (offset + total as u64).min(disk_size);
407
408 while byte_off < end {
409 let sector = byte_off / SECTOR_SIZE as u64;
410 let sector_off = (byte_off % SECTOR_SIZE as u64) as usize;
411 let available = (SECTOR_SIZE - sector_off).min((end - byte_off) as usize);
412
413 let mut tmp = [0u8; SECTOR_SIZE];
414 dev.read_sector(sector, &mut tmp)?;
415
416 buf[buf_pos..buf_pos + available].copy_from_slice(&tmp[sector_off..sector_off + available]);
417
418 buf_pos += available;
419 byte_off += available as u64;
420 }
421
422 Ok(buf_pos)
423}
424
425fn sector_write<D: BlockDevice>(dev: &D, offset: u64, data: &[u8]) -> Result<usize, BlockError> {
430 let total = data.len();
431 if total == 0 {
432 return Ok(0);
433 }
434
435 let disk_size = BlockDevice::sector_count(dev) * SECTOR_SIZE as u64;
436 if offset >= disk_size {
437 return Err(BlockError::InvalidSector);
438 }
439 let end = (offset + total as u64).min(disk_size);
440
441 let mut data_pos: usize = 0;
442 let mut byte_off: u64 = offset;
443
444 while data_pos < total && byte_off < end {
445 let sector = byte_off / SECTOR_SIZE as u64;
446 let sector_off = (byte_off % SECTOR_SIZE as u64) as usize;
447 let remaining = (end - byte_off) as usize;
448 let to_write = (SECTOR_SIZE - sector_off).min(remaining);
449
450 let mut tmp = [0u8; SECTOR_SIZE];
452 if sector_off != 0 || to_write != SECTOR_SIZE {
453 dev.read_sector(sector, &mut tmp)?;
454 }
455
456 tmp[sector_off..sector_off + to_write]
457 .copy_from_slice(&data[data_pos..data_pos + to_write]);
458 dev.write_sector(sector, &tmp)?;
459
460 data_pos += to_write;
461 byte_off += to_write as u64;
462 }
463
464 Ok(data_pos)
465}