1 use super::{_port, hba::HbaCmdTable, virt_2_phys}; 2 use crate::driver::base::block::block_device::{BlockDevice, BlockId}; 3 use crate::driver::base::block::disk_info::Partition; 4 use crate::driver::base::block::SeekFrom; 5 use crate::driver::base::class::Class; 6 use crate::driver::base::device::bus::Bus; 7 8 use crate::driver::base::device::driver::Driver; 9 use crate::driver::base::device::{Device, DeviceType, IdTable}; 10 use crate::driver::base::kobject::{KObjType, KObject, KObjectState}; 11 use crate::driver::base::kset::KSet; 12 use crate::driver::disk::ahci::HBA_PxIS_TFES; 13 14 use crate::filesystem::kernfs::KernFSInode; 15 use crate::filesystem::mbr::MbrDiskPartionTable; 16 17 use crate::kdebug; 18 use crate::libs::rwlock::{RwLockReadGuard, RwLockWriteGuard}; 19 use crate::libs::{spinlock::SpinLock, vec_cursor::VecCursor}; 20 use crate::mm::{phys_2_virt, verify_area, VirtAddr}; 21 use crate::{ 22 driver::disk::ahci::hba::{ 23 FisRegH2D, FisType, HbaCmdHeader, ATA_CMD_READ_DMA_EXT, ATA_CMD_WRITE_DMA_EXT, 24 ATA_DEV_BUSY, ATA_DEV_DRQ, 25 }, 26 kerror, 27 }; 28 use system_error::SystemError; 29 30 use alloc::sync::Weak; 31 use alloc::{string::String, sync::Arc, vec::Vec}; 32 33 use core::fmt::Debug; 34 use core::sync::atomic::{compiler_fence, Ordering}; 35 use core::{mem::size_of, ptr::write_bytes}; 36 37 /// @brief: 只支持MBR分区格式的磁盘结构体 38 pub struct AhciDisk { 39 pub name: String, 40 pub flags: u16, // 磁盘的状态flags 41 pub partitions: Vec<Arc<Partition>>, // 磁盘分区数组 42 // port: &'static mut HbaPort, // 控制硬盘的端口 43 pub ctrl_num: u8, 44 pub port_num: u8, 45 /// 指向LockAhciDisk的弱引用 46 self_ref: Weak<LockedAhciDisk>, 47 } 48 49 /// @brief: 带锁的AhciDisk 50 #[derive(Debug)] 51 pub struct LockedAhciDisk(pub SpinLock<AhciDisk>); 52 /// 函数实现 53 impl Debug for AhciDisk { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result54 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 55 write!( 56 f, 57 "{{ name: {}, flags: {}, part_s: {:?} }}", 58 self.name, self.flags, self.partitions 59 )?; 60 return Ok(()); 61 } 62 } 63 64 impl AhciDisk { read_at( &self, lba_id_start: BlockId, count: usize, buf: &mut [u8], ) -> Result<usize, SystemError>65 fn read_at( 66 &self, 67 lba_id_start: BlockId, // 起始lba编号 68 count: usize, // 读取lba的数量 69 buf: &mut [u8], 70 ) -> Result<usize, SystemError> { 71 assert!((buf.len() & 511) == 0); 72 compiler_fence(core::sync::atomic::Ordering::SeqCst); 73 let check_length = ((count - 1) >> 4) + 1; // prdt length 74 if count * 512 > buf.len() || check_length > 8 as usize { 75 kerror!("ahci read: e2big"); 76 // 不可能的操作 77 return Err(SystemError::E2BIG); 78 } else if count == 0 { 79 return Ok(0); 80 } 81 82 let port = _port(self.ctrl_num, self.port_num); 83 volatile_write!(port.is, u32::MAX); // Clear pending interrupt bits 84 85 let slot = port.find_cmdslot().unwrap_or(u32::MAX); 86 87 if slot == u32::MAX { 88 return Err(SystemError::EIO); 89 } 90 91 #[allow(unused_unsafe)] 92 let cmdheader: &mut HbaCmdHeader = unsafe { 93 (phys_2_virt( 94 volatile_read!(port.clb) as usize 95 + slot as usize * size_of::<HbaCmdHeader>() as usize, 96 ) as *mut HbaCmdHeader) 97 .as_mut() 98 .unwrap() 99 }; 100 101 cmdheader.cfl = (size_of::<FisRegH2D>() / size_of::<u32>()) as u8; 102 103 volatile_set_bit!(cmdheader.cfl, 1 << 6, false); // Read/Write bit : Read from device 104 volatile_write!(cmdheader.prdtl, check_length as u16); // PRDT entries count 105 106 // 设置数据存放地址 107 let mut buf_ptr = buf as *mut [u8] as *mut usize as usize; 108 109 // 由于目前的内存管理机制无法把用户空间的内存地址转换为物理地址,所以只能先把数据拷贝到内核空间 110 // TODO:在内存管理重构后,可以直接使用用户空间的内存地址 111 112 let user_buf = verify_area(VirtAddr::new(buf_ptr as usize), buf.len()).is_ok(); 113 let mut kbuf = if user_buf { 114 let mut x: Vec<u8> = Vec::new(); 115 x.resize(buf.len(), 0); 116 Some(x) 117 } else { 118 None 119 }; 120 121 if kbuf.is_some() { 122 buf_ptr = kbuf.as_mut().unwrap().as_mut_ptr() as usize; 123 } 124 125 #[allow(unused_unsafe)] 126 let cmdtbl = unsafe { 127 (phys_2_virt(volatile_read!(cmdheader.ctba) as usize) as *mut HbaCmdTable) 128 .as_mut() 129 .unwrap() // 必须使用 as_mut ,得到的才是原来的变量 130 }; 131 let mut tmp_count = count; 132 133 unsafe { 134 // 清空整个table的旧数据 135 write_bytes(cmdtbl, 0, 1); 136 } 137 // kdebug!("cmdheader.prdtl={}", volatile_read!(cmdheader.prdtl)); 138 139 // 8K bytes (16 sectors) per PRDT 140 for i in 0..((volatile_read!(cmdheader.prdtl) - 1) as usize) { 141 volatile_write!(cmdtbl.prdt_entry[i].dba, virt_2_phys(buf_ptr) as u64); 142 cmdtbl.prdt_entry[i].dbc = 8 * 1024 - 1; 143 volatile_set_bit!(cmdtbl.prdt_entry[i].dbc, 1 << 31, true); // 允许中断 prdt_entry.i 144 buf_ptr += 8 * 1024; 145 tmp_count -= 16; 146 } 147 148 // Last entry 149 let las = (volatile_read!(cmdheader.prdtl) - 1) as usize; 150 volatile_write!(cmdtbl.prdt_entry[las].dba, virt_2_phys(buf_ptr) as u64); 151 cmdtbl.prdt_entry[las].dbc = ((tmp_count << 9) - 1) as u32; // 数据长度 152 153 volatile_set_bit!(cmdtbl.prdt_entry[las].dbc, 1 << 31, true); // 允许中断 154 155 // 设置命令 156 let cmdfis = unsafe { 157 ((&mut cmdtbl.cfis) as *mut [u8] as *mut usize as *mut FisRegH2D) 158 .as_mut() 159 .unwrap() 160 }; 161 volatile_write!(cmdfis.fis_type, FisType::RegH2D as u8); 162 volatile_set_bit!(cmdfis.pm, 1 << 7, true); // command_bit set 163 volatile_write!(cmdfis.command, ATA_CMD_READ_DMA_EXT); 164 165 volatile_write!(cmdfis.lba0, (lba_id_start & 0xFF) as u8); 166 volatile_write!(cmdfis.lba1, ((lba_id_start >> 8) & 0xFF) as u8); 167 volatile_write!(cmdfis.lba2, ((lba_id_start >> 16) & 0xFF) as u8); 168 volatile_write!(cmdfis.lba3, ((lba_id_start >> 24) & 0xFF) as u8); 169 volatile_write!(cmdfis.lba4, ((lba_id_start >> 32) & 0xFF) as u8); 170 volatile_write!(cmdfis.lba5, ((lba_id_start >> 40) & 0xFF) as u8); 171 172 volatile_write!(cmdfis.countl, (count & 0xFF) as u8); 173 volatile_write!(cmdfis.counth, ((count >> 8) & 0xFF) as u8); 174 175 volatile_write!(cmdfis.device, 1 << 6); // LBA Mode 176 177 // 等待之前的操作完成 178 let mut spin_count = 0; 179 const SPIN_LIMIT: u32 = 10000; 180 181 while (volatile_read!(port.tfd) as u8 & (ATA_DEV_BUSY | ATA_DEV_DRQ)) > 0 182 && spin_count < SPIN_LIMIT 183 { 184 spin_count += 1; 185 } 186 187 if spin_count == SPIN_LIMIT { 188 kerror!("Port is hung"); 189 return Err(SystemError::EIO); 190 } 191 192 volatile_set_bit!(port.ci, 1 << slot, true); // Issue command 193 // kdebug!("To wait ahci read complete."); 194 // 等待操作完成 195 loop { 196 if (volatile_read!(port.ci) & (1 << slot)) == 0 { 197 break; 198 } 199 if (volatile_read!(port.is) & HBA_PxIS_TFES) > 0 { 200 kerror!("Read disk error"); 201 return Err(SystemError::EIO); 202 } 203 } 204 205 if kbuf.is_some() { 206 buf.copy_from_slice(kbuf.as_ref().unwrap()); 207 } 208 209 compiler_fence(core::sync::atomic::Ordering::SeqCst); 210 // successfully read 211 return Ok(count * 512); 212 } 213 write_at( &self, lba_id_start: BlockId, count: usize, buf: &[u8], ) -> Result<usize, SystemError>214 fn write_at( 215 &self, 216 lba_id_start: BlockId, 217 count: usize, 218 buf: &[u8], 219 ) -> Result<usize, SystemError> { 220 assert!((buf.len() & 511) == 0); 221 compiler_fence(core::sync::atomic::Ordering::SeqCst); 222 let check_length = ((count - 1) >> 4) + 1; // prdt length 223 if count * 512 > buf.len() || check_length > 8 as usize { 224 // 不可能的操作 225 return Err(SystemError::E2BIG); 226 } else if count == 0 { 227 return Ok(0); 228 } 229 230 let port = _port(self.ctrl_num, self.port_num); 231 232 volatile_write!(port.is, u32::MAX); // Clear pending interrupt bits 233 234 let slot = port.find_cmdslot().unwrap_or(u32::MAX); 235 236 if slot == u32::MAX { 237 return Err(SystemError::EIO); 238 } 239 240 compiler_fence(core::sync::atomic::Ordering::SeqCst); 241 #[allow(unused_unsafe)] 242 let cmdheader: &mut HbaCmdHeader = unsafe { 243 (phys_2_virt( 244 volatile_read!(port.clb) as usize 245 + slot as usize * size_of::<HbaCmdHeader>() as usize, 246 ) as *mut HbaCmdHeader) 247 .as_mut() 248 .unwrap() 249 }; 250 compiler_fence(core::sync::atomic::Ordering::SeqCst); 251 252 volatile_write_bit!( 253 cmdheader.cfl, 254 (1 << 5) - 1 as u8, 255 (size_of::<FisRegH2D>() / size_of::<u32>()) as u8 256 ); // Command FIS size 257 258 volatile_set_bit!(cmdheader.cfl, 7 << 5, true); // (p,c,w)都设置为1, Read/Write bit : Write from device 259 volatile_write!(cmdheader.prdtl, check_length as u16); // PRDT entries count 260 261 // 设置数据存放地址 262 compiler_fence(core::sync::atomic::Ordering::SeqCst); 263 let mut buf_ptr = buf as *const [u8] as *mut usize as usize; 264 265 // 由于目前的内存管理机制无法把用户空间的内存地址转换为物理地址,所以只能先把数据拷贝到内核空间 266 // TODO:在内存管理重构后,可以直接使用用户空间的内存地址 267 let user_buf = verify_area(VirtAddr::new(buf_ptr as usize), buf.len()).is_ok(); 268 let mut kbuf = if user_buf { 269 let mut x: Vec<u8> = Vec::with_capacity(buf.len()); 270 x.resize(buf.len(), 0); 271 x.copy_from_slice(buf); 272 Some(x) 273 } else { 274 None 275 }; 276 277 if kbuf.is_some() { 278 buf_ptr = kbuf.as_mut().unwrap().as_mut_ptr() as usize; 279 } 280 281 #[allow(unused_unsafe)] 282 let cmdtbl = unsafe { 283 (phys_2_virt(volatile_read!(cmdheader.ctba) as usize) as *mut HbaCmdTable) 284 .as_mut() 285 .unwrap() 286 }; 287 let mut tmp_count = count; 288 compiler_fence(core::sync::atomic::Ordering::SeqCst); 289 290 unsafe { 291 // 清空整个table的旧数据 292 write_bytes(cmdtbl, 0, 1); 293 } 294 295 // 8K bytes (16 sectors) per PRDT 296 for i in 0..((volatile_read!(cmdheader.prdtl) - 1) as usize) { 297 volatile_write!(cmdtbl.prdt_entry[i].dba, virt_2_phys(buf_ptr) as u64); 298 volatile_write_bit!(cmdtbl.prdt_entry[i].dbc, (1 << 22) - 1, 8 * 1024 - 1); // 数据长度 299 volatile_set_bit!(cmdtbl.prdt_entry[i].dbc, 1 << 31, true); // 允许中断 300 buf_ptr += 8 * 1024; 301 tmp_count -= 16; 302 } 303 304 // Last entry 305 let las = (volatile_read!(cmdheader.prdtl) - 1) as usize; 306 volatile_write!(cmdtbl.prdt_entry[las].dba, virt_2_phys(buf_ptr) as u64); 307 volatile_set_bit!(cmdtbl.prdt_entry[las].dbc, 1 << 31, true); // 允许中断 308 volatile_write_bit!( 309 cmdtbl.prdt_entry[las].dbc, 310 (1 << 22) - 1, 311 ((tmp_count << 9) - 1) as u32 312 ); // 数据长度 313 314 // 设置命令 315 let cmdfis = unsafe { 316 ((&mut cmdtbl.cfis) as *mut [u8] as *mut usize as *mut FisRegH2D) 317 .as_mut() 318 .unwrap() 319 }; 320 volatile_write!(cmdfis.fis_type, FisType::RegH2D as u8); 321 volatile_set_bit!(cmdfis.pm, 1 << 7, true); // command_bit set 322 volatile_write!(cmdfis.command, ATA_CMD_WRITE_DMA_EXT); 323 324 volatile_write!(cmdfis.lba0, (lba_id_start & 0xFF) as u8); 325 volatile_write!(cmdfis.lba1, ((lba_id_start >> 8) & 0xFF) as u8); 326 volatile_write!(cmdfis.lba2, ((lba_id_start >> 16) & 0xFF) as u8); 327 volatile_write!(cmdfis.lba3, ((lba_id_start >> 24) & 0xFF) as u8); 328 volatile_write!(cmdfis.lba4, ((lba_id_start >> 32) & 0xFF) as u8); 329 volatile_write!(cmdfis.lba5, ((lba_id_start >> 40) & 0xFF) as u8); 330 331 volatile_write!(cmdfis.countl, (count & 0xFF) as u8); 332 volatile_write!(cmdfis.counth, ((count >> 8) & 0xFF) as u8); 333 334 volatile_write!(cmdfis.device, 1 << 6); // LBA Mode 335 336 volatile_set_bit!(port.ci, 1 << slot, true); // Issue command 337 338 // 等待操作完成 339 loop { 340 if (volatile_read!(port.ci) & (1 << slot)) == 0 { 341 break; 342 } 343 if (volatile_read!(port.is) & HBA_PxIS_TFES) > 0 { 344 kerror!("Write disk error"); 345 return Err(SystemError::EIO); 346 } 347 } 348 349 compiler_fence(core::sync::atomic::Ordering::SeqCst); 350 // successfully read 351 return Ok(count * 512); 352 } 353 sync(&self) -> Result<(), SystemError>354 fn sync(&self) -> Result<(), SystemError> { 355 // 由于目前没有block cache, 因此sync返回成功即可 356 return Ok(()); 357 } 358 } 359 360 impl LockedAhciDisk { new( name: String, flags: u16, ctrl_num: u8, port_num: u8, ) -> Result<Arc<LockedAhciDisk>, SystemError>361 pub fn new( 362 name: String, 363 flags: u16, 364 ctrl_num: u8, 365 port_num: u8, 366 ) -> Result<Arc<LockedAhciDisk>, SystemError> { 367 // 构建磁盘结构体 368 let result: Arc<LockedAhciDisk> = Arc::new(LockedAhciDisk(SpinLock::new(AhciDisk { 369 name, 370 flags, 371 partitions: Default::default(), 372 ctrl_num, 373 port_num, 374 self_ref: Weak::default(), 375 }))); 376 377 let table: MbrDiskPartionTable = result.read_mbr_table()?; 378 379 // 求出有多少可用分区 380 for i in 0..4 { 381 compiler_fence(Ordering::SeqCst); 382 if table.dpte[i].part_type != 0 { 383 let w = Arc::downgrade(&result); 384 result.0.lock().partitions.push(Partition::new( 385 table.dpte[i].starting_sector() as u64, 386 table.dpte[i].starting_lba as u64, 387 table.dpte[i].total_sectors as u64, 388 w, 389 i as u16, 390 )); 391 } 392 } 393 394 result.0.lock().self_ref = Arc::downgrade(&result); 395 396 return Ok(result); 397 } 398 399 /// @brief: 从磁盘中读取 MBR 分区表结构体 TODO: Cursor read_mbr_table(&self) -> Result<MbrDiskPartionTable, SystemError>400 pub fn read_mbr_table(&self) -> Result<MbrDiskPartionTable, SystemError> { 401 let mut table: MbrDiskPartionTable = Default::default(); 402 403 // 数据缓冲区 404 let mut buf: Vec<u8> = Vec::new(); 405 buf.resize(size_of::<MbrDiskPartionTable>(), 0); 406 407 self.read_at(0, 1, &mut buf)?; 408 // 创建 Cursor 用于按字节读取 409 let mut cursor = VecCursor::new(buf); 410 cursor.seek(SeekFrom::SeekCurrent(446))?; 411 412 for i in 0..4 { 413 kdebug!("infomation of partition {}:\n", i); 414 415 table.dpte[i].flags = cursor.read_u8()?; 416 table.dpte[i].starting_head = cursor.read_u8()?; 417 table.dpte[i].starting_sector_cylinder = cursor.read_u16()?; 418 table.dpte[i].part_type = cursor.read_u8()?; 419 table.dpte[i].ending_head = cursor.read_u8()?; 420 table.dpte[i].ending_sector_cylingder = cursor.read_u16()?; 421 table.dpte[i].starting_lba = cursor.read_u32()?; 422 table.dpte[i].total_sectors = cursor.read_u32()?; 423 424 kdebug!("dpte[i] = {:?}", table.dpte[i]); 425 } 426 table.bs_trailsig = cursor.read_u16()?; 427 // kdebug!("bs_trailsig = {}", unsafe { 428 // read_unaligned(addr_of!(table.bs_trailsig)) 429 // }); 430 431 return Ok(table); 432 } 433 } 434 435 impl KObject for LockedAhciDisk { as_any_ref(&self) -> &dyn core::any::Any436 fn as_any_ref(&self) -> &dyn core::any::Any { 437 self 438 } 439 inode(&self) -> Option<Arc<KernFSInode>>440 fn inode(&self) -> Option<Arc<KernFSInode>> { 441 todo!() 442 } 443 kobj_type(&self) -> Option<&'static dyn KObjType>444 fn kobj_type(&self) -> Option<&'static dyn KObjType> { 445 todo!() 446 } 447 kset(&self) -> Option<Arc<KSet>>448 fn kset(&self) -> Option<Arc<KSet>> { 449 todo!() 450 } 451 parent(&self) -> Option<Weak<dyn KObject>>452 fn parent(&self) -> Option<Weak<dyn KObject>> { 453 todo!() 454 } 455 set_inode(&self, _inode: Option<Arc<KernFSInode>>)456 fn set_inode(&self, _inode: Option<Arc<KernFSInode>>) { 457 todo!() 458 } 459 kobj_state(&self) -> RwLockReadGuard<KObjectState>460 fn kobj_state(&self) -> RwLockReadGuard<KObjectState> { 461 todo!() 462 } 463 kobj_state_mut(&self) -> RwLockWriteGuard<KObjectState>464 fn kobj_state_mut(&self) -> RwLockWriteGuard<KObjectState> { 465 todo!() 466 } 467 set_kobj_state(&self, _state: KObjectState)468 fn set_kobj_state(&self, _state: KObjectState) { 469 todo!() 470 } 471 name(&self) -> alloc::string::String472 fn name(&self) -> alloc::string::String { 473 todo!() 474 } 475 set_name(&self, _name: alloc::string::String)476 fn set_name(&self, _name: alloc::string::String) { 477 todo!() 478 } 479 set_kset(&self, _kset: Option<Arc<KSet>>)480 fn set_kset(&self, _kset: Option<Arc<KSet>>) { 481 todo!() 482 } 483 set_parent(&self, _parent: Option<Weak<dyn KObject>>)484 fn set_parent(&self, _parent: Option<Weak<dyn KObject>>) { 485 todo!() 486 } 487 set_kobj_type(&self, _ktype: Option<&'static dyn KObjType>)488 fn set_kobj_type(&self, _ktype: Option<&'static dyn KObjType>) { 489 todo!() 490 } 491 } 492 493 impl Device for LockedAhciDisk { dev_type(&self) -> DeviceType494 fn dev_type(&self) -> DeviceType { 495 return DeviceType::Block; 496 } 497 id_table(&self) -> IdTable498 fn id_table(&self) -> IdTable { 499 todo!() 500 } 501 bus(&self) -> Option<Weak<dyn Bus>>502 fn bus(&self) -> Option<Weak<dyn Bus>> { 503 todo!("LockedAhciDisk::bus()") 504 } 505 set_bus(&self, _bus: Option<Weak<dyn Bus>>)506 fn set_bus(&self, _bus: Option<Weak<dyn Bus>>) { 507 todo!("LockedAhciDisk::set_bus()") 508 } 509 driver(&self) -> Option<Arc<dyn Driver>>510 fn driver(&self) -> Option<Arc<dyn Driver>> { 511 todo!("LockedAhciDisk::driver()") 512 } 513 is_dead(&self) -> bool514 fn is_dead(&self) -> bool { 515 false 516 } 517 set_driver(&self, _driver: Option<Weak<dyn Driver>>)518 fn set_driver(&self, _driver: Option<Weak<dyn Driver>>) { 519 todo!("LockedAhciDisk::set_driver()") 520 } 521 can_match(&self) -> bool522 fn can_match(&self) -> bool { 523 todo!() 524 } 525 set_can_match(&self, _can_match: bool)526 fn set_can_match(&self, _can_match: bool) { 527 todo!() 528 } 529 state_synced(&self) -> bool530 fn state_synced(&self) -> bool { 531 todo!() 532 } 533 set_class(&self, _class: Option<Arc<dyn Class>>)534 fn set_class(&self, _class: Option<Arc<dyn Class>>) { 535 todo!() 536 } 537 } 538 539 impl BlockDevice for LockedAhciDisk { 540 #[inline] as_any_ref(&self) -> &dyn core::any::Any541 fn as_any_ref(&self) -> &dyn core::any::Any { 542 self 543 } 544 545 #[inline] blk_size_log2(&self) -> u8546 fn blk_size_log2(&self) -> u8 { 547 9 548 } 549 sync(&self) -> Result<(), SystemError>550 fn sync(&self) -> Result<(), SystemError> { 551 return self.0.lock().sync(); 552 } 553 554 #[inline] device(&self) -> Arc<dyn Device>555 fn device(&self) -> Arc<dyn Device> { 556 return self.0.lock().self_ref.upgrade().unwrap(); 557 } 558 block_size(&self) -> usize559 fn block_size(&self) -> usize { 560 todo!() 561 } 562 partitions(&self) -> Vec<Arc<Partition>>563 fn partitions(&self) -> Vec<Arc<Partition>> { 564 return self.0.lock().partitions.clone(); 565 } 566 567 #[inline] read_at( &self, lba_id_start: BlockId, count: usize, buf: &mut [u8], ) -> Result<usize, SystemError>568 fn read_at( 569 &self, 570 lba_id_start: BlockId, // 起始lba编号 571 count: usize, // 读取lba的数量 572 buf: &mut [u8], 573 ) -> Result<usize, SystemError> { 574 self.0.lock().read_at(lba_id_start, count, buf) 575 } 576 577 #[inline] write_at( &self, lba_id_start: BlockId, count: usize, buf: &[u8], ) -> Result<usize, SystemError>578 fn write_at( 579 &self, 580 lba_id_start: BlockId, 581 count: usize, 582 buf: &[u8], 583 ) -> Result<usize, SystemError> { 584 self.0.lock().write_at(lba_id_start, count, buf) 585 } 586 } 587