From: ccardenas Date: Wed, 3 Jan 2018 05:39:56 +0000 (+0000) Subject: Add initial CD-ROM support to VMD via vioscsi. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=95ab188f112e9222b375a8ba5e758d43cbe60f74;p=openbsd Add initial CD-ROM support to VMD via vioscsi. * Adds 'cdrom' keyword to vm.conf(5) and '-r' to vmctl(8) * Support various sized ISOs (Limitation of 4G ISOs on Linux guests) * Known working guests: OpenBSD (primary), Alpine Linux (primary), CentOS 6 (secondary), Ubuntu 17.10 (secondary). NOTE: Secondary indicates some issue(s) preventing full/reliable functionality outside the scope of the vioscsi work. * If the attached disks are non-bootable (i.e. empty), SeaBIOS (vmd's default BIOS) will boot from CD-ROM. ok mlarkin@, jca@ --- diff --git a/usr.sbin/vmctl/main.c b/usr.sbin/vmctl/main.c index 91fac99b48c..329504638e3 100644 --- a/usr.sbin/vmctl/main.c +++ b/usr.sbin/vmctl/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.33 2017/10/07 19:48:30 guenther Exp $ */ +/* $OpenBSD: main.c,v 1.34 2018/01/03 05:39:56 ccardenas Exp $ */ /* * Copyright (c) 2015 Reyk Floeter @@ -69,7 +69,7 @@ struct ctl_command ctl_commands[] = { { "reload", CMD_RELOAD, ctl_reload, "" }, { "reset", CMD_RESET, ctl_reset, "[all|vms|switches]" }, { "start", CMD_START, ctl_start, "\"name\"" - " [-Lc] [-b image] [-m size]\n" + " [-Lc] [-b image] [-r image] [-m size]\n" "\t\t[-n switch] [-i count] [-d disk]*" }, { "status", CMD_STATUS, ctl_status, "[id]" }, { "stop", CMD_STOP, ctl_stop, "id" }, @@ -203,7 +203,8 @@ vmmaction(struct parse_result *res) switch (res->action) { case CMD_START: ret = vm_start(res->id, res->name, res->size, res->nifs, - res->nets, res->ndisks, res->disks, res->path); + res->nets, res->ndisks, res->disks, res->path, + res->isopath); if (ret) { errno = ret; err(1, "start VM operation failed"); @@ -324,6 +325,7 @@ parse_free(struct parse_result *res) free(res->name); free(res->path); + free(res->isopath); for (i = 0; i < res->ndisks; i++) free(res->disks[i]); free(res->disks); @@ -571,7 +573,7 @@ ctl_start(struct parse_result *res, int argc, char *argv[]) argc--; argv++; - while ((ch = getopt(argc, argv, "b:cLm:n:d:i:")) != -1) { + while ((ch = getopt(argc, argv, "b:r:cLm:n:d:i:")) != -1) { switch (ch) { case 'b': if (res->path) @@ -581,6 +583,14 @@ ctl_start(struct parse_result *res, int argc, char *argv[]) if ((res->path = strdup(path)) == NULL) errx(1, "strdup"); break; + case 'r': + if (res->isopath) + errx(1, "iso image specified multiple times"); + if (realpath(optarg, path) == NULL) + err(1, "invalid iso image path"); + if ((res->isopath = strdup(path)) == NULL) + errx(1, "strdup"); + break; case 'c': tty_autoconnect = 1; break; diff --git a/usr.sbin/vmctl/vmctl.8 b/usr.sbin/vmctl/vmctl.8 index 2efcf035531..bcdbca3d9ad 100644 --- a/usr.sbin/vmctl/vmctl.8 +++ b/usr.sbin/vmctl/vmctl.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: vmctl.8,v 1.35 2017/11/05 20:01:09 reyk Exp $ +.\" $OpenBSD: vmctl.8,v 1.36 2018/01/03 05:39:56 ccardenas Exp $ .\" .\" Copyright (c) 2015 Mike Larkin .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: November 5 2017 $ +.Dd $Mdocdate: January 3 2018 $ .Dt VMCTL 8 .Os .Sh NAME @@ -84,6 +84,7 @@ to standard output and terminate it. .It Xo Cm start Ar name .Op Fl Lc .Op Fl b Ar path +.Op Fl r Ar path .Op Fl d Ar path .Op Fl i Ar count .Op Fl m Ar size @@ -126,6 +127,10 @@ See in .Xr vm.conf 5 for more information. +.It Fl r Ar path +ISO image file for virtual CD-ROM. This image file will be available in the +selected VM as a SCSI CD-ROM device attached to a virtio SCSI adapter (eg. +.Xr vioscsi 4 ) .El .Pp Note that the VM name supplied to the 'start' command can only consist of diff --git a/usr.sbin/vmctl/vmctl.c b/usr.sbin/vmctl/vmctl.c index c6cd8e3dc8a..fbe0ff66a7e 100644 --- a/usr.sbin/vmctl/vmctl.c +++ b/usr.sbin/vmctl/vmctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vmctl.c,v 1.44 2017/09/08 07:08:49 mlarkin Exp $ */ +/* $OpenBSD: vmctl.c,v 1.45 2018/01/03 05:39:56 ccardenas Exp $ */ /* * Copyright (c) 2014 Mike Larkin @@ -61,6 +61,7 @@ int info_console; * ndisks: number of disk images * disks: disk image file names * kernel: kernel image to load + * iso: iso image file * * Return: * 0 if the request to start the VM was sent successfully. @@ -68,7 +69,7 @@ int info_console; */ int vm_start(uint32_t start_id, const char *name, int memsize, int nnics, - char **nics, int ndisks, char **disks, char *kernel) + char **nics, int ndisks, char **disks, char *kernel, char *iso) { struct vmop_create_params *vmc; struct vm_create_params *vcp; @@ -84,6 +85,8 @@ vm_start(uint32_t start_id, const char *name, int memsize, int nnics, flags |= VMOP_CREATE_DISK; if (kernel) flags |= VMOP_CREATE_KERNEL; + if (iso) + flags |= VMOP_CREATE_CDROM; if (flags != 0) { if (memsize < 1) memsize = VM_DEFAULT_MEMORY; @@ -155,6 +158,9 @@ vm_start(uint32_t start_id, const char *name, int memsize, int nnics, if (kernel != NULL) strlcpy(vcp->vcp_kernel, kernel, VMM_MAX_KERNEL_PATH); + if (iso != NULL) + strlcpy(vcp->vcp_cdrom, iso, VMM_MAX_PATH_CDROM); + imsg_compose(ibuf, IMSG_VMDOP_START_VM_REQUEST, 0, 0, -1, vmc, sizeof(struct vmop_create_params)); @@ -209,6 +215,15 @@ vm_start_complete(struct imsg *imsg, int *ret, int autoconnect) "not regular files"); *ret = ENOENT; break; + case VMD_CDROM_MISSING: + warnx("could not find specified iso image"); + *ret = ENOENT; + break; + case VMD_CDROM_INVALID: + warnx("specified iso image is " + "not a regular file"); + *ret = ENOENT; + break; default: errno = res; warn("start vm command failed"); diff --git a/usr.sbin/vmctl/vmctl.h b/usr.sbin/vmctl/vmctl.h index 0fc019e9eeb..203c89b85e8 100644 --- a/usr.sbin/vmctl/vmctl.h +++ b/usr.sbin/vmctl/vmctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: vmctl.h,v 1.17 2017/08/15 15:51:54 jasper Exp $ */ +/* $OpenBSD: vmctl.h,v 1.18 2018/01/03 05:39:56 ccardenas Exp $ */ /* * Copyright (c) 2015 Reyk Floeter @@ -45,6 +45,7 @@ struct parse_result { uint32_t id; char *name; char *path; + char *isopath; long long size; int nifs; char **nets; @@ -82,7 +83,7 @@ __dead void /* vmctl.c */ int create_imagefile(const char *, long); int vm_start(uint32_t, const char *, int, int, char **, int, - char **, char *); + char **, char *, char *); int vm_start_complete(struct imsg *, int *, int); void terminate_vm(uint32_t, const char *); int terminate_vm_complete(struct imsg *, int *); diff --git a/usr.sbin/vmd/Makefile b/usr.sbin/vmd/Makefile index 0f100873966..d31a8cb910d 100644 --- a/usr.sbin/vmd/Makefile +++ b/usr.sbin/vmd/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.16 2017/07/03 22:21:47 espie Exp $ +# $OpenBSD: Makefile,v 1.17 2018/01/03 05:39:56 ccardenas Exp $ .if ${MACHINE} == "amd64" || ${MACHINE} == "i386" @@ -6,7 +6,7 @@ PROG= vmd SRCS= vmd.c control.c log.c priv.c proc.c config.c vmm.c SRCS+= vm.c loadfile_elf.c pci.c virtio.c i8259.c mc146818.c SRCS+= ns8250.c i8253.c vmboot.c ufs.c disklabel.c dhcp.c packet.c -SRCS+= parse.y atomicio.c +SRCS+= parse.y atomicio.c vioscsi.c CFLAGS+= -Wall -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes diff --git a/usr.sbin/vmd/config.c b/usr.sbin/vmd/config.c index e0d4339559d..399279bd993 100644 --- a/usr.sbin/vmd/config.c +++ b/usr.sbin/vmd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.37 2017/11/04 07:57:14 mlarkin Exp $ */ +/* $OpenBSD: config.c,v 1.38 2018/01/03 05:39:56 ccardenas Exp $ */ /* * Copyright (c) 2015 Reyk Floeter @@ -169,6 +169,7 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid) unsigned int i; int fd = -1, vmboot = 0; int kernfd = -1, *diskfds = NULL, *tapfds = NULL; + int cdromfd = -1; int saved_errno = 0; char ifname[IF_NAMESIZE], *s; char path[PATH_MAX]; @@ -234,6 +235,30 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid) } } + /* Open CDROM image for child */ + if (strlen(vcp->vcp_cdrom)) { + /* Stat cdrom to ensure it is a regular file */ + if ((cdromfd = + open(vcp->vcp_cdrom, O_RDONLY)) == -1) { + log_warn("%s: can't open cdrom %s", __func__, + vcp->vcp_cdrom); + errno = VMD_CDROM_MISSING; + goto fail; + } + if (fstat(cdromfd, &stat_buf) == -1) { + log_warn("%s: can't open cdrom %s", __func__, + vcp->vcp_cdrom); + errno = VMD_CDROM_MISSING; + goto fail; + } + if (S_ISREG(stat_buf.st_mode) == 0) { + log_warn("%s: cdrom %s is not a regular file", __func__, + vcp->vcp_cdrom); + errno = VMD_CDROM_INVALID; + goto fail; + } + } + /* Open disk images for child */ for (i = 0 ; i < vcp->vcp_ndisks; i++) { /* Stat disk[i] to ensure it is a regular file */ @@ -345,6 +370,12 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid) proc_compose_imsg(ps, PROC_VMM, -1, IMSG_VMDOP_START_VM_REQUEST, vm->vm_vmid, kernfd, vmc, sizeof(*vmc)); + + if (strlen(vcp->vcp_cdrom)) + proc_compose_imsg(ps, PROC_VMM, -1, + IMSG_VMDOP_START_VM_CDROM, vm->vm_vmid, cdromfd, + NULL, 0); + for (i = 0; i < vcp->vcp_ndisks; i++) { proc_compose_imsg(ps, PROC_VMM, -1, IMSG_VMDOP_START_VM_DISK, vm->vm_vmid, diskfds[i], @@ -372,6 +403,8 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid) if (kernfd != -1) close(kernfd); + if (cdromfd != -1) + close(cdromfd); if (diskfds != NULL) { for (i = 0; i < vcp->vcp_ndisks; i++) close(diskfds[i]); @@ -477,3 +510,28 @@ config_getif(struct privsep *ps, struct imsg *imsg) errno = EINVAL; return (-1); } + +int +config_getcdrom(struct privsep *ps, struct imsg *imsg) +{ + struct vmd_vm *vm; + + errno = 0; + if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) { + errno = ENOENT; + return (-1); + } + + if (imsg->fd == -1) { + log_debug("invalid cdrom id"); + goto fail; + } + + vm->vm_cdrom = imsg->fd; + return (0); + fail: + if (imsg->fd != -1) + close(imsg->fd); + errno = EINVAL; + return (-1); +} diff --git a/usr.sbin/vmd/parse.y b/usr.sbin/vmd/parse.y index b909660829e..7655ba5c89b 100644 --- a/usr.sbin/vmd/parse.y +++ b/usr.sbin/vmd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.31 2017/11/11 02:50:07 mlarkin Exp $ */ +/* $OpenBSD: parse.y,v 1.32 2018/01/03 05:39:56 ccardenas Exp $ */ /* * Copyright (c) 2007-2016 Reyk Floeter @@ -113,8 +113,8 @@ typedef struct { %token INCLUDE ERROR -%token ADD BOOT DISABLE DISK DOWN ENABLE GROUP INTERFACE LLADDR LOCAL LOCKED -%token MEMORY NIFS OWNER PATH PREFIX RDOMAIN SIZE SWITCH UP VM VMID +%token ADD BOOT CDROM DISABLE DISK DOWN ENABLE GROUP INTERFACE LLADDR LOCAL +%token LOCKED MEMORY NIFS OWNER PATH PREFIX RDOMAIN SIZE SWITCH UP VM VMID %token NUMBER %token STRING %type lladdr @@ -388,6 +388,23 @@ vm_opts : disable { free($2); vmc.vmc_flags |= VMOP_CREATE_KERNEL; } + | CDROM string { + if (vcp->vcp_cdrom[0] != '\0') { + yyerror("cdrom specified more than once"); + free($2); + YYERROR; + + } + if (strlcpy(vcp->vcp_cdrom, $2, + sizeof(vcp->vcp_cdrom)) >= + sizeof(vcp->vcp_cdrom)) { + yyerror("cdrom name too long"); + free($2); + YYERROR; + } + free($2); + vmc.vmc_flags |= VMOP_CREATE_CDROM; + } | NIFS NUMBER { if (vcp->vcp_nnics != 0) { yyerror("interfaces specified more than once"); @@ -632,6 +649,7 @@ lookup(char *s) static const struct keywords keywords[] = { { "add", ADD }, { "boot", BOOT }, + { "cdrom", CDROM }, { "disable", DISABLE }, { "disk", DISK }, { "down", DOWN }, diff --git a/usr.sbin/vmd/vioscsi.c b/usr.sbin/vmd/vioscsi.c new file mode 100644 index 00000000000..290de68e453 --- /dev/null +++ b/usr.sbin/vmd/vioscsi.c @@ -0,0 +1,2218 @@ +/* $OpenBSD: vioscsi.c,v 1.1 2018/01/03 05:39:56 ccardenas Exp $ */ + +/* + * Copyright (c) 2017 Carlos Cardenas + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "vmd.h" +#include "vioscsi.h" +#include "virtio.h" + +extern char *__progname; + +static void +vioscsi_prepare_resp(struct virtio_scsi_res_hdr *resp, uint8_t vio_status, + uint8_t scsi_status, uint8_t err_flags, uint8_t add_sense_code, + uint8_t add_sense_code_qual) +{ + /* Set lower 8 bits of status and response fields */ + resp->response &= 0xFFFFFF00; + resp->response |= vio_status; + resp->status &= 0xFFFFFF00; + resp->status |= scsi_status; + + resp->sense_len = 0; + + /* determine if we need to populate the sense field */ + if (scsi_status == SCSI_CHECK) { + /* + * sense data is a 96 byte field. + * We only need to use the first 14 bytes + * - set the sense_len accordingly + * - set error_code to Current Command + * ref scsi/scsi_all.h:struct scsi_sense_data + */ + memset(resp->sense, 0, VIOSCSI_SENSE_LEN); + resp->sense_len = RESP_SENSE_LEN; + resp->sense[0] = SSD_ERRCODE_CURRENT; + resp->sense[2] = err_flags; + resp->sense[12] = add_sense_code; + resp->sense[13] = add_sense_code_qual; + } +} + +static struct vring_desc* +vioscsi_next_ring_desc(struct vring_desc* desc, struct vring_desc* cur, + uint16_t *idx) +{ + *idx = cur->next & VIOSCSI_QUEUE_MASK; + return &desc[*idx]; +} + +static void +vioscsi_next_ring_item(struct vioscsi_dev *dev, struct vring_avail *avail, + struct vring_used *used, struct vring_desc *desc, uint16_t idx) +{ + used->ring[used->idx & VIOSCSI_QUEUE_MASK].id = idx; + used->ring[used->idx & VIOSCSI_QUEUE_MASK].len = desc->len; + used->idx++; + + dev->vq[dev->cfg.queue_notify].last_avail = + avail->idx & VIOSCSI_QUEUE_MASK; +} + +static const char * +vioscsi_op_names(uint8_t type) +{ + switch(type) { + /* defined in scsi_all.h */ + case TEST_UNIT_READY: return "TEST_UNIT_READY"; + case REQUEST_SENSE: return "REQUEST_SENSE"; + case INQUIRY: return "INQUIRY"; + case MODE_SELECT: return "MODE_SELECT"; + case RESERVE: return "RESERVE"; + case RELEASE: return "RELEASE"; + case MODE_SENSE: return "MODE_SENSE"; + case START_STOP: return "START_STOP"; + case RECEIVE_DIAGNOSTIC: return "RECEIVE_DIAGNOSTIC"; + case SEND_DIAGNOSTIC: return "SEND_DIAGNOSTIC"; + case PREVENT_ALLOW: return "PREVENT_ALLOW"; + case POSITION_TO_ELEMENT: return "POSITION_TO_ELEMENT"; + case WRITE_BUFFER: return "WRITE_BUFFER"; + case READ_BUFFER: return "READ_BUFFER"; + case CHANGE_DEFINITION: return "CHANGE_DEFINITION"; + case MODE_SELECT_BIG: return "MODE_SELECT_BIG"; + case MODE_SENSE_BIG: return "MODE_SENSE_BIG"; + case REPORT_LUNS: return "REPORT_LUNS"; + /* defined in scsi_disk.h */ + case REASSIGN_BLOCKS: return "REASSIGN_BLOCKS"; + case READ_COMMAND: return "READ_COMMAND"; + case WRITE_COMMAND: return "WRITE_COMMAND"; + case READ_CAPACITY: return "READ_CAPACITY"; + case READ_CAPACITY_16: return "READ_CAPACITY_16"; + case READ_BIG: return "READ_BIG"; + case WRITE_BIG: return "WRITE_BIG"; + case READ_12: return "READ_12"; + case WRITE_12: return "WRITE_12"; + case READ_16: return "READ_16"; + case WRITE_16: return "WRITE_16"; + case SYNCHRONIZE_CACHE: return "SYNCHRONIZE_CACHE"; + case WRITE_SAME_10: return "WRITE_SAME_10"; + case WRITE_SAME_16: return "WRITE_SAME_16"; + /* defined in cd.h */ + case READ_SUBCHANNEL: return "READ_SUBCHANNEL"; + case READ_TOC: return "READ_TOC"; + case READ_HEADER: return "READ_HEADER"; + case PLAY: return "PLAY"; + case PLAY_MSF: return "PLAY_MSF"; + case PLAY_TRACK: return "PLAY_TRACK"; + case PLAY_TRACK_REL: return "PLAY_TRACK_REL"; + case PAUSE: return "PAUSE"; + case READ_TRACK_INFO: return "READ_TRACK_INFO"; + case CLOSE_TRACK: return "CLOSE_TRACK"; + case BLANK: return "BLANK"; + case PLAY_BIG: return "PLAY_BIG"; + case LOAD_UNLOAD: return "LOAD_UNLOAD"; + case PLAY_TRACK_REL_BIG: return "PLAY_TRACK_REL_BIG"; + case SET_CD_SPEED: return "SET_CD_SPEED"; + /* defined locally */ + case READ_DISC_INFORMATION: return "READ_DISC_INFORMATION"; + case GET_CONFIGURATION: return "GET_CONFIGURATION"; + case MECHANISM_STATUS: return "MECHANISM_STATUS"; + case GET_EVENT_STATUS_NOTIFICATION: + return "GET_EVENT_STATUS_NOTIFICATION"; + default: return "UNKNOWN"; + } +} + +static const char * +vioscsi_reg_name(uint8_t reg) +{ + switch (reg) { + case VIRTIO_CONFIG_DEVICE_FEATURES: return "device feature"; + case VIRTIO_CONFIG_GUEST_FEATURES: return "guest feature"; + case VIRTIO_CONFIG_QUEUE_ADDRESS: return "queue address"; + case VIRTIO_CONFIG_QUEUE_SIZE: return "queue size"; + case VIRTIO_CONFIG_QUEUE_SELECT: return "queue select"; + case VIRTIO_CONFIG_QUEUE_NOTIFY: return "queue notify"; + case VIRTIO_CONFIG_DEVICE_STATUS: return "device status"; + case VIRTIO_CONFIG_ISR_STATUS: return "isr status"; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI: return "num_queues"; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4: return "seg_max"; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 8: return "max_sectors"; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12: return "cmd_per_lun"; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 16: return "event_info_size"; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 20: return "sense_size"; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 24: return "cdb_size"; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 28: return "max_channel"; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 30: return "max_target"; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 32: return "max_lun"; + default: return "unknown"; + } +} + +static void +vioscsi_free_info(struct ioinfo *info) +{ + if (!info) + return; + free(info->buf); + free(info); +} + +static struct ioinfo * +vioscsi_start_read(struct vioscsi_dev *dev, off_t block, ssize_t n_blocks) +{ + struct ioinfo *info; + + info = calloc(1, sizeof(*info)); + if (!info) + goto nomem; + info->buf = malloc(n_blocks * VIOSCSI_BLOCK_SIZE_CDROM); + if (info->buf == NULL) + goto nomem; + info->len = n_blocks * VIOSCSI_BLOCK_SIZE_CDROM; + info->offset = block * VIOSCSI_BLOCK_SIZE_CDROM; + info->fd = dev->fd; + + return info; + +nomem: + free(info); + log_warn("malloc errror vioscsi read"); + return (NULL); +} + +static const uint8_t * +vioscsi_finish_read(struct ioinfo *info) +{ + if (pread(info->fd, info->buf, info->len, info->offset) != info->len) { + info->error = errno; + log_warn("vioscsi read error"); + return NULL; + } + + return info->buf; +} + +int +vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr, + void *cookie, uint8_t sz) +{ + struct vioscsi_dev *dev = (struct vioscsi_dev *)cookie; + + *intr = 0xFF; + + log_debug("%s: request %s reg %u,%s sz %u", __func__, + dir ? "READ" : "WRITE", reg, vioscsi_reg_name(reg), sz); + + if (dir == 0) { + switch (reg) { + case VIRTIO_CONFIG_DEVICE_FEATURES: + case VIRTIO_CONFIG_QUEUE_SIZE: + case VIRTIO_CONFIG_ISR_STATUS: + log_warnx("%s: illegal write %x to %s", + __progname, *data, vioscsi_reg_name(reg)); + break; + case VIRTIO_CONFIG_GUEST_FEATURES: + dev->cfg.guest_feature = *data; + log_debug("%s: guest feature set to %u", + __func__, dev->cfg.guest_feature); + break; + case VIRTIO_CONFIG_QUEUE_ADDRESS: + dev->cfg.queue_address = *data; + vioscsi_update_qa(dev); + break; + case VIRTIO_CONFIG_QUEUE_SELECT: + dev->cfg.queue_select = *data; + vioscsi_update_qs(dev); + break; + case VIRTIO_CONFIG_QUEUE_NOTIFY: + dev->cfg.queue_notify = *data; + if (vioscsi_notifyq(dev)) + *intr = 1; + break; + case VIRTIO_CONFIG_DEVICE_STATUS: + dev->cfg.device_status = *data; + log_debug("%s: device status set to %u", + __func__, dev->cfg.device_status); + if (dev->cfg.device_status == 0) { + log_debug("%s: device reset", __func__); + dev->cfg.guest_feature = 0; + dev->cfg.queue_address = 0; + vioscsi_update_qa(dev); + dev->cfg.queue_size = 0; + vioscsi_update_qs(dev); + dev->cfg.queue_select = 0; + dev->cfg.queue_notify = 0; + dev->cfg.isr_status = 0; + dev->vq[0].last_avail = 0; + dev->vq[1].last_avail = 0; + dev->vq[2].last_avail = 0; + } + break; + default: + break; + } + } else { + switch (reg) { + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI: + /* VIRTIO_SCSI_CONFIG_NUM_QUEUES, 32bit */ + if (sz == 4) + *data = (uint32_t)VIOSCSI_NUM_QUEUES; + else if (sz == 1) { + /* read first byte of num_queues */ + *data &= 0xFFFFFF00; + *data |= (uint32_t)(VIOSCSI_NUM_QUEUES) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 1: + if (sz == 1) { + /* read second byte of num_queues */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_NUM_QUEUES >> 8) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 2: + if (sz == 1) { + /* read third byte of num_queues */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_NUM_QUEUES >> 16) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 3: + if (sz == 1) { + /* read fourth byte of num_queues */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_NUM_QUEUES >> 24) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4: + /* VIRTIO_SCSI_CONFIG_SEG_MAX, 32bit */ + if (sz == 4) + *data = (uint32_t)(VIOSCSI_SEG_MAX); + else if (sz == 1) { + /* read first byte of seg_max */ + *data &= 0xFFFFFF00; + *data |= (uint32_t)(VIOSCSI_SEG_MAX) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 5: + if (sz == 1) { + /* read second byte of seg_max */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_SEG_MAX >> 8) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 6: + if (sz == 1) { + /* read third byte of seg_max */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_SEG_MAX >> 16) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 7: + if (sz == 1) { + /* read fourth byte of seg_max */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_SEG_MAX >> 24) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 8: + /* VIRTIO_SCSI_CONFIG_MAX_SECTORS, 32bit */ + if (sz == 4) + *data = (uint32_t)(dev->max_xfer); + else if (sz == 1) { + /* read first byte of max_xfer */ + *data &= 0xFFFFFF00; + *data |= (uint32_t)(dev->max_xfer) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 9: + if (sz == 1) { + /* read second byte of max_xfer */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(dev->max_xfer >> 8) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 10: + if (sz == 1) { + /* read third byte of max_xfer */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(dev->max_xfer >> 16) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 11: + if (sz == 1) { + /* read fourth byte of max_xfer */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(dev->max_xfer >> 24) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12: + /* VIRTIO_SCSI_CONFIG_CMD_PER_LUN, 32bit */ + if (sz == 4) + *data = (uint32_t)(VIOSCSI_CMD_PER_LUN); + else if (sz == 1) { + /* read first byte of cmd_per_lun */ + *data &= 0xFFFFFF00; + *data |= (uint32_t)(VIOSCSI_CMD_PER_LUN) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 13: + if (sz == 1) { + /* read second byte of cmd_per_lun */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_CMD_PER_LUN >> 8) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 14: + if (sz == 1) { + /* read third byte of cmd_per_lun */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_CMD_PER_LUN >> 16) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 15: + if (sz == 1) { + /* read fourth byte of cmd_per_lun */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_CMD_PER_LUN >> 24) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 16: + /* VIRTIO_SCSI_CONFIG_EVENT_INFO_SIZE, 32bit */ + *data = 0x00; + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 20: + /* VIRTIO_SCSI_CONFIG_SENSE_SIZE, 32bit */ + if (sz == 4) + *data = (uint32_t)(VIOSCSI_SENSE_LEN); + else if (sz == 1) { + /* read first byte of sense_size */ + *data &= 0xFFFFFF00; + *data |= (uint32_t)(VIOSCSI_SENSE_LEN) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 21: + if (sz == 1) { + /* read second byte of sense_size */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_SENSE_LEN >> 8) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 22: + if (sz == 1) { + /* read third byte of sense_size */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_SENSE_LEN >> 16) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 23: + if (sz == 1) { + /* read fourth byte of sense_size */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_SENSE_LEN >> 24) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 24: + /* VIRTIO_SCSI_CONFIG_CDB_SIZE, 32bit */ + if (sz == 4) + *data = (uint32_t)(VIOSCSI_CDB_LEN); + else if (sz == 1) { + /* read first byte of cdb_len */ + *data &= 0xFFFFFF00; + *data |= (uint32_t)(VIOSCSI_CDB_LEN) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 25: + if (sz == 1) { + /* read second byte of cdb_len */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_CDB_LEN >> 8) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 26: + if (sz == 1) { + /* read third byte of cdb_len */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_CDB_LEN >> 16) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 27: + if (sz == 1) { + /* read fourth byte of cdb_len */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_CDB_LEN >> 24) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 28: + /* VIRTIO_SCSI_CONFIG_MAX_CHANNEL, 16bit */ + *data &= 0xFFFF0000; /* defined by standard to be zero */ + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 29: + *data &= 0xFFFF0000; /* defined by standard to be zero */ + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 30: + /* VIRTIO_SCSI_CONFIG_MAX_TARGET, 16bit */ + if (sz == 2) { + *data &= 0xFFFF0000; + *data |= + (uint32_t)(VIOSCSI_MAX_TARGET) & 0xFFFF; + } else if (sz == 1) { + /* read first byte of max_target */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_MAX_TARGET) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 31: + if (sz == 1) { + /* read second byte of max_target */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_MAX_TARGET >> 8) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 32: + /* VIRTIO_SCSI_CONFIG_MAX_LUN, 32bit */ + if (sz == 4) + *data = (uint32_t)(VIOSCSI_MAX_LUN); + else if (sz == 1) { + /* read first byte of max_lun */ + *data &= 0xFFFFFF00; + *data |= (uint32_t)(VIOSCSI_MAX_LUN) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 33: + if (sz == 1) { + /* read second byte of max_lun */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_MAX_LUN >> 8) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 34: + if (sz == 1) { + /* read third byte of max_lun */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_MAX_LUN >> 16) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 35: + if (sz == 1) { + /* read fourth byte of max_lun */ + *data &= 0xFFFFFF00; + *data |= + (uint32_t)(VIOSCSI_MAX_LUN >> 24) & 0xFF; + } + break; + case VIRTIO_CONFIG_DEVICE_FEATURES: + *data = dev->cfg.device_feature; + break; + case VIRTIO_CONFIG_GUEST_FEATURES: + *data = dev->cfg.guest_feature; + break; + case VIRTIO_CONFIG_QUEUE_ADDRESS: + *data = dev->cfg.queue_address; + break; + case VIRTIO_CONFIG_QUEUE_SIZE: + if (sz == 4) + *data = dev->cfg.queue_size; + else if (sz == 2) { + *data &= 0xFFFF0000; + *data |= (uint16_t)dev->cfg.queue_size; + } else if (sz == 1) { + *data &= 0xFFFFFF00; + *data |= (uint8_t)dev->cfg.queue_size; + } + break; + case VIRTIO_CONFIG_QUEUE_SELECT: + *data = dev->cfg.queue_select; + break; + case VIRTIO_CONFIG_QUEUE_NOTIFY: + *data = dev->cfg.queue_notify; + break; + case VIRTIO_CONFIG_DEVICE_STATUS: + if (sz == 4) + *data = dev->cfg.device_status; + else if (sz == 2) { + *data &= 0xFFFF0000; + *data |= (uint16_t)dev->cfg.device_status; + } else if (sz == 1) { + *data &= 0xFFFFFF00; + *data |= (uint8_t)dev->cfg.device_status; + } + break; + case VIRTIO_CONFIG_ISR_STATUS: + *data = dev->cfg.isr_status; + dev->cfg.isr_status = 0; + break; + } + } + + + return (0); +} + +void +vioscsi_update_qs(struct vioscsi_dev *dev) +{ + /* Invalid queue? */ + if(dev->cfg.queue_select > VIRTIO_MAX_QUEUES) { + dev->cfg.queue_size = 0; + return; + } + + /* Update queue address/size based on queue select */ + dev->cfg.queue_address = dev->vq[dev->cfg.queue_select].qa; + dev->cfg.queue_size = dev->vq[dev->cfg.queue_select].qs; +} + +void +vioscsi_update_qa(struct vioscsi_dev *dev) +{ + /* Invalid queue? */ + if(dev->cfg.queue_select > VIRTIO_MAX_QUEUES) + return; + + dev->vq[dev->cfg.queue_select].qa = dev->cfg.queue_address; +} + +/* + * Process message(s) in the queue(s) + * vioscsi driver will be placing the following in the queue for each iteration + * virtio_scsi_req_hdr with a possible SCSI_DATA_OUT buffer + * along with a virtio_scsi_res_hdr with a possible SCSI_DATA_IN buffer + * for consumption. + * + * Return 1 if an interrupt should be generated (response written) + * 0 otherwise + */ +int +vioscsi_notifyq(struct vioscsi_dev *dev) +{ + uint64_t q_gpa; + uint32_t vr_sz; + uint16_t idx, req_idx, resp_idx; + int ret; + char *vr; + struct vring_desc *desc, *req_desc, *resp_desc; + struct vring_avail *avail; + struct vring_used *used; + struct virtio_scsi_req_hdr req; + struct virtio_scsi_res_hdr resp; + + /* various helper values */ + uint16_t inq_len; + uint32_t r_cap_addr; + uint64_t r_cap_addr_16; + uint16_t toc_len; + uint16_t toc_data_len; + uint8_t toc_data[TOC_DATA_SIZE]; + uint8_t *toc_data_p; + const uint8_t *read_buf; + uint32_t read_lba; + uint16_t read_10_len; + struct ioinfo *info; + off_t chunk_offset; + uint8_t mode_page_ctl; + uint8_t mode_page_code; + uint8_t *mode_reply; + uint8_t mode_reply_len; + uint16_t mode_sense_len; + uint8_t gesn_reply[GESN_SIZE]; + uint16_t get_conf_feature; + uint16_t get_conf_len; + uint8_t *get_conf_reply; + uint16_t mech_status_len; + + /* helper structs */ + struct scsi_inquiry *inq; + struct scsi_inquiry_data *inq_data; + struct scsi_read_capacity *r_cap; + struct scsi_read_cap_data *r_cap_data; + struct scsi_read_capacity_16 *r_cap_16; + struct scsi_read_cap_data_16 *r_cap_data_16; + struct scsi_read_toc *toc; + struct scsi_rw *read_6; + struct scsi_rw_big *read_10; + struct scsi_mode_sense *mode_sense; + struct scsi_mode_sense_big *mode_sense_10; + struct scsi_mechanism_status *mech_status; + struct scsi_mechanism_status_header *mech_status_header; + struct scsi_read_disc_information *read_disc; + struct scsi_gesn *gesn; + struct scsi_gesn_event_header *gesn_event_header; + struct scsi_gesn_power_event *gesn_power_event; + struct scsi_get_configuration *get_configuration; + struct scsi_config_feature_header *config_feature_header; + struct scsi_config_generic_descriptor *config_generic_desc; + struct scsi_config_profile_descriptor *config_profile_desc; + struct scsi_config_core_descriptor *config_core_desc; + struct scsi_config_morphing_descriptor *config_morphing_desc; + struct scsi_config_remove_media_descriptor *config_remove_media_desc; + struct scsi_config_random_read_descriptor *config_random_read_desc; + + ret = 0; + + /* Invalid queue? */ + if (dev->cfg.queue_notify > VIRTIO_MAX_QUEUES) + return (ret); + + vr_sz = vring_size(VIOSCSI_QUEUE_SIZE); + q_gpa = dev->vq[dev->cfg.queue_notify].qa; + q_gpa = q_gpa * VIRTIO_PAGE_SIZE; + + vr = calloc(1, vr_sz); + if (vr == NULL) { + log_warn("%s: calloc error getting vioscsi ring", __func__); + return (ret); + } + + if (read_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error reading gpa 0x%llx", __func__, q_gpa); + goto out; + } + + /* Compute offsets in ring of descriptors, avail ring, and used ring */ + desc = (struct vring_desc *)(vr); + avail = (struct vring_avail *)(vr + + dev->vq[dev->cfg.queue_notify].vq_availoffset); + used = (struct vring_used *)(vr + + dev->vq[dev->cfg.queue_notify].vq_usedoffset); + + idx = dev->vq[dev->cfg.queue_notify].last_avail & VIOSCSI_QUEUE_MASK; + + if ((avail->idx & VIOSCSI_QUEUE_MASK) == idx) { + log_warnx("%s:nothing to do?", __func__); + goto out; + } + + while (idx != (avail->idx & VIOSCSI_QUEUE_MASK)) { + + req_idx = avail->ring[idx] & VIOSCSI_QUEUE_MASK; + req_desc = &desc[req_idx]; + + /* Clear resp for next message */ + memset(&resp, 0, sizeof(resp)); + + if ((req_desc->flags & VRING_DESC_F_NEXT) == 0) { + log_warnx("%s: unchained req descriptor received " + "(idx %d)", __func__, req_idx); + goto out; + } + + /* Read command from descriptor ring */ + if (read_mem(req_desc->addr, &req, req_desc->len)) { + log_warnx("%s: command read_mem error @ 0x%llx", + __func__, req_desc->addr); + goto out; + } + + /* + * req.lun is defined by virtio as + * lun[0] - Always set to 1 + * lun[1] - Target, negotiated as VIOSCSI_MAX_TARGET + * lun[2-3] - represent single level LUN structure + * lun[4-7] - Zero + * At this current time, we are only servicing one device per + * bus (1:0:X:0). + * + * Various implementations will attempt to scan all possible + * targets (256) looking for devices or scan for all possible + * LUNs in a single level. When Target is greater than + * VIOSCSI_MAX_TARGET or when lun[3] is greater than zero, + * respond with a BAD_TARGET response. + */ + if (req.lun[1] >= VIOSCSI_MAX_TARGET || req.lun[3] > 0) { + log_debug("%s: Ignore CMD 0x%02x,%s on lun %u:%u:%u:%u", + __func__, req.cdb[0], vioscsi_op_names(req.cdb[0]), + req.lun[0], req.lun[1], req.lun[2], req.lun[3]); + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, req_desc, + &resp_idx); + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_BAD_TARGET, SCSI_OK, 0, 0, 0); + + if (write_mem(resp_desc->addr, &resp, resp_desc->len)) { + log_warnx("%s: unable to write BAD_TARGET" + " resp status data @ 0x%llx", + __func__, resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, req_desc, + req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + goto next_msg; + } + + log_debug("%s: Queue %d id 0x%llx lun %u:%u:%u:%u" + " cdb OP 0x%02x,%s", + __func__, dev->cfg.queue_notify, req.id, + req.lun[0], req.lun[1], req.lun[2], req.lun[3], + req.cdb[0], vioscsi_op_names(req.cdb[0])); + + /* opcode is first byte */ + switch(req.cdb[0]) { + case TEST_UNIT_READY: + case START_STOP: + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, req_desc, + &resp_idx); + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0); + + if (write_mem(resp_desc->addr, &resp, resp_desc->len)) { + log_warnx("%s: unable to write OK resp status " + "data @ 0x%llx", __func__, resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, req_desc, + req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + break; + case PREVENT_ALLOW: + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, req_desc, + &resp_idx); + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0); + + if (dev->locked) { + log_debug("%s: unlocking medium", __func__); + } else { + log_debug("%s: locking medium", __func__); + } + + dev->locked = dev->locked ? 0 : 1; + + if (write_mem(resp_desc->addr, &resp, resp_desc->len)) { + log_warnx("%s: unable to write OK resp status " + "data @ 0x%llx", __func__, resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, req_desc, + req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + break; + case READ_TOC: + toc = (struct scsi_read_toc *)(req.cdb); + toc_len = (uint16_t)_2btol(toc->data_len); + log_debug("%s: %s - MSF %d Track 0x%02x Addr 0x%04x", + __func__, vioscsi_op_names(toc->opcode), + ((toc->byte2 >> 1) & 1), toc->from_track, toc_len); + + memset(toc_data, 0, sizeof(toc_data)); + + /* Tracks should be 0, 1, or LEAD_OUT_TRACK, 0xaa */ + if (toc->from_track > 1 && + toc->from_track != READ_TOC_LEAD_OUT_TRACK) { + /* illegal request */ + log_debug("%s: illegal request Track 0x%02x", + __func__, toc->from_track); + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_CHECK, + SKEY_ILLEGAL_REQUEST, + SENSE_ILLEGAL_CDB_FIELD, + SENSE_DEFAULT_ASCQ); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to set ERR " + "status data @ 0x%llx", __func__, + resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + + goto next_msg; + } + + /* + * toc_data is defined as: + * [0-1]: TOC Data Length, typically 0x1a + * [2]: First Track, 1 + * [3]: Last Track, 1 + * + * Track 1 Descriptor + * [0]: Reserved, 0 + * [1]: ADR,Control, 0x14 + * [2]: Track #, 1 + * [3]: Reserved, 0 + * [4-7]: Track Start Address, LBA + * + * Track 0xaa (Lead Out) Descriptor + * [0]: Reserved, 0 + * [1]: ADR,Control, 0x14 + * [2]: Track #, 0xaa + * [3]: Reserved, 0 + * [4-7]: Track Start Address, LBA + */ + toc_data_p = toc_data + 2; + *toc_data_p++ = READ_TOC_START_TRACK; + *toc_data_p++ = READ_TOC_LAST_TRACK; + if (toc->from_track <= 1) { + /* first track descriptor */ + *toc_data_p++ = 0x0; + *toc_data_p++ = READ_TOC_ADR_CTL; + *toc_data_p++ = READ_TOC_START_TRACK; + *toc_data_p++ = 0x0; + /* start addr for first track is 0 */ + *toc_data_p++ = 0x0; + *toc_data_p++ = 0x0; + *toc_data_p++ = 0x0; + *toc_data_p++ = 0x0; + } + + /* last track descriptor */ + *toc_data_p++ = 0x0; + *toc_data_p++ = READ_TOC_ADR_CTL; + *toc_data_p++ = READ_TOC_LEAD_OUT_TRACK; + *toc_data_p++ = 0x0; + + _lto4b((uint32_t)dev->n_blocks, toc_data_p); + toc_data_p += 4; + + toc_data_len = toc_data_p - toc_data; + _lto2b((uint32_t)toc_data_len - 2, toc_data); + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, req_desc, + &resp_idx); + + dprintf("%s: writing resp to 0x%llx size %d at local " + "idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, &resp, resp_desc->len)) { + log_warnx("%s: unable to write OK resp status " + "data @ 0x%llx", __func__, resp_desc->addr); + goto out; + } + + /* Move index for toc descriptor */ + resp_desc = vioscsi_next_ring_desc(desc, resp_desc, + &resp_idx); + + dprintf("%s: writing toc_data to 0x%llx size %d at " + "local idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, toc_data, + resp_desc->len)) { + log_warnx("%s: unable to write toc descriptor" + "data @ 0x%llx", __func__, resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, req_desc, + req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + + break; + case READ_CAPACITY: + r_cap = (struct scsi_read_capacity *)(req.cdb); + r_cap_addr = _4btol(r_cap->addr); + log_debug("%s: %s - Addr 0x%08x", __func__, + vioscsi_op_names(r_cap->opcode), + r_cap_addr); + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0); + + r_cap_data = + calloc(1, sizeof(struct scsi_read_cap_data)); + + if (r_cap_data == NULL) { + log_warnx("%s: cannot alloc r_cap_data", + __func__); + goto out; + } + + log_debug("%s: ISO has %lld bytes and %lld blocks", + __func__, dev->sz, dev->n_blocks); + + /* + * determine if size of iso image > UINT32_MAX + * if it is, set addr to UINT32_MAX (0xffffffff) + * indicating to hosts that READ_CAPACITY_16 should + * be called to retrieve the full size + */ + if (dev->sz >= UINT32_MAX) { + _lto4b(UINT32_MAX, r_cap_data->addr); + _lto4b(VIOSCSI_BLOCK_SIZE_CDROM, + r_cap_data->length); + log_warnx("%s: ISO sz %lld is bigger than " + "UINT32_MAX %u, all data may not be read", + __func__, dev->sz, UINT32_MAX); + } else { + _lto4b(dev->n_blocks - 1, r_cap_data->addr); + _lto4b(VIOSCSI_BLOCK_SIZE_CDROM, + r_cap_data->length); + } + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, req_desc, + &resp_idx); + + dprintf("%s: writing resp to 0x%llx size %d at local " + "idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, &resp, resp_desc->len)) { + log_warnx("%s: unable to write OK resp status " + "data @ 0x%llx", __func__, resp_desc->addr); + free(r_cap_data); + goto out; + } + + /* Move index for r_cap_data */ + resp_desc = vioscsi_next_ring_desc(desc, resp_desc, + &resp_idx); + + dprintf("%s: writing r_cap_data to 0x%llx size %d at " + "local idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, r_cap_data, + resp_desc->len)) { + log_warnx("%s: unable to write read_cap_data" + " response to gpa @ 0x%llx", + __func__, resp_desc->addr); + free(r_cap_data); + goto out; + } + + free(r_cap_data); + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, req_desc, + req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + break; + case READ_CAPACITY_16: + r_cap_16 = (struct scsi_read_capacity_16 *)(req.cdb); + r_cap_addr_16 = _8btol(r_cap_16->addr); + log_debug("%s: %s - Addr 0x%016llx", __func__, + vioscsi_op_names(r_cap_16->opcode), + r_cap_addr_16); + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0); + + r_cap_data_16 = + calloc(1, sizeof(struct scsi_read_cap_data_16)); + + if (r_cap_data_16 == NULL) { + log_warnx("%s: cannot alloc r_cap_data_16", + __func__); + goto out; + } + + log_debug("%s: ISO has %lld bytes and %lld blocks", + __func__, dev->sz, dev->n_blocks); + + _lto8b(dev->n_blocks - 1, r_cap_data_16->addr); + _lto4b(VIOSCSI_BLOCK_SIZE_CDROM, + r_cap_data_16->length); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, req_desc, + &resp_idx); + + dprintf("%s: writing resp to 0x%llx size %d at local " + "idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, &resp, resp_desc->len)) { + log_warnx("%s: unable to write OK resp status " + "data @ 0x%llx", __func__, resp_desc->addr); + free(r_cap_data); + goto out; + } + + /* Move index for r_cap_data_16 */ + resp_desc = vioscsi_next_ring_desc(desc, resp_desc, + &resp_idx); + + dprintf("%s: writing r_cap_data_16 to 0x%llx size %d " + "at local idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, r_cap_data_16, + resp_desc->len)) { + log_warnx("%s: unable to write read_cap_data_16" + " response to gpa @ 0x%llx", + __func__, resp_desc->addr); + free(r_cap_data); + goto out; + } + + free(r_cap_data_16); + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, req_desc, + req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + break; + case READ_COMMAND: + read_6 = (struct scsi_rw *)(req.cdb); + read_lba = ((read_6->addr[0] & SRW_TOPADDR) << 16 ) | + (read_6->addr[1] << 8) | read_6->addr[2]; + + log_debug("%s: READ Addr 0x%08x Len %d (%d)", + __func__, read_lba, read_6->length, + read_6->length * dev->max_xfer); + + /* check if lba is in range */ + if (read_lba > dev->n_blocks - 1) { + log_debug("%s: requested block out of range " + "req: %ud max: %lld", __func__, + read_lba, dev->n_blocks); + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_CHECK, + SKEY_ILLEGAL_REQUEST, + SENSE_LBA_OUT_OF_RANGE, + SENSE_DEFAULT_ASCQ); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to set ERR " + "status data @ 0x%llx", __func__, + resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + + goto next_msg; + } + + info = vioscsi_start_read(dev, read_lba, + read_6->length); + + if (info == NULL) { + log_warnx("%s: cannot alloc for read", + __func__); + goto out; + } + + /* read block */ + read_buf = vioscsi_finish_read(info); + + if (read_buf == NULL) { + vioscsi_free_info(info); + + log_warnx("%s: error reading position %ud", + __func__, read_lba); + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_CHECK, + SKEY_MEDIUM_ERROR, + SENSE_MEDIUM_NOT_PRESENT, + SENSE_DEFAULT_ASCQ); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to set ERR " + "status data @ 0x%llx", __func__, + resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + + goto next_msg; + } + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, req_desc, + &resp_idx); + + dprintf("%s: writing resp to 0x%llx size %d at local " + "idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, &resp, resp_desc->len)) { + log_warnx("%s: unable to write OK resp status " + "data @ 0x%llx", __func__, resp_desc->addr); + vioscsi_free_info(info); + goto out; + } + + /* Move index for read_buf */ + resp_desc = vioscsi_next_ring_desc(desc, resp_desc, + &resp_idx); + + dprintf("%s: writing read_buf to 0x%llx size %d at " + "local idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, read_buf, + resp_desc->len)) { + log_warnx("%s: unable to write read_buf" + " to gpa @ 0x%llx", __func__, + resp_desc->addr); + vioscsi_free_info(info); + goto out; + } + + vioscsi_free_info(info); + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, req_desc, + req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + + break; + case READ_BIG: + read_10 = (struct scsi_rw_big *)(req.cdb); + read_lba = _4btol(read_10->addr); + read_10_len = _2btol(read_10->length); + chunk_offset = 0; + + log_debug("%s: READ_10 Addr 0x%08x Len %d (%d)", + __func__, read_lba, read_10_len, + read_10_len * dev->max_xfer); + + /* check if lba is in range */ + if (read_lba > dev->n_blocks - 1) { + log_debug("%s: requested block out of range " + "req: %ud max: %lld", __func__, + read_lba, dev->n_blocks); + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_CHECK, + SKEY_ILLEGAL_REQUEST, + SENSE_LBA_OUT_OF_RANGE, + SENSE_DEFAULT_ASCQ); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to set ERR " + "status data @ 0x%llx", __func__, + resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + + goto next_msg; + } + + info = vioscsi_start_read(dev, read_lba, read_10_len); + + if (info == NULL) { + log_warnx("%s: cannot alloc for read", + __func__); + goto out; + } + + /* read block */ + read_buf = vioscsi_finish_read(info); + + if (read_buf == NULL) { + vioscsi_free_info(info); + + log_warnx("%s: error reading position %ud", + __func__, read_lba); + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_CHECK, + SKEY_MEDIUM_ERROR, + SENSE_MEDIUM_NOT_PRESENT, + SENSE_DEFAULT_ASCQ); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to set ERR " + "status data @ 0x%llx", __func__, + resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + + goto next_msg; + } + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, req_desc, + &resp_idx); + + dprintf("%s: writing resp to 0x%llx size %d at local " + "idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, &resp, resp_desc->len)) { + log_warnx("%s: unable to write OK resp status " + "data @ 0x%llx", __func__, resp_desc->addr); + vioscsi_free_info(info); + goto out; + } + + /* + * Perform possible chunking of writes of read_buf + * based on the segment length allocated by the host. + * At least one write will be performed. + * If chunk_offset == info->len, no more writes + */ + do { + /* Move index for read_buf */ + resp_desc = vioscsi_next_ring_desc(desc, + resp_desc, &resp_idx); + + dprintf("%s: writing read_buf to 0x%llx size " + "%d at local idx %d req_idx %d " + "global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, + read_buf + chunk_offset, resp_desc->len)) { + log_warnx("%s: unable to write read_buf" + " to gpa @ 0x%llx", __func__, + resp_desc->addr); + vioscsi_free_info(info); + goto out; + } + chunk_offset += resp_desc->len; + } while (chunk_offset < info->len); + + vioscsi_free_info(info); + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, req_desc, + req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + + break; + case INQUIRY: + inq = (struct scsi_inquiry *)(req.cdb); + inq_len = (uint16_t)_2btol(inq->length); + + log_debug("%s: INQ - EVPD %d PAGE_CODE 0x%08x LEN %d", + __func__, inq->flags & SI_EVPD, inq->pagecode, + inq_len); + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0); + + inq_data = calloc(1, sizeof(struct scsi_inquiry_data)); + + if (inq_data == NULL) { + log_warnx("%s: cannot alloc inq_data", + __func__); + goto out; + } + + inq_data->device = T_CDROM; + inq_data->dev_qual2 = SID_REMOVABLE; + /* Leave version zero to say we don't comply */ + inq_data->response_format = INQUIRY_RESPONSE_FORMAT; + inq_data->additional_length = SID_SCSI2_ALEN; + memcpy(inq_data->vendor, INQUIRY_VENDOR, + INQUIRY_VENDOR_LEN); + memcpy(inq_data->product, INQUIRY_PRODUCT, + INQUIRY_PRODUCT_LEN); + memcpy(inq_data->revision, INQUIRY_REVISION, + INQUIRY_REVISION_LEN); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, req_desc, + &resp_idx); + + dprintf("%s: writing resp to 0x%llx size %d at local " + "idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, &resp, resp_desc->len)) { + log_warnx("%s: unable to write OK resp status " + "data @ 0x%llx", __func__, resp_desc->addr); + free(inq_data); + goto out; + } + + /* Move index for inquiry_data */ + resp_desc = vioscsi_next_ring_desc(desc, resp_desc, + &resp_idx); + + dprintf("%s: writing inq_data to 0x%llx size %d at " + "local idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, inq_data, + resp_desc->len)) { + log_warnx("%s: unable to write inquiry" + " response to gpa @ 0x%llx", + __func__, resp_desc->addr); + free(inq_data); + goto out; + } + + free(inq_data); + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, req_desc, + req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + break; + case MODE_SENSE: + mode_sense = (struct scsi_mode_sense *)(req.cdb); + mode_page_ctl = mode_sense->page & SMS_PAGE_CTRL; + mode_page_code = mode_sense->page & SMS_PAGE_CODE; + + log_debug("%s: M_SENSE - DBD %d Page Ctrl 0x%x" + " Code 0x%x Len %u", + __func__, mode_sense->byte2 & SMS_DBD, + mode_page_ctl, mode_page_code, mode_sense->length); + + if (mode_page_ctl == SMS_PAGE_CTRL_CURRENT && + (mode_page_code == ERR_RECOVERY_PAGE || + mode_page_code == CDVD_CAPABILITIES_PAGE)) { + /* + * mode sense header is 4 bytes followed + * by a variable page + * ERR_RECOVERY_PAGE is 12 bytes + * CDVD_CAPABILITIES_PAGE is 27 bytes + */ + switch(mode_page_code) { + case ERR_RECOVERY_PAGE: + mode_reply_len = 16; + mode_reply = + (uint8_t*)calloc(mode_reply_len, + sizeof(uint8_t)); + if (mode_reply == NULL) + goto out; + + /* set the page header */ + *mode_reply = mode_reply_len - 1; + *(mode_reply + 1) = + MODE_MEDIUM_TYPE_CODE; + + /* set the page data, 7.3.2.1 mmc-5 */ + *(mode_reply + 4) = + MODE_ERR_RECOVERY_PAGE_CODE; + *(mode_reply + 5) = + MODE_ERR_RECOVERY_PAGE_LEN; + *(mode_reply + 7) = + MODE_READ_RETRY_COUNT; + break; + case CDVD_CAPABILITIES_PAGE: + mode_reply_len = 31; + mode_reply = + (uint8_t*)calloc(mode_reply_len, + sizeof(uint8_t)); + if (mode_reply == NULL) + goto out; + + /* set the page header */ + *mode_reply = mode_reply_len - 1; + *(mode_reply + 1) = + MODE_MEDIUM_TYPE_CODE; + + /* set the page data, 6.3.11 mmc-3 */ + *(mode_reply + 4) = + MODE_CDVD_CAP_PAGE_CODE; + *(mode_reply + 5) = mode_reply_len - 6; + *(mode_reply + 6) = + MODE_CDVD_CAP_READ_CODE; + _lto2b(MODE_CDVD_CAP_NUM_LEVELS, + mode_reply + 14); + break; + default: + goto mode_sense_error; + break; + } + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + dprintf("%s: writing resp to 0x%llx size %d " + "at local idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to write OK" + " resp status data @ 0x%llx", + __func__, resp_desc->addr); + free(mode_reply); + goto out; + } + + /* Move index for mode_reply */ + resp_desc = vioscsi_next_ring_desc(desc, + resp_desc, &resp_idx); + + dprintf("%s: writing mode_reply to 0x%llx " + "size %d at local idx %d req_idx %d " + "global_idx %d",__func__, resp_desc->addr, + resp_desc->len, resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, mode_reply, + resp_desc->len)) { + log_warnx("%s: unable to write " + "mode_reply to gpa @ 0x%llx", + __func__, resp_desc->addr); + free(mode_reply); + goto out; + } + + free(mode_reply); + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + + } else { +mode_sense_error: + /* send back un-supported */ + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_CHECK, + SKEY_ILLEGAL_REQUEST, + SENSE_ILLEGAL_CDB_FIELD, + SENSE_DEFAULT_ASCQ); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to set ERR " + "status data @ 0x%llx", __func__, + resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + } + break; + case MODE_SENSE_BIG: + mode_sense_10 = (struct scsi_mode_sense_big *)(req.cdb); + mode_page_ctl = mode_sense_10->page & SMS_PAGE_CTRL; + mode_page_code = mode_sense_10->page & SMS_PAGE_CODE; + mode_sense_len = + (uint16_t)_2btol(mode_sense_10->length); + + log_debug("%s: M_SENSE_10 - DBD %d Page Ctrl 0x%x" + " Code 0x%x Len %u", + __func__, mode_sense_10->byte2 & SMS_DBD, + mode_page_ctl, mode_page_code, mode_sense_len); + + if (mode_page_ctl == SMS_PAGE_CTRL_CURRENT && + (mode_page_code == ERR_RECOVERY_PAGE || + mode_page_code == CDVD_CAPABILITIES_PAGE)) { + /* + * mode sense header is 8 bytes followed + * by a variable page + * ERR_RECOVERY_PAGE is 12 bytes + * CDVD_CAPABILITIES_PAGE is 27 bytes + */ + switch(mode_page_code) { + case ERR_RECOVERY_PAGE: + mode_reply_len = 20; + mode_reply = + (uint8_t*)calloc(mode_reply_len, + sizeof(uint8_t)); + if (mode_reply == NULL) + goto out; + + /* set the page header */ + _lto2b(mode_reply_len - 2, mode_reply); + *(mode_reply + 2) = + MODE_MEDIUM_TYPE_CODE; + + /* set the page data, 7.3.2.1 mmc-5 */ + *(mode_reply + 8) = + MODE_ERR_RECOVERY_PAGE_CODE; + *(mode_reply + 9) = + MODE_ERR_RECOVERY_PAGE_LEN; + *(mode_reply + 11) = + MODE_READ_RETRY_COUNT; + break; + case CDVD_CAPABILITIES_PAGE: + mode_reply_len = 35; + mode_reply = + (uint8_t*)calloc(mode_reply_len, + sizeof(uint8_t)); + if (mode_reply == NULL) + goto out; + + /* set the page header */ + _lto2b(mode_reply_len - 2, mode_reply); + *(mode_reply + 2) = + MODE_MEDIUM_TYPE_CODE; + + /* set the page data, 6.3.11 mmc-3 */ + *(mode_reply + 8) = + MODE_CDVD_CAP_PAGE_CODE; + *(mode_reply + 9) = mode_reply_len - 6; + *(mode_reply + 10) = + MODE_CDVD_CAP_READ_CODE; + _lto2b(MODE_CDVD_CAP_NUM_LEVELS, + mode_reply + 18); + break; + default: + goto mode_sense_10_error; + break; + } + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + dprintf("%s: writing resp to 0x%llx size %d " + "at local idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to write OK" + " resp status data @ 0x%llx", + __func__, resp_desc->addr); + free(mode_reply); + goto out; + } + + /* Move index for mode_reply */ + resp_desc = vioscsi_next_ring_desc(desc, + resp_desc, &resp_idx); + + dprintf("%s: writing mode_reply to 0x%llx " + "size %d at local idx %d req_idx %d " + "global_idx %d",__func__, resp_desc->addr, + resp_desc->len, resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, mode_reply, + resp_desc->len)) { + log_warnx("%s: unable to write " + "mode_reply to gpa @ 0x%llx", + __func__, resp_desc->addr); + free(mode_reply); + goto out; + } + + free(mode_reply); + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + + } else { +mode_sense_10_error: + /* send back un-supported */ + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_CHECK, + SKEY_ILLEGAL_REQUEST, + SENSE_ILLEGAL_CDB_FIELD, + SENSE_DEFAULT_ASCQ); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to set ERR " + "status data @ 0x%llx", __func__, + resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + } + break; + case GET_EVENT_STATUS_NOTIFICATION: + gesn = (struct scsi_gesn *)(req.cdb); + log_debug("%s: GESN Method %s", __func__, + gesn->byte2 ? "Polling" : "Asynchronous"); + + if (gesn->byte2 == 0) { + /* we don't support asynchronous */ + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_CHECK, + SKEY_ILLEGAL_REQUEST, + SENSE_ILLEGAL_CDB_FIELD, + SENSE_DEFAULT_ASCQ); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to set ERR " + "status data @ 0x%llx", __func__, + resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + goto out; + } + memset(gesn_reply, 0, sizeof(gesn_reply)); + gesn_event_header = + (struct scsi_gesn_event_header *)(gesn_reply); + gesn_power_event = + (struct scsi_gesn_power_event *)(gesn_reply + 4); + /* set event header length and notification */ + _lto2b(GESN_HEADER_LEN, gesn_event_header->length); + gesn_event_header->notification = + GESN_NOTIFY_POWER_MGMT; + gesn_event_header->supported_event = + GESN_EVENT_POWER_MGMT; + + /* set event descriptor */ + gesn_power_event->event_code = GESN_CODE_NOCHG; + if (dev->locked) + gesn_power_event->status = GESN_STATUS_ACTIVE; + else + gesn_power_event->status = GESN_STATUS_IDLE; + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, req_desc, + &resp_idx); + + dprintf("%s: writing resp to 0x%llx size %d at local " + "idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, &resp, resp_desc->len)) { + log_warnx("%s: unable to write OK resp status " + "data @ 0x%llx", __func__, resp_desc->addr); + free(inq_data); + goto out; + } + + /* Move index for gesn_reply */ + resp_desc = vioscsi_next_ring_desc(desc, resp_desc, + &resp_idx); + + dprintf("%s: writing gesn_reply to 0x%llx size %d at " + "local idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, gesn_reply, + resp_desc->len)) { + log_warnx("%s: unable to write gesn_reply" + " response to gpa @ 0x%llx", + __func__, resp_desc->addr); + free(inq_data); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, req_desc, + req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + + break; + case READ_DISC_INFORMATION: + read_disc = + (struct scsi_read_disc_information *)(req.cdb); + log_debug("%s: Disc Info %x", __func__, + read_disc->byte2); + + /* send back unsupported */ + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_CHECK, + SKEY_ILLEGAL_REQUEST, + SENSE_ILLEGAL_CDB_FIELD, + SENSE_DEFAULT_ASCQ); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to set ERR " + "status data @ 0x%llx", __func__, + resp_desc->addr); + goto out; + } + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + break; + case GET_CONFIGURATION: + get_configuration = + (struct scsi_get_configuration *)(req.cdb); + get_conf_feature = + (uint16_t)_2btol(get_configuration->feature); + get_conf_len = + (uint16_t)_2btol(get_configuration->length); + log_debug("%s: Conf RT %x Feature %d Len %d", + __func__, get_configuration->byte2, + get_conf_feature, get_conf_len); + + get_conf_reply = (uint8_t*)calloc(G_CONFIG_REPLY_SIZE, + sizeof(uint8_t)); + + if (get_conf_reply == NULL) + goto out; + + /* + * Use MMC-5 6.6 for structure and + * MMC-5 5.2 to send back: + * feature header - 8 bytes + * feature descriptor for profile list - 8 bytes + * feature descriptor for core feature - 12 bytes + * feature descriptor for morphing feature - 8 bytes + * feature descriptor for removable media - 8 bytes + * feature descriptor for random read feature - 12 bytes + */ + + config_feature_header = + (struct scsi_config_feature_header *)(get_conf_reply); + config_generic_desc = + (struct scsi_config_generic_descriptor *)(get_conf_reply + 8); + config_profile_desc = + (struct scsi_config_profile_descriptor *)(get_conf_reply + 12); + config_core_desc = + (struct scsi_config_core_descriptor *)(get_conf_reply + 16); + config_morphing_desc = + (struct scsi_config_morphing_descriptor *)(get_conf_reply + 28); + config_remove_media_desc = + (struct scsi_config_remove_media_descriptor *)(get_conf_reply + 36); + config_random_read_desc = + (struct scsi_config_random_read_descriptor *)(get_conf_reply + 44); + + /* set size to be get_conf_reply - size field */ + _lto4b(G_CONFIG_REPLY_SIZE_HEX, + config_feature_header->length); + /* set current profile to be non-conforming */ + _lto2b(CONFIG_PROFILE_NON_CONFORM, + config_feature_header->current_profile); + + /* fill out profile list feature */ + _lto2b(CONFIG_FEATURE_CODE_PROFILE, + config_generic_desc->feature_code); + config_generic_desc->byte3 = CONFIG_PROFILELIST_BYTE3; + config_generic_desc->length = CONFIG_PROFILELIST_LENGTH; + /* fill out profile descriptor for NON_COFORM */ + _lto2b(CONFIG_PROFILE_NON_CONFORM, + config_profile_desc->profile_number); + config_profile_desc->byte3 = CONFIG_PROFILE_BYTE3; + + /* fill out core feature */ + _lto2b(CONFIG_FEATURE_CODE_CORE, + config_core_desc->feature_code); + config_core_desc->byte3 = CONFIG_CORE_BYTE3; + config_core_desc->length = CONFIG_CORE_LENGTH; + _lto4b(CONFIG_CORE_PHY_SCSI, config_core_desc->phy_std); + + /* fill out morphing feature */ + _lto2b(CONFIG_FEATURE_CODE_MORPHING, + config_morphing_desc->feature_code); + config_morphing_desc->byte3 = CONFIG_MORPHING_BYTE3; + config_morphing_desc->length = CONFIG_MORPHING_LENGTH; + config_morphing_desc->byte5 = CONFIG_MORPHING_BYTE5; + + /* fill out removable media feature */ + _lto2b(CONFIG_FEATURE_CODE_REMOVE_MEDIA, + config_remove_media_desc->feature_code); + config_remove_media_desc->byte3 = + CONFIG_REMOVE_MEDIA_BYTE3; + config_remove_media_desc->length = + CONFIG_REMOVE_MEDIA_LENGTH; + config_remove_media_desc->byte5 = + CONFIG_REMOVE_MEDIA_BYTE5; + + /* fill out random read feature */ + _lto2b(CONFIG_FEATURE_CODE_RANDOM_READ, + config_random_read_desc->feature_code); + config_random_read_desc->byte3 = + CONFIG_RANDOM_READ_BYTE3; + config_random_read_desc->length = + CONFIG_RANDOM_READ_LENGTH; + if (dev->sz >= UINT32_MAX) + _lto4b(UINT32_MAX, + config_random_read_desc->block_size); + else + _lto4b(dev->n_blocks - 1, + config_random_read_desc->block_size); + _lto2b(CONFIG_RANDOM_READ_BLOCKING_TYPE, + config_random_read_desc->blocking_type); + + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + dprintf("%s: writing resp to 0x%llx size %d at local " + "idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to set ERR " + "status data @ 0x%llx", __func__, + resp_desc->addr); + free(get_conf_reply); + goto out; + } + + /* Move index for get_conf_reply */ + resp_desc = vioscsi_next_ring_desc(desc, resp_desc, + &resp_idx); + + dprintf("%s: writing get_conf_reply to 0x%llx size %d " + "at local idx %d req_idx %d global_idx %d", + __func__, resp_desc->addr, resp_desc->len, + resp_idx, req_idx, idx); + + if (write_mem(resp_desc->addr, get_conf_reply, + resp_desc->len)) { + log_warnx("%s: unable to write get_conf_reply" + " response to gpa @ 0x%llx", + __func__, resp_desc->addr); + free(get_conf_reply); + goto out; + } + + free(get_conf_reply); + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + break; + case MECHANISM_STATUS: + mech_status = (struct scsi_mechanism_status *)(req.cdb); + mech_status_len = (uint16_t)_2btol(mech_status->length); + log_debug("%s: MECH_STATUS Len %u", __func__, + mech_status_len); + + mech_status_header = + calloc(1, sizeof(*mech_status_header)); + + if (mech_status_header == NULL) + goto out; + + /* return a 0 header since we are not a changer */ + vioscsi_prepare_resp(&resp, + VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0); + + /* Move index for response */ + resp_desc = vioscsi_next_ring_desc(desc, + req_desc, &resp_idx); + + if (write_mem(resp_desc->addr, &resp, + resp_desc->len)) { + log_warnx("%s: unable to set ERR " + "status data @ 0x%llx", __func__, + resp_desc->addr); + free(mech_status_header); + goto out; + } + + /* Move index for mech_status_header */ + resp_desc = vioscsi_next_ring_desc(desc, resp_desc, + &resp_idx); + + if (write_mem(resp_desc->addr, mech_status_header, + resp_desc->len)) { + log_warnx("%s: unable to write " + "mech_status_header response to " + "gpa @ 0x%llx", + __func__, resp_desc->addr); + free(mech_status_header); + goto out; + } + + free(mech_status_header); + + ret = 1; + dev->cfg.isr_status = 1; + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, + req_desc, req_idx); + + if (write_mem(q_gpa, vr, vr_sz)) { + log_warnx("%s: error writing vioring", + __func__); + } + break; + default: + log_warnx("%s: unsupported opcode 0x%02x,%s", + __func__, req.cdb[0], vioscsi_op_names(req.cdb[0])); + /* Move ring indexes */ + vioscsi_next_ring_item(dev, avail, used, req_desc, + req_idx); + + break; + } +next_msg: + /* Increment to the next queue slot */ + idx = (idx + 1) & VIOSCSI_QUEUE_MASK; + } +out: + free(vr); + return (ret); +} diff --git a/usr.sbin/vmd/vioscsi.h b/usr.sbin/vmd/vioscsi.h new file mode 100644 index 00000000000..ffaa21f7a0a --- /dev/null +++ b/usr.sbin/vmd/vioscsi.h @@ -0,0 +1,231 @@ +/* $OpenBSD: vioscsi.h,v 1.1 2018/01/03 05:39:56 ccardenas Exp $ */ + +/* + * Copyright (c) 2017 Carlos Cardenas + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Constants */ +#define VIOSCSI_SEG_MAX 17 +#define VIOSCSI_CDB_LEN 32 +#define VIOSCSI_SENSE_LEN 96 + +#define VIOSCSI_NUM_QUEUES 1 +#define VIOSCSI_CMD_PER_LUN 1 +#define VIOSCSI_MAX_TARGET 1 +#define VIOSCSI_MAX_LUN 1 + +#define VIOSCSI_BLOCK_SIZE_CDROM 2048 + +#define READ_TOC_START_TRACK 0x01 +#define READ_TOC_LAST_TRACK 0x01 +#define READ_TOC_LEAD_OUT_TRACK 0xaa +#define READ_TOC_ADR_CTL 0x14 + +#define SENSE_DEFAULT_ASCQ 0x00 +#define SENSE_LBA_OUT_OF_RANGE 0x21 +#define SENSE_ILLEGAL_CDB_FIELD 0x24 +#define SENSE_MEDIUM_NOT_PRESENT 0x3a + +#define INQUIRY_RESPONSE_FORMAT 0x02 +#define INQUIRY_VENDOR "OpenBSD " +#define INQUIRY_VENDOR_LEN 8 +#define INQUIRY_PRODUCT "VMM CD-ROM " +#define INQUIRY_PRODUCT_LEN 16 +#define INQUIRY_REVISION "001 " +#define INQUIRY_REVISION_LEN 4 + +#define MODE_MEDIUM_TYPE_CODE 0x70 +#define MODE_ERR_RECOVERY_PAGE_CODE 0x01 +#define MODE_ERR_RECOVERY_PAGE_LEN 0x0a +#define MODE_READ_RETRY_COUNT 0x05 +#define MODE_CDVD_CAP_PAGE_CODE 0x2a +#define MODE_CDVD_CAP_READ_CODE 0x08 +#define MODE_CDVD_CAP_NUM_LEVELS 0x02 + +#define GESN_HEADER_LEN 0x06 + +#define G_CONFIG_REPLY_SIZE 56 +#define G_CONFIG_REPLY_SIZE_HEX 0x0034 + +/* Opcodes not defined in scsi */ +#define GET_EVENT_STATUS_NOTIFICATION 0x4a +#define GET_CONFIGURATION 0x46 +#define READ_DISC_INFORMATION 0x51 +#define MECHANISM_STATUS 0xbd + +/* Sizes for reply structures */ +#define TOC_DATA_SIZE 20 +#define GESN_SIZE 8 +#define RESP_SENSE_LEN 14 + +/* Structures for Opcodes defined locally */ +struct scsi_mechanism_status { + u_int8_t opcode; + u_int8_t unused[7]; + u_int8_t length[2]; + u_int8_t unused1; + u_int8_t control; +}; + +struct scsi_mechanism_status_header { + u_int8_t byte1; + u_int8_t byte2; + u_int8_t addr[3]; + u_int8_t num_slots; + u_int8_t slot_len[2]; +}; + +struct scsi_read_disc_information { + u_int8_t opcode; + u_int8_t byte2; + u_int8_t unused[5]; + u_int8_t length[2]; + u_int8_t control; +}; + +struct scsi_gesn { + u_int8_t opcode; + u_int8_t byte2; + u_int8_t unused[2]; + u_int8_t notify_class; + u_int8_t unused1[2]; + u_int8_t length[2]; + u_int8_t control; +}; + +struct scsi_gesn_event_header { + u_int8_t length[2]; + u_int8_t notification; +#define GESN_NOTIFY_NONE 0x0 +#define GESN_NOTIFY_OP_CHANGE 0x1 +#define GESN_NOTIFY_POWER_MGMT 0x2 +#define GESN_NOTIFY_EXT_REQUEST 0x3 +#define GESN_NOTIFY_MEDIA 0x4 +#define GESN_NOTIFY_MULTIPLE_HOSTS 0x5 +#define GESN_NOTIFY_DEVICE_BUSY 0x6 +#define GESN_NOTIFY_RESERVED 0x7 + u_int8_t supported_event; +#define GESN_EVENT_NONE 0x1 +#define GESN_EVENT_OP_CHANGE 0x2 +#define GESN_EVENT_POWER_MGMT 0x4 +#define GESN_EVENT_EXT_REQUEST 0x8 +#define GESN_EVENT_MEDIA 0x10 +#define GESN_EVENT_MULTIPLE_HOSTS 0x20 +#define GESN_EVENT_DEVICE_BUSY 0x40 +#define GESN_EVENT_RESERVED 0x80 +}; + +struct scsi_gesn_power_event { + u_int8_t event_code; +#define GESN_CODE_NOCHG 0x0 +#define GESN_CODE_PWRCHG_SUCCESS 0x1 +#define GESN_CODE_PWRCHG_FAIL 0x2 +#define GESN_CODE_RESERVED 0x3 + u_int8_t status; +#define GESN_STATUS_RESERVED 0x0 +#define GESN_STATUS_ACTIVE 0x1 +#define GESN_STATUS_IDLE 0x2 +#define GESN_STATUS_STANDBY 0x3 +#define GESN_STATUS_SLEEP 0x4 + u_int8_t unused[2]; +}; + +struct scsi_get_configuration { + u_int8_t opcode; + u_int8_t byte2; + u_int8_t feature[2]; + u_int8_t unused[3]; + u_int8_t length[2]; + u_int8_t control; +}; + +struct scsi_config_feature_header { + u_int8_t length[4]; + u_int8_t unused[2]; + u_int8_t current_profile[2]; + /* Complete Profile List in MMC-5, 5.3.1, Table 89 */ +#define CONFIG_PROFILE_RESERVED 0x0000 +#define CONFIG_PROFILE_CD_ROM 0x0008 +#define CONFIG_PROFILE_NON_CONFORM 0xffff +}; + +struct scsi_config_generic_descriptor { + u_int8_t feature_code[2]; + /* Complete Feature Code List in MMC-5, 5.2.3, Table 86 */ +#define CONFIG_FEATURE_CODE_PROFILE 0x0000 +#define CONFIG_FEATURE_CODE_CORE 0x0001 +#define CONFIG_FEATURE_CODE_MORPHING 0x0002 +#define CONFIG_FEATURE_CODE_REMOVE_MEDIA 0x0004 +#define CONFIG_FEATURE_CODE_RANDOM_READ 0x0010 + u_int8_t byte3; +#define CONFIG_PROFILELIST_BYTE3 0x03 + u_int8_t length; +#define CONFIG_PROFILELIST_LENGTH 0x04 +}; + +struct scsi_config_profile_descriptor { + u_int8_t profile_number[2]; + u_int8_t byte3; +#define CONFIG_PROFILE_BYTE3 0x01 + u_int8_t unused; +}; + +struct scsi_config_core_descriptor { + u_int8_t feature_code[2]; + u_int8_t byte3; +#define CONFIG_CORE_BYTE3 0x11 + u_int8_t length; +#define CONFIG_CORE_LENGTH 0x08 + u_int8_t phy_std[4]; + /* Complete PHYs List in MMC-5, 5.3.2, Table 91 */ +#define CONFIG_CORE_PHY_SCSI 0x00000001 + u_int8_t unused[4]; +}; + +struct scsi_config_morphing_descriptor { + u_int8_t feature_code[2]; + u_int8_t byte3; +#define CONFIG_MORPHING_BYTE3 0x07 + u_int8_t length; +#define CONFIG_MORPHING_LENGTH 0x04 + /* OCE (bit 1), always set and ASYNC (bit 0) Bit */ + u_int8_t byte5; +#define CONFIG_MORPHING_BYTE5 0x2 + u_int8_t unused[3]; +}; + +struct scsi_config_remove_media_descriptor { + u_int8_t feature_code[2]; + u_int8_t byte3; +#define CONFIG_REMOVE_MEDIA_BYTE3 0x03 + u_int8_t length; +#define CONFIG_REMOVE_MEDIA_LENGTH 0x04 + /* Ejection Type */ + u_int8_t byte5; +#define CONFIG_REMOVE_MEDIA_BYTE5 0x09 + u_int8_t unused[3]; +}; + +struct scsi_config_random_read_descriptor { + u_int8_t feature_code[2]; + u_int8_t byte3; +#define CONFIG_RANDOM_READ_BYTE3 0x03 + u_int8_t length; +#define CONFIG_RANDOM_READ_LENGTH 0x08 + u_int8_t block_size[4]; + u_int8_t blocking_type[2]; +#define CONFIG_RANDOM_READ_BLOCKING_TYPE 0x0010 + u_int8_t unused[2]; +}; diff --git a/usr.sbin/vmd/virtio.c b/usr.sbin/vmd/virtio.c index 00a11a583b1..c7c28cdc09a 100644 --- a/usr.sbin/vmd/virtio.c +++ b/usr.sbin/vmd/virtio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: virtio.c,v 1.54 2017/09/17 23:07:56 pd Exp $ */ +/* $OpenBSD: virtio.c,v 1.55 2018/01/03 05:39:56 ccardenas Exp $ */ /* * Copyright (c) 2015 Mike Larkin @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,7 @@ #include "vmd.h" #include "vmm.h" #include "virtio.h" +#include "vioscsi.h" #include "loadfile.h" #include "atomicio.h" @@ -49,6 +51,7 @@ extern char *__progname; struct viornd_dev viornd; struct vioblk_dev *vioblk; struct vionet_dev *vionet; +struct vioscsi_dev *vioscsi; struct vmmci_dev vmmci; int nr_vionet; @@ -62,14 +65,6 @@ int nr_vioblk; #define VMMCI_F_ACK (1<<1) #define VMMCI_F_SYNCRTC (1<<2) -struct ioinfo { - uint8_t *buf; - ssize_t len; - off_t offset; - int fd; - int error; -}; - const char * vioblk_cmd_name(uint32_t type) { @@ -1666,7 +1661,8 @@ vmmci_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr, } void -virtio_init(struct vmd_vm *vm, int *child_disks, int *child_taps) +virtio_init(struct vmd_vm *vm, int child_cdrom, int *child_disks, + int *child_taps) { struct vmop_create_params *vmc = &vm->vm_params; struct vm_create_params *vcp = &vmc->vmc_params; @@ -1830,6 +1826,51 @@ virtio_init(struct vmd_vm *vm, int *child_disks, int *child_taps) } } + /* vioscsi cdrom */ + if (strlen(vcp->vcp_cdrom)) { + vioscsi = calloc(1, sizeof(struct vioscsi_dev)); + if (vioscsi == NULL) { + log_warn("%s: calloc failure allocating vioscsi", + __progname); + return; + } + + if (pci_add_device(&id, PCI_VENDOR_QUMRANET, + PCI_PRODUCT_QUMRANET_VIO_SCSI, + PCI_CLASS_MASS_STORAGE, + PCI_SUBCLASS_MASS_STORAGE_SCSI, + PCI_VENDOR_OPENBSD, + PCI_PRODUCT_VIRTIO_SCSI, 1, NULL)) { + log_warnx("%s: can't add PCI vioscsi device", + __progname); + return; + } + + if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, vioscsi_io, vioscsi)) { + log_warnx("%s: can't add bar for vioscsi device", + __progname); + return; + } + + for ( i = 0; i < VIRTIO_MAX_QUEUES; i++) { + vioscsi->vq[i].qs = VIOSCSI_QUEUE_SIZE; + vioscsi->vq[i].vq_availoffset = + sizeof(struct vring_desc) * VIOSCSI_QUEUE_SIZE; + vioscsi->vq[i].vq_usedoffset = VIRTQUEUE_ALIGN( + sizeof(struct vring_desc) * VIOSCSI_QUEUE_SIZE + + sizeof(uint16_t) * (2 + VIOSCSI_QUEUE_SIZE)); + vioscsi->vq[i].last_avail = 0; + } + sz = lseek(child_cdrom, 0, SEEK_END); + vioscsi->fd = child_cdrom; + vioscsi->locked = 0; + vioscsi->lba = 0; + vioscsi->sz = sz; + vioscsi->n_blocks = sz >> 11; /* num of 2048 blocks in file */ + vioscsi->max_xfer = VIOSCSI_BLOCK_SIZE_CDROM; + vioscsi->pci_id = id; + } + /* virtio control device */ if (pci_add_device(&id, PCI_VENDOR_OPENBSD, PCI_PRODUCT_OPENBSD_CONTROL, @@ -1990,7 +2031,40 @@ vioblk_restore(int fd, struct vm_create_params *vcp, int *child_disks) } int -virtio_restore(int fd, struct vmd_vm *vm, int *child_disks, int *child_taps) +vioscsi_restore(int fd, struct vm_create_params *vcp, int child_cdrom) +{ + off_t sz; + + vioscsi = calloc(1, sizeof(struct vioscsi_dev)); + if (vioscsi == NULL) { + log_warn("%s: calloc failure allocating vioscsi", __progname); + return (-1); + } + + log_debug("%s: receiving vioscsi", __func__); + + if (atomicio(read, fd, vioscsi, sizeof(struct vioscsi_dev)) != + sizeof(struct vioscsi_dev)) { + log_warnx("%s: error reading vioscsi from fd", __func__); + return (-1); + } + + sz = lseek(child_cdrom, 0, SEEK_END); + + if (pci_set_bar_fn(vioscsi->pci_id, 0, vioscsi_io, NULL)) { + log_warnx("%s: can't set bar fn for vmm control device", + __progname); + return (-1); + } + + vioscsi->fd = child_cdrom; + + return (0); +} + +int +virtio_restore(int fd, struct vmd_vm *vm, int child_cdrom, int *child_disks, + int *child_taps) { struct vmop_create_params *vmc = &vm->vm_params; struct vm_create_params *vcp = &vmc->vmc_params; @@ -2002,6 +2076,9 @@ virtio_restore(int fd, struct vmd_vm *vm, int *child_disks, int *child_taps) if ((ret = vioblk_restore(fd, vcp, child_disks)) == -1) return ret; + if ((ret = vioscsi_restore(fd, vcp, child_cdrom)) == -1) + return ret; + if ((ret = vionet_restore(fd, vm, child_taps)) == -1) return ret; @@ -2059,6 +2136,18 @@ vioblk_dump(int fd) return (0); } +int +vioscsi_dump(int fd) +{ + log_debug("%s: sending vioscsi", __func__); + if (atomicio(vwrite, fd, &vioscsi, sizeof(vioscsi)) != + sizeof(vioscsi)) { + log_warnx("%s: error writing vioscsi to fd", __func__); + return (-1); + } + return (0); +} + int virtio_dump(int fd) { @@ -2070,6 +2159,9 @@ virtio_dump(int fd) if ((ret = vioblk_dump(fd)) == -1) return ret; + if ((ret = vioscsi_dump(fd)) == -1) + return ret; + if ((ret = vionet_dump(fd)) == -1) return ret; diff --git a/usr.sbin/vmd/virtio.h b/usr.sbin/vmd/virtio.h index c7977d5e415..55640d6ab59 100644 --- a/usr.sbin/vmd/virtio.h +++ b/usr.sbin/vmd/virtio.h @@ -1,4 +1,4 @@ -/* $OpenBSD: virtio.h,v 1.21 2017/09/17 23:07:56 pd Exp $ */ +/* $OpenBSD: virtio.h,v 1.22 2018/01/03 05:39:56 ccardenas Exp $ */ /* * Copyright (c) 2015 Mike Larkin @@ -28,6 +28,9 @@ #define VIOBLK_QUEUE_SIZE 128 #define VIOBLK_QUEUE_MASK (VIOBLK_QUEUE_SIZE - 1) +#define VIOSCSI_QUEUE_SIZE 128 +#define VIOSCSI_QUEUE_MASK (VIOSCSI_QUEUE_SIZE - 1) + #define VIONET_QUEUE_SIZE 128 #define VIONET_QUEUE_MASK (VIONET_QUEUE_SIZE - 1) @@ -35,8 +38,13 @@ #define VMMCI_TIMEOUT 3 #define VMMCI_SHUTDOWN_TIMEOUT 30 -/* All the devices we support have either 1 or 2 queues */ -#define VIRTIO_MAX_QUEUES 2 +/* All the devices we support have either 1, 2 or 3 queues */ +/* viornd - 1 queue + * vioblk - 1 queue + * vionet - 2 queues + * vioscsi - 3 queues + */ +#define VIRTIO_MAX_QUEUES 3 /* * This struct stores notifications from a virtio driver. There is @@ -111,6 +119,31 @@ struct vioblk_dev { uint8_t pci_id; }; +/* vioscsi will use at least 3 queues - 5.6.2 Virtqueues + * Current implementation will use 3 + * 0 - control + * 1 - event + * 2 - requests + */ +struct vioscsi_dev { + struct virtio_io_cfg cfg; + + struct virtio_vq_info vq[VIRTIO_MAX_QUEUES]; + + /* is the device locked */ + int locked; + int fd; + /* size of iso file in bytes */ + uint64_t sz; + /* last block address read */ + uint64_t lba; + /* number of blocks represented in iso */ + uint64_t n_blocks; + uint32_t max_xfer; + + uint8_t pci_id; +}; + struct vionet_dev { pthread_mutex_t mutex; struct event event; @@ -167,10 +200,18 @@ struct vmmci_dev { uint8_t pci_id; }; +struct ioinfo { + uint8_t *buf; + ssize_t len; + off_t offset; + int fd; + int error; +}; + /* virtio.c */ -void virtio_init(struct vmd_vm *, int *, int *); +void virtio_init(struct vmd_vm *, int, int *, int *); int virtio_dump(int); -int virtio_restore(int, struct vmd_vm *, int *, int *); +int virtio_restore(int, struct vmd_vm *, int, int *, int *); uint32_t vring_size(uint32_t); int virtio_rnd_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t); @@ -205,6 +246,14 @@ void vmmci_ack(unsigned int); void vmmci_timeout(int, short, void *); const char *vioblk_cmd_name(uint32_t); +int vioscsi_dump(int); +int vioscsi_restore(int, struct vm_create_params *, int); /* dhcp.c */ ssize_t dhcp_request(struct vionet_dev *, char *, size_t, char **); + +/* vioscsi.c */ +int vioscsi_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t); +void vioscsi_update_qs(struct vioscsi_dev *); +void vioscsi_update_qa(struct vioscsi_dev *); +int vioscsi_notifyq(struct vioscsi_dev *); diff --git a/usr.sbin/vmd/vm.c b/usr.sbin/vmd/vm.c index 05292be8f18..32c5c52454d 100644 --- a/usr.sbin/vmd/vm.c +++ b/usr.sbin/vmd/vm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm.c,v 1.30 2017/11/29 02:46:10 mlarkin Exp $ */ +/* $OpenBSD: vm.c,v 1.31 2018/01/03 05:39:56 ccardenas Exp $ */ /* * Copyright (c) 2015 Mike Larkin @@ -65,7 +65,8 @@ io_fn_t ioports_map[MAX_PORTS]; -int run_vm(int *, int *, struct vmop_create_params *, struct vcpu_reg_state *); +int run_vm(int, int *, int *, struct vmop_create_params *, + struct vcpu_reg_state *); void vm_dispatch_vmm(int, short, void *); void *event_thread(void *); void *vcpu_run_loop(void *); @@ -74,8 +75,8 @@ int vcpu_reset(uint32_t, uint32_t, struct vcpu_reg_state *); void create_memory_map(struct vm_create_params *); int alloc_guest_mem(struct vm_create_params *); int vmm_create_vm(struct vm_create_params *); -void init_emulated_hw(struct vmop_create_params *, int *, int *); -void restore_emulated_hw(struct vm_create_params *,int , int *, int *); +void init_emulated_hw(struct vmop_create_params *, int, int *, int *); +void restore_emulated_hw(struct vm_create_params *, int, int *, int *,int); void vcpu_exit_inout(struct vm_run_params *); uint8_t vcpu_exit_pci(struct vm_run_params *); int vcpu_pic_intr(uint32_t, uint32_t, uint8_t); @@ -359,7 +360,7 @@ start_vm(struct vmd_vm *vm, int fd) if (vm->vm_received) { restore_emulated_hw(vcp, vm->vm_receive_fd, nicfds, - vm->vm_disks); + vm->vm_disks, vm->vm_cdrom); mc146818_start(); restore_mem(vm->vm_receive_fd, vcp); } @@ -368,7 +369,7 @@ start_vm(struct vmd_vm *vm, int fd) fatal("setup vm pipe"); /* Execute the vcpu run loop(s) for this VM */ - ret = run_vm(vm->vm_disks, nicfds, &vm->vm_params, &vrs); + ret = run_vm(vm->vm_cdrom, vm->vm_disks, nicfds, &vm->vm_params, &vrs); return (ret); } @@ -897,8 +898,8 @@ vmm_create_vm(struct vm_create_params *vcp) * Initializes the userspace hardware emulation */ void -init_emulated_hw(struct vmop_create_params *vmc, int *child_disks, - int *child_taps) +init_emulated_hw(struct vmop_create_params *vmc, int child_cdrom, + int *child_disks, int *child_taps) { struct vm_create_params *vcp = &vmc->vmc_params; int i; @@ -951,7 +952,7 @@ init_emulated_hw(struct vmop_create_params *vmc, int *child_disks, pci_init(); /* Initialize virtio devices */ - virtio_init(current_vm, child_disks, child_taps); + virtio_init(current_vm, child_cdrom, child_disks, child_taps); } /* * restore_emulated_hw @@ -960,7 +961,7 @@ init_emulated_hw(struct vmop_create_params *vmc, int *child_disks, */ void restore_emulated_hw(struct vm_create_params *vcp, int fd, - int *child_taps, int *child_disks) + int *child_taps, int *child_disks, int child_cdrom) { /* struct vm_create_params *vcp = &vmc->vmc_params; */ int i; @@ -1000,7 +1001,7 @@ restore_emulated_hw(struct vm_create_params *vcp, int fd, ioports_map[PCI_MODE1_DATA_REG + 2] = vcpu_exit_pci; ioports_map[PCI_MODE1_DATA_REG + 3] = vcpu_exit_pci; pci_restore(fd); - virtio_restore(fd, current_vm, child_disks, child_taps); + virtio_restore(fd, current_vm, child_cdrom, child_disks, child_taps); } /* @@ -1009,6 +1010,7 @@ restore_emulated_hw(struct vm_create_params *vcp, int fd, * Runs the VM whose creation parameters are specified in vcp * * Parameters: + * child_cdrom: previously-opened child ISO disk file descriptor * child_disks: previously-opened child VM disk file file descriptors * child_taps: previously-opened child tap file descriptors * vmc: vmop_create_params struct containing the VM's desired creation @@ -1020,8 +1022,8 @@ restore_emulated_hw(struct vm_create_params *vcp, int fd, * !0 : the VM exited abnormally or failed to start */ int -run_vm(int *child_disks, int *child_taps, struct vmop_create_params *vmc, - struct vcpu_reg_state *vrs) +run_vm(int child_cdrom, int *child_disks, int *child_taps, + struct vmop_create_params *vmc, struct vcpu_reg_state *vrs) { struct vm_create_params *vcp = &vmc->vmc_params; struct vm_rwregs_params vregsp; @@ -1035,6 +1037,9 @@ run_vm(int *child_disks, int *child_taps, struct vmop_create_params *vmc, if (vcp == NULL) return (EINVAL); + if (child_cdrom == -1 && strlen(vcp->vcp_cdrom)) + return (EINVAL); + if (child_disks == NULL && vcp->vcp_ndisks != 0) return (EINVAL); @@ -1066,7 +1071,7 @@ run_vm(int *child_disks, int *child_taps, struct vmop_create_params *vmc, vcp->vcp_name); if (!current_vm->vm_received) - init_emulated_hw(vmc, child_disks, child_taps); + init_emulated_hw(vmc, child_cdrom, child_disks, child_taps); ret = pthread_mutex_init(&threadmutex, NULL); if (ret) { diff --git a/usr.sbin/vmd/vm.conf.5 b/usr.sbin/vmd/vm.conf.5 index 7863d9b7b86..9119ac30d4d 100644 --- a/usr.sbin/vmd/vm.conf.5 +++ b/usr.sbin/vmd/vm.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: vm.conf.5,v 1.26 2017/11/11 07:36:56 jmc Exp $ +.\" $OpenBSD: vm.conf.5,v 1.27 2018/01/03 05:39:56 ccardenas Exp $ .\" .\" Copyright (c) 2015 Mike Larkin .\" Copyright (c) 2015 Reyk Floeter @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: November 11 2017 $ +.Dd $Mdocdate: January 3 2018 $ .Dt VM.CONF 5 .Os .Sh NAME @@ -119,6 +119,8 @@ Followed by a block of parameters that is enclosed in curly brackets: Kernel or BIOS image to load when booting the VM. If not specified, the default is to boot using the BIOS image in .Pa /etc/firmware/vmm-bios . +.It Cm cdrom Ar path +ISO image file. .It Cm enable Automatically start the VM. This is the default if neither diff --git a/usr.sbin/vmd/vmd.c b/usr.sbin/vmd/vmd.c index 469a2856338..f0ca84709d2 100644 --- a/usr.sbin/vmd/vmd.c +++ b/usr.sbin/vmd/vmd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vmd.c,v 1.76 2017/12/06 13:29:02 abieber Exp $ */ +/* $OpenBSD: vmd.c,v 1.77 2018/01/03 05:39:56 ccardenas Exp $ */ /* * Copyright (c) 2015 Reyk Floeter @@ -1064,6 +1064,10 @@ vm_stop(struct vmd_vm *vm, int keeptty) close(vm->vm_kernel); vm->vm_kernel = -1; } + if (vm->vm_cdrom != -1) { + close(vm->vm_cdrom); + vm->vm_cdrom = -1; + } if (!keeptty) { vm_closetty(vm); vm->vm_uid = 0; @@ -1194,6 +1198,7 @@ vm_register(struct privsep *ps, struct vmop_create_params *vmc, } } vm->vm_kernel = -1; + vm->vm_cdrom = -1; vm->vm_iev.ibuf.fd = -1; if (++env->vmd_nvm == 0) diff --git a/usr.sbin/vmd/vmd.h b/usr.sbin/vmd/vmd.h index 0263cb44a4e..1b9a18322fb 100644 --- a/usr.sbin/vmd/vmd.h +++ b/usr.sbin/vmd/vmd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: vmd.h,v 1.66 2017/11/11 02:50:08 mlarkin Exp $ */ +/* $OpenBSD: vmd.h,v 1.67 2018/01/03 05:39:56 ccardenas Exp $ */ /* * Copyright (c) 2015 Mike Larkin @@ -59,6 +59,8 @@ #define VMD_DISK_MISSING 1002 #define VMD_DISK_INVALID 1003 #define VMD_VM_STOP_INVALID 1004 +#define VMD_CDROM_MISSING 1005 +#define VMD_CDROM_INVALID 1006 /* 100.64.0.0/10 from rfc6598 (IPv4 Prefix for Shared Address Space) */ #define VMD_DHCP_PREFIX "100.64.0.0/10" @@ -71,6 +73,7 @@ enum imsg_type { IMSG_VMDOP_START_VM_REQUEST = IMSG_PROC_MAX, + IMSG_VMDOP_START_VM_CDROM, IMSG_VMDOP_START_VM_DISK, IMSG_VMDOP_START_VM_IF, IMSG_VMDOP_START_VM_END, @@ -139,6 +142,7 @@ struct vmop_create_params { #define VMOP_CREATE_MEMORY 0x02 #define VMOP_CREATE_NETWORK 0x04 #define VMOP_CREATE_DISK 0x08 +#define VMOP_CREATE_CDROM 0x10 /* userland-only part of the create params */ unsigned int vmc_ifflags[VMM_MAX_NICS_PER_VM]; @@ -209,6 +213,7 @@ struct vmd_vm { pid_t vm_pid; uint32_t vm_vmid; int vm_kernel; + int vm_cdrom; int vm_disks[VMM_MAX_DISKS_PER_VM]; struct vmd_if vm_ifs[VMM_MAX_NICS_PER_VM]; char *vm_ttyname; @@ -354,6 +359,7 @@ int config_setvm(struct privsep *, struct vmd_vm *, uint32_t, uid_t); int config_getvm(struct privsep *, struct imsg *); int config_getdisk(struct privsep *, struct imsg *); int config_getif(struct privsep *, struct imsg *); +int config_getcdrom(struct privsep *, struct imsg *); /* vmboot.c */ FILE *vmboot_open(int, int, struct vmboot_params *); diff --git a/usr.sbin/vmd/vmm.c b/usr.sbin/vmd/vmm.c index 087dfb30ae2..cb20c38472d 100644 --- a/usr.sbin/vmd/vmm.c +++ b/usr.sbin/vmd/vmm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vmm.c,v 1.78 2017/10/24 07:58:52 mlarkin Exp $ */ +/* $OpenBSD: vmm.c,v 1.79 2018/01/03 05:39:56 ccardenas Exp $ */ /* * Copyright (c) 2015 Mike Larkin @@ -121,6 +121,13 @@ vmm_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) cmd = IMSG_VMDOP_START_VM_RESPONSE; } break; + case IMSG_VMDOP_START_VM_CDROM: + res = config_getcdrom(ps, imsg); + if (res == -1) { + res = errno; + cmd = IMSG_VMDOP_START_VM_RESPONSE; + } + break; case IMSG_VMDOP_START_VM_DISK: res = config_getdisk(ps, imsg); if (res == -1) { @@ -635,6 +642,9 @@ vmm_start_vm(struct imsg *imsg, uint32_t *id) close(vm->vm_kernel); vm->vm_kernel = -1; + close(vm->vm_cdrom); + vm->vm_cdrom = -1; + close(vm->vm_tty); vm->vm_tty = -1;