VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/ahci.c@ 37268

Last change on this file since 37268 was 37268, checked in by vboxsync, 14 years ago

BIOS: Updates for the AHCI driver

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.0 KB
Line 
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
20typedef 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 */
41typedef 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 */
189Bit16u ahci_pci_find_classcode(u16Class)
190 Bit32u u16Class;
191{
192 Bit16u u16BusDevFn;
193
194ASM_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
211ahci_pci_find_classcode_not_found:
212 mov _ahci_pci_find_classcode.u16BusDevFn + 2[bp], #VBOX_AHCI_NO_DEVICE
213
214ahci_pci_find_classcode_done:
215 pop bp
216ASM_END
217
218 return u16BusDevFn;
219}
220
221Bit8u ahci_pci_read_config_byte(u8Bus, u8DevFn, u8Reg)
222 Bit8u u8Bus, u8DevFn, u8Reg;
223{
224 uint8_t u8Val;
225
226 u8Val = 0;
227
228ASM_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
245ahci_pci_read_config_byte_done:
246 pop bp
247ASM_END
248
249 return u8Val;
250}
251
252Bit16u ahci_pci_read_config_word(u8Bus, u8DevFn, u8Reg)
253 Bit8u u8Bus, u8DevFn, u8Reg;
254{
255 Bit16u u16Val;
256
257 u16Val = 0;
258
259ASM_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
276ahci_pci_read_config_word_done:
277 pop bp
278ASM_END
279
280 return u16Val;
281}
282
283Bit32u ahci_pci_read_config_dword(u8Bus, u8DevFn, u8Reg)
284 Bit8u u8Bus, u8DevFn, u8Reg;
285{
286 Bit32u u32Val;
287
288 u32Val = 0;
289
290ASM_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
307ahci_pci_read_config_dword_done:
308 pop bp
309ASM_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 */
326Bit16u ahci_pci_find_device(u16Vendor, u16Device)
327 Bit16u u16Vendor;
328 Bit16u u16Device;
329{
330 Bit16u u16BusDevFn;
331
332ASM_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
350ahci_pci_find_device_not_found:
351 mov _ahci_pci_find_device.u16BusDevFn + 2[bp], #VBOX_AHCI_NO_DEVICE
352
353ahci_pci_find_device_done:
354 pop bp
355ASM_END
356
357 return u16BusDevFn;
358}
359
360void ahci_pci_write_config_byte(u8Bus, u8DevFn, u8Reg, u8Val)
361 Bit8u u8Bus, u8DevFn, u8Reg, u8Val;
362{
363ASM_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
377ASM_END
378}
379
380void ahci_pci_write_config_word(u8Bus, u8DevFn, u8Reg, u16Val)
381 Bit8u u8Bus, u8DevFn, u8Reg;
382 Bit16u u16Val;
383{
384ASM_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
398ASM_END
399}
400
401void ahci_pci_write_config_dword(u8Bus, u8DevFn, u8Reg, u32Val)
402 Bit8u u8Bus, u8DevFn, u8Reg;
403 Bit32u u32Val;
404{
405ASM_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
419ASM_END
420}
421#endif /* 0 */
422
423/**
424 * Sets a given set of bits in a register.
425 */
426static void ahci_ctrl_set_bits(u16IoBase, u32Reg, u32Set)
427 Bit16u u16IoBase;
428 Bit32u u32Reg, u32Set;
429{
430ASM_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
455ASM_END
456}
457
458/**
459 * Clears a given set of bits in a register.
460 */
461static void ahci_ctrl_clear_bits(u16IoBase, u32Reg, u32Clear)
462 Bit16u u16IoBase;
463 Bit32u u32Reg, u32Clear;
464{
465ASM_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
494ASM_END
495}
496
497/**
498 * Returns whether at least one of the bits in the given mask is set
499 * for a register.
500 */
501static Bit8u ahci_ctrl_is_bit_set(u16IoBase, u32Reg, u32Mask)
502 Bit16u u16IoBase;
503 Bit32u u32Reg, u32Mask;
504{
505ASM_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
528ahci_ctrl_is_bit_set_not_set:
529 mov al, #0 ; No bit is set
530
531ahci_ctrl_is_bit_set_done:
532 pop edx
533 pop eax
534
535 pop bp
536ASM_END
537}
538
539/**
540 * Extracts a range of bits from a register and shifts them
541 * to the right.
542 */
543static Bit16u ahci_ctrl_extract_bits(u32Reg, u32Mask, u8Shift)
544 Bit32u u32Reg, u32Mask, u8Shift;
545{
546ASM_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
560ASM_END
561}
562
563/**
564 * Converts a segment:offset pair into a 32bit physical address.
565 */
566static Bit32u ahci_addr_to_phys(u16Segment, u16Offset)
567 Bit16u u16Segment, u16Offset;
568{
569ASM_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
592ASM_END
593}
594
595/**
596 * Issues a command to the SATA controller and waits for completion.
597 */
598static 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 */
659static 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 */
700static 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 */
745static 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
792static 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
853static 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
873static 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 */
932void 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
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette