Loading hw/ide/core.c +174 −151 Original line number Diff line number Diff line Loading @@ -1438,145 +1438,30 @@ static bool cmd_ibm_sense_condition(IDEState *s, uint8_t cmd) return true; } #define HD_OK (1u << IDE_HD) #define CD_OK (1u << IDE_CD) #define CFA_OK (1u << IDE_CFATA) #define HD_CFA_OK (HD_OK | CFA_OK) #define ALL_OK (HD_OK | CD_OK | CFA_OK) /* Set the Disk Seek Completed status bit during completion */ #define SET_DSC (1u << 8) /*** SMART commands ***/ /* See ACS-2 T13/2015-D Table B.2 Command codes */ static const struct { /* Returns true if the completion code should be run */ bool (*handler)(IDEState *s, uint8_t cmd); int flags; } ide_cmd_table[0x100] = { /* NOP not implemented, mandatory for CD */ [CFA_REQ_EXT_ERROR_CODE] = { cmd_cfa_req_ext_error_code, CFA_OK }, [WIN_DSM] = { cmd_data_set_management, ALL_OK }, [WIN_DEVICE_RESET] = { cmd_device_reset, CD_OK }, [WIN_RECAL] = { cmd_nop, HD_CFA_OK | SET_DSC}, [WIN_READ] = { cmd_read_pio, ALL_OK }, [WIN_READ_ONCE] = { cmd_read_pio, ALL_OK }, [WIN_READ_EXT] = { cmd_read_pio, HD_CFA_OK }, [WIN_READDMA_EXT] = { cmd_read_dma, HD_CFA_OK }, [WIN_READ_NATIVE_MAX_EXT] = { cmd_read_native_max, HD_CFA_OK | SET_DSC }, [WIN_MULTREAD_EXT] = { cmd_read_multiple, HD_CFA_OK }, [WIN_WRITE] = { cmd_write_pio, HD_CFA_OK }, [WIN_WRITE_ONCE] = { cmd_write_pio, HD_CFA_OK }, [WIN_WRITE_EXT] = { cmd_write_pio, HD_CFA_OK }, [WIN_WRITEDMA_EXT] = { cmd_write_dma, HD_CFA_OK }, [CFA_WRITE_SECT_WO_ERASE] = { cmd_write_pio, CFA_OK }, [WIN_MULTWRITE_EXT] = { cmd_write_multiple, HD_CFA_OK }, [WIN_WRITE_VERIFY] = { cmd_write_pio, HD_CFA_OK }, [WIN_VERIFY] = { cmd_verify, HD_CFA_OK | SET_DSC }, [WIN_VERIFY_ONCE] = { cmd_verify, HD_CFA_OK | SET_DSC }, [WIN_VERIFY_EXT] = { cmd_verify, HD_CFA_OK | SET_DSC }, [WIN_SEEK] = { cmd_seek, HD_CFA_OK | SET_DSC }, [CFA_TRANSLATE_SECTOR] = { cmd_cfa_translate_sector, CFA_OK }, [WIN_DIAGNOSE] = { cmd_exec_dev_diagnostic, ALL_OK }, [WIN_SPECIFY] = { cmd_nop, HD_CFA_OK | SET_DSC }, [WIN_STANDBYNOW2] = { cmd_nop, ALL_OK }, [WIN_IDLEIMMEDIATE2] = { cmd_nop, ALL_OK }, [WIN_STANDBY2] = { cmd_nop, ALL_OK }, [WIN_SETIDLE2] = { cmd_nop, ALL_OK }, [WIN_CHECKPOWERMODE2] = { cmd_check_power_mode, ALL_OK | SET_DSC }, [WIN_SLEEPNOW2] = { cmd_nop, ALL_OK }, [WIN_PACKETCMD] = { cmd_packet, CD_OK }, [WIN_PIDENTIFY] = { cmd_identify_packet, CD_OK }, [WIN_SMART] = { NULL, HD_CFA_OK }, [CFA_ACCESS_METADATA_STORAGE] = { cmd_cfa_access_metadata_storage, CFA_OK }, [CFA_ERASE_SECTORS] = { cmd_cfa_erase_sectors, CFA_OK | SET_DSC }, [WIN_MULTREAD] = { cmd_read_multiple, HD_CFA_OK }, [WIN_MULTWRITE] = { cmd_write_multiple, HD_CFA_OK }, [WIN_SETMULT] = { cmd_set_multiple_mode, HD_CFA_OK | SET_DSC }, [WIN_READDMA] = { cmd_read_dma, HD_CFA_OK }, [WIN_READDMA_ONCE] = { cmd_read_dma, HD_CFA_OK }, [WIN_WRITEDMA] = { cmd_write_dma, HD_CFA_OK }, [WIN_WRITEDMA_ONCE] = { cmd_write_dma, HD_CFA_OK }, [CFA_WRITE_MULTI_WO_ERASE] = { cmd_write_multiple, CFA_OK }, [WIN_STANDBYNOW1] = { cmd_nop, ALL_OK }, [WIN_IDLEIMMEDIATE] = { cmd_nop, ALL_OK }, [WIN_STANDBY] = { cmd_nop, ALL_OK }, [WIN_SETIDLE1] = { cmd_nop, ALL_OK }, [WIN_CHECKPOWERMODE1] = { cmd_check_power_mode, ALL_OK | SET_DSC }, [WIN_SLEEPNOW1] = { cmd_nop, ALL_OK }, [WIN_FLUSH_CACHE] = { cmd_flush_cache, ALL_OK }, [WIN_FLUSH_CACHE_EXT] = { cmd_flush_cache, HD_CFA_OK }, [WIN_IDENTIFY] = { cmd_identify, ALL_OK }, [WIN_SETFEATURES] = { cmd_set_features, ALL_OK | SET_DSC }, [IBM_SENSE_CONDITION] = { cmd_ibm_sense_condition, CFA_OK | SET_DSC }, [CFA_WEAR_LEVEL] = { cmd_cfa_erase_sectors, HD_CFA_OK | SET_DSC }, [WIN_READ_NATIVE_MAX] = { cmd_read_native_max, ALL_OK | SET_DSC }, }; static bool ide_cmd_permitted(IDEState *s, uint32_t cmd) static bool cmd_smart(IDEState *s, uint8_t cmd) { return cmd < ARRAY_SIZE(ide_cmd_table) && (ide_cmd_table[cmd].flags & (1u << s->drive_kind)); } void ide_exec_cmd(IDEBus *bus, uint32_t val) { IDEState *s; int n; #if defined(DEBUG_IDE) printf("ide: CMD=%02x\n", val); #endif s = idebus_active_if(bus); /* ignore commands to non existent slave */ if (s != bus->ifs && !s->bs) return; /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */ if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET) return; if (!ide_cmd_permitted(s, val)) { if (s->hcyl != 0xc2 || s->lcyl != 0x4f) { goto abort_cmd; } if (ide_cmd_table[val].handler != NULL) { bool complete; s->status = READY_STAT | BUSY_STAT; s->error = 0; complete = ide_cmd_table[val].handler(s, val); if (complete) { s->status &= ~BUSY_STAT; assert(!!s->error == !!(s->status & ERR_STAT)); if ((ide_cmd_table[val].flags & SET_DSC) && !s->error) { s->status |= SEEK_STAT; } ide_set_irq(s->bus); } return; if (!s->smart_enabled && s->feature != SMART_ENABLE) { goto abort_cmd; } switch(val) { case WIN_SMART: 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->bus); break; return true; case SMART_ENABLE: s->smart_enabled = 1; s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; return true; case SMART_ATTR_AUTOSAVE: switch (s->sector) { case 0x00: Loading @@ -1588,9 +1473,8 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) default: goto abort_cmd; } s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; return true; case SMART_STATUS: if (!s->smart_errors) { s->hcyl = 0xc2; Loading @@ -1599,32 +1483,39 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->hcyl = 0x2c; s->lcyl = 0xf4; } s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; return true; case SMART_READ_THRESH: memset(s->io_buffer, 0, 0x200); s->io_buffer[0] = 0x01; /* smart struct version */ for (n = 0; n < ARRAY_SIZE(smart_attributes); n++) { s->io_buffer[2 + 0 + (n * 12)] = smart_attributes[n][0]; s->io_buffer[2 + 1 + (n * 12)] = smart_attributes[n][11]; } for (n=0; n<511; n++) /* checksum */ /* checksum */ 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->bus); break; return false; case SMART_READ_DATA: memset(s->io_buffer, 0, 0x200); s->io_buffer[0] = 0x01; /* smart struct version */ for (n = 0; n < ARRAY_SIZE(smart_attributes); n++) { int i; for (i = 0; i < 11; i++) { s->io_buffer[2 + i + (n * 12)] = smart_attributes[n][i]; } } s->io_buffer[362] = 0x02 | (s->smart_autosave ? 0x80 : 0x00); if (s->smart_selftest_count == 0) { s->io_buffer[363] = 0; Loading @@ -1645,13 +1536,16 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) 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++) 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->bus); break; return false; case SMART_READ_LOG: switch (s->sector) { case 0x01: /* summary smart error log */ Loading @@ -1661,8 +1555,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->io_buffer[452] = s->smart_errors & 0xff; s->io_buffer[453] = (s->smart_errors & 0xff00) >> 8; for (n=0; n<511; 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; case 0x06: /* smart self test log */ Loading @@ -1672,11 +1567,14 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->io_buffer[508] = 0; } else { s->io_buffer[508] = s->smart_selftest_count; for (n=2; n<506; n++) for (n = 2; n < 506; n++) { s->io_buffer[n] = s->smart_selftest_data[n]; } for (n=0; n<511; 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: Loading @@ -1685,31 +1583,156 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); ide_set_irq(s->bus); break; return false; 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) 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->bus); break; default: goto abort_cmd; } break; default: return true; } abort_cmd: ide_abort_command(s); return true; } #define HD_OK (1u << IDE_HD) #define CD_OK (1u << IDE_CD) #define CFA_OK (1u << IDE_CFATA) #define HD_CFA_OK (HD_OK | CFA_OK) #define ALL_OK (HD_OK | CD_OK | CFA_OK) /* Set the Disk Seek Completed status bit during completion */ #define SET_DSC (1u << 8) /* See ACS-2 T13/2015-D Table B.2 Command codes */ static const struct { /* Returns true if the completion code should be run */ bool (*handler)(IDEState *s, uint8_t cmd); int flags; } ide_cmd_table[0x100] = { /* NOP not implemented, mandatory for CD */ [CFA_REQ_EXT_ERROR_CODE] = { cmd_cfa_req_ext_error_code, CFA_OK }, [WIN_DSM] = { cmd_data_set_management, ALL_OK }, [WIN_DEVICE_RESET] = { cmd_device_reset, CD_OK }, [WIN_RECAL] = { cmd_nop, HD_CFA_OK | SET_DSC}, [WIN_READ] = { cmd_read_pio, ALL_OK }, [WIN_READ_ONCE] = { cmd_read_pio, ALL_OK }, [WIN_READ_EXT] = { cmd_read_pio, HD_CFA_OK }, [WIN_READDMA_EXT] = { cmd_read_dma, HD_CFA_OK }, [WIN_READ_NATIVE_MAX_EXT] = { cmd_read_native_max, HD_CFA_OK | SET_DSC }, [WIN_MULTREAD_EXT] = { cmd_read_multiple, HD_CFA_OK }, [WIN_WRITE] = { cmd_write_pio, HD_CFA_OK }, [WIN_WRITE_ONCE] = { cmd_write_pio, HD_CFA_OK }, [WIN_WRITE_EXT] = { cmd_write_pio, HD_CFA_OK }, [WIN_WRITEDMA_EXT] = { cmd_write_dma, HD_CFA_OK }, [CFA_WRITE_SECT_WO_ERASE] = { cmd_write_pio, CFA_OK }, [WIN_MULTWRITE_EXT] = { cmd_write_multiple, HD_CFA_OK }, [WIN_WRITE_VERIFY] = { cmd_write_pio, HD_CFA_OK }, [WIN_VERIFY] = { cmd_verify, HD_CFA_OK | SET_DSC }, [WIN_VERIFY_ONCE] = { cmd_verify, HD_CFA_OK | SET_DSC }, [WIN_VERIFY_EXT] = { cmd_verify, HD_CFA_OK | SET_DSC }, [WIN_SEEK] = { cmd_seek, HD_CFA_OK | SET_DSC }, [CFA_TRANSLATE_SECTOR] = { cmd_cfa_translate_sector, CFA_OK }, [WIN_DIAGNOSE] = { cmd_exec_dev_diagnostic, ALL_OK }, [WIN_SPECIFY] = { cmd_nop, HD_CFA_OK | SET_DSC }, [WIN_STANDBYNOW2] = { cmd_nop, ALL_OK }, [WIN_IDLEIMMEDIATE2] = { cmd_nop, ALL_OK }, [WIN_STANDBY2] = { cmd_nop, ALL_OK }, [WIN_SETIDLE2] = { cmd_nop, ALL_OK }, [WIN_CHECKPOWERMODE2] = { cmd_check_power_mode, ALL_OK | SET_DSC }, [WIN_SLEEPNOW2] = { cmd_nop, ALL_OK }, [WIN_PACKETCMD] = { cmd_packet, CD_OK }, [WIN_PIDENTIFY] = { cmd_identify_packet, CD_OK }, [WIN_SMART] = { cmd_smart, HD_CFA_OK | SET_DSC }, [CFA_ACCESS_METADATA_STORAGE] = { cmd_cfa_access_metadata_storage, CFA_OK }, [CFA_ERASE_SECTORS] = { cmd_cfa_erase_sectors, CFA_OK | SET_DSC }, [WIN_MULTREAD] = { cmd_read_multiple, HD_CFA_OK }, [WIN_MULTWRITE] = { cmd_write_multiple, HD_CFA_OK }, [WIN_SETMULT] = { cmd_set_multiple_mode, HD_CFA_OK | SET_DSC }, [WIN_READDMA] = { cmd_read_dma, HD_CFA_OK }, [WIN_READDMA_ONCE] = { cmd_read_dma, HD_CFA_OK }, [WIN_WRITEDMA] = { cmd_write_dma, HD_CFA_OK }, [WIN_WRITEDMA_ONCE] = { cmd_write_dma, HD_CFA_OK }, [CFA_WRITE_MULTI_WO_ERASE] = { cmd_write_multiple, CFA_OK }, [WIN_STANDBYNOW1] = { cmd_nop, ALL_OK }, [WIN_IDLEIMMEDIATE] = { cmd_nop, ALL_OK }, [WIN_STANDBY] = { cmd_nop, ALL_OK }, [WIN_SETIDLE1] = { cmd_nop, ALL_OK }, [WIN_CHECKPOWERMODE1] = { cmd_check_power_mode, ALL_OK | SET_DSC }, [WIN_SLEEPNOW1] = { cmd_nop, ALL_OK }, [WIN_FLUSH_CACHE] = { cmd_flush_cache, ALL_OK }, [WIN_FLUSH_CACHE_EXT] = { cmd_flush_cache, HD_CFA_OK }, [WIN_IDENTIFY] = { cmd_identify, ALL_OK }, [WIN_SETFEATURES] = { cmd_set_features, ALL_OK | SET_DSC }, [IBM_SENSE_CONDITION] = { cmd_ibm_sense_condition, CFA_OK | SET_DSC }, [CFA_WEAR_LEVEL] = { cmd_cfa_erase_sectors, HD_CFA_OK | SET_DSC }, [WIN_READ_NATIVE_MAX] = { cmd_read_native_max, ALL_OK | SET_DSC }, }; static bool ide_cmd_permitted(IDEState *s, uint32_t cmd) { return cmd < ARRAY_SIZE(ide_cmd_table) && (ide_cmd_table[cmd].flags & (1u << s->drive_kind)); } void ide_exec_cmd(IDEBus *bus, uint32_t val) { IDEState *s; #if defined(DEBUG_IDE) printf("ide: CMD=%02x\n", val); #endif s = idebus_active_if(bus); /* ignore commands to non existent slave */ if (s != bus->ifs && !s->bs) return; /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */ if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET) return; if (!ide_cmd_permitted(s, val)) { goto abort_cmd; } break; if (ide_cmd_table[val].handler != NULL) { bool complete; s->status = READY_STAT | BUSY_STAT; s->error = 0; complete = ide_cmd_table[val].handler(s, val); if (complete) { s->status &= ~BUSY_STAT; assert(!!s->error == !!(s->status & ERR_STAT)); if ((ide_cmd_table[val].flags & SET_DSC) && !s->error) { s->status |= SEEK_STAT; } ide_set_irq(s->bus); } return; } switch(val) { default: /* should not be reachable */ abort_cmd: Loading Loading
hw/ide/core.c +174 −151 Original line number Diff line number Diff line Loading @@ -1438,145 +1438,30 @@ static bool cmd_ibm_sense_condition(IDEState *s, uint8_t cmd) return true; } #define HD_OK (1u << IDE_HD) #define CD_OK (1u << IDE_CD) #define CFA_OK (1u << IDE_CFATA) #define HD_CFA_OK (HD_OK | CFA_OK) #define ALL_OK (HD_OK | CD_OK | CFA_OK) /* Set the Disk Seek Completed status bit during completion */ #define SET_DSC (1u << 8) /*** SMART commands ***/ /* See ACS-2 T13/2015-D Table B.2 Command codes */ static const struct { /* Returns true if the completion code should be run */ bool (*handler)(IDEState *s, uint8_t cmd); int flags; } ide_cmd_table[0x100] = { /* NOP not implemented, mandatory for CD */ [CFA_REQ_EXT_ERROR_CODE] = { cmd_cfa_req_ext_error_code, CFA_OK }, [WIN_DSM] = { cmd_data_set_management, ALL_OK }, [WIN_DEVICE_RESET] = { cmd_device_reset, CD_OK }, [WIN_RECAL] = { cmd_nop, HD_CFA_OK | SET_DSC}, [WIN_READ] = { cmd_read_pio, ALL_OK }, [WIN_READ_ONCE] = { cmd_read_pio, ALL_OK }, [WIN_READ_EXT] = { cmd_read_pio, HD_CFA_OK }, [WIN_READDMA_EXT] = { cmd_read_dma, HD_CFA_OK }, [WIN_READ_NATIVE_MAX_EXT] = { cmd_read_native_max, HD_CFA_OK | SET_DSC }, [WIN_MULTREAD_EXT] = { cmd_read_multiple, HD_CFA_OK }, [WIN_WRITE] = { cmd_write_pio, HD_CFA_OK }, [WIN_WRITE_ONCE] = { cmd_write_pio, HD_CFA_OK }, [WIN_WRITE_EXT] = { cmd_write_pio, HD_CFA_OK }, [WIN_WRITEDMA_EXT] = { cmd_write_dma, HD_CFA_OK }, [CFA_WRITE_SECT_WO_ERASE] = { cmd_write_pio, CFA_OK }, [WIN_MULTWRITE_EXT] = { cmd_write_multiple, HD_CFA_OK }, [WIN_WRITE_VERIFY] = { cmd_write_pio, HD_CFA_OK }, [WIN_VERIFY] = { cmd_verify, HD_CFA_OK | SET_DSC }, [WIN_VERIFY_ONCE] = { cmd_verify, HD_CFA_OK | SET_DSC }, [WIN_VERIFY_EXT] = { cmd_verify, HD_CFA_OK | SET_DSC }, [WIN_SEEK] = { cmd_seek, HD_CFA_OK | SET_DSC }, [CFA_TRANSLATE_SECTOR] = { cmd_cfa_translate_sector, CFA_OK }, [WIN_DIAGNOSE] = { cmd_exec_dev_diagnostic, ALL_OK }, [WIN_SPECIFY] = { cmd_nop, HD_CFA_OK | SET_DSC }, [WIN_STANDBYNOW2] = { cmd_nop, ALL_OK }, [WIN_IDLEIMMEDIATE2] = { cmd_nop, ALL_OK }, [WIN_STANDBY2] = { cmd_nop, ALL_OK }, [WIN_SETIDLE2] = { cmd_nop, ALL_OK }, [WIN_CHECKPOWERMODE2] = { cmd_check_power_mode, ALL_OK | SET_DSC }, [WIN_SLEEPNOW2] = { cmd_nop, ALL_OK }, [WIN_PACKETCMD] = { cmd_packet, CD_OK }, [WIN_PIDENTIFY] = { cmd_identify_packet, CD_OK }, [WIN_SMART] = { NULL, HD_CFA_OK }, [CFA_ACCESS_METADATA_STORAGE] = { cmd_cfa_access_metadata_storage, CFA_OK }, [CFA_ERASE_SECTORS] = { cmd_cfa_erase_sectors, CFA_OK | SET_DSC }, [WIN_MULTREAD] = { cmd_read_multiple, HD_CFA_OK }, [WIN_MULTWRITE] = { cmd_write_multiple, HD_CFA_OK }, [WIN_SETMULT] = { cmd_set_multiple_mode, HD_CFA_OK | SET_DSC }, [WIN_READDMA] = { cmd_read_dma, HD_CFA_OK }, [WIN_READDMA_ONCE] = { cmd_read_dma, HD_CFA_OK }, [WIN_WRITEDMA] = { cmd_write_dma, HD_CFA_OK }, [WIN_WRITEDMA_ONCE] = { cmd_write_dma, HD_CFA_OK }, [CFA_WRITE_MULTI_WO_ERASE] = { cmd_write_multiple, CFA_OK }, [WIN_STANDBYNOW1] = { cmd_nop, ALL_OK }, [WIN_IDLEIMMEDIATE] = { cmd_nop, ALL_OK }, [WIN_STANDBY] = { cmd_nop, ALL_OK }, [WIN_SETIDLE1] = { cmd_nop, ALL_OK }, [WIN_CHECKPOWERMODE1] = { cmd_check_power_mode, ALL_OK | SET_DSC }, [WIN_SLEEPNOW1] = { cmd_nop, ALL_OK }, [WIN_FLUSH_CACHE] = { cmd_flush_cache, ALL_OK }, [WIN_FLUSH_CACHE_EXT] = { cmd_flush_cache, HD_CFA_OK }, [WIN_IDENTIFY] = { cmd_identify, ALL_OK }, [WIN_SETFEATURES] = { cmd_set_features, ALL_OK | SET_DSC }, [IBM_SENSE_CONDITION] = { cmd_ibm_sense_condition, CFA_OK | SET_DSC }, [CFA_WEAR_LEVEL] = { cmd_cfa_erase_sectors, HD_CFA_OK | SET_DSC }, [WIN_READ_NATIVE_MAX] = { cmd_read_native_max, ALL_OK | SET_DSC }, }; static bool ide_cmd_permitted(IDEState *s, uint32_t cmd) static bool cmd_smart(IDEState *s, uint8_t cmd) { return cmd < ARRAY_SIZE(ide_cmd_table) && (ide_cmd_table[cmd].flags & (1u << s->drive_kind)); } void ide_exec_cmd(IDEBus *bus, uint32_t val) { IDEState *s; int n; #if defined(DEBUG_IDE) printf("ide: CMD=%02x\n", val); #endif s = idebus_active_if(bus); /* ignore commands to non existent slave */ if (s != bus->ifs && !s->bs) return; /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */ if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET) return; if (!ide_cmd_permitted(s, val)) { if (s->hcyl != 0xc2 || s->lcyl != 0x4f) { goto abort_cmd; } if (ide_cmd_table[val].handler != NULL) { bool complete; s->status = READY_STAT | BUSY_STAT; s->error = 0; complete = ide_cmd_table[val].handler(s, val); if (complete) { s->status &= ~BUSY_STAT; assert(!!s->error == !!(s->status & ERR_STAT)); if ((ide_cmd_table[val].flags & SET_DSC) && !s->error) { s->status |= SEEK_STAT; } ide_set_irq(s->bus); } return; if (!s->smart_enabled && s->feature != SMART_ENABLE) { goto abort_cmd; } switch(val) { case WIN_SMART: 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->bus); break; return true; case SMART_ENABLE: s->smart_enabled = 1; s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; return true; case SMART_ATTR_AUTOSAVE: switch (s->sector) { case 0x00: Loading @@ -1588,9 +1473,8 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) default: goto abort_cmd; } s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; return true; case SMART_STATUS: if (!s->smart_errors) { s->hcyl = 0xc2; Loading @@ -1599,32 +1483,39 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->hcyl = 0x2c; s->lcyl = 0xf4; } s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; return true; case SMART_READ_THRESH: memset(s->io_buffer, 0, 0x200); s->io_buffer[0] = 0x01; /* smart struct version */ for (n = 0; n < ARRAY_SIZE(smart_attributes); n++) { s->io_buffer[2 + 0 + (n * 12)] = smart_attributes[n][0]; s->io_buffer[2 + 1 + (n * 12)] = smart_attributes[n][11]; } for (n=0; n<511; n++) /* checksum */ /* checksum */ 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->bus); break; return false; case SMART_READ_DATA: memset(s->io_buffer, 0, 0x200); s->io_buffer[0] = 0x01; /* smart struct version */ for (n = 0; n < ARRAY_SIZE(smart_attributes); n++) { int i; for (i = 0; i < 11; i++) { s->io_buffer[2 + i + (n * 12)] = smart_attributes[n][i]; } } s->io_buffer[362] = 0x02 | (s->smart_autosave ? 0x80 : 0x00); if (s->smart_selftest_count == 0) { s->io_buffer[363] = 0; Loading @@ -1645,13 +1536,16 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) 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++) 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->bus); break; return false; case SMART_READ_LOG: switch (s->sector) { case 0x01: /* summary smart error log */ Loading @@ -1661,8 +1555,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->io_buffer[452] = s->smart_errors & 0xff; s->io_buffer[453] = (s->smart_errors & 0xff00) >> 8; for (n=0; n<511; 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; case 0x06: /* smart self test log */ Loading @@ -1672,11 +1567,14 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->io_buffer[508] = 0; } else { s->io_buffer[508] = s->smart_selftest_count; for (n=2; n<506; n++) for (n = 2; n < 506; n++) { s->io_buffer[n] = s->smart_selftest_data[n]; } for (n=0; n<511; 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: Loading @@ -1685,31 +1583,156 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); ide_set_irq(s->bus); break; return false; 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) 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->bus); break; default: goto abort_cmd; } break; default: return true; } abort_cmd: ide_abort_command(s); return true; } #define HD_OK (1u << IDE_HD) #define CD_OK (1u << IDE_CD) #define CFA_OK (1u << IDE_CFATA) #define HD_CFA_OK (HD_OK | CFA_OK) #define ALL_OK (HD_OK | CD_OK | CFA_OK) /* Set the Disk Seek Completed status bit during completion */ #define SET_DSC (1u << 8) /* See ACS-2 T13/2015-D Table B.2 Command codes */ static const struct { /* Returns true if the completion code should be run */ bool (*handler)(IDEState *s, uint8_t cmd); int flags; } ide_cmd_table[0x100] = { /* NOP not implemented, mandatory for CD */ [CFA_REQ_EXT_ERROR_CODE] = { cmd_cfa_req_ext_error_code, CFA_OK }, [WIN_DSM] = { cmd_data_set_management, ALL_OK }, [WIN_DEVICE_RESET] = { cmd_device_reset, CD_OK }, [WIN_RECAL] = { cmd_nop, HD_CFA_OK | SET_DSC}, [WIN_READ] = { cmd_read_pio, ALL_OK }, [WIN_READ_ONCE] = { cmd_read_pio, ALL_OK }, [WIN_READ_EXT] = { cmd_read_pio, HD_CFA_OK }, [WIN_READDMA_EXT] = { cmd_read_dma, HD_CFA_OK }, [WIN_READ_NATIVE_MAX_EXT] = { cmd_read_native_max, HD_CFA_OK | SET_DSC }, [WIN_MULTREAD_EXT] = { cmd_read_multiple, HD_CFA_OK }, [WIN_WRITE] = { cmd_write_pio, HD_CFA_OK }, [WIN_WRITE_ONCE] = { cmd_write_pio, HD_CFA_OK }, [WIN_WRITE_EXT] = { cmd_write_pio, HD_CFA_OK }, [WIN_WRITEDMA_EXT] = { cmd_write_dma, HD_CFA_OK }, [CFA_WRITE_SECT_WO_ERASE] = { cmd_write_pio, CFA_OK }, [WIN_MULTWRITE_EXT] = { cmd_write_multiple, HD_CFA_OK }, [WIN_WRITE_VERIFY] = { cmd_write_pio, HD_CFA_OK }, [WIN_VERIFY] = { cmd_verify, HD_CFA_OK | SET_DSC }, [WIN_VERIFY_ONCE] = { cmd_verify, HD_CFA_OK | SET_DSC }, [WIN_VERIFY_EXT] = { cmd_verify, HD_CFA_OK | SET_DSC }, [WIN_SEEK] = { cmd_seek, HD_CFA_OK | SET_DSC }, [CFA_TRANSLATE_SECTOR] = { cmd_cfa_translate_sector, CFA_OK }, [WIN_DIAGNOSE] = { cmd_exec_dev_diagnostic, ALL_OK }, [WIN_SPECIFY] = { cmd_nop, HD_CFA_OK | SET_DSC }, [WIN_STANDBYNOW2] = { cmd_nop, ALL_OK }, [WIN_IDLEIMMEDIATE2] = { cmd_nop, ALL_OK }, [WIN_STANDBY2] = { cmd_nop, ALL_OK }, [WIN_SETIDLE2] = { cmd_nop, ALL_OK }, [WIN_CHECKPOWERMODE2] = { cmd_check_power_mode, ALL_OK | SET_DSC }, [WIN_SLEEPNOW2] = { cmd_nop, ALL_OK }, [WIN_PACKETCMD] = { cmd_packet, CD_OK }, [WIN_PIDENTIFY] = { cmd_identify_packet, CD_OK }, [WIN_SMART] = { cmd_smart, HD_CFA_OK | SET_DSC }, [CFA_ACCESS_METADATA_STORAGE] = { cmd_cfa_access_metadata_storage, CFA_OK }, [CFA_ERASE_SECTORS] = { cmd_cfa_erase_sectors, CFA_OK | SET_DSC }, [WIN_MULTREAD] = { cmd_read_multiple, HD_CFA_OK }, [WIN_MULTWRITE] = { cmd_write_multiple, HD_CFA_OK }, [WIN_SETMULT] = { cmd_set_multiple_mode, HD_CFA_OK | SET_DSC }, [WIN_READDMA] = { cmd_read_dma, HD_CFA_OK }, [WIN_READDMA_ONCE] = { cmd_read_dma, HD_CFA_OK }, [WIN_WRITEDMA] = { cmd_write_dma, HD_CFA_OK }, [WIN_WRITEDMA_ONCE] = { cmd_write_dma, HD_CFA_OK }, [CFA_WRITE_MULTI_WO_ERASE] = { cmd_write_multiple, CFA_OK }, [WIN_STANDBYNOW1] = { cmd_nop, ALL_OK }, [WIN_IDLEIMMEDIATE] = { cmd_nop, ALL_OK }, [WIN_STANDBY] = { cmd_nop, ALL_OK }, [WIN_SETIDLE1] = { cmd_nop, ALL_OK }, [WIN_CHECKPOWERMODE1] = { cmd_check_power_mode, ALL_OK | SET_DSC }, [WIN_SLEEPNOW1] = { cmd_nop, ALL_OK }, [WIN_FLUSH_CACHE] = { cmd_flush_cache, ALL_OK }, [WIN_FLUSH_CACHE_EXT] = { cmd_flush_cache, HD_CFA_OK }, [WIN_IDENTIFY] = { cmd_identify, ALL_OK }, [WIN_SETFEATURES] = { cmd_set_features, ALL_OK | SET_DSC }, [IBM_SENSE_CONDITION] = { cmd_ibm_sense_condition, CFA_OK | SET_DSC }, [CFA_WEAR_LEVEL] = { cmd_cfa_erase_sectors, HD_CFA_OK | SET_DSC }, [WIN_READ_NATIVE_MAX] = { cmd_read_native_max, ALL_OK | SET_DSC }, }; static bool ide_cmd_permitted(IDEState *s, uint32_t cmd) { return cmd < ARRAY_SIZE(ide_cmd_table) && (ide_cmd_table[cmd].flags & (1u << s->drive_kind)); } void ide_exec_cmd(IDEBus *bus, uint32_t val) { IDEState *s; #if defined(DEBUG_IDE) printf("ide: CMD=%02x\n", val); #endif s = idebus_active_if(bus); /* ignore commands to non existent slave */ if (s != bus->ifs && !s->bs) return; /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */ if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET) return; if (!ide_cmd_permitted(s, val)) { goto abort_cmd; } break; if (ide_cmd_table[val].handler != NULL) { bool complete; s->status = READY_STAT | BUSY_STAT; s->error = 0; complete = ide_cmd_table[val].handler(s, val); if (complete) { s->status &= ~BUSY_STAT; assert(!!s->error == !!(s->status & ERR_STAT)); if ((ide_cmd_table[val].flags & SET_DSC) && !s->error) { s->status |= SEEK_STAT; } ide_set_irq(s->bus); } return; } switch(val) { default: /* should not be reachable */ abort_cmd: Loading