Merge remote-tracking branch 'kwolf/for-anthony' into staging
Conflicts: block/vmdk.c
This commit is contained in:
commit
8494a397b6
@ -36,6 +36,7 @@ block-nested-y += qed-check.o
|
||||
block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o
|
||||
block-nested-$(CONFIG_WIN32) += raw-win32.o
|
||||
block-nested-$(CONFIG_POSIX) += raw-posix.o
|
||||
block-nested-$(CONFIG_LIBISCSI) += iscsi.o
|
||||
block-nested-$(CONFIG_CURL) += curl.o
|
||||
block-nested-$(CONFIG_RBD) += rbd.o
|
||||
|
||||
|
18
block.c
18
block.c
@ -473,10 +473,13 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
|
||||
bs->total_sectors = 0;
|
||||
bs->encrypted = 0;
|
||||
bs->valid_key = 0;
|
||||
bs->sg = 0;
|
||||
bs->open_flags = flags;
|
||||
bs->growable = 0;
|
||||
bs->buffer_alignment = 512;
|
||||
|
||||
pstrcpy(bs->filename, sizeof(bs->filename), filename);
|
||||
bs->backing_file[0] = '\0';
|
||||
|
||||
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) {
|
||||
return -ENOTSUP;
|
||||
@ -485,8 +488,7 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
|
||||
bs->drv = drv;
|
||||
bs->opaque = g_malloc0(drv->instance_size);
|
||||
|
||||
if (flags & BDRV_O_CACHE_WB)
|
||||
bs->enable_write_cache = 1;
|
||||
bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB);
|
||||
|
||||
/*
|
||||
* Clear flags that are internal to the block layer before opening the
|
||||
@ -501,6 +503,8 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
|
||||
open_flags |= BDRV_O_RDWR;
|
||||
}
|
||||
|
||||
bs->keep_read_only = bs->read_only = !(open_flags & BDRV_O_RDWR);
|
||||
|
||||
/* Open the image, either directly or using a protocol */
|
||||
if (drv->bdrv_file_open) {
|
||||
ret = drv->bdrv_file_open(bs, filename, open_flags);
|
||||
@ -515,8 +519,6 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
|
||||
goto free_and_fail;
|
||||
}
|
||||
|
||||
bs->keep_read_only = bs->read_only = !(open_flags & BDRV_O_RDWR);
|
||||
|
||||
ret = refresh_total_sectors(bs, bs->total_sectors);
|
||||
if (ret < 0) {
|
||||
goto free_and_fail;
|
||||
@ -572,6 +574,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
||||
BlockDriver *drv)
|
||||
{
|
||||
int ret;
|
||||
char tmp_filename[PATH_MAX];
|
||||
|
||||
if (flags & BDRV_O_SNAPSHOT) {
|
||||
BlockDriverState *bs1;
|
||||
@ -579,7 +582,6 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
||||
int is_protocol = 0;
|
||||
BlockDriver *bdrv_qcow2;
|
||||
QEMUOptionParameter *options;
|
||||
char tmp_filename[PATH_MAX];
|
||||
char backing_filename[PATH_MAX];
|
||||
|
||||
/* if snapshot, we create a temporary backing file and open it
|
||||
@ -1939,11 +1941,7 @@ const char *bdrv_get_encrypted_filename(BlockDriverState *bs)
|
||||
void bdrv_get_backing_filename(BlockDriverState *bs,
|
||||
char *filename, int filename_size)
|
||||
{
|
||||
if (!bs->backing_file) {
|
||||
pstrcpy(filename, filename_size, "");
|
||||
} else {
|
||||
pstrcpy(filename, filename_size, bs->backing_file);
|
||||
}
|
||||
pstrcpy(filename, filename_size, bs->backing_file);
|
||||
}
|
||||
|
||||
int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
|
591
block/iscsi.c
Normal file
591
block/iscsi.c
Normal file
@ -0,0 +1,591 @@
|
||||
/*
|
||||
* QEMU Block driver for iSCSI images
|
||||
*
|
||||
* Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config-host.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-error.h"
|
||||
#include "block_int.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include <iscsi/iscsi.h>
|
||||
#include <iscsi/scsi-lowlevel.h>
|
||||
|
||||
|
||||
typedef struct IscsiLun {
|
||||
struct iscsi_context *iscsi;
|
||||
int lun;
|
||||
int block_size;
|
||||
unsigned long num_blocks;
|
||||
} IscsiLun;
|
||||
|
||||
typedef struct IscsiAIOCB {
|
||||
BlockDriverAIOCB common;
|
||||
QEMUIOVector *qiov;
|
||||
QEMUBH *bh;
|
||||
IscsiLun *iscsilun;
|
||||
struct scsi_task *task;
|
||||
uint8_t *buf;
|
||||
int status;
|
||||
int canceled;
|
||||
size_t read_size;
|
||||
size_t read_offset;
|
||||
} IscsiAIOCB;
|
||||
|
||||
struct IscsiTask {
|
||||
IscsiLun *iscsilun;
|
||||
BlockDriverState *bs;
|
||||
int status;
|
||||
int complete;
|
||||
};
|
||||
|
||||
static void
|
||||
iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
||||
void *private_data)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
IscsiAIOCB *acb = (IscsiAIOCB *)blockacb;
|
||||
IscsiLun *iscsilun = acb->iscsilun;
|
||||
|
||||
acb->common.cb(acb->common.opaque, -ECANCELED);
|
||||
acb->canceled = 1;
|
||||
|
||||
/* send a task mgmt call to the target to cancel the task on the target */
|
||||
iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
|
||||
iscsi_abort_task_cb, NULL);
|
||||
|
||||
/* then also cancel the task locally in libiscsi */
|
||||
iscsi_scsi_task_cancel(iscsilun->iscsi, acb->task);
|
||||
}
|
||||
|
||||
static AIOPool iscsi_aio_pool = {
|
||||
.aiocb_size = sizeof(IscsiAIOCB),
|
||||
.cancel = iscsi_aio_cancel,
|
||||
};
|
||||
|
||||
|
||||
static void iscsi_process_read(void *arg);
|
||||
static void iscsi_process_write(void *arg);
|
||||
|
||||
static int iscsi_process_flush(void *arg)
|
||||
{
|
||||
IscsiLun *iscsilun = arg;
|
||||
|
||||
return iscsi_queue_length(iscsilun->iscsi) > 0;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_set_events(IscsiLun *iscsilun)
|
||||
{
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
|
||||
qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read,
|
||||
(iscsi_which_events(iscsi) & POLLOUT)
|
||||
? iscsi_process_write : NULL,
|
||||
iscsi_process_flush, NULL, iscsilun);
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_process_read(void *arg)
|
||||
{
|
||||
IscsiLun *iscsilun = arg;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
|
||||
iscsi_service(iscsi, POLLIN);
|
||||
iscsi_set_events(iscsilun);
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_process_write(void *arg)
|
||||
{
|
||||
IscsiLun *iscsilun = arg;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
|
||||
iscsi_service(iscsi, POLLOUT);
|
||||
iscsi_set_events(iscsilun);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
iscsi_schedule_bh(QEMUBHFunc *cb, IscsiAIOCB *acb)
|
||||
{
|
||||
acb->bh = qemu_bh_new(cb, acb);
|
||||
if (!acb->bh) {
|
||||
error_report("oom: could not create iscsi bh");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
qemu_bh_schedule(acb->bh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_readv_writev_bh_cb(void *p)
|
||||
{
|
||||
IscsiAIOCB *acb = p;
|
||||
|
||||
qemu_bh_delete(acb->bh);
|
||||
|
||||
if (acb->canceled == 0) {
|
||||
acb->common.cb(acb->common.opaque, acb->status);
|
||||
}
|
||||
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
iscsi_aio_write10_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
{
|
||||
IscsiAIOCB *acb = opaque;
|
||||
|
||||
trace_iscsi_aio_write10_cb(iscsi, status, acb, acb->canceled);
|
||||
|
||||
g_free(acb->buf);
|
||||
|
||||
if (acb->canceled != 0) {
|
||||
qemu_aio_release(acb);
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
acb->status = 0;
|
||||
if (status < 0) {
|
||||
error_report("Failed to write10 data to iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = -EIO;
|
||||
}
|
||||
|
||||
iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
}
|
||||
|
||||
static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun)
|
||||
{
|
||||
return sector * BDRV_SECTOR_SIZE / iscsilun->block_size;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
IscsiAIOCB *acb;
|
||||
size_t size;
|
||||
int fua = 0;
|
||||
|
||||
/* set FUA on writes when cache mode is write through */
|
||||
if (!(bs->open_flags & BDRV_O_CACHE_WB)) {
|
||||
fua = 1;
|
||||
}
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
|
||||
trace_iscsi_aio_writev(iscsi, sector_num, nb_sectors, opaque, acb);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->qiov = qiov;
|
||||
|
||||
acb->canceled = 0;
|
||||
|
||||
/* XXX we should pass the iovec to write10 to avoid the extra copy */
|
||||
/* this will allow us to get rid of 'buf' completely */
|
||||
size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||
acb->buf = g_malloc(size);
|
||||
qemu_iovec_to_buffer(acb->qiov, acb->buf);
|
||||
acb->task = iscsi_write10_task(iscsi, iscsilun->lun, acb->buf, size,
|
||||
sector_qemu2lun(sector_num, iscsilun),
|
||||
fua, 0, iscsilun->block_size,
|
||||
iscsi_aio_write10_cb, acb);
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to send write10 command. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
g_free(acb->buf);
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iscsi_set_events(iscsilun);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_aio_read10_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
{
|
||||
IscsiAIOCB *acb = opaque;
|
||||
|
||||
trace_iscsi_aio_read10_cb(iscsi, status, acb, acb->canceled);
|
||||
|
||||
if (acb->canceled != 0) {
|
||||
qemu_aio_release(acb);
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
acb->status = 0;
|
||||
if (status != 0) {
|
||||
error_report("Failed to read10 data from iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = -EIO;
|
||||
}
|
||||
|
||||
iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
IscsiAIOCB *acb;
|
||||
size_t qemu_read_size, lun_read_size;
|
||||
int i;
|
||||
|
||||
qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors;
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
|
||||
trace_iscsi_aio_readv(iscsi, sector_num, nb_sectors, opaque, acb);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->qiov = qiov;
|
||||
|
||||
acb->canceled = 0;
|
||||
acb->read_size = qemu_read_size;
|
||||
acb->buf = NULL;
|
||||
|
||||
/* If LUN blocksize is bigger than BDRV_BLOCK_SIZE a read from QEMU
|
||||
* may be misaligned to the LUN, so we may need to read some extra
|
||||
* data.
|
||||
*/
|
||||
acb->read_offset = 0;
|
||||
if (iscsilun->block_size > BDRV_SECTOR_SIZE) {
|
||||
uint64_t bdrv_offset = BDRV_SECTOR_SIZE * sector_num;
|
||||
|
||||
acb->read_offset = bdrv_offset % iscsilun->block_size;
|
||||
}
|
||||
|
||||
lun_read_size = (qemu_read_size + iscsilun->block_size
|
||||
+ acb->read_offset - 1)
|
||||
/ iscsilun->block_size * iscsilun->block_size;
|
||||
acb->task = iscsi_read10_task(iscsi, iscsilun->lun,
|
||||
sector_qemu2lun(sector_num, iscsilun),
|
||||
lun_read_size, iscsilun->block_size,
|
||||
iscsi_aio_read10_cb, acb);
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to send read10 command. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < acb->qiov->niov; i++) {
|
||||
scsi_task_add_data_in_buffer(acb->task,
|
||||
acb->qiov->iov[i].iov_len,
|
||||
acb->qiov->iov[i].iov_base);
|
||||
}
|
||||
|
||||
iscsi_set_events(iscsilun);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
iscsi_synccache10_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
{
|
||||
IscsiAIOCB *acb = opaque;
|
||||
|
||||
if (acb->canceled != 0) {
|
||||
qemu_aio_release(acb);
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
acb->status = 0;
|
||||
if (status < 0) {
|
||||
error_report("Failed to sync10 data on iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
acb->status = -EIO;
|
||||
}
|
||||
|
||||
iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
|
||||
scsi_free_scsi_task(acb->task);
|
||||
acb->task = NULL;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *
|
||||
iscsi_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
IscsiAIOCB *acb;
|
||||
|
||||
acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
|
||||
|
||||
acb->iscsilun = iscsilun;
|
||||
acb->canceled = 0;
|
||||
|
||||
acb->task = iscsi_synchronizecache10_task(iscsi, iscsilun->lun,
|
||||
0, 0, 0, 0,
|
||||
iscsi_synccache10_cb,
|
||||
acb);
|
||||
if (acb->task == NULL) {
|
||||
error_report("iSCSI: Failed to send synchronizecache10 command. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
qemu_aio_release(acb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iscsi_set_events(iscsilun);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
iscsi_getlength(BlockDriverState *bs)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
int64_t len;
|
||||
|
||||
len = iscsilun->num_blocks;
|
||||
len *= iscsilun->block_size;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *opaque)
|
||||
{
|
||||
struct IscsiTask *itask = opaque;
|
||||
struct scsi_readcapacity10 *rc10;
|
||||
struct scsi_task *task = command_data;
|
||||
|
||||
if (status != 0) {
|
||||
error_report("iSCSI: Failed to read capacity of iSCSI lun. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
scsi_free_scsi_task(task);
|
||||
return;
|
||||
}
|
||||
|
||||
rc10 = scsi_datain_unmarshall(task);
|
||||
if (rc10 == NULL) {
|
||||
error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
scsi_free_scsi_task(task);
|
||||
return;
|
||||
}
|
||||
|
||||
itask->iscsilun->block_size = rc10->block_size;
|
||||
itask->iscsilun->num_blocks = rc10->lba;
|
||||
itask->bs->total_sectors = (uint64_t)rc10->lba *
|
||||
rc10->block_size / BDRV_SECTOR_SIZE ;
|
||||
|
||||
itask->status = 0;
|
||||
itask->complete = 1;
|
||||
scsi_free_scsi_task(task);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
||||
void *opaque)
|
||||
{
|
||||
struct IscsiTask *itask = opaque;
|
||||
struct scsi_task *task;
|
||||
|
||||
if (status != 0) {
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
task = iscsi_readcapacity10_task(iscsi, itask->iscsilun->lun, 0, 0,
|
||||
iscsi_readcapacity10_cb, opaque);
|
||||
if (task == NULL) {
|
||||
error_report("iSCSI: failed to send readcapacity command.");
|
||||
itask->status = 1;
|
||||
itask->complete = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We support iscsi url's on the form
|
||||
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
|
||||
*/
|
||||
static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = NULL;
|
||||
struct iscsi_url *iscsi_url = NULL;
|
||||
struct IscsiTask task;
|
||||
int ret;
|
||||
|
||||
if ((BDRV_SECTOR_SIZE % 512) != 0) {
|
||||
error_report("iSCSI: Invalid BDRV_SECTOR_SIZE. "
|
||||
"BDRV_SECTOR_SIZE(%lld) is not a multiple "
|
||||
"of 512", BDRV_SECTOR_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
|
||||
/* Should really append the KVM name after the ':' here */
|
||||
iscsi = iscsi_create_context("iqn.2008-11.org.linux-kvm:");
|
||||
if (iscsi == NULL) {
|
||||
error_report("iSCSI: Failed to create iSCSI context.");
|
||||
ret = -ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
iscsi_url = iscsi_parse_full_url(iscsi, filename);
|
||||
if (iscsi_url == NULL) {
|
||||
error_report("Failed to parse URL : %s %s", filename,
|
||||
iscsi_get_error(iscsi));
|
||||
ret = -EINVAL;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
|
||||
error_report("iSCSI: Failed to set target name.");
|
||||
ret = -EINVAL;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (iscsi_url->user != NULL) {
|
||||
ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user,
|
||||
iscsi_url->passwd);
|
||||
if (ret != 0) {
|
||||
error_report("Failed to set initiator username and password");
|
||||
ret = -EINVAL;
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
|
||||
error_report("iSCSI: Failed to set session type to normal.");
|
||||
ret = -EINVAL;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
||||
|
||||
task.iscsilun = iscsilun;
|
||||
task.status = 0;
|
||||
task.complete = 0;
|
||||
task.bs = bs;
|
||||
|
||||
iscsilun->iscsi = iscsi;
|
||||
iscsilun->lun = iscsi_url->lun;
|
||||
|
||||
if (iscsi_full_connect_async(iscsi, iscsi_url->portal, iscsi_url->lun,
|
||||
iscsi_connect_cb, &task)
|
||||
!= 0) {
|
||||
error_report("iSCSI: Failed to start async connect.");
|
||||
ret = -EINVAL;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
while (!task.complete) {
|
||||
iscsi_set_events(iscsilun);
|
||||
qemu_aio_wait();
|
||||
}
|
||||
if (task.status != 0) {
|
||||
error_report("iSCSI: Failed to connect to LUN : %s",
|
||||
iscsi_get_error(iscsi));
|
||||
ret = -EINVAL;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (iscsi_url != NULL) {
|
||||
iscsi_destroy_url(iscsi_url);
|
||||
}
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
if (iscsi_url != NULL) {
|
||||
iscsi_destroy_url(iscsi_url);
|
||||
}
|
||||
if (iscsi != NULL) {
|
||||
iscsi_destroy_context(iscsi);
|
||||
}
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void iscsi_close(BlockDriverState *bs)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = iscsilun->iscsi;
|
||||
|
||||
qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL, NULL);
|
||||
iscsi_destroy_context(iscsi);
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_iscsi = {
|
||||
.format_name = "iscsi",
|
||||
.protocol_name = "iscsi",
|
||||
|
||||
.instance_size = sizeof(IscsiLun),
|
||||
.bdrv_file_open = iscsi_open,
|
||||
.bdrv_close = iscsi_close,
|
||||
|
||||
.bdrv_getlength = iscsi_getlength,
|
||||
|
||||
.bdrv_aio_readv = iscsi_aio_readv,
|
||||
.bdrv_aio_writev = iscsi_aio_writev,
|
||||
.bdrv_aio_flush = iscsi_aio_flush,
|
||||
};
|
||||
|
||||
static void iscsi_block_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_iscsi);
|
||||
}
|
||||
|
||||
block_init(iscsi_block_init);
|
30
block/qcow.c
30
block/qcow.c
@ -736,8 +736,6 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
return -EINVAL;
|
||||
|
||||
out_buf = g_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
|
||||
if (!out_buf)
|
||||
return -1;
|
||||
|
||||
/* best compression, small window, no zlib header */
|
||||
memset(&strm, 0, sizeof(strm));
|
||||
@ -745,8 +743,8 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
Z_DEFLATED, -12,
|
||||
9, Z_DEFAULT_STRATEGY);
|
||||
if (ret != 0) {
|
||||
g_free(out_buf);
|
||||
return -1;
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
strm.avail_in = s->cluster_size;
|
||||
@ -756,9 +754,9 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
ret = deflate(&strm, Z_FINISH);
|
||||
if (ret != Z_STREAM_END && ret != Z_OK) {
|
||||
g_free(out_buf);
|
||||
deflateEnd(&strm);
|
||||
return -1;
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
out_len = strm.next_out - out_buf;
|
||||
|
||||
@ -766,19 +764,29 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
|
||||
/* could not compress: write normal cluster */
|
||||
bdrv_write(bs, sector_num, buf, s->cluster_sectors);
|
||||
ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 2,
|
||||
out_len, 0, 0);
|
||||
if (cluster_offset == 0) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cluster_offset &= s->cluster_offset_mask;
|
||||
if (bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len) != out_len) {
|
||||
g_free(out_buf);
|
||||
return -1;
|
||||
ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
g_free(out_buf);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow_co_flush(BlockDriverState *bs)
|
||||
|
@ -1113,11 +1113,13 @@ static int qcow2_co_flush(BlockDriverState *bs)
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = qcow2_cache_flush(bs, s->l2_table_cache);
|
||||
if (ret < 0) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||
if (ret < 0) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
23
block/vdi.c
23
block/vdi.c
@ -114,8 +114,13 @@ void uuid_unparse(const uuid_t uu, char *out);
|
||||
*/
|
||||
#define VDI_TEXT "<<< QEMU VM Virtual Disk Image >>>\n"
|
||||
|
||||
/* Unallocated blocks use this index (no need to convert endianness). */
|
||||
#define VDI_UNALLOCATED UINT32_MAX
|
||||
/* A never-allocated block; semantically arbitrary content. */
|
||||
#define VDI_UNALLOCATED 0xffffffffU
|
||||
|
||||
/* A discarded (no longer allocated) block; semantically zero-filled. */
|
||||
#define VDI_DISCARDED 0xfffffffeU
|
||||
|
||||
#define VDI_IS_ALLOCATED(X) ((X) < VDI_DISCARDED)
|
||||
|
||||
#if !defined(CONFIG_UUID)
|
||||
void uuid_generate(uuid_t out)
|
||||
@ -307,10 +312,10 @@ static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res)
|
||||
/* Check block map and value of blocks_allocated. */
|
||||
for (block = 0; block < s->header.blocks_in_image; block++) {
|
||||
uint32_t bmap_entry = le32_to_cpu(s->bmap[block]);
|
||||
if (bmap_entry != VDI_UNALLOCATED) {
|
||||
if (VDI_IS_ALLOCATED(bmap_entry)) {
|
||||
if (bmap_entry < s->header.blocks_in_image) {
|
||||
blocks_allocated++;
|
||||
if (bmap[bmap_entry] == VDI_UNALLOCATED) {
|
||||
if (!VDI_IS_ALLOCATED(bmap[bmap_entry])) {
|
||||
bmap[bmap_entry] = bmap_entry;
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: block index %" PRIu32
|
||||
@ -472,7 +477,7 @@ static int vdi_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
n_sectors = nb_sectors;
|
||||
}
|
||||
*pnum = n_sectors;
|
||||
return bmap_entry != VDI_UNALLOCATED;
|
||||
return VDI_IS_ALLOCATED(bmap_entry);
|
||||
}
|
||||
|
||||
static void vdi_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
@ -603,7 +608,7 @@ static void vdi_aio_read_cb(void *opaque, int ret)
|
||||
/* prepare next AIO request */
|
||||
acb->n_sectors = n_sectors;
|
||||
bmap_entry = le32_to_cpu(s->bmap[block_index]);
|
||||
if (bmap_entry == VDI_UNALLOCATED) {
|
||||
if (!VDI_IS_ALLOCATED(bmap_entry)) {
|
||||
/* Block not allocated, return zeros, no need to wait. */
|
||||
memset(acb->buf, 0, n_sectors * SECTOR_SIZE);
|
||||
ret = vdi_schedule_bh(vdi_aio_rw_bh, acb);
|
||||
@ -685,7 +690,7 @@ static void vdi_aio_write_cb(void *opaque, int ret)
|
||||
if (acb->header_modified) {
|
||||
VdiHeader *header = acb->block_buffer;
|
||||
logout("now writing modified header\n");
|
||||
assert(acb->bmap_first != VDI_UNALLOCATED);
|
||||
assert(VDI_IS_ALLOCATED(acb->bmap_first));
|
||||
*header = s->header;
|
||||
vdi_header_to_le(header);
|
||||
acb->header_modified = 0;
|
||||
@ -699,7 +704,7 @@ static void vdi_aio_write_cb(void *opaque, int ret)
|
||||
goto done;
|
||||
}
|
||||
return;
|
||||
} else if (acb->bmap_first != VDI_UNALLOCATED) {
|
||||
} else if (VDI_IS_ALLOCATED(acb->bmap_first)) {
|
||||
/* One or more new blocks were allocated. */
|
||||
uint64_t offset;
|
||||
uint32_t bmap_first;
|
||||
@ -749,7 +754,7 @@ static void vdi_aio_write_cb(void *opaque, int ret)
|
||||
/* prepare next AIO request */
|
||||
acb->n_sectors = n_sectors;
|
||||
bmap_entry = le32_to_cpu(s->bmap[block_index]);
|
||||
if (bmap_entry == VDI_UNALLOCATED) {
|
||||
if (!VDI_IS_ALLOCATED(bmap_entry)) {
|
||||
/* Allocate new block and write to it. */
|
||||
uint64_t offset;
|
||||
uint8_t *block;
|
||||
|
28
block/vmdk.c
28
block/vmdk.c
@ -212,8 +212,10 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
||||
const char *p_name, *cid_str;
|
||||
size_t cid_str_size;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
|
||||
ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -225,6 +227,7 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
||||
cid_str_size = sizeof("CID");
|
||||
}
|
||||
|
||||
desc[DESC_SIZE - 1] = '\0';
|
||||
p_name = strstr(desc, cid_str);
|
||||
if (p_name != NULL) {
|
||||
p_name += cid_str_size;
|
||||
@ -239,13 +242,19 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
|
||||
char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
|
||||
char *p_name, *tmp_str;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
memset(desc, 0, sizeof(desc));
|
||||
if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
|
||||
return -EIO;
|
||||
ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
desc[DESC_SIZE - 1] = '\0';
|
||||
tmp_str = strstr(desc, "parentCID");
|
||||
if (tmp_str == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
|
||||
p_name = strstr(desc, "CID");
|
||||
if (p_name != NULL) {
|
||||
@ -254,9 +263,11 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
|
||||
pstrcat(desc, sizeof(desc), tmp_desc);
|
||||
}
|
||||
|
||||
if (bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE) < 0) {
|
||||
return -EIO;
|
||||
ret = bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1109,7 +1120,10 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
/* update CID on the first write every time the virtual disk is
|
||||
* opened */
|
||||
if (!s->cid_updated) {
|
||||
vmdk_write_cid(bs, time(NULL));
|
||||
ret = vmdk_write_cid(bs, time(NULL));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
s->cid_updated = true;
|
||||
}
|
||||
}
|
||||
|
31
configure
vendored
31
configure
vendored
@ -182,6 +182,7 @@ usb_redir=""
|
||||
opengl=""
|
||||
zlib="yes"
|
||||
guest_agent="yes"
|
||||
libiscsi=""
|
||||
|
||||
# parse CC options first
|
||||
for opt do
|
||||
@ -657,6 +658,10 @@ for opt do
|
||||
;;
|
||||
--enable-spice) spice="yes"
|
||||
;;
|
||||
--disable-libiscsi) libiscsi="no"
|
||||
;;
|
||||
--enable-libiscsi) libiscsi="yes"
|
||||
;;
|
||||
--enable-profiler) profiler="yes"
|
||||
;;
|
||||
--enable-cocoa)
|
||||
@ -1046,6 +1051,8 @@ echo " Default:trace-<pid>"
|
||||
echo " --disable-spice disable spice"
|
||||
echo " --enable-spice enable spice"
|
||||
echo " --enable-rbd enable building the rados block device (rbd)"
|
||||
echo " --disable-libiscsi disable iscsi support"
|
||||
echo " --enable-libiscsi enable iscsi support"
|
||||
echo " --disable-smartcard disable smartcard support"
|
||||
echo " --enable-smartcard enable smartcard support"
|
||||
echo " --disable-smartcard-nss disable smartcard nss support"
|
||||
@ -2334,6 +2341,25 @@ if compile_prog "" "" ; then
|
||||
bswap_h=yes
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# Do we have libiscsi
|
||||
if test "$libiscsi" != "no" ; then
|
||||
cat > $TMPC << EOF
|
||||
#include <iscsi/iscsi.h>
|
||||
int main(void) { iscsi_create_context(""); return 0; }
|
||||
EOF
|
||||
if compile_prog "-Werror" "-liscsi" ; then
|
||||
libiscsi="yes"
|
||||
LIBS="$LIBS -liscsi"
|
||||
else
|
||||
if test "$libiscsi" = "yes" ; then
|
||||
feature_not_found "libiscsi"
|
||||
fi
|
||||
libiscsi="no"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
##########################################
|
||||
# Do we need librt
|
||||
cat > $TMPC <<EOF
|
||||
@ -2744,6 +2770,7 @@ echo "xfsctl support $xfs"
|
||||
echo "nss used $smartcard_nss"
|
||||
echo "usb net redir $usb_redir"
|
||||
echo "OpenGL support $opengl"
|
||||
echo "libiscsi support $libiscsi"
|
||||
echo "build guest agent $guest_agent"
|
||||
|
||||
if test "$sdl_too_old" = "yes"; then
|
||||
@ -3042,6 +3069,10 @@ if test "$opengl" = "yes" ; then
|
||||
echo "CONFIG_OPENGL=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$libiscsi" = "yes" ; then
|
||||
echo "CONFIG_LIBISCSI=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
# XXX: suppress that
|
||||
if [ "$bsd" = "yes" ] ; then
|
||||
echo "CONFIG_BSD=y" >> $config_host_mak
|
||||
|
@ -108,8 +108,8 @@ as follows:
|
||||
|
||||
refcount_block_entries = (cluster_size / sizeof(uint16_t))
|
||||
|
||||
refcount_block_index = (offset / cluster_size) % refcount_table_entries
|
||||
refcount_table_index = (offset / cluster_size) / refcount_table_entries
|
||||
refcount_block_index = (offset / cluster_size) % refcount_block_entries
|
||||
refcount_table_index = (offset / cluster_size) / refcount_block_entries
|
||||
|
||||
refcount_block = load_cluster(refcount_table[refcount_table_index]);
|
||||
return refcount_block[refcount_block_index];
|
||||
@ -211,7 +211,7 @@ switch the active L1 table, so that a different set of host clusters are
|
||||
exposed to the guest.
|
||||
|
||||
When creating a snapshot, the L1 table should be copied and the refcount of all
|
||||
L2 tables and clusters reachable form this L1 table must be increased, so that
|
||||
L2 tables and clusters reachable from this L1 table must be increased, so that
|
||||
a write causes a COW and isn't visible in other snapshots.
|
||||
|
||||
When loading a snapshot, bit 63 of all entries in the new active L1 table and
|
||||
|
@ -276,7 +276,7 @@ static void piix4_update_hotplug(PIIX4PMState *s)
|
||||
|
||||
s->pci0_hotplug_enable = ~0;
|
||||
|
||||
QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
|
||||
QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
|
||||
PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev);
|
||||
PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, qdev);
|
||||
int slot = PCI_SLOT(pdev->devfn);
|
||||
@ -486,7 +486,7 @@ static void pciej_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
PCIDeviceInfo *info;
|
||||
int slot = ffs(val) - 1;
|
||||
|
||||
QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
|
||||
QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
|
||||
dev = DO_UPCAST(PCIDevice, qdev, qdev);
|
||||
info = container_of(qdev->info, PCIDeviceInfo, qdev);
|
||||
if (PCI_SLOT(dev->devfn) == slot && !info->no_hotplug) {
|
||||
|
16
hw/esp.c
16
hw/esp.c
@ -217,7 +217,8 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
|
||||
s->async_len = 0;
|
||||
}
|
||||
|
||||
if (target >= ESP_MAX_DEVS || !s->bus.devs[target]) {
|
||||
s->current_dev = scsi_device_find(&s->bus, 0, target, 0);
|
||||
if (!s->current_dev) {
|
||||
// No such drive
|
||||
s->rregs[ESP_RSTAT] = 0;
|
||||
s->rregs[ESP_RINTR] = INTR_DC;
|
||||
@ -225,7 +226,6 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
|
||||
esp_raise_irq(s);
|
||||
return 0;
|
||||
}
|
||||
s->current_dev = s->bus.devs[target];
|
||||
return dmalen;
|
||||
}
|
||||
|
||||
@ -233,10 +233,12 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
|
||||
{
|
||||
int32_t datalen;
|
||||
int lun;
|
||||
SCSIDevice *current_lun;
|
||||
|
||||
trace_esp_do_busid_cmd(busid);
|
||||
lun = busid & 7;
|
||||
s->current_req = scsi_req_new(s->current_dev, 0, lun, buf, NULL);
|
||||
current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, lun);
|
||||
s->current_req = scsi_req_new(current_lun, 0, lun, buf, NULL);
|
||||
datalen = scsi_req_enqueue(s->current_req);
|
||||
s->ti_size = datalen;
|
||||
if (datalen != 0) {
|
||||
@ -720,7 +722,11 @@ void esp_init(target_phys_addr_t espaddr, int it_shift,
|
||||
*dma_enable = qdev_get_gpio_in(dev, 1);
|
||||
}
|
||||
|
||||
static const struct SCSIBusOps esp_scsi_ops = {
|
||||
static const struct SCSIBusInfo esp_scsi_info = {
|
||||
.tcq = false,
|
||||
.max_target = ESP_MAX_DEVS,
|
||||
.max_lun = 7,
|
||||
|
||||
.transfer_data = esp_transfer_data,
|
||||
.complete = esp_command_complete,
|
||||
.cancel = esp_request_cancelled
|
||||
@ -740,7 +746,7 @@ static int esp_init1(SysBusDevice *dev)
|
||||
|
||||
qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2);
|
||||
|
||||
scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, &esp_scsi_ops);
|
||||
scsi_bus_new(&s->bus, &dev->qdev, &esp_scsi_info);
|
||||
return scsi_bus_legacy_handle_cmdline(&s->bus);
|
||||
}
|
||||
|
||||
|
2
hw/i2c.c
2
hw/i2c.c
@ -84,7 +84,7 @@ int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv)
|
||||
DeviceState *qdev;
|
||||
i2c_slave *slave = NULL;
|
||||
|
||||
QLIST_FOREACH(qdev, &bus->qbus.children, sibling) {
|
||||
QTAILQ_FOREACH(qdev, &bus->qbus.children, sibling) {
|
||||
i2c_slave *candidate = I2C_SLAVE_FROM_QDEV(qdev);
|
||||
if (candidate->address == address) {
|
||||
slave = candidate;
|
||||
|
119
hw/ide/atapi.c
119
hw/ide/atapi.c
@ -154,10 +154,10 @@ void ide_atapi_io_error(IDEState *s, int ret)
|
||||
{
|
||||
/* XXX: handle more errors */
|
||||
if (ret == -ENOMEDIUM) {
|
||||
ide_atapi_cmd_error(s, SENSE_NOT_READY,
|
||||
ide_atapi_cmd_error(s, NOT_READY,
|
||||
ASC_MEDIUM_NOT_PRESENT);
|
||||
} else {
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
|
||||
ASC_LOGICAL_BLOCK_OOR);
|
||||
}
|
||||
}
|
||||
@ -282,7 +282,7 @@ static void ide_atapi_cmd_check_status(IDEState *s)
|
||||
#ifdef DEBUG_IDE_ATAPI
|
||||
printf("atapi_cmd_check_status\n");
|
||||
#endif
|
||||
s->error = MC_ERR | (SENSE_UNIT_ATTENTION << 4);
|
||||
s->error = MC_ERR | (UNIT_ATTENTION << 4);
|
||||
s->status = ERR_STAT;
|
||||
s->nsector = 0;
|
||||
ide_set_irq(s->bus);
|
||||
@ -354,7 +354,7 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
|
||||
ide_atapi_cmd_read_dma_cb, s);
|
||||
if (!s->bus->dma->aiocb) {
|
||||
/* Note: media not present is the most likely case */
|
||||
ide_atapi_cmd_error(s, SENSE_NOT_READY,
|
||||
ide_atapi_cmd_error(s, NOT_READY,
|
||||
ASC_MEDIUM_NOT_PRESENT);
|
||||
goto eot;
|
||||
}
|
||||
@ -505,19 +505,6 @@ static int ide_dvd_read_structure(IDEState *s, int format,
|
||||
static unsigned int event_status_media(IDEState *s,
|
||||
uint8_t *buf)
|
||||
{
|
||||
enum media_event_code {
|
||||
MEC_NO_CHANGE = 0, /* Status unchanged */
|
||||
MEC_EJECT_REQUESTED, /* received a request from user to eject */
|
||||
MEC_NEW_MEDIA, /* new media inserted and ready for access */
|
||||
MEC_MEDIA_REMOVAL, /* only for media changers */
|
||||
MEC_MEDIA_CHANGED, /* only for media changers */
|
||||
MEC_BG_FORMAT_COMPLETED, /* MRW or DVD+RW b/g format completed */
|
||||
MEC_BG_FORMAT_RESTARTED, /* MRW or DVD+RW b/g format restarted */
|
||||
};
|
||||
enum media_status {
|
||||
MS_TRAY_OPEN = 1,
|
||||
MS_MEDIA_PRESENT = 2,
|
||||
};
|
||||
uint8_t event_code, media_status;
|
||||
|
||||
media_status = 0;
|
||||
@ -564,27 +551,6 @@ static void cmd_get_event_status_notification(IDEState *s,
|
||||
uint8_t notification_class;
|
||||
uint8_t supported_events;
|
||||
} QEMU_PACKED *gesn_event_header;
|
||||
|
||||
enum notification_class_request_type {
|
||||
NCR_RESERVED1 = 1 << 0,
|
||||
NCR_OPERATIONAL_CHANGE = 1 << 1,
|
||||
NCR_POWER_MANAGEMENT = 1 << 2,
|
||||
NCR_EXTERNAL_REQUEST = 1 << 3,
|
||||
NCR_MEDIA = 1 << 4,
|
||||
NCR_MULTI_HOST = 1 << 5,
|
||||
NCR_DEVICE_BUSY = 1 << 6,
|
||||
NCR_RESERVED2 = 1 << 7,
|
||||
};
|
||||
enum event_notification_class_field {
|
||||
ENC_NO_EVENTS = 0,
|
||||
ENC_OPERATIONAL_CHANGE,
|
||||
ENC_POWER_MANAGEMENT,
|
||||
ENC_EXTERNAL_REQUEST,
|
||||
ENC_MEDIA,
|
||||
ENC_MULTIPLE_HOSTS,
|
||||
ENC_DEVICE_BUSY,
|
||||
ENC_RESERVED,
|
||||
};
|
||||
unsigned int max_len, used_len;
|
||||
|
||||
gesn_cdb = (void *)packet;
|
||||
@ -595,7 +561,7 @@ static void cmd_get_event_status_notification(IDEState *s,
|
||||
/* It is fine by the MMC spec to not support async mode operations */
|
||||
if (!(gesn_cdb->polled & 0x01)) { /* asynchronous mode */
|
||||
/* Only polling is supported, asynchronous mode is not. */
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
|
||||
ASC_INV_FIELD_IN_CMD_PACKET);
|
||||
return;
|
||||
}
|
||||
@ -606,8 +572,11 @@ static void cmd_get_event_status_notification(IDEState *s,
|
||||
* These are the supported events.
|
||||
*
|
||||
* We currently only support requests of the 'media' type.
|
||||
* Notification class requests and supported event classes are bitmasks,
|
||||
* but they are build from the same values as the "notification class"
|
||||
* field.
|
||||
*/
|
||||
gesn_event_header->supported_events = NCR_MEDIA;
|
||||
gesn_event_header->supported_events = 1 << GESN_MEDIA;
|
||||
|
||||
/*
|
||||
* We use |= below to set the class field; other bits in this byte
|
||||
@ -621,8 +590,8 @@ static void cmd_get_event_status_notification(IDEState *s,
|
||||
* notification_class_request_type enum above specifies the
|
||||
* priority: upper elements are higher prio than lower ones.
|
||||
*/
|
||||
if (gesn_cdb->class & NCR_MEDIA) {
|
||||
gesn_event_header->notification_class |= ENC_MEDIA;
|
||||
if (gesn_cdb->class & (1 << GESN_MEDIA)) {
|
||||
gesn_event_header->notification_class |= GESN_MEDIA;
|
||||
used_len = event_status_media(s, buf);
|
||||
} else {
|
||||
gesn_event_header->notification_class = 0x80; /* No event available */
|
||||
@ -643,8 +612,8 @@ static void cmd_request_sense(IDEState *s, uint8_t *buf)
|
||||
buf[7] = 10;
|
||||
buf[12] = s->asc;
|
||||
|
||||
if (s->sense_key == SENSE_UNIT_ATTENTION) {
|
||||
s->sense_key = SENSE_NONE;
|
||||
if (s->sense_key == UNIT_ATTENTION) {
|
||||
s->sense_key = NO_SENSE;
|
||||
}
|
||||
|
||||
ide_atapi_cmd_reply(s, 18, max_len);
|
||||
@ -676,7 +645,7 @@ static void cmd_get_configuration(IDEState *s, uint8_t *buf)
|
||||
|
||||
/* only feature 0 is supported */
|
||||
if (buf[2] != 0 || buf[3] != 0) {
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
|
||||
ASC_INV_FIELD_IN_CMD_PACKET);
|
||||
return;
|
||||
}
|
||||
@ -733,7 +702,7 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
|
||||
switch(action) {
|
||||
case 0: /* current values */
|
||||
switch(code) {
|
||||
case GPMODE_R_W_ERROR_PAGE: /* error recovery */
|
||||
case MODE_PAGE_R_W_ERROR: /* error recovery */
|
||||
cpu_to_ube16(&buf[0], 16 + 6);
|
||||
buf[2] = 0x70;
|
||||
buf[3] = 0;
|
||||
@ -742,8 +711,8 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
|
||||
buf[6] = 0;
|
||||
buf[7] = 0;
|
||||
|
||||
buf[8] = 0x01;
|
||||
buf[9] = 0x06;
|
||||
buf[8] = MODE_PAGE_R_W_ERROR;
|
||||
buf[9] = 16 - 10;
|
||||
buf[10] = 0x00;
|
||||
buf[11] = 0x05;
|
||||
buf[12] = 0x00;
|
||||
@ -752,7 +721,7 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
|
||||
buf[15] = 0x00;
|
||||
ide_atapi_cmd_reply(s, 16, max_len);
|
||||
break;
|
||||
case GPMODE_AUDIO_CTL_PAGE:
|
||||
case MODE_PAGE_AUDIO_CTL:
|
||||
cpu_to_ube16(&buf[0], 24 + 6);
|
||||
buf[2] = 0x70;
|
||||
buf[3] = 0;
|
||||
@ -761,6 +730,8 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
|
||||
buf[6] = 0;
|
||||
buf[7] = 0;
|
||||
|
||||
buf[8] = MODE_PAGE_AUDIO_CTL;
|
||||
buf[9] = 24 - 10;
|
||||
/* Fill with CDROM audio volume */
|
||||
buf[17] = 0;
|
||||
buf[19] = 0;
|
||||
@ -769,7 +740,7 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
|
||||
|
||||
ide_atapi_cmd_reply(s, 24, max_len);
|
||||
break;
|
||||
case GPMODE_CAPABILITIES_PAGE:
|
||||
case MODE_PAGE_CAPABILITIES:
|
||||
cpu_to_ube16(&buf[0], 28 + 6);
|
||||
buf[2] = 0x70;
|
||||
buf[3] = 0;
|
||||
@ -778,9 +749,9 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
|
||||
buf[6] = 0;
|
||||
buf[7] = 0;
|
||||
|
||||
buf[8] = 0x2a;
|
||||
buf[9] = 0x12;
|
||||
buf[10] = 0x00;
|
||||
buf[8] = MODE_PAGE_CAPABILITIES;
|
||||
buf[9] = 28 - 10;
|
||||
buf[10] = 0x3b; /* read CDR/CDRW/DVDROM/DVDR/DVDRAM */
|
||||
buf[11] = 0x00;
|
||||
|
||||
/* Claim PLAY_AUDIO capability (0x01) since some Linux
|
||||
@ -789,14 +760,14 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
|
||||
buf[13] = 3 << 5;
|
||||
buf[14] = (1 << 0) | (1 << 3) | (1 << 5);
|
||||
if (s->tray_locked) {
|
||||
buf[6] |= 1 << 1;
|
||||
buf[14] |= 1 << 1;
|
||||
}
|
||||
buf[15] = 0x00;
|
||||
cpu_to_ube16(&buf[16], 706);
|
||||
buf[18] = 0;
|
||||
buf[15] = 0x00; /* No volume & mute control, no changer */
|
||||
cpu_to_ube16(&buf[16], 704); /* 4x read speed */
|
||||
buf[18] = 0; /* Two volume levels */
|
||||
buf[19] = 2;
|
||||
cpu_to_ube16(&buf[20], 512);
|
||||
cpu_to_ube16(&buf[22], 706);
|
||||
cpu_to_ube16(&buf[20], 512); /* 512k buffer */
|
||||
cpu_to_ube16(&buf[22], 704); /* 4x read speed current */
|
||||
buf[24] = 0;
|
||||
buf[25] = 0;
|
||||
buf[26] = 0;
|
||||
@ -813,14 +784,14 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
|
||||
goto error_cmd;
|
||||
default:
|
||||
case 3: /* saved values */
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
|
||||
ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
error_cmd:
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
|
||||
}
|
||||
|
||||
static void cmd_test_unit_ready(IDEState *s, uint8_t *buf)
|
||||
@ -883,7 +854,7 @@ static void cmd_read_cd(IDEState *s, uint8_t* buf)
|
||||
ide_atapi_cmd_read(s, lba, nb_sectors, 2352);
|
||||
break;
|
||||
default:
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
|
||||
ASC_INV_FIELD_IN_CMD_PACKET);
|
||||
break;
|
||||
}
|
||||
@ -896,7 +867,7 @@ static void cmd_seek(IDEState *s, uint8_t* buf)
|
||||
|
||||
lba = ube32_to_cpu(buf + 2);
|
||||
if (lba >= total_sectors) {
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -912,7 +883,7 @@ static void cmd_start_stop_unit(IDEState *s, uint8_t* buf)
|
||||
if (loej) {
|
||||
if (!start && !s->tray_open && s->tray_locked) {
|
||||
sense = bdrv_is_inserted(s->bs)
|
||||
? SENSE_NOT_READY : SENSE_ILLEGAL_REQUEST;
|
||||
? NOT_READY : ILLEGAL_REQUEST;
|
||||
ide_atapi_cmd_error(s, sense, ASC_MEDIA_REMOVAL_PREVENTED);
|
||||
return;
|
||||
}
|
||||
@ -971,7 +942,7 @@ static void cmd_read_toc_pma_atip(IDEState *s, uint8_t* buf)
|
||||
break;
|
||||
default:
|
||||
error_cmd:
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
|
||||
ASC_INV_FIELD_IN_CMD_PACKET);
|
||||
}
|
||||
}
|
||||
@ -997,11 +968,11 @@ static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf)
|
||||
|
||||
if (format < 0xff) {
|
||||
if (media_is_cd(s)) {
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
|
||||
ASC_INCOMPATIBLE_FORMAT);
|
||||
return;
|
||||
} else if (!media_present(s)) {
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
|
||||
ASC_INV_FIELD_IN_CMD_PACKET);
|
||||
return;
|
||||
}
|
||||
@ -1017,7 +988,7 @@ static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf)
|
||||
ret = ide_dvd_read_structure(s, format, buf, buf);
|
||||
|
||||
if (ret < 0) {
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret);
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST, -ret);
|
||||
} else {
|
||||
ide_atapi_cmd_reply(s, ret, max_len);
|
||||
}
|
||||
@ -1034,7 +1005,7 @@ static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf)
|
||||
case 0x90: /* TODO: List of recognized format layers */
|
||||
case 0xc0: /* TODO: Write protection status */
|
||||
default:
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
|
||||
ASC_INV_FIELD_IN_CMD_PACKET);
|
||||
break;
|
||||
}
|
||||
@ -1106,7 +1077,7 @@ void ide_atapi_cmd(IDEState *s)
|
||||
* condition response unless a higher priority status, defined by the drive
|
||||
* here, is pending.
|
||||
*/
|
||||
if (s->sense_key == SENSE_UNIT_ATTENTION &&
|
||||
if (s->sense_key == UNIT_ATTENTION &&
|
||||
!(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA)) {
|
||||
ide_atapi_cmd_check_status(s);
|
||||
return;
|
||||
@ -1119,10 +1090,10 @@ void ide_atapi_cmd(IDEState *s)
|
||||
* states rely on this behavior.
|
||||
*/
|
||||
if (!s->tray_open && bdrv_is_inserted(s->bs) && s->cdrom_changed) {
|
||||
ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
|
||||
ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT);
|
||||
|
||||
s->cdrom_changed = 0;
|
||||
s->sense_key = SENSE_UNIT_ATTENTION;
|
||||
s->sense_key = UNIT_ATTENTION;
|
||||
s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED;
|
||||
return;
|
||||
}
|
||||
@ -1131,7 +1102,7 @@ void ide_atapi_cmd(IDEState *s)
|
||||
if ((atapi_cmd_table[s->io_buffer[0]].flags & CHECK_READY) &&
|
||||
(!media_present(s) || !bdrv_is_inserted(s->bs)))
|
||||
{
|
||||
ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
|
||||
ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1141,5 +1112,5 @@ void ide_atapi_cmd(IDEState *s)
|
||||
return;
|
||||
}
|
||||
|
||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE);
|
||||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE);
|
||||
}
|
||||
|
@ -799,7 +799,7 @@ static void ide_cd_change_cb(void *opaque, bool load)
|
||||
* First indicate to the guest that a CD has been removed. That's
|
||||
* done on the next command the guest sends us.
|
||||
*
|
||||
* Then we set SENSE_UNIT_ATTENTION, by which the guest will
|
||||
* Then we set UNIT_ATTENTION, by which the guest will
|
||||
* detect a new CD in the drive. See ide_atapi_cmd() for details.
|
||||
*/
|
||||
s->cdrom_changed = 1;
|
||||
@ -2027,7 +2027,7 @@ static int ide_drive_post_load(void *opaque, int version_id)
|
||||
IDEState *s = opaque;
|
||||
|
||||
if (version_id < 3) {
|
||||
if (s->sense_key == SENSE_UNIT_ATTENTION &&
|
||||
if (s->sense_key == UNIT_ATTENTION &&
|
||||
s->asc == ASC_MEDIUM_MAY_HAVE_CHANGED) {
|
||||
s->cdrom_changed = 1;
|
||||
}
|
||||
@ -2039,7 +2039,7 @@ static int ide_drive_pio_post_load(void *opaque, int version_id)
|
||||
{
|
||||
IDEState *s = opaque;
|
||||
|
||||
if (s->end_transfer_fn_idx > ARRAY_SIZE(transfer_end_table)) {
|
||||
if (s->end_transfer_fn_idx >= ARRAY_SIZE(transfer_end_table)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx];
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "iorange.h"
|
||||
#include "dma.h"
|
||||
#include "sysemu.h"
|
||||
#include "hw/scsi-defs.h"
|
||||
|
||||
/* debug IDE devices */
|
||||
//#define DEBUG_IDE
|
||||
@ -280,71 +281,6 @@ typedef struct IDEDMAOps IDEDMAOps;
|
||||
#define GPCMD_GET_MEDIA_STATUS 0xda
|
||||
#define GPCMD_MODE_SENSE_6 0x1a
|
||||
|
||||
/* Mode page codes for mode sense/set */
|
||||
#define GPMODE_R_W_ERROR_PAGE 0x01
|
||||
#define GPMODE_WRITE_PARMS_PAGE 0x05
|
||||
#define GPMODE_AUDIO_CTL_PAGE 0x0e
|
||||
#define GPMODE_POWER_PAGE 0x1a
|
||||
#define GPMODE_FAULT_FAIL_PAGE 0x1c
|
||||
#define GPMODE_TO_PROTECT_PAGE 0x1d
|
||||
#define GPMODE_CAPABILITIES_PAGE 0x2a
|
||||
#define GPMODE_ALL_PAGES 0x3f
|
||||
/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor
|
||||
* of MODE_SENSE_POWER_PAGE */
|
||||
#define GPMODE_CDROM_PAGE 0x0d
|
||||
|
||||
/*
|
||||
* Based on values from <linux/cdrom.h> but extending CD_MINS
|
||||
* to the maximum common size allowed by the Orange's Book ATIP
|
||||
*
|
||||
* 90 and 99 min CDs are also available but using them as the
|
||||
* upper limit reduces the effectiveness of the heuristic to
|
||||
* detect DVDs burned to less than 25% of their maximum capacity
|
||||
*/
|
||||
|
||||
/* Some generally useful CD-ROM information */
|
||||
#define CD_MINS 80 /* max. minutes per CD */
|
||||
#define CD_SECS 60 /* seconds per minute */
|
||||
#define CD_FRAMES 75 /* frames per second */
|
||||
#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */
|
||||
#define CD_MAX_BYTES (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE)
|
||||
#define CD_MAX_SECTORS (CD_MAX_BYTES / 512)
|
||||
|
||||
/*
|
||||
* The MMC values are not IDE specific and might need to be moved
|
||||
* to a common header if they are also needed for the SCSI emulation
|
||||
*/
|
||||
|
||||
/* Profile list from MMC-6 revision 1 table 91 */
|
||||
#define MMC_PROFILE_NONE 0x0000
|
||||
#define MMC_PROFILE_CD_ROM 0x0008
|
||||
#define MMC_PROFILE_CD_R 0x0009
|
||||
#define MMC_PROFILE_CD_RW 0x000A
|
||||
#define MMC_PROFILE_DVD_ROM 0x0010
|
||||
#define MMC_PROFILE_DVD_R_SR 0x0011
|
||||
#define MMC_PROFILE_DVD_RAM 0x0012
|
||||
#define MMC_PROFILE_DVD_RW_RO 0x0013
|
||||
#define MMC_PROFILE_DVD_RW_SR 0x0014
|
||||
#define MMC_PROFILE_DVD_R_DL_SR 0x0015
|
||||
#define MMC_PROFILE_DVD_R_DL_JR 0x0016
|
||||
#define MMC_PROFILE_DVD_RW_DL 0x0017
|
||||
#define MMC_PROFILE_DVD_DDR 0x0018
|
||||
#define MMC_PROFILE_DVD_PLUS_RW 0x001A
|
||||
#define MMC_PROFILE_DVD_PLUS_R 0x001B
|
||||
#define MMC_PROFILE_DVD_PLUS_RW_DL 0x002A
|
||||
#define MMC_PROFILE_DVD_PLUS_R_DL 0x002B
|
||||
#define MMC_PROFILE_BD_ROM 0x0040
|
||||
#define MMC_PROFILE_BD_R_SRM 0x0041
|
||||
#define MMC_PROFILE_BD_R_RRM 0x0042
|
||||
#define MMC_PROFILE_BD_RE 0x0043
|
||||
#define MMC_PROFILE_HDDVD_ROM 0x0050
|
||||
#define MMC_PROFILE_HDDVD_R 0x0051
|
||||
#define MMC_PROFILE_HDDVD_RAM 0x0052
|
||||
#define MMC_PROFILE_HDDVD_RW 0x0053
|
||||
#define MMC_PROFILE_HDDVD_R_DL 0x0058
|
||||
#define MMC_PROFILE_HDDVD_RW_DL 0x005A
|
||||
#define MMC_PROFILE_INVALID 0xFFFF
|
||||
|
||||
#define ATAPI_INT_REASON_CD 0x01 /* 0 = data transfer */
|
||||
#define ATAPI_INT_REASON_IO 0x02 /* 1 = transfer to the host */
|
||||
#define ATAPI_INT_REASON_REL 0x04
|
||||
@ -366,11 +302,6 @@ typedef struct IDEDMAOps IDEDMAOps;
|
||||
#define CFA_INVALID_ADDRESS 0x21
|
||||
#define CFA_ADDRESS_OVERFLOW 0x2f
|
||||
|
||||
#define SENSE_NONE 0
|
||||
#define SENSE_NOT_READY 2
|
||||
#define SENSE_ILLEGAL_REQUEST 5
|
||||
#define SENSE_UNIT_ATTENTION 6
|
||||
|
||||
#define SMART_READ_DATA 0xd0
|
||||
#define SMART_READ_THRESH 0xd1
|
||||
#define SMART_ATTR_AUTOSAVE 0xd2
|
||||
|
@ -87,7 +87,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
|
||||
if (!m->aiocb) {
|
||||
qemu_sglist_destroy(&s->sg);
|
||||
/* Note: media not present is the most likely case */
|
||||
ide_atapi_cmd_error(s, SENSE_NOT_READY,
|
||||
ide_atapi_cmd_error(s, NOT_READY,
|
||||
ASC_MEDIUM_NOT_PRESENT);
|
||||
goto done;
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad)
|
||||
DeviceState *qdev;
|
||||
HDACodecDevice *cdev;
|
||||
|
||||
QLIST_FOREACH(qdev, &bus->qbus.children, sibling) {
|
||||
QTAILQ_FOREACH(qdev, &bus->qbus.children, sibling) {
|
||||
cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
|
||||
if (cdev->cad == cad) {
|
||||
return cdev;
|
||||
@ -490,7 +490,7 @@ static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool runn
|
||||
DeviceState *qdev;
|
||||
HDACodecDevice *cdev;
|
||||
|
||||
QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
|
||||
QTAILQ_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
|
||||
cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
|
||||
if (cdev->info->stream) {
|
||||
cdev->info->stream(cdev, stream, running, output);
|
||||
@ -1114,7 +1114,7 @@ static void intel_hda_reset(DeviceState *dev)
|
||||
d->wall_base_ns = qemu_get_clock_ns(vm_clock);
|
||||
|
||||
/* reset codecs */
|
||||
QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
|
||||
QTAILQ_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
|
||||
cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
|
||||
if (qdev->info->reset) {
|
||||
qdev->info->reset(qdev);
|
||||
|
@ -531,7 +531,7 @@ static void lsi_bad_selection(LSIState *s, uint32_t id)
|
||||
/* Initiate a SCSI layer data transfer. */
|
||||
static void lsi_do_dma(LSIState *s, int out)
|
||||
{
|
||||
uint32_t count, id;
|
||||
uint32_t count;
|
||||
target_phys_addr_t addr;
|
||||
SCSIDevice *dev;
|
||||
|
||||
@ -542,12 +542,8 @@ static void lsi_do_dma(LSIState *s, int out)
|
||||
return;
|
||||
}
|
||||
|
||||
id = (s->current->tag >> 8) & 0xf;
|
||||
dev = s->bus.devs[id];
|
||||
if (!dev) {
|
||||
lsi_bad_selection(s, id);
|
||||
return;
|
||||
}
|
||||
dev = s->current->req->dev;
|
||||
assert(dev);
|
||||
|
||||
count = s->dbc;
|
||||
if (count > s->current->dma_len)
|
||||
@ -771,7 +767,7 @@ static void lsi_do_command(LSIState *s)
|
||||
s->command_complete = 0;
|
||||
|
||||
id = (s->select_tag >> 8) & 0xf;
|
||||
dev = s->bus.devs[id];
|
||||
dev = scsi_device_find(&s->bus, 0, id, s->current_lun);
|
||||
if (!dev) {
|
||||
lsi_bad_selection(s, id);
|
||||
return;
|
||||
@ -1202,7 +1198,7 @@ again:
|
||||
}
|
||||
s->sstat0 |= LSI_SSTAT0_WOA;
|
||||
s->scntl1 &= ~LSI_SCNTL1_IARB;
|
||||
if (id >= LSI_MAX_DEVS || !s->bus.devs[id]) {
|
||||
if (!scsi_device_find(&s->bus, 0, id, 0)) {
|
||||
lsi_bad_selection(s, id);
|
||||
break;
|
||||
}
|
||||
@ -1684,13 +1680,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
|
||||
if (val & LSI_SCNTL1_RST) {
|
||||
if (!(s->sstat0 & LSI_SSTAT0_RST)) {
|
||||
DeviceState *dev;
|
||||
int id;
|
||||
|
||||
for (id = 0; id < s->bus.ndev; id++) {
|
||||
if (s->bus.devs[id]) {
|
||||
dev = &s->bus.devs[id]->qdev;
|
||||
dev->info->reset(dev);
|
||||
}
|
||||
QTAILQ_FOREACH(dev, &s->bus.qbus.children, sibling) {
|
||||
dev->info->reset(dev);
|
||||
}
|
||||
s->sstat0 |= LSI_SSTAT0_RST;
|
||||
lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0);
|
||||
@ -2091,7 +2083,11 @@ static int lsi_scsi_uninit(PCIDevice *d)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct SCSIBusOps lsi_scsi_ops = {
|
||||
static const struct SCSIBusInfo lsi_scsi_info = {
|
||||
.tcq = true,
|
||||
.max_target = LSI_MAX_DEVS,
|
||||
.max_lun = 0, /* LUN support is buggy */
|
||||
|
||||
.transfer_data = lsi_transfer_data,
|
||||
.complete = lsi_command_complete,
|
||||
.cancel = lsi_request_cancelled
|
||||
@ -2118,7 +2114,7 @@ static int lsi_scsi_init(PCIDevice *dev)
|
||||
pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io);
|
||||
QTAILQ_INIT(&s->queue);
|
||||
|
||||
scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, &lsi_scsi_ops);
|
||||
scsi_bus_new(&s->bus, &dev->qdev, &lsi_scsi_info);
|
||||
if (!dev->qdev.hotplugged) {
|
||||
return scsi_bus_legacy_handle_cmdline(&s->bus);
|
||||
}
|
||||
|
24
hw/qdev.c
24
hw/qdev.c
@ -91,7 +91,7 @@ static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info)
|
||||
qdev_prop_set_defaults(dev, dev->info->props);
|
||||
qdev_prop_set_defaults(dev, dev->parent_bus->info->props);
|
||||
qdev_prop_set_globals(dev);
|
||||
QLIST_INSERT_HEAD(&bus->children, dev, sibling);
|
||||
QTAILQ_INSERT_HEAD(&bus->children, dev, sibling);
|
||||
if (qdev_hotplug) {
|
||||
assert(bus->allow_hotplug);
|
||||
dev->hotplugged = 1;
|
||||
@ -408,7 +408,7 @@ void qdev_free(DeviceState *dev)
|
||||
if (dev->opts)
|
||||
qemu_opts_del(dev->opts);
|
||||
}
|
||||
QLIST_REMOVE(dev, sibling);
|
||||
QTAILQ_REMOVE(&dev->parent_bus->children, dev, sibling);
|
||||
for (prop = dev->info->props; prop && prop->name; prop++) {
|
||||
if (prop->info->free) {
|
||||
prop->info->free(dev, prop);
|
||||
@ -510,7 +510,7 @@ int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
|
||||
}
|
||||
}
|
||||
|
||||
QLIST_FOREACH(dev, &bus->children, sibling) {
|
||||
QTAILQ_FOREACH(dev, &bus->children, sibling) {
|
||||
err = qdev_walk_children(dev, devfn, busfn, opaque);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
@ -560,7 +560,7 @@ static BusState *qbus_find_recursive(BusState *bus, const char *name,
|
||||
return bus;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(dev, &bus->children, sibling) {
|
||||
QTAILQ_FOREACH(dev, &bus->children, sibling) {
|
||||
QLIST_FOREACH(child, &dev->child_bus, sibling) {
|
||||
ret = qbus_find_recursive(child, name, info);
|
||||
if (ret) {
|
||||
@ -576,7 +576,7 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id)
|
||||
DeviceState *dev, *ret;
|
||||
BusState *child;
|
||||
|
||||
QLIST_FOREACH(dev, &bus->children, sibling) {
|
||||
QTAILQ_FOREACH(dev, &bus->children, sibling) {
|
||||
if (dev->id && strcmp(dev->id, id) == 0)
|
||||
return dev;
|
||||
QLIST_FOREACH(child, &dev->child_bus, sibling) {
|
||||
@ -609,7 +609,7 @@ static void qbus_list_dev(BusState *bus)
|
||||
const char *sep = " ";
|
||||
|
||||
error_printf("devices at \"%s\":", bus->name);
|
||||
QLIST_FOREACH(dev, &bus->children, sibling) {
|
||||
QTAILQ_FOREACH(dev, &bus->children, sibling) {
|
||||
error_printf("%s\"%s\"", sep, dev->info->name);
|
||||
if (dev->id)
|
||||
error_printf("/\"%s\"", dev->id);
|
||||
@ -640,17 +640,17 @@ static DeviceState *qbus_find_dev(BusState *bus, char *elem)
|
||||
* (2) driver name
|
||||
* (3) driver alias, if present
|
||||
*/
|
||||
QLIST_FOREACH(dev, &bus->children, sibling) {
|
||||
QTAILQ_FOREACH(dev, &bus->children, sibling) {
|
||||
if (dev->id && strcmp(dev->id, elem) == 0) {
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
QLIST_FOREACH(dev, &bus->children, sibling) {
|
||||
QTAILQ_FOREACH(dev, &bus->children, sibling) {
|
||||
if (strcmp(dev->info->name, elem) == 0) {
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
QLIST_FOREACH(dev, &bus->children, sibling) {
|
||||
QTAILQ_FOREACH(dev, &bus->children, sibling) {
|
||||
if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
|
||||
return dev;
|
||||
}
|
||||
@ -774,7 +774,7 @@ void qbus_create_inplace(BusState *bus, BusInfo *info,
|
||||
bus->name = buf;
|
||||
}
|
||||
|
||||
QLIST_INIT(&bus->children);
|
||||
QTAILQ_INIT(&bus->children);
|
||||
if (parent) {
|
||||
QLIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
|
||||
parent->num_child_bus++;
|
||||
@ -809,7 +809,7 @@ void qbus_free(BusState *bus)
|
||||
{
|
||||
DeviceState *dev;
|
||||
|
||||
while ((dev = QLIST_FIRST(&bus->children)) != NULL) {
|
||||
while ((dev = QTAILQ_FIRST(&bus->children)) != NULL) {
|
||||
qdev_free(dev);
|
||||
}
|
||||
if (bus->parent) {
|
||||
@ -878,7 +878,7 @@ static void qbus_print(Monitor *mon, BusState *bus, int indent)
|
||||
qdev_printf("bus: %s\n", bus->name);
|
||||
indent += 2;
|
||||
qdev_printf("type %s\n", bus->info->name);
|
||||
QLIST_FOREACH(dev, &bus->children, sibling) {
|
||||
QTAILQ_FOREACH(dev, &bus->children, sibling) {
|
||||
qdev_print(mon, dev, indent);
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ struct DeviceState {
|
||||
qemu_irq *gpio_in;
|
||||
QLIST_HEAD(, BusState) child_bus;
|
||||
int num_child_bus;
|
||||
QLIST_ENTRY(DeviceState) sibling;
|
||||
QTAILQ_ENTRY(DeviceState) sibling;
|
||||
int instance_id_alias;
|
||||
int alias_required_for_version;
|
||||
};
|
||||
@ -73,7 +73,7 @@ struct BusState {
|
||||
const char *name;
|
||||
int allow_hotplug;
|
||||
int qdev_allocated;
|
||||
QLIST_HEAD(, DeviceState) children;
|
||||
QTAILQ_HEAD(ChildrenHead, DeviceState) children;
|
||||
QLIST_ENTRY(BusState) sibling;
|
||||
};
|
||||
|
||||
|
@ -274,7 +274,7 @@ VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus,
|
||||
DeviceState *dev;
|
||||
int i;
|
||||
|
||||
QLIST_FOREACH(dev, &bus->bus.children, sibling) {
|
||||
QTAILQ_FOREACH(dev, &bus->bus.children, sibling) {
|
||||
_dev = (VirtIOS390Device *)dev;
|
||||
for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
|
||||
if (!virtio_queue_get_addr(_dev->vdev, i))
|
||||
@ -297,7 +297,7 @@ VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem)
|
||||
VirtIOS390Device *_dev;
|
||||
DeviceState *dev;
|
||||
|
||||
QLIST_FOREACH(dev, &bus->bus.children, sibling) {
|
||||
QTAILQ_FOREACH(dev, &bus->bus.children, sibling) {
|
||||
_dev = (VirtIOS390Device *)dev;
|
||||
if (_dev->dev_offs == mem) {
|
||||
return _dev;
|
||||
|
283
hw/scsi-bus.c
283
hw/scsi-bus.c
@ -8,6 +8,7 @@
|
||||
|
||||
static char *scsibus_get_fw_dev_path(DeviceState *dev);
|
||||
static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf);
|
||||
static void scsi_req_dequeue(SCSIRequest *req);
|
||||
static int scsi_build_sense(uint8_t *in_buf, int in_len,
|
||||
uint8_t *buf, int len, bool fixed);
|
||||
|
||||
@ -16,53 +17,123 @@ static struct BusInfo scsi_bus_info = {
|
||||
.size = sizeof(SCSIBus),
|
||||
.get_fw_dev_path = scsibus_get_fw_dev_path,
|
||||
.props = (Property[]) {
|
||||
DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0),
|
||||
DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1),
|
||||
DEFINE_PROP_UINT32("lun", SCSIDevice, lun, 0),
|
||||
DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
},
|
||||
};
|
||||
static int next_scsi_bus;
|
||||
|
||||
/* Create a scsi bus, and attach devices to it. */
|
||||
void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
|
||||
const SCSIBusOps *ops)
|
||||
void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info)
|
||||
{
|
||||
qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL);
|
||||
bus->busnr = next_scsi_bus++;
|
||||
bus->tcq = tcq;
|
||||
bus->ndev = ndev;
|
||||
bus->ops = ops;
|
||||
bus->info = info;
|
||||
bus->qbus.allow_hotplug = 1;
|
||||
}
|
||||
|
||||
static void scsi_dma_restart_bh(void *opaque)
|
||||
{
|
||||
SCSIDevice *s = opaque;
|
||||
SCSIRequest *req, *next;
|
||||
|
||||
qemu_bh_delete(s->bh);
|
||||
s->bh = NULL;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) {
|
||||
scsi_req_ref(req);
|
||||
if (req->retry) {
|
||||
req->retry = false;
|
||||
switch (req->cmd.mode) {
|
||||
case SCSI_XFER_FROM_DEV:
|
||||
case SCSI_XFER_TO_DEV:
|
||||
scsi_req_continue(req);
|
||||
break;
|
||||
case SCSI_XFER_NONE:
|
||||
scsi_req_dequeue(req);
|
||||
scsi_req_enqueue(req);
|
||||
break;
|
||||
}
|
||||
}
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
}
|
||||
|
||||
void scsi_req_retry(SCSIRequest *req)
|
||||
{
|
||||
/* No need to save a reference, because scsi_dma_restart_bh just
|
||||
* looks at the request list. */
|
||||
req->retry = true;
|
||||
}
|
||||
|
||||
static void scsi_dma_restart_cb(void *opaque, int running, RunState state)
|
||||
{
|
||||
SCSIDevice *s = opaque;
|
||||
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
if (!s->bh) {
|
||||
s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
|
||||
qemu_bh_schedule(s->bh);
|
||||
}
|
||||
}
|
||||
|
||||
static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base)
|
||||
{
|
||||
SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
|
||||
SCSIDeviceInfo *info = DO_UPCAST(SCSIDeviceInfo, qdev, base);
|
||||
SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
|
||||
SCSIDevice *d;
|
||||
int rc = -1;
|
||||
|
||||
if (dev->id == -1) {
|
||||
for (dev->id = 0; dev->id < bus->ndev; dev->id++) {
|
||||
if (bus->devs[dev->id] == NULL)
|
||||
break;
|
||||
}
|
||||
if (dev->channel > bus->info->max_channel) {
|
||||
error_report("bad scsi channel id: %d", dev->channel);
|
||||
goto err;
|
||||
}
|
||||
if (dev->id >= bus->ndev) {
|
||||
if (dev->id != -1 && dev->id > bus->info->max_target) {
|
||||
error_report("bad scsi device id: %d", dev->id);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bus->devs[dev->id]) {
|
||||
qdev_free(&bus->devs[dev->id]->qdev);
|
||||
if (dev->id == -1) {
|
||||
int id = -1;
|
||||
if (dev->lun == -1) {
|
||||
dev->lun = 0;
|
||||
}
|
||||
do {
|
||||
d = scsi_device_find(bus, dev->channel, ++id, dev->lun);
|
||||
} while (d && d->lun == dev->lun && id <= bus->info->max_target);
|
||||
if (id > bus->info->max_target) {
|
||||
error_report("no free target");
|
||||
goto err;
|
||||
}
|
||||
dev->id = id;
|
||||
} else if (dev->lun == -1) {
|
||||
int lun = -1;
|
||||
do {
|
||||
d = scsi_device_find(bus, dev->channel, dev->id, ++lun);
|
||||
} while (d && d->lun == lun && lun < bus->info->max_lun);
|
||||
if (lun > bus->info->max_lun) {
|
||||
error_report("no free lun");
|
||||
goto err;
|
||||
}
|
||||
dev->lun = lun;
|
||||
} else {
|
||||
d = scsi_device_find(bus, dev->channel, dev->id, dev->lun);
|
||||
if (dev->lun == d->lun && dev != d) {
|
||||
qdev_free(&d->qdev);
|
||||
}
|
||||
}
|
||||
bus->devs[dev->id] = dev;
|
||||
|
||||
dev->info = info;
|
||||
QTAILQ_INIT(&dev->requests);
|
||||
rc = dev->info->init(dev);
|
||||
if (rc != 0) {
|
||||
bus->devs[dev->id] = NULL;
|
||||
if (rc == 0) {
|
||||
dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb,
|
||||
dev);
|
||||
}
|
||||
|
||||
err:
|
||||
@ -72,13 +143,13 @@ err:
|
||||
static int scsi_qdev_exit(DeviceState *qdev)
|
||||
{
|
||||
SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
|
||||
SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
|
||||
|
||||
assert(bus->devs[dev->id] != NULL);
|
||||
if (bus->devs[dev->id]->info->destroy) {
|
||||
bus->devs[dev->id]->info->destroy(bus->devs[dev->id]);
|
||||
if (dev->vmsentry) {
|
||||
qemu_del_vm_change_state_handler(dev->vmsentry);
|
||||
}
|
||||
if (dev->info->destroy) {
|
||||
dev->info->destroy(dev);
|
||||
}
|
||||
bus->devs[dev->id] = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -120,7 +191,7 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
|
||||
int res = 0, unit;
|
||||
|
||||
loc_push_none(&loc);
|
||||
for (unit = 0; unit < bus->ndev; unit++) {
|
||||
for (unit = 0; unit < bus->info->max_target; unit++) {
|
||||
dinfo = drive_get(IF_SCSI, bus->busnr, unit);
|
||||
if (dinfo == NULL) {
|
||||
continue;
|
||||
@ -144,7 +215,7 @@ static int32_t scsi_invalid_command(SCSIRequest *req, uint8_t *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct SCSIReqOps reqops_invalid_opcode = {
|
||||
static const struct SCSIReqOps reqops_invalid_opcode = {
|
||||
.size = sizeof(SCSIRequest),
|
||||
.send_command = scsi_invalid_command
|
||||
};
|
||||
@ -162,7 +233,7 @@ static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct SCSIReqOps reqops_unit_attention = {
|
||||
static const struct SCSIReqOps reqops_unit_attention = {
|
||||
.size = sizeof(SCSIRequest),
|
||||
.send_command = scsi_unit_attention
|
||||
};
|
||||
@ -175,7 +246,7 @@ typedef struct SCSITargetReq SCSITargetReq;
|
||||
struct SCSITargetReq {
|
||||
SCSIRequest req;
|
||||
int len;
|
||||
uint8_t buf[64];
|
||||
uint8_t buf[2056];
|
||||
};
|
||||
|
||||
static void store_lun(uint8_t *outbuf, int lun)
|
||||
@ -190,23 +261,53 @@ static void store_lun(uint8_t *outbuf, int lun)
|
||||
|
||||
static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
|
||||
{
|
||||
int len;
|
||||
DeviceState *qdev;
|
||||
int i, len, n;
|
||||
int channel, id;
|
||||
bool found_lun0;
|
||||
|
||||
if (r->req.cmd.xfer < 16) {
|
||||
return false;
|
||||
}
|
||||
if (r->req.cmd.buf[2] > 2) {
|
||||
return false;
|
||||
}
|
||||
len = MIN(sizeof r->buf, r->req.cmd.xfer);
|
||||
memset(r->buf, 0, len);
|
||||
if (r->req.dev->lun != 0) {
|
||||
r->buf[3] = 16;
|
||||
r->len = 24;
|
||||
store_lun(&r->buf[16], r->req.dev->lun);
|
||||
} else {
|
||||
r->buf[3] = 8;
|
||||
r->len = 16;
|
||||
channel = r->req.dev->channel;
|
||||
id = r->req.dev->id;
|
||||
found_lun0 = false;
|
||||
n = 0;
|
||||
QTAILQ_FOREACH(qdev, &r->req.bus->qbus.children, sibling) {
|
||||
SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
|
||||
|
||||
if (dev->channel == channel && dev->id == id) {
|
||||
if (dev->lun == 0) {
|
||||
found_lun0 = true;
|
||||
}
|
||||
n += 8;
|
||||
}
|
||||
}
|
||||
if (!found_lun0) {
|
||||
n += 8;
|
||||
}
|
||||
len = MIN(n + 8, r->req.cmd.xfer & ~7);
|
||||
if (len > sizeof(r->buf)) {
|
||||
/* TODO: > 256 LUNs? */
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(r->buf, 0, len);
|
||||
stl_be_p(&r->buf, n);
|
||||
i = found_lun0 ? 8 : 16;
|
||||
QTAILQ_FOREACH(qdev, &r->req.bus->qbus.children, sibling) {
|
||||
SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
|
||||
|
||||
if (dev->channel == channel && dev->id == id) {
|
||||
store_lun(&r->buf[i], dev->lun);
|
||||
i += 8;
|
||||
}
|
||||
}
|
||||
assert(i == n + 8);
|
||||
r->len = len;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -265,7 +366,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
|
||||
r->buf[2] = 5; /* Version */
|
||||
r->buf[3] = 2 | 0x10; /* HiSup, response data format */
|
||||
r->buf[4] = r->len - 5; /* Additional Length = (Len - 1) - 4 */
|
||||
r->buf[7] = 0x10 | (r->req.bus->tcq ? 0x02 : 0); /* Sync, TCQ. */
|
||||
r->buf[7] = 0x10 | (r->req.bus->info->tcq ? 0x02 : 0); /* Sync, TCQ. */
|
||||
memcpy(&r->buf[8], "QEMU ", 8);
|
||||
memcpy(&r->buf[16], "QEMU TARGET ", 16);
|
||||
strncpy((char *) &r->buf[32], QEMU_VERSION, 4);
|
||||
@ -295,6 +396,13 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf)
|
||||
r->len = scsi_device_get_sense(r->req.dev, r->buf,
|
||||
MIN(req->cmd.xfer, sizeof r->buf),
|
||||
(req->cmd.buf[1] & 1) == 0);
|
||||
if (r->req.dev->sense_is_ua) {
|
||||
if (r->req.dev->info->unit_attention_reported) {
|
||||
r->req.dev->info->unit_attention_reported(req->dev);
|
||||
}
|
||||
r->req.dev->sense_len = 0;
|
||||
r->req.dev->sense_is_ua = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED));
|
||||
@ -333,7 +441,7 @@ static uint8_t *scsi_target_get_buf(SCSIRequest *req)
|
||||
return r->buf;
|
||||
}
|
||||
|
||||
struct SCSIReqOps reqops_target_command = {
|
||||
static const struct SCSIReqOps reqops_target_command = {
|
||||
.size = sizeof(SCSITargetReq),
|
||||
.send_command = scsi_target_send_command,
|
||||
.read_data = scsi_target_read_data,
|
||||
@ -341,8 +449,8 @@ struct SCSIReqOps reqops_target_command = {
|
||||
};
|
||||
|
||||
|
||||
SCSIRequest *scsi_req_alloc(SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag,
|
||||
uint32_t lun, void *hba_private)
|
||||
SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
|
||||
uint32_t tag, uint32_t lun, void *hba_private)
|
||||
{
|
||||
SCSIRequest *req;
|
||||
|
||||
@ -383,7 +491,13 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
|
||||
(buf[0] != INQUIRY &&
|
||||
buf[0] != REPORT_LUNS &&
|
||||
buf[0] != GET_CONFIGURATION &&
|
||||
buf[0] != GET_EVENT_STATUS_NOTIFICATION)) {
|
||||
buf[0] != GET_EVENT_STATUS_NOTIFICATION &&
|
||||
|
||||
/*
|
||||
* If we already have a pending unit attention condition,
|
||||
* report this one before triggering another one.
|
||||
*/
|
||||
!(buf[0] == REQUEST_SENSE && d->sense_is_ua))) {
|
||||
req = scsi_req_alloc(&reqops_unit_attention, d, tag, lun,
|
||||
hba_private);
|
||||
} else if (lun != d->lun ||
|
||||
@ -392,7 +506,7 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
|
||||
req = scsi_req_alloc(&reqops_target_command, d, tag, lun,
|
||||
hba_private);
|
||||
} else {
|
||||
req = d->info->alloc_req(d, tag, lun, hba_private);
|
||||
req = d->info->alloc_req(d, tag, lun, buf, hba_private);
|
||||
}
|
||||
}
|
||||
|
||||
@ -479,10 +593,15 @@ int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len)
|
||||
*
|
||||
* We assume UA_INTLCK_CTRL to be 00b for HBAs that support autosense, and
|
||||
* 10b for HBAs that do not support it (do not call scsi_req_get_sense).
|
||||
* In the latter case, scsi_req_complete clears unit attention conditions
|
||||
* after moving them to the device's sense buffer.
|
||||
* Here we handle unit attention clearing for UA_INTLCK_CTRL == 00b.
|
||||
*/
|
||||
scsi_clear_unit_attention(req);
|
||||
if (req->dev->sense_is_ua) {
|
||||
if (req->dev->info->unit_attention_reported) {
|
||||
req->dev->info->unit_attention_reported(req->dev);
|
||||
}
|
||||
req->dev->sense_len = 0;
|
||||
req->dev->sense_is_ua = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -522,6 +641,7 @@ int32_t scsi_req_enqueue(SCSIRequest *req)
|
||||
static void scsi_req_dequeue(SCSIRequest *req)
|
||||
{
|
||||
trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
|
||||
req->retry = false;
|
||||
if (req->enqueued) {
|
||||
QTAILQ_REMOVE(&req->dev->requests, req, next);
|
||||
req->enqueued = false;
|
||||
@ -800,7 +920,7 @@ const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED = {
|
||||
};
|
||||
|
||||
/* Illegal request, Incompatible medium installed */
|
||||
const struct SCSISense sense_code_INCOMPATIBLE_MEDIUM = {
|
||||
const struct SCSISense sense_code_INCOMPATIBLE_FORMAT = {
|
||||
.key = ILLEGAL_REQUEST, .asc = 0x30, .ascq = 0x00
|
||||
};
|
||||
|
||||
@ -829,6 +949,11 @@ const struct SCSISense sense_code_RESET = {
|
||||
.key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00
|
||||
};
|
||||
|
||||
/* Unit attention, No medium */
|
||||
const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = {
|
||||
.key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00
|
||||
};
|
||||
|
||||
/* Unit attention, Medium may have changed */
|
||||
const struct SCSISense sense_code_MEDIUM_CHANGED = {
|
||||
.key = UNIT_ATTENTION, .asc = 0x28, .ascq = 0x00
|
||||
@ -1038,8 +1163,12 @@ void scsi_req_continue(SCSIRequest *req)
|
||||
Once it completes, calling scsi_req_continue will restart I/O. */
|
||||
void scsi_req_data(SCSIRequest *req, int len)
|
||||
{
|
||||
trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
|
||||
req->bus->ops->transfer_data(req, len);
|
||||
if (req->io_canceled) {
|
||||
trace_scsi_req_data_canceled(req->dev->id, req->lun, req->tag, len);
|
||||
} else {
|
||||
trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
|
||||
req->bus->info->transfer_data(req, len);
|
||||
}
|
||||
}
|
||||
|
||||
void scsi_req_print(SCSIRequest *req)
|
||||
@ -1082,8 +1211,12 @@ void scsi_req_complete(SCSIRequest *req, int status)
|
||||
|
||||
if (req->sense_len) {
|
||||
memcpy(req->dev->sense, req->sense, req->sense_len);
|
||||
req->dev->sense_len = req->sense_len;
|
||||
req->dev->sense_is_ua = (req->ops == &reqops_unit_attention);
|
||||
} else {
|
||||
req->dev->sense_len = 0;
|
||||
req->dev->sense_is_ua = false;
|
||||
}
|
||||
req->dev->sense_len = req->sense_len;
|
||||
|
||||
/*
|
||||
* Unit attention state is now stored in the device's sense buffer
|
||||
@ -1094,29 +1227,40 @@ void scsi_req_complete(SCSIRequest *req, int status)
|
||||
|
||||
scsi_req_ref(req);
|
||||
scsi_req_dequeue(req);
|
||||
req->bus->ops->complete(req, req->status);
|
||||
req->bus->info->complete(req, req->status);
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
|
||||
void scsi_req_cancel(SCSIRequest *req)
|
||||
{
|
||||
if (req->ops->cancel_io) {
|
||||
req->ops->cancel_io(req);
|
||||
if (!req->enqueued) {
|
||||
return;
|
||||
}
|
||||
scsi_req_ref(req);
|
||||
scsi_req_dequeue(req);
|
||||
if (req->bus->ops->cancel) {
|
||||
req->bus->ops->cancel(req);
|
||||
req->io_canceled = true;
|
||||
if (req->ops->cancel_io) {
|
||||
req->ops->cancel_io(req);
|
||||
}
|
||||
if (req->bus->info->cancel) {
|
||||
req->bus->info->cancel(req);
|
||||
}
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
|
||||
void scsi_req_abort(SCSIRequest *req, int status)
|
||||
{
|
||||
if (!req->enqueued) {
|
||||
return;
|
||||
}
|
||||
scsi_req_ref(req);
|
||||
scsi_req_dequeue(req);
|
||||
req->io_canceled = true;
|
||||
if (req->ops->cancel_io) {
|
||||
req->ops->cancel_io(req);
|
||||
}
|
||||
scsi_req_complete(req, status);
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
|
||||
void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
|
||||
@ -1133,19 +1277,28 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
|
||||
static char *scsibus_get_fw_dev_path(DeviceState *dev)
|
||||
{
|
||||
SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
|
||||
SCSIBus *bus = scsi_bus_from_device(d);
|
||||
char path[100];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bus->ndev; i++) {
|
||||
if (bus->devs[i] == d) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(i != bus->ndev);
|
||||
|
||||
snprintf(path, sizeof(path), "%s@%x", qdev_fw_name(dev), i);
|
||||
snprintf(path, sizeof(path), "%s@%d:%d:%d", qdev_fw_name(dev),
|
||||
d->channel, d->id, d->lun);
|
||||
|
||||
return strdup(path);
|
||||
}
|
||||
|
||||
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun)
|
||||
{
|
||||
DeviceState *qdev;
|
||||
SCSIDevice *target_dev = NULL;
|
||||
|
||||
QTAILQ_FOREACH_REVERSE(qdev, &bus->qbus.children, ChildrenHead, sibling) {
|
||||
SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
|
||||
|
||||
if (dev->channel == channel && dev->id == id) {
|
||||
if (dev->lun == lun) {
|
||||
return dev;
|
||||
}
|
||||
target_dev = dev;
|
||||
}
|
||||
}
|
||||
return target_dev;
|
||||
}
|
||||
|
@ -113,6 +113,7 @@
|
||||
#define READ_12 0xa8
|
||||
#define WRITE_12 0xaa
|
||||
#define SERVICE_ACTION_IN_12 0xab
|
||||
#define READ_DVD_STRUCTURE 0xad
|
||||
#define WRITE_VERIFY_12 0xae
|
||||
#define VERIFY_12 0xaf
|
||||
#define SEARCH_HIGH_12 0xb0
|
||||
@ -122,6 +123,8 @@
|
||||
#define SEND_VOLUME_TAG 0xb6
|
||||
#define READ_DEFECT_DATA_12 0xb7
|
||||
#define SET_CD_SPEED 0xbb
|
||||
#define MECHANISM_STATUS 0xbd
|
||||
#define READ_CD 0xbe
|
||||
|
||||
/*
|
||||
* SERVICE ACTION IN subcodes
|
||||
@ -188,3 +191,90 @@
|
||||
#define TYPE_INACTIVE 0x20
|
||||
#define TYPE_NO_LUN 0x7f
|
||||
|
||||
/* Mode page codes for mode sense/set */
|
||||
#define MODE_PAGE_R_W_ERROR 0x01
|
||||
#define MODE_PAGE_HD_GEOMETRY 0x04
|
||||
#define MODE_PAGE_FLEXIBLE_DISK_GEOMETRY 0x05
|
||||
#define MODE_PAGE_CACHING 0x08
|
||||
#define MODE_PAGE_AUDIO_CTL 0x0e
|
||||
#define MODE_PAGE_POWER 0x1a
|
||||
#define MODE_PAGE_FAULT_FAIL 0x1c
|
||||
#define MODE_PAGE_TO_PROTECT 0x1d
|
||||
#define MODE_PAGE_CAPABILITIES 0x2a
|
||||
#define MODE_PAGE_ALLS 0x3f
|
||||
/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor
|
||||
* of MODE_PAGE_SENSE_POWER */
|
||||
#define MODE_PAGE_CDROM 0x0d
|
||||
|
||||
/* Event notification classes for GET EVENT STATUS NOTIFICATION */
|
||||
#define GESN_NO_EVENTS 0
|
||||
#define GESN_OPERATIONAL_CHANGE 1
|
||||
#define GESN_POWER_MANAGEMENT 2
|
||||
#define GESN_EXTERNAL_REQUEST 3
|
||||
#define GESN_MEDIA 4
|
||||
#define GESN_MULTIPLE_HOSTS 5
|
||||
#define GESN_DEVICE_BUSY 6
|
||||
|
||||
/* Event codes for MEDIA event status notification */
|
||||
#define MEC_NO_CHANGE 0
|
||||
#define MEC_EJECT_REQUESTED 1
|
||||
#define MEC_NEW_MEDIA 2
|
||||
#define MEC_MEDIA_REMOVAL 3 /* only for media changers */
|
||||
#define MEC_MEDIA_CHANGED 4 /* only for media changers */
|
||||
#define MEC_BG_FORMAT_COMPLETED 5 /* MRW or DVD+RW b/g format completed */
|
||||
#define MEC_BG_FORMAT_RESTARTED 6 /* MRW or DVD+RW b/g format restarted */
|
||||
|
||||
#define MS_TRAY_OPEN 1
|
||||
#define MS_MEDIA_PRESENT 2
|
||||
|
||||
/*
|
||||
* Based on values from <linux/cdrom.h> but extending CD_MINS
|
||||
* to the maximum common size allowed by the Orange's Book ATIP
|
||||
*
|
||||
* 90 and 99 min CDs are also available but using them as the
|
||||
* upper limit reduces the effectiveness of the heuristic to
|
||||
* detect DVDs burned to less than 25% of their maximum capacity
|
||||
*/
|
||||
|
||||
/* Some generally useful CD-ROM information */
|
||||
#define CD_MINS 80 /* max. minutes per CD */
|
||||
#define CD_SECS 60 /* seconds per minute */
|
||||
#define CD_FRAMES 75 /* frames per second */
|
||||
#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */
|
||||
#define CD_MAX_BYTES (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE)
|
||||
#define CD_MAX_SECTORS (CD_MAX_BYTES / 512)
|
||||
|
||||
/*
|
||||
* The MMC values are not IDE specific and might need to be moved
|
||||
* to a common header if they are also needed for the SCSI emulation
|
||||
*/
|
||||
|
||||
/* Profile list from MMC-6 revision 1 table 91 */
|
||||
#define MMC_PROFILE_NONE 0x0000
|
||||
#define MMC_PROFILE_CD_ROM 0x0008
|
||||
#define MMC_PROFILE_CD_R 0x0009
|
||||
#define MMC_PROFILE_CD_RW 0x000A
|
||||
#define MMC_PROFILE_DVD_ROM 0x0010
|
||||
#define MMC_PROFILE_DVD_R_SR 0x0011
|
||||
#define MMC_PROFILE_DVD_RAM 0x0012
|
||||
#define MMC_PROFILE_DVD_RW_RO 0x0013
|
||||
#define MMC_PROFILE_DVD_RW_SR 0x0014
|
||||
#define MMC_PROFILE_DVD_R_DL_SR 0x0015
|
||||
#define MMC_PROFILE_DVD_R_DL_JR 0x0016
|
||||
#define MMC_PROFILE_DVD_RW_DL 0x0017
|
||||
#define MMC_PROFILE_DVD_DDR 0x0018
|
||||
#define MMC_PROFILE_DVD_PLUS_RW 0x001A
|
||||
#define MMC_PROFILE_DVD_PLUS_R 0x001B
|
||||
#define MMC_PROFILE_DVD_PLUS_RW_DL 0x002A
|
||||
#define MMC_PROFILE_DVD_PLUS_R_DL 0x002B
|
||||
#define MMC_PROFILE_BD_ROM 0x0040
|
||||
#define MMC_PROFILE_BD_R_SRM 0x0041
|
||||
#define MMC_PROFILE_BD_R_RRM 0x0042
|
||||
#define MMC_PROFILE_BD_RE 0x0043
|
||||
#define MMC_PROFILE_HDDVD_ROM 0x0050
|
||||
#define MMC_PROFILE_HDDVD_R 0x0051
|
||||
#define MMC_PROFILE_HDDVD_RAM 0x0052
|
||||
#define MMC_PROFILE_HDDVD_RW 0x0053
|
||||
#define MMC_PROFILE_HDDVD_R_DL 0x0058
|
||||
#define MMC_PROFILE_HDDVD_RW_DL 0x005A
|
||||
#define MMC_PROFILE_INVALID 0xFFFF
|
||||
|
824
hw/scsi-disk.c
824
hw/scsi-disk.c
File diff suppressed because it is too large
Load Diff
@ -39,15 +39,18 @@ do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
|
||||
|
||||
#define SCSI_SENSE_BUF_SIZE 96
|
||||
|
||||
#define SG_ERR_DRIVER_TIMEOUT 0x06
|
||||
#define SG_ERR_DRIVER_SENSE 0x08
|
||||
#define SG_ERR_DRIVER_TIMEOUT 0x06
|
||||
#define SG_ERR_DRIVER_SENSE 0x08
|
||||
|
||||
#define SG_ERR_DID_OK 0x00
|
||||
#define SG_ERR_DID_NO_CONNECT 0x01
|
||||
#define SG_ERR_DID_BUS_BUSY 0x02
|
||||
#define SG_ERR_DID_TIME_OUT 0x03
|
||||
|
||||
#ifndef MAX_UINT
|
||||
#define MAX_UINT ((unsigned int)-1)
|
||||
#endif
|
||||
|
||||
typedef struct SCSIGenericState SCSIGenericState;
|
||||
|
||||
typedef struct SCSIGenericReq {
|
||||
SCSIRequest req;
|
||||
uint8_t *buf;
|
||||
@ -56,12 +59,6 @@ typedef struct SCSIGenericReq {
|
||||
sg_io_hdr_t io_header;
|
||||
} SCSIGenericReq;
|
||||
|
||||
struct SCSIGenericState
|
||||
{
|
||||
SCSIDevice qdev;
|
||||
BlockDriverState *bs;
|
||||
};
|
||||
|
||||
static void scsi_free_request(SCSIRequest *req)
|
||||
{
|
||||
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
|
||||
@ -76,8 +73,9 @@ static void scsi_command_complete(void *opaque, int ret)
|
||||
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
|
||||
|
||||
r->req.aiocb = NULL;
|
||||
if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE)
|
||||
if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
|
||||
r->req.sense_len = r->io_header.sb_len_wr;
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
switch (ret) {
|
||||
@ -94,9 +92,15 @@ static void scsi_command_complete(void *opaque, int ret)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT) {
|
||||
if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT ||
|
||||
r->io_header.host_status == SG_ERR_DID_BUS_BUSY ||
|
||||
r->io_header.host_status == SG_ERR_DID_TIME_OUT ||
|
||||
(r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) {
|
||||
status = BUSY;
|
||||
BADF("Driver Timeout\n");
|
||||
} else if (r->io_header.host_status) {
|
||||
status = CHECK_CONDITION;
|
||||
scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS));
|
||||
} else if (r->io_header.status) {
|
||||
status = r->io_header.status;
|
||||
} else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
|
||||
@ -109,6 +113,9 @@ static void scsi_command_complete(void *opaque, int ret)
|
||||
r, r->req.tag, status);
|
||||
|
||||
scsi_req_complete(&r->req, status);
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cancel a pending data transfer. */
|
||||
@ -119,6 +126,11 @@ static void scsi_cancel_io(SCSIRequest *req)
|
||||
DPRINTF("Cancel tag=0x%x\n", req->tag);
|
||||
if (r->req.aiocb) {
|
||||
bdrv_aio_cancel(r->req.aiocb);
|
||||
|
||||
/* This reference was left in by scsi_*_data. We take ownership of
|
||||
* it independent of whether bdrv_aio_cancel completes the request
|
||||
* or not. */
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
r->req.aiocb = NULL;
|
||||
}
|
||||
@ -151,6 +163,7 @@ static int execute_command(BlockDriverState *bdrv,
|
||||
static void scsi_read_complete(void * opaque, int ret)
|
||||
{
|
||||
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
|
||||
SCSIDevice *s = r->req.dev;
|
||||
int len;
|
||||
|
||||
r->req.aiocb = NULL;
|
||||
@ -166,7 +179,21 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||
if (len == 0) {
|
||||
scsi_command_complete(r, 0);
|
||||
} else {
|
||||
/* Snoop READ CAPACITY output to set the blocksize. */
|
||||
if (r->req.cmd.buf[0] == READ_CAPACITY_10) {
|
||||
s->blocksize = ldl_be_p(&r->buf[4]);
|
||||
s->max_lba = ldl_be_p(&r->buf[0]);
|
||||
} else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
|
||||
(r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
|
||||
s->blocksize = ldl_be_p(&r->buf[8]);
|
||||
s->max_lba = ldq_be_p(&r->buf[0]);
|
||||
}
|
||||
bdrv_set_buffer_alignment(s->conf.bs, s->blocksize);
|
||||
|
||||
scsi_req_data(&r->req, len);
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,26 +201,28 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||
static void scsi_read_data(SCSIRequest *req)
|
||||
{
|
||||
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
|
||||
SCSIDevice *s = r->req.dev;
|
||||
int ret;
|
||||
|
||||
DPRINTF("scsi_read_data 0x%x\n", req->tag);
|
||||
|
||||
/* The request is used as the AIO opaque value, so add a ref. */
|
||||
scsi_req_ref(&r->req);
|
||||
if (r->len == -1) {
|
||||
scsi_command_complete(r, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
|
||||
ret = execute_command(s->conf.bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
|
||||
if (ret < 0) {
|
||||
scsi_command_complete(r, ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void scsi_write_complete(void * opaque, int ret)
|
||||
{
|
||||
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
|
||||
SCSIDevice *s = r->req.dev;
|
||||
|
||||
DPRINTF("scsi_write_complete() ret = %d\n", ret);
|
||||
r->req.aiocb = NULL;
|
||||
@ -204,9 +233,9 @@ static void scsi_write_complete(void * opaque, int ret)
|
||||
}
|
||||
|
||||
if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
|
||||
s->qdev.type == TYPE_TAPE) {
|
||||
s->qdev.blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
|
||||
DPRINTF("block size %d\n", s->qdev.blocksize);
|
||||
s->type == TYPE_TAPE) {
|
||||
s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
|
||||
DPRINTF("block size %d\n", s->blocksize);
|
||||
}
|
||||
|
||||
scsi_command_complete(r, ret);
|
||||
@ -216,8 +245,8 @@ static void scsi_write_complete(void * opaque, int ret)
|
||||
The transfer may complete asynchronously. */
|
||||
static void scsi_write_data(SCSIRequest *req)
|
||||
{
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
|
||||
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
|
||||
SCSIDevice *s = r->req.dev;
|
||||
int ret;
|
||||
|
||||
DPRINTF("scsi_write_data 0x%x\n", req->tag);
|
||||
@ -227,7 +256,9 @@ static void scsi_write_data(SCSIRequest *req)
|
||||
return;
|
||||
}
|
||||
|
||||
ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
|
||||
/* The request is used as the AIO opaque value, so add a ref. */
|
||||
scsi_req_ref(&r->req);
|
||||
ret = execute_command(s->conf.bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
|
||||
if (ret < 0) {
|
||||
scsi_command_complete(r, ret);
|
||||
}
|
||||
@ -241,19 +272,6 @@ static uint8_t *scsi_get_buf(SCSIRequest *req)
|
||||
return r->buf;
|
||||
}
|
||||
|
||||
static void scsi_req_fixup(SCSIRequest *req)
|
||||
{
|
||||
switch(req->cmd.buf[0]) {
|
||||
case REWIND:
|
||||
case START_STOP:
|
||||
if (req->dev->type == TYPE_TAPE) {
|
||||
/* force IMMED, otherwise qemu waits end of command */
|
||||
req->cmd.buf[1] = 0x01;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute a scsi command. Returns the length of the data expected by the
|
||||
command. This will be Positive for data transfers from the device
|
||||
(eg. disk reads), negative for transfers to the device (eg. disk writes),
|
||||
@ -261,12 +279,10 @@ static void scsi_req_fixup(SCSIRequest *req)
|
||||
|
||||
static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
|
||||
{
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
|
||||
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
|
||||
SCSIDevice *s = r->req.dev;
|
||||
int ret;
|
||||
|
||||
scsi_req_fixup(&r->req);
|
||||
|
||||
DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag,
|
||||
r->req.cmd.xfer, cmd[0]);
|
||||
|
||||
@ -285,7 +301,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
|
||||
g_free(r->buf);
|
||||
r->buflen = 0;
|
||||
r->buf = NULL;
|
||||
ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete);
|
||||
/* The request is used as the AIO opaque value, so add a ref. */
|
||||
scsi_req_ref(&r->req);
|
||||
ret = execute_command(s->conf.bs, r, SG_DXFER_NONE, scsi_command_complete);
|
||||
if (ret < 0) {
|
||||
scsi_command_complete(r, ret);
|
||||
return 0;
|
||||
@ -310,36 +328,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
|
||||
}
|
||||
}
|
||||
|
||||
static int get_blocksize(BlockDriverState *bdrv)
|
||||
{
|
||||
uint8_t cmd[10];
|
||||
uint8_t buf[8];
|
||||
uint8_t sensebuf[8];
|
||||
sg_io_hdr_t io_header;
|
||||
int ret;
|
||||
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
memset(buf, 0, sizeof(buf));
|
||||
cmd[0] = READ_CAPACITY_10;
|
||||
|
||||
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 = bdrv_ioctl(bdrv, SG_IO, &io_header);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
|
||||
}
|
||||
|
||||
static int get_stream_blocksize(BlockDriverState *bdrv)
|
||||
{
|
||||
uint8_t cmd[6];
|
||||
@ -365,89 +353,92 @@ static int get_stream_blocksize(BlockDriverState *bdrv)
|
||||
io_header.timeout = 6000; /* XXX */
|
||||
|
||||
ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
|
||||
if (ret < 0)
|
||||
if (ret < 0 || io_header.driver_status || io_header.host_status) {
|
||||
return -1;
|
||||
|
||||
}
|
||||
return (buf[9] << 16) | (buf[10] << 8) | buf[11];
|
||||
}
|
||||
|
||||
static void scsi_generic_reset(DeviceState *dev)
|
||||
{
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev);
|
||||
SCSIDevice *s = DO_UPCAST(SCSIDevice, qdev, dev);
|
||||
|
||||
scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET));
|
||||
scsi_device_purge_requests(s, SENSE_CODE(RESET));
|
||||
}
|
||||
|
||||
static void scsi_destroy(SCSIDevice *d)
|
||||
static void scsi_destroy(SCSIDevice *s)
|
||||
{
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
|
||||
|
||||
scsi_device_purge_requests(&s->qdev, SENSE_CODE(NO_SENSE));
|
||||
blockdev_mark_auto_del(s->qdev.conf.bs);
|
||||
scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE));
|
||||
blockdev_mark_auto_del(s->conf.bs);
|
||||
}
|
||||
|
||||
static int scsi_generic_initfn(SCSIDevice *dev)
|
||||
static int scsi_generic_initfn(SCSIDevice *s)
|
||||
{
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, dev);
|
||||
int sg_version;
|
||||
struct sg_scsi_id scsiid;
|
||||
|
||||
if (!s->qdev.conf.bs) {
|
||||
if (!s->conf.bs) {
|
||||
error_report("scsi-generic: drive property not set");
|
||||
return -1;
|
||||
}
|
||||
s->bs = s->qdev.conf.bs;
|
||||
|
||||
/* check we are really using a /dev/sg* file */
|
||||
if (!bdrv_is_sg(s->bs)) {
|
||||
if (!bdrv_is_sg(s->conf.bs)) {
|
||||
error_report("scsi-generic: not /dev/sg*");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bdrv_get_on_error(s->bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
|
||||
if (bdrv_get_on_error(s->conf.bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
|
||||
error_report("Device doesn't support drive option werror");
|
||||
return -1;
|
||||
}
|
||||
if (bdrv_get_on_error(s->bs, 1) != BLOCK_ERR_REPORT) {
|
||||
if (bdrv_get_on_error(s->conf.bs, 1) != BLOCK_ERR_REPORT) {
|
||||
error_report("Device doesn't support drive option rerror");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check we are using a driver managing SG_IO (version 3 and after */
|
||||
if (bdrv_ioctl(s->bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
|
||||
if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
|
||||
sg_version < 30000) {
|
||||
error_report("scsi-generic: scsi generic interface too old");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get LUN of the /dev/sg? */
|
||||
if (bdrv_ioctl(s->bs, SG_GET_SCSI_ID, &scsiid)) {
|
||||
if (bdrv_ioctl(s->conf.bs, SG_GET_SCSI_ID, &scsiid)) {
|
||||
error_report("scsi-generic: SG_GET_SCSI_ID ioctl failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* define device state */
|
||||
s->qdev.type = scsiid.scsi_type;
|
||||
DPRINTF("device type %d\n", s->qdev.type);
|
||||
if (s->qdev.type == TYPE_TAPE) {
|
||||
s->qdev.blocksize = get_stream_blocksize(s->bs);
|
||||
if (s->qdev.blocksize == -1)
|
||||
s->qdev.blocksize = 0;
|
||||
} else {
|
||||
s->qdev.blocksize = get_blocksize(s->bs);
|
||||
/* removable media returns 0 if not present */
|
||||
if (s->qdev.blocksize <= 0) {
|
||||
if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM)
|
||||
s->qdev.blocksize = 2048;
|
||||
else
|
||||
s->qdev.blocksize = 512;
|
||||
s->type = scsiid.scsi_type;
|
||||
DPRINTF("device type %d\n", s->type);
|
||||
switch (s->type) {
|
||||
case TYPE_TAPE:
|
||||
s->blocksize = get_stream_blocksize(s->conf.bs);
|
||||
if (s->blocksize == -1) {
|
||||
s->blocksize = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
/* Make a guess for block devices, we'll fix it when the guest sends.
|
||||
* READ CAPACITY. If they don't, they likely would assume these sizes
|
||||
* anyway. (TODO: they could also send MODE SENSE).
|
||||
*/
|
||||
case TYPE_ROM:
|
||||
case TYPE_WORM:
|
||||
s->blocksize = 2048;
|
||||
break;
|
||||
default:
|
||||
s->blocksize = 512;
|
||||
break;
|
||||
}
|
||||
DPRINTF("block size %d\n", s->qdev.blocksize);
|
||||
|
||||
DPRINTF("block size %d\n", s->blocksize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SCSIReqOps scsi_generic_req_ops = {
|
||||
const SCSIReqOps scsi_generic_req_ops = {
|
||||
.size = sizeof(SCSIGenericReq),
|
||||
.free_req = scsi_free_request,
|
||||
.send_command = scsi_send_command,
|
||||
@ -458,7 +449,7 @@ static SCSIReqOps scsi_generic_req_ops = {
|
||||
};
|
||||
|
||||
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
|
||||
void *hba_private)
|
||||
uint8_t *buf, void *hba_private)
|
||||
{
|
||||
SCSIRequest *req;
|
||||
|
||||
@ -469,13 +460,13 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
|
||||
static SCSIDeviceInfo scsi_generic_info = {
|
||||
.qdev.name = "scsi-generic",
|
||||
.qdev.desc = "pass through generic scsi device (/dev/sg*)",
|
||||
.qdev.size = sizeof(SCSIGenericState),
|
||||
.qdev.size = sizeof(SCSIDevice),
|
||||
.qdev.reset = scsi_generic_reset,
|
||||
.init = scsi_generic_initfn,
|
||||
.destroy = scsi_destroy,
|
||||
.alloc_req = scsi_new_request,
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf),
|
||||
DEFINE_BLOCK_PROPERTIES(SCSIDevice, conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
},
|
||||
};
|
||||
|
39
hw/scsi.h
39
hw/scsi.h
@ -3,13 +3,14 @@
|
||||
|
||||
#include "qdev.h"
|
||||
#include "block.h"
|
||||
#include "sysemu.h"
|
||||
|
||||
#define MAX_SCSI_DEVS 255
|
||||
|
||||
#define SCSI_CMD_BUF_SIZE 16
|
||||
|
||||
typedef struct SCSIBus SCSIBus;
|
||||
typedef struct SCSIBusOps SCSIBusOps;
|
||||
typedef struct SCSIBusInfo SCSIBusInfo;
|
||||
typedef struct SCSICommand SCSICommand;
|
||||
typedef struct SCSIDevice SCSIDevice;
|
||||
typedef struct SCSIDeviceInfo SCSIDeviceInfo;
|
||||
@ -41,7 +42,7 @@ struct SCSICommand {
|
||||
struct SCSIRequest {
|
||||
SCSIBus *bus;
|
||||
SCSIDevice *dev;
|
||||
SCSIReqOps *ops;
|
||||
const SCSIReqOps *ops;
|
||||
uint32_t refcount;
|
||||
uint32_t tag;
|
||||
uint32_t lun;
|
||||
@ -51,6 +52,8 @@ struct SCSIRequest {
|
||||
uint8_t sense[SCSI_SENSE_BUF_SIZE];
|
||||
uint32_t sense_len;
|
||||
bool enqueued;
|
||||
bool io_canceled;
|
||||
bool retry;
|
||||
void *hba_private;
|
||||
QTAILQ_ENTRY(SCSIRequest) next;
|
||||
};
|
||||
@ -58,16 +61,21 @@ struct SCSIRequest {
|
||||
struct SCSIDevice
|
||||
{
|
||||
DeviceState qdev;
|
||||
VMChangeStateEntry *vmsentry;
|
||||
QEMUBH *bh;
|
||||
uint32_t id;
|
||||
BlockConf conf;
|
||||
SCSIDeviceInfo *info;
|
||||
SCSISense unit_attention;
|
||||
bool sense_is_ua;
|
||||
uint8_t sense[SCSI_SENSE_BUF_SIZE];
|
||||
uint32_t sense_len;
|
||||
QTAILQ_HEAD(, SCSIRequest) requests;
|
||||
uint32_t channel;
|
||||
uint32_t lun;
|
||||
int blocksize;
|
||||
int type;
|
||||
uint64_t max_lba;
|
||||
};
|
||||
|
||||
/* cdrom.c */
|
||||
@ -91,11 +99,13 @@ struct SCSIDeviceInfo {
|
||||
scsi_qdev_initfn init;
|
||||
void (*destroy)(SCSIDevice *s);
|
||||
SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun,
|
||||
void *hba_private);
|
||||
SCSIReqOps reqops;
|
||||
uint8_t *buf, void *hba_private);
|
||||
void (*unit_attention_reported)(SCSIDevice *s);
|
||||
};
|
||||
|
||||
struct SCSIBusOps {
|
||||
struct SCSIBusInfo {
|
||||
int tcq;
|
||||
int max_channel, max_target, max_lun;
|
||||
void (*transfer_data)(SCSIRequest *req, uint32_t arg);
|
||||
void (*complete)(SCSIRequest *req, uint32_t arg);
|
||||
void (*cancel)(SCSIRequest *req);
|
||||
@ -106,14 +116,10 @@ struct SCSIBus {
|
||||
int busnr;
|
||||
|
||||
SCSISense unit_attention;
|
||||
int tcq, ndev;
|
||||
const SCSIBusOps *ops;
|
||||
|
||||
SCSIDevice *devs[MAX_SCSI_DEVS];
|
||||
const SCSIBusInfo *info;
|
||||
};
|
||||
|
||||
void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
|
||||
const SCSIBusOps *ops);
|
||||
void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info);
|
||||
void scsi_qdev_register(SCSIDeviceInfo *info);
|
||||
|
||||
static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d)
|
||||
@ -159,6 +165,8 @@ extern const struct SCSISense sense_code_IO_ERROR;
|
||||
extern const struct SCSISense sense_code_I_T_NEXUS_LOSS;
|
||||
/* Command aborted, Logical Unit failure */
|
||||
extern const struct SCSISense sense_code_LUN_FAILURE;
|
||||
/* LUN not ready, Medium not present */
|
||||
extern const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM;
|
||||
/* Unit attention, Power on, reset or bus device reset occurred */
|
||||
extern const struct SCSISense sense_code_RESET;
|
||||
/* Unit attention, Medium may have changed*/
|
||||
@ -172,8 +180,8 @@ extern const struct SCSISense sense_code_DEVICE_INTERNAL_RESET;
|
||||
|
||||
int scsi_sense_valid(SCSISense sense);
|
||||
|
||||
SCSIRequest *scsi_req_alloc(SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag,
|
||||
uint32_t lun, void *hba_private);
|
||||
SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
|
||||
uint32_t tag, uint32_t lun, void *hba_private);
|
||||
SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
|
||||
uint8_t *buf, void *hba_private);
|
||||
int32_t scsi_req_enqueue(SCSIRequest *req);
|
||||
@ -190,7 +198,12 @@ uint8_t *scsi_req_get_buf(SCSIRequest *req);
|
||||
int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len);
|
||||
void scsi_req_abort(SCSIRequest *req, int status);
|
||||
void scsi_req_cancel(SCSIRequest *req);
|
||||
void scsi_req_retry(SCSIRequest *req);
|
||||
void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense);
|
||||
int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
|
||||
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);
|
||||
|
||||
/* scsi-generic.c. */
|
||||
extern const SCSIReqOps scsi_generic_req_ops;
|
||||
|
||||
#endif
|
||||
|
@ -63,7 +63,7 @@ VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg)
|
||||
DeviceState *qdev;
|
||||
VIOsPAPRDevice *dev = NULL;
|
||||
|
||||
QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
|
||||
QTAILQ_FOREACH(qdev, &bus->bus.children, sibling) {
|
||||
dev = (VIOsPAPRDevice *)qdev;
|
||||
if (dev->reg == reg) {
|
||||
break;
|
||||
@ -588,7 +588,7 @@ static void rtas_quiesce(sPAPREnvironment *spapr, uint32_t token,
|
||||
return;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
|
||||
QTAILQ_FOREACH(qdev, &bus->bus.children, sibling) {
|
||||
dev = (VIOsPAPRDevice *)qdev;
|
||||
spapr_vio_quiesce_one(dev);
|
||||
}
|
||||
@ -726,7 +726,7 @@ int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt)
|
||||
DeviceState *qdev;
|
||||
int ret = 0;
|
||||
|
||||
QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
|
||||
QTAILQ_FOREACH(qdev, &bus->bus.children, sibling) {
|
||||
VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
|
||||
|
||||
ret = vio_make_devnode(dev, fdt);
|
||||
|
@ -129,11 +129,38 @@ static void vscsi_put_req(vscsi_req *req)
|
||||
req->active = 0;
|
||||
}
|
||||
|
||||
static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun)
|
||||
static SCSIDevice *vscsi_device_find(SCSIBus *bus, uint64_t srp_lun, int *lun)
|
||||
{
|
||||
/* XXX Figure that one out properly ! This is crackpot */
|
||||
*id = (srp_lun >> 56) & 0x7f;
|
||||
*lun = (srp_lun >> 48) & 0xff;
|
||||
int channel = 0, id = 0;
|
||||
|
||||
retry:
|
||||
switch (srp_lun >> 62) {
|
||||
case 0:
|
||||
if ((srp_lun >> 56) != 0) {
|
||||
channel = (srp_lun >> 56) & 0x3f;
|
||||
id = (srp_lun >> 48) & 0xff;
|
||||
srp_lun <<= 16;
|
||||
goto retry;
|
||||
}
|
||||
*lun = (srp_lun >> 48) & 0xff;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*lun = (srp_lun >> 48) & 0x3fff;
|
||||
break;
|
||||
case 2:
|
||||
channel = (srp_lun >> 53) & 0x7;
|
||||
id = (srp_lun >> 56) & 0x3f;
|
||||
*lun = (srp_lun >> 48) & 0x1f;
|
||||
break;
|
||||
case 3:
|
||||
*lun = -1;
|
||||
return NULL;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
return scsi_device_find(bus, channel, id, *lun);
|
||||
}
|
||||
|
||||
static int vscsi_send_iu(VSCSIState *s, vscsi_req *req,
|
||||
@ -582,14 +609,11 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
|
||||
{
|
||||
union srp_iu *srp = &req->iu.srp;
|
||||
SCSIDevice *sdev;
|
||||
int n, id, lun;
|
||||
int n, lun;
|
||||
|
||||
vscsi_decode_id_lun(be64_to_cpu(srp->cmd.lun), &id, &lun);
|
||||
|
||||
/* Qemu vs. linux issue with LUNs to be sorted out ... */
|
||||
sdev = (id < 8 && lun < 16) ? s->bus.devs[id] : NULL;
|
||||
sdev = vscsi_device_find(&s->bus, be64_to_cpu(srp->cmd.lun), &lun);
|
||||
if (!sdev) {
|
||||
dprintf("VSCSI: Command for id %d with no drive\n", id);
|
||||
dprintf("VSCSI: Command for lun %08" PRIx64 " with no drive\n", be64_to_cpu(srp->cmd.lun));
|
||||
if (srp->cmd.cdb[0] == INQUIRY) {
|
||||
vscsi_inquiry_no_target(s, req);
|
||||
} else {
|
||||
@ -862,7 +886,12 @@ static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct SCSIBusOps vscsi_scsi_ops = {
|
||||
static const struct SCSIBusInfo vscsi_scsi_info = {
|
||||
.tcq = true,
|
||||
.max_channel = 7, /* logical unit addressing format */
|
||||
.max_target = 63,
|
||||
.max_lun = 31,
|
||||
|
||||
.transfer_data = vscsi_transfer_data,
|
||||
.complete = vscsi_command_complete,
|
||||
.cancel = vscsi_request_cancelled
|
||||
@ -883,8 +912,7 @@ static int spapr_vscsi_init(VIOsPAPRDevice *dev)
|
||||
|
||||
dev->crq.SendFunc = vscsi_do_crq;
|
||||
|
||||
scsi_bus_new(&s->bus, &dev->qdev, 1, VSCSI_REQ_LIMIT,
|
||||
&vscsi_scsi_ops);
|
||||
scsi_bus_new(&s->bus, &dev->qdev, &vscsi_scsi_info);
|
||||
if (!dev->qdev.hotplugged) {
|
||||
scsi_bus_legacy_handle_cmdline(&s->bus);
|
||||
}
|
||||
|
6
hw/ssi.c
6
hw/ssi.c
@ -25,8 +25,8 @@ static int ssi_slave_init(DeviceState *dev, DeviceInfo *base_info)
|
||||
SSIBus *bus;
|
||||
|
||||
bus = FROM_QBUS(SSIBus, qdev_get_parent_bus(dev));
|
||||
if (QLIST_FIRST(&bus->qbus.children) != dev
|
||||
|| QLIST_NEXT(dev, sibling) != NULL) {
|
||||
if (QTAILQ_FIRST(&bus->qbus.children) != dev
|
||||
|| QTAILQ_NEXT(dev, sibling) != NULL) {
|
||||
hw_error("Too many devices on SSI bus");
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ uint32_t ssi_transfer(SSIBus *bus, uint32_t val)
|
||||
{
|
||||
DeviceState *dev;
|
||||
SSISlave *slave;
|
||||
dev = QLIST_FIRST(&bus->qbus.children);
|
||||
dev = QTAILQ_FIRST(&bus->qbus.children);
|
||||
if (!dev) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -495,7 +495,11 @@ static void usb_msd_password_cb(void *opaque, int err)
|
||||
qdev_unplug(&s->dev.qdev);
|
||||
}
|
||||
|
||||
static const struct SCSIBusOps usb_msd_scsi_ops = {
|
||||
static const struct SCSIBusInfo usb_msd_scsi_info = {
|
||||
.tcq = false,
|
||||
.max_target = 0,
|
||||
.max_lun = 0,
|
||||
|
||||
.transfer_data = usb_msd_transfer_data,
|
||||
.complete = usb_msd_command_complete,
|
||||
.cancel = usb_msd_request_cancelled
|
||||
@ -536,7 +540,7 @@ static int usb_msd_initfn(USBDevice *dev)
|
||||
}
|
||||
|
||||
usb_desc_init(dev);
|
||||
scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, &usb_msd_scsi_ops);
|
||||
scsi_bus_new(&s->bus, &s->dev.qdev, &usb_msd_scsi_info);
|
||||
s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable);
|
||||
if (!s->scsi_dev) {
|
||||
return -1;
|
||||
|
@ -1582,7 +1582,7 @@ static const cmdinfo_t map_cmd = {
|
||||
|
||||
static int close_f(int argc, char **argv)
|
||||
{
|
||||
bdrv_close(bs);
|
||||
bdrv_delete(bs);
|
||||
bs = NULL;
|
||||
return 0;
|
||||
}
|
||||
@ -1611,6 +1611,7 @@ static int openfile(char *name, int flags, int growable)
|
||||
|
||||
if (bdrv_open(bs, name, flags, NULL) < 0) {
|
||||
fprintf(stderr, "%s: can't open device %s\n", progname, name);
|
||||
bdrv_delete(bs);
|
||||
bs = NULL;
|
||||
return 1;
|
||||
}
|
||||
@ -1834,7 +1835,7 @@ int main(int argc, char **argv)
|
||||
qemu_aio_flush();
|
||||
|
||||
if (bs) {
|
||||
bdrv_close(bs);
|
||||
bdrv_delete(bs);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -148,6 +148,9 @@ Define a new drive. Valid options are:
|
||||
This option defines which disk image (@pxref{disk_images}) to use with
|
||||
this drive. If the filename contains comma, you must double it
|
||||
(for instance, "file=my,,file" to use file "my,file").
|
||||
|
||||
Special files such as iSCSI devices can be specified using protocol
|
||||
specific URLs. See the section for "Device URL Syntax" for more information.
|
||||
@item if=@var{interface}
|
||||
This option defines on which type on interface the drive is connected.
|
||||
Available types are: ide, scsi, sd, mtd, floppy, pflash, virtio.
|
||||
@ -1733,6 +1736,93 @@ ETEXI
|
||||
|
||||
DEFHEADING()
|
||||
|
||||
STEXI
|
||||
DEFHEADING(Device URL Syntax:)
|
||||
|
||||
In addition to using normal file images for the emulated storage devices,
|
||||
QEMU can also use networked resources such as iSCSI devices. These are
|
||||
specified using a special URL syntax.
|
||||
|
||||
@table @option
|
||||
@item iSCSI
|
||||
iSCSI support allows QEMU to access iSCSI resources directly and use as
|
||||
images for the guest storage. Both disk and cdrom images are supported.
|
||||
|
||||
Syntax for specifying iSCSI LUNs is
|
||||
``iscsi://<target-ip>[:<port>]/<target-iqn>/<lun>''
|
||||
|
||||
Example (without authentication):
|
||||
@example
|
||||
qemu -cdrom iscsi://192.0.2.1/iqn.2001-04.com.example/2 \
|
||||
--drive file=iscsi://192.0.2.1/iqn.2001-04.com.example/1
|
||||
@end example
|
||||
|
||||
Example (CHAP username/password via URL):
|
||||
@example
|
||||
qemu --drive file=iscsi://user%password@@192.0.2.1/iqn.2001-04.com.example/1
|
||||
@end example
|
||||
|
||||
Example (CHAP username/password via environment variables):
|
||||
@example
|
||||
LIBISCSI_CHAP_USERNAME="user" \
|
||||
LIBISCSI_CHAP_PASSWORD="password" \
|
||||
qemu --drive file=iscsi://192.0.2.1/iqn.2001-04.com.example/1
|
||||
@end example
|
||||
|
||||
iSCSI support is an optional feature of QEMU and only available when
|
||||
compiled and linked against libiscsi.
|
||||
|
||||
@item NBD
|
||||
QEMU supports NBD (Network Block Devices) both using TCP protocol as well
|
||||
as Unix Domain Sockets.
|
||||
|
||||
Syntax for specifying a NBD device using TCP
|
||||
``nbd:<server-ip>:<port>[:exportname=<export>]''
|
||||
|
||||
Syntax for specifying a NBD device using Unix Domain Sockets
|
||||
``nbd:unix:<domain-socket>[:exportname=<export>]''
|
||||
|
||||
|
||||
Example for TCP
|
||||
@example
|
||||
qemu --drive file=nbd:192.0.2.1:30000
|
||||
@end example
|
||||
|
||||
Example for Unix Domain Sockets
|
||||
@example
|
||||
qemu --drive file=nbd:unix:/tmp/nbd-socket
|
||||
@end example
|
||||
|
||||
@item Sheepdog
|
||||
Sheepdog is a distributed storage system for QEMU.
|
||||
QEMU supports using either local sheepdog devices or remote networked
|
||||
devices.
|
||||
|
||||
Syntax for specifying a sheepdog device
|
||||
@table @list
|
||||
``sheepdog:<vdiname>''
|
||||
|
||||
``sheepdog:<vdiname>:<snapid>''
|
||||
|
||||
``sheepdog:<vdiname>:<tag>''
|
||||
|
||||
``sheepdog:<host>:<port>:<vdiname>''
|
||||
|
||||
``sheepdog:<host>:<port>:<vdiname>:<snapid>''
|
||||
|
||||
``sheepdog:<host>:<port>:<vdiname>:<tag>''
|
||||
@end table
|
||||
|
||||
Example
|
||||
@example
|
||||
qemu --drive file=sheepdog:192.0.2.1:30000:MyVirtualMachine
|
||||
@end example
|
||||
|
||||
See also @url{http://http://www.osrg.net/sheepdog/}.
|
||||
|
||||
@end table
|
||||
ETEXI
|
||||
|
||||
DEFHEADING(Bluetooth(R) options:)
|
||||
|
||||
DEF("bt", HAS_ARG, QEMU_OPTION_bt, \
|
||||
|
@ -278,6 +278,7 @@ usb_host_claim_port(int bus, int hub, int port) "bus %d, hub addr %d, port %d"
|
||||
# hw/scsi-bus.c
|
||||
scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d"
|
||||
scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
|
||||
scsi_req_data_canceled(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
|
||||
scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d"
|
||||
scsi_req_continue(int target, int lun, int tag) "target %d lun %d tag %d"
|
||||
scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer) "target %d lun %d tag %d command %d dir %d length %d"
|
||||
@ -505,6 +506,12 @@ escc_sunkbd_event_out(int ch) "Translated keycode %2.2x"
|
||||
escc_kbd_command(int val) "Command %d"
|
||||
escc_sunmouse_event(int dx, int dy, int buttons_state) "dx=%d dy=%d buttons=%01x"
|
||||
|
||||
# block/iscsi.c
|
||||
iscsi_aio_write10_cb(void *iscsi, int status, void *acb, int canceled) "iscsi %p status %d acb %p canceled %d"
|
||||
iscsi_aio_writev(void *iscsi, int64_t sector_num, int nb_sectors, void *opaque, void *acb) "iscsi %p sector_num %"PRId64" nb_sectors %d opaque %p acb %p"
|
||||
iscsi_aio_read10_cb(void *iscsi, int status, void *acb, int canceled) "iscsi %p status %d acb %p canceled %d"
|
||||
iscsi_aio_readv(void *iscsi, int64_t sector_num, int nb_sectors, void *opaque, void *acb) "iscsi %p sector_num %"PRId64" nb_sectors %d opaque %p acb %p"
|
||||
|
||||
# hw/esp.c
|
||||
esp_raise_irq(void) "Raise IRQ"
|
||||
esp_lower_irq(void) "Lower IRQ"
|
||||
|
Loading…
Reference in New Issue
Block a user