mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-08 02:03:56 -06:00
* "info mtree" improvements (Alexey)
* fake VPD block limits for SCSI passthrough (Daniel Barboza) * chardev and main loop fixes (Daniel Berrangé, Sergio, Stefan) * help fixes (Eduardo) * pc-dimm refactoring (David) * tests improvements and fixes (Emilio, Thomas) * SVM emulation fixes (Jan) * MemoryRegionCache fix (Eric) * WHPX improvements (Justin) * ESP cleanup (Mark) * -overcommit option (Michael) * qemu-pr-helper fixes (me) * "info pic" improvements for x86 (Peter) * x86 TCG emulation fixes (Richard) * KVM slot handling fix (Shannon) * Next round of deprecation (Thomas) * Windows dump format support (Viktor) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQEcBAABAgAGBQJbNhHpAAoJEL/70l94x66D4ZoH/22cAWj2SOEVbHt1id1noVpI VxyS+MWXYG3se1HseFpNVlI32f3XyyABGMtJqzNuusD5s5Him8yZcwPxsu1RmEy2 uOk+PIo67qbLhJyZ+f3Q+rWRbFV9W+DvrRBM7RCArWUDCDOBaEVoPrRTWC2y3oId EdcLDc2tP/DvOmXtbNcELCuS3w6G2Nly0WwRI4VLJ2aJT6jAZoSfOONjuRg1gamw 7iUwk6UlCHmIMawnlwe1iQHtleX9KNYv0bA9etDrYIpNoZP935pGybchvztcmgMv QymjNptqse65emcbZ9rp0tqNyJhvP2wOyjQCWlCsooyRSoPQDY2Qc7623cRthqU= =D07Z -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging * "info mtree" improvements (Alexey) * fake VPD block limits for SCSI passthrough (Daniel Barboza) * chardev and main loop fixes (Daniel Berrangé, Sergio, Stefan) * help fixes (Eduardo) * pc-dimm refactoring (David) * tests improvements and fixes (Emilio, Thomas) * SVM emulation fixes (Jan) * MemoryRegionCache fix (Eric) * WHPX improvements (Justin) * ESP cleanup (Mark) * -overcommit option (Michael) * qemu-pr-helper fixes (me) * "info pic" improvements for x86 (Peter) * x86 TCG emulation fixes (Richard) * KVM slot handling fix (Shannon) * Next round of deprecation (Thomas) * Windows dump format support (Viktor) # gpg: Signature made Fri 29 Jun 2018 12:03:05 BST # gpg: using RSA key BFFBD25F78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini/tags/for-upstream: (60 commits) tests/boot-serial: Do not delete the output file in case of errors hw/scsi: add VPD Block Limits emulation hw/scsi: centralize SG_IO calls into single function hw/scsi: cleanups before VPD BL emulation dump: add Windows live system dump dump: add fallback KDBG using in Windows dump dump: use system context in Windows dump dump: add Windows dump format to dump-guest-memory i386/cpu: make -cpu host support monitor/mwait kvm: support -overcommit cpu-pm=on|off hmp: obsolete "info ioapic" ioapic: support "info irq" ioapic: some proper indents when dump info ioapic: support "info pic" doc: another fix to "info pic" target-i386: Mark cpu_vmexit noreturn target-i386: Allow interrupt injection after STGI target-i386: Add NMI interception to SVM memory/hmp: Print owners/parents in "info mtree" WHPX: register for unrecognized MSR exits ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
109b25045b
84 changed files with 1947 additions and 705 deletions
|
@ -190,8 +190,8 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev)
|
|||
/* Set up guest notifier (irq) */
|
||||
r = k->set_guest_notifiers(qbus->parent, nvqs, true);
|
||||
if (r != 0) {
|
||||
fprintf(stderr, "virtio-blk failed to set guest notifier (%d), "
|
||||
"ensure -enable-kvm is set\n", r);
|
||||
error_report("virtio-blk failed to set guest notifier (%d), "
|
||||
"ensure -accel kvm is set.", r);
|
||||
goto fail_guest_notifiers;
|
||||
}
|
||||
|
||||
|
|
|
@ -260,7 +260,7 @@ static void serial_xmit(SerialState *s)
|
|||
if (s->mcr & UART_MCR_LOOP) {
|
||||
/* in loopback mode, say that we just received a char */
|
||||
serial_receive1(s, &s->tsr, 1);
|
||||
} else if (qemu_chr_fe_write(&s->chr, &s->tsr, 1) != 1 &&
|
||||
} else if (qemu_chr_fe_write(&s->chr, &s->tsr, 1) == 0 &&
|
||||
s->tsr_retry < MAX_XMIT_RETRY) {
|
||||
assert(s->watch_tag == 0);
|
||||
s->watch_tag =
|
||||
|
|
|
@ -112,15 +112,6 @@ static void kvm_ioapic_put(IOAPICCommonState *s)
|
|||
}
|
||||
}
|
||||
|
||||
void kvm_ioapic_dump_state(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
IOAPICCommonState *s = IOAPIC_COMMON(object_resolve_path("ioapic", NULL));
|
||||
|
||||
assert(s);
|
||||
kvm_ioapic_get(s);
|
||||
ioapic_print_redtbl(mon, s);
|
||||
}
|
||||
|
||||
static void kvm_ioapic_reset(DeviceState *dev)
|
||||
{
|
||||
IOAPICCommonState *s = IOAPIC_COMMON(dev);
|
||||
|
@ -132,8 +123,10 @@ static void kvm_ioapic_reset(DeviceState *dev)
|
|||
static void kvm_ioapic_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
KVMIOAPICState *s = opaque;
|
||||
IOAPICCommonState *common = IOAPIC_COMMON(s);
|
||||
int delivered;
|
||||
|
||||
ioapic_stat_update_irq(common, irq, level);
|
||||
delivered = kvm_set_irq(kvm_state, s->kvm_gsi_base + irq, level);
|
||||
apic_report_irq_delivered(delivered);
|
||||
}
|
||||
|
|
73
hw/i386/pc.c
73
hw/i386/pc.c
|
@ -1674,27 +1674,11 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name)
|
|||
}
|
||||
}
|
||||
|
||||
static void pc_dimm_plug(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
Error **errp)
|
||||
{
|
||||
HotplugHandlerClass *hhc;
|
||||
Error *local_err = NULL;
|
||||
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
PCDIMMDevice *dimm = PC_DIMM(dev);
|
||||
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
|
||||
MemoryRegion *mr;
|
||||
uint64_t align = TARGET_PAGE_SIZE;
|
||||
bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
|
||||
|
||||
mr = ddc->get_memory_region(dimm, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) {
|
||||
align = memory_region_get_alignment(mr);
|
||||
}
|
||||
const PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
||||
const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
|
||||
|
||||
/*
|
||||
* When -no-acpi is used with Q35 machine type, no ACPI is built,
|
||||
|
@ -1702,18 +1686,35 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
|
|||
* addition to cover this case.
|
||||
*/
|
||||
if (!pcms->acpi_dev || !acpi_enabled) {
|
||||
error_setg(&local_err,
|
||||
error_setg(errp,
|
||||
"memory hotplug is not enabled: missing acpi device or acpi disabled");
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_nvdimm && !pcms->acpi_nvdimm_state.is_enabled) {
|
||||
error_setg(&local_err,
|
||||
"nvdimm is not enabled: missing 'nvdimm' in '-M'");
|
||||
goto out;
|
||||
error_setg(errp, "nvdimm is not enabled: missing 'nvdimm' in '-M'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void pc_memory_plug(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
HotplugHandlerClass *hhc;
|
||||
Error *local_err = NULL;
|
||||
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
PCDIMMDevice *dimm = PC_DIMM(dev);
|
||||
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
|
||||
MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort);
|
||||
uint64_t align = TARGET_PAGE_SIZE;
|
||||
bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
|
||||
|
||||
if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) {
|
||||
align = memory_region_get_alignment(mr);
|
||||
}
|
||||
|
||||
pc_dimm_memory_plug(dev, MACHINE(pcms), align, &local_err);
|
||||
pc_dimm_plug(dev, MACHINE(pcms), align, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -1728,8 +1729,8 @@ out:
|
|||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static void pc_dimm_unplug_request(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
static void pc_memory_unplug_request(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
HotplugHandlerClass *hhc;
|
||||
Error *local_err = NULL;
|
||||
|
@ -1759,8 +1760,8 @@ out:
|
|||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static void pc_dimm_unplug(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
static void pc_memory_unplug(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
||||
HotplugHandlerClass *hhc;
|
||||
|
@ -1773,7 +1774,7 @@ static void pc_dimm_unplug(HotplugHandler *hotplug_dev,
|
|||
goto out;
|
||||
}
|
||||
|
||||
pc_dimm_memory_unplug(dev, MACHINE(pcms));
|
||||
pc_dimm_unplug(dev, MACHINE(pcms));
|
||||
object_unparent(OBJECT(dev));
|
||||
|
||||
out:
|
||||
|
@ -2006,7 +2007,9 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
|
|||
static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
||||
pc_memory_pre_plug(hotplug_dev, dev, errp);
|
||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
||||
pc_cpu_pre_plug(hotplug_dev, dev, errp);
|
||||
}
|
||||
}
|
||||
|
@ -2015,7 +2018,7 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
|
|||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
||||
pc_dimm_plug(hotplug_dev, dev, errp);
|
||||
pc_memory_plug(hotplug_dev, dev, errp);
|
||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
||||
pc_cpu_plug(hotplug_dev, dev, errp);
|
||||
}
|
||||
|
@ -2025,7 +2028,7 @@ static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
|
|||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
||||
pc_dimm_unplug_request(hotplug_dev, dev, errp);
|
||||
pc_memory_unplug_request(hotplug_dev, dev, errp);
|
||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
||||
pc_cpu_unplug_request_cb(hotplug_dev, dev, errp);
|
||||
} else {
|
||||
|
@ -2038,7 +2041,7 @@ static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev,
|
|||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
||||
pc_dimm_unplug(hotplug_dev, dev, errp);
|
||||
pc_memory_unplug(hotplug_dev, dev, errp);
|
||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
||||
pc_cpu_unplug_cb(hotplug_dev, dev, errp);
|
||||
} else {
|
||||
|
|
|
@ -148,6 +148,7 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
|
|||
* the cleanest way of doing it but it should work. */
|
||||
|
||||
trace_ioapic_set_irq(vector, level);
|
||||
ioapic_stat_update_irq(s, vector, level);
|
||||
if (vector == 0) {
|
||||
vector = 2;
|
||||
}
|
||||
|
@ -233,17 +234,6 @@ void ioapic_eoi_broadcast(int vector)
|
|||
}
|
||||
}
|
||||
|
||||
void ioapic_dump_state(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_IOAPICS; i++) {
|
||||
if (ioapics[i] != 0) {
|
||||
ioapic_print_redtbl(mon, ioapics[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "monitor/monitor.h"
|
||||
#include "hw/i386/ioapic.h"
|
||||
#include "hw/i386/ioapic_internal.h"
|
||||
#include "hw/intc/intc.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
/* ioapic_no count start from 0 to MAX_IOAPICS,
|
||||
|
@ -34,6 +35,28 @@
|
|||
*/
|
||||
int ioapic_no;
|
||||
|
||||
void ioapic_stat_update_irq(IOAPICCommonState *s, int irq, int level)
|
||||
{
|
||||
if (level != s->irq_level[irq]) {
|
||||
s->irq_level[irq] = level;
|
||||
if (level == 1) {
|
||||
s->irq_count[irq]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool ioapic_get_statistics(InterruptStatsProvider *obj,
|
||||
uint64_t **irq_counts,
|
||||
unsigned int *nb_irqs)
|
||||
{
|
||||
IOAPICCommonState *s = IOAPIC_COMMON(obj);
|
||||
|
||||
*irq_counts = s->irq_count;
|
||||
*nb_irqs = IOAPIC_NUM_PINS;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ioapic_irr_dump(Monitor *mon, const char *name, uint32_t bitmap)
|
||||
{
|
||||
int i;
|
||||
|
@ -58,7 +81,7 @@ void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s)
|
|||
uint32_t remote_irr = 0;
|
||||
int i;
|
||||
|
||||
monitor_printf(mon, "ioapic ver=0x%x id=0x%02x sel=0x%02x",
|
||||
monitor_printf(mon, "ioapic0: ver=0x%x id=0x%02x sel=0x%02x",
|
||||
s->version, s->id, s->ioregsel);
|
||||
if (s->ioregsel) {
|
||||
monitor_printf(mon, " (redir[%u])\n",
|
||||
|
@ -70,7 +93,7 @@ void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s)
|
|||
uint64_t entry = s->ioredtbl[i];
|
||||
uint32_t delm = (uint32_t)((entry & IOAPIC_LVT_DELIV_MODE) >>
|
||||
IOAPIC_LVT_DELIV_MODE_SHIFT);
|
||||
monitor_printf(mon, "pin %-2u 0x%016"PRIx64" dest=%"PRIx64
|
||||
monitor_printf(mon, " pin %-2u 0x%016"PRIx64" dest=%"PRIx64
|
||||
" vec=%-3"PRIu64" %s %-5s %-6s %-6s %s\n",
|
||||
i, entry,
|
||||
(entry >> IOAPIC_LVT_DEST_SHIFT) &
|
||||
|
@ -85,8 +108,8 @@ void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s)
|
|||
remote_irr |= entry & IOAPIC_LVT_TRIGGER_MODE ?
|
||||
(entry & IOAPIC_LVT_REMOTE_IRR ? (1 << i) : 0) : 0;
|
||||
}
|
||||
ioapic_irr_dump(mon, "IRR", s->irr);
|
||||
ioapic_irr_dump(mon, "Remote IRR", remote_irr);
|
||||
ioapic_irr_dump(mon, " IRR", s->irr);
|
||||
ioapic_irr_dump(mon, " Remote IRR", remote_irr);
|
||||
}
|
||||
|
||||
void ioapic_reset_common(DeviceState *dev)
|
||||
|
@ -142,6 +165,15 @@ static void ioapic_common_realize(DeviceState *dev, Error **errp)
|
|||
ioapic_no++;
|
||||
}
|
||||
|
||||
static void ioapic_print_info(InterruptStatsProvider *obj,
|
||||
Monitor *mon)
|
||||
{
|
||||
IOAPICCommonState *s = IOAPIC_COMMON(obj);
|
||||
|
||||
ioapic_dispatch_pre_save(s);
|
||||
ioapic_print_redtbl(mon, s);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_ioapic_common = {
|
||||
.name = "ioapic",
|
||||
.version_id = 3,
|
||||
|
@ -161,9 +193,12 @@ static const VMStateDescription vmstate_ioapic_common = {
|
|||
static void ioapic_common_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass);
|
||||
|
||||
dc->realize = ioapic_common_realize;
|
||||
dc->vmsd = &vmstate_ioapic_common;
|
||||
ic->print_info = ioapic_print_info;
|
||||
ic->get_statistics = ioapic_get_statistics;
|
||||
}
|
||||
|
||||
static const TypeInfo ioapic_common_type = {
|
||||
|
@ -173,6 +208,10 @@ static const TypeInfo ioapic_common_type = {
|
|||
.class_size = sizeof(IOAPICCommonClass),
|
||||
.class_init = ioapic_common_class_init,
|
||||
.abstract = true,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_INTERRUPT_STATS_PROVIDER },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void ioapic_common_register_types(void)
|
||||
|
|
|
@ -116,9 +116,15 @@ uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint,
|
|||
address_space_start = ms->device_memory->base;
|
||||
address_space_end = address_space_start +
|
||||
memory_region_size(&ms->device_memory->mr);
|
||||
g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start);
|
||||
g_assert(address_space_end >= address_space_start);
|
||||
|
||||
/* address_space_start indicates the maximum alignment we expect */
|
||||
if (QEMU_ALIGN_UP(address_space_start, align) != address_space_start) {
|
||||
error_setg(errp, "the alignment (0%" PRIx64 ") is not supported",
|
||||
align);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memory_device_check_addable(ms, size, errp);
|
||||
if (*errp) {
|
||||
return 0;
|
||||
|
|
|
@ -43,7 +43,7 @@ static void nvdimm_set_label_size(Object *obj, Visitor *v, const char *name,
|
|||
Error *local_err = NULL;
|
||||
uint64_t value;
|
||||
|
||||
if (memory_region_size(&nvdimm->nvdimm_mr)) {
|
||||
if (nvdimm->nvdimm_mr) {
|
||||
error_setg(&local_err, "cannot change property value");
|
||||
goto out;
|
||||
}
|
||||
|
@ -64,52 +64,36 @@ out:
|
|||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static bool nvdimm_get_unarmed(Object *obj, Error **errp)
|
||||
{
|
||||
NVDIMMDevice *nvdimm = NVDIMM(obj);
|
||||
|
||||
return nvdimm->unarmed;
|
||||
}
|
||||
|
||||
static void nvdimm_set_unarmed(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
NVDIMMDevice *nvdimm = NVDIMM(obj);
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (memory_region_size(&nvdimm->nvdimm_mr)) {
|
||||
error_setg(&local_err, "cannot change property value");
|
||||
goto out;
|
||||
}
|
||||
|
||||
nvdimm->unarmed = value;
|
||||
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static void nvdimm_init(Object *obj)
|
||||
{
|
||||
object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "int",
|
||||
nvdimm_get_label_size, nvdimm_set_label_size, NULL,
|
||||
NULL, NULL);
|
||||
object_property_add_bool(obj, NVDIMM_UNARMED_PROP,
|
||||
nvdimm_get_unarmed, nvdimm_set_unarmed, NULL);
|
||||
}
|
||||
|
||||
static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm, Error **errp)
|
||||
static void nvdimm_finalize(Object *obj)
|
||||
{
|
||||
NVDIMMDevice *nvdimm = NVDIMM(dimm);
|
||||
NVDIMMDevice *nvdimm = NVDIMM(obj);
|
||||
|
||||
return &nvdimm->nvdimm_mr;
|
||||
g_free(nvdimm->nvdimm_mr);
|
||||
}
|
||||
|
||||
static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp)
|
||||
static void nvdimm_prepare_memory_region(NVDIMMDevice *nvdimm, Error **errp)
|
||||
{
|
||||
MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem, errp);
|
||||
NVDIMMDevice *nvdimm = NVDIMM(dimm);
|
||||
uint64_t align, pmem_size, size = memory_region_size(mr);
|
||||
PCDIMMDevice *dimm = PC_DIMM(nvdimm);
|
||||
uint64_t align, pmem_size, size;
|
||||
MemoryRegion *mr;
|
||||
|
||||
g_assert(!nvdimm->nvdimm_mr);
|
||||
|
||||
if (!dimm->hostmem) {
|
||||
error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property must be set");
|
||||
return;
|
||||
}
|
||||
|
||||
mr = host_memory_backend_get_memory(dimm->hostmem);
|
||||
align = memory_region_get_alignment(mr);
|
||||
size = memory_region_size(mr);
|
||||
|
||||
pmem_size = size - nvdimm->label_size;
|
||||
nvdimm->label_data = memory_region_get_ram_ptr(mr) + pmem_size;
|
||||
|
@ -127,9 +111,34 @@ static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
memory_region_init_alias(&nvdimm->nvdimm_mr, OBJECT(dimm),
|
||||
nvdimm->nvdimm_mr = g_new(MemoryRegion, 1);
|
||||
memory_region_init_alias(nvdimm->nvdimm_mr, OBJECT(dimm),
|
||||
"nvdimm-memory", mr, 0, pmem_size);
|
||||
nvdimm->nvdimm_mr.align = align;
|
||||
nvdimm->nvdimm_mr->align = align;
|
||||
}
|
||||
|
||||
static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm, Error **errp)
|
||||
{
|
||||
NVDIMMDevice *nvdimm = NVDIMM(dimm);
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!nvdimm->nvdimm_mr) {
|
||||
nvdimm_prepare_memory_region(nvdimm, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return nvdimm->nvdimm_mr;
|
||||
}
|
||||
|
||||
static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp)
|
||||
{
|
||||
NVDIMMDevice *nvdimm = NVDIMM(dimm);
|
||||
|
||||
if (!nvdimm->nvdimm_mr) {
|
||||
nvdimm_prepare_memory_region(nvdimm, errp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -161,24 +170,25 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf,
|
|||
|
||||
memcpy(nvdimm->label_data + offset, buf, size);
|
||||
|
||||
mr = host_memory_backend_get_memory(dimm->hostmem, &error_abort);
|
||||
mr = host_memory_backend_get_memory(dimm->hostmem);
|
||||
backend_offset = memory_region_size(mr) - nvdimm->label_size + offset;
|
||||
memory_region_set_dirty(mr, backend_offset, size);
|
||||
}
|
||||
|
||||
static MemoryRegion *nvdimm_get_vmstate_memory_region(PCDIMMDevice *dimm)
|
||||
{
|
||||
return host_memory_backend_get_memory(dimm->hostmem, &error_abort);
|
||||
}
|
||||
static Property nvdimm_properties[] = {
|
||||
DEFINE_PROP_BOOL(NVDIMM_UNARMED_PROP, NVDIMMDevice, unarmed, false),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void nvdimm_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc);
|
||||
NVDIMMClass *nvc = NVDIMM_CLASS(oc);
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
ddc->realize = nvdimm_realize;
|
||||
ddc->get_memory_region = nvdimm_get_memory_region;
|
||||
ddc->get_vmstate_memory_region = nvdimm_get_vmstate_memory_region;
|
||||
dc->props = nvdimm_properties;
|
||||
|
||||
nvc->read_label_data = nvdimm_read_label_data;
|
||||
nvc->write_label_data = nvdimm_write_label_data;
|
||||
|
@ -191,6 +201,7 @@ static TypeInfo nvdimm_info = {
|
|||
.class_init = nvdimm_class_init,
|
||||
.instance_size = sizeof(NVDIMMDevice),
|
||||
.instance_init = nvdimm_init,
|
||||
.instance_finalize = nvdimm_finalize,
|
||||
};
|
||||
|
||||
static void nvdimm_register_types(void)
|
||||
|
|
|
@ -27,27 +27,20 @@
|
|||
#include "sysemu/numa.h"
|
||||
#include "trace.h"
|
||||
|
||||
typedef struct pc_dimms_capacity {
|
||||
uint64_t size;
|
||||
Error **errp;
|
||||
} pc_dimms_capacity;
|
||||
static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp);
|
||||
|
||||
void pc_dimm_memory_plug(DeviceState *dev, MachineState *machine,
|
||||
uint64_t align, Error **errp)
|
||||
void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align,
|
||||
Error **errp)
|
||||
{
|
||||
int slot;
|
||||
PCDIMMDevice *dimm = PC_DIMM(dev);
|
||||
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
|
||||
MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm);
|
||||
MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm,
|
||||
&error_abort);
|
||||
MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort);
|
||||
Error *local_err = NULL;
|
||||
MemoryRegion *mr;
|
||||
uint64_t addr;
|
||||
|
||||
mr = ddc->get_memory_region(dimm, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
addr = object_property_get_uint(OBJECT(dimm),
|
||||
PC_DIMM_ADDR_PROP, &local_err);
|
||||
if (local_err) {
|
||||
|
@ -89,11 +82,12 @@ out:
|
|||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
void pc_dimm_memory_unplug(DeviceState *dev, MachineState *machine)
|
||||
void pc_dimm_unplug(DeviceState *dev, MachineState *machine)
|
||||
{
|
||||
PCDIMMDevice *dimm = PC_DIMM(dev);
|
||||
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
|
||||
MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm);
|
||||
MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm,
|
||||
&error_abort);
|
||||
MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort);
|
||||
|
||||
memory_device_unplug_region(machine, mr);
|
||||
|
@ -116,7 +110,7 @@ static int pc_dimm_slot2bitmap(Object *obj, void *opaque)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp)
|
||||
static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp)
|
||||
{
|
||||
unsigned long *bitmap;
|
||||
int slot = 0;
|
||||
|
@ -229,12 +223,7 @@ static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
return host_memory_backend_get_memory(dimm->hostmem, errp);
|
||||
}
|
||||
|
||||
static MemoryRegion *pc_dimm_get_vmstate_memory_region(PCDIMMDevice *dimm)
|
||||
{
|
||||
return host_memory_backend_get_memory(dimm->hostmem, &error_abort);
|
||||
return host_memory_backend_get_memory(dimm->hostmem);
|
||||
}
|
||||
|
||||
static uint64_t pc_dimm_md_get_addr(const MemoryDeviceState *md)
|
||||
|
@ -301,7 +290,7 @@ static void pc_dimm_class_init(ObjectClass *oc, void *data)
|
|||
dc->desc = "DIMM memory module";
|
||||
|
||||
ddc->get_memory_region = pc_dimm_get_memory_region;
|
||||
ddc->get_vmstate_memory_region = pc_dimm_get_vmstate_memory_region;
|
||||
ddc->get_vmstate_memory_region = pc_dimm_get_memory_region;
|
||||
|
||||
mdc->get_addr = pc_dimm_md_get_addr;
|
||||
/* for a dimm plugged_size == region_size */
|
||||
|
|
|
@ -145,10 +145,10 @@ static void mips_jazz_init(MachineState *machine,
|
|||
ISABus *isa_bus;
|
||||
ISADevice *pit;
|
||||
DriveInfo *fds[MAX_FD];
|
||||
qemu_irq esp_reset, dma_enable;
|
||||
MemoryRegion *ram = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *bios = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *bios2 = g_new(MemoryRegion, 1);
|
||||
SysBusESPState *sysbus_esp;
|
||||
ESPState *esp;
|
||||
|
||||
/* init CPUs */
|
||||
|
@ -281,8 +281,21 @@ static void mips_jazz_init(MachineState *machine,
|
|||
}
|
||||
|
||||
/* SCSI adapter */
|
||||
esp = esp_init(0x80002000, 0, rc4030_dma_read, rc4030_dma_write, dmas[0],
|
||||
qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable);
|
||||
dev = qdev_create(NULL, TYPE_ESP);
|
||||
sysbus_esp = ESP_STATE(dev);
|
||||
esp = &sysbus_esp->esp;
|
||||
esp->dma_memory_read = rc4030_dma_read;
|
||||
esp->dma_memory_write = rc4030_dma_write;
|
||||
esp->dma_opaque = dmas[0];
|
||||
sysbus_esp->it_shift = 0;
|
||||
/* XXX for now until rc4030 has been changed to use DMA enable signal */
|
||||
esp->dma_enabled = 1;
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
sysbus = SYS_BUS_DEVICE(dev);
|
||||
sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 5));
|
||||
sysbus_mmio_map(sysbus, 0, 0x80002000);
|
||||
|
||||
scsi_bus_legacy_handle_cmdline(&esp->bus);
|
||||
|
||||
/* Floppy */
|
||||
|
|
|
@ -909,8 +909,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
|
|||
if (s->hostmem != NULL) {
|
||||
IVSHMEM_DPRINTF("using hostmem\n");
|
||||
|
||||
s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem,
|
||||
&error_abort);
|
||||
s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem);
|
||||
} else {
|
||||
Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr);
|
||||
assert(chr);
|
||||
|
|
|
@ -3149,18 +3149,14 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|||
sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev);
|
||||
PCDIMMDevice *dimm = PC_DIMM(dev);
|
||||
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
|
||||
MemoryRegion *mr;
|
||||
MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort);
|
||||
uint64_t align, size, addr;
|
||||
uint32_t node;
|
||||
|
||||
mr = ddc->get_memory_region(dimm, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
align = memory_region_get_alignment(mr);
|
||||
size = memory_region_size(mr);
|
||||
|
||||
pc_dimm_memory_plug(dev, MACHINE(ms), align, &local_err);
|
||||
pc_dimm_plug(dev, MACHINE(ms), align, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -3183,7 +3179,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|||
return;
|
||||
|
||||
out_unplug:
|
||||
pc_dimm_memory_unplug(dev, MACHINE(ms));
|
||||
pc_dimm_unplug(dev, MACHINE(ms));
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
@ -3332,7 +3328,7 @@ static void spapr_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev)
|
|||
sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev);
|
||||
sPAPRDIMMState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev));
|
||||
|
||||
pc_dimm_memory_unplug(dev, MACHINE(hotplug_dev));
|
||||
pc_dimm_unplug(dev, MACHINE(hotplug_dev));
|
||||
object_unparent(OBJECT(dev));
|
||||
spapr_pending_dimm_unplugs_remove(spapr, ds);
|
||||
}
|
||||
|
@ -3344,16 +3340,12 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev,
|
|||
Error *local_err = NULL;
|
||||
PCDIMMDevice *dimm = PC_DIMM(dev);
|
||||
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
|
||||
MemoryRegion *mr;
|
||||
MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort);
|
||||
uint32_t nr_lmbs;
|
||||
uint64_t size, addr_start, addr;
|
||||
int i;
|
||||
sPAPRDRConnector *drc;
|
||||
|
||||
mr = ddc->get_memory_region(dimm, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
size = memory_region_size(mr);
|
||||
nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE;
|
||||
|
||||
|
|
|
@ -619,36 +619,6 @@ static const MemoryRegionOps sysbus_esp_mem_ops = {
|
|||
.valid.accepts = esp_mem_accepts,
|
||||
};
|
||||
|
||||
ESPState *esp_init(hwaddr espaddr, int it_shift,
|
||||
ESPDMAMemoryReadWriteFunc dma_memory_read,
|
||||
ESPDMAMemoryReadWriteFunc dma_memory_write,
|
||||
void *dma_opaque, qemu_irq irq, qemu_irq *reset,
|
||||
qemu_irq *dma_enable)
|
||||
{
|
||||
DeviceState *dev;
|
||||
SysBusDevice *s;
|
||||
SysBusESPState *sysbus;
|
||||
ESPState *esp;
|
||||
|
||||
dev = qdev_create(NULL, TYPE_ESP);
|
||||
sysbus = ESP_STATE(dev);
|
||||
esp = &sysbus->esp;
|
||||
esp->dma_memory_read = dma_memory_read;
|
||||
esp->dma_memory_write = dma_memory_write;
|
||||
esp->dma_opaque = dma_opaque;
|
||||
sysbus->it_shift = it_shift;
|
||||
/* XXX for now until rc4030 has been changed to use DMA enable signal */
|
||||
esp->dma_enabled = 1;
|
||||
qdev_init_nofail(dev);
|
||||
s = SYS_BUS_DEVICE(dev);
|
||||
sysbus_connect_irq(s, 0, irq);
|
||||
sysbus_mmio_map(s, 0, espaddr);
|
||||
*reset = qdev_get_gpio_in(dev, 0);
|
||||
*dma_enable = qdev_get_gpio_in(dev, 1);
|
||||
|
||||
return esp;
|
||||
}
|
||||
|
||||
static const struct SCSIBusInfo esp_scsi_info = {
|
||||
.tcq = false,
|
||||
.max_target = ESP_MAX_DEVS,
|
||||
|
|
|
@ -585,219 +585,228 @@ static uint8_t *scsi_get_buf(SCSIRequest *req)
|
|||
return (uint8_t *)r->iov.iov_base;
|
||||
}
|
||||
|
||||
int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
||||
uint8_t page_code = req->cmd.buf[2];
|
||||
int start, buflen = 0;
|
||||
|
||||
outbuf[buflen++] = s->qdev.type & 0x1f;
|
||||
outbuf[buflen++] = page_code;
|
||||
outbuf[buflen++] = 0x00;
|
||||
outbuf[buflen++] = 0x00;
|
||||
start = buflen;
|
||||
|
||||
switch (page_code) {
|
||||
case 0x00: /* Supported page codes, mandatory */
|
||||
{
|
||||
DPRINTF("Inquiry EVPD[Supported pages] "
|
||||
"buffer size %zd\n", req->cmd.xfer);
|
||||
outbuf[buflen++] = 0x00; /* list of supported pages (this page) */
|
||||
if (s->serial) {
|
||||
outbuf[buflen++] = 0x80; /* unit serial number */
|
||||
}
|
||||
outbuf[buflen++] = 0x83; /* device identification */
|
||||
if (s->qdev.type == TYPE_DISK) {
|
||||
outbuf[buflen++] = 0xb0; /* block limits */
|
||||
outbuf[buflen++] = 0xb1; /* block device characteristics */
|
||||
outbuf[buflen++] = 0xb2; /* thin provisioning */
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x80: /* Device serial number, optional */
|
||||
{
|
||||
int l;
|
||||
|
||||
if (!s->serial) {
|
||||
DPRINTF("Inquiry (EVPD[Serial number] not supported\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
l = strlen(s->serial);
|
||||
if (l > 36) {
|
||||
l = 36;
|
||||
}
|
||||
|
||||
DPRINTF("Inquiry EVPD[Serial number] "
|
||||
"buffer size %zd\n", req->cmd.xfer);
|
||||
memcpy(outbuf + buflen, s->serial, l);
|
||||
buflen += l;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x83: /* Device identification page, mandatory */
|
||||
{
|
||||
const char *str = s->serial ?: blk_name(s->qdev.conf.blk);
|
||||
int max_len = s->serial ? 20 : 255 - 8;
|
||||
int id_len = strlen(str);
|
||||
|
||||
if (id_len > max_len) {
|
||||
id_len = max_len;
|
||||
}
|
||||
DPRINTF("Inquiry EVPD[Device identification] "
|
||||
"buffer size %zd\n", req->cmd.xfer);
|
||||
|
||||
outbuf[buflen++] = 0x2; /* ASCII */
|
||||
outbuf[buflen++] = 0; /* not officially assigned */
|
||||
outbuf[buflen++] = 0; /* reserved */
|
||||
outbuf[buflen++] = id_len; /* length of data following */
|
||||
memcpy(outbuf + buflen, str, id_len);
|
||||
buflen += id_len;
|
||||
|
||||
if (s->qdev.wwn) {
|
||||
outbuf[buflen++] = 0x1; /* Binary */
|
||||
outbuf[buflen++] = 0x3; /* NAA */
|
||||
outbuf[buflen++] = 0; /* reserved */
|
||||
outbuf[buflen++] = 8;
|
||||
stq_be_p(&outbuf[buflen], s->qdev.wwn);
|
||||
buflen += 8;
|
||||
}
|
||||
|
||||
if (s->qdev.port_wwn) {
|
||||
outbuf[buflen++] = 0x61; /* SAS / Binary */
|
||||
outbuf[buflen++] = 0x93; /* PIV / Target port / NAA */
|
||||
outbuf[buflen++] = 0; /* reserved */
|
||||
outbuf[buflen++] = 8;
|
||||
stq_be_p(&outbuf[buflen], s->qdev.port_wwn);
|
||||
buflen += 8;
|
||||
}
|
||||
|
||||
if (s->port_index) {
|
||||
outbuf[buflen++] = 0x61; /* SAS / Binary */
|
||||
|
||||
/* PIV/Target port/relative target port */
|
||||
outbuf[buflen++] = 0x94;
|
||||
|
||||
outbuf[buflen++] = 0; /* reserved */
|
||||
outbuf[buflen++] = 4;
|
||||
stw_be_p(&outbuf[buflen + 2], s->port_index);
|
||||
buflen += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xb0: /* block limits */
|
||||
{
|
||||
unsigned int unmap_sectors =
|
||||
s->qdev.conf.discard_granularity / s->qdev.blocksize;
|
||||
unsigned int min_io_size =
|
||||
s->qdev.conf.min_io_size / s->qdev.blocksize;
|
||||
unsigned int opt_io_size =
|
||||
s->qdev.conf.opt_io_size / s->qdev.blocksize;
|
||||
unsigned int max_unmap_sectors =
|
||||
s->max_unmap_size / s->qdev.blocksize;
|
||||
unsigned int max_io_sectors =
|
||||
s->max_io_size / s->qdev.blocksize;
|
||||
|
||||
if (s->qdev.type == TYPE_ROM) {
|
||||
DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
|
||||
page_code);
|
||||
return -1;
|
||||
}
|
||||
if (s->qdev.type == TYPE_DISK) {
|
||||
int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk);
|
||||
int max_io_sectors_blk =
|
||||
max_transfer_blk / s->qdev.blocksize;
|
||||
|
||||
max_io_sectors =
|
||||
MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors);
|
||||
|
||||
/* min_io_size and opt_io_size can't be greater than
|
||||
* max_io_sectors */
|
||||
if (min_io_size) {
|
||||
min_io_size = MIN(min_io_size, max_io_sectors);
|
||||
}
|
||||
if (opt_io_size) {
|
||||
opt_io_size = MIN(opt_io_size, max_io_sectors);
|
||||
}
|
||||
}
|
||||
/* required VPD size with unmap support */
|
||||
buflen = 0x40;
|
||||
memset(outbuf + 4, 0, buflen - 4);
|
||||
|
||||
outbuf[4] = 0x1; /* wsnz */
|
||||
|
||||
/* optimal transfer length granularity */
|
||||
outbuf[6] = (min_io_size >> 8) & 0xff;
|
||||
outbuf[7] = min_io_size & 0xff;
|
||||
|
||||
/* maximum transfer length */
|
||||
outbuf[8] = (max_io_sectors >> 24) & 0xff;
|
||||
outbuf[9] = (max_io_sectors >> 16) & 0xff;
|
||||
outbuf[10] = (max_io_sectors >> 8) & 0xff;
|
||||
outbuf[11] = max_io_sectors & 0xff;
|
||||
|
||||
/* optimal transfer length */
|
||||
outbuf[12] = (opt_io_size >> 24) & 0xff;
|
||||
outbuf[13] = (opt_io_size >> 16) & 0xff;
|
||||
outbuf[14] = (opt_io_size >> 8) & 0xff;
|
||||
outbuf[15] = opt_io_size & 0xff;
|
||||
|
||||
/* max unmap LBA count, default is 1GB */
|
||||
outbuf[20] = (max_unmap_sectors >> 24) & 0xff;
|
||||
outbuf[21] = (max_unmap_sectors >> 16) & 0xff;
|
||||
outbuf[22] = (max_unmap_sectors >> 8) & 0xff;
|
||||
outbuf[23] = max_unmap_sectors & 0xff;
|
||||
|
||||
/* max unmap descriptors, 255 fit in 4 kb with an 8-byte header */
|
||||
outbuf[24] = 0;
|
||||
outbuf[25] = 0;
|
||||
outbuf[26] = 0;
|
||||
outbuf[27] = 255;
|
||||
|
||||
/* optimal unmap granularity */
|
||||
outbuf[28] = (unmap_sectors >> 24) & 0xff;
|
||||
outbuf[29] = (unmap_sectors >> 16) & 0xff;
|
||||
outbuf[30] = (unmap_sectors >> 8) & 0xff;
|
||||
outbuf[31] = unmap_sectors & 0xff;
|
||||
|
||||
/* max write same size */
|
||||
outbuf[36] = 0;
|
||||
outbuf[37] = 0;
|
||||
outbuf[38] = 0;
|
||||
outbuf[39] = 0;
|
||||
|
||||
outbuf[40] = (max_io_sectors >> 24) & 0xff;
|
||||
outbuf[41] = (max_io_sectors >> 16) & 0xff;
|
||||
outbuf[42] = (max_io_sectors >> 8) & 0xff;
|
||||
outbuf[43] = max_io_sectors & 0xff;
|
||||
break;
|
||||
}
|
||||
case 0xb1: /* block device characteristics */
|
||||
{
|
||||
buflen = 8;
|
||||
outbuf[4] = (s->rotation_rate >> 8) & 0xff;
|
||||
outbuf[5] = s->rotation_rate & 0xff;
|
||||
outbuf[6] = 0;
|
||||
outbuf[7] = 0;
|
||||
break;
|
||||
}
|
||||
case 0xb2: /* thin provisioning */
|
||||
{
|
||||
buflen = 8;
|
||||
outbuf[4] = 0;
|
||||
outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */
|
||||
outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
|
||||
outbuf[7] = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
/* done with EVPD */
|
||||
assert(buflen - start <= 255);
|
||||
outbuf[start - 1] = buflen - start;
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
||||
int buflen = 0;
|
||||
int start;
|
||||
|
||||
if (req->cmd.buf[1] & 0x1) {
|
||||
/* Vital product data */
|
||||
uint8_t page_code = req->cmd.buf[2];
|
||||
|
||||
outbuf[buflen++] = s->qdev.type & 0x1f;
|
||||
outbuf[buflen++] = page_code ; // this page
|
||||
outbuf[buflen++] = 0x00;
|
||||
outbuf[buflen++] = 0x00;
|
||||
start = buflen;
|
||||
|
||||
switch (page_code) {
|
||||
case 0x00: /* Supported page codes, mandatory */
|
||||
{
|
||||
DPRINTF("Inquiry EVPD[Supported pages] "
|
||||
"buffer size %zd\n", req->cmd.xfer);
|
||||
outbuf[buflen++] = 0x00; // list of supported pages (this page)
|
||||
if (s->serial) {
|
||||
outbuf[buflen++] = 0x80; // unit serial number
|
||||
}
|
||||
outbuf[buflen++] = 0x83; // device identification
|
||||
if (s->qdev.type == TYPE_DISK) {
|
||||
outbuf[buflen++] = 0xb0; // block limits
|
||||
outbuf[buflen++] = 0xb1; /* block device characteristics */
|
||||
outbuf[buflen++] = 0xb2; // thin provisioning
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x80: /* Device serial number, optional */
|
||||
{
|
||||
int l;
|
||||
|
||||
if (!s->serial) {
|
||||
DPRINTF("Inquiry (EVPD[Serial number] not supported\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
l = strlen(s->serial);
|
||||
if (l > 36) {
|
||||
l = 36;
|
||||
}
|
||||
|
||||
DPRINTF("Inquiry EVPD[Serial number] "
|
||||
"buffer size %zd\n", req->cmd.xfer);
|
||||
memcpy(outbuf+buflen, s->serial, l);
|
||||
buflen += l;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x83: /* Device identification page, mandatory */
|
||||
{
|
||||
const char *str = s->serial ?: blk_name(s->qdev.conf.blk);
|
||||
int max_len = s->serial ? 20 : 255 - 8;
|
||||
int id_len = strlen(str);
|
||||
|
||||
if (id_len > max_len) {
|
||||
id_len = max_len;
|
||||
}
|
||||
DPRINTF("Inquiry EVPD[Device identification] "
|
||||
"buffer size %zd\n", req->cmd.xfer);
|
||||
|
||||
outbuf[buflen++] = 0x2; // ASCII
|
||||
outbuf[buflen++] = 0; // not officially assigned
|
||||
outbuf[buflen++] = 0; // reserved
|
||||
outbuf[buflen++] = id_len; // length of data following
|
||||
memcpy(outbuf+buflen, str, id_len);
|
||||
buflen += id_len;
|
||||
|
||||
if (s->qdev.wwn) {
|
||||
outbuf[buflen++] = 0x1; // Binary
|
||||
outbuf[buflen++] = 0x3; // NAA
|
||||
outbuf[buflen++] = 0; // reserved
|
||||
outbuf[buflen++] = 8;
|
||||
stq_be_p(&outbuf[buflen], s->qdev.wwn);
|
||||
buflen += 8;
|
||||
}
|
||||
|
||||
if (s->qdev.port_wwn) {
|
||||
outbuf[buflen++] = 0x61; // SAS / Binary
|
||||
outbuf[buflen++] = 0x93; // PIV / Target port / NAA
|
||||
outbuf[buflen++] = 0; // reserved
|
||||
outbuf[buflen++] = 8;
|
||||
stq_be_p(&outbuf[buflen], s->qdev.port_wwn);
|
||||
buflen += 8;
|
||||
}
|
||||
|
||||
if (s->port_index) {
|
||||
outbuf[buflen++] = 0x61; // SAS / Binary
|
||||
outbuf[buflen++] = 0x94; // PIV / Target port / relative target port
|
||||
outbuf[buflen++] = 0; // reserved
|
||||
outbuf[buflen++] = 4;
|
||||
stw_be_p(&outbuf[buflen + 2], s->port_index);
|
||||
buflen += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xb0: /* block limits */
|
||||
{
|
||||
unsigned int unmap_sectors =
|
||||
s->qdev.conf.discard_granularity / s->qdev.blocksize;
|
||||
unsigned int min_io_size =
|
||||
s->qdev.conf.min_io_size / s->qdev.blocksize;
|
||||
unsigned int opt_io_size =
|
||||
s->qdev.conf.opt_io_size / s->qdev.blocksize;
|
||||
unsigned int max_unmap_sectors =
|
||||
s->max_unmap_size / s->qdev.blocksize;
|
||||
unsigned int max_io_sectors =
|
||||
s->max_io_size / s->qdev.blocksize;
|
||||
|
||||
if (s->qdev.type == TYPE_ROM) {
|
||||
DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
|
||||
page_code);
|
||||
return -1;
|
||||
}
|
||||
if (s->qdev.type == TYPE_DISK) {
|
||||
int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk);
|
||||
int max_io_sectors_blk =
|
||||
max_transfer_blk / s->qdev.blocksize;
|
||||
|
||||
max_io_sectors =
|
||||
MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors);
|
||||
|
||||
/* min_io_size and opt_io_size can't be greater than
|
||||
* max_io_sectors */
|
||||
if (min_io_size) {
|
||||
min_io_size = MIN(min_io_size, max_io_sectors);
|
||||
}
|
||||
if (opt_io_size) {
|
||||
opt_io_size = MIN(opt_io_size, max_io_sectors);
|
||||
}
|
||||
}
|
||||
/* required VPD size with unmap support */
|
||||
buflen = 0x40;
|
||||
memset(outbuf + 4, 0, buflen - 4);
|
||||
|
||||
outbuf[4] = 0x1; /* wsnz */
|
||||
|
||||
/* optimal transfer length granularity */
|
||||
outbuf[6] = (min_io_size >> 8) & 0xff;
|
||||
outbuf[7] = min_io_size & 0xff;
|
||||
|
||||
/* maximum transfer length */
|
||||
outbuf[8] = (max_io_sectors >> 24) & 0xff;
|
||||
outbuf[9] = (max_io_sectors >> 16) & 0xff;
|
||||
outbuf[10] = (max_io_sectors >> 8) & 0xff;
|
||||
outbuf[11] = max_io_sectors & 0xff;
|
||||
|
||||
/* optimal transfer length */
|
||||
outbuf[12] = (opt_io_size >> 24) & 0xff;
|
||||
outbuf[13] = (opt_io_size >> 16) & 0xff;
|
||||
outbuf[14] = (opt_io_size >> 8) & 0xff;
|
||||
outbuf[15] = opt_io_size & 0xff;
|
||||
|
||||
/* max unmap LBA count, default is 1GB */
|
||||
outbuf[20] = (max_unmap_sectors >> 24) & 0xff;
|
||||
outbuf[21] = (max_unmap_sectors >> 16) & 0xff;
|
||||
outbuf[22] = (max_unmap_sectors >> 8) & 0xff;
|
||||
outbuf[23] = max_unmap_sectors & 0xff;
|
||||
|
||||
/* max unmap descriptors, 255 fit in 4 kb with an 8-byte header. */
|
||||
outbuf[24] = 0;
|
||||
outbuf[25] = 0;
|
||||
outbuf[26] = 0;
|
||||
outbuf[27] = 255;
|
||||
|
||||
/* optimal unmap granularity */
|
||||
outbuf[28] = (unmap_sectors >> 24) & 0xff;
|
||||
outbuf[29] = (unmap_sectors >> 16) & 0xff;
|
||||
outbuf[30] = (unmap_sectors >> 8) & 0xff;
|
||||
outbuf[31] = unmap_sectors & 0xff;
|
||||
|
||||
/* max write same size */
|
||||
outbuf[36] = 0;
|
||||
outbuf[37] = 0;
|
||||
outbuf[38] = 0;
|
||||
outbuf[39] = 0;
|
||||
|
||||
outbuf[40] = (max_io_sectors >> 24) & 0xff;
|
||||
outbuf[41] = (max_io_sectors >> 16) & 0xff;
|
||||
outbuf[42] = (max_io_sectors >> 8) & 0xff;
|
||||
outbuf[43] = max_io_sectors & 0xff;
|
||||
break;
|
||||
}
|
||||
case 0xb1: /* block device characteristics */
|
||||
{
|
||||
buflen = 8;
|
||||
outbuf[4] = (s->rotation_rate >> 8) & 0xff;
|
||||
outbuf[5] = s->rotation_rate & 0xff;
|
||||
outbuf[6] = 0;
|
||||
outbuf[7] = 0;
|
||||
break;
|
||||
}
|
||||
case 0xb2: /* thin provisioning */
|
||||
{
|
||||
buflen = 8;
|
||||
outbuf[4] = 0;
|
||||
outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */
|
||||
outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
|
||||
outbuf[7] = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
/* done with EVPD */
|
||||
assert(buflen - start <= 255);
|
||||
outbuf[start - 1] = buflen - start;
|
||||
return buflen;
|
||||
return scsi_disk_emulate_vpd_page(req, outbuf);
|
||||
}
|
||||
|
||||
/* Standard INQUIRY data */
|
||||
|
@ -2569,8 +2578,6 @@ static int get_device_type(SCSIDiskState *s)
|
|||
{
|
||||
uint8_t cmd[16];
|
||||
uint8_t buf[36];
|
||||
uint8_t sensebuf[8];
|
||||
sg_io_hdr_t io_header;
|
||||
int ret;
|
||||
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
|
@ -2578,19 +2585,9 @@ static int get_device_type(SCSIDiskState *s)
|
|||
cmd[0] = INQUIRY;
|
||||
cmd[4] = sizeof(buf);
|
||||
|
||||
memset(&io_header, 0, sizeof(io_header));
|
||||
io_header.interface_id = 'S';
|
||||
io_header.dxfer_direction = SG_DXFER_FROM_DEV;
|
||||
io_header.dxfer_len = sizeof(buf);
|
||||
io_header.dxferp = buf;
|
||||
io_header.cmdp = cmd;
|
||||
io_header.cmd_len = sizeof(cmd);
|
||||
io_header.mx_sb_len = sizeof(sensebuf);
|
||||
io_header.sbp = sensebuf;
|
||||
io_header.timeout = 6000; /* XXX */
|
||||
|
||||
ret = blk_ioctl(s->qdev.conf.blk, SG_IO, &io_header);
|
||||
if (ret < 0 || io_header.driver_status || io_header.host_status) {
|
||||
ret = scsi_SG_IO_FROM_DEV(s->qdev.conf.blk, cmd, sizeof(cmd),
|
||||
buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
s->qdev.type = buf[0];
|
||||
|
@ -2648,7 +2645,7 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
|
|||
s->features |= (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS);
|
||||
|
||||
scsi_realize(&s->qdev, errp);
|
||||
scsi_generic_read_device_identification(&s->qdev);
|
||||
scsi_generic_read_device_inquiry(&s->qdev);
|
||||
}
|
||||
|
||||
typedef struct SCSIBlockReq {
|
||||
|
@ -3039,6 +3036,10 @@ static Property scsi_block_properties[] = {
|
|||
DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk),
|
||||
DEFINE_PROP_BOOL("share-rw", SCSIDiskState, qdev.conf.share_rw, false),
|
||||
DEFINE_PROP_UINT16("rotation_rate", SCSIDiskState, rotation_rate, 0),
|
||||
DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size,
|
||||
DEFAULT_MAX_UNMAP_SIZE),
|
||||
DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size,
|
||||
DEFAULT_MAX_IO_SIZE),
|
||||
DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version,
|
||||
-1),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
|
|
|
@ -142,10 +142,84 @@ static int execute_command(BlockBackend *blk,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
|
||||
{
|
||||
uint8_t page, page_len;
|
||||
|
||||
/*
|
||||
* EVPD set to zero returns the standard INQUIRY data.
|
||||
*
|
||||
* Check if scsi_version is unset (-1) to avoid re-defining it
|
||||
* each time an INQUIRY with standard data is received.
|
||||
* scsi_version is initialized with -1 in scsi_generic_reset
|
||||
* and scsi_disk_reset, making sure that we'll set the
|
||||
* scsi_version after a reset. If the version field of the
|
||||
* INQUIRY response somehow changes after a guest reboot,
|
||||
* we'll be able to keep track of it.
|
||||
*
|
||||
* On SCSI-2 and older, first 3 bits of byte 2 is the
|
||||
* ANSI-approved version, while on later versions the
|
||||
* whole byte 2 contains the version. Check if we're dealing
|
||||
* with a newer version and, in that case, assign the
|
||||
* whole byte.
|
||||
*/
|
||||
if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) {
|
||||
s->scsi_version = r->buf[2] & 0x07;
|
||||
if (s->scsi_version > 2) {
|
||||
s->scsi_version = r->buf[2];
|
||||
}
|
||||
}
|
||||
|
||||
if (s->type == TYPE_DISK && (r->req.cmd.buf[1] & 0x01)) {
|
||||
page = r->req.cmd.buf[2];
|
||||
if (page == 0xb0) {
|
||||
uint32_t max_transfer =
|
||||
blk_get_max_transfer(s->conf.blk) / s->blocksize;
|
||||
|
||||
assert(max_transfer);
|
||||
stl_be_p(&r->buf[8], max_transfer);
|
||||
/* Also take care of the opt xfer len. */
|
||||
stl_be_p(&r->buf[12],
|
||||
MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
|
||||
} else if (page == 0x00 && s->needs_vpd_bl_emulation) {
|
||||
/*
|
||||
* Now we're capable of supplying the VPD Block Limits
|
||||
* response if the hardware can't. Add it in the INQUIRY
|
||||
* Supported VPD pages response in case we are using the
|
||||
* emulation for this device.
|
||||
*
|
||||
* This way, the guest kernel will be aware of the support
|
||||
* and will use it to proper setup the SCSI device.
|
||||
*/
|
||||
page_len = r->buf[3];
|
||||
r->buf[page_len + 4] = 0xb0;
|
||||
r->buf[3] = ++page_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int scsi_emulate_block_limits(SCSIGenericReq *r)
|
||||
{
|
||||
r->buflen = scsi_disk_emulate_vpd_page(&r->req, r->buf);
|
||||
r->io_header.sb_len_wr = 0;
|
||||
|
||||
/*
|
||||
* We have valid contents in the reply buffer but the
|
||||
* io_header can report a sense error coming from
|
||||
* the hardware in scsi_command_complete_noio. Clean
|
||||
* up the io_header to avoid reporting it.
|
||||
*/
|
||||
r->io_header.driver_status = 0;
|
||||
r->io_header.status = 0;
|
||||
|
||||
return r->buflen;
|
||||
}
|
||||
|
||||
static void scsi_read_complete(void * opaque, int ret)
|
||||
{
|
||||
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
|
||||
SCSIDevice *s = r->req.dev;
|
||||
SCSISense sense;
|
||||
int len;
|
||||
|
||||
assert(r->req.aiocb != NULL);
|
||||
|
@ -162,6 +236,27 @@ static void scsi_read_complete(void * opaque, int ret)
|
|||
DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
|
||||
|
||||
r->len = -1;
|
||||
|
||||
/*
|
||||
* Check if this is a VPD Block Limits request that
|
||||
* resulted in sense error but would need emulation.
|
||||
* In this case, emulate a valid VPD response.
|
||||
*/
|
||||
if (s->needs_vpd_bl_emulation) {
|
||||
int is_vpd_bl = r->req.cmd.buf[0] == INQUIRY &&
|
||||
r->req.cmd.buf[1] & 0x01 &&
|
||||
r->req.cmd.buf[2] == 0xb0;
|
||||
|
||||
if (is_vpd_bl && sg_io_sense_from_errno(-ret, &r->io_header, &sense)) {
|
||||
len = scsi_emulate_block_limits(r);
|
||||
/*
|
||||
* No need to let scsi_read_complete go on and handle an
|
||||
* INQUIRY VPD BL request we created manually.
|
||||
*/
|
||||
goto req_complete;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
scsi_command_complete_noio(r, 0);
|
||||
goto done;
|
||||
|
@ -194,40 +289,10 @@ static void scsi_read_complete(void * opaque, int ret)
|
|||
}
|
||||
}
|
||||
if (r->req.cmd.buf[0] == INQUIRY) {
|
||||
/*
|
||||
* EVPD set to zero returns the standard INQUIRY data.
|
||||
*
|
||||
* Check if scsi_version is unset (-1) to avoid re-defining it
|
||||
* each time an INQUIRY with standard data is received.
|
||||
* scsi_version is initialized with -1 in scsi_generic_reset
|
||||
* and scsi_disk_reset, making sure that we'll set the
|
||||
* scsi_version after a reset. If the version field of the
|
||||
* INQUIRY response somehow changes after a guest reboot,
|
||||
* we'll be able to keep track of it.
|
||||
*
|
||||
* On SCSI-2 and older, first 3 bits of byte 2 is the
|
||||
* ANSI-approved version, while on later versions the
|
||||
* whole byte 2 contains the version. Check if we're dealing
|
||||
* with a newer version and, in that case, assign the
|
||||
* whole byte.
|
||||
*/
|
||||
if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) {
|
||||
s->scsi_version = r->buf[2] & 0x07;
|
||||
if (s->scsi_version > 2) {
|
||||
s->scsi_version = r->buf[2];
|
||||
}
|
||||
}
|
||||
if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) {
|
||||
uint32_t max_transfer =
|
||||
blk_get_max_transfer(s->conf.blk) / s->blocksize;
|
||||
|
||||
assert(max_transfer);
|
||||
stl_be_p(&r->buf[8], max_transfer);
|
||||
/* Also take care of the opt xfer len. */
|
||||
stl_be_p(&r->buf[12],
|
||||
MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
|
||||
}
|
||||
scsi_handle_inquiry_reply(r, s);
|
||||
}
|
||||
|
||||
req_complete:
|
||||
scsi_req_data(&r->req, len);
|
||||
scsi_req_unref(&r->req);
|
||||
|
||||
|
@ -404,12 +469,77 @@ static int read_naa_id(const uint8_t *p, uint64_t *p_wwn)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
void scsi_generic_read_device_identification(SCSIDevice *s)
|
||||
int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size,
|
||||
uint8_t *buf, uint8_t buf_size)
|
||||
{
|
||||
sg_io_hdr_t io_header;
|
||||
uint8_t sensebuf[8];
|
||||
int ret;
|
||||
|
||||
memset(&io_header, 0, sizeof(io_header));
|
||||
io_header.interface_id = 'S';
|
||||
io_header.dxfer_direction = SG_DXFER_FROM_DEV;
|
||||
io_header.dxfer_len = buf_size;
|
||||
io_header.dxferp = buf;
|
||||
io_header.cmdp = cmd;
|
||||
io_header.cmd_len = cmd_size;
|
||||
io_header.mx_sb_len = sizeof(sensebuf);
|
||||
io_header.sbp = sensebuf;
|
||||
io_header.timeout = 6000; /* XXX */
|
||||
|
||||
ret = blk_ioctl(blk, SG_IO, &io_header);
|
||||
if (ret < 0 || io_header.driver_status || io_header.host_status) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes an INQUIRY request with EVPD set to retrieve the
|
||||
* available VPD pages of the device. If the device does
|
||||
* not support the Block Limits page (page 0xb0), set
|
||||
* the needs_vpd_bl_emulation flag for future use.
|
||||
*/
|
||||
static void scsi_generic_set_vpd_bl_emulation(SCSIDevice *s)
|
||||
{
|
||||
uint8_t cmd[6];
|
||||
uint8_t buf[250];
|
||||
uint8_t page_len;
|
||||
int ret, i;
|
||||
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
memset(buf, 0, sizeof(buf));
|
||||
cmd[0] = INQUIRY;
|
||||
cmd[1] = 1;
|
||||
cmd[2] = 0x00;
|
||||
cmd[4] = sizeof(buf);
|
||||
|
||||
ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd),
|
||||
buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
/*
|
||||
* Do not assume anything if we can't retrieve the
|
||||
* INQUIRY response to assert the VPD Block Limits
|
||||
* support.
|
||||
*/
|
||||
s->needs_vpd_bl_emulation = false;
|
||||
return;
|
||||
}
|
||||
|
||||
page_len = buf[3];
|
||||
for (i = 4; i < page_len + 4; i++) {
|
||||
if (buf[i] == 0xb0) {
|
||||
s->needs_vpd_bl_emulation = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
s->needs_vpd_bl_emulation = true;
|
||||
}
|
||||
|
||||
static void scsi_generic_read_device_identification(SCSIDevice *s)
|
||||
{
|
||||
uint8_t cmd[6];
|
||||
uint8_t buf[250];
|
||||
uint8_t sensebuf[8];
|
||||
sg_io_hdr_t io_header;
|
||||
int ret;
|
||||
int i, len;
|
||||
|
||||
|
@ -420,19 +550,9 @@ void scsi_generic_read_device_identification(SCSIDevice *s)
|
|||
cmd[2] = 0x83;
|
||||
cmd[4] = sizeof(buf);
|
||||
|
||||
memset(&io_header, 0, sizeof(io_header));
|
||||
io_header.interface_id = 'S';
|
||||
io_header.dxfer_direction = SG_DXFER_FROM_DEV;
|
||||
io_header.dxfer_len = sizeof(buf);
|
||||
io_header.dxferp = buf;
|
||||
io_header.cmdp = cmd;
|
||||
io_header.cmd_len = sizeof(cmd);
|
||||
io_header.mx_sb_len = sizeof(sensebuf);
|
||||
io_header.sbp = sensebuf;
|
||||
io_header.timeout = 6000; /* XXX */
|
||||
|
||||
ret = blk_ioctl(s->conf.blk, SG_IO, &io_header);
|
||||
if (ret < 0 || io_header.driver_status || io_header.host_status) {
|
||||
ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd),
|
||||
buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -461,12 +581,20 @@ void scsi_generic_read_device_identification(SCSIDevice *s)
|
|||
}
|
||||
}
|
||||
|
||||
void scsi_generic_read_device_inquiry(SCSIDevice *s)
|
||||
{
|
||||
scsi_generic_read_device_identification(s);
|
||||
if (s->type == TYPE_DISK) {
|
||||
scsi_generic_set_vpd_bl_emulation(s);
|
||||
} else {
|
||||
s->needs_vpd_bl_emulation = false;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_stream_blocksize(BlockBackend *blk)
|
||||
{
|
||||
uint8_t cmd[6];
|
||||
uint8_t buf[12];
|
||||
uint8_t sensebuf[8];
|
||||
sg_io_hdr_t io_header;
|
||||
int ret;
|
||||
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
|
@ -474,21 +602,11 @@ static int get_stream_blocksize(BlockBackend *blk)
|
|||
cmd[0] = MODE_SENSE;
|
||||
cmd[4] = sizeof(buf);
|
||||
|
||||
memset(&io_header, 0, sizeof(io_header));
|
||||
io_header.interface_id = 'S';
|
||||
io_header.dxfer_direction = SG_DXFER_FROM_DEV;
|
||||
io_header.dxfer_len = sizeof(buf);
|
||||
io_header.dxferp = buf;
|
||||
io_header.cmdp = cmd;
|
||||
io_header.cmd_len = sizeof(cmd);
|
||||
io_header.mx_sb_len = sizeof(sensebuf);
|
||||
io_header.sbp = sensebuf;
|
||||
io_header.timeout = 6000; /* XXX */
|
||||
|
||||
ret = blk_ioctl(blk, SG_IO, &io_header);
|
||||
if (ret < 0 || io_header.driver_status || io_header.host_status) {
|
||||
ret = scsi_SG_IO_FROM_DEV(blk, cmd, sizeof(cmd), buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (buf[9] << 16) | (buf[10] << 8) | buf[11];
|
||||
}
|
||||
|
||||
|
@ -574,7 +692,7 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp)
|
|||
|
||||
/* Only used by scsi-block, but initialize it nevertheless to be clean. */
|
||||
s->default_scsi_version = -1;
|
||||
scsi_generic_read_device_identification(s);
|
||||
scsi_generic_read_device_inquiry(s);
|
||||
}
|
||||
|
||||
const SCSIReqOps scsi_generic_req_ops = {
|
||||
|
|
|
@ -142,8 +142,8 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev)
|
|||
/* Set up guest notifier (irq) */
|
||||
rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), "
|
||||
"ensure -enable-kvm is set\n", rc);
|
||||
error_report("virtio-scsi: Failed to set guest notifiers (%d), "
|
||||
"ensure -accel kvm is set.", rc);
|
||||
goto fail_guest_notifiers;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue