diff --git a/blockdev.c b/blockdev.c index c31bf3d98d..09a4fb4f58 100644 --- a/blockdev.c +++ b/blockdev.c @@ -776,6 +776,10 @@ QemuOptsList qemu_legacy_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 */ } @@ -804,6 +808,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) const char *deprecated[] = { "serial", "trans", "secs", "heads", "cyls", "addr" }; + bool locked; /* Change legacy command line options into QMP ones */ static const struct { @@ -1021,6 +1026,9 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) goto fail; } + /* Locked */ + locked = qemu_opt_get_bool(legacy_opts, "locked", false); + /* Serial number */ serial = qemu_opt_get(legacy_opts, "serial"); @@ -1114,6 +1122,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) dinfo->unit = unit_id; dinfo->devaddr = devaddr; dinfo->serial = g_strdup(serial); + dinfo->locked = locked; blk_set_legacy_dinfo(blk, dinfo); diff --git a/hw/block/block.c b/hw/block/block.c index b91e2b6d7e..bef24bfca8 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -15,6 +15,14 @@ #include "qapi/qapi-types-block.h" #include "qemu/error-report.h" +void blkconf_locked(BlockConf *conf, bool *locked) +{ + DriveInfo *dinfo; + + dinfo = blk_legacy_dinfo(conf->blk); + *locked = dinfo->locked; +} + void blkconf_serial(BlockConf *conf, char **serial) { DriveInfo *dinfo; diff --git a/hw/ide/core.c b/hw/ide/core.c index 866c659498..c1dfab4135 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -76,6 +76,7 @@ static const char *IDE_DMA_CMD_str(enum ide_dma_cmd enval) } 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) { @@ -213,6 +214,13 @@ static void ide_identify(IDEState *s) put_le16(p + 217, dev->rotation_rate); /* Nominal media rotation rate */ } + /* 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)); + } + ide_identify_size(s); s->identify_set = 1; @@ -1712,6 +1720,15 @@ static bool cmd_packet(IDEState *s, uint8_t cmd) return false; } +static bool cmd_security_unlock(IDEState *s, uint8_t cmd) +{ + s->error = 0; + s->status = READY_STAT | SEEK_STAT; + ide_transfer_start(s, s->io_buffer, 512, + ide_security_cmd); + return false; +} + /*** CF-ATA commands ***/ @@ -2046,6 +2063,7 @@ static const struct { [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, HD_CFA_OK | SET_DSC }, + [WIN_SECURITY_UNLOCK] = { cmd_security_unlock, ALL_OK | SET_DSC }, }; static bool ide_cmd_permitted(IDEState *s, uint32_t cmd) @@ -2264,7 +2282,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 || @@ -2404,6 +2423,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); +} + void ide_bus_reset(IDEBus *bus) { bus->unit = 0; @@ -2724,6 +2754,7 @@ static EndTransferFunc* transfer_end_table[] = { ide_atapi_cmd_reply_end, ide_atapi_cmd, ide_dummy_transfer_stop, + ide_security_cmd, }; static int transfer_end_table_idx(EndTransferFunc *fn) diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index f395d24592..11dd6ee611 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -188,6 +188,8 @@ static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp) return; } + blkconf_locked(&dev->conf, &dev->locked); + blkconf_serial(&dev->conf, &dev->serial); if (kind != IDE_CD) { if (!blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, diff --git a/include/hw/block/block.h b/include/hw/block/block.h index d4f4dfffab..ac51280d86 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -72,6 +72,7 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) /* Configuration helpers */ +void blkconf_locked(BlockConf *conf, bool *locked); void blkconf_serial(BlockConf *conf, char **serial); bool blkconf_geometry(BlockConf *conf, int *trans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 88212f59df..dcad2af2a1 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -506,6 +506,7 @@ struct IDEDevice { char *version; char *serial; char *model; + bool locked; uint64_t wwn; /* * 0x0000 - rotation rate not reported diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index ac22f2ae1f..73b6439055 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -38,6 +38,7 @@ struct DriveInfo { int cyls, heads, secs, trans; QemuOpts *opts; char *serial; + bool locked; QTAILQ_ENTRY(DriveInfo) next; };