Question regarding LBA28 and 48 IO
Posted: Sun Feb 03, 2013 11:23 pm
First post, and it's a question, huzzah. I've been here a while though.
In any case, I've written a PIO driver for reading from a hard drive using LBA28 and 48.
The result was code that was seemingly functional at first, but I've now found it to be a major problem.
Basically here's the issue I've found:
Regardless of where I write and read from, (LBA address wise) I get the same result;
I tried writing 5 different strings to consecutive addresses, but when I tried reading them back out,
I ended with the same string times.
(The result string that was that which was written to the last address.)
I also tried changing the address values to ones grossly far away from each other (0, 512, 1024, etc.)
And ended up with the same result.
I made sure that I was clearing my buffers correctly between operations by the way.
Below is my code:
(Despite my best efforts, my comments are not correctly aligned in the post.)
And now for my 2 main questions;
1. I've spent a good deal of time trying to find out what purpose sector count serves,
The wiki didn't yield much insight into what it does, and from my observations a value of 1
Will make all subsequent read/write attempts hang whereas any other value will not,
Regardless how many operation you make.
I've tried to look it up in Wikipedia as well... Not much help either sadly.
2. Why do my read/write operations not go through correctly?
The fact writing a string "Hello" to address 4 and then reading from address 512 will retrieve that string
Suggests to me that all my reads and write are going to sector 0 on the drive.
That and the fact that the virtual drive file is still 1MB in size.
Remark: I'm working under Virtual Box using a dynamically sized vdi drive image.
I'm also working in 32 bits, so u64int is typedef for unsigned long long.
Thanks in advance to anyone who takes the time to make heads or tails of this!
In any case, I've written a PIO driver for reading from a hard drive using LBA28 and 48.
The result was code that was seemingly functional at first, but I've now found it to be a major problem.
Basically here's the issue I've found:
Regardless of where I write and read from, (LBA address wise) I get the same result;
I tried writing 5 different strings to consecutive addresses, but when I tried reading them back out,
I ended with the same string times.
(The result string that was that which was written to the last address.)
I also tried changing the address values to ones grossly far away from each other (0, 512, 1024, etc.)
And ended up with the same result.
I made sure that I was clearing my buffers correctly between operations by the way.
Below is my code:
Code: Select all
// Read operations
#define DRIVE_READ 0x20
#define DRIVE_READ_EXT 0x24
// Write operations
#define DRIVE_WRITE 0x30
#define DRIVE_WRITE_EXT 0x34
// Master/lave masks
#define MASTER 0xE0
#define MASTER_EXT 0x40
// Register masks
#define REG_DATA 0
#define REG_FEATURES 1
#define REG_SECT_COUNT 2
#define REG_SECT_NUM 3
#define REG_CYL_LOW 4
#define REG_CYL_HIGH 5
#define REG_DRIVE 6
#define REG_COMMAND 7
u8int read_lba28(u16int drive, u32int lba_address, u8int *buffer) {
u16int i = 0, tmp = 0;
// Master/Slave and high nibble
outportb(drive | REG_DRIVE, MASTER | (drive << 4) | ((lba_address >> 24) & 0x0F));
outportb(drive | REG_FEATURES, 0x00); // Null byte
outportb(drive | REG_SECT_COUNT, 0x00); // Sector count
outportb(drive | REG_SECT_NUM, (u8int) lba_address); // Low 8 bits
outportb(drive | REG_CYL_LOW, (u8int) (lba_address >> 8)); // Next 8 bits
outportb(drive | REG_CYL_HIGH, (u8int) (lba_address >> 16)); // Next 8 bits
outportb(drive | REG_COMMAND, DRIVE_READ); // Read command
timer_wait(1); // Tiny delay
while(!(inportb(drive | REG_COMMAND) & 0x08)); // Wait for drive to be ready
for(i = 0; i < 256; i++) { // Read data into buffer
tmp = inportw(drive); // Read a word
*buffer++ = (u8int)tmp; // Low byte
*buffer++ = (u8int)(tmp >> 8); // High byte
}
return 1; // Return on success
}
u8int write_lba28(u16int drive, u32int lba_address, u8int *buffer) {
u16int i = 0, tmp = 0;
// Master/Slave and high nibble
outportb(drive | REG_DRIVE, MASTER | (drive << 4) | ((lba_address >> 24) & 0x0F));
outportb(drive | REG_FEATURES, 0x00); // Null byte
outportb(drive | REG_SECT_COUNT, 0x00); // Sector count
outportb(drive | REG_SECT_NUM, (u8int) lba_address); // Low 8 bits
outportb(drive | REG_CYL_LOW, (u8int) (lba_address >> 8)); // Next 8 bits
outportb(drive | REG_CYL_HIGH, (u8int) (lba_address >> 16)); // Next 8 bits
outportb(drive | REG_COMMAND, DRIVE_WRITE); // Write command
timer_wait(1); // Tiny delay
while(!(inportb(drive | REG_COMMAND) & 0x08)); // Wait for drive to be ready
for(i = 0; i < 256; i++) { // Write data from buffer
tmp = *buffer++; // Store low byte
tmp |= (*buffer++ << 8); // OR with high byte
outportw(drive, tmp); // Write word to drive
}
outportb(drive | REG_COMMAND, 0xE7); // Flush the cache
return 1; // Return on success
}
u8int read_lba48(u16int drive, u64int lba_address, u8int *buffer, u32int sector_count) {
u16int i = 0, tmp = 0;
outportb(drive | REG_DRIVE, MASTER_EXT | (drive << 4)); // Master/Slave byte
outportb(drive | REG_SECT_COUNT, (u8int)(sector_count >> 8)); // Sector count high byte
outportb(drive | REG_SECT_NUM, (u8int)(lba_address >> 24)); // High byte 1 LBA address
outportb(drive | REG_CYL_LOW, (u8int)(lba_address >> 32)); // High byte 2 LBA address
outportb(drive | REG_CYL_HIGH, (u8int)(lba_address >> 40)); // High byte 3 LBA address
outportb(drive | REG_SECT_COUNT, (u8int)(sector_count)); // Sector count low byte
outportb(drive | REG_SECT_NUM, (u8int)(lba_address)); // Low byte 1 LBA address
outportb(drive | REG_CYL_LOW, (u8int)(lba_address >> 8)); // Low byte 2 LBA address
outportb(drive | REG_CYL_HIGH, (u8int)(lba_address >> 16)); // Low byte 3 LBA address
outportb(drive | REG_COMMAND, DRIVE_READ_EXT); // Read operation
while(!(inportb(drive | REG_COMMAND) & 0x08)); // Wait for drive to be ready
for(i = 0; i < 256; i++) { // Read data into buffer
tmp = inportw(drive); // Read a word
*buffer++ = (u8int)tmp; // Low byte
*buffer++ = (u8int)(tmp >> 8); // High byte
}
return 1; // Return on success
}
u8int write_lba48(u16int drive, u64int lba_address, u8int *buffer, u32int sector_count) {
u16int i = 0, tmp = 0;
outportb(drive | REG_DRIVE, MASTER_EXT | (drive << 4)); // Master/Slave byte
outportb(drive | REG_SECT_COUNT, (u8int)(sector_count >> 8)); // Sector count high byte
outportb(drive | REG_SECT_NUM, (u8int)(lba_address >> 24)); // High byte 1 LBA address
outportb(drive | REG_CYL_LOW, (u8int)(lba_address >> 32)); // High byte 2 LBA address
outportb(drive | REG_CYL_HIGH, (u8int)(lba_address >> 40)); // High byte 3 LBA address
outportb(drive | REG_SECT_COUNT, (u8int)(sector_count)); // Sector count low byte
outportb(drive | REG_SECT_NUM, (u8int)(lba_address)); // Low byte 1 LBA address
outportb(drive | REG_CYL_LOW, (u8int)(lba_address >> 8)); // Low byte 2 LBA address
outportb(drive | REG_CYL_HIGH, (u8int)(lba_address >> 16)); // Low byte 3 LBA address
outportb(drive | REG_COMMAND, DRIVE_WRITE_EXT); // Write operation
while(!(inportb(drive | 7) & 0x08)); // Wait for drive to be ready
for(i = 0; i < 256; i++) { // Write data from buffer
tmp = *buffer++; // Store low byte
tmp |= (*buffer++ << 8); // OR with high byte
outportw(drive, tmp); // Write word to drive
}
outportb(drive | REG_COMMAND, 0xE7); // Flush the cache
return 1; // Return on success
}
And now for my 2 main questions;
1. I've spent a good deal of time trying to find out what purpose sector count serves,
The wiki didn't yield much insight into what it does, and from my observations a value of 1
Will make all subsequent read/write attempts hang whereas any other value will not,
Regardless how many operation you make.
I've tried to look it up in Wikipedia as well... Not much help either sadly.
2. Why do my read/write operations not go through correctly?
The fact writing a string "Hello" to address 4 and then reading from address 512 will retrieve that string
Suggests to me that all my reads and write are going to sector 0 on the drive.
That and the fact that the virtual drive file is still 1MB in size.
Remark: I'm working under Virtual Box using a dynamically sized vdi drive image.
I'm also working in 32 bits, so u64int is typedef for unsigned long long.
Thanks in advance to anyone who takes the time to make heads or tails of this!