Confusion with port sizes and LBA instructions for ATA

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Confusion with port sizes and LBA instructions for ATA

Post by Ethin »

So, I've done some major restructuring with my kernel, and I'm changing the port mappings to use actual readable names instead of direct out() function calls. However, I'm confused on what types to assign to the ports (I'm putting them all in a struct). The ACS-4 standard says:
7.1.3 Feature Set
The feature set subclause for each command lists the feature set (see clause 4) along with a statement that indicates if the command uses 28-bit field formatting or 48-bit field formatting. If a command uses 28-bit formatting, then:
a) the FEATURE field, COUNT field, DEVICE field, ERROR field, STATUS field, and COMMAND field are each eight bits in length; and
b) the LBA field is 28 bits in length.
If a command uses 48-bit formatting, then:
a) the DEVICE field, ERROR field, STATUS field, and COMMAND field are each eight bits in length;
b) the FEATURE field and COUNT field are 16 bits in length; and
c) the LBA field is 48 bits in length.
EXAMPLE - A feature set subclause reads:
Feature Set
This 28-bit command is for all ATA devices.
Following the ATA in PIO mode wiki article, as well as the above mappings, my struct is defined like this:

Code: Select all

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, partialEq, Ord, PartialOrd, Hash)]
pub enum Drive {
    Master,
    Slave,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct AtaDevice {
    master_data: Port<u16>,
    master_error28: PortReadOnly<u8>,
    master_error48: PortReadOnly<u16>,
    master_features28: PortWriteOnly<u8>,
    master_features48: PortWriteOnly<u16>,
    master_count28: Port<u8>,
    master_count48: Port<u16>,
    master_lba28_lo: Port<u8>,
    master_lba28_mid: Port<u8>,
    master_lba28_hi: Port<u8>,
    master_lba48_lo: Port<u16>,
    master_lba48_mid: Port<u16>,
    master_lba48_hi: Port<u16>,
    master_drive: Port<u8>,
    master_status: PortReadOnly<u8>,
    master_command: PortWriteOnly<u8>,
    master_alt_status: PortReadOnly<u8>,
    master_dev_ctl: PortWriteOnly<u8>,
    master_drive_addr: PortReadOnly<u8>,
    slave_data: Port<u16>,
    slave_error28: PortReadOnly<u8>,
    slave_error48: PortReadOnly<u16>,
    slave_features28: PortWriteOnly<u8>,
    slave_features48: PortWriteOnly<u16>,
    slave_count28: Port<u8>,
    slave_count48: Port<u16>,
    slave_lba28_lo: Port<u8>,
    slave_lba28_mid: Port<u8>,
    slave_lba28_hi: Port<u8>,
    slave_lba48_lo: Port<u16>,
    slave_lba48_mid: Port<u16>,
    slave_lba48_hi: Port<u16>,
    slave_drive: Port<u8>,
    slave_status: PortReadOnly<u8>,
    slave_command: PortWriteOnly<u8>,
    slave_alt_status: PortReadOnly<u8>,
    slave_dev_ctl: PortWriteOnly<u8>,
    slave_drive_addr: PortReadOnly<u8>,
    drive: Drive,
}
I initialize this using the port offsets in the ATA PIO Mode article. But I'm kinda confused on the port sizes for these ports, aswell as the instructions for reading and writing 28-bit LBAs. The wiki article says to write LBA lo, LBA mid, and LBA hi, as bits 7:0, 15:8, and 24:16, respectively. But what about bits 31:25? I know that bits 31:29 are ignored, but bits 28:25 are not.
Octocontrabass
Member
Member
Posts: 5575
Joined: Mon Mar 25, 2013 7:01 pm

Re: Confusion with port sizes and LBA instructions for ATA

Post by Octocontrabass »

The data port is 16 bits. All other ports are 8 bits.

The registers you access using the 8-bit ports may be either 8 bits or 16 bits. If the drive doesn't support the LBA48 feature, all of those registers are 8 bits. If it does support the LBA48 feature, five of those registers are extended to 16 bits: features, sector count, LBA low, LBA mid, and LBA high. Each time you write a byte to a 16-bit register, the low 8 bits of the register are moved to the high 8 bits, and the value you wrote is placed in the low 8 bits. This means that you must write the high byte before you write the low byte when you want to write all 16 bits of the register, but it also means that you may skip writing the high byte for commands that only use the low 8 bits.

The LBA field is mapped to those registers differently depending on whether the command is LBA28 or LBA48. For LBA28, only the low byte of each register is used, and the high byte is ignored. Bits 0-7 are mapped to LBA low, bits 8-15 are mapped to LBA mid, bits 16-23 are mapped to LBA high, and bits 24-27 are mapped to the low four bits of the device register. For LBA48, bits 0-23 are mapped the same way as LBA28, and bits 24-47 are mapped to the high byte of each of the three LBA registers. Bits 24-31 are mapped to LBA low, bits 32-39 are mapped to LBA mid, and bits 40-47 are mapped to LBA high.
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Confusion with port sizes and LBA instructions for ATA

Post by Ethin »

Got it. Thanks! (Perhaps that should be documented in the article itself... There doesn't seem to be an explanation to port sizes and mappings, and so I wouldn't be surprised if many people get it wrong the first few times.)
Edit: Also, the wiki article says:
(Notes: A sector count of 0 means 65536 sectors = 32MB. Try not to send bytes to the same IO port twice in a row. Doing so is much slower than doing two outb() commands to different IO ports. The important thing is that the high byte of the sector count, features and LBA bytes 4, 5, & 6 go to their respective ports before the low bytes.)
Does this work the same way when reading data back? For example, the GET NATIVE MAX ADDRESS EXT command (command 78h, feature 0001h) returns the native maximum LBA in the LBA field. To reconstruct that LBA, would I read the LBA field lo, mid, and hi bytes twice, then merge the first lo, mid, and hi into bits 24-31, 32-39, and 40-47, then the rest into bits 0-7, 8-15, and 16-23?
Octocontrabass
Member
Member
Posts: 5575
Joined: Mon Mar 25, 2013 7:01 pm

Re: Confusion with port sizes and LBA instructions for ATA

Post by Octocontrabass »

Ethin wrote:Does this work the same way when reading data back?
No. Reads are controlled by HOB (bit 7) in the device control register. When HOB is clear, you'll read the low byte. When HOB is set, you'll read the high byte. You can set and clear HOB by writing to the device control register, but writing to any other register will also clear HOB.

In the GET NATIVE MAX ADDRESS EXT example, you'd read the LBA low/mid/high registers once with HOB clear and once with HOB set.
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Confusion with port sizes and LBA instructions for ATA

Post by Ethin »

Octocontrabass wrote:
Ethin wrote:Does this work the same way when reading data back?
No. Reads are controlled by HOB (bit 7) in the device control register. When HOB is clear, you'll read the low byte. When HOB is set, you'll read the high byte. You can set and clear HOB by writing to the device control register, but writing to any other register will also clear HOB.

In the GET NATIVE MAX ADDRESS EXT example, you'd read the LBA low/mid/high registers once with HOB clear and once with HOB set.
Aha. So I'd set HOB, read, then clear it and read again? Like what's the order I do that in?
Octocontrabass
Member
Member
Posts: 5575
Joined: Mon Mar 25, 2013 7:01 pm

Re: Confusion with port sizes and LBA instructions for ATA

Post by Octocontrabass »

Whatever order you want.

I'd aim for the fewest port accesses: read the low bytes, set HOB, and read the high bytes. That way there's no need to explicitly clear HOB, since it will be cleared by the writes for the next command.
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Confusion with port sizes and LBA instructions for ATA

Post by Ethin »

Alright. Thanks!
Post Reply