OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 11:02 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 11 posts ] 
Author Message
 Post subject: ATA Status BSY bit never clears
PostPosted: Sun Feb 02, 2020 9:48 pm 
Offline

Joined: Sun Jan 19, 2020 12:25 pm
Posts: 4
Hi all-
I've recently been trying to identify an ATA PIO drive, following the wiki's instructions. Reading the STATUS port works, but the BSY bit of the STATUS port never clears.

My code is in Rust, but it should hopefully be pretty self explanatory.
Code:
pub fn init() {
    unsafe {
        io::outb(DRIVESEL, 0xE0);
        io::outb(SECTOR_COUNT, 0);
        io::outb(LBAL, 0);
        io::outb(LBAM, 0);
        io::outb(LBAH, 0);
        io::outb(COMMAND, ATACommand::IdentifyDevice as u8);

        if io::inb(STATUS) == 0 {
            println!("ATA: master not found");
        } else {
            println!("ATA: master found");
        }

        io::outb(DRIVESEL, 0xF0);
        io::outb(SECTOR_COUNT, 0);
        io::outb(LBAL, 0);
        io::outb(LBAM, 0);
        io::outb(LBAH, 0);
        io::outb(COMMAND, ATACommand::IdentifyDevice as u8);

        if io::inb(STATUS) == 0 {
            println!("ATA: slave not found");
        } else {
            println!("ATA: slave found");
        }

        while io::inb(STATUS).get_bit(BSY) {
            crate::hlt_loop();
        }

        if io::inb(STATUS).get_bit(DRQ) && !io::inb(STATUS).get_bit(ERR) {
            let mut data: [u16; 256] = [0; 256];
            for i in 0..data.len() {
                data[i] = io::inw(DATA);
                print!("{}", data[i]);
            }
        } else {
            println!("ATA: read error");
            return;
        }

    }
}


Top
 Profile  
 
 Post subject: Re: ATA Status BSY bit never clears
PostPosted: Mon Feb 03, 2020 8:13 am 
Offline
Member
Member

Joined: Tue May 13, 2014 3:02 am
Posts: 280
Location: Private, UK
How are you determining that it never clears? Could it not be that "crate::hlt_loop();" never returns? If that function does what its name suggests (the HLT instruction) it definitely won't return unless/until there's a CPU interrupt, so are interrupts enabled and unmasked in the PIC?

Also, surely you should be status-polling after every command you send and before you trust the resulting status value...?

_________________
Image


Top
 Profile  
 
 Post subject: Re: ATA Status BSY bit never clears
PostPosted: Mon Feb 03, 2020 8:16 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
I'm pretty sure hlt_loop() is an infinite loop. It'll never return, even if the BSY bit eventually clears.

With that said, I wouldn't be surprised if it never clears. When no hardware is responding at a particular address, you'll read the open bus value. It's common (but not guaranteed) that open bus will have all bits set; in other words, it will look like the status register is always 0xFF.

Also, keep in mind that IDE is legacy hardware, and most new PCs don't support it at all. The wiki article you're reading was first written almost 12 years ago, and hasn't been updated to take this into account. You should search for IDE-compatible PCI devices before assuming an IDE controller exists and is present at a specific address. (Of course, if you're only using an emulator, you can cheat a little.)


Top
 Profile  
 
 Post subject: Re: ATA Status BSY bit never clears
PostPosted: Mon Feb 03, 2020 10:18 am 
Offline

Joined: Sun Jan 19, 2020 12:25 pm
Posts: 4
Octocontrabass wrote:
With that said, I wouldn't be surprised if it never clears. When no hardware is responding at a particular address, you'll read the open bus value. It's common (but not guaranteed) that open bus will have all bits set; in other words, it will look like the status register is always 0xFF.

It looks like you're right that STATUS is returning 0xFF. I don't know if this is related or not, but I'm running qemu with -machine q35, which seems to function the same as my hardware (Thinkpad X220). When I run regular qemu, I get a double fault when reading status.
Are there any more up to date resources for using ATA PIO, or should I just go with writing an AHCI driver?


