I can't get my PIO ATA driver to identify drives

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
j4cobgarby
Member
Member
Posts: 64
Joined: Fri Jan 26, 2018 11:43 am

I can't get my PIO ATA driver to identify drives

Post by j4cobgarby »

I'm working on an ATA driver using PIO mode. First, I'm writing a procedure to use the IDENTIFY command to check if a particular drive exists. To begin with, I'm only using bus 1. Here's my code:

Code: Select all

ATA_BUS1_BASE       equ 0x1f0
ATA_BUS2_BASE       equ 0x170
ATA_BUS1_CTRL       equ 0x3f6
ATA_BUS2_CTRL       equ 0x376

ATA_BUS1_DATA       equ ATA_BUS1_BASE + 0
ATA_BUS1_ERR        equ ATA_BUS1_BASE + 1
ATA_BUS1_FEATURES   equ ATA_BUS1_BASE + 1
ATA_BUS1_SCOUNT     equ ATA_BUS1_BASE + 2
ATA_BUS1_LBALOW     equ ATA_BUS1_BASE + 3
ATA_BUS1_LBAMID     equ ATA_BUS1_BASE + 4
ATA_BUS1_LBAHI      equ ATA_BUS1_BASE + 5
ATA_BUS1_DRIVEHEAD  equ ATA_BUS1_BASE + 6
ATA_BUS1_STATUS     equ ATA_BUS1_BASE + 7
ATA_BUS1_COMMAND    equ ATA_BUS1_BASE + 7

ATA_BUS2_DATA       equ ATA_BUS2_BASE + 0
ATA_BUS2_ERR        equ ATA_BUS2_BASE + 1
ATA_BUS2_FEATURES   equ ATA_BUS2_BASE + 1
ATA_BUS2_SCOUNT     equ ATA_BUS2_BASE + 2
ATA_BUS2_LBALOW     equ ATA_BUS2_BASE + 3
ATA_BUS2_LBAMID     equ ATA_BUS2_BASE + 4
ATA_BUS2_LBAHI      equ ATA_BUS2_BASE + 5
ATA_BUS2_DRIVEHEAD  equ ATA_BUS2_BASE + 6
ATA_BUS2_STATUS     equ ATA_BUS2_BASE + 7
ATA_BUS2_COMMAND    equ ATA_BUS2_BASE + 7

ATA_BUS1_ALTSTATUS  equ ATA_BUS1_CTRL + 0
ATA_BUS1_DEVCTRL    equ ATA_BUS1_CTRL + 0
ATA_BUS1_DRIVEADDR  equ ATA_BUS1_CTRL + 1

ATA_BUS2_ALTSTATUS  equ ATA_BUS2_CTRL + 0
ATA_BUS2_DEVCTRL    equ ATA_BUS2_CTRL + 0
ATA_BUS2_DRIVEADDR  equ ATA_BUS2_CTRL + 1

ATA_TIMEOUT         equ 1000

ata_pio_detect:
    ; first check if any of the busses are floating (0xff)
    ; for any non-floating bus, use the IDENTIFY command for
    ; both master and slave

    mov dx, ATA_BUS1_STATUS
    in al, dx
    cmp al, 0xff
    jne .bus1_notff
.bus1_isff:
    and byte [ATA_BUS1_MASTER_STATUS], 11111110b ; the whole bus doesn't exist. this
    and byte [ATA_BUS1_SLAVE_STATUS],  11111110b ; is an error
    mov eax, 0
    mov ebx, bus1_nodrivesfound_str
    int 0x80
    jmp $ ; just wait here for now
    jmp .endffcheck
.bus1_notff:
    mov eax, 0
    mov ebx, bus1_drivepossible_str
    int 0x80
.endffcheck:
    ; now need to use the IDENTIFY command
    mov dx, ATA_BUS1_DRIVEHEAD
    mov ax, 0xa0 ; select master
    call select_drive

    call identify_drive

    mov dx, ATA_BUS1_DRIVEHEAD
    mov ax, 0xb0
    call select_drive

    call identify_drive

    ret
    
identify_drive:
    mov al, 0x00
    mov dx, ATA_BUS1_SCOUNT
    out dx, al
    mov dx, ATA_BUS1_LBALOW
    out dx, al
    mov dx, ATA_BUS1_LBAMID
    out dx, al
    mov dx, ATA_BUS1_LBAHI
    out dx, al
    mov al, 0xec
    mov dx, ATA_BUS1_COMMAND ; identify drive
    out dx, al

    mov dx, ATA_BUS1_STATUS
    in al, dx ; read status

    cmp al, 0
    je .master_notexists
.master_exists:
    or byte [ATA_BUS1_MASTER_STATUS], 0x1 ; set first bit of status
    mov ebx, bus1_master_exists_str
    mov eax, 0
    int 0x80
    
    jmp .mastercheck_end
.master_notexists:
    and byte [ATA_BUS1_MASTER_STATUS], 0x0 ; clear first bit of status
    mov ebx, bus1_master_not_exists_str
    mov eax, 0
    int 0x80
.mastercheck_end:
    ret

ata_pio_read48:
ata_pio_write48:

select_drive: ; selects a drive and also waits for bsy and drq to clear
    pusha
    ; dx = the IO addr of the drive register
    ; ax = the drive (0xa0 for master, 0xb0 for slave)

    out dx, ax

    inc dx ; set dx to the status register
    times 4 in ax, dx ; ax = the status register

    xor ecx, ecx
.hang:
    inc ecx
    cmp ecx, ATA_TIMEOUT
    je .skip
    in ax, dx
    bt ax, 7
    jc .hang ; wait until bsy is cleared
    bt ax, 3
    jc .hang ; and also until drq is cleared
.skip:
    popa
    ret

ATA_BUS1_MASTER_STATUS db 00000000b
ATA_BUS1_SLAVE_STATUS db 00000000b
; ^^
; bit 0: drive exists
; bit 1: supports lba48 mode

ATA_BUS1_MASTER_LBA28_SECTORS   dd 0
ATA_BUS1_MASTER_LBA48_SECTORS   dq 0
ATA_BUS1_SLAVE_LBA28_SECTORS    dd 0
ATA_BUS1_SLAVE_LBA48_SECTORS    dq 0

ata_identify_buffer:
    times 512 db 0 ; 256 16-bit values for returned data from IDENTIFY
bus1_nodrivesfound_str      db "No drive found on bus 1.",10,0
bus1_drivepossible_str      db "Drive(s) possible on bus 1.",10,0
bus1_master_exists_str      db "Master drive exists on ATA bus 1.",10,0
bus1_master_not_exists_str  db "Master drive doesn't exist on ATA bus 1.",10,0
bus1_slave_exists_str       db "Slave drive exists on ATA bus 1.",10,0
bus1_slave_not_exists_str   db "Slave drive doesn't exist on ATA bus 1.",10,0
(I know it's quite messy. Also ignore the fact that regardless of the drive which has been detected, it'll print that it found the master drive. That's something I'll change later, but at the moment it doesn't matter. That's just what it's printing.)

ata_pio_detect is the procedure I call from the main kernel procedure to detect drives. That then calls identify_drive, which assumes that the drive to test has already been selected. It then sends the command 0xec, which is IDENTIFY, and then checks if the status on the bus is 0. If it is 0, that should mean that the drive doesn't exist, otherwise it does exist. This is my understanding of how ATA PIO mode should work, please corect me if I'm wrong.

So the issue is, in my bochsrc I've set the system up such that the operating system boots from ata0-master, which is a disk. That should be the only drive on any bus. When I run the kernel, it prints:

Code: Select all

Drive(s) possible on bus 1.
Master drive exists on ATA bus 1.
Master drive exists on ATA bus 1.
The fact that it prints that twice means it thinks both the master and slave drive exist. This is wrong, I believe.

Any ideas? :)
I can give more information, or my bochsrc, if needed. Also sorry again for how messy the code is :P
Octocontrabass
Member
Member
Posts: 5580
Joined: Mon Mar 25, 2013 7:01 pm

Re: I can't get my PIO ATA driver to identify drives

Post by Octocontrabass »

j4cobgarby wrote:Any ideas? :)
Make sure all of your IN and OUT instructions are the correct size. The only ATA register that allows accesses larger than a single byte at a time is the data register (0x1F0/0x170).
j4cobgarby
Member
Member
Posts: 64
Joined: Fri Jan 26, 2018 11:43 am

Re: I can't get my PIO ATA driver to identify drives

Post by j4cobgarby »

Octocontrabass wrote:
j4cobgarby wrote:Any ideas? :)
Make sure all of your IN and OUT instructions are the correct size. The only ATA register that allows accesses larger than a single byte at a time is the data register (0x1F0/0x170).
That could well be the issue actually - quite a lot of the time I seem to be using the `in ax, dx` instruction rather than `in al, dx`. However, how would this cause such an issue?
Thanks.
Octocontrabass
Member
Member
Posts: 5580
Joined: Mon Mar 25, 2013 7:01 pm

Re: I can't get my PIO ATA driver to identify drives

Post by Octocontrabass »

The behavior isn't defined. I'm not entirely certain what Bochs does in this situation, but I wouldn't be surprised if it's just ignoring your attempts at writing the drive/head register.
j4cobgarby
Member
Member
Posts: 64
Joined: Fri Jan 26, 2018 11:43 am

Re: I can't get my PIO ATA driver to identify drives

Post by j4cobgarby »

Octocontrabass wrote:The behavior isn't defined. I'm not entirely certain what Bochs does in this situation, but I wouldn't be surprised if it's just ignoring your attempts at writing the drive/head register.
You were right, that fixed it! Thanks!
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: I can't get my PIO ATA driver to identify drives

Post by BenLunt »

While you are on the IDENTIFY subject, I would like to add that the specs state specifically how to identify an ATA or an ATAPI device.

You are first to send the ATA Identify command. If it fails, you either don't have a drive or you have an ATAPI drive attached. An ATAPI device will purposely fail the ATA Identify command for this purpose. Therefore sending the ATA Identify command first, then after fail, send the ATAPI Identify command will then allow you to identify if it is an ATA only or and ATAPI device attached. You can then verify in the IDENTIFY block returned if the ATAPI bit is set.

Trying adding a CDROM to your Bochsrc.txt file and watch your code run. Your current code will not find the CDROM because of this.

Of course if both commands fail, you don't have anything attached or you are doing something else wrong.

Ben
- http://www.fysnet.net/media_storage_devices.htm
User avatar
~
Member
Member
Posts: 1227
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

Re: I can't get my PIO ATA driver to identify drives

Post by ~ »

This thread explains how:

http://forum.osdev.org/viewtopic.php?f=1&t=20690

The detection code is well debugged. It won't fail to detect standard ATA/ATAPI hard disks/CDs/DVDs.
YouTube:
http://youtube.com/@AltComp126

My x86 emulator/kernel project and software tools/documentation:
http://master.dl.sourceforge.net/projec ... ip?viasf=1
Post Reply