SMART ATA Functionality
For the lulz I implemented basic SMART functionality in ide.c. smartctl on linux recognizes it just fine and starting self tests with it complete successfully. Signed-off-by: Brian Wheeler <bdwheele@indiana.edu> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
parent
7dc3fa0913
commit
e8b5439495
205
hw/ide.c
205
hw/ide.c
|
@ -372,6 +372,28 @@
|
|||
#define SENSE_ILLEGAL_REQUEST 5
|
||||
#define SENSE_UNIT_ATTENTION 6
|
||||
|
||||
#define SMART_READ_DATA 0xd0
|
||||
#define SMART_READ_THRESH 0xd1
|
||||
#define SMART_ATTR_AUTOSAVE 0xd2
|
||||
#define SMART_SAVE_ATTR 0xd3
|
||||
#define SMART_EXECUTE_OFFLINE 0xd4
|
||||
#define SMART_READ_LOG 0xd5
|
||||
#define SMART_WRITE_LOG 0xd6
|
||||
#define SMART_ENABLE 0xd8
|
||||
#define SMART_DISABLE 0xd9
|
||||
#define SMART_STATUS 0xda
|
||||
|
||||
static int smart_attributes[][5] = {
|
||||
/* id, flags, val, wrst, thrsh */
|
||||
{ 0x01, 0x03, 0x64, 0x64, 0x06}, /* raw read */
|
||||
{ 0x03, 0x03, 0x64, 0x64, 0x46}, /* spin up */
|
||||
{ 0x04, 0x02, 0x64, 0x64, 0x14}, /* start stop count */
|
||||
{ 0x05, 0x03, 0x64, 0x64, 0x36}, /* remapped sectors */
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct IDEState;
|
||||
|
||||
typedef void EndTransferFunc(struct IDEState *);
|
||||
|
@ -444,6 +466,13 @@ typedef struct IDEState {
|
|||
int media_changed;
|
||||
/* for pmac */
|
||||
int is_read;
|
||||
/* SMART */
|
||||
uint8_t smart_enabled;
|
||||
uint8_t smart_autosave;
|
||||
int smart_errors;
|
||||
uint8_t smart_selftest_count;
|
||||
uint8_t *smart_selftest_data;
|
||||
|
||||
} IDEState;
|
||||
|
||||
/* XXX: DVDs that could fit on a CD will be reported as a CD */
|
||||
|
@ -594,14 +623,18 @@ static void ide_identify(IDEState *s)
|
|||
put_le16(p + 68, 120);
|
||||
put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
|
||||
put_le16(p + 81, 0x16); /* conforms to ata5 */
|
||||
put_le16(p + 82, (1 << 14));
|
||||
/* 14=NOP supported, 0=SMART supported */
|
||||
put_le16(p + 82, (1 << 14) | 1);
|
||||
/* 13=flush_cache_ext,12=flush_cache,10=lba48 */
|
||||
put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
|
||||
put_le16(p + 84, (1 << 14));
|
||||
put_le16(p + 85, (1 << 14));
|
||||
/* 14=set to 1, 1=SMART self test, 0=SMART error logging */
|
||||
put_le16(p + 84, (1 << 14) | 0);
|
||||
/* 14 = NOP supported, 0=SMART feature set enabled */
|
||||
put_le16(p + 85, (1 << 14) | 1);
|
||||
/* 13=flush_cache_ext,12=flush_cache,10=lba48 */
|
||||
put_le16(p + 86, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
|
||||
put_le16(p + 87, (1 << 14));
|
||||
/* 14=set to 1, 1=smart self test, 0=smart error logging */
|
||||
put_le16(p + 87, (1 << 14) | 0);
|
||||
put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
|
||||
put_le16(p + 93, 1 | (1 << 14) | 0x2000);
|
||||
put_le16(p + 100, s->nb_sectors);
|
||||
|
@ -2567,6 +2600,162 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
|||
s->status = READY_STAT | SEEK_STAT;
|
||||
ide_set_irq(s);
|
||||
break;
|
||||
|
||||
case WIN_SMART:
|
||||
if (s->is_cdrom)
|
||||
goto abort_cmd;
|
||||
if (s->hcyl != 0xc2 || s->lcyl != 0x4f)
|
||||
goto abort_cmd;
|
||||
if (!s->smart_enabled && s->feature != SMART_ENABLE)
|
||||
goto abort_cmd;
|
||||
switch (s->feature) {
|
||||
case SMART_DISABLE:
|
||||
s->smart_enabled = 0;
|
||||
s->status = READY_STAT | SEEK_STAT;
|
||||
ide_set_irq(s);
|
||||
break;
|
||||
case SMART_ENABLE:
|
||||
s->smart_enabled = 1;
|
||||
s->status = READY_STAT | SEEK_STAT;
|
||||
ide_set_irq(s);
|
||||
break;
|
||||
case SMART_ATTR_AUTOSAVE:
|
||||
switch (s->sector) {
|
||||
case 0x00:
|
||||
s->smart_autosave = 0;
|
||||
break;
|
||||
case 0xf1:
|
||||
s->smart_autosave = 1;
|
||||
break;
|
||||
default:
|
||||
goto abort_cmd;
|
||||
}
|
||||
s->status = READY_STAT | SEEK_STAT;
|
||||
ide_set_irq(s);
|
||||
break;
|
||||
case SMART_STATUS:
|
||||
if (!s->smart_errors) {
|
||||
s->hcyl = 0xc2;
|
||||
s->lcyl = 0x4f;
|
||||
} else {
|
||||
s->hcyl = 0x2c;
|
||||
s->lcyl = 0xf4;
|
||||
}
|
||||
s->status = READY_STAT | SEEK_STAT;
|
||||
ide_set_irq(s);
|
||||
break;
|
||||
case SMART_READ_THRESH:
|
||||
memset(s->io_buffer, 0, 0x200);
|
||||
s->io_buffer[0] = 0x01; /* smart struct version */
|
||||
for (n=0; n<30; n++) {
|
||||
if (smart_attributes[n][0] == 0)
|
||||
break;
|
||||
s->io_buffer[2+0+(n*12)] = smart_attributes[n][0];
|
||||
s->io_buffer[2+1+(n*12)] = smart_attributes[n][4];
|
||||
}
|
||||
for (n=0; n<511; n++) /* checksum */
|
||||
s->io_buffer[511] += s->io_buffer[n];
|
||||
s->io_buffer[511] = 0x100 - s->io_buffer[511];
|
||||
s->status = READY_STAT | SEEK_STAT;
|
||||
ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
|
||||
ide_set_irq(s);
|
||||
break;
|
||||
case SMART_READ_DATA:
|
||||
memset(s->io_buffer, 0, 0x200);
|
||||
s->io_buffer[0] = 0x01; /* smart struct version */
|
||||
for (n=0; n<30; n++) {
|
||||
if (smart_attributes[n][0] == 0)
|
||||
break;
|
||||
s->io_buffer[2+0+(n*12)] = smart_attributes[n][0];
|
||||
s->io_buffer[2+1+(n*12)] = smart_attributes[n][1];
|
||||
s->io_buffer[2+3+(n*12)] = smart_attributes[n][2];
|
||||
s->io_buffer[2+4+(n*12)] = smart_attributes[n][3];
|
||||
}
|
||||
s->io_buffer[362] = 0x02 | (s->smart_autosave?0x80:0x00);
|
||||
if (s->smart_selftest_count == 0) {
|
||||
s->io_buffer[363] = 0;
|
||||
} else {
|
||||
s->io_buffer[363] =
|
||||
s->smart_selftest_data[3 +
|
||||
(s->smart_selftest_count - 1) *
|
||||
24];
|
||||
}
|
||||
s->io_buffer[364] = 0x20;
|
||||
s->io_buffer[365] = 0x01;
|
||||
/* offline data collection capacity: execute + self-test*/
|
||||
s->io_buffer[367] = (1<<4 | 1<<3 | 1);
|
||||
s->io_buffer[368] = 0x03; /* smart capability (1) */
|
||||
s->io_buffer[369] = 0x00; /* smart capability (2) */
|
||||
s->io_buffer[370] = 0x01; /* error logging supported */
|
||||
s->io_buffer[372] = 0x02; /* minutes for poll short test */
|
||||
s->io_buffer[373] = 0x36; /* minutes for poll ext test */
|
||||
s->io_buffer[374] = 0x01; /* minutes for poll conveyance */
|
||||
|
||||
for (n=0; n<511; n++)
|
||||
s->io_buffer[511] += s->io_buffer[n];
|
||||
s->io_buffer[511] = 0x100 - s->io_buffer[511];
|
||||
s->status = READY_STAT | SEEK_STAT;
|
||||
ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
|
||||
ide_set_irq(s);
|
||||
break;
|
||||
case SMART_READ_LOG:
|
||||
switch (s->sector) {
|
||||
case 0x01: /* summary smart error log */
|
||||
memset(s->io_buffer, 0, 0x200);
|
||||
s->io_buffer[0] = 0x01;
|
||||
s->io_buffer[1] = 0x00; /* no error entries */
|
||||
s->io_buffer[452] = s->smart_errors & 0xff;
|
||||
s->io_buffer[453] = (s->smart_errors & 0xff00) >> 8;
|
||||
|
||||
for (n=0; n<511; n++)
|
||||
s->io_buffer[511] += s->io_buffer[n];
|
||||
s->io_buffer[511] = 0x100 - s->io_buffer[511];
|
||||
break;
|
||||
case 0x06: /* smart self test log */
|
||||
memset(s->io_buffer, 0, 0x200);
|
||||
s->io_buffer[0] = 0x01;
|
||||
if (s->smart_selftest_count == 0) {
|
||||
s->io_buffer[508] = 0;
|
||||
} else {
|
||||
s->io_buffer[508] = s->smart_selftest_count;
|
||||
for (n=2; n<506; n++)
|
||||
s->io_buffer[n] = s->smart_selftest_data[n];
|
||||
}
|
||||
for (n=0; n<511; n++)
|
||||
s->io_buffer[511] += s->io_buffer[n];
|
||||
s->io_buffer[511] = 0x100 - s->io_buffer[511];
|
||||
break;
|
||||
default:
|
||||
goto abort_cmd;
|
||||
}
|
||||
s->status = READY_STAT | SEEK_STAT;
|
||||
ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
|
||||
ide_set_irq(s);
|
||||
break;
|
||||
case SMART_EXECUTE_OFFLINE:
|
||||
switch (s->sector) {
|
||||
case 0: /* off-line routine */
|
||||
case 1: /* short self test */
|
||||
case 2: /* extended self test */
|
||||
s->smart_selftest_count++;
|
||||
if(s->smart_selftest_count > 21)
|
||||
s->smart_selftest_count = 0;
|
||||
n = 2 + (s->smart_selftest_count - 1) * 24;
|
||||
s->smart_selftest_data[n] = s->sector;
|
||||
s->smart_selftest_data[n+1] = 0x00; /* OK and finished */
|
||||
s->smart_selftest_data[n+2] = 0x34; /* hour count lsb */
|
||||
s->smart_selftest_data[n+3] = 0x12; /* hour count msb */
|
||||
s->status = READY_STAT | SEEK_STAT;
|
||||
ide_set_irq(s);
|
||||
break;
|
||||
default:
|
||||
goto abort_cmd;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto abort_cmd;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort_cmd:
|
||||
ide_abort_command(s);
|
||||
|
@ -2828,7 +3017,13 @@ static void ide_init2(IDEState *ide_state,
|
|||
s->heads = heads;
|
||||
s->sectors = secs;
|
||||
s->nb_sectors = nb_sectors;
|
||||
|
||||
/* The SMART values should be preserved across power cycles
|
||||
but they aren't. */
|
||||
s->smart_enabled = 1;
|
||||
s->smart_autosave = 1;
|
||||
s->smart_errors = 0;
|
||||
s->smart_selftest_count = 0;
|
||||
s->smart_selftest_data = qemu_blockalign(s->bs, 512);
|
||||
if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
|
||||
s->is_cdrom = 1;
|
||||
bdrv_set_change_cb(s->bs, cdrom_change_cb, s);
|
||||
|
|
Loading…
Reference in a new issue