diff --git a/blockdev.c b/blockdev.c index 99828ad2bd..9988eb4320 100644 --- a/blockdev.c +++ b/blockdev.c @@ -296,6 +296,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) BlockIOLimit io_limits; int snapshot = 0; bool copy_on_read; + bool locked; int ret; translation = BIOS_ATA_TRANSLATION_AUTO; @@ -314,6 +315,8 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) ro = qemu_opt_get_bool(opts, "readonly", 0); copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false); + locked = qemu_opt_get_bool(opts, "locked", false); + file = qemu_opt_get(opts, "file"); serial = qemu_opt_get(opts, "serial"); @@ -541,6 +544,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) dinfo->opts = opts; dinfo->refcount = 1; dinfo->serial = serial; + dinfo->locked = locked; QTAILQ_INSERT_TAIL(&drives, dinfo, next); bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error); diff --git a/blockdev.h b/blockdev.h index 5f27b643be..d9345fbf19 100644 --- a/blockdev.h +++ b/blockdev.h @@ -34,6 +34,7 @@ struct DriveInfo { int auto_del; /* see blockdev_mark_auto_del() */ int media_cd; int cyls, heads, secs, trans; + bool locked; QemuOpts *opts; const char *serial; QTAILQ_ENTRY(DriveInfo) next; diff --git a/hw/block-common.c b/hw/block-common.c index f0196d78dc..f1ba554c6e 100644 --- a/hw/block-common.c +++ b/hw/block-common.c @@ -24,6 +24,14 @@ void blkconf_serial(BlockConf *conf, char **serial) } } +void blkconf_locked(BlockConf *conf, bool *locked) +{ + DriveInfo *dinfo; + + dinfo = drive_get_by_blockdev(conf->bs); + *locked = dinfo->locked; +} + int blkconf_geometry(BlockConf *conf, int *ptrans, unsigned cyls_max, unsigned heads_max, unsigned secs_max) { diff --git a/hw/block-common.h b/hw/block-common.h index bb808f7f56..6b36808dfd 100644 --- a/hw/block-common.h +++ b/hw/block-common.h @@ -60,6 +60,7 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) /* Configuration helpers */ void blkconf_serial(BlockConf *conf, char **serial); +void blkconf_locked(BlockConf *conf, bool *locked); int blkconf_geometry(BlockConf *conf, int *trans, unsigned cyls_max, unsigned heads_max, unsigned secs_max); diff --git a/hw/ide/core.c b/hw/ide/core.c index d683a8cc84..b05617cb4a 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -57,6 +57,7 @@ static const int smart_attributes[][12] = { static int ide_handle_rw_error(IDEState *s, int error, int op); static void ide_dummy_transfer_stop(IDEState *s); +static void ide_security_cmd(IDEState *s); static void padstr(char *str, const char *src, int len) { @@ -176,6 +177,14 @@ static void ide_identify(IDEState *s) put_le16(p + 110, s->wwn >> 16); put_le16(p + 111, s->wwn); } + + /* 2: locked, 1: security enabled, 0: security supported */ + if (dev && dev->locked) { + put_le16(p + 128, (1 << 2) | (1 << 1) | 1); + } else { + put_le16(p + 128, (1 << 0)); + } + if (dev && dev->conf.discard_granularity) { put_le16(p + 169, 1); /* TRIM support */ } @@ -1026,6 +1035,7 @@ static const uint8_t ide_cmd_table[0x100] = { [IBM_SENSE_CONDITION] = CFA_OK, [CFA_WEAR_LEVEL] = HD_CFA_OK, [WIN_READ_NATIVE_MAX] = ALL_OK, + [WIN_SECURITY_UNLOCK] = ALL_OK, }; static bool ide_cmd_permitted(IDEState *s, uint32_t cmd) @@ -1343,6 +1353,12 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE, ide_atapi_cmd); break; + case WIN_SECURITY_UNLOCK: + s->error = 0; + s->status = READY_STAT | SEEK_STAT; + ide_transfer_start(s, s->io_buffer, 512, + ide_security_cmd); + break; /* CF-ATA commands */ case CFA_REQ_EXT_ERROR_CODE: s->error = 0x09; /* miscellaneous error */ @@ -1706,7 +1722,8 @@ void ide_cmd_write(void *opaque, uint32_t addr, uint32_t val) static bool ide_is_pio_out(IDEState *s) { if (s->end_transfer_func == ide_sector_write || - s->end_transfer_func == ide_atapi_cmd) { + s->end_transfer_func == ide_atapi_cmd || + s->end_transfer_func == ide_security_cmd) { return false; } else if (s->end_transfer_func == ide_sector_read || s->end_transfer_func == ide_transfer_stop || @@ -1812,6 +1829,17 @@ static void ide_dummy_transfer_stop(IDEState *s) s->io_buffer[3] = 0xff; } +static void ide_security_cmd(IDEState *s) +{ + /* XXX: Actually verify the password... */ + + put_le16((uint16_t *)s->identify_data + 128, (1 << 1) | 1); + + s->error = 0; + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s->bus); +} + static void ide_reset(IDEState *s) { #ifdef DEBUG_IDE @@ -2129,6 +2157,7 @@ static EndTransferFunc* transfer_end_table[] = { ide_transfer_stop, ide_atapi_cmd_reply_end, ide_atapi_cmd, + ide_security_cmd, ide_dummy_transfer_stop, }; diff --git a/hw/ide/internal.h b/hw/ide/internal.h index bf7d313cf4..e21729503a 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -481,6 +481,7 @@ struct IDEDevice { char *serial; char *model; uint64_t wwn; + bool locked; }; #define BM_STATUS_DMAING 0x01 diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index f2e4ea4207..5735a7d9d5 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -148,6 +148,7 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) return -1; } + blkconf_locked(&dev->conf, &dev->locked); blkconf_serial(&dev->conf, &dev->serial); if (kind != IDE_CD && blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255) < 0) { diff --git a/qemu-config.c b/qemu-config.c index cd1ec2165a..413ad1bcd4 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -114,6 +114,10 @@ static QemuOptsList qemu_drive_opts = { .name = "copy-on-read", .type = QEMU_OPT_BOOL, .help = "copy read data from backing file into image file", + },{ + .name = "locked", + .type = QEMU_OPT_BOOL, + .help = "emulate a security locked drive", }, { /* end of list */ } },