1 ; =============================================================================
2 ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems
3 ; Copyright (C) 2008-2012 Return Infinity -- see LICENSE.TXT
6 ; =============================================================================
13 ; NOTE: These functions use LBA28. Maximum visible drive size is 128GiB
14 ; LBA48 would be needed to access sectors over 128GiB (up to 128PiB)
15 ; These functions are hard coded to access the Primary Master HDD
18 ; -----------------------------------------------------------------------------
19 ; readsectors -- Read sectors on the hard drive
20 ; IN: RAX = starting sector to read
21 ; RCX = number of sectors to read (1 - 256)
22 ; RDI = memory location to store sectors
23 ; OUT: RAX = RAX + number of sectors that were read
24 ; RCX = number of sectors that were read (0 on error)
25 ; RDI = RDI + (number of sectors * 512)
26 ; All other registers preserved
33 push rcx
; Save RCX for use in the read loop
34 mov rbx
, rcx
; Store number of sectors to read
36 jg readsectors_fail
; Over 256? Fail!
37 jne readsectors_skip
; Not 256? No need to modify CL
38 xor rcx
, rcx
; 0 translates to 256
41 push rax
; Save RAX since we are about to overwrite it
42 mov dx, 0x01F2 ; 0x01F2 - Sector count Port 7:0
43 mov al, cl ; Read CL sectors
45 pop rax
; Restore RAX which is our sector number
46 inc dx ; 0x01F3 - LBA Low Port 7:0
48 inc dx ; 0x01F4 - LBA Mid Port 15:8
51 inc dx ; 0x01F5 - LBA High Port 23:16
54 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)
56 and al, 00001111b ; Clear bits 4-7 just to be safe
57 or al, 01000000b ; Turn bit 6 on since we want to use LBA addressing, leave device at 0 (master)
59 inc dx ; 0x01F7 - Command Port
60 mov al, 0x20 ; Read sector(s). 0x24 if LBA48
65 in al, dx ; Read status from 0x01F7
66 test al, 0x80 ; BSY flag set?
68 test al, 0x08 ; DRQ set?
69 jne readsectors_dataready
73 readsectors_nextsector:
74 in al, dx ; Read status from 0x01F7
75 test al, 0x80 ; BSY flag set?
76 jne readsectors_nextsector
77 test al, 0x21 ; ERR or DF set?
80 readsectors_dataready:
81 sub dx, 7 ; Data port (0x1F0)
83 rep insw ; Copy a 512 byte sector to RDI
84 add dx, 7 ; Set DX back to status register (0x01F7)
85 in al, dx ; Delay ~400ns to allow drive to set new values of BSY and DRQ
90 dec rbx
; RBX is the "sectors to read" counter
92 jne readsectors_nextsector
108 xor rcx
, rcx
; Set RCX to 0 since nothing was read
110 ; -----------------------------------------------------------------------------
113 ; -----------------------------------------------------------------------------
114 ; writesectors -- Write sectors on the hard drive
115 ; IN: RAX = starting sector to write
116 ; RCX = number of sectors to write (1 - 256)
117 ; RSI = memory location of sectors
118 ; OUT: RAX = RAX + number of sectors that were written
119 ; RCX = number of sectors that were written (0 on error)
120 ; RSI = RSI + (number of sectors * 512)
121 ; All other registers preserved
128 push rcx
; Save RCX for use in the write loop
129 mov rbx
, rcx
; Store number of sectors to write
131 jg writesectors_fail
; Over 256? Fail!
132 jne writesectors_skip
; Not 256? No need to modify CL
133 xor rcx
, rcx
; 0 translates to 256
136 push rax
; Save RAX since we are about to overwrite it
137 mov dx, 0x01F2 ; 0x01F2 - Sector count Port 7:0
138 mov al, cl ; Write CL sectors
140 pop rax
; Restore RAX which is our sector number
141 inc dx ; 0x01F3 - LBA Low Port 7:0
143 inc dx ; 0x01F4 - LBA Mid Port 15:8
146 inc dx ; 0x01F5 - LBA High Port 23:16
149 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)
150 shr rax
, 8 ; Bits 7 and 5 are obsolete in LBA mode so set to 0
151 and al, 00001111b ; Clear bits 4-7 just to be safe
152 or al, 01000000b ; Turn bit 6 on since we want to use LBA addressing, leave device at 0 (master)
154 inc dx ; 0x01F7 - Command Port
155 mov al, 0x30 ; Write sector(s). 0x34 if LBA48
160 in al, dx ; Read status from 0x01F7
161 test al, 0x80 ; BSY flag set?
162 jne writesectors_retry
163 test al, 0x08 ; DRQ set?
164 jne writesectors_dataready
168 writesectors_nextsector:
169 in al, dx ; Read status from 0x01F7
170 test al, 0x80 ; BSY flag set?
171 jne writesectors_nextsector
172 test al, 0x21 ; ERR or DF set?
173 jne writesectors_fail
175 writesectors_dataready:
176 sub dx, 7 ; Data port (0x01F0)
177 mov rcx
, 256 ; Write 256 words (512 bytes)
178 writesectors_nextword:
179 outsw ; Cannot use rep as a small delay is needed between each out
182 jne writesectors_nextword
183 add dx, 7 ; Set DX back to Command / Status Register (0x01F7)
184 mov al, 0xE7 ; Cache Flush command
186 in al, dx ; Delay ~400ns to allow drive to set new values of BSY and DRQ
191 dec rbx
; RBX is the "sectors to write" counter
193 jne writesectors_nextsector
209 xor rcx
, rcx
; Set RCX to 0 since nothing was written
211 ; -----------------------------------------------------------------------------
214 ; =============================================================================