1 ; =============================================================================
2 ; Pure64 -- a 64-bit OS loader written in Assembly for x86-64 systems
3 ; Copyright (C) 2008-2012 Return Infinity -- see LICENSE.TXT
6 ; =============================================================================
8 ; The code below only utilizes the Master drive on the Primary ATA Bus.
9 ; Port IO is hard-coded to Primary : 0x01F0 - 0x01F7, 0x03F6
10 ; Secondary Bus would be 0x0170 - 0x0177, 0x0376
13 ; Probe for PATA hard drive
16 add dx, 7 ; Primary ATA Regular Status Byte
18 cmp al, 0xFF ; Check for "float" value
19 je hdd_setup_try_sata
; No drive detected
20 test al, 0xA9 ; Is ERR, DRQ, DF, or BSY set?
21 jne hdd_setup_err_read
22 jmp hdd_setup_load_sector
24 ; Probe for a hard drive controller
29 cmp bx, 256 ; Search up to 256 buses
30 je hdd_setup_err_drive
; No drive detected
31 cmp cl, 32 ; Up to 32 devices per bus
36 mov dl, 2 ; We want the Class/Device code
40 cmp ax, 0x0106 ; Mass storage device, SATA
44 call os_pci_read_reg
; BAR5 (AHCI Base Address Register)
46 call os_debug_dump_eax
50 call os_debug_dump_eax
55 mov rsi
, no_sata_legacy
62 call os_debug_dump_eax
71 and ax, 0xFFFC ; AX now holds the Base IO Address (clear the low 2 bits)
72 ; call os_debug_dump_eax
76 cmp al, 0xFF ; Check for "float" value
77 je hdd_setup_err_drive
; No drive detected
78 test al, 0xA9 ; Is ERR, DRQ, DF, or BSY set?
79 jne hdd_setup_err_read
81 ; Read first sector of HDD into memory
82 hdd_setup_load_sector:
83 xor rax
, rax
; We want sector 0
86 mov rcx
, 1 ; Read one sector
92 cmp byte [cfg_mbr
], 0x01 ; Did we boot from a MBR drive
93 jne hdd_setup_no_mbr
; If not then we already have the correct sector
95 ; Grab the partition offset value for the first partition
97 mov [fat16_PartitionOffset
], eax
99 ; Read the first sector of the first partition
106 je hdd_setup_err_read
109 ; Get the values we need to start using fat16
111 mov [fat16_BytesPerSector
], ax ; This will probably be 512
113 mov [fat16_SectorsPerCluster
], al ; This will be 128 or less (Max cluster size is 64KiB)
115 mov [fat16_ReservedSectors
], ax
116 mov [fat16_FatStart
], eax
118 mov [fat16_Fats
], al ; This will probably be 2
120 mov [fat16_RootDirEnts
], ax
122 mov [fat16_SectorsPerFat
], ax
124 ; Find out how many sectors are on the disk
128 jne lessthan65536sectors
130 lessthan65536sectors:
131 mov [fat16_TotalSectors
], eax
133 ; Calculate the size in MiB
135 mov eax, [fat16_TotalSectors
]
136 mov [hd1_maxlba
], rax
137 shr rax
, 11 ; rax = rax * 512 / 1048576
138 mov [hd1_size
], eax ; in mebibytes (MiB)
140 ; Create a string of the harddrive size
141 mov rdi
, hdtempstring
142 call os_int_to_string
146 mov ax, [fat16_SectorsPerFat
]
147 shl ax, 1 ; quick multiply by two
148 add ax, [fat16_ReservedSectors
]
149 mov [fat16_RootStart
], eax
150 mov bx, [fat16_RootDirEnts
]
151 shr ebx, 4 ; bx = (bx * 32) / 512
152 add ebx, eax ; BX now holds the datastart sector number
153 mov [fat16_DataStart
], ebx
158 mov rsi
, hdd_setup_no_drive
160 jmp exception_gate_halt
163 mov rsi
, hdd_setup_read_error
165 jmp exception_gate_halt
167 ; -----------------------------------------------------------------------------
168 ; readsectors -- Read sectors on the hard drive
169 ; IN: RAX = starting sector to read
170 ; RCX = number of sectors to read (1 - 256)
171 ; RDI = memory location to store sectors
172 ; OUT: RAX = RAX + number of sectors that were read
173 ; RCX = number of sectors that were read (0 on error)
174 ; RDI = RDI + (number of sectors * 512)
175 ; All other registers preserved
182 push rcx
; Save RCX for use in the read loop
183 mov rbx
, rcx
; Store number of sectors to read
185 je readsectors_fail
; Try to read nothing? Fail!
187 jg readsectors_fail
; Over 256? Fail!
188 jne readsectors_skip
; Not 256? No need to modify CL
189 xor rcx
, rcx
; 0 translates to 256
192 push rax
; Save RAX since we are about to overwrite it
194 ;mov dx, 0x01F2 ; 0x01F2 - Sector count Port 7:0
196 mov al, cl ; Read CL sectors
198 pop rax
; Restore RAX which is our sector number
199 inc dx ; 0x01F3 - LBA Low Port 7:0
201 inc dx ; 0x01F4 - LBA Mid Port 15:8
204 inc dx ; 0x01F5 - LBA High Port 23:16
207 inc dx ; 0x01F6 - Device Port. Bit 6 set for LBA mode, Bit 4 for device (0 = master, 1 = slave), Bits 3-0 for LBA "Extra High" (27:24)
209 and al, 00001111b ; Clear bits 4-7 just to be safe
210 or al, 01000000b ; Turn bit 6 on since we want to use LBA addressing, leave device at 0 (master)
212 inc dx ; 0x01F7 - Command Port
213 mov al, 0x20 ; Read sector(s). 0x24 if LBA48
218 in al, dx ; Read status from 0x01F7
219 test al, 0x80 ; BSY flag set?
220 jne readsectors_retry
221 test al, 0x08 ; DRQ set?
222 jne readsectors_dataready
226 readsectors_nextsector:
227 in al, dx ; Read status from 0x01F7
228 test al, 0x80 ; BSY flag set?
229 jne readsectors_nextsector
230 test al, 0x21 ; ERR or DF set?
233 readsectors_dataready:
234 sub dx, 7 ; Data port (0x1F0)
236 rep insw ; Copy a 512 byte sector to RDI
237 add dx, 7 ; Set DX back to status register (0x01F7)
238 in al, dx ; Delay ~400ns to allow drive to set new values of BSY and DRQ
243 dec rbx
; RBX is the "sectors to read" counter
245 jne readsectors_nextsector
247 test al, 0x21 ; ERR or DF set?
264 xor rcx
, rcx
; Set RCX to 0 since nothing was read
266 ; -----------------------------------------------------------------------------
268 no_sata_legacy: db "SATA Controller only suppports native mode!", 13, 0
270 ; =============================================================================