1 | /* $Id: ahci.c 37268 2011-05-30 21:14:14Z 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 | #define AHCI_MAX_STORAGE_DEVICES 4
|
---|
19 |
|
---|
20 | typedef struct
|
---|
21 | {
|
---|
22 | Bit8u type; // Detected type of ata (ata/atapi/none/unknown/scsi)
|
---|
23 | Bit8u device; // Detected type of attached devices (hd/cd/none)
|
---|
24 | #if 0
|
---|
25 | Bit8u removable; // Removable device flag
|
---|
26 | Bit8u lock; // Locks for removable devices
|
---|
27 | #endif
|
---|
28 | Bit16u blksize; // block size
|
---|
29 |
|
---|
30 | Bit8u translation; // type of translation
|
---|
31 | chs_t lchs; // Logical CHS
|
---|
32 | chs_t pchs; // Physical CHS
|
---|
33 |
|
---|
34 | Bit32u sectors; // Total sectors count
|
---|
35 | Bit8u port; // Port this device is on.
|
---|
36 | } ahci_device_t;
|
---|
37 |
|
---|
38 | /**
|
---|
39 | * AHCI controller data.
|
---|
40 | */
|
---|
41 | typedef struct
|
---|
42 | {
|
---|
43 | /** The AHCI command list as defined by chapter 4.2.2 of the Intel AHCI spec.
|
---|
44 | * Because the BIOS doesn't support NCQ only the first command header is defined
|
---|
45 | * to save memory. - Must be aligned on a 1K boundary.
|
---|
46 | */
|
---|
47 | Bit32u abCmdHdr[0x8];
|
---|
48 | /** Align the next structure on a 128 byte boundary. */
|
---|
49 | Bit8u abAlignment1[0x60];
|
---|
50 | /** The command table of one request as defined by chapter 4.2.3 of the Intel AHCI spec.
|
---|
51 | * Must be aligned on 128 byte boundary.
|
---|
52 | */
|
---|
53 | Bit8u abCmd[0x90];
|
---|
54 | /** Alignment */
|
---|
55 | Bit8u abAlignment2[0xF0];
|
---|
56 | /** Memory for the received command FIS area as specified by chapter 4.2.1
|
---|
57 | * of the Intel AHCI spec. This area is normally 256 bytes big but to save memory
|
---|
58 | * only the first 96 bytes are used because it is assumed that the controller
|
---|
59 | * never writes to the UFIS or reserved area. - Must be aligned on a 256byte boundary.
|
---|
60 | */
|
---|
61 | Bit8u abFisRecv[0x60];
|
---|
62 | /** Base I/O port for the index/data register pair. */
|
---|
63 | Bit16u iobase;
|
---|
64 | /** Current port which uses the memory to communicate with the controller. */
|
---|
65 | Bit8u port;
|
---|
66 | /** AHCI device information. */
|
---|
67 | ahci_device_t aDevices[AHCI_MAX_STORAGE_DEVICES];
|
---|
68 | /** Map between (bios hd id - 0x80) and ahci devices. */
|
---|
69 | Bit8u cHardDisks;
|
---|
70 | Bit8u aHdIdMap[AHCI_MAX_STORAGE_DEVICES];
|
---|
71 | /** Map between (bios cd id - 0xE0) and ahci_devices. */
|
---|
72 | Bit8u cCdDrives;
|
---|
73 | Bit8u aCdIdMap[AHCI_MAX_STORAGE_DEVICES];
|
---|
74 | } ahci_t;
|
---|
75 |
|
---|
76 | #define AhciData ((ahci_t *) 0)
|
---|
77 |
|
---|
78 | /** Supported methods of the PCI BIOS. */
|
---|
79 | #define PCIBIOS_ID 0xb1
|
---|
80 | #define PCIBIOS_PCI_BIOS_PRESENT 0x01
|
---|
81 | #define PCIBIOS_FIND_PCI_DEVICE 0x02
|
---|
82 | #define PCIBIOS_FIND_CLASS_CODE 0x03
|
---|
83 | #define PCIBIOS_GENERATE_SPECIAL_CYCLE 0x06
|
---|
84 | #define PCIBIOS_READ_CONFIG_BYTE 0x08
|
---|
85 | #define PCIBIOS_READ_CONFIG_WORD 0x09
|
---|
86 | #define PCIBIOS_READ_CONFIG_DWORD 0x0a
|
---|
87 | #define PCIBIOS_WRITE_CONFIG_BYTE 0x0b
|
---|
88 | #define PCIBIOS_WRITE_CONFIG_WORD 0x0c
|
---|
89 | #define PCIBIOS_WRITE_CONFIG_DWORD 0x0d
|
---|
90 | #define PCIBIOS_GET_IRQ_ROUTING_OPTIONS 0x0e
|
---|
91 | #define PCIBIOS_SET_PCI_IRQ 0x0f
|
---|
92 |
|
---|
93 | /** Status codes. */
|
---|
94 | #define SUCCESSFUL 0x00
|
---|
95 | #define FUNC_NOT_SUPPORTED 0x81
|
---|
96 | #define BAD_VENDOR_ID 0x83
|
---|
97 | #define DEVICE_NOT_FOUND 0x86
|
---|
98 | #define BAD_REGISTER_NUMBER 0x87
|
---|
99 | #define SET_FAILED 0x88
|
---|
100 | #define BUFFER_TOO_SMALL 0x89
|
---|
101 |
|
---|
102 | /** PCI configuration fields. */
|
---|
103 | #define PCI_CONFIG_CAP 0x34
|
---|
104 |
|
---|
105 | #define PCI_CAP_ID_SATACR 0x12
|
---|
106 | #define VBOX_AHCI_NO_DEVICE 0xffff
|
---|
107 |
|
---|
108 | #define VBOX_AHCI_DEBUG 1 /* temporary */
|
---|
109 |
|
---|
110 | #ifdef VBOX_AHCI_DEBUG
|
---|
111 | # define VBOXAHCI_DEBUG(a...) BX_INFO(a)
|
---|
112 | #else
|
---|
113 | # define VBOXAHCI_DEBUG(a...)
|
---|
114 | #endif
|
---|
115 |
|
---|
116 | #define RT_BIT_32(bit) ((Bit32u)(1 << (bit)))
|
---|
117 |
|
---|
118 | /** Global register set. */
|
---|
119 | #define AHCI_HBA_SIZE 0x100
|
---|
120 |
|
---|
121 | #define AHCI_REG_CAP ((Bit32u)0x00)
|
---|
122 | #define AHCI_REG_GHC ((Bit32u)0x04)
|
---|
123 | # define AHCI_GHC_AE RT_BIT_32(31)
|
---|
124 | # define AHCI_GHC_IR RT_BIT_32(1)
|
---|
125 | # define AHCI_GHC_HR RT_BIT_32(0)
|
---|
126 | #define AHCI_REG_IS ((Bit32u)0x08)
|
---|
127 | #define AHCI_REG_PI ((Bit32u)0x0c)
|
---|
128 | #define AHCI_REG_VS ((Bit32u)0x10)
|
---|
129 |
|
---|
130 | /** Per port register set. */
|
---|
131 | #define AHCI_PORT_SIZE 0x80
|
---|
132 |
|
---|
133 | #define AHCI_REG_PORT_CLB 0x00
|
---|
134 | #define AHCI_REG_PORT_CLBU 0x04
|
---|
135 | #define AHCI_REG_PORT_FB 0x08
|
---|
136 | #define AHCI_REG_PORT_FBU 0x0c
|
---|
137 | #define AHCI_REG_PORT_IS 0x10
|
---|
138 | # define AHCI_REG_PORT_IS_DHRS RT_BIT_32(0)
|
---|
139 | #define AHCI_REG_PORT_IE 0x14
|
---|
140 | #define AHCI_REG_PORT_CMD 0x18
|
---|
141 | # define AHCI_REG_PORT_CMD_ST RT_BIT_32(0)
|
---|
142 | # define AHCI_REG_PORT_CMD_FRE RT_BIT_32(4)
|
---|
143 | # define AHCI_REG_PORT_CMD_FR RT_BIT_32(14)
|
---|
144 | # define AHCI_REG_PORT_CMD_CR RT_BIT_32(15)
|
---|
145 | #define AHCI_REG_PORT_TFD 0x20
|
---|
146 | #define AHCI_REG_PORT_SIG 0x24
|
---|
147 | #define AHCI_REG_PORT_SSTS 0x28
|
---|
148 | #define AHCI_REG_PORT_SCTL 0x2c
|
---|
149 | #define AHCI_REG_PORT_SERR 0x30
|
---|
150 | #define AHCI_REG_PORT_SACT 0x34
|
---|
151 | #define AHCI_REG_PORT_CI 0x38
|
---|
152 |
|
---|
153 | /** Returns the absolute register offset from a given port and port register. */
|
---|
154 | #define VBOXAHCI_PORT_REG(port, reg) ((Bit32u)(AHCI_HBA_SIZE + (port) * AHCI_PORT_SIZE + (reg)))
|
---|
155 |
|
---|
156 | #define VBOXAHCI_REG_IDX 0
|
---|
157 | #define VBOXAHCI_REG_DATA 4
|
---|
158 |
|
---|
159 | /** Writes the given value to a AHCI register. */
|
---|
160 | #define VBOXAHCI_WRITE_REG(iobase, reg, val) \
|
---|
161 | outl((iobase) + VBOXAHCI_REG_IDX, (Bit32u)(reg)); \
|
---|
162 | outl((iobase) + VBOXAHCI_REG_DATA, (Bit32u)(val))
|
---|
163 |
|
---|
164 | /** Reads from a AHCI register. */
|
---|
165 | #define VBOXAHCI_READ_REG(iobase, reg, val) \
|
---|
166 | outl((iobase) + VBOXAHCI_REG_IDX, (Bit32u)(reg)); \
|
---|
167 | (val) = inl((iobase) + VBOXAHCI_REG_DATA)
|
---|
168 |
|
---|
169 | /** Writes to the given port register. */
|
---|
170 | #define VBOXAHCI_PORT_WRITE_REG(iobase, port, reg, val) \
|
---|
171 | VBOXAHCI_WRITE_REG((iobase), VBOXAHCI_PORT_REG((port), (reg)), val)
|
---|
172 |
|
---|
173 | /** Reads from the given port register. */
|
---|
174 | #define VBOXAHCI_PORT_READ_REG(iobase, port, reg, val) \
|
---|
175 | VBOXAHCI_READ_REG((iobase), VBOXAHCI_PORT_REG((port), (reg)), val)
|
---|
176 |
|
---|
177 | #define ATA_CMD_IDENTIFY_DEVICE 0xEC
|
---|
178 |
|
---|
179 | /**
|
---|
180 | * Returns the bus/device/function of a PCI device with
|
---|
181 | * the given classcode.
|
---|
182 | *
|
---|
183 | * @returns bus/device/fn in one 16bit integer where
|
---|
184 | * where the upper byte contains the bus number
|
---|
185 | * and lower one the device and function number.
|
---|
186 | * VBOX_AHCI_NO_DEVICE if no device was found.
|
---|
187 | * @param u16Class The classcode to search for.
|
---|
188 | */
|
---|
189 | Bit16u ahci_pci_find_classcode(u16Class)
|
---|
190 | Bit32u u16Class;
|
---|
191 | {
|
---|
192 | Bit16u u16BusDevFn;
|
---|
193 |
|
---|
194 | ASM_START
|
---|
195 | push bp
|
---|
196 | mov bp, sp
|
---|
197 |
|
---|
198 | mov ah, #PCIBIOS_ID
|
---|
199 | mov al, #PCIBIOS_FIND_CLASS_CODE
|
---|
200 | mov ecx, _ahci_pci_find_classcode.u16Class + 2[bp]
|
---|
201 | mov si, #0 ; First controller
|
---|
202 | int 0x1a
|
---|
203 |
|
---|
204 | ; Return from PCIBIOS
|
---|
205 | cmp ah, #SUCCESSFUL
|
---|
206 | jne ahci_pci_find_classcode_not_found
|
---|
207 |
|
---|
208 | mov _ahci_pci_find_classcode.u16BusDevFn + 2[bp], bx
|
---|
209 | jmp ahci_pci_find_classcode_done
|
---|
210 |
|
---|
211 | ahci_pci_find_classcode_not_found:
|
---|
212 | mov _ahci_pci_find_classcode.u16BusDevFn + 2[bp], #VBOX_AHCI_NO_DEVICE
|
---|
213 |
|
---|
214 | ahci_pci_find_classcode_done:
|
---|
215 | pop bp
|
---|
216 | ASM_END
|
---|
217 |
|
---|
218 | return u16BusDevFn;
|
---|
219 | }
|
---|
220 |
|
---|
221 | Bit8u ahci_pci_read_config_byte(u8Bus, u8DevFn, u8Reg)
|
---|
222 | Bit8u u8Bus, u8DevFn, u8Reg;
|
---|
223 | {
|
---|
224 | uint8_t u8Val;
|
---|
225 |
|
---|
226 | u8Val = 0;
|
---|
227 |
|
---|
228 | ASM_START
|
---|
229 | push bp
|
---|
230 | mov bp, sp
|
---|
231 |
|
---|
232 | mov ah, #PCIBIOS_ID
|
---|
233 | mov al, #PCIBIOS_READ_CONFIG_BYTE
|
---|
234 | mov bh, _ahci_pci_read_config_byte.u8Bus + 2[bp]
|
---|
235 | mov bl, _ahci_pci_read_config_byte.u8DevFn + 2[bp]
|
---|
236 | mov di, _ahci_pci_read_config_byte.u8Reg + 2[bp]
|
---|
237 | int 0x1a
|
---|
238 |
|
---|
239 | ; Return from PCIBIOS
|
---|
240 | cmp ah, #SUCCESSFUL
|
---|
241 | jne ahci_pci_read_config_byte_done
|
---|
242 |
|
---|
243 | mov _ahci_pci_read_config_byte.u8Val + 2[bp], cl
|
---|
244 |
|
---|
245 | ahci_pci_read_config_byte_done:
|
---|
246 | pop bp
|
---|
247 | ASM_END
|
---|
248 |
|
---|
249 | return u8Val;
|
---|
250 | }
|
---|
251 |
|
---|
252 | Bit16u ahci_pci_read_config_word(u8Bus, u8DevFn, u8Reg)
|
---|
253 | Bit8u u8Bus, u8DevFn, u8Reg;
|
---|
254 | {
|
---|
255 | Bit16u u16Val;
|
---|
256 |
|
---|
257 | u16Val = 0;
|
---|
258 |
|
---|
259 | ASM_START
|
---|
260 | push bp
|
---|
261 | mov bp, sp
|
---|
262 |
|
---|
263 | mov ah, #PCIBIOS_ID
|
---|
264 | mov al, #PCIBIOS_READ_CONFIG_WORD
|
---|
265 | mov bh, _ahci_pci_read_config_word.u8Bus + 2[bp]
|
---|
266 | mov bl, _ahci_pci_read_config_word.u8DevFn + 2[bp]
|
---|
267 | mov di, _ahci_pci_read_config_word.u8Reg + 2[bp]
|
---|
268 | int 0x1a
|
---|
269 |
|
---|
270 | ; Return from PCIBIOS
|
---|
271 | cmp ah, #SUCCESSFUL
|
---|
272 | jne ahci_pci_read_config_word_done
|
---|
273 |
|
---|
274 | mov _ahci_pci_read_config_word.u16Val + 2[bp], cx
|
---|
275 |
|
---|
276 | ahci_pci_read_config_word_done:
|
---|
277 | pop bp
|
---|
278 | ASM_END
|
---|
279 |
|
---|
280 | return u16Val;
|
---|
281 | }
|
---|
282 |
|
---|
283 | Bit32u ahci_pci_read_config_dword(u8Bus, u8DevFn, u8Reg)
|
---|
284 | Bit8u u8Bus, u8DevFn, u8Reg;
|
---|
285 | {
|
---|
286 | Bit32u u32Val;
|
---|
287 |
|
---|
288 | u32Val = 0;
|
---|
289 |
|
---|
290 | ASM_START
|
---|
291 | push bp
|
---|
292 | mov bp, sp
|
---|
293 |
|
---|
294 | mov ah, #PCIBIOS_ID
|
---|
295 | mov al, #PCIBIOS_READ_CONFIG_DWORD
|
---|
296 | mov bh, _ahci_pci_read_config_dword.u8Bus + 2[bp]
|
---|
297 | mov bl, _ahci_pci_read_config_dword.u8DevFn + 2[bp]
|
---|
298 | mov di, _ahci_pci_read_config_dword.u8Reg + 2[bp]
|
---|
299 | int 0x1a
|
---|
300 |
|
---|
301 | ; Return from PCIBIOS
|
---|
302 | cmp ah, #SUCCESSFUL
|
---|
303 | jne ahci_pci_read_config_dword_done
|
---|
304 |
|
---|
305 | mov _ahci_pci_read_config_dword.u32Val + 2[bp], ecx
|
---|
306 |
|
---|
307 | ahci_pci_read_config_dword_done:
|
---|
308 | pop bp
|
---|
309 | ASM_END
|
---|
310 |
|
---|
311 | return u32Val;
|
---|
312 | }
|
---|
313 |
|
---|
314 | #if 0 /* Disabled to save space because they are not needed. Might become useful in the future. */
|
---|
315 | /**
|
---|
316 | * Returns the bus/device/function of a PCI device with
|
---|
317 | * the given vendor and device id.
|
---|
318 | *
|
---|
319 | * @returns bus/device/fn in one 16bit integer where
|
---|
320 | * where the upper byte contains the bus number
|
---|
321 | * and lower one the device and function number.
|
---|
322 | * VBOX_AHCI_NO_DEVICE if no device was found.
|
---|
323 | * @param u16Vendor The vendor ID.
|
---|
324 | * @param u16Device The device ID.
|
---|
325 | */
|
---|
326 | Bit16u ahci_pci_find_device(u16Vendor, u16Device)
|
---|
327 | Bit16u u16Vendor;
|
---|
328 | Bit16u u16Device;
|
---|
329 | {
|
---|
330 | Bit16u u16BusDevFn;
|
---|
331 |
|
---|
332 | ASM_START
|
---|
333 | push bp
|
---|
334 | mov bp, sp
|
---|
335 |
|
---|
336 | mov ah, #PCIBIOS_ID
|
---|
337 | mov al, #PCIBIOS_FIND_PCI_DEVICE
|
---|
338 | mov cx, _ahci_pci_find_device.u16Device + 2[bp]
|
---|
339 | mov dx, _ahci_pci_find_device.u16Vendor + 2[bp]
|
---|
340 | mov si, #0 ; First controller
|
---|
341 | int 0x1a
|
---|
342 |
|
---|
343 | ; Return from PCIBIOS
|
---|
344 | cmp ah, #SUCCESSFUL
|
---|
345 | jne ahci_pci_find_device_not_found
|
---|
346 |
|
---|
347 | mov _ahci_pci_find_device.u16BusDevFn + 2[bp], bx
|
---|
348 | jmp ahci_pci_find_device_done
|
---|
349 |
|
---|
350 | ahci_pci_find_device_not_found:
|
---|
351 | mov _ahci_pci_find_device.u16BusDevFn + 2[bp], #VBOX_AHCI_NO_DEVICE
|
---|
352 |
|
---|
353 | ahci_pci_find_device_done:
|
---|
354 | pop bp
|
---|
355 | ASM_END
|
---|
356 |
|
---|
357 | return u16BusDevFn;
|
---|
358 | }
|
---|
359 |
|
---|
360 | void ahci_pci_write_config_byte(u8Bus, u8DevFn, u8Reg, u8Val)
|
---|
361 | Bit8u u8Bus, u8DevFn, u8Reg, u8Val;
|
---|
362 | {
|
---|
363 | ASM_START
|
---|
364 | push bp
|
---|
365 | mov bp, sp
|
---|
366 |
|
---|
367 | mov ah, #PCIBIOS_ID
|
---|
368 | mov al, #PCIBIOS_WRITE_CONFIG_BYTE
|
---|
369 | mov bh, _ahci_pci_write_config_byte.u8Bus + 2[bp]
|
---|
370 | mov bl, _ahci_pci_write_config_byte.u8DevFn + 2[bp]
|
---|
371 | mov di, _ahci_pci_write_config_byte.u8Reg + 2[bp]
|
---|
372 | mov cl, _ahci_pci_write_config_byte.u8Val + 2[bp]
|
---|
373 | int 0x1a
|
---|
374 |
|
---|
375 | ; Return from PCIBIOS
|
---|
376 | pop bp
|
---|
377 | ASM_END
|
---|
378 | }
|
---|
379 |
|
---|
380 | void ahci_pci_write_config_word(u8Bus, u8DevFn, u8Reg, u16Val)
|
---|
381 | Bit8u u8Bus, u8DevFn, u8Reg;
|
---|
382 | Bit16u u16Val;
|
---|
383 | {
|
---|
384 | ASM_START
|
---|
385 | push bp
|
---|
386 | mov bp, sp
|
---|
387 |
|
---|
388 | mov ah, #PCIBIOS_ID
|
---|
389 | mov al, #PCIBIOS_WRITE_CONFIG_WORD
|
---|
390 | mov bh, _ahci_pci_write_config_word.u8Bus + 2[bp]
|
---|
391 | mov bl, _ahci_pci_write_config_word.u8DevFn + 2[bp]
|
---|
392 | mov di, _ahci_pci_write_config_word.u8Reg + 2[bp]
|
---|
393 | mov cx, _ahci_pci_write_config_word.u16Val + 2[bp]
|
---|
394 | int 0x1a
|
---|
395 |
|
---|
396 | ; Return from PCIBIOS
|
---|
397 | pop bp
|
---|
398 | ASM_END
|
---|
399 | }
|
---|
400 |
|
---|
401 | void ahci_pci_write_config_dword(u8Bus, u8DevFn, u8Reg, u32Val)
|
---|
402 | Bit8u u8Bus, u8DevFn, u8Reg;
|
---|
403 | Bit32u u32Val;
|
---|
404 | {
|
---|
405 | ASM_START
|
---|
406 | push bp
|
---|
407 | mov bp, sp
|
---|
408 |
|
---|
409 | mov ah, #PCIBIOS_ID
|
---|
410 | mov al, #PCIBIOS_WRITE_CONFIG_WORD
|
---|
411 | mov bh, _ahci_pci_write_config_dword.u8Bus + 2[bp]
|
---|
412 | mov bl, _ahci_pci_write_config_dword.u8DevFn + 2[bp]
|
---|
413 | mov di, _ahci_pci_write_config_dword.u8Reg + 2[bp]
|
---|
414 | mov cx, _ahci_pci_write_config_dword.u32Val + 2[bp]
|
---|
415 | int 0x1a
|
---|
416 |
|
---|
417 | ; Return from PCIBIOS
|
---|
418 | pop bp
|
---|
419 | ASM_END
|
---|
420 | }
|
---|
421 | #endif /* 0 */
|
---|
422 |
|
---|
423 | /**
|
---|
424 | * Sets a given set of bits in a register.
|
---|
425 | */
|
---|
426 | static void ahci_ctrl_set_bits(u16IoBase, u32Reg, u32Set)
|
---|
427 | Bit16u u16IoBase;
|
---|
428 | Bit32u u32Reg, u32Set;
|
---|
429 | {
|
---|
430 | ASM_START
|
---|
431 | push bp
|
---|
432 | mov bp, sp
|
---|
433 |
|
---|
434 | push eax
|
---|
435 | push edx
|
---|
436 |
|
---|
437 | ; Read from the register
|
---|
438 | mov eax, _ahci_ctrl_set_bits.u32Reg + 2[bp]
|
---|
439 | mov dx, _ahci_ctrl_set_bits.u16IoBase + 2[bp]
|
---|
440 | add dx, #VBOXAHCI_REG_IDX
|
---|
441 | out dx, eax
|
---|
442 |
|
---|
443 | mov dx, _ahci_ctrl_set_bits.u16IoBase + 2[bp]
|
---|
444 | add dx, #VBOXAHCI_REG_DATA
|
---|
445 | in eax, dx
|
---|
446 |
|
---|
447 | ; Set the new bits and write the result to the register again
|
---|
448 | or eax, _ahci_ctrl_set_bits.u32Set + 2[bp]
|
---|
449 | out dx, eax
|
---|
450 |
|
---|
451 | pop edx
|
---|
452 | pop eax
|
---|
453 |
|
---|
454 | pop bp
|
---|
455 | ASM_END
|
---|
456 | }
|
---|
457 |
|
---|
458 | /**
|
---|
459 | * Clears a given set of bits in a register.
|
---|
460 | */
|
---|
461 | static void ahci_ctrl_clear_bits(u16IoBase, u32Reg, u32Clear)
|
---|
462 | Bit16u u16IoBase;
|
---|
463 | Bit32u u32Reg, u32Clear;
|
---|
464 | {
|
---|
465 | ASM_START
|
---|
466 | push bp
|
---|
467 | mov bp, sp
|
---|
468 |
|
---|
469 | push eax
|
---|
470 | push ecx
|
---|
471 | push edx
|
---|
472 |
|
---|
473 | ; Read from the register
|
---|
474 | mov eax, _ahci_ctrl_clear_bits.u32Reg + 2[bp]
|
---|
475 | mov dx, _ahci_ctrl_clear_bits.u16IoBase + 2[bp]
|
---|
476 | add dx, #VBOXAHCI_REG_IDX
|
---|
477 | out dx, eax
|
---|
478 |
|
---|
479 | mov dx, _ahci_ctrl_clear_bits.u16IoBase + 2[bp]
|
---|
480 | add dx, #VBOXAHCI_REG_DATA
|
---|
481 | in eax, dx
|
---|
482 |
|
---|
483 | ; Clear the bits and write the result to the register again
|
---|
484 | mov ecx, _ahci_ctrl_clear_bits.u32Clear + 2[bp]
|
---|
485 | not ecx
|
---|
486 | and eax, ecx
|
---|
487 | out dx, eax
|
---|
488 |
|
---|
489 | pop edx
|
---|
490 | pop ecx
|
---|
491 | pop eax
|
---|
492 |
|
---|
493 | pop bp
|
---|
494 | ASM_END
|
---|
495 | }
|
---|
496 |
|
---|
497 | /**
|
---|
498 | * Returns whether at least one of the bits in the given mask is set
|
---|
499 | * for a register.
|
---|
500 | */
|
---|
501 | static Bit8u ahci_ctrl_is_bit_set(u16IoBase, u32Reg, u32Mask)
|
---|
502 | Bit16u u16IoBase;
|
---|
503 | Bit32u u32Reg, u32Mask;
|
---|
504 | {
|
---|
505 | ASM_START
|
---|
506 | push bp
|
---|
507 | mov bp, sp
|
---|
508 |
|
---|
509 | push eax
|
---|
510 | push edx
|
---|
511 |
|
---|
512 | ; Read from the register
|
---|
513 | mov eax, _ahci_ctrl_is_bit_set.u32Reg + 2[bp]
|
---|
514 | mov dx, _ahci_ctrl_is_bit_set.u16IoBase + 2[bp]
|
---|
515 | add dx, #VBOXAHCI_REG_IDX
|
---|
516 | out dx, eax
|
---|
517 |
|
---|
518 | mov dx, _ahci_ctrl_is_bit_set.u16IoBase + 2[bp]
|
---|
519 | add dx, #VBOXAHCI_REG_DATA
|
---|
520 | in eax, dx
|
---|
521 |
|
---|
522 | ; Check for set bits
|
---|
523 | test eax, _ahci_ctrl_is_bit_set.u32Mask + 2[bp]
|
---|
524 | je ahci_ctrl_is_bit_set_not_set
|
---|
525 | mov al, #1 ; At least one of the bits is set
|
---|
526 | jmp ahci_ctrl_is_bit_set_done
|
---|
527 |
|
---|
528 | ahci_ctrl_is_bit_set_not_set:
|
---|
529 | mov al, #0 ; No bit is set
|
---|
530 |
|
---|
531 | ahci_ctrl_is_bit_set_done:
|
---|
532 | pop edx
|
---|
533 | pop eax
|
---|
534 |
|
---|
535 | pop bp
|
---|
536 | ASM_END
|
---|
537 | }
|
---|
538 |
|
---|
539 | /**
|
---|
540 | * Extracts a range of bits from a register and shifts them
|
---|
541 | * to the right.
|
---|
542 | */
|
---|
543 | static Bit16u ahci_ctrl_extract_bits(u32Reg, u32Mask, u8Shift)
|
---|
544 | Bit32u u32Reg, u32Mask, u8Shift;
|
---|
545 | {
|
---|
546 | ASM_START
|
---|
547 | push bp
|
---|
548 | mov bp, sp
|
---|
549 |
|
---|
550 | push cx
|
---|
551 |
|
---|
552 | mov eax, _ahci_ctrl_extract_bits.u32Reg + 2[bp]
|
---|
553 | mov cl, _ahci_ctrl_extract_bits.u8Shift + 2[bp]
|
---|
554 | and eax, _ahci_ctrl_extract_bits.u32Mask + 2[bp]
|
---|
555 | shr eax, cl
|
---|
556 |
|
---|
557 | pop cx
|
---|
558 |
|
---|
559 | pop bp
|
---|
560 | ASM_END
|
---|
561 | }
|
---|
562 |
|
---|
563 | /**
|
---|
564 | * Converts a segment:offset pair into a 32bit physical address.
|
---|
565 | */
|
---|
566 | static Bit32u ahci_addr_to_phys(u16Segment, u16Offset)
|
---|
567 | Bit16u u16Segment, u16Offset;
|
---|
568 | {
|
---|
569 | ASM_START
|
---|
570 | push bp
|
---|
571 | mov bp, sp
|
---|
572 |
|
---|
573 | push bx
|
---|
574 | push eax
|
---|
575 |
|
---|
576 | xor eax, eax
|
---|
577 | xor ebx, ebx
|
---|
578 | mov ax, _ahci_addr_to_phys.u16Segment + 2[bp]
|
---|
579 | shl eax, #4
|
---|
580 | add bx, _ahci_addr_to_phys.u16Offset + 2[bp]
|
---|
581 | add eax, ebx
|
---|
582 |
|
---|
583 | mov bx, ax
|
---|
584 | shr eax, #16
|
---|
585 | mov dx, ax
|
---|
586 |
|
---|
587 | pop eax
|
---|
588 | mov ax, bx
|
---|
589 | pop bx
|
---|
590 |
|
---|
591 | pop bp
|
---|
592 | ASM_END
|
---|
593 | }
|
---|
594 |
|
---|
595 | /**
|
---|
596 | * Issues a command to the SATA controller and waits for completion.
|
---|
597 | */
|
---|
598 | static void ahci_port_cmd_sync(SegAhci, u16IoBase, fWrite, fAtapi, cFisDWords, cbData)
|
---|
599 | Bit16u SegAhci;
|
---|
600 | Bit16u u16IoBase;
|
---|
601 | bx_bool fWrite;
|
---|
602 | bx_bool fAtapi;
|
---|
603 | Bit8u cFisDWords;
|
---|
604 | Bit16u cbData;
|
---|
605 | {
|
---|
606 | Bit8u u8Port;
|
---|
607 |
|
---|
608 | u8Port = read_byte(SegAhci, &AhciData->port);
|
---|
609 |
|
---|
610 | if (u8Port != 0xff)
|
---|
611 | {
|
---|
612 | Bit32u u32Val;
|
---|
613 |
|
---|
614 | /* Prepare the command header. */
|
---|
615 | u32Val = (1L << 16) | RT_BIT_32(7);
|
---|
616 | if (fWrite)
|
---|
617 | u32Val |= RT_BIT_32(6);
|
---|
618 |
|
---|
619 | if (fAtapi)
|
---|
620 | u32Val |= RT_BIT_32(5);
|
---|
621 |
|
---|
622 | u32Val |= cFisDWords;
|
---|
623 |
|
---|
624 | write_dword(SegAhci, &AhciData->abCmdHdr[0], u32Val);
|
---|
625 | write_dword(SegAhci, &AhciData->abCmdHdr[1], (Bit32u)cbData);
|
---|
626 | write_dword(SegAhci, &AhciData->abCmdHdr[2],
|
---|
627 | ahci_addr_to_phys(SegAhci, &AhciData->abCmd[0]));
|
---|
628 |
|
---|
629 | /* Enable Command engine. */
|
---|
630 | ahci_ctrl_set_bits(u16IoBase, VBOXAHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
|
---|
631 | AHCI_REG_PORT_CMD_ST);
|
---|
632 |
|
---|
633 | /* Queue command. */
|
---|
634 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_CI, 0x1L);
|
---|
635 |
|
---|
636 | /* Wait for a D2H Fis. */
|
---|
637 | while (ahci_ctrl_is_bit_set(u16IoBase, VBOXAHCI_PORT_REG(u8Port, AHCI_REG_PORT_IS),
|
---|
638 | AHCI_REG_PORT_IS_DHRS) == 0)
|
---|
639 | {
|
---|
640 | VBOXAHCI_DEBUG("AHCI: Waiting for a D2H Fis\n");
|
---|
641 | }
|
---|
642 |
|
---|
643 | ahci_ctrl_set_bits(u16IoBase, VBOXAHCI_PORT_REG(u8Port, AHCI_REG_PORT_IS),
|
---|
644 | AHCI_REG_PORT_IS_DHRS); /* Acknowledge received D2H FIS. */
|
---|
645 |
|
---|
646 | /* Disable command engine. */
|
---|
647 | ahci_ctrl_clear_bits(u16IoBase, VBOXAHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
|
---|
648 | AHCI_REG_PORT_CMD_ST);
|
---|
649 |
|
---|
650 | /** @todo: Examine status. */
|
---|
651 | }
|
---|
652 | else
|
---|
653 | VBOXAHCI_DEBUG("AHCI: Invalid port given\n");
|
---|
654 | }
|
---|
655 |
|
---|
656 | /**
|
---|
657 | * Issue command to device.
|
---|
658 | */
|
---|
659 | static void ahci_cmd_data(SegAhci, u16IoBase, u8Cmd, u8Feat, u8Device, u8CylHigh, u8CylLow, u8Sect,
|
---|
660 | u8FeatExp, u8CylHighExp, u8CylLowExp, u8SectExp, u8SectCount, u8SectCountExp,
|
---|
661 | SegData, OffData, cbData, fWrite)
|
---|
662 | Bit16u SegAhci;
|
---|
663 | Bit16u u16IoBase;
|
---|
664 | Bit8u u8Cmd, u8Feat, u8Device, u8CylHigh, u8CylLow, u8Sect, u8FeatExp,
|
---|
665 | u8CylHighExp, u8CylLowExp, u8SectExp, u8SectCount, u8SectCountExp;
|
---|
666 | Bit16u SegData, OffData, cbData;
|
---|
667 | bx_bool fWrite;
|
---|
668 | {
|
---|
669 | memsetb(SegAhci, &AhciData->abCmd[0], 0, sizeof(AhciData->abCmd));
|
---|
670 |
|
---|
671 | /* Prepare the FIS. */
|
---|
672 | write_byte(SegAhci, &AhciData->abCmd[0], 0x27); /* FIS type H2D. */
|
---|
673 | write_byte(SegAhci, &AhciData->abCmd[1], 1 << 7); /* Command update. */
|
---|
674 | write_byte(SegAhci, &AhciData->abCmd[2], u8Cmd);
|
---|
675 | write_byte(SegAhci, &AhciData->abCmd[3], u8Feat);
|
---|
676 |
|
---|
677 | write_byte(SegAhci, &AhciData->abCmd[4], u8Sect);
|
---|
678 | write_byte(SegAhci, &AhciData->abCmd[5], u8CylLow);
|
---|
679 | write_byte(SegAhci, &AhciData->abCmd[6], u8CylHigh);
|
---|
680 | write_byte(SegAhci, &AhciData->abCmd[7], u8Device);
|
---|
681 |
|
---|
682 | write_byte(SegAhci, &AhciData->abCmd[8], u8SectExp);
|
---|
683 | write_byte(SegAhci, &AhciData->abCmd[9], u8CylLowExp);
|
---|
684 | write_byte(SegAhci, &AhciData->abCmd[10], u8CylHighExp);
|
---|
685 | write_byte(SegAhci, &AhciData->abCmd[11], u8FeatExp);
|
---|
686 |
|
---|
687 | write_byte(SegAhci, &AhciData->abCmd[12], u8SectCount);
|
---|
688 | write_byte(SegAhci, &AhciData->abCmd[13], u8SectCountExp);
|
---|
689 |
|
---|
690 | /* Prepare PRDT. */
|
---|
691 | write_dword(SegAhci, &AhciData->abCmd[0x80], ahci_addr_to_phys(SegData, OffData));
|
---|
692 | write_dword(SegAhci, &AhciData->abCmd[0x8c], (Bit32u)cbData);
|
---|
693 |
|
---|
694 | ahci_port_cmd_sync(SegAhci, u16IoBase, fWrite, 0, 5, cbData);
|
---|
695 | }
|
---|
696 |
|
---|
697 | /**
|
---|
698 | * Deinits the curent active port.
|
---|
699 | */
|
---|
700 | static void ahci_port_deinit_current(SegAhci, u16IoBase)
|
---|
701 | Bit16u SegAhci;
|
---|
702 | Bit16u u16IoBase;
|
---|
703 | {
|
---|
704 | Bit8u u8Port;
|
---|
705 |
|
---|
706 | u8Port = read_byte(SegAhci, &AhciData->port);
|
---|
707 |
|
---|
708 | if (u8Port != 0xff)
|
---|
709 | {
|
---|
710 | /* Put the port into an idle state. */
|
---|
711 | ahci_ctrl_clear_bits(u16IoBase, VBOXAHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
|
---|
712 | AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_ST);
|
---|
713 |
|
---|
714 | while (ahci_ctrl_is_bit_set(u16IoBase, VBOXAHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
|
---|
715 | AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_ST | AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_CR) == 1)
|
---|
716 | {
|
---|
717 | VBOXAHCI_DEBUG("AHCI: Waiting for the port to idle\n");
|
---|
718 | }
|
---|
719 |
|
---|
720 | /*
|
---|
721 | * Port idles, set up memory for commands and received FIS and program the
|
---|
722 | * address registers.
|
---|
723 | */
|
---|
724 | memsetb(SegAhci, &AhciData->abFisRecv[0], 0, 0x60);
|
---|
725 | memsetb(SegAhci, &AhciData->abCmdHdr[0], 0, 0x20);
|
---|
726 | memsetb(SegAhci, &AhciData->abCmd[0], 0, 0x84);
|
---|
727 |
|
---|
728 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_FB, 0L);
|
---|
729 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_FBU, 0L);
|
---|
730 |
|
---|
731 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_CLB, 0L);
|
---|
732 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_CLBU, 0L);
|
---|
733 |
|
---|
734 | /* Disable all interrupts. */
|
---|
735 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_IE, 0L);
|
---|
736 |
|
---|
737 | write_byte(SegAhci, &AhciData->port, 0xff);
|
---|
738 | }
|
---|
739 | }
|
---|
740 |
|
---|
741 | /**
|
---|
742 | * Brings a port into a minimal state to make device detection possible
|
---|
743 | * or to queue requests.
|
---|
744 | */
|
---|
745 | static void ahci_port_init(SegAhci, u16IoBase, u8Port)
|
---|
746 | Bit16u SegAhci;
|
---|
747 | Bit16u u16IoBase;
|
---|
748 | Bit8u u8Port;
|
---|
749 | {
|
---|
750 | Bit32u u32PhysAddr;
|
---|
751 |
|
---|
752 | /* Deinit any other port first. */
|
---|
753 | ahci_port_deinit_current(SegAhci, u16IoBase);
|
---|
754 |
|
---|
755 | /* Put the port into an idle state. */
|
---|
756 | ahci_ctrl_clear_bits(u16IoBase, VBOXAHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
|
---|
757 | AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_ST);
|
---|
758 |
|
---|
759 | while (ahci_ctrl_is_bit_set(u16IoBase, VBOXAHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
|
---|
760 | AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_ST | AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_CR) == 1)
|
---|
761 | {
|
---|
762 | VBOXAHCI_DEBUG("AHCI: Waiting for the port to idle\n");
|
---|
763 | }
|
---|
764 |
|
---|
765 | /*
|
---|
766 | * Port idles, set up memory for commands and received FIS and program the
|
---|
767 | * address registers.
|
---|
768 | */
|
---|
769 | memsetb(SegAhci, &AhciData->abFisRecv[0], 0, 0x60);
|
---|
770 | memsetb(SegAhci, &AhciData->abCmdHdr[0], 0, 0x20);
|
---|
771 | memsetb(SegAhci, &AhciData->abCmd[0], 0, 0x84);
|
---|
772 |
|
---|
773 | u32PhysAddr = ahci_addr_to_phys(SegAhci, &AhciData->abFisRecv);
|
---|
774 | VBOXAHCI_DEBUG("AHCI: FIS receive area %lx from %x:%x\n", u32PhysAddr, SegAhci, &AhciData->abFisRecv);
|
---|
775 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_FB, u32PhysAddr);
|
---|
776 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_FBU, 0L);
|
---|
777 |
|
---|
778 | u32PhysAddr = ahci_addr_to_phys(SegAhci, &AhciData->abCmdHdr);
|
---|
779 | VBOXAHCI_DEBUG("AHCI: CMD list area %lx\n", u32PhysAddr);
|
---|
780 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_CLB, u32PhysAddr);
|
---|
781 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_CLBU, 0L);
|
---|
782 |
|
---|
783 | /* Disable all interrupts. */
|
---|
784 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_IE, 0L);
|
---|
785 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_IS, 0xffffffffL);
|
---|
786 | /* Clear all errors. */
|
---|
787 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_SERR, 0xffffffffL);
|
---|
788 |
|
---|
789 | write_byte(SegAhci, &AhciData->port, u8Port);
|
---|
790 | }
|
---|
791 |
|
---|
792 | static void ahci_port_detect_device(SegAhci, u16IoBase, u8Port)
|
---|
793 | Bit16u SegAhci;
|
---|
794 | Bit16u u16IoBase;
|
---|
795 | Bit8u u8Port;
|
---|
796 | {
|
---|
797 | Bit32u val;
|
---|
798 |
|
---|
799 | ahci_port_init(SegAhci, u16IoBase, u8Port);
|
---|
800 |
|
---|
801 | /* Reset connection. */
|
---|
802 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_SCTL, 0x01L);
|
---|
803 | /*
|
---|
804 | * According to the spec we should wait at least 1msec until the reset
|
---|
805 | * is cleared but this is a virtual controller so we don't have to.
|
---|
806 | */
|
---|
807 | VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_SCTL, 0x00L);
|
---|
808 |
|
---|
809 | /* Check if there is a device on the port. */
|
---|
810 | VBOXAHCI_PORT_READ_REG(u16IoBase, u8Port, AHCI_REG_PORT_SSTS, val);
|
---|
811 | if (ahci_ctrl_extract_bits(val, 0xfL, 0) == 0x3L)
|
---|
812 | {
|
---|
813 | VBOXAHCI_DEBUG("AHCI: Device detected on port %d\n", u8Port);
|
---|
814 |
|
---|
815 | /* Device detected, enable FIS receive. */
|
---|
816 | ahci_ctrl_set_bits(u16IoBase, VBOXAHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
|
---|
817 | AHCI_REG_PORT_CMD_FRE);
|
---|
818 |
|
---|
819 | /* Check signature to determine device type. */
|
---|
820 | VBOXAHCI_PORT_READ_REG(u16IoBase, u8Port, AHCI_REG_PORT_SIG, val);
|
---|
821 | if (val == 0x101L)
|
---|
822 | {
|
---|
823 | Bit8u idxHdCurr = read_byte(SegAhci, &AhciData->cHardDisks);
|
---|
824 | if (idxHdCurr < AHCI_MAX_STORAGE_DEVICES)
|
---|
825 | {
|
---|
826 | Bit32u cSectors;
|
---|
827 | Bit8u abBuffer[0x0200];
|
---|
828 |
|
---|
829 | VBOXAHCI_DEBUG("AHCI: Detected hard disk\n");
|
---|
830 |
|
---|
831 | /* Identify device. */
|
---|
832 | ahci_cmd_data(SegAhci, u16IoBase, ATA_CMD_IDENTIFY_DEVICE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, get_SS(), abBuffer, sizeof(abBuffer), 0);
|
---|
833 |
|
---|
834 | write_byte(SegAhci, &AhciData->aDevices[idxHdCurr].port, u8Port);
|
---|
835 | VBOXAHCI_DEBUG("AHCI: %lld sectors\n", cSectors);
|
---|
836 |
|
---|
837 |
|
---|
838 | idxHdCurr++;
|
---|
839 | write_byte(SegAhci, &AhciData->cHardDisks, idxHdCurr);
|
---|
840 | }
|
---|
841 | else
|
---|
842 | VBOXAHCI_DEBUG("AHCI: Reached maximum hard disk count, skipping\n");
|
---|
843 | }
|
---|
844 | else if (val == 0xeb140101)
|
---|
845 | {
|
---|
846 | VBOXAHCI_DEBUG("AHCI: Detected ATAPI device\n");
|
---|
847 | }
|
---|
848 | else
|
---|
849 | VBOXAHCI_DEBUG("AHCI: Unknown device ignoring\n");
|
---|
850 | }
|
---|
851 | }
|
---|
852 |
|
---|
853 | static Bit16u ahci_mem_alloc()
|
---|
854 | {
|
---|
855 | Bit16u cBaseMem1K;
|
---|
856 | Bit16u SegStart;
|
---|
857 |
|
---|
858 | cBaseMem1K = read_byte(0x00, 0x0413);
|
---|
859 |
|
---|
860 | VBOXAHCI_DEBUG("AHCI: %x K of base memory available\n", cBaseMem1K);
|
---|
861 |
|
---|
862 | if (cBaseMem1K == 0)
|
---|
863 | return 0;
|
---|
864 |
|
---|
865 | cBaseMem1K--; /* Allocate one block. */
|
---|
866 | SegStart = (Bit16u)(((Bit32u)cBaseMem1K * 1024) >> 4); /* Calculate start segment. */
|
---|
867 |
|
---|
868 | write_byte(0x00, 0x0413, cBaseMem1K);
|
---|
869 |
|
---|
870 | return SegStart;
|
---|
871 | }
|
---|
872 |
|
---|
873 | static int ahci_ctrl_init(u16IoBase)
|
---|
874 | Bit16u u16IoBase;
|
---|
875 | {
|
---|
876 | Bit8u i, cPorts;
|
---|
877 | Bit32u val;
|
---|
878 | Bit16u ebda_seg;
|
---|
879 | Bit16u SegAhci;
|
---|
880 |
|
---|
881 | ebda_seg = read_word(0x0040, 0x000E);
|
---|
882 |
|
---|
883 | VBOXAHCI_READ_REG(u16IoBase, AHCI_REG_VS, val);
|
---|
884 | VBOXAHCI_DEBUG("AHCI: Controller has version: 0x%x (major) 0x%x (minor)\n",
|
---|
885 | ahci_ctrl_extract_bits(val, 0xffff0000, 16),
|
---|
886 | ahci_ctrl_extract_bits(val, 0x0000ffff, 0));
|
---|
887 |
|
---|
888 | /* Allocate 1K of base memory. */
|
---|
889 | SegAhci = ahci_mem_alloc();
|
---|
890 | if (SegAhci == 0)
|
---|
891 | {
|
---|
892 | VBOXAHCI_DEBUG("AHCI: Could not allocate 1K of memory, can't boot from controller\n");
|
---|
893 | return 0;
|
---|
894 | }
|
---|
895 |
|
---|
896 | write_word(ebda_seg, &EbdaData->SegAhci, SegAhci);
|
---|
897 | write_byte(SegAhci, &AhciData->port, 0xff);
|
---|
898 |
|
---|
899 | /* Reset the controller. */
|
---|
900 | ahci_ctrl_set_bits(u16IoBase, AHCI_REG_GHC, AHCI_GHC_HR);
|
---|
901 | do
|
---|
902 | {
|
---|
903 | VBOXAHCI_READ_REG(u16IoBase, AHCI_REG_GHC, val);
|
---|
904 | } while (val & AHCI_GHC_HR != 0);
|
---|
905 |
|
---|
906 | VBOXAHCI_READ_REG(u16IoBase, AHCI_REG_CAP, val);
|
---|
907 | cPorts = ahci_ctrl_extract_bits(val, 0x1f, 0) + 1; /* Extract number of ports.*/
|
---|
908 |
|
---|
909 | VBOXAHCI_DEBUG("AHCI: Controller has %u ports\n", cPorts);
|
---|
910 |
|
---|
911 | /* Go through the ports. */
|
---|
912 | i = 0;
|
---|
913 | while (i < 32)
|
---|
914 | {
|
---|
915 | if (ahci_ctrl_is_bit_set(u16IoBase, AHCI_REG_PI, RT_BIT_32(i)) != 0)
|
---|
916 | {
|
---|
917 | VBOXAHCI_DEBUG("AHCI: Port %u is present\n", i);
|
---|
918 | ahci_port_detect_device(SegAhci, u16IoBase, i);
|
---|
919 | cPorts--;
|
---|
920 | if (cPorts == 0)
|
---|
921 | break;
|
---|
922 | }
|
---|
923 | i++;
|
---|
924 | }
|
---|
925 |
|
---|
926 | return 0;
|
---|
927 | }
|
---|
928 |
|
---|
929 | /**
|
---|
930 | * Init the AHCI driver and detect attached disks.
|
---|
931 | */
|
---|
932 | void ahci_init( )
|
---|
933 | {
|
---|
934 | Bit16u ebda_seg;
|
---|
935 | Bit16u busdevfn;
|
---|
936 |
|
---|
937 | ebda_seg = read_word(0x0040, 0x000E);
|
---|
938 |
|
---|
939 | busdevfn = ahci_pci_find_classcode(0x00010601);
|
---|
940 | if (busdevfn != VBOX_AHCI_NO_DEVICE)
|
---|
941 | {
|
---|
942 | Bit8u u8Bus, u8DevFn;
|
---|
943 | Bit8u u8PciCapOff;
|
---|
944 |
|
---|
945 | u8Bus = (busdevfn & 0xff00) >> 8;
|
---|
946 | u8DevFn = busdevfn & 0x00ff;
|
---|
947 |
|
---|
948 | VBOXAHCI_DEBUG("Found AHCI controller at Bus %u DevFn 0x%x (raw 0x%x)\n", u8Bus, u8DevFn, busdevfn);
|
---|
949 |
|
---|
950 | /* Examine the capability list and search for the Serial ATA Capability Register. */
|
---|
951 | u8PciCapOff = ahci_pci_read_config_byte(u8Bus, u8DevFn, PCI_CONFIG_CAP);
|
---|
952 |
|
---|
953 | while (u8PciCapOff != 0)
|
---|
954 | {
|
---|
955 | Bit8u u8PciCapId = ahci_pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff);
|
---|
956 |
|
---|
957 | VBOXAHCI_DEBUG("Capability ID 0x%x at offset 0x%x found\n", u8PciCapId, u8PciCapOff);
|
---|
958 |
|
---|
959 | if (u8PciCapId == PCI_CAP_ID_SATACR)
|
---|
960 | break;
|
---|
961 |
|
---|
962 | /* Go on to the next capability. */
|
---|
963 | u8PciCapOff = ahci_pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff + 1);
|
---|
964 | }
|
---|
965 |
|
---|
966 | if (u8PciCapOff != 0)
|
---|
967 | {
|
---|
968 | Bit8u u8Rev;
|
---|
969 |
|
---|
970 | VBOXAHCI_DEBUG("AHCI controller with SATA Capability register at offset 0x%x found\n", u8PciCapOff);
|
---|
971 |
|
---|
972 | /* Advance to the stuff behind the id and next capability pointer. */
|
---|
973 | u8PciCapOff += 2;
|
---|
974 |
|
---|
975 | u8Rev = ahci_pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff);
|
---|
976 | if (u8Rev == 0x10)
|
---|
977 | {
|
---|
978 | /* Read the SATACR1 register and get the bar and offset of the index/data pair register. */
|
---|
979 | Bit8u u8Bar = 0x00;
|
---|
980 | Bit16u u16Off = 0x00;
|
---|
981 | Bit16u u16BarOff = ahci_pci_read_config_word(u8Bus, u8DevFn, u8PciCapOff + 2);
|
---|
982 |
|
---|
983 | VBOXAHCI_DEBUG("SATACR1 register contains 0x%x\n", u16BarOff);
|
---|
984 |
|
---|
985 | switch (u16BarOff & 0xf)
|
---|
986 | {
|
---|
987 | case 0x04:
|
---|
988 | u8Bar = 0x10;
|
---|
989 | break;
|
---|
990 | case 0x05:
|
---|
991 | u8Bar = 0x14;
|
---|
992 | break;
|
---|
993 | case 0x06:
|
---|
994 | u8Bar = 0x18;
|
---|
995 | break;
|
---|
996 | case 0x07:
|
---|
997 | u8Bar = 0x1c;
|
---|
998 | break;
|
---|
999 | case 0x08:
|
---|
1000 | u8Bar = 0x20;
|
---|
1001 | break;
|
---|
1002 | case 0x09:
|
---|
1003 | u8Bar = 0x24;
|
---|
1004 | break;
|
---|
1005 | case 0x0f:
|
---|
1006 | default:
|
---|
1007 | /* Reserved or unsupported. */
|
---|
1008 | VBOXAHCI_DEBUG("BAR location 0x%x is unsupported\n", u16BarOff & 0xf);
|
---|
1009 | }
|
---|
1010 |
|
---|
1011 | /* Get the offset inside the BAR from bits 4:15. */
|
---|
1012 | u16Off = (u16BarOff >> 4) * 4;
|
---|
1013 |
|
---|
1014 | if (u8Bar != 0x00)
|
---|
1015 | {
|
---|
1016 | Bit32u u32Bar = ahci_pci_read_config_dword(u8Bus, u8DevFn, u8Bar);
|
---|
1017 |
|
---|
1018 | VBOXAHCI_DEBUG("BAR at offset 0x%x contains 0x%x\n", u8Bar, u32Bar);
|
---|
1019 |
|
---|
1020 | if ((u32Bar & 0x01) != 0)
|
---|
1021 | {
|
---|
1022 | int rc;
|
---|
1023 | Bit16u u16AhciIoBase = (u32Bar & 0xfff0) + u16Off;
|
---|
1024 |
|
---|
1025 | VBOXAHCI_DEBUG("I/O base is 0x%x\n", u16AhciIoBase);
|
---|
1026 | rc = ahci_ctrl_init(u16AhciIoBase);
|
---|
1027 | }
|
---|
1028 | else
|
---|
1029 | VBOXAHCI_DEBUG("BAR is MMIO\n");
|
---|
1030 | }
|
---|
1031 | }
|
---|
1032 | else
|
---|
1033 | VBOXAHCI_DEBUG("Invalid revision 0x%x\n", u8Rev);
|
---|
1034 | }
|
---|
1035 | else
|
---|
1036 | VBOXAHCI_DEBUG("AHCI controller without usable Index/Data register pair found\n");
|
---|
1037 | }
|
---|
1038 | else
|
---|
1039 | VBOXAHCI_DEBUG("No AHCI controller found\n");
|
---|
1040 | }
|
---|
1041 |
|
---|