VirtualBox

Changeset 37268 in vbox


Ignore:
Timestamp:
May 30, 2011 9:14:14 PM (14 years ago)
Author:
vboxsync
Message:

BIOS: Updates for the AHCI driver

Location:
trunk/src/VBox/Devices/PC/BIOS
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/PC/BIOS/ahci.c

    r36877 r37268  
    1515 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
    1616 */
     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)
    1777
    1878/** Supported methods of the PCI BIOS. */
     
    76136#define AHCI_REG_PORT_FBU  0x0c
    77137#define AHCI_REG_PORT_IS   0x10
     138# define AHCI_REG_PORT_IS_DHRS RT_BIT_32(0)
    78139#define AHCI_REG_PORT_IE   0x14
    79140#define AHCI_REG_PORT_CMD  0x18
     
    108169/** Writes to the given port register. */
    109170#define VBOXAHCI_PORT_WRITE_REG(iobase, port, reg, val) \
    110     VBOX_AHCI_WRITE_REG((iobase), VBOXAHCI_PORT_REG((port), (reg)), val)
     171    VBOXAHCI_WRITE_REG((iobase), VBOXAHCI_PORT_REG((port), (reg)), val)
    111172
    112173/** Reads from the given port register. */
    113174#define VBOXAHCI_PORT_READ_REG(iobase, port, reg, val) \
    114     VBOX_AHCI_READ_REG((iobase), VBOXAHCI_PORT_REG((port), (reg)), val)
     175    VBOXAHCI_READ_REG((iobase), VBOXAHCI_PORT_REG((port), (reg)), val)
     176
     177#define ATA_CMD_IDENTIFY_DEVICE 0xEC
    115178
    116179/**
     
    498561}
    499562
    500 static int ahci_port_init(u16IoBase, u8Port)
     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;
    501747    Bit16u u16IoBase;
    502748    Bit8u u8Port;
    503749{
     750    Bit32u u32PhysAddr;
     751
     752    /* Deinit any other port first. */
     753    ahci_port_deinit_current(SegAhci, u16IoBase);
    504754
    505755    /* Put the port into an idle state. */
     
    517767     * address registers.
    518768     */
    519 
    520     return -1;
     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;
    521871}
    522872
     
    524874    Bit16u u16IoBase;
    525875{
    526     int rc = 0;
    527876    Bit8u i, cPorts;
    528877    Bit32u val;
     878    Bit16u ebda_seg;
     879    Bit16u SegAhci;
     880
     881    ebda_seg = read_word(0x0040, 0x000E);
    529882
    530883    VBOXAHCI_READ_REG(u16IoBase, AHCI_REG_VS, val);
     
    532885                   ahci_ctrl_extract_bits(val, 0xffff0000, 16),
    533886                   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);
    534898
    535899    /* Reset the controller. */
     
    552916        {
    553917            VBOXAHCI_DEBUG("AHCI: Port %u is present\n", i);
    554             rc = ahci_port_init(u16IoBase, i);
     918            ahci_port_detect_device(SegAhci, u16IoBase, i);
    555919            cPorts--;
    556920            if (cPorts == 0)
     
    560924    }
    561925
    562     return rc;
     926    return 0;
    563927}
    564928
  • trunk/src/VBox/Devices/PC/BIOS/rombios.c

    r37049 r37268  
    778778#endif
    779779
    780 #ifdef VBOX_WITH_BIOS_AHCI
    781   typedef struct {
    782     Bit16u iobase;
    783     } ahci_t;
    784 #endif
    785 
    786780  // for access to EBDA area
    787781  //     The EBDA structure should conform to
     
    813807
    814808#ifdef VBOX_WITH_BIOS_AHCI
    815     ahci_t ahci;
     809    // AHCI driver data segment;
     810    Bit16u SegAhci;
    816811#endif
    817812
     
    12901285  mov  bp, sp
    12911286
    1292     push dx
     1287    push bx
    12931288    mov  dx, 4[bp]
    12941289    in   eax, dx
    1295     pop  dx
     1290    mov bx, ax   ; Save lower 16 bits
     1291    shr eax, #16
     1292    mov dx, ax
     1293    mov ax, bx
     1294    pop bx
    12961295
    12971296  pop  bp
     
    27312730      }
    27322731
     2732#ifdef VBOX
     2733      // we don't want any noisy output for now
     2734#else /* !VBOX */
    27332735      {
    27342736      Bit32u sizeinmb;
     
    27642766        }
    27652767
    2766 #ifdef VBOX
    2767       // we don't want any noisy output for now
    2768 #else /* !VBOX */
    27692768      switch (type) {
    27702769        case ATA_TYPE_ATA:
     
    27852784          break;
    27862785        }
     2786      }
    27872787#endif /* !VBOX */
    2788       }
    27892788    }
    27902789
     
    48514850                         * use the 0xe0000-0xeffff area. It does use the
    48524851                         * 0xd0000-0xdffff area for the BIOS logo, but it's
    4853                          * not worth marking it as reserved. Note that various
     4852                         * not worth marking it as reserved. (this is not
     4853                         * true anymore because the VGA adapter handles the logo stuff)
     4854                         * The whole 0xe0000-0xfffff can be used for the BIOS.
     4855                         * Note that various
    48544856                         * Windows versions don't accept (read: in debug builds
    48554857                         * they trigger the "Too many similar traps" assertion)
Note: See TracChangeset for help on using the changeset viewer.

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