Top
 Profile  
 
 Post subject: Re: ATA Status BSY bit never clears
PostPosted: Mon Feb 03, 2020 10:34 am 
Offline
Member
Member

Joined: Thu May 17, 2007 1:27 pm
Posts: 999
Is it even supported to send a request to one device, while switching to the other device, without waiting for completion?

_________________
managarm: Microkernel-based OS capable of running a Wayland desktop (Discord: https://discord.gg/7WB6Ur3). My OS-dev projects: [mlibc: Portable C library for managarm, qword, Linux, Sigma, ...] [LAI: AML interpreter] [xbstrap: Build system for OS distributions].


Top
 Profile  
 
 Post subject: Re: ATA Status BSY bit never clears
PostPosted: Mon Feb 03, 2020 11:22 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
wsavage wrote:
I don't know if this is related or not, but I'm running qemu with -machine q35, which seems to function the same as my hardware (Thinkpad X220).

QEMU's q35 machine provides an AHCI controller instead of an IDE controller. Your code will only work with an IDE controller.

wsavage wrote:
When I run regular qemu, I get a double fault when reading status.

Reading the status register can't directly cause a double fault. There must be a problem somewhere else. For example, if you've enabled interrupts without configuring the interrupt controllers, the timer will cause IRQ0 which is by default mapped to interrupt 8 which is indistinguishable from a double fault.

wsavage wrote:
Are there any more up to date resources for using ATA PIO, or should I just go with writing an AHCI driver?

This page explains how to search the PCI bus, and this page describes what to look for. In short, you're looking for a device with class 0x01 and subclass 0x01, and if you find it you'll read BAR0 and BAR1 to determine the correct I/O ports.

With QEMU, you can cheat a bit: the default "pc" (i440fx) machine always has an IDE controller mapped at the legacy addresses, so you can skip all of the PCI stuff and keep doing what you're doing now. Then, once you have your driver working, you can add PCI support to make it work with a wider variety of hardware (and make sure it doesn't try to run when there is no IDE controller).


Top
 Profile  
 
 Post subject: Re: ATA Status BSY bit never clears
PostPosted: Mon Feb 03, 2020 9:23 pm 
Offline

Joined: Sun Jan 19, 2020 12:25 pm
Posts: 4
Thanks for the help. The cause of the double fault was a lack of interrupt handlers, as you had said. When I run without
Code:
machine -q35
, status reads 88 for both master and slave. The wiki, as well as other code examples that I have looked at, say to read 256 u16s from the data port to get device information. However, when I do this, only 2/3s of the values are actual numbers, and the rest are zeroes. A screenshot of the output is attached. Am I doing something wrong here? I can't seem to find much about this online.

Here's my read code. Apologies for rust.
Code:
      let mut raw: [u16; 256] = [0; 256];
        if io::inb(STATUS).get_bit(DRQ) && !io::inb(STATUS).get_bit(ERR) {
            for i in 0..raw.len() {
                raw[i] = io::inw(DATA);
                print!("{}", raw[i]);
            }
        } else {
            println!("ATA: read error");
        }


Attachments:
wrong3.png
wrong3.png [ 71.53 KiB | Viewed 2800 times ]
Top
 Profile  
 
 Post subject: Re: ATA Status BSY bit never clears
PostPosted: Tue Feb 04, 2020 2:28 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
wsavage wrote:
Am I doing something wrong here?

It looks right to me, although the way you're displaying those numbers makes it very difficult to interpret the results.

Look for the IDENTIFY DEVICE section in the ATA specification if you want to know how to interpret that data. A lot of it is considered obsolete, so you might have to look at several different versions of the ATA specification to understand all of the information the drive gives you.


Top
 Profile  
 
 Post subject: Re: ATA Status BSY bit never clears
PostPosted: Tue Feb 07, 2023 12:55 pm 
Offline

Joined: Tue Mar 29, 2022 10:49 am
Posts: 2
Sorry for reviving this topic, however got the same problem.

