VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS-new/ahci.c@ 39366

Last change on this file since 39366 was 39366, checked in by vboxsync, 13 years ago

Further streamlined BIOS disk code for ATA/SCSI.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.2 KB
Line 
1/* $Id: ahci.c 39366 2011-11-18 13:57:42Z 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 * Parts are based on the int13_harddisk code in rombios.c
19 */
20
21//@todo!!!! save/restore high bits of EAX/ECX and whatever else may be needed.
22
23#include <stdint.h>
24#include <string.h>
25#include "biosint.h"
26#include "ebda.h"
27#include "inlines.h"
28#include "pciutil.h"
29#include "vds.h"
30
31#define VBOX_AHCI_DEBUG 1
32#define VBOX_AHCI_INT13_DEBUG 0
33
34#if VBOX_AHCI_DEBUG
35# define VBOXAHCI_DEBUG(...) BX_INFO(__VA_ARGS__)
36#else
37# define VBOXAHCI_DEBUG(...)
38#endif
39
40#if VBOX_AHCI_INT13_DEBUG
41# define VBOXAHCI_INT13_DEBUG(...) BX_INFO(__VA_ARGS__)
42#else
43# define VBOXAHCI_INT13_DEBUG(...)
44#endif
45
46#define AHCI_MAX_STORAGE_DEVICES 4
47
48/* Number of S/G table entries in EDDS. */
49#define NUM_EDDS_SG 16
50
51
52/**
53 * AHCI device data.
54 */
55typedef struct
56{
57 uint8_t type; // Detected type of ata (ata/atapi/none/unknown/scsi)
58 uint8_t device; // Detected type of attached devices (hd/cd/none)
59 uint8_t removable; // Removable device flag
60 uint8_t lock; // Locks for removable devices
61 uint16_t blksize; // block size
62 chs_t lchs; // Logical CHS
63 chs_t pchs; // Physical CHS
64 uint32_t cSectors; // Total sectors count
65 uint8_t port; // Port this device is on.
66} ahci_device_t;
67
68/**
69 * AHCI PRDT structure.
70 */
71typedef struct
72{
73 uint32_t phys_addr;
74 uint32_t something;
75 uint32_t reserved;
76 uint32_t len;
77} ahci_prdt;
78
79/**
80 * AHCI controller data.
81 */
82typedef struct
83{
84 /** The AHCI command list as defined by chapter 4.2.2 of the Intel AHCI spec.
85 * Because the BIOS doesn't support NCQ only the first command header is defined
86 * to save memory. - Must be aligned on a 1K boundary.
87 */
88 uint32_t aCmdHdr[0x8];
89 /** Align the next structure on a 128 byte boundary. */
90 uint8_t abAlignment1[0x60];
91 /** The command table of one request as defined by chapter 4.2.3 of the Intel AHCI spec.
92 * Must be aligned on 128 byte boundary.
93 */
94 uint8_t abCmd[0x80];
95 /** Physical Region Descriptor Table (PRDT) array. In other
96 * words, a scatter/gather descriptor list.
97 */
98 ahci_prdt aPrdt[16];
99 /** Memory for the received command FIS area as specified by chapter 4.2.1
100 * of the Intel AHCI spec. This area is normally 256 bytes big but to save memory
101 * only the first 96 bytes are used because it is assumed that the controller
102 * never writes to the UFIS or reserved area. - Must be aligned on a 256byte boundary.
103 */
104 uint8_t abFisRecv[0x60];
105 /** Base I/O port for the index/data register pair. */
106 uint16_t iobase;
107 /** Current port which uses the memory to communicate with the controller. */
108 uint8_t port;
109 /** AHCI device information. */
110 ahci_device_t aDevices[AHCI_MAX_STORAGE_DEVICES];
111 /** Index of the next unoccupied device slot. */
112 uint8_t cDevices;
113 /** Map between (bios hd id - 0x80) and ahci devices. */
114 uint8_t cHardDisks;
115 uint8_t aHdIdMap[AHCI_MAX_STORAGE_DEVICES];
116 /** Map between (bios cd id - 0xE0) and ahci_devices. */
117 uint8_t cCdDrives;
118 uint8_t aCdIdMap[AHCI_MAX_STORAGE_DEVICES];
119 /** Number of harddisks detected before the AHCI driver started detection. */
120 uint8_t cHardDisksOld;
121 /** int13 handler to call if given device is not from AHCI. */
122 uint16_t pfnInt13Old;
123 /** VDS EDDS DMA buffer descriptor structure. */
124 vds_edds edds;
125 vds_sg edds_more_sg[NUM_EDDS_SG - 1];
126} ahci_t;
127
128#define AhciData ((ahci_t *) 0)
129
130/** PCI configuration fields. */
131#define PCI_CONFIG_CAP 0x34
132
133#define PCI_CAP_ID_SATACR 0x12
134#define VBOX_AHCI_NO_DEVICE 0xffff
135
136#define RT_BIT_32(bit) ((uint32_t)(1L << (bit)))
137
138/** Global register set. */
139#define AHCI_HBA_SIZE 0x100
140
141//@todo: what are the casts good for?
142#define AHCI_REG_CAP ((uint32_t)0x00)
143#define AHCI_REG_GHC ((uint32_t)0x04)
144# define AHCI_GHC_AE RT_BIT_32(31)
145# define AHCI_GHC_IR RT_BIT_32(1)
146# define AHCI_GHC_HR RT_BIT_32(0)
147#define AHCI_REG_IS ((uint32_t)0x08)
148#define AHCI_REG_PI ((uint32_t)0x0c)
149#define AHCI_REG_VS ((uint32_t)0x10)
150
151/** Per port register set. */
152#define AHCI_PORT_SIZE 0x80
153
154#define AHCI_REG_PORT_CLB 0x00
155#define AHCI_REG_PORT_CLBU 0x04
156#define AHCI_REG_PORT_FB 0x08
157#define AHCI_REG_PORT_FBU 0x0c
158#define AHCI_REG_PORT_IS 0x10
159# define AHCI_REG_PORT_IS_DHRS RT_BIT_32(0)
160#define AHCI_REG_PORT_IE 0x14
161#define AHCI_REG_PORT_CMD 0x18
162# define AHCI_REG_PORT_CMD_ST RT_BIT_32(0)
163# define AHCI_REG_PORT_CMD_FRE RT_BIT_32(4)
164# define AHCI_REG_PORT_CMD_FR RT_BIT_32(14)
165# define AHCI_REG_PORT_CMD_CR RT_BIT_32(15)
166#define AHCI_REG_PORT_TFD 0x20
167#define AHCI_REG_PORT_SIG 0x24
168#define AHCI_REG_PORT_SSTS 0x28
169#define AHCI_REG_PORT_SCTL 0x2c
170#define AHCI_REG_PORT_SERR 0x30
171#define AHCI_REG_PORT_SACT 0x34
172#define AHCI_REG_PORT_CI 0x38
173
174/** Returns the absolute register offset from a given port and port register. */
175#define AHCI_PORT_REG(port, reg) ((uint32_t)(AHCI_HBA_SIZE + (port) * AHCI_PORT_SIZE + (reg)))
176
177#define AHCI_REG_IDX 0
178#define AHCI_REG_DATA 4
179
180/** Writes the given value to a AHCI register. */
181#define AHCI_WRITE_REG(iobase, reg, val) \
182 outpd((iobase) + AHCI_REG_IDX, (uint32_t)(reg)); \
183 outpd((iobase) + AHCI_REG_DATA, (uint32_t)(val))
184
185/** Reads from a AHCI register. */
186#define AHCI_READ_REG(iobase, reg, val) \
187 outpd((iobase) + AHCI_REG_IDX, (uint32_t)(reg)); \
188 (val) = inpd((iobase) + AHCI_REG_DATA)
189
190/** Writes to the given port register. */
191#define VBOXAHCI_PORT_WRITE_REG(iobase, port, reg, val) \
192 AHCI_WRITE_REG((iobase), AHCI_PORT_REG((port), (reg)), val)
193
194/** Reads from the given port register. */
195#define VBOXAHCI_PORT_READ_REG(iobase, port, reg, val) \
196 AHCI_READ_REG((iobase), AHCI_PORT_REG((port), (reg)), val)
197
198#define ATA_CMD_IDENTIFY_DEVICE 0xEC
199#define AHCI_CMD_READ_DMA_EXT 0x25
200#define AHCI_CMD_WRITE_DMA_EXT 0x35
201
202
203/* Warning: Destroys high bits of EAX. */
204uint32_t inpd(uint16_t port);
205#pragma aux inpd = \
206 ".386" \
207 "in eax, dx" \
208 "mov dx, ax" \
209 "shr eax, 16" \
210 "xchg ax, dx" \
211 parm [dx] value [dx ax] modify nomemory;
212
213void outpd(uint16_t port, uint32_t val);
214#pragma aux outpd = \
215 ".386" \
216 "xchg ax, cx" \
217 "shl eax, 16" \
218 "mov ax, cx" \
219 "out dx, eax" \
220 parm [dx] [cx ax] modify nomemory;
221
222
223/**
224 * Sets a given set of bits in a register.
225 */
226static void ahci_ctrl_set_bits(uint16_t iobase, uint32_t reg, uint32_t mask)
227{
228 outpd(iobase + AHCI_REG_IDX, reg);
229 outpd(iobase + AHCI_REG_DATA, inpd(iobase + AHCI_REG_DATA) | mask);
230}
231
232/**
233 * Clears a given set of bits in a register.
234 */
235static void ahci_ctrl_clear_bits(uint16_t iobase, uint32_t reg, uint32_t mask)
236{
237 outpd(iobase + AHCI_REG_IDX, reg);
238 outpd(iobase + AHCI_REG_DATA, inpd(iobase + AHCI_REG_DATA) & ~mask);
239}
240
241/**
242 * Returns whether at least one of the bits in the given mask is set
243 * for a register.
244 */
245static uint8_t ahci_ctrl_is_bit_set(uint16_t iobase, uint32_t reg, uint32_t mask)
246{
247 outpd(iobase + AHCI_REG_IDX, reg);
248 return (inpd(iobase + AHCI_REG_DATA) & mask) != 0;
249}
250
251/**
252 * Extracts a range of bits from a register and shifts them
253 * to the right.
254 */
255static uint16_t ahci_ctrl_extract_bits(uint32_t val, uint32_t mask, uint8_t shift)
256{
257 return (val & mask) >> shift;
258}
259
260/**
261 * Converts a segment:offset pair into a 32bit physical address.
262 */
263static uint32_t ahci_addr_to_phys(void __far *ptr)
264{
265 return ((uint32_t)FP_SEG(ptr) << 4) + FP_OFF(ptr);
266}
267
268/**
269 * Issues a command to the SATA controller and waits for completion.
270 */
271static void ahci_port_cmd_sync(uint16_t ahci_seg, uint16_t u16IoBase, bx_bool fWrite,
272 bx_bool fAtapi, uint8_t cFisDWords, uint16_t cbData)
273{
274 uint8_t u8Port;
275 ahci_t __far *ahci = ahci_seg :> 0;
276
277 u8Port = ahci->port;
278
279 if (u8Port != 0xff)
280 {
281 uint32_t u32Val;
282
283 /* Prepare the command header. */
284 u32Val = (1L << 16) | RT_BIT_32(7);
285 if (fWrite)
286 u32Val |= RT_BIT_32(6);
287
288 if (fAtapi)
289 u32Val |= RT_BIT_32(5);
290
291 u32Val |= cFisDWords;
292
293 ahci->aCmdHdr[0] = u32Val;
294 ahci->aCmdHdr[1] = cbData;
295 ahci->aCmdHdr[2] = ahci_addr_to_phys(&ahci->abCmd[0]);
296
297 /* Enable Command and FIS receive engine. */
298 ahci_ctrl_set_bits(u16IoBase, AHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
299 AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_ST);
300
301 /* Queue command. */
302 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_CI, 0x1);
303
304 /* Wait for a D2H FIS. */
305 VBOXAHCI_DEBUG("AHCI: Waiting for D2H FIS\n");
306 while (ahci_ctrl_is_bit_set(u16IoBase, AHCI_PORT_REG(u8Port, AHCI_REG_PORT_IS),
307 AHCI_REG_PORT_IS_DHRS) == 0)
308 {
309 // This is where we'd need some kind of a yield functionality...
310 }
311
312 ahci_ctrl_set_bits(u16IoBase, AHCI_PORT_REG(u8Port, AHCI_REG_PORT_IS),
313 AHCI_REG_PORT_IS_DHRS); /* Acknowledge received D2H FIS. */
314
315 /* Disable command engine. */
316 ahci_ctrl_clear_bits(u16IoBase, AHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
317 AHCI_REG_PORT_CMD_ST);
318
319 /** @todo: Examine status. */
320 }
321 else
322 VBOXAHCI_DEBUG("AHCI: Invalid port given\n");
323}
324
325/**
326 * Issue command to device.
327 */
328//@todo: don't chop up arguments into bytes?!
329static void ahci_cmd_data(uint16_t ahci_seg, uint16_t u16IoBase, uint8_t u8Cmd, uint8_t u8Feat, uint8_t u8Device, uint8_t u8CylHigh, uint8_t u8CylLow, uint8_t u8Sect,
330 uint8_t u8FeatExp, uint8_t u8CylHighExp, uint8_t u8CylLowExp, uint8_t u8SectExp, uint8_t u8SectCount, uint8_t u8SectCountExp,
331 void __far *buf, uint16_t cbData, bx_bool fWrite)
332{
333 ahci_t __far *ahci = ahci_seg :> 0;
334
335 _fmemset(&ahci->abCmd[0], 0, sizeof(ahci->abCmd));
336
337 /* Prepare the FIS. */
338 ahci->abCmd[0] = 0x27; /* FIS type H2D. */
339 ahci->abCmd[1] = 1 << 7; /* Command update. */
340 ahci->abCmd[2] = u8Cmd;
341 ahci->abCmd[3] = u8Feat;
342
343 ahci->abCmd[4] = u8Sect;
344 ahci->abCmd[5] = u8CylLow;
345 ahci->abCmd[6] = u8CylHigh;
346 ahci->abCmd[7] = u8Device;
347
348 ahci->abCmd[8] = u8SectExp;
349 ahci->abCmd[9] = u8CylLowExp;
350 ahci->abCmd[10] = u8CylHighExp;
351 ahci->abCmd[11] = u8FeatExp;
352
353 ahci->abCmd[12] = u8SectCount;
354 ahci->abCmd[13] = u8SectCountExp;
355
356 /* Lock memory needed for DMA. */
357 ahci->edds.num_avail = NUM_EDDS_SG;
358 vds_build_sg_list( &ahci->edds, buf, cbData );
359
360 /* Set up the PRDT. */
361 ahci->aPrdt[0].phys_addr = ahci->edds.u.sg[0].phys_addr;
362 ahci->aPrdt[0].len = ahci->edds.u.sg[0].size - 1;
363
364 ahci_port_cmd_sync(ahci_seg, u16IoBase, fWrite, 0, 5, cbData);
365
366 /* Unlock the buffer again. */
367 vds_free_sg_list( &ahci->edds );
368}
369
370/**
371 * Deinits the curent active port.
372 */
373static void ahci_port_deinit_current(uint16_t ahci_seg, uint16_t u16IoBase)
374{
375 uint8_t u8Port;
376 ahci_t __far *ahci = ahci_seg :> 0;
377
378 u8Port = ahci->port;
379
380 if (u8Port != 0xff)
381 {
382 /* Put the port into an idle state. */
383 ahci_ctrl_clear_bits(u16IoBase, AHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
384 AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_ST);
385
386 while (ahci_ctrl_is_bit_set(u16IoBase, AHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
387 AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_ST | AHCI_REG_PORT_CMD_FR | AHCI_REG_PORT_CMD_CR) == 1)
388 {
389 VBOXAHCI_DEBUG("AHCI: Waiting for the port to idle\n");
390 }
391
392 /*
393 * Port idles, set up memory for commands and received FIS and program the
394 * address registers.
395 */
396 //@todo: merge memsets?
397 _fmemset(&ahci->aCmdHdr[0], 0, sizeof(ahci->aCmdHdr));
398 _fmemset(&ahci->abCmd[0], 0, sizeof(ahci->abCmd));
399 _fmemset(&ahci->abFisRecv[0], 0, sizeof(ahci->abFisRecv));
400
401 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_FB, 0);
402 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_FBU, 0);
403
404 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_CLB, 0);
405 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_CLBU, 0);
406
407 /* Disable all interrupts. */
408 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_IE, 0);
409
410 ahci->port = 0xff;
411 }
412}
413
414/**
415 * Brings a port into a minimal state to make device detection possible
416 * or to queue requests.
417 */
418static void ahci_port_init(uint16_t ahci_seg, uint16_t u16IoBase, uint8_t u8Port)
419{
420 uint32_t u32PhysAddr;
421 ahci_t __far *ahci = ahci_seg :> 0;
422
423 /* Deinit any other port first. */
424 ahci_port_deinit_current(ahci_seg, u16IoBase);
425
426 /* Put the port into an idle state. */
427 ahci_ctrl_clear_bits(u16IoBase, AHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
428 AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_ST);
429
430 while (ahci_ctrl_is_bit_set(u16IoBase, AHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
431 AHCI_REG_PORT_CMD_FRE | AHCI_REG_PORT_CMD_ST | AHCI_REG_PORT_CMD_FR | AHCI_REG_PORT_CMD_CR) == 1)
432 {
433 VBOXAHCI_DEBUG("AHCI: Waiting for the port to idle\n");
434 }
435
436 /*
437 * Port idles, set up memory for commands and received FIS and program the
438 * address registers.
439 */
440 //@todo: just one memset?
441 _fmemset(&ahci->aCmdHdr[0], 0, sizeof(ahci->aCmdHdr));
442 _fmemset(&ahci->abCmd[0], 0, sizeof(ahci->abCmd));
443 _fmemset(&ahci->abFisRecv[0], 0, sizeof(ahci->abFisRecv));
444
445 u32PhysAddr = ahci_addr_to_phys(&ahci->abFisRecv);
446 VBOXAHCI_DEBUG("AHCI: FIS receive area %lx from %x:%x\n", u32PhysAddr, ahci_seg, &AhciData->abFisRecv);
447 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_FB, u32PhysAddr);
448 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_FBU, 0);
449
450 u32PhysAddr = ahci_addr_to_phys(&ahci->aCmdHdr);
451 VBOXAHCI_DEBUG("AHCI: CMD list area %lx\n", u32PhysAddr);
452 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_CLB, u32PhysAddr);
453 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_CLBU, 0);
454
455 /* Disable all interrupts. */
456 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_IE, 0);
457 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_IS, 0xffffffff);
458 /* Clear all errors. */
459 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_SERR, 0xffffffff);
460
461 ahci->port = u8Port;
462}
463
464/**
465 * Write data to the device.
466 */
467static void ahci_cmd_data_out(uint16_t ahci_seg, uint16_t u16IoBase, uint8_t u8Port, uint8_t u8Cmd, uint32_t u32Lba, uint16_t u16Sectors, uint16_t SegData, uint16_t OffData)
468{
469 uint8_t u8CylLow, u8CylHigh, u8Device, u8Sect, u8CylHighExp, u8CylLowExp, u8SectExp, u8SectCount, u8SectCountExp;
470
471 u8SectCount = (uint8_t)(u16Sectors & 0xff);
472 u8SectCountExp = (uint8_t)((u16Sectors >> 8) & 0xff);;
473 u8Sect = (uint8_t)(u32Lba & 0xff);
474 u8SectExp = (uint8_t)((u32Lba >> 24) & 0xff);
475 u8CylLow = (uint8_t)((u32Lba >> 8) & 0xff);
476 u8CylLowExp = 0;
477 u8CylHigh = (uint8_t)((u32Lba >> 16) & 0xff);
478 u8CylHighExp = 0;
479 u8Device = (1 << 6); /* LBA access */
480
481 ahci_port_init(ahci_seg, u16IoBase, u8Port);
482 ahci_cmd_data(ahci_seg, u16IoBase, u8Cmd, 0, u8Device, u8CylHigh, u8CylLow,
483 u8Sect,0, u8CylHighExp, u8CylLowExp, u8SectExp, u8SectCount,
484 u8SectCountExp, SegData :> OffData, u16Sectors * 512, 1);
485}
486
487
488/**
489 * Read data from the device.
490 */
491static void ahci_cmd_data_in(uint16_t ahci_seg, uint16_t u16IoBase, uint8_t u8Port, uint8_t u8Cmd,
492 uint32_t u32Lba, uint16_t u16Sectors, uint16_t SegData, uint16_t OffData)
493{
494 uint8_t u8CylLow, u8CylHigh, u8Device, u8Sect, u8CylHighExp, u8CylLowExp, u8SectExp, u8SectCount, u8SectCountExp;
495
496 u8SectCount = (uint8_t)(u16Sectors & 0xff);
497 u8SectCountExp = (uint8_t)((u16Sectors >> 8) & 0xff);;
498 u8Sect = (uint8_t)(u32Lba & 0xff);
499 u8SectExp = (uint8_t)((u32Lba >> 24) & 0xff);
500 u8CylLow = (uint8_t)((u32Lba >> 8) & 0xff);
501 u8CylLowExp = 0;
502 u8CylHigh = (uint8_t)((u32Lba >> 16) & 0xff);
503 u8CylHighExp = 0;
504
505 u8Device = (1 << 6); /* LBA access */
506
507 ahci_port_init(ahci_seg, u16IoBase, u8Port);
508 ahci_cmd_data(ahci_seg, u16IoBase, u8Cmd, 0, u8Device, u8CylHigh, u8CylLow,
509 u8Sect, 0, u8CylHighExp, u8CylLowExp, u8SectExp, u8SectCount,
510 u8SectCountExp, SegData :> OffData, u16Sectors * 512, 0);
511#ifdef DMA_WORKAROUND
512 rep_movsw(SegData :> OffData, SegData :> OffData, u16Sectors * 512 / 2);
513#endif
514}
515
516static void ahci_port_detect_device(uint16_t ahci_seg, uint16_t u16IoBase, uint8_t u8Port)
517{
518 uint32_t val;
519
520 ahci_port_init(ahci_seg, u16IoBase, u8Port);
521
522 /* Reset connection. */
523 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_SCTL, 0x01);
524 /*
525 * According to the spec we should wait at least 1msec until the reset
526 * is cleared but this is a virtual controller so we don't have to.
527 */
528 VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_SCTL, 0);
529
530 /* Check if there is a device on the port. */
531 VBOXAHCI_PORT_READ_REG(u16IoBase, u8Port, AHCI_REG_PORT_SSTS, val);
532 if (ahci_ctrl_extract_bits(val, 0xfL, 0) == 0x3L)
533 {
534 uint8_t idxDevice;
535
536 idxDevice = read_byte(ahci_seg, (uint16_t)&AhciData->cDevices);
537 VBOXAHCI_DEBUG("AHCI: Device detected on port %d\n", u8Port);
538
539 if (idxDevice < AHCI_MAX_STORAGE_DEVICES)
540 {
541 /* Device detected, enable FIS receive. */
542 ahci_ctrl_set_bits(u16IoBase, AHCI_PORT_REG(u8Port, AHCI_REG_PORT_CMD),
543 AHCI_REG_PORT_CMD_FRE);
544
545 /* Check signature to determine device type. */
546 VBOXAHCI_PORT_READ_REG(u16IoBase, u8Port, AHCI_REG_PORT_SIG, val);
547 if (val == 0x101L)
548 {
549 uint8_t idxHdCurr;
550 uint32_t cSectors;
551 uint8_t abBuffer[0x0200];
552 uint8_t fRemovable;
553 uint16_t cCylinders, cHeads, cSectorsPerTrack;
554 uint8_t cHardDisksOld;
555 uint8_t idxCmosChsBase;
556
557 idxHdCurr = read_byte(ahci_seg, (uint16_t)&AhciData->cHardDisks);
558 VBOXAHCI_DEBUG("AHCI: Detected hard disk\n");
559
560 /* Identify device. */
561 ahci_cmd_data(ahci_seg, u16IoBase, ATA_CMD_IDENTIFY_DEVICE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &abBuffer, sizeof(abBuffer), 0);
562
563 write_byte(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].port, u8Port);
564
565 fRemovable = *(abBuffer+0) & 0x80 ? 1 : 0;
566 cCylinders = *(uint16_t *)(abBuffer+(1*2)); // word 1
567 cHeads = *(uint16_t *)(abBuffer+(3*2)); // word 3
568 cSectorsPerTrack = *(uint16_t *)(abBuffer+(6*2)); // word 6
569 cSectors = *(uint32_t *)(abBuffer+(60*2)); // word 60 and word 61
570
571 /** @todo update sectors to be a 64 bit number (also lba...). */
572 if (cSectors == 268435455)
573 cSectors = *(uint16_t *)(abBuffer+(100*2)); // words 100 to 103 (someday)
574
575 VBOXAHCI_DEBUG("AHCI: %ld sectors\n", cSectors);
576
577 write_byte(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].device,ATA_DEVICE_HD);
578 write_byte(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].removable, fRemovable);
579 write_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].blksize, 512);
580 write_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].pchs.heads, cHeads);
581 write_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].pchs.cylinders, cCylinders);
582 write_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].pchs.spt, cSectorsPerTrack);
583 write_dword(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].cSectors, cSectors);
584
585 /* Get logical CHS geometry. */
586 switch (idxDevice)
587 {
588 case 0:
589 idxCmosChsBase = 0x40;
590 break;
591 case 1:
592 idxCmosChsBase = 0x48;
593 break;
594 case 2:
595 idxCmosChsBase = 0x50;
596 break;
597 case 3:
598 idxCmosChsBase = 0x58;
599 break;
600 default:
601 idxCmosChsBase = 0;
602 }
603 if (idxCmosChsBase != 0)
604 {
605 cCylinders = inb_cmos(idxCmosChsBase) + (inb_cmos(idxCmosChsBase+1) << 8);
606 cHeads = inb_cmos(idxCmosChsBase+2);
607 cSectorsPerTrack = inb_cmos(idxCmosChsBase+7);
608 }
609 else
610 {
611 cCylinders = 0;
612 cHeads = 0;
613 cSectorsPerTrack = 0;
614 }
615 VBOXAHCI_DEBUG("AHCI: Dev %d LCHS=%d/%d/%d\n",
616 idxDevice, cCylinders, cHeads, cSectorsPerTrack);
617 write_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].lchs.heads, cHeads);
618 write_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].lchs.cylinders, cCylinders);
619 write_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].lchs.spt, cSectorsPerTrack);
620
621 write_byte(ahci_seg, (uint16_t)&AhciData->aHdIdMap[idxHdCurr], idxDevice);
622 idxHdCurr++;
623 write_byte(ahci_seg, (uint16_t)&AhciData->cHardDisks, idxHdCurr);
624
625 /* Update hdcount in the BDA. */
626 cHardDisksOld = read_byte(0x40, 0x75);
627 cHardDisksOld++;
628 write_byte(0x40, 0x75, cHardDisksOld);
629 }
630 else if (val == 0xeb140101)
631 {
632 VBOXAHCI_DEBUG("AHCI: Detected ATAPI device\n");
633 }
634 else
635 VBOXAHCI_DEBUG("AHCI: Ignoring unknown device\n");
636
637 idxDevice++;
638 write_byte(ahci_seg, (uint16_t)&AhciData->cDevices, idxDevice);
639 }
640 else
641 VBOXAHCI_DEBUG("AHCI: Reached maximum device count, skipping\n");
642 }
643}
644
645#define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
646
647/**
648 * Int 13 handler.
649 */
650void BIOSCALL ahci_int13(volatile uint16_t RET, volatile uint16_t ES, volatile uint16_t DS, volatile uint16_t DI,
651 volatile uint16_t SI, volatile uint16_t BP, volatile uint16_t SP, volatile uint16_t BX,
652 volatile uint16_t DX, volatile uint16_t CX, volatile uint16_t AX, volatile uint16_t IPIRET,
653 volatile uint16_t CSIRET, volatile uint16_t FLAGSIRET, volatile uint16_t IP,
654 volatile uint16_t CS, volatile uint16_t FLAGS)
655{
656 uint16_t ebda_seg;
657 uint16_t ahci_seg, u16IoBase;
658 uint8_t idxDevice;
659 uint8_t old_disks;
660 uint8_t u8Port;
661
662 uint32_t lba;
663 uint16_t cylinder, head, sector;
664 uint16_t segment, offset;
665 uint16_t npc, nph, npspt, nlc, nlh, nlspt;
666 uint16_t count;
667// uint16_t size;
668 uint8_t status;
669
670 VBOXAHCI_INT13_DEBUG("ahci_int13 AX=%x CX=%x DX=%x BX=%x ES=%x SP=%x BP=%x SI=%x DI=%x IP=%x CS=%x FLAGS=%x\n",
671 AX, CX, DX, BX, ES, SP, BP, SI, DI, IP, CS, FLAGS);
672
673 ebda_seg = read_word(0x0040, 0x000E);
674 ahci_seg = read_word(ebda_seg, (uint16_t)&EbdaData->ahci_seg);
675 u16IoBase = read_word(ahci_seg, (uint16_t)&AhciData->iobase);
676 old_disks = read_byte(ahci_seg, (uint16_t)&AhciData->cHardDisksOld);
677 VBOXAHCI_INT13_DEBUG("ahci_int13: ahci_seg=%x u16IoBase=%x old_disks=%d\n", ahci_seg, u16IoBase, old_disks);
678
679 /* Check if the device is controlled by us first. */
680 if ( (GET_DL() < 0x80)
681 || (GET_DL() < 0x80 + old_disks)
682 || ((GET_DL() & 0xe0) != 0x80) /* No CD-ROM drives supported for now */)
683 {
684 VBOXAHCI_INT13_DEBUG("ahci_int13: device not controlled by us, forwarding to old handler (%d)\n", old_disks);
685 /* Fill the iret frame to jump to the old handler. */
686 IPIRET = read_word(ahci_seg, (uint16_t)&AhciData->pfnInt13Old);
687 CSIRET = 0xf000;
688 FLAGSIRET = FLAGS;
689 RET = 1;
690 return;
691 }
692
693 //@todo: pre-init aHdIdMap!
694 idxDevice = read_byte(ahci_seg, (uint16_t)&AhciData->aHdIdMap[GET_DL() - 0x80 - old_disks]);
695
696 if (idxDevice >= AHCI_MAX_STORAGE_DEVICES)
697 {
698 VBOXAHCI_INT13_DEBUG("ahci_int13: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_DL());
699 goto ahci_int13_fail;
700 }
701
702 u8Port = read_byte(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].port);
703
704 switch (GET_AH())
705 {
706 case 0x00: /* disk controller reset */
707 {
708 /** @todo: not really important I think. */
709 goto ahci_int13_success;
710 break;
711 }
712 case 0x01: /* read disk status */
713 {
714 status = read_byte(0x0040, 0x0074);
715 SET_AH(status);
716 SET_DISK_RET_STATUS(0);
717 /* set CF if error status read */
718 if (status)
719 goto ahci_int13_fail_nostatus;
720 else
721 goto ahci_int13_success_noah;
722 break;
723 }
724 case 0x02: // read disk sectors
725 case 0x03: // write disk sectors
726 case 0x04: // verify disk sectors
727 {
728 count = GET_AL();
729 cylinder = GET_CH();
730 cylinder |= ( ((uint16_t) GET_CL()) << 2) & 0x300;
731 sector = (GET_CL() & 0x3f);
732 head = GET_DH();
733
734 segment = ES;
735 offset = BX;
736
737 if ((count > 128) || (count == 0))
738 {
739 BX_INFO("ahci_int13: function %02x, count out of range!\n",GET_AH());
740 goto ahci_int13_fail;
741 }
742
743 nlc = read_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].lchs.cylinders);
744 nlh = read_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].lchs.heads);
745 nlspt = read_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].lchs.spt);
746
747 // sanity check on cyl heads, sec
748 if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt ))
749 {
750 BX_INFO("ahci_int13: function %02x, disk %02x, idx %02x, parameters out of range %04x/%04x/%04x!\n",
751 GET_AH(), GET_DL(), idxDevice, cylinder, head, sector);
752 goto ahci_int13_fail;
753 }
754
755 // FIXME verify
756 if ( GET_AH() == 0x04 )
757 goto ahci_int13_success;
758
759 lba = ((((uint32_t)cylinder * (uint32_t)nlh) + (uint32_t)head) * (uint32_t)nlspt) + (uint32_t)sector - 1;
760
761 status = 0;
762 if ( GET_AH() == 0x02 )
763 ahci_cmd_data_in(ahci_seg, u16IoBase, u8Port, AHCI_CMD_READ_DMA_EXT, lba, count, segment, offset);
764 else
765 ahci_cmd_data_out(ahci_seg, u16IoBase, u8Port, AHCI_CMD_WRITE_DMA_EXT, lba, count, segment, offset);
766
767 // Set nb of sector transferred
768 SET_AL(read_word(ebda_seg, (uint16_t)&EbdaData->bdisk.drqp.trsfsectors));
769
770 if (status != 0)
771 {
772 BX_INFO("ahci_int13: function %02x, error %02x !\n",GET_AH(),status);
773 SET_AH(0x0c);
774 goto ahci_int13_fail_noah;
775 }
776
777 goto ahci_int13_success;
778 break;
779 }
780 case 0x05: /* format disk track */
781 BX_INFO("format disk track called\n");
782 goto ahci_int13_success;
783 break;
784 case 0x08: /* read disk drive parameters */
785 {
786 // Get logical geometry from table
787 nlc = read_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].lchs.cylinders);
788 nlh = read_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].lchs.heads);
789 nlspt = read_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].lchs.spt);
790
791 count = read_byte(ahci_seg, (uint16_t)&AhciData->cHardDisks); /** @todo correct? */
792 /* Maximum cylinder number is just one less than the number of cylinders. */
793 nlc = nlc - 1; /* 0 based , last sector not used */
794 SET_AL(0);
795 SET_CH(nlc & 0xff);
796 SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
797 SET_DH(nlh - 1);
798 SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
799 // FIXME should set ES & DI
800 goto ahci_int13_success;
801 break;
802 }
803 case 0x10: /* check drive ready */
804 {
805 /** @todo */
806 goto ahci_int13_success;
807 break;
808 }
809 case 0x15: /* read disk drive size */
810 {
811 // Get physical geometry from table
812 npc = read_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].pchs.cylinders);
813 nph = read_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].pchs.heads);
814 npspt = read_word(ahci_seg, (uint16_t)&AhciData->aDevices[idxDevice].pchs.spt);
815
816 // Compute sector count seen by int13
817 lba = (uint32_t)npc * (uint32_t)nph * (uint32_t)npspt;
818 CX = lba >> 16;
819 DX = lba & 0xffff;
820
821 SET_AH(3); // hard disk accessible
822 goto ahci_int13_success_noah;
823 break;
824 }
825#if 0
826 case 0x41: // IBM/MS installation check
827 {
828 BX=0xaa55; // install check
829 SET_AH(0x30); // EDD 3.0
830 CX=0x0007; // ext disk access and edd, removable supported
831 goto ahci_int13_success_noah;
832 break;
833 }
834 case 0x42: // IBM/MS extended read
835 case 0x43: // IBM/MS extended write
836 case 0x44: // IBM/MS verify
837 case 0x47: // IBM/MS extended seek
838 {
839 count=read_word(DS, SI+(uint16_t)&Int13Ext->count);
840 segment=read_word(DS, SI+(uint16_t)&Int13Ext->segment);
841 offset=read_word(DS, SI+(uint16_t)&Int13Ext->offset);
842
843 // Can't use 64 bits lba
844 lba=read_dword(DS, SI+(uint16_t)&Int13Ext->lba2);
845 if (lba != 0L)
846 {
847 BX_PANIC("ahci_int13: function %02x. Can't use 64bits lba\n",GET_AH());
848 goto ahci_int13_fail;
849 }
850
851 // Get 32 bits lba and check
852 lba=read_dword(DS, SI+(uint16_t)&Int13Ext->lba1);
853
854 if (lba >= read_word(ahci_seg, &AhciData->aDevices[idxDevice].cSectors) )
855 {
856 BX_INFO("ahci_int13: function %02x. LBA out of range\n",GET_AH());
857 goto ahci_int13_fail;
858 }
859
860 // If verify or seek
861 if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
862 goto ahci_int13_success;
863
864 // Execute the command
865 if ( GET_AH() == 0x42 )
866 {
867 ...
868 }
869 else
870 {
871 ...
872 }
873
874 count=read_word(ebda_seg, &EbdaData->bdisk.drqp.trsfsectors);
875 write_word(DS, SI+(uint16_t)&Int13Ext->count, count);
876
877 if (status != 0)
878 {
879 BX_INFO("ahci_int13: function %02x, error %02x !\n",GET_AH(),status);
880 SET_AH(0x0c);
881 goto ahci_int13_fail_noah;
882 }
883 goto ahci_int13_success;
884 break;
885 }
886 case 0x45: // IBM/MS lock/unlock drive
887 case 0x49: // IBM/MS extended media change
888 goto ahci_int13_success; // Always success for HD
889 break;
890 case 0x46: // IBM/MS eject media
891 SET_AH(0xb2); // Volume Not Removable
892 goto ahci_int13_fail_noah; // Always fail for HD
893 break;
894
895 case 0x48: // IBM/MS get drive parameters
896 size=read_word(DS,SI+(uint16_t)&Int13DPT->size);
897
898 // Buffer is too small
899 if(size < 0x1a)
900 goto ahci_int13_fail;
901
902 // EDD 1.x
903 if(size >= 0x1a)
904 {
905 uint16_t blksize;
906
907 npc = read_word(ahci_seg, &AhciData->aDevices[idxDevice].pchs.cylinders);
908 nph = read_word(ahci_seg, &AhciData->aDevices[idxDevice].pchs.heads);
909 npspt = read_word(ahci_seg, &AhciData->aDevices[idxDevice].pchs.spt);
910 lba = read_dword(ahci_seg, &AhciData->aDevices[idxDevice].cSectors);
911 blksize = read_word(ahci_seg, &AhciData->aDevices[idxDevice].blksize);
912
913 write_word(DS, SI+(uint16_t)&Int13DPT->size, 0x1a);
914 write_word(DS, SI+(uint16_t)&Int13DPT->infos, 0x02); // geometry is valid
915 write_dword(DS, SI+(uint16_t)&Int13DPT->cylinders, (uint32_t)npc);
916 write_dword(DS, SI+(uint16_t)&Int13DPT->heads, (uint32_t)nph);
917 write_dword(DS, SI+(uint16_t)&Int13DPT->spt, (uint32_t)npspt);
918 write_dword(DS, SI+(uint16_t)&Int13DPT->sector_count1, lba); // FIXME should be Bit64
919 write_dword(DS, SI+(uint16_t)&Int13DPT->sector_count2, 0L);
920 write_word(DS, SI+(uint16_t)&Int13DPT->blksize, blksize);
921 }
922
923#if 0 /* Disable EDD 2.X and 3.x for now, don't know if it is required by any OS loader yet */
924 // EDD 2.x
925 if(size >= 0x1e)
926 {
927 uint8_t channel, dev, irq, mode, checksum, i, translation;
928 uint16_t iobase1, iobase2, options;
929
930 translation = ATA_TRANSLATION_LBA;
931
932 write_word(DS, SI+(uint16_t)&Int13DPT->size, 0x1e);
933
934 write_word(DS, SI+(uint16_t)&Int13DPT->dpte_segment, ebda_seg);
935 write_word(DS, SI+(uint16_t)&Int13DPT->dpte_offset, &EbdaData->bdisk.dpte);
936
937 // Fill in dpte
938 channel = device / 2;
939 iobase1 = read_word(ebda_seg, &EbdaData->bdisk.channels[channel].iobase1);
940 iobase2 = read_word(ebda_seg, &EbdaData->bdisk.channels[channel].iobase2);
941 irq = read_byte(ebda_seg, &EbdaData->bdisk.channels[channel].irq);
942 mode = read_byte(ebda_seg, &EbdaData->bdisk.devices[device].mode);
943 translation = read_byte(ebda_seg, &EbdaData->bdisk.devices[device].translation);
944
945 options = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
946 options |= (1<<4); // lba translation
947 options |= (mode==ATA_MODE_PIO32?1:0<<7);
948 options |= (translation==ATA_TRANSLATION_LBA?1:0<<9);
949 options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9);
950
951 write_word(ebda_seg, &EbdaData->bdisk.dpte.iobase1, iobase1);
952 write_word(ebda_seg, &EbdaData->bdisk.dpte.iobase2, iobase2);
953 //write_byte(ebda_seg, &EbdaData->bdisk.dpte.prefix, (0xe | /*(device % 2))<<4*/ );
954 write_byte(ebda_seg, &EbdaData->bdisk.dpte.unused, 0xcb );
955 write_byte(ebda_seg, &EbdaData->bdisk.dpte.irq, irq );
956 write_byte(ebda_seg, &EbdaData->bdisk.dpte.blkcount, 1 );
957 write_byte(ebda_seg, &EbdaData->bdisk.dpte.dma, 0 );
958 write_byte(ebda_seg, &EbdaData->bdisk.dpte.pio, 0 );
959 write_word(ebda_seg, &EbdaData->bdisk.dpte.options, options);
960 write_word(ebda_seg, &EbdaData->bdisk.dpte.reserved, 0);
961 write_byte(ebda_seg, &EbdaData->bdisk.dpte.revision, 0x11);
962
963 checksum=0;
964 for (i=0; i<15; i++)
965 checksum+=read_byte(ebda_seg, (&EbdaData->bdisk.dpte) + i);
966
967 checksum = -checksum;
968 write_byte(ebda_seg, &EbdaData->bdisk.dpte.checksum, checksum);
969 }
970
971 // EDD 3.x
972 if(size >= 0x42)
973 {
974 uint8_t channel, iface, checksum, i;
975 uint16_t iobase1;
976
977 channel = device / 2;
978 iface = read_byte(ebda_seg, &EbdaData->bdisk.channels[channel].iface);
979 iobase1 = read_word(ebda_seg, &EbdaData->bdisk.channels[channel].iobase1);
980
981 write_word(DS, SI+(uint16_t)&Int13DPT->size, 0x42);
982 write_word(DS, SI+(uint16_t)&Int13DPT->key, 0xbedd);
983 write_byte(DS, SI+(uint16_t)&Int13DPT->dpi_length, 0x24);
984 write_byte(DS, SI+(uint16_t)&Int13DPT->reserved1, 0);
985 write_word(DS, SI+(uint16_t)&Int13DPT->reserved2, 0);
986
987 if (iface==ATA_IFACE_ISA) {
988 write_byte(DS, SI+(uint16_t)&Int13DPT->host_bus[0], 'I');
989 write_byte(DS, SI+(uint16_t)&Int13DPT->host_bus[1], 'S');
990 write_byte(DS, SI+(uint16_t)&Int13DPT->host_bus[2], 'A');
991 write_byte(DS, SI+(uint16_t)&Int13DPT->host_bus[3], 0);
992 }
993 else {
994 // FIXME PCI
995 }
996 write_byte(DS, SI+(uint16_t)&Int13DPT->iface_type[0], 'A');
997 write_byte(DS, SI+(uint16_t)&Int13DPT->iface_type[1], 'T');
998 write_byte(DS, SI+(uint16_t)&Int13DPT->iface_type[2], 'A');
999 write_byte(DS, SI+(uint16_t)&Int13DPT->iface_type[3], 0);
1000
1001 if (iface==ATA_IFACE_ISA) {
1002 write_word(DS, SI+(uint16_t)&Int13DPT->iface_path[0], iobase1);
1003 write_word(DS, SI+(uint16_t)&Int13DPT->iface_path[2], 0);
1004 write_dword(DS, SI+(uint16_t)&Int13DPT->iface_path[4], 0L);
1005 }
1006 else {
1007 // FIXME PCI
1008 }
1009 //write_byte(DS, SI+(uint16_t)&Int13DPT->device_path[0], device%2);
1010 write_byte(DS, SI+(uint16_t)&Int13DPT->device_path[1], 0);
1011 write_word(DS, SI+(uint16_t)&Int13DPT->device_path[2], 0);
1012 write_dword(DS, SI+(uint16_t)&Int13DPT->device_path[4], 0L);
1013
1014 checksum=0;
1015 for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
1016 checksum = -checksum;
1017 write_byte(DS, SI+(uint16_t)&Int13DPT->checksum, checksum);
1018 }
1019#endif
1020 goto ahci_int13_success;
1021 break;
1022 case 0x4e: // // IBM/MS set hardware configuration
1023 // DMA, prefetch, PIO maximum not supported
1024 switch (GET_AL())
1025 {
1026 case 0x01:
1027 case 0x03:
1028 case 0x04:
1029 case 0x06:
1030 goto ahci_int13_success;
1031 break;
1032 default :
1033 goto ahci_int13_fail;
1034 }
1035 break;
1036#endif
1037 case 0x09: /* initialize drive parameters */
1038 case 0x0c: /* seek to specified cylinder */
1039 case 0x0d: /* alternate disk reset */
1040 case 0x11: /* recalibrate */
1041 case 0x14: /* controller internal diagnostic */
1042 BX_INFO("ahci_int13: function %02xh unimplemented, returns success\n", GET_AH());
1043 goto ahci_int13_success;
1044 break;
1045
1046 case 0x0a: /* read disk sectors with ECC */
1047 case 0x0b: /* write disk sectors with ECC */
1048 case 0x18: // set media type for format
1049 case 0x50: // IBM/MS send packet command
1050 default:
1051 BX_INFO("ahci_int13: function %02xh unsupported, returns fail\n", GET_AH());
1052 goto ahci_int13_fail;
1053 break;
1054 }
1055
1056 //@todo: this is really badly written, reuse the code more!
1057ahci_int13_fail:
1058 SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
1059ahci_int13_fail_noah:
1060 SET_DISK_RET_STATUS(GET_AH());
1061ahci_int13_fail_nostatus:
1062 VBOXAHCI_INT13_DEBUG("ahci_int13: done, AH=%02x\n", GET_AH());
1063 SET_CF(); // error occurred
1064 return;
1065
1066ahci_int13_success:
1067 SET_AH(0x00); // no error
1068ahci_int13_success_noah:
1069 SET_DISK_RET_STATUS(0x00);
1070 VBOXAHCI_INT13_DEBUG("ahci_int13: done, AH=%02x\n", GET_AH());
1071 CLEAR_CF(); // no error
1072 return;
1073}
1074
1075#undef SET_DISK_RET_STATUS
1076
1077/* Defined in assembler code. */
1078extern void ahci_int13_handler(void);
1079#pragma aux ahci_int13_handler "*";
1080
1081/**
1082 * Install the in13 interrupt handler
1083 * preserving the previous one.
1084 */
1085static void ahci_install_int_handler(uint16_t ahci_seg)
1086{
1087
1088 uint16_t pfnInt13Old;
1089
1090 VBOXAHCI_DEBUG("AHCI: Hooking int 13h vector\n");
1091
1092 /* Read the old interrupt handler. */
1093 pfnInt13Old = read_word(0x0000, 0x0013*4);
1094 write_word(ahci_seg, (uint16_t)&AhciData->pfnInt13Old, pfnInt13Old);
1095
1096 /* Set our own */
1097 write_word(0x0000, 0x0013*4, (uint16_t)ahci_int13_handler);
1098}
1099
1100/**
1101 * Allocates 1K from the base memory.
1102 */
1103static uint16_t ahci_mem_alloc(void)
1104{
1105 uint16_t base_mem_kb;
1106 uint16_t ahci_seg;
1107
1108 base_mem_kb = read_word(0x00, 0x0413);
1109
1110 VBOXAHCI_DEBUG("AHCI: %dK of base mem\n", base_mem_kb);
1111
1112 if (base_mem_kb == 0)
1113 return 0;
1114
1115 base_mem_kb--; /* Allocate one block. */
1116 ahci_seg = (((uint32_t)base_mem_kb * 1024) >> 4); /* Calculate start segment. */
1117
1118 write_word(0x00, 0x0413, base_mem_kb);
1119
1120 return ahci_seg;
1121}
1122
1123/**
1124 * Initializes the AHCI HBA and detects attached devices.
1125 */
1126static int ahci_hba_init(uint16_t u16IoBase)
1127{
1128 uint8_t i, cPorts;
1129 uint32_t val;
1130 uint16_t ebda_seg;
1131 uint16_t ahci_seg;
1132
1133 ebda_seg = read_word(0x0040, 0x000E);
1134
1135 AHCI_READ_REG(u16IoBase, AHCI_REG_VS, val);
1136 VBOXAHCI_DEBUG("AHCI: Controller has version: 0x%x (major) 0x%x (minor)\n",
1137 ahci_ctrl_extract_bits(val, 0xffff0000, 16),
1138 ahci_ctrl_extract_bits(val, 0x0000ffff, 0));
1139
1140 /* Allocate 1K of base memory. */
1141 ahci_seg = ahci_mem_alloc();
1142 if (ahci_seg == 0)
1143 {
1144 VBOXAHCI_DEBUG("AHCI: Could not allocate 1K of memory, can't boot from controller\n");
1145 return 0;
1146 }
1147 VBOXAHCI_DEBUG("AHCI: ahci_seg=%04x, size=%04x, pointer at EBDA:%04x (EBDA size=%04x)\n",
1148 ahci_seg, sizeof(ahci_t), (uint16_t)&EbdaData->ahci_seg, sizeof(ebda_data_t));
1149
1150 write_word(ebda_seg, (uint16_t)&EbdaData->ahci_seg, ahci_seg);
1151 write_byte(ahci_seg, (uint16_t)&AhciData->port, 0xff);
1152 write_word(ahci_seg, (uint16_t)&AhciData->iobase, u16IoBase);
1153 write_byte(ahci_seg, (uint16_t)&AhciData->cHardDisksOld, read_byte(0x40, 0x75));
1154
1155 /* Reset the controller. */
1156 ahci_ctrl_set_bits(u16IoBase, AHCI_REG_GHC, AHCI_GHC_HR);
1157 do
1158 {
1159 AHCI_READ_REG(u16IoBase, AHCI_REG_GHC, val);
1160 } while (val & AHCI_GHC_HR != 0);
1161
1162 AHCI_READ_REG(u16IoBase, AHCI_REG_CAP, val);
1163 cPorts = ahci_ctrl_extract_bits(val, 0x1f, 0) + 1; /* Extract number of ports.*/
1164
1165 VBOXAHCI_DEBUG("AHCI: HBA has %u ports\n", cPorts);
1166
1167 /* Go through the ports. */
1168 i = 0;
1169 while (i < 32)
1170 {
1171 if (ahci_ctrl_is_bit_set(u16IoBase, AHCI_REG_PI, RT_BIT_32(i)) != 0)
1172 {
1173 VBOXAHCI_DEBUG("AHCI: Port %u is present\n", i);
1174 ahci_port_detect_device(ahci_seg, u16IoBase, i);
1175 cPorts--;
1176 if (cPorts == 0)
1177 break;
1178 }
1179 i++;
1180 }
1181
1182 if (read_byte(ahci_seg, (uint16_t)&AhciData->cDevices) > 0)
1183 {
1184 /*
1185 * Init completed and there is at least one device present.
1186 * Install our int13 handler.
1187 */
1188 ahci_install_int_handler(ahci_seg);
1189 }
1190
1191 return 0;
1192}
1193
1194/**
1195 * Init the AHCI driver and detect attached disks.
1196 */
1197void BIOSCALL ahci_init(void)
1198{
1199 uint16_t busdevfn;
1200
1201 busdevfn = pci_find_classcode(0x00010601);
1202 if (busdevfn != VBOX_AHCI_NO_DEVICE)
1203 {
1204 uint8_t u8Bus, u8DevFn;
1205 uint8_t u8PciCapOff;
1206
1207 u8Bus = (busdevfn & 0xff00) >> 8;
1208 u8DevFn = busdevfn & 0x00ff;
1209
1210 VBOXAHCI_DEBUG("AHCI HBA at Bus %u DevFn 0x%x (raw 0x%x)\n", u8Bus, u8DevFn, busdevfn);
1211
1212 /* Examine the capability list and search for the Serial ATA Capability Register. */
1213 u8PciCapOff = pci_read_config_byte(u8Bus, u8DevFn, PCI_CONFIG_CAP);
1214
1215 while (u8PciCapOff != 0)
1216 {
1217 uint8_t u8PciCapId = pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff);
1218
1219 VBOXAHCI_DEBUG("Capability ID 0x%x at 0x%x\n", u8PciCapId, u8PciCapOff);
1220
1221 if (u8PciCapId == PCI_CAP_ID_SATACR)
1222 break;
1223
1224 /* Go on to the next capability. */
1225 u8PciCapOff = pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff + 1);
1226 }
1227
1228 if (u8PciCapOff != 0)
1229 {
1230 uint8_t u8Rev;
1231
1232 VBOXAHCI_DEBUG("AHCI HBA with SATA Capability register at 0x%x\n", u8PciCapOff);
1233
1234 /* Advance to the stuff behind the id and next capability pointer. */
1235 u8PciCapOff += 2;
1236
1237 u8Rev = pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff);
1238 if (u8Rev == 0x10)
1239 {
1240 /* Read the SATACR1 register and get the bar and offset of the index/data pair register. */
1241 uint8_t u8Bar = 0x00;
1242 uint16_t u16Off = 0x00;
1243 uint16_t u16BarOff = pci_read_config_word(u8Bus, u8DevFn, u8PciCapOff + 2);
1244
1245 VBOXAHCI_DEBUG("SATACR1: 0x%x\n", u16BarOff);
1246
1247 switch (u16BarOff & 0xf)
1248 {
1249 case 0x04:
1250 u8Bar = 0x10;
1251 break;
1252 case 0x05:
1253 u8Bar = 0x14;
1254 break;
1255 case 0x06:
1256 u8Bar = 0x18;
1257 break;
1258 case 0x07:
1259 u8Bar = 0x1c;
1260 break;
1261 case 0x08:
1262 u8Bar = 0x20;
1263 break;
1264 case 0x09:
1265 u8Bar = 0x24;
1266 break;
1267 case 0x0f:
1268 default:
1269 /* Reserved or unsupported. */
1270 VBOXAHCI_DEBUG("BAR 0x%x unsupported\n", u16BarOff & 0xf);
1271 }
1272
1273 /* Get the offset inside the BAR from bits 4:15. */
1274 u16Off = (u16BarOff >> 4) * 4;
1275
1276 if (u8Bar != 0x00)
1277 {
1278 uint32_t u32Bar = pci_read_config_dword(u8Bus, u8DevFn, u8Bar);
1279
1280 VBOXAHCI_DEBUG("BAR at 0x%x : 0x%x\n", u8Bar, u32Bar);
1281
1282 if ((u32Bar & 0x01) != 0)
1283 {
1284 int rc;
1285 uint16_t u16AhciIoBase = (u32Bar & 0xfff0) + u16Off;
1286
1287 VBOXAHCI_DEBUG("I/O base: 0x%x\n", u16AhciIoBase);
1288 rc = ahci_hba_init(u16AhciIoBase);
1289 }
1290 else
1291 VBOXAHCI_DEBUG("BAR is MMIO\n");
1292 }
1293 }
1294 else
1295 VBOXAHCI_DEBUG("Invalid revision 0x%x\n", u8Rev);
1296 }
1297 else
1298 VBOXAHCI_DEBUG("AHCI HBA with no usable Index/Data register pair!\n");
1299 }
1300 else
1301 VBOXAHCI_DEBUG("No AHCI HBA!\n");
1302}
Note: See TracBrowser for help on using the repository browser.

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