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
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.
Any ideas?
I can give more information, or my bochsrc, if needed. Also sorry again for how messy the code is