I tested a lot of things, but none of them seem to work.
My ATA identify structure (taken from MSDN) looks like this:

Code:
struct ata_identify {
  struct {
    uint16_t reserved1 : 1;
    uint16_t obsolete1 : 1;
    uint16_t response_incomplete : 1;
    uint16_t obsolete2 : 3;
    uint16_t fixed_device : 1;
    uint16_t removable_media : 1;
    uint16_t obsolete3 : 7;
    uint16_t atapi : 1;
  } general_info;
  uint16_t num_cylinders;
  uint16_t specific_configuration;
  uint16_t num_heads;
  uint16_t retired1[2];
  uint16_t num_sectors_per_track;
  uint16_t vendor_unique1[3];
  char serialnumber[20];
  uint16_t deprecated[2];
  uint16_t obsolete1;
  char firmware_revision[8];
  char model_number[40];
  char maximum_block_transfer;
  char vendor_unique2;

} __pack;



I have looked into specification of ATA, it should be correct.

The polling and data code:

Code:
  bool drq = false, err = false;
  status = ata_read_status(dev);
  klogf("polling ata status: %d", status);
  while (!(err = (status & ATA_STATUS_ERR) != 0) &&
         !(drq = (status & ATA_STATUS_DRQ) != 0)) {
    klogf("polling ata status: %d", status);
    if (inb(dev->pio_base + ATA_REG_LBA_MID) ||
        inb(dev->pio_base + ATA_REG_LBA_HI)) {
      klogf("device is not ata");
      kfree(data, 256 * sizeof(uint16_t));
      return NULL;
    }
    status = ata_read_status(dev);
  }

  klogf("finished polling ata status: %d (err=%d drq=%d)", status, err, drq);

  if (err) {
    klogf("error reading ata device: err=%d drq=%d", err, drq);
    return NULL;
  }

  for (int i = 0; i < 256; i++)
    *(uint16_t *)(data + i) = inw(dev->pio_base + ATA_REG_DATA);


Te data from this code is not meaningful. The model & serial number are incorrect and consist of junk characters. Number of cylinders and heads also seems way too big (screenshot).

The emulator I'm using is QEMU, and I've attached the drives using the following snippet
Code:
QEMU=qemu-system-i386
QEMU_ARGS="-M pc-i440fx-2.8 -m 4G -monitor none -serial stdio"
BOOT_ISO=myiso.iso

$QEMU $QEMU_ARGS \
  -drive id=boot,file=$BOOT_ISO,format=raw,if=none,unit=0 \
  -drive id=disk1,file=mydisk.img,format=raw,if=none,unit=1 \
  -device ide-hd,drive=boot,bus=ide.0,serial=hello,model=world -device ide-hd,drive=disk1,bus=ide.0



Thanks in advance.


Attachments:
File comment: The model is model_number and serial is serialnumber
WindowsTerminal_qF4mx8Yoa2.png
WindowsTerminal_qF4mx8Yoa2.png [ 7.99 KiB | Viewed 1505 times ]
Top
 Profile  
 
 Post subject: Re: ATA Status BSY bit never clears
PostPosted: Wed Feb 08, 2023 11:14 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
lapfed255 wrote:
Code:
    *(uint16_t *)(data + i) = inw(dev->pio_base + ATA_REG_DATA);

Unless "data" is already a pointer to a type compatible with uint16_t, this code is not going to fill the buffer correctly and might even be undefined behavior.


Top
 Profile  
 
 Post subject: Re: ATA Status BSY bit never clears
PostPosted: Thu Feb 09, 2023 8:32 am 
Offline

Joined: Tue Mar 29, 2022 10:49 am
Posts: 2
Octocontrabass wrote:
lapfed255 wrote:
Code:
    *(uint16_t *)(data + i) = inw(dev->pio_base + ATA_REG_DATA);

Unless "data" is already a pointer to a type compatible with uint16_t, this code is not going to fill the buffer correctly and might even be undefined behavior.


Yep that was the problem. I've already solved it, but thanks nonetheless.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Google [Bot], Yahoo [Bot] and 68 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group