From patchwork Mon May 10 11:17:22 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: QiaoChong X-Patchwork-Id: 52085 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 ozlabs.org (Postfix) with ESMTPS id C168FB7D78 for ; Mon, 10 May 2010 22:07:35 +1000 (EST) Received: from localhost ([127.0.0.1]:56110 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OBRli-0007El-Hz for incoming@patchwork.ozlabs.org; Mon, 10 May 2010 08:07:30 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1OBRhd-0004jF-Qi for qemu-devel@nongnu.org; Mon, 10 May 2010 08:03:17 -0400 Received: from [140.186.70.92] (port=55082 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OBRhX-0004gJ-S0 for qemu-devel@nongnu.org; Mon, 10 May 2010 08:03:17 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OBRhO-0003Nr-7f for qemu-devel@nongnu.org; Mon, 10 May 2010 08:03:11 -0400 Received: from [159.226.40.154] (port=41432 helo=mail.loongson.cn) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OBRhN-0003Ma-2o for qemu-devel@nongnu.org; Mon, 10 May 2010 08:03:02 -0400 Received: from localhost.localdomain (unknown [10.2.1.99]) by mail.loongson.cn (Postfix) with ESMTPA id 664F5C622F; Mon, 10 May 2010 19:17:42 +0800 (CST) From: QiaoChong To: qemu-devel@nongnu.org Date: Mon, 10 May 2010 19:17:22 +0800 Message-Id: <1273490242-14870-2-git-send-email-qiaochong@loongson.cn> X-Mailer: git-send-email 1.7.0.3.254.g4503b.dirty In-Reply-To: <1273490242-14870-1-git-send-email-qiaochong@loongson.cn> References: <1273490242-14870-1-git-send-email-qiaochong@loongson.cn> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 1) Cc: QiaoChong Subject: [Qemu-devel] [PATCH 1/1] add cdrom support for ahci. 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 ahci disk look up from IF_SCSI now. test a sata disk: ./i386-softmmu/qemu -cdrom KNOPPIX_V6.0.1CD-2009-02-08-EN.iso -drive if=scsi,file=/tmp/disk test a sata cd: ./i386-softmmu/qemu -cdrom KNOPPIX_V6.0.1CD-2009-02-08-EN.iso -drive if=scsi,media=cdrom,file=KNOPPIX_V6.0.1CD-2009-02-08-EN.iso Signed-off-by: QiaoChong --- hw/ahci.c | 425 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 422 insertions(+), 3 deletions(-) diff --git a/hw/ahci.c b/hw/ahci.c index e1aed4a..fa32c68 100644 --- a/hw/ahci.c +++ b/hw/ahci.c @@ -16,7 +16,7 @@ * License along with this library; if not, see . * * TODO: - * o ahci cd support + * o ahci cd support should use ide,but now ide 's bmdma prdt is different from ahci's prdt. */ #include "hw.h" #include "qemu-timer.h" @@ -25,13 +25,15 @@ #include "pci.h" #include "dma.h" #include "cpu-common.h" +#include "scsi-defs.h" +#include "scsi.h" #include #define DEBUG_AHCI #ifdef DEBUG_AHCI #define DPRINTF(fmt, ...) \ -do { printf("ahci: " fmt , ## __VA_ARGS__); } while (0) +do { fprintf(stderr,"ahci: " fmt , ## __VA_ARGS__); } while (0) #else #define DPRINTF(fmt, ...) do {} while(0) #endif @@ -160,6 +162,15 @@ enum { AHCI_FLAG_32BIT_ONLY = (1 << 28), /* force 32bit */ }; +enum { + ATA_SRST = (1 << 2), /* software reset */ +}; + +enum { +STATE_RUN=0, +STATE_RESET +}; + /* * ATA Commands (only mandatory commands listed here) */ @@ -250,6 +261,7 @@ typedef struct ahci_sg { typedef struct AHCIState{ ahci_control_regs control_regs; ahci_port_regs port_regs[SATA_PORTS]; + uint32_t port_state[SATA_PORTS]; int mem; QEMUTimer *timer; IDEBus *ide; @@ -472,10 +484,13 @@ static CPUWriteMemoryFunc *ahci_writefn[3]={ static void ahci_reg_init(AHCIState *s) { + int i; s->control_regs.cap = 3 | (0x1f << 8) | (1 << 20) ; /* 4 ports, 32 command slots, 1.5 Gb/s */ s->control_regs.ghc = 1 << 31; /* AHCI Enable */ s->control_regs.impl = 1; /* Port 0 implemented */ s->control_regs.version = 0x10000; + for(i=0;iport_state[i]=STATE_RUN; } static void padstr(char *str, const char *src, int len) @@ -490,12 +505,91 @@ static void padstr(char *str, const char *src, int len) } } +static void padstr8(uint8_t *buf, int buf_size, const char *src) +{ + int i; + for(i = 0; i < buf_size; i++) { + if (*src) + buf[i] = *src++; + else + buf[i] = ' '; + } +} static void put_le16(uint16_t *p, unsigned int v) { *p = cpu_to_le16(v); } +static inline void cpu_to_ube16(uint8_t *buf, int val) +{ + buf[0] = val >> 8; + buf[1] = val & 0xff; +} + +static inline int ube16_to_cpu(const uint8_t *buf) +{ + return (buf[0] << 8) | buf[1]; +} + +static inline int ube32_to_cpu(const uint8_t *buf) +{ + return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; +} +static inline void cpu_to_ube32(uint8_t *buf, unsigned int val) +{ + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val & 0xff; +} +static void ide_atapi_identify(IDEState *s) +{ + uint16_t *p; + + if (s->identify_set) { + memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data)); + return; + } + + memset(s->io_buffer, 0, 512); + p = (uint16_t *)s->io_buffer; + /* Removable CDROM, 50us response, 12 byte packets */ + put_le16(p + 0, (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0)); + padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ + put_le16(p + 20, 3); /* buffer type */ + put_le16(p + 21, 512); /* cache size in sectors */ + put_le16(p + 22, 4); /* ecc bytes */ + padstr((char *)(p + 23), s->version, 8); /* firmware version */ + padstr((char *)(p + 27), "QEMU DVD-ROM", 40); /* model */ + put_le16(p + 48, 1); /* dword I/O (XXX: should not be set on CDROM) */ +#ifdef USE_DMA_CDROM + put_le16(p + 49, 1 << 9 | 1 << 8); /* DMA and LBA supported */ + put_le16(p + 53, 7); /* words 64-70, 54-58, 88 valid */ + put_le16(p + 62, 7); /* single word dma0-2 supported */ + put_le16(p + 63, 7); /* mdma0-2 supported */ + put_le16(p + 64, 0x3f); /* PIO modes supported */ +#else + put_le16(p + 49, 1 << 9); /* LBA supported, no DMA */ + put_le16(p + 53, 3); /* words 64-70, 54-58 valid */ + put_le16(p + 63, 0x103); /* DMA modes XXX: may be incorrect */ + put_le16(p + 64, 1); /* PIO modes */ +#endif + put_le16(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */ + put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */ + put_le16(p + 67, 0x12c); /* minimum PIO cycle time without flow control */ + put_le16(p + 68, 0xb4); /* minimum PIO cycle time with IORDY flow control */ + + put_le16(p + 71, 30); /* in ns */ + put_le16(p + 72, 30); /* in ns */ + + put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */ +#ifdef USE_DMA_CDROM + put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */ +#endif + memcpy(s->identify_data, p, sizeof(s->identify_data)); + s->identify_set = 1; +} static void ide_identify(IDEState *s) { @@ -611,6 +705,289 @@ static uint32_t read_from_sglist(uint8_t *buffer,uint32_t len,ahci_sg *sglist,ui return total; } +static inline uint8_t ide_atapi_set_profile(uint8_t *buf, uint8_t *index, + uint16_t profile) +{ + uint8_t *buf_profile = buf + 12; /* start of profiles */ + + buf_profile += ((*index) * 4); /* start of indexed profile */ + cpu_to_ube16 (buf_profile, profile); + buf_profile[2] = ((buf_profile[0] == buf[6]) && (buf_profile[1] == buf[7])); + + /* each profile adds 4 bytes to the response */ + (*index)++; + buf[11] += 4; /* Additional Length */ + + return 4; +} + +static inline int media_present(IDEState *s) +{ + return (s->nb_sectors > 0); +} +static inline int media_is_dvd(IDEState *s) +{ + return (media_present(s) && s->nb_sectors > CD_MAX_SECTORS); +} + +static inline int media_is_cd(IDEState *s) +{ + return (media_present(s) && s->nb_sectors <= CD_MAX_SECTORS); +} +static void ide_atapi_cmd(AHCIState *s,int prdt_num) +{ + const uint8_t *packet; + uint8_t *buf; + int max_len; + IDEState *ide_state; + ide_state=&s->ide->ifs[0]; + + packet = ide_state->io_buffer; + buf = ide_state->io_buffer; + switch(ide_state->io_buffer[0]) { + case GPCMD_TEST_UNIT_READY: + break; + case GPCMD_INQUIRY: + max_len = packet[4]; + buf[0] = 0x05; /* CD-ROM */ + buf[1] = 0x80; /* removable */ + buf[2] = 0x00; /* ISO */ + buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */ + buf[4] = 31; /* additional length */ + buf[5] = 0; /* reserved */ + buf[6] = 0; /* reserved */ + buf[7] = 0; /* reserved */ + padstr8(buf + 8, 8, "QEMU"); + padstr8(buf + 16, 16, "QEMU DVD-ROM"); + padstr8(buf + 32, 4, ide_state->version); + write_to_sglist(buf,36,s->prdt_buf,prdt_num); + break; + case GPCMD_MODE_SENSE_6: + case GPCMD_MODE_SENSE_10: + { + int action, code; + if (packet[0] == GPCMD_MODE_SENSE_10) + max_len = ube16_to_cpu(packet + 7); + else + max_len = packet[4]; + action = packet[2] >> 6; + code = packet[2] & 0x3f; + switch(action) { + case 0: /* current values */ + switch(code) { + case GPMODE_R_W_ERROR_PAGE: /* error recovery */ + cpu_to_ube16(&buf[0], 16 + 6); + buf[2] = 0x70; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + + buf[8] = 0x01; + buf[9] = 0x06; + buf[10] = 0x00; + buf[11] = 0x05; + buf[12] = 0x00; + buf[13] = 0x00; + buf[14] = 0x00; + buf[15] = 0x00; + write_to_sglist(buf,16,s->prdt_buf,prdt_num); + break; + case GPMODE_AUDIO_CTL_PAGE: + cpu_to_ube16(&buf[0], 24 + 6); + buf[2] = 0x70; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + + /* Fill with CDROM audio volume */ + buf[17] = 0; + buf[19] = 0; + buf[21] = 0; + buf[23] = 0; + + write_to_sglist(buf,24,s->prdt_buf,prdt_num); + break; + case GPMODE_CAPABILITIES_PAGE: + cpu_to_ube16(&buf[0], 28 + 6); + buf[2] = 0x70; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + + buf[8] = 0x2a; + buf[9] = 0x12; + buf[10] = 0x00; + buf[11] = 0x00; + + /* Claim PLAY_AUDIO capability (0x01) since some Linux + code checks for this to automount media. */ + buf[12] = 0x71; + buf[13] = 3 << 5; + buf[14] = (1 << 0) | (1 << 3) | (1 << 5); + if (bdrv_is_locked(ide_state->bs)) + buf[6] |= 1 << 1; + buf[15] = 0x00; + cpu_to_ube16(&buf[16], 706); + buf[18] = 0; + buf[19] = 2; + cpu_to_ube16(&buf[20], 512); + cpu_to_ube16(&buf[22], 706); + buf[24] = 0; + buf[25] = 0; + buf[26] = 0; + buf[27] = 0; + write_to_sglist(buf,28,s->prdt_buf,prdt_num); + break; + default: + goto error_cmd; + } + break; + case 1: /* changeable values */ + goto error_cmd; + case 2: /* default values */ + goto error_cmd; + default: + case 3: /* saved values */ + goto error_cmd; + break; + } + } + break; + case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + break; + case GPCMD_READ_TOC_PMA_ATIP: + { + int format, msf, start_track, len; + uint64_t total_sectors; + + bdrv_get_geometry(ide_state->bs, &total_sectors); + total_sectors >>= 2; + if (total_sectors == 0) { + ide_atapi_cmd_error(ide_state, SENSE_NOT_READY, + ASC_MEDIUM_NOT_PRESENT); + break; + } + max_len = ube16_to_cpu(packet + 7); + format = packet[9] >> 6; + msf = (packet[1] >> 1) & 1; + start_track = packet[6]; + switch(format) { + case 0: + len = cdrom_read_toc(total_sectors, buf, msf, start_track); + if (len < 0) + goto error_cmd; + write_to_sglist(buf,len,s->prdt_buf,prdt_num); + break; + case 1: + /* multi session : only a single session defined */ + memset(buf, 0, 12); + buf[1] = 0x0a; + buf[2] = 0x01; + buf[3] = 0x01; + write_to_sglist(buf,12,s->prdt_buf,prdt_num); + break; + case 2: + len = cdrom_read_toc_raw(total_sectors, buf, msf, start_track); + if (len < 0) + goto error_cmd; + write_to_sglist(buf,len,s->prdt_buf,prdt_num); + break; + default: + goto error_cmd; + break; + } + } + break; + case GPCMD_READ_10: + case GPCMD_READ_12: + { + int nb_sectors, lba; + int ret; + + if (packet[0] == GPCMD_READ_10) + nb_sectors = ube16_to_cpu(packet + 7); + else + nb_sectors = ube32_to_cpu(packet + 6); + lba = ube32_to_cpu(packet + 2); + if (nb_sectors == 0) { + //ide_atapi_cmd_ok(s); + break; + } + ret = bdrv_read(ide_state->bs, lba<<2, ide_state->io_buffer, nb_sectors); + if(ret==0) + { + write_to_sglist(ide_state->io_buffer,nb_sectors*512,s->prdt_buf,prdt_num); + } + break; + } + case GPCMD_READ_CDVD_CAPACITY: + { + uint64_t total_sectors; + + bdrv_get_geometry(ide_state->bs, &total_sectors); + total_sectors >>= 2; + if (total_sectors == 0) { + ide_atapi_cmd_error(ide_state, SENSE_NOT_READY, + ASC_MEDIUM_NOT_PRESENT); + break; + } + /* NOTE: it is really the number of sectors minus 1 */ + cpu_to_ube32(buf, total_sectors - 1); + cpu_to_ube32(buf + 4, 2048); + write_to_sglist(buf,8,s->prdt_buf,prdt_num); + } + break; + case GPCMD_GET_CONFIGURATION: + { + uint32_t len; + uint8_t index = 0; + + /* only feature 0 is supported */ + + /* XXX: could result in alignment problems in some architectures */ + max_len = ube16_to_cpu(packet + 7); + + /* + * XXX: avoid overflow for io_buffer if max_len is bigger than + * the size of that buffer (dimensioned to max number of + * sectors to transfer at once) + * + * Only a problem if the feature/profiles grow. + */ + if (max_len > 512) /* XXX: assume 1 sector */ + max_len = 512; + + memset(buf, 0, max_len); + /* + * the number of sectors from the media tells us which profile + * to use as current. 0 means there is no media + */ + if (media_is_dvd(ide_state)) + cpu_to_ube16(buf + 6, MMC_PROFILE_DVD_ROM); + else if (media_is_cd(ide_state)) + cpu_to_ube16(buf + 6, MMC_PROFILE_CD_ROM); + + buf[10] = 0x02 | 0x01; /* persistent and current */ + len = 12; /* headers: 8 + 4 */ + len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_DVD_ROM); + len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_CD_ROM); + cpu_to_ube32(buf, len - 4); /* data length */ + + write_to_sglist(buf,len,s->prdt_buf,prdt_num); + break; + } + default: + error_cmd: + hw_error("unsupport cmd 0x%08x\n",ide_state->io_buffer[0]); + break; + } +} static void handle_cmd(AHCIState *s,int port,int slot) { int64_t sector_num; @@ -619,6 +996,7 @@ static void handle_cmd(AHCIState *s,int port,int slot) int ret; int cmdaddr; uint8_t fis[0x80]; + uint8_t acmd[0x20]; int cmd_len; int prdt_num; int i; @@ -667,17 +1045,56 @@ static void handle_cmd(AHCIState *s,int port,int slot) #endif } + switch(s->port_state[port]) + { + case STATE_RUN: + if(fis[15]&ATA_SRST) s->port_state[port]=STATE_RESET; + break; + case STATE_RESET: + if(!(fis[15]&ATA_SRST)) + { + if(!s->ide)hw_error("no ahci sata disk now\n"); + ide_state=&s->ide->ifs[0]; + s->port_state[port]=STATE_RUN; + if(bdrv_get_type_hint(ide_state->bs)==BDRV_TYPE_CDROM) + s->port_regs[port].sig=0xeb14<<16; + else s->port_regs[port].sig=0; + } + break; + } + if(fis[1]==(1<<7)) { if(!s->ide)hw_error("no ahci sata disk now\n"); ide_state=&s->ide->ifs[0]; switch(fis[2]) { + case WIN_PIDENTIFY: + ide_atapi_identify(ide_state); + write_to_sglist(ide_state->identify_data, sizeof(ide_state->identify_data),s->prdt_buf,prdt_num); + pr->irq_stat |= (1<<2); + break; + case ATA_CMD_IDENT: ide_identify(ide_state); write_to_sglist(ide_state->identify_data, sizeof(ide_state->identify_data),s->prdt_buf,prdt_num); pr->irq_stat |= (1<<2); break; + case WIN_PACKETCMD: + cpu_physical_memory_read(cmd_hdr.tbl_addr+0x40,acmd,0x20); +#ifdef DEBUG_AHCI + for(i=0;i<32;i++) + { + if((i&0xf)==0)DPRINTF("\n%02x:",i); + DPRINTF("%02x ",acmd[i]); + } +#endif + ide_state->atapi_dma = 1; + ide_state->nsector = 1; + memcpy(ide_state->io_buffer,acmd,32); + ide_atapi_cmd(s,prdt_num); + pr->irq_stat |= (1<<2); + break; case WIN_STANDBYNOW1: case WIN_SETFEATURES: pr->irq_stat |= (1<<2); @@ -734,16 +1151,18 @@ static AHCIState *ahci_new(void) { DriveInfo *dinfo; IDEBus *bus = qemu_mallocz(sizeof(IDEBus)); + BMDMAState *bmdma=qemu_mallocz(sizeof(BMDMAState)); AHCIState *s = qemu_mallocz(sizeof(AHCIState)); ahci_reg_init(s); s->mem = cpu_register_io_memory(ahci_readfn, ahci_writefn, s); s->timer = qemu_new_timer(vm_clock, ahci_timer_function, s); s->prdt_buf = qemu_malloc(65535*32); - if ((dinfo = drive_get(IF_SD, 0, 0)) != NULL) + if ((dinfo = drive_get(IF_SCSI, 0, 0)) != NULL) { ide_init2(bus, dinfo, NULL,0); s->ide=bus; + s->ide->bmdma=bmdma; } return s; }