ata.c revision 13fe8ee80c2ad706f6d07067ab94702a9f432a68
/*
* Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
* --------------------------------------------------------------------
*
* This code is based on:
*
*
* Copyright (C) 2002 MandrakeSoft S.A.
*
* MandrakeSoft S.A.
* 43, rue d'Aboukir
* 75002 Paris - France
*
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <stdint.h>
#include <stdarg.h>
#include "inlines.h"
#include "biosint.h"
#include "ebda.h"
#include "ata.h"
#if DEBUG_ATA
#else
# define BX_DEBUG_ATA(...)
#endif
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
#pragma aux insw_discard = \
".286" \
"again:" \
"in ax,dx" \
"loop again" \
#pragma aux insd_discard = \
".386" \
"push eax" \
"again:" \
"in eax,dx" \
"loop again" \
"pop eax" \
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
{
// Channels info init.
}
// Devices info init.
}
// hdidmap and cdidmap init.
}
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ATA-3
// 8.2.1 Software reset - Device 0
{
// Reset
// 8.2.1 (a) -- set SRST in DC
// 8.2.1 (b) -- wait for BSY
max=0xff;
while(--max>0) {
if ((status & ATA_CB_STAT_BSY) != 0)
break;
}
// 8.2.1 (f) -- clear SRST
// 8.2.1 (g) -- check for sc==sn==0x01
// select device
// 8.2.1 (h) -- wait for not BSY
while(--max>0) {
if ((status & ATA_CB_STAT_BSY) == 0)
break;
pdelay=0xffff;
while (--pdelay>0) {
/* nothing */
}
}
}
}
// 8.2.1 (i) -- wait for DRDY
while(--max>0) {
if ((status & ATA_CB_STAT_RDY) != 0)
break;
}
// Enable interrupts
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// returns
// 0 : no error
// 1 : BUSY bit set
// 2 : read error
// 3 : expected DRQ=1
// 6 : no sectors left to write
// 7 : more sectors to write
{
if (blksize == 0) { /* If transfer size is exactly 64K */
if (mode == ATA_MODE_PIO32)
blksize = 0x4000;
else
blksize = 0x8000;
} else {
if (mode == ATA_MODE_PIO32)
blksize >>= 2;
else
blksize >>= 1;
}
if (status & ATA_CB_STAT_BSY)
{
// Enable interrupts
return 1;
}
// sector will be 0 only on lba access. Convert to lba-chs
if (sector == 0) {
{
cylinder = 0; /* The parameter lba is just a 32 bit value. */
/* Leave the bottom 24 bits as is, they are treated correctly by the
* LBA28 code path. */
lba &= 0xffffff;
}
lba >>= 8;
lba >>= 16;
}
count = 1;
} else {
mult_blk_cnt = 1;
}
while (1) {
if ( !(status & ATA_CB_STAT_BSY) )
break;
}
if (status & ATA_CB_STAT_ERR) {
// Enable interrupts
return 2;
} else if ( !(status & ATA_CB_STAT_DRQ) ) {
// Enable interrupts
return 3;
}
int_enable(); // enable higher priority interrupts
while (1) {
// adjust if there will be an overrun. 2K max sector size
if (mode == ATA_MODE_PIO32) {
} else {
}
count--;
while (1) {
if ( !(status & ATA_CB_STAT_BSY) )
break;
}
if (count == 0) {
!= ATA_CB_STAT_RDY ) {
// Enable interrupts
return 4;
}
break;
}
else {
!= (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
// Enable interrupts
return 5;
}
continue;
}
}
// Enable interrupts
return 0;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void BIOSCALL ata_detect(void)
{
#if BX_MAX_ATA_INTERFACES > 0
#endif
#if BX_MAX_ATA_INTERFACES > 1
#endif
#if 0 //@todo - temporarily removed to avoid conflict with AHCI
#if BX_MAX_ATA_INTERFACES > 2
#endif
#if BX_MAX_ATA_INTERFACES > 3
#endif
#endif
#if BX_MAX_ATA_INTERFACES > 4
#endif
// Device detection
// Disable interrupts
// Look for device
// If we found something
// reset the channel
// check for ATA or ATAPI
}
}
}
// Enable interrupts
// Now we send a IDENTIFY command to ATA device
if (type == DSK_TYPE_ATA) {
//Temporary values to do the transfer
BX_PANIC("ata-detect: Failed to detect ATA device\n");
/** @todo update sectors to be a 64 bit number (also lba...). */
switch (device)
{
case 0:
chsgeo_base = 0x1e;
break;
case 1:
chsgeo_base = 0x26;
break;
case 2:
chsgeo_base = 0x67;
break;
case 3:
chsgeo_base = 0x70;
break;
default:
chsgeo_base = 0;
}
if (chsgeo_base)
{
}
else
if (device < 2)
{
if (device == 0)
else
/* Update the DPT for drive 0/1 pointed to by Int41/46. This used
* to be done at POST time with lots of ugly assembler code, which
* isn't worth the effort of converting from AMI to Award CMOS
* format. Just do it here. */
sum = 0;
for (i = 0; i < 0xf; i++)
}
// fill hdidmap
hdcount++;
}
// Now we send an IDENTIFY command to ATAPI device
if (type == DSK_TYPE_ATAPI) {
// Temporary values to do the transfer
BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
blksize = 2048;
// fill cdidmap
cdcount++;
}
{
int i;
switch (type) {
case DSK_TYPE_ATA:
sizeinmb >>= 11;
case DSK_TYPE_ATAPI:
break;
}
// Read model name
for (i = 0; i < 20; i++ ) {
}
// Reformat
for ( i = 39; i > 0; i-- ){
if (*(model+i) == 0x20)
*(model+i) = 0x00;
else
break;
}
break;
}
#ifdef VBOXz
// we don't want any noisy output for now
#else /* !VBOX */
switch (type) {
int c;
case DSK_TYPE_ATA:
i=0;
while(c=*(model+i++))
printf("%c", c);
break;
case DSK_TYPE_ATAPI:
i=0;
while(c=*(model+i++))
printf("%c", c);
else
break;
case DSK_TYPE_UNKNOWN:
break;
}
#endif /* !VBOX */
}
}
// Store the devices counts
#ifdef VBOX
// we don't want any noisy output for now
#else /* !VBOX */
printf("\n");
#endif /* !VBOX */
// FIXME : should use bios=cmos|auto|disable bits
// FIXME : should know about translation bits
// FIXME : move hard_drive_post here
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// returns
// 0 : no error
// 1 : BUSY bit set
// 2 : read error
// 3 : expected DRQ=1
// 6 : no sectors left to write
// 7 : more sectors to write
{
if (mode == ATA_MODE_PIO32)
blksize >>= 2;
else
blksize >>= 1;
if (status & ATA_CB_STAT_BSY)
{
// Enable interrupts
return 1;
}
// sector will be 0 only on lba access. Convert to lba-chs
if (sector == 0) {
{
cylinder = 0; /* The parameter lba is just a 32 bit value. */
/* Leave the bottom 24 bits as is, they are treated correctly by the
* LBA28 code path. */
lba &= 0xffffff;
}
lba >>= 8;
lba >>= 16;
}
while (1) {
if ( !(status & ATA_CB_STAT_BSY) )
break;
}
if (status & ATA_CB_STAT_ERR) {
// Enable interrupts
return 2;
} else if ( !(status & ATA_CB_STAT_DRQ) ) {
// Enable interrupts
return 3;
}
int_enable(); // enable higher priority interrupts
while (1) {
// adjust if there will be an overrun. 2K max sector size
if (mode == ATA_MODE_PIO32) {
} else {
}
count--;
while (1) {
if ( !(status & ATA_CB_STAT_BSY) )
break;
}
if (count == 0) {
if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
!= ATA_CB_STAT_RDY ) {
// Enable interrupts
return 6;
}
break;
}
else {
!= (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
// Enable interrupts
return 7;
}
continue;
}
}
// Enable interrupts
return 0;
}
/**
* Read sectors from an attached ATA device.
*
* @returns status code.
* @param bios_dsk Pointer to disk request packet (in the
* EBDA).
*/
{
int status;
/* CHS addressing. */
} else {
/* LBA addressing. */
else {
}
}
return status;
}
/**
* Write sectors to an attached ATA device.
*
* @returns status code.
* @param bios_dsk Pointer to disk request packet (in the
* EBDA).
*/
{
/* CHS addressing. */
} else {
/* LBA addressing. */
else
}
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// returns
// 0 : no error
// 1 : error in parameters
// 2 : BUSY bit set
// 3 : error
// 4 : not ready
{
// Data out is not supported yet
if (inout == ATA_DATA_OUT) {
return 1;
}
// The header length must be even
if (header & 1) {
return 1;
}
transfer = 0L;
if (cmdlen < 12)
cmdlen = 12;
if (cmdlen > 12)
cmdlen = 16;
cmdlen >>= 1;
// Reset count of transferred data
// @todo: clear in calling code?
if (status & ATA_CB_STAT_BSY)
return 2;
// outb(iobase1 + ATA_CB_FR, 0x00);
// outb(iobase1 + ATA_CB_SC, 0x00);
// outb(iobase1 + ATA_CB_SN, 0x00);
// Device should ok to receive command
while (1) {
if ( !(status & ATA_CB_STAT_BSY) ) break;
}
if (status & ATA_CB_STAT_ERR) {
// Enable interrupts
return 3;
} else if ( !(status & ATA_CB_STAT_DRQ) ) {
// Enable interrupts
return 4;
}
int_enable(); // enable higher priority interrupts
// Normalize address
// cmdseg += (cmdoff / 16);
// cmdoff %= 16;
// Send command to device
if (inout == ATA_DATA_NO) {
}
else {
while (1) {
while (1) {
if ( !(status & ATA_CB_STAT_BSY) )
break;
}
// Check if command completed
break;
if (status & ATA_CB_STAT_ERR) {
// Enable interrupts
return 3;
}
// Device must be ready to send data
!= (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
// Enable interrupts
return 4;
}
// Normalize address
// bufseg += (bufoff / 16);
// bufoff %= 16;
// Get the byte count
// adjust to read what we want
lcount = 0;
}
else {
header = 0;
}
length = 0;
}
else {
lafter = 0;
}
// Save byte count
BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
// If counts not dividable by 4, use 16bits mode
if (lbefore & 0x03)
if (lcount & 0x03)
if (lafter & 0x03)
// adds an extra byte if count are odd. before is always even
if (lcount & 0x01) {
lcount += 1;
lafter -= 1;
}
}
if (lmode == ATA_MODE_PIO32) {
lcount >>= 2;
lbefore >>= 2;
lafter >>= 2;
}
else {
lcount >>= 1;
lbefore >>= 1;
lafter >>= 1;
}
if (lmode == ATA_MODE_PIO32) {
if (lbefore)
if (lafter)
} else {
if (lbefore)
if (lafter)
}
// Compute new buffer address
// Save transferred bytes count
}
}
// Final check, device must be ready
if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
!= ATA_CB_STAT_RDY ) {
// Enable interrupts
return 4;
}
// Enable interrupts
return 0;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------