diff --git a/kernel/src/arch/x86_64/driver/ide.rs b/kernel/src/arch/x86_64/driver/ide.rs index 475a95b..356978e 100644 --- a/kernel/src/arch/x86_64/driver/ide.rs +++ b/kernel/src/arch/x86_64/driver/ide.rs @@ -5,267 +5,120 @@ use spin::Mutex; lazy_static! { - pub static ref DISK0: LockedIde = LockedIde(Mutex::new(DmaController::new(0))); - pub static ref DISK1: LockedIde = LockedIde(Mutex::new(DmaController::new(1))); + pub static ref DISK0: LockedIde = LockedIde(Mutex::new(IDE::new(0))); + pub static ref DISK1: LockedIde = LockedIde(Mutex::new(IDE::new(1))); } pub const BLOCK_SIZE: usize = 512; -pub struct LockedIde(pub Mutex); +pub struct LockedIde(pub Mutex); -pub struct DmaController { +pub struct IDE { num: u8, + /// I/O Base + base: u16, + /// Control Base + ctrl: u16, } -impl DmaController -{ - /// Read ATA DMA. Block size = 512 bytes. - pub fn read(&self, blockidx: u64, count: usize, dst: &mut [u32]) -> Result { - assert_eq!(dst.len(), count * SECTOR_SIZE); - let dst = if count > MAX_DMA_SECTORS { &mut dst[..MAX_DMA_SECTORS * SECTOR_SIZE] } else { dst }; - //self.do_dma(blockidx, DMABuffer::new_mut(dst, 32), disk, false); - self.ide_read_secs(self.num, blockidx, dst, count as u8) - } - /// Write ATA DMA. Block size = 512 bytes. - pub fn write(&self, blockidx: u64, count: usize, dst: &[u32]) -> Result { - assert_eq!(dst.len(), count * SECTOR_SIZE); - let dst = if count > MAX_DMA_SECTORS { &dst[..MAX_DMA_SECTORS * SECTOR_SIZE] } else { dst }; - //println!("ide_write_secs: disk={},blockidx={},count={}",disk,blockidx,count); - self.ide_write_secs(self.num, blockidx, dst, count as u8) - } - /// Create structure and init - fn new(num: u8) -> Self { - assert!(num < MAX_IDE as u8); - let ide = DmaController { num }; - ide.ide_init(); +impl IDE { + pub fn new(num: u8) -> Self { + let ide = match num { + 0 => IDE { num: 0, base: 0x1f0, ctrl: 0x3f4 }, + 1 => IDE { num: 1, base: 0x1f0, ctrl: 0x3f4 }, + 2 => IDE { num: 2, base: 0x170, ctrl: 0x374 }, + 3 => IDE { num: 3, base: 0x170, ctrl: 0x374 }, + _ => panic!("ide number should be 0,1,2,3"), + }; + ide.init(); ide } - fn ide_wait_ready(&self, iobase: u16, check_error: usize) -> usize { + /// Read ATA DMA. Block size = 512 bytes. + pub fn read(&self, sector: u64, count: usize, data: &mut [u32]) -> Result<(), ()> { + assert_eq!(data.len(), count * SECTOR_SIZE); + self.wait(); unsafe { - let mut r = port::inb(iobase + ISA_STATUS); - //println!("iobase:{} ready:{}",iobase,r); - while (r & IDE_BSY) > 0 { - r = port::inb(iobase + ISA_STATUS); - //println!("busy"); - } - /* nothing */ - if check_error == 1 && (r & (IDE_DF | IDE_ERR)) != 0 { - return 1; + self.select(sector, count as u8); + port::outb(self.base + ISA_COMMAND, IDE_CMD_READ); + for i in 0..count { + let ptr = &data[(i as usize) * SECTOR_SIZE]; + if self.wait_error() { + return Err(()); + } + asm!("rep insl" :: "{dx}"(self.base), "{rdi}"(ptr), "{cx}"(SECTOR_SIZE) : "rdi" : "volatile"); } } - return 0; + Ok(()) } - - fn ide_init(&self) { - //static_assert((SECTSIZE % 4) == 0); - let ideno = self.num; - //println!("ideno:{}",ideno); - /* assume that no device here */ - //ide_devices[ideno].valid = 0; - - //let iobase = IO_BASE(ideno); - let iobase = CHANNELS[if ideno > 2 { 1 } else { 0 }].0; - - /* wait device ready */ - self.ide_wait_ready(iobase, 0); - //println!("ide_wait_ready"); + /// Write ATA DMA. Block size = 512 bytes. + pub fn write(&self, sector: u64, count: usize, data: &[u32]) -> Result<(), ()> { + assert_eq!(data.len(), count * SECTOR_SIZE); + self.wait(); unsafe { - /* step1: select drive */ - //println!("outb"); - port::outb(iobase + ISA_SDH, (0xE0 | ((ideno & 1) << 4)) as u8); - self.ide_wait_ready(iobase, 0); - - /* step2: send ATA identify command */ - //println!("outb"); - port::outb(iobase + ISA_COMMAND, IDE_CMD_IDENTIFY); - self.ide_wait_ready(iobase, 0); - - /* step3: polling */ - //println!("inb"); - if port::inb(iobase + ISA_STATUS) == 0 || self.ide_wait_ready(iobase, 1) != 0 { - return; - } - - //println!("insl"); - let mut buffer: [u32; 128] = [0; 128]; - for i in 0..buffer.len() { - buffer[i] = i as u32; - if i == 1 { - //println!("{:#x}",&buffer[i] as *const u32 as usize - ::consts::KERNEL_OFFSET) + self.select(sector, count as u8); + port::outb(self.base + ISA_COMMAND, IDE_CMD_WRITE); + for i in 0..count { + let ptr = &data[(i as usize) * SECTOR_SIZE]; + if self.wait_error() { + return Err(()); } - } - //println!("insl {:#x}",&buffer as *const u32 as usize - ::consts::KERNEL_OFFSET); - - //println!("insl {:#x}",buffer.as_ptr() as usize - ::consts::KERNEL_OFFSET); - //port::insl(iobase + ISA_DATA, &mut buffer); - let port = iobase + ISA_DATA; - //let buf=&mut buffer; - for i in 0..buffer.len() { - asm!("insl %dx, (%rdi)" - :: "{dx}"(port), "{rdi}"(&buffer[i]) - : "rdi" : "volatile"); - } - //println!("insl"); - for i in 0..4 { - info!("ide init: {}", buffer[i]); + asm!("rep outsl" :: "{dx}"(self.base), "{rsi}"(ptr), "{cx}"(SECTOR_SIZE) : "rsi" : "volatile"); } } - /* device is ok */ - //ide_devices[ideno].valid = 1; - - /* read identification space of the device */ - /*let buffer[128]; - insl(iobase + ISA_DATA, buffer, sizeof(buffer) / sizeof(unsigned int)); - - unsigned char *ident = (unsigned char *)buffer; - unsigned int sectors; - unsigned int cmdsets = *(unsigned int *)(ident + IDE_IDENT_CMDSETS); - /* device use 48-bits or 28-bits addressing */ - if (cmdsets & (1 << 26)) { - sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA_EXT); - } - else { - sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA); - } - ide_devices[ideno].sets = cmdsets; - ide_devices[ideno].size = sectors; - - /* check if supports LBA */ - assert((*(unsigned short *)(ident + IDE_IDENT_CAPABILITIES) & 0x200) != 0); - - unsigned char *model = ide_devices[ideno].model, *data = ident + IDE_IDENT_MODEL; - unsigned int i, length = 40; - for (i = 0; i < length; i += 2) { - model[i] = data[i + 1], model[i + 1] = data[i]; - } - do { - model[i] = '\0'; - } while (i -- > 0 && model[i] == ' '); - - cprintf("ide %d: %10u(sectors), '%s'.\n", ideno, ide_devices[ideno].size, ide_devices[ideno].model);*/ + Ok(()) + } - // enable ide interrupt - //pic_enable(IRQ_IDE1); - //pic_enable(IRQ_IDE2); + fn wait(&self) { + while unsafe { port::inb(self.base + ISA_STATUS) } & IDE_BUSY != 0 {} + } - info!("ide {} init end", self.num); + fn wait_error(&self) -> bool { + self.wait(); + let status = unsafe { port::inb(self.base + ISA_STATUS) }; + status & (IDE_DF | IDE_ERR) != 0 } - fn ide_read_secs<'a>(&'a self, ideno: u8, secno: u64, dst: &'a mut [u32], nsecs: u8) -> Result { - //assert(nsecs <= MAX_NSECS && VALID_IDE(ideno)); - //assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS); - let iobase = CHANNELS[if ideno > 2 { 1 } else { 0 }].0; - let ioctrl = CHANNELS[if ideno > 2 { 1 } else { 0 }].1; - //ide_wait_ready(iobase, 0); + fn init(&self) { + self.wait(); + unsafe { + // step1: select drive + port::outb(self.base + ISA_SDH, (0xE0 | ((self.num & 1) << 4)) as u8); + self.wait(); - self.ide_wait_ready(iobase, 0); + // step2: send ATA identify command + port::outb(self.base + ISA_COMMAND, IDE_CMD_IDENTIFY); + self.wait(); - let ret = 0; - // generate interrupt - unsafe { - port::outb(ioctrl + ISA_CTRL, 0); - port::outb(iobase + ISA_SECCNT, nsecs); - port::outb(iobase + ISA_SECTOR, (secno & 0xFF) as u8); - port::outb(iobase + ISA_CYL_LO, ((secno >> 8) & 0xFF) as u8); - port::outb(iobase + ISA_CYL_HI, ((secno >> 16) & 0xFF) as u8); - port::outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | (((secno >> 24) & 0xF) as u8)); - //port::outb(iobase + ISA_SDH, (0xE0 | ((ideno & 1) << 4)) as u8); - //self.ide_wait_ready(iobase, 0); - port::outb(iobase + ISA_COMMAND, IDE_CMD_READ); - //self.ide_wait_ready(iobase, 0); - // if port::inb(iobase + ISA_STATUS) == 0 || self.ide_wait_ready(iobase, 1) != 0 { - // println!("error?"); - // } - for i in 0..nsecs { - //dst = dst + SECTSIZE; - let tmp = &mut dst[(i as usize) * SECTOR_SIZE..((i + 1) as usize) * SECTOR_SIZE]; - if self.ide_wait_ready(iobase, 1) != 0 { - println!("wait ready error"); - } - //self.ide_wait_ready(iobase, 1); - //port::insl(iobase, tmp); - let port = iobase; - //let buf=&mut buffer; - for i in 0..tmp.len() { - asm!("insl %dx, (%rdi)" - :: "{dx}"(port), "{rdi}"(&tmp[i]) - : "rdi" : "volatile"); - } - //println!("read :{}",i); + // step3: polling + if port::inb(self.base + ISA_STATUS) == 0 || self.wait_error() { + return; } + + // ??? + let mut data = [0; SECTOR_SIZE]; + asm!("rep insl" :: "{dx}"(self.base + ISA_DATA), "{rdi}"(data.as_ptr()), "{cx}"(SECTOR_SIZE) : "rdi" : "volatile"); } - Ok(ret) } - fn ide_write_secs<'a>(&'a self, ideno: u8, secno: u64, src: &'a [u32], nsecs: u8) -> Result { - //assert(nsecs <= MAX_NSECS && VALID_IDE(ideno)); - //assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS); - let iobase = CHANNELS[if ideno > 2 { 1 } else { 0 }].0; - let ioctrl = CHANNELS[if ideno > 2 { 1 } else { 0 }].1; - - //ide_wait_ready(iobase, 0); - - self.ide_wait_ready(iobase, 0); - - let ret = 0; - // generate interrupt + fn select(&self, sector: u64, count: u8) { + assert_ne!(count, 0); + self.wait(); unsafe { - port::outb(ioctrl + ISA_CTRL, 0); - port::outb(iobase + ISA_SECCNT, nsecs); - port::outb(iobase + ISA_SECTOR, (secno & 0xFF) as u8); - port::outb(iobase + ISA_CYL_LO, ((secno >> 8) & 0xFF) as u8); - port::outb(iobase + ISA_CYL_HI, ((secno >> 16) & 0xFF) as u8); - port::outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | (((secno >> 24) & 0xF) as u8)); - port::outb(iobase + ISA_COMMAND, IDE_CMD_WRITE); - //println!("{}",nsecs); - for i in 0..nsecs { - //dst = dst + SECTSIZE; - // if ((ret = ide_wait_ready(iobase, 1)) != 0) { - // goto out; - // } - //port::insb(iobase, dst); - //println!("i={}",i); - let tmp = &src[(i as usize) * SECTOR_SIZE..((i + 1) as usize) * SECTOR_SIZE]; - if self.ide_wait_ready(iobase, 1) != 0 { - println!("wait ready error"); - } - //println!("write {}:{}",i,src[i as usize]); - //println!("outsl"); - //port::outsl(iobase, tmp); - let port = iobase; - //let buf=&mut buffer; - for i in 0..tmp.len() { - asm!("outsl (%rsi), %dx" - :: "{dx}"(port), "{rsi}"(&tmp[i]) - : "rsi"); - } - //println!("write :{}",i); - // for i in 0..4 { - // println!("{}",src[i as usize]); - // } - //port::outb(iobase, src[i as usize]); - } + // generate interrupt + port::outb(self.ctrl + ISA_CTRL, 0); + port::outb(self.base + ISA_SECCNT, count); + port::outb(self.base + ISA_SECTOR, (sector & 0xFF) as u8); + port::outb(self.base + ISA_CYL_LO, ((sector >> 8) & 0xFF) as u8); + port::outb(self.base + ISA_CYL_HI, ((sector >> 16) & 0xFF) as u8); + port::outb(self.base + ISA_SDH, 0xE0 | ((self.num & 1) << 4) | (((sector >> 24) & 0xF) as u8)); } - Ok(ret) } } const SECTOR_SIZE: usize = 128; -//const MAX_DMA_SECTORS: usize = 0x2_0000 / SECTOR_SIZE; // Limited by sector count (and PRDT entries) const MAX_DMA_SECTORS: usize = 0x1F_F000 / SECTOR_SIZE; // Limited by sector count (and PRDT entries) // 512 PDRT entries, assume maximum fragmentation = 512 * 4K max = 2^21 = 2MB per transfer -const HDD_PIO_W28: u8 = 0x30; -const HDD_PIO_R28: u8 = 0x20; -const HDD_PIO_W48: u8 = 0x34; -const HDD_PIO_R48: u8 = 0x24; -const HDD_IDENTIFY: u8 = 0xEC; - -const HDD_DMA_R28: u8 = 0xC8; -const HDD_DMA_W28: u8 = 0xCA; -const HDD_DMA_R48: u8 = 0x25; -const HDD_DMA_W48: u8 = 0x35; - const ISA_DATA: u16 = 0x00; const ISA_ERROR: u16 = 0x01; const ISA_PRECOMP: u16 = 0x01; @@ -278,7 +131,7 @@ const ISA_SDH: u16 = 0x06; const ISA_COMMAND: u16 = 0x07; const ISA_STATUS: u16 = 0x07; -const IDE_BSY: u8 = 0x80; +const IDE_BUSY: u8 = 0x80; const IDE_DRDY: u8 = 0x40; const IDE_DF: u8 = 0x20; const IDE_DRQ: u8 = 0x08; @@ -288,33 +141,7 @@ const IDE_CMD_READ: u8 = 0x20; const IDE_CMD_WRITE: u8 = 0x30; const IDE_CMD_IDENTIFY: u8 = 0xEC; -const IDE_IDENT_SECTORS: usize = 20; -const IDE_IDENT_MODEL: usize = 54; -const IDE_IDENT_CAPABILITIES: usize = 98; -const IDE_IDENT_CMDSETS: usize = 164; -const IDE_IDENT_MAX_LBA: usize = 120; -const IDE_IDENT_MAX_LBA_EXT: usize = 200; - -const IO_BASE0: u16 = 0x1F0; -const IO_BASE1: u16 = 0x170; -const IO_CTRL0: u16 = 0x3F4; -const IO_CTRL1: u16 = 0x374; - -const MAX_IDE: usize = 4; const MAX_NSECS: usize = 128; -//const MAX_DISK_NSECS 0x10000000U; -//const VALID_IDE(ideno) (((ideno) >= 0) && ((ideno) < MAX_IDE) && (ide_devices[ideno].valid)) - -struct Channels { - base: u16, - // I/O Base - ctrl: u16, // Control Base -} - -const CHANNELS: [(u16, u16); 2] = [(IO_BASE0, IO_CTRL0), (IO_BASE1, IO_CTRL1)]; - -//const IO_BASE(ideno) (CHANNELS[(ideno) >> 1].base) -//const IO_CTRL(ideno) (CHANNELS[(ideno) >> 1].ctrl) mod port { use x86_64::instructions::port::Port;