From patchwork Fri Aug 7 19:33:04 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Wheeler X-Patchwork-Id: 30965 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by bilbo.ozlabs.org (Postfix) with ESMTPS id AC7F0B70C4 for ; Sat, 8 Aug 2009 05:33:55 +1000 (EST) Received: from localhost ([127.0.0.1]:41739 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MZVCJ-00012E-Gq for incoming@patchwork.ozlabs.org; Fri, 07 Aug 2009 15:33:51 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1MZVBs-00011z-Ct for qemu-devel@nongnu.org; Fri, 07 Aug 2009 15:33:24 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1MZVBm-00011k-7N for qemu-devel@nongnu.org; Fri, 07 Aug 2009 15:33:23 -0400 Received: from [199.232.76.173] (port=33882 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MZVBm-00011h-1V for qemu-devel@nongnu.org; Fri, 07 Aug 2009 15:33:18 -0400 Received: from mx20.gnu.org ([199.232.41.8]:21063) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1MZVBl-0007bV-Ee for qemu-devel@nongnu.org; Fri, 07 Aug 2009 15:33:17 -0400 Received: from belushi.uits.indiana.edu ([129.79.1.188]) by mx20.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1MZVBh-000305-T1 for qemu-devel@nongnu.org; Fri, 07 Aug 2009 15:33:14 -0400 Received: from mail-relay.iu.edu (burns.uits.indiana.edu [129.79.1.202]) by belushi.uits.indiana.edu (8.14.2/8.13.8/IU Messaging Team) with ESMTP id n77JX9ME032006 for ; Fri, 7 Aug 2009 15:33:10 -0400 Received: from [129.79.35.119] (nibbler.dlib.indiana.edu [129.79.35.119]) (authenticated bits=0) by mail-relay.iu.edu (8.14.2/8.13.8/IU Messaging Team Submission) with ESMTP id n77JX7ZN023938 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Fri, 7 Aug 2009 15:33:09 -0400 From: Brian Wheeler To: qemu-devel@nongnu.org Date: Fri, 07 Aug 2009 15:33:04 -0400 Message-Id: <1249673584.16514.8.camel@nibbler.dlib.indiana.edu> Mime-Version: 1.0 X-Mailer: Evolution 2.26.3 (2.26.3-1.fc11) X-Detected-Operating-System: by mx20.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-detected-operating-system: by monty-python.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) Subject: [Qemu-devel] [PATCH 1/1] SMART ATA Functionality X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- hw/ide.c | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 200 insertions(+), 5 deletions(-) diff --git a/hw/ide.c b/hw/ide.c index 6cf04a6..f397b9c 100644 --- a/hw/ide.c +++ b/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 *); @@ -443,6 +465,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 */ @@ -593,14 +622,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); @@ -2565,6 +2598,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); @@ -2826,7 +3015,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);