1 | /* $Id: ahci.c 36877 2011-04-28 19:38:13Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * AHCI host adapter driver to boot from SATA disks.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2011 Oracle Corporation
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
10 | * available from http://www.virtualbox.org. This file is free software;
|
---|
11 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
12 | * General Public License (GPL) as published by the Free Software
|
---|
13 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
14 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
15 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
16 | */
|
---|
17 |
|
---|
18 | /** Supported methods of the PCI BIOS. */
|
---|
19 | #define PCIBIOS_ID 0xb1
|
---|
20 | #define PCIBIOS_PCI_BIOS_PRESENT 0x01
|
---|
21 | #define PCIBIOS_FIND_PCI_DEVICE 0x02
|
---|
22 | #define PCIBIOS_FIND_CLASS_CODE 0x03
|
---|
23 | #define PCIBIOS_GENERATE_SPECIAL_CYCLE 0x06
|
---|
24 | #define PCIBIOS_READ_CONFIG_BYTE 0x08
|
---|
25 | #define PCIBIOS_READ_CONFIG_WORD 0x09
|
---|
26 | #define PCIBIOS_READ_CONFIG_DWORD 0x0a
|
---|
27 | #define PCIBIOS_WRITE_CONFIG_BYTE 0x0b
|
---|
28 | #define PCIBIOS_WRITE_CONFIG_WORD 0x0c
|
---|
29 | #define PCIBIOS_WRITE_CONFIG_DWORD 0x0d
|
---|
30 | #define PCIBIOS_GET_IRQ_ROUTING_OPTIONS 0x0e
|
---|
31 | #define PCIBIOS_SET_PCI_IRQ 0x0f
|
---|
32 |
|
---|
33 | /** Status codes. */
|
---|
34 | #define SUCCESSFUL 0x00
|
---|
35 | #define FUNC_NOT_SUPPORTED 0x81
|
---|
36 | #define BAD_VENDOR_ID 0x83
|
---|
37 | #define DEVICE_NOT_FOUND 0x86
|
---|
38 | #define BAD_REGISTER_NUMBER 0x87
|
---|
39 | #define SET_FAILED 0x88
|
---|
40 | #define BUFFER_TOO_SMALL 0x89
|
---|
41 |
|
---|
42 | /** PCI configuration fields. */
|
---|
43 | #define PCI_CONFIG_CAP 0x34
|
---|
44 |
|
---|
45 | #define PCI_CAP_ID_SATACR 0x12
|
---|
46 | #define VBOX_AHCI_NO_DEVICE 0xffff
|
---|
47 |
|
---|
48 | #define VBOX_AHCI_DEBUG 1 /* temporary */
|
---|
49 |
|
---|
50 | #ifdef VBOX_AHCI_DEBUG
|
---|
51 | # define VBOXAHCI_DEBUG(a...) BX_INFO(a)
|
---|
52 | #else
|
---|
53 | # define VBOXAHCI_DEBUG(a...)
|
---|
54 | #endif
|
---|
55 |
|
---|
56 | #define RT_BIT_32(bit) ((Bit32u)(1 << (bit)))
|
---|
57 |
|
---|
58 | /** Global register set. */
|
---|
59 | #define AHCI_HBA_SIZE 0x100
|
---|
60 |
|
---|
61 | #define AHCI_REG_CAP ((Bit32u)0x00)
|
---|
62 | #define AHCI_REG_GHC ((Bit32u)0x04)
|
---|
63 | # define AHCI_GHC_AE RT_BIT_32(31)
|
---|
64 | # define AHCI_GHC_IR RT_BIT_32(1)
|
---|
65 | # define AHCI_GHC_HR RT_BIT_32(0)
|
---|
66 | #define AHCI_REG_IS ((Bit32u)0x08)
|
---|
67 | #define AHCI_REG_PI ((Bit32u)0x0c)
|
---|
68 | #define AHCI_REG_VS ((Bit32u)0x10)
|
---|
69 |
|
---|
70 | /** Per port register set. */
|
---|
71 | #define AHCI_PORT_SIZE 0x80
|
---|
72 |
|
---|
73 | #define AHCI_REG_PORT_CLB 0x00
|
---|
74 | #define AHCI_REG_PORT_CLBU 0x04
|
---|
75 | #define AHCI_REG_PORT_FB 0x08
|
---|
76 | #define AHCI_REG_PORT_FBU 0x0c
|
---|
77 | #define AHCI_REG_PORT_IS 0x10
|
---|
78 | #define AHCI_REG_PORT_IE 0x14
|
---|
79 | #define AHCI_REG_PORT_CMD 0x18
|
---|
80 | # define AHCI_REG_PORT_CMD_ST RT_BIT_32(0)
|
---|
81 | # define AHCI_REG_PORT_CMD_FRE RT_BIT_32(4)
|
---|
82 | # define AHCI_REG_PORT_CMD_FR RT_BIT_32(14)
|
---|
83 | # define AHCI_REG_PORT_CMD_CR RT_BIT_32(15)
|
---|
84 | #define AHCI_REG_PORT_TFD 0x20
|
---|
85 | #define AHCI_REG_PORT_SIG 0x24
|
---|
86 | #define AHCI_REG_PORT_SSTS 0x28
|
---|
87 | #define AHCI_REG_PORT_SCTL 0x2c
|
---|
88 | #define AHCI_REG_PORT_SERR 0x30
|
---|
89 | #define AHCI_REG_PORT_SACT 0x34
|
---|
90 | #define AHCI_REG_PORT_CI 0x38
|
---|
91 |
|
---|
92 | /** Returns the absolute register offset from a given port and port register. */
|
---|
93 | #define VBOXAHCI_PORT_REG(port, reg) ((Bit32u)(AHCI_HBA_SIZE + (port) * AHCI_PORT_SIZE + (reg)))
|
---|
94 |
|
---|
95 | #define VBOXAHCI_REG_IDX 0
|
---|
96 | #define VBOXAHCI_REG_DATA 4
|
---|
97 |
|
---|
98 | /** Writes the given value to a AHCI register. */
|
---|
99 | #define VBOXAHCI_WRITE_REG(iobase, reg, val) \
|
---|
100 | outl((iobase) + VBOXAHCI_REG_IDX, (Bit32u)(reg)); \
|
---|
101 | outl((iobase) + VBOXAHCI_REG_DATA, (Bit32u)(val))
|
---|
102 |
|
---|
103 | /** Reads from a AHCI register. */
|
---|
104 | #define VBOXAHCI_READ_REG(iobase, reg, val) \
|
---|
105 | outl((iobase) + VBOXAHCI_REG_IDX, (Bit32u)(reg)); \
|
---|
106 | (val) = inl((iobase) + VBOXAHCI_REG_DATA)
|
---|
107 |
|
---|
108 | /** Writes to the given port register. */
|
---|
109 | #define VBOXAHCI_PORT_WRITE_REG(iobase, port, reg, val) \
|
---|
110 | VBOX_AHCI_WRITE_REG((iobase), VBOXAHCI_PORT_REG((port), (reg)), val)
|
---|
111 |
|
---|
112 | /** Reads from the given port register. */
|
---|
113 | #define VBOXAHCI_PORT_READ_REG(iobase, port, reg, val) \
|
---|
114 | VBOX_AHCI_READ_REG((iobase), VBOXAHCI_PORT_REG((port), (reg)), val)
|
---|
115 |
|
---|
116 | /**
|
---|
117 | * Returns the bus/device/function of a PCI device with
|
---|
118 | * the given classcode.
|
---|
119 | *
|
---|
120 | * @returns bus/device/fn in one 16bit integer where
|
---|
121 | * where the upper byte contains the bus number
|
---|
122 | * and lower one the device and function number.
|
---|
123 | * VBOX_AHCI_NO_DEVICE if no device was found.
|
---|
124 | * @param u16Class The classcode to search for.
|
---|
125 | */
|
---|
126 | Bit16u ahci_pci_find_classcode(u16Class)
|
---|
127 | Bit32u u16Class;
|
---|
128 | {
|
---|
129 | Bit16u u16BusDevFn;
|
---|
130 |
|
---|
131 | ASM_START
|
---|
132 | push bp
|
---|
133 | mov bp, sp
|
---|
134 |
|
---|
135 | mov ah, #PCIBIOS_ID
|
---|
136 | mov al, #PCIBIOS_FIND_CLASS_CODE
|
---|
137 | mov ecx, _ahci_pci_find_classcode.u16Class + 2[bp]
|
---|
138 | mov si, #0 ; First controller
|
---|
139 | int 0x1a
|
---|
140 |
|
---|
141 | ; Return from PCIBIOS
|
---|
142 | cmp ah, #SUCCESSFUL
|
---|
143 | jne ahci_pci_find_classcode_not_found
|
---|
144 |
|
---|
145 | mov _ahci_pci_find_classcode.u16BusDevFn + 2[bp], bx
|
---|
146 | jmp ahci_pci_find_classcode_done
|
---|
147 |
|
---|
148 | ahci_pci_find_classcode_not_found:
|
---|
149 | mov _ahci_pci_find_classcode.u16BusDevFn + 2[bp], #VBOX_AHCI_NO_DEVICE
|
---|
150 |
|
---|
151 | ahci_pci_find_classcode_done:
|
---|
152 | pop bp
|
---|
153 | ASM_END
|
---|
154 |
|
---|
155 | return u16BusDevFn;
|
---|
156 | }
|
---|
157 |
|
---|
158 | Bit8u ahci_pci_read_config_byte(u8Bus, u8DevFn, u8Reg)
|
---|
159 | Bit8u u8Bus, u8DevFn, u8Reg;
|
---|
160 | {
|
---|
161 | uint8_t u8Val;
|
---|
162 |
|
---|
163 | u8Val = 0;
|
---|
164 |
|
---|
165 | ASM_START
|
---|
166 | push bp
|
---|
167 | mov bp, sp
|
---|
168 |
|
---|
169 | mov ah, #PCIBIOS_ID
|
---|
170 | mov al, #PCIBIOS_READ_CONFIG_BYTE
|
---|
171 | mov bh, _ahci_pci_read_config_byte.u8Bus + 2[bp]
|
---|
172 | mov bl, _ahci_pci_read_config_byte.u8DevFn + 2[bp]
|
---|
173 | mov di, _ahci_pci_read_config_byte.u8Reg + 2[bp]
|
---|
174 | int 0x1a
|
---|
175 |
|
---|
176 | ; Return from PCIBIOS
|
---|
177 | cmp ah, #SUCCESSFUL
|
---|
178 | jne ahci_pci_read_config_byte_done
|
---|
179 |
|
---|
180 | mov _ahci_pci_read_config_byte.u8Val + 2[bp], cl
|
---|
181 |
|
---|
182 | ahci_pci_read_config_byte_done:
|
---|
183 | pop bp
|
---|
184 | ASM_END
|
---|
185 |
|
---|
186 | return u8Val;
|
---|
187 | }
|
---|
188 |
|
---|
189 | Bit16u ahci_pci_read_config_word(u8Bus, u8DevFn, u8Reg)
|
---|
190 | Bit8u u8Bus, u8DevFn, u8Reg;
|
---|
191 | {
|
---|
192 | Bit16u u16Val;
|
---|
193 |
|
---|
194 | u16Val = 0;
|
---|
195 |
|
---|
196 | ASM_START
|
---|
197 | push bp
|
---|
198 | mov bp, sp
|
---|
199 |
|
---|
200 | mov ah, #PCIBIOS_ID
|
---|
201 | mov al, #PCIBIOS_READ_CONFIG_WORD
|
---|
202 | mov bh, _ahci_pci_read_config_word.u8Bus + 2[bp]
|
---|
203 | mov bl, _ahci_pci_read_config_word.u8DevFn + 2[bp]
|
---|
204 | mov di, _ahci_pci_read_config_word.u8Reg + 2[bp]
|
---|
205 | int 0x1a
|
---|
206 |
|
---|
207 | ; Return from PCIBIOS
|
---|
208 | cmp ah, #SUCCESSFUL
|
---|
209 | jne ahci_pci_read_config_word_done
|
---|
210 |
|
---|
211 | mov _ahci_pci_read_config_word.u16Val + 2[bp], cx
|
---|
212 |
|
---|
213 | ahci_pci_read_config_word_done:
|
---|
214 | pop bp
|
---|
215 | ASM_END
|
---|
216 |
|
---|
217 | return u16Val;
|
---|
218 | }
|
---|
219 |
|
---|
220 | Bit32u ahci_pci_read_config_dword(u8Bus, u8DevFn, u8Reg)
|
---|
221 | Bit8u u8Bus, u8DevFn, u8Reg;
|
---|
222 | {
|
---|
223 | Bit32u u32Val;
|
---|
224 |
|
---|
225 | u32Val = 0;
|
---|
226 |
|
---|
227 | ASM_START
|
---|
228 | push bp
|
---|
229 | mov bp, sp
|
---|
230 |
|
---|
231 | mov ah, #PCIBIOS_ID
|
---|
232 | mov al, #PCIBIOS_READ_CONFIG_DWORD
|
---|
233 | mov bh, _ahci_pci_read_config_dword.u8Bus + 2[bp]
|
---|
234 | mov bl, _ahci_pci_read_config_dword.u8DevFn + 2[bp]
|
---|
235 | mov di, _ahci_pci_read_config_dword.u8Reg + 2[bp]
|
---|
236 | int 0x1a
|
---|
237 |
|
---|
238 | ; Return from PCIBIOS
|
---|
239 | cmp ah, #SUCCESSFUL
|
---|
240 | jne ahci_pci_read_config_dword_done
|
---|
241 |
|
---|
242 | mov _ahci_pci_read_config_dword.u32Val + 2[bp], ecx
|
---|
243 |
|
---|
244 | ahci_pci_read_config_dword_done:
|
---|
245 | pop bp
|
---|
246 | ASM_END
|
---|
247 |
|
---|
248 | return u32Val;
|
---|
249 | }
|
---|
250 |
|
---|
251 | #if 0 /* Disabled to save space because they are not needed. Might become useful in the future. */
|
---|
252 | /**
|
---|
253 | * Returns the bus/device/function of a PCI device with
|
---|
254 | * the given vendor and device id.
|
---|
255 | *
|
---|
256 | * @returns bus/device/fn in one 16bit integer where
|
---|
257 | * where the upper byte contains the bus number
|
---|
258 | * and lower one the device and function number.
|
---|
259 | * VBOX_AHCI_NO_DEVICE if no device was found.
|
---|
260 | * @param u16Vendor The vendor ID.
|
---|
261 | * @param u16Device The device ID.
|
---|
262 | */
|
---|
263 | Bit16u ahci_pci_find_device(u16Vendor, u16Device)
|
---|
264 | Bit16u u16Vendor;
|
---|
265 | Bit16u u16Device;
|
---|
266 | {
|
---|
267 | Bit16u u16BusDevFn;
|
---|
268 |
|
---|
269 | ASM_START
|
---|
270 | push bp
|
---|
271 | mov bp, sp
|
---|
272 |
|
---|
273 | mov ah, #PCIBIOS_ID
|
---|
274 | mov al, #PCIBIOS_FIND_PCI_DEVICE
|
---|
275 | mov cx, _ahci_pci_find_device.u16Device + 2[bp]
|
---|
276 | mov dx, _ahci_pci_find_device.u16Vendor + 2[bp]
|
---|
277 | mov si, #0 ; First controller
|
---|
278 | int 0x1a
|
---|
279 |
|
---|
280 | ; Return from PCIBIOS
|
---|
281 | cmp ah, #SUCCESSFUL
|
---|
282 | jne ahci_pci_find_device_not_found
|
---|
283 |
|
---|
284 | mov _ahci_pci_find_device.u16BusDevFn + 2[bp], bx
|
---|
285 | jmp ahci_pci_find_device_done
|
---|
286 |
|
---|
287 | ahci_pci_find_device_not_found:
|
---|
288 | mov _ahci_pci_find_device.u16BusDevFn + 2[bp], #VBOX_AHCI_NO_DEVICE
|
---|
289 |
|
---|
290 | ahci_pci_find_device_done:
|
---|
291 | pop bp
|
---|
292 | ASM_END
|
---|
293 |
|
---|
294 | return u16BusDevFn;
|
---|
295 | }
|
---|
296 |
|
---|
297 | void ahci_pci_write_config_byte(u8Bus, u8DevFn, u8Reg, u8Val)
|
---|
298 | Bit8u u8Bus, u8DevFn, u8Reg, u8Val;
|
---|
299 | {
|
---|
300 | ASM_START
|
---|
301 | push bp
|
---|
302 | mov bp, sp
|
---|
303 |
|
---|
304 | mov ah, #PCIBIOS_ID
|
---|
305 | mov al, #PCIBIOS_WRITE_CONFIG_BYTE
|
---|
306 | mov bh, _ahci_pci_write_config_byte.u8Bus + 2[bp]
|
---|
307 | mov bl, _ahci_pci_write_config_byte.u8DevFn + 2[bp]
|
---|
308 | mov di, _ahci_pci_write_config_byte.u8Reg + 2[bp]
|
---|
309 | mov cl, _ahci_pci_write_config_byte.u8Val + 2[bp]
|
---|
310 | int 0x1a
|
---|
311 |
|
---|
312 | ; Return from PCIBIOS
|
---|
313 | pop bp
|
---|
314 | ASM_END
|
---|
315 | }
|
---|
316 |
|
---|
317 | void ahci_pci_write_config_word(u8Bus, u8DevFn, u8Reg, u16Val)
|
---|
318 | Bit8u u8Bus, u8DevFn, u8Reg;
|
---|
319 | Bit16u u16Val;
|
---|
320 | {
|
---|
321 | ASM_START
|
---|
322 | push bp
|
---|
323 | mov bp, sp
|
---|
324 |
|
---|
325 | mov ah, #PCIBIOS_ID
|
---|
326 | mov al, #PCIBIOS_WRITE_CONFIG_WORD
|
---|
327 | mov bh, _ahci_pci_write_config_word.u8Bus + 2[bp]
|
---|
328 | mov bl, _ahci_pci_write_config_word.u8DevFn + 2[bp]
|
---|
329 | mov di, _ahci_pci_write_config_word.u8Reg + 2[bp]
|
---|
330 | mov cx, _ahci_pci_write_config_word.u16Val + 2[bp]
|
---|
331 | int 0x1a
|
---|
332 |
|
---|
333 | ; Return from PCIBIOS
|
---|
334 | pop bp
|
---|
335 | ASM_END
|
---|
336 | }
|
---|
337 |
|
---|
338 | void ahci_pci_write_config_dword(u8Bus, u8DevFn, u8Reg, u32Val)
|
---|
339 | Bit8u u8Bus, u8DevFn, u8Reg;
|
---|
340 | Bit32u u32Val;
|
---|
341 | {
|
---|
342 | ASM_START
|
---|
343 | push bp
|
---|
344 | mov bp, sp
|
---|
345 |
|
---|
346 | mov ah, #PCIBIOS_ID
|
---|
347 | mov al, #PCIBIOS_WRITE_CONFIG_WORD
|
---|
348 | mov bh, _ahci_pci_write_config_dword.u8Bus + 2[bp]
|
---|
349 | mov bl, _ahci_pci_write_config_dword.u8DevFn + 2[bp]
|
---|
350 | mov di, _ahci_pci_write_config_dword.u8Reg + 2[bp]
|
---|
351 | mov cx, _ahci_pci_write_config_dword.u32Val + 2[bp]
|
---|
352 | int 0x1a
|
---|
353 |
|
---|
354 | ; Return from PCIBIOS
|
---|
355 | pop bp
|
---|
356 | ASM_END
|
---|
357 | }
|
---|
358 | #endif /* 0 */
|
---|
359 |
|
---|
360 | /**
|
---|
361 | * Sets a given set of bits in a register.
|
---|
362 | */
|
---|
363 | static void ahci_ctrl_set_bits(u16IoBase, u32Reg, u32Set)
|
---|
364 | Bit16u u16IoBase;
|
---|
365 | Bit32u u32Reg, u32Set;
|
---|
366 | {
|
---|
367 | ASM_START
|
---|
368 | push bp
|
---|
369 | mov bp, sp
|
---|
370 |
|
---|
371 | push eax
|
---|
372 | push edx
|
---|
373 |
|
---|
374 | ; Read from the register
|
---|
375 | mov eax, _ahci_ctrl_set_bits.u32Reg + 2[bp]
|
---|
376 | mov dx, _ahci_ctrl_set_bits.u16IoBase + 2[bp]
|
---|
377 | add dx, #VBOXAHCI_REG_IDX
|
---|
378 | out dx, eax
|
---|
379 |
|
---|
380 | mov dx, _ahci_ctrl_set_bits.u16IoBase + 2[bp]
|
---|
381 | add dx, #VBOXAHCI_REG_DATA
|
---|
382 | in eax, dx
|
---|
383 |
|
---|
384 | ; Set the new bits and write the result to the register again
|
---|
385 | or eax, _ahci_ctrl_set_bits.u32Set + 2[bp]
|
---|
386 | out dx, eax
|
---|
387 |
|
---|
388 | pop edx
|
---|
389 | pop eax
|
---|
390 |
|
---|
391 | pop bp
|
---|
392 | ASM_END
|
---|
393 | }
|
---|
394 |
|
---|
395 | /**
|
---|
396 | * Clears a given set of bits in a register.
|
---|
397 | */
|
---|
398 | static void ahci_ctrl_clear_bits(u16IoBase, u32Reg, u32Clear)
|
---|
399 | Bit16u u16IoBase;
|
---|
400 | Bit32u u32Reg, u32Clear;
|
---|
401 | {
|
---|
402 | ASM_START
|
---|
403 | push bp
|
---|
404 | mov bp, sp
|
---|
405 |
|
---|
406 | push eax
|
---|
407 | push ecx
|
---|
408 | push edx
|
---|
409 |
|
---|
410 | ; Read from the register
|
---|
411 | mov eax, _ahci_ctrl_clear_bits.u32Reg + 2[bp]
|
---|
412 | mov dx, _ahci_ctrl_clear_bits.u16IoBase + 2[bp]
|
---|
413 | add dx, #VBOXAHCI_REG_IDX
|
---|
414 | out dx, eax
|
---|
415 |
|
---|
416 | mov dx, _ahci_ctrl_clear_bits.u16IoBase + 2[bp]
|
---|
417 | add dx, #VBOXAHCI_REG_DATA
|
---|
418 | in eax, dx
|
---|
419 |
|
---|
420 | ; Clear the bits and write the result to the register again
|
---|
421 | mov ecx, _ahci_ctrl_clear_bits.u32Clear + 2[bp]
|
---|
422 | not ecx
|
---|
423 | and eax, ecx
|
---|
424 | out dx, eax
|
---|
425 |
|
---|
426 | pop edx
|
---|
427 | pop ecx
|
---|
428 | pop eax
|
---|
429 |
|
---|
430 | pop bp
|
---|
431 | ASM_END
|
---|
432 | }
|
---|
433 |
|
---|
434 | /**
|
---|
435 | * Returns whether at least one of the bits in the given mask is set
|
---|
436 | * for a register.
|
---|
437 | */
|
---|
438 | static Bit8u ahci_ctrl_is_bit_set(u16IoBase, u32Reg, u32Mask)
|
---|
439 | Bit16u u16IoBase;
|
---|
440 | Bit32u u32Reg, u32Mask;
|
---|
441 | {
|
---|
442 | ASM_START
|
---|
443 | push bp
|
---|
444 | mov bp, sp
|
---|
445 |
|
---|
446 | push eax
|
---|
447 | push edx
|
---|
448 |
|
---|
449 | ; Read from the register
|
---|
450 | mov eax, _ahci_ctrl_is_bit_set.u32Reg + 2[bp]
|
---|
451 | mov dx, _ahci_ctrl_is_bit_set.u16IoBase + 2[bp]
|
---|
452 | add dx, #VBOXAHCI_REG_IDX
|
---|
453 | out dx, eax
|
---|
454 |
|
---|
455 | mov dx, _ahci_ctrl_is_bit_set.u16IoBase + 2[bp]
|
---|
456 | add dx, #VBOXAHCI_REG_DATA
|
---|
457 | in eax, dx
|
---|
458 |
|
---|
459 | ; Check for set bits
|
---|
460 | test eax, _ahci_ctrl_is_bit_set.u32Mask + 2[bp]
|
---|
461 | je ahci_ctrl_is_bit_set_not_set
|
---|
462 | mov al, #1 ; At least one of the bits is set
|
---|
463 | jmp ahci_ctrl_is_bit_set_done
|
---|
464 |
|
---|
465 | ahci_ctrl_is_bit_set_not_set:
|
---|
466 | mov al, #0 ; No bit is set
|
---|
467 |
|
---|
468 | ahci_ctrl_is_bit_set_done:
|
---|
469 | pop edx
|
---|
470 | pop eax
|
---|
471 |
|
---|
472 | pop bp
|
---|
473 | ASM_END
|
---|
474 | }
|
---|
475 |
|
---|
476 | /**
|
---|
477 | * Extracts a range of bits from a register and shifts them
|
---|
478 | * to the right.
|
---|
479 | */
|
---|
480 | static Bit16u ahci_ctrl_extract_bits(u32Reg, u32Mask, u8Shift)
|
---|
481 | Bit32u u32Reg, u32Mask, u8Shift;
|
---|
482 | {
|
---|
483 | ASM_START
|
---|
484 | push bp
|
---|
485 | mov bp, sp
|
---|
486 |
|
---|
487 | push cx
|
---|
488 |
|
---|
489 | mov eax, _ahci_ctrl_extract_bits.u32Reg + 2[bp]
|
---|
490 | mov cl, _ahci_ctrl_extract_bits.u8Shift + 2[bp]
|
---|
491 | and eax, _ahci_ctrl_extract_bits.u32Mask + 2[bp]
|
---|
492 | shr eax, cl
|
---|
493 |
|
---|
494 | pop cx
|
---|
495 |
|
---|
496 | pop bp
|
---|
497 | ASM_END
|
---|
498 | }
|
---|
499 |
|
---|
500 | static int ahci_port_init(u16IoBase, u8Port)
|
---|
501 | Bit16u u16IoBase;
|
---|
502 | Bit8u u8Port;
|
---|
503 | {
|
---|
504 |
|
---|
505 | /* Put the port into an idle state. */
|
---|
506 | ahci_ctrl_clear_bits(u16IoBase, VBOXAHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
|
---|
507 | AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_ST);
|
---|
508 |
|
---|
509 | while (ahci_ctrl_is_bit_set(u16IoBase, VBOXAHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
|
---|
510 | AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_ST | AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_CR) == 1)
|
---|
511 | {
|
---|
512 | VBOXAHCI_DEBUG("AHCI: Waiting for the port to idle\n");
|
---|
513 | }
|
---|
514 |
|
---|
515 | /*
|
---|
516 | * Port idles, set up memory for commands and received FIS and program the
|
---|
517 | * address registers.
|
---|
518 | */
|
---|
519 |
|
---|
520 | return -1;
|
---|
521 | }
|
---|
522 |
|
---|
523 | static int ahci_ctrl_init(u16IoBase)
|
---|
524 | Bit16u u16IoBase;
|
---|
525 | {
|
---|
526 | int rc = 0;
|
---|
527 | Bit8u i, cPorts;
|
---|
528 | Bit32u val;
|
---|
529 |
|
---|
530 | VBOXAHCI_READ_REG(u16IoBase, AHCI_REG_VS, val);
|
---|
531 | VBOXAHCI_DEBUG("AHCI: Controller has version: 0x%x (major) 0x%x (minor)\n",
|
---|
532 | ahci_ctrl_extract_bits(val, 0xffff0000, 16),
|
---|
533 | ahci_ctrl_extract_bits(val, 0x0000ffff, 0));
|
---|
534 |
|
---|
535 | /* Reset the controller. */
|
---|
536 | ahci_ctrl_set_bits(u16IoBase, AHCI_REG_GHC, AHCI_GHC_HR);
|
---|
537 | do
|
---|
538 | {
|
---|
539 | VBOXAHCI_READ_REG(u16IoBase, AHCI_REG_GHC, val);
|
---|
540 | } while (val & AHCI_GHC_HR != 0);
|
---|
541 |
|
---|
542 | VBOXAHCI_READ_REG(u16IoBase, AHCI_REG_CAP, val);
|
---|
543 | cPorts = ahci_ctrl_extract_bits(val, 0x1f, 0) + 1; /* Extract number of ports.*/
|
---|
544 |
|
---|
545 | VBOXAHCI_DEBUG("AHCI: Controller has %u ports\n", cPorts);
|
---|
546 |
|
---|
547 | /* Go through the ports. */
|
---|
548 | i = 0;
|
---|
549 | while (i < 32)
|
---|
550 | {
|
---|
551 | if (ahci_ctrl_is_bit_set(u16IoBase, AHCI_REG_PI, RT_BIT_32(i)) != 0)
|
---|
552 | {
|
---|
553 | VBOXAHCI_DEBUG("AHCI: Port %u is present\n", i);
|
---|
554 | rc = ahci_port_init(u16IoBase, i);
|
---|
555 | cPorts--;
|
---|
556 | if (cPorts == 0)
|
---|
557 | break;
|
---|
558 | }
|
---|
559 | i++;
|
---|
560 | }
|
---|
561 |
|
---|
562 | return rc;
|
---|
563 | }
|
---|
564 |
|
---|
565 | /**
|
---|
566 | * Init the AHCI driver and detect attached disks.
|
---|
567 | */
|
---|
568 | void ahci_init( )
|
---|
569 | {
|
---|
570 | Bit16u ebda_seg;
|
---|
571 | Bit16u busdevfn;
|
---|
572 |
|
---|
573 | ebda_seg = read_word(0x0040, 0x000E);
|
---|
574 |
|
---|
575 | busdevfn = ahci_pci_find_classcode(0x00010601);
|
---|
576 | if (busdevfn != VBOX_AHCI_NO_DEVICE)
|
---|
577 | {
|
---|
578 | Bit8u u8Bus, u8DevFn;
|
---|
579 | Bit8u u8PciCapOff;
|
---|
580 |
|
---|
581 | u8Bus = (busdevfn & 0xff00) >> 8;
|
---|
582 | u8DevFn = busdevfn & 0x00ff;
|
---|
583 |
|
---|
584 | VBOXAHCI_DEBUG("Found AHCI controller at Bus %u DevFn 0x%x (raw 0x%x)\n", u8Bus, u8DevFn, busdevfn);
|
---|
585 |
|
---|
586 | /* Examine the capability list and search for the Serial ATA Capability Register. */
|
---|
587 | u8PciCapOff = ahci_pci_read_config_byte(u8Bus, u8DevFn, PCI_CONFIG_CAP);
|
---|
588 |
|
---|
589 | while (u8PciCapOff != 0)
|
---|
590 | {
|
---|
591 | Bit8u u8PciCapId = ahci_pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff);
|
---|
592 |
|
---|
593 | VBOXAHCI_DEBUG("Capability ID 0x%x at offset 0x%x found\n", u8PciCapId, u8PciCapOff);
|
---|
594 |
|
---|
595 | if (u8PciCapId == PCI_CAP_ID_SATACR)
|
---|
596 | break;
|
---|
597 |
|
---|
598 | /* Go on to the next capability. */
|
---|
599 | u8PciCapOff = ahci_pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff + 1);
|
---|
600 | }
|
---|
601 |
|
---|
602 | if (u8PciCapOff != 0)
|
---|
603 | {
|
---|
604 | Bit8u u8Rev;
|
---|
605 |
|
---|
606 | VBOXAHCI_DEBUG("AHCI controller with SATA Capability register at offset 0x%x found\n", u8PciCapOff);
|
---|
607 |
|
---|
608 | /* Advance to the stuff behind the id and next capability pointer. */
|
---|
609 | u8PciCapOff += 2;
|
---|
610 |
|
---|
611 | u8Rev = ahci_pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff);
|
---|
612 | if (u8Rev == 0x10)
|
---|
613 | {
|
---|
614 | /* Read the SATACR1 register and get the bar and offset of the index/data pair register. */
|
---|
615 | Bit8u u8Bar = 0x00;
|
---|
616 | Bit16u u16Off = 0x00;
|
---|
617 | Bit16u u16BarOff = ahci_pci_read_config_word(u8Bus, u8DevFn, u8PciCapOff + 2);
|
---|
618 |
|
---|
619 | VBOXAHCI_DEBUG("SATACR1 register contains 0x%x\n", u16BarOff);
|
---|
620 |
|
---|
621 | switch (u16BarOff & 0xf)
|
---|
622 | {
|
---|
623 | case 0x04:
|
---|
624 | u8Bar = 0x10;
|
---|
625 | break;
|
---|
626 | case 0x05:
|
---|
627 | u8Bar = 0x14;
|
---|
628 | break;
|
---|
629 | case 0x06:
|
---|
630 | u8Bar = 0x18;
|
---|
631 | break;
|
---|
632 | case 0x07:
|
---|
633 | u8Bar = 0x1c;
|
---|
634 | break;
|
---|
635 | case 0x08:
|
---|
636 | u8Bar = 0x20;
|
---|
637 | break;
|
---|
638 | case 0x09:
|
---|
639 | u8Bar = 0x24;
|
---|
640 | break;
|
---|
641 | case 0x0f:
|
---|
642 | default:
|
---|
643 | /* Reserved or unsupported. */
|
---|
644 | VBOXAHCI_DEBUG("BAR location 0x%x is unsupported\n", u16BarOff & 0xf);
|
---|
645 | }
|
---|
646 |
|
---|
647 | /* Get the offset inside the BAR from bits 4:15. */
|
---|
648 | u16Off = (u16BarOff >> 4) * 4;
|
---|
649 |
|
---|
650 | if (u8Bar != 0x00)
|
---|
651 | {
|
---|
652 | Bit32u u32Bar = ahci_pci_read_config_dword(u8Bus, u8DevFn, u8Bar);
|
---|
653 |
|
---|
654 | VBOXAHCI_DEBUG("BAR at offset 0x%x contains 0x%x\n", u8Bar, u32Bar);
|
---|
655 |
|
---|
656 | if ((u32Bar & 0x01) != 0)
|
---|
657 | {
|
---|
658 | int rc;
|
---|
659 | Bit16u u16AhciIoBase = (u32Bar & 0xfff0) + u16Off;
|
---|
660 |
|
---|
661 | VBOXAHCI_DEBUG("I/O base is 0x%x\n", u16AhciIoBase);
|
---|
662 | rc = ahci_ctrl_init(u16AhciIoBase);
|
---|
663 | }
|
---|
664 | else
|
---|
665 | VBOXAHCI_DEBUG("BAR is MMIO\n");
|
---|
666 | }
|
---|
667 | }
|
---|
668 | else
|
---|
669 | VBOXAHCI_DEBUG("Invalid revision 0x%x\n", u8Rev);
|
---|
670 | }
|
---|
671 | else
|
---|
672 | VBOXAHCI_DEBUG("AHCI controller without usable Index/Data register pair found\n");
|
---|
673 | }
|
---|
674 | else
|
---|
675 | VBOXAHCI_DEBUG("No AHCI controller found\n");
|
---|
676 | }
|
---|
677 |
|
---|