static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = VIRTIO_BALLOON(vdev); VirtQueueElement elem; MemoryRegionSection section; while (virtqueue_pop(vq, &elem)) { size_t offset = 0; uint32_t pfn; while (iov_to_buf(elem.out_sg, elem.out_num, offset, &pfn, 4) == 4) { ram_addr_t pa; ram_addr_t addr; pa = (ram_addr_t)ldl_p(&pfn) << VIRTIO_BALLOON_PFN_SHIFT; offset += 4; /* FIXME: remove get_system_memory(), but how? */ section = memory_region_find(get_system_memory(), pa, 1); if (!int128_nz(section.size) || !memory_region_is_ram(section.mr)) continue; /* Using memory_region_get_ram_ptr is bending the rules a bit, but should be OK because we only want a single page. */ addr = section.offset_within_region; balloon_page(memory_region_get_ram_ptr(section.mr) + addr, !!(vq == s->dvq)); memory_region_unref(section.mr); } virtqueue_push(vq, &elem, offset); virtio_notify(vdev, vq); } }
static int filter_send(CharBackend *chr_out, const struct iovec *iov, int iovcnt) { int ret = 0; ssize_t size = 0; uint32_t len = 0; char *buf; size = iov_size(iov, iovcnt); if (!size) { return 0; } len = htonl(size); ret = qemu_chr_fe_write_all(chr_out, (uint8_t *)&len, sizeof(len)); if (ret != sizeof(len)) { goto err; } buf = g_malloc(size); iov_to_buf(iov, iovcnt, 0, buf, size); ret = qemu_chr_fe_write_all(chr_out, (uint8_t *)buf, size); g_free(buf); if (ret != size) { goto err; } return 0; err: return ret < 0 ? ret : -EIO; }
static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); VirtQueueElement *elem = &s->stats_vq_elem; VirtIOBalloonStat stat; size_t offset = 0; if (!virtqueue_pop(vq, elem)) { return; } /* Initialize the stats to get rid of any stale values. This is only * needed to handle the case where a guest supports fewer stats than it * used to (ie. it has booted into an old kernel). */ reset_stats(s); while (iov_to_buf(elem->out_sg, elem->out_num, &stat, offset, sizeof(stat)) == sizeof(stat)) { uint16_t tag = tswap16(stat.tag); uint64_t val = tswap64(stat.val); offset += sizeof(stat); if (tag < VIRTIO_BALLOON_S_NR) s->stats[tag] = val; } s->stats_vq_offset = offset; complete_stats_request(s); }
static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = to_virtio_balloon(vdev); VirtQueueElement elem; while (virtqueue_pop(vq, &elem)) { size_t offset = 0; uint32_t pfn; while (iov_to_buf(elem.out_sg, elem.out_num, &pfn, offset, 4) == 4) { ram_addr_t pa; ram_addr_t addr; pa = (ram_addr_t)ldl_p(&pfn) << VIRTIO_BALLOON_PFN_SHIFT; offset += 4; addr = cpu_get_physical_page_desc(pa); if ((addr & ~TARGET_PAGE_MASK) != IO_MEM_RAM) continue; /* Using qemu_get_ram_ptr is bending the rules a bit, but should be OK because we only want a single page. */ balloon_page(qemu_get_ram_ptr(addr), !!(vq == s->dvq)); } virtqueue_push(vq, &elem, offset); virtio_notify(vdev, vq); } }
/* * Handler for control c_ovq virtqueue. * Called when guest sends a control message to the host. */ static void control_out(VirtIODevice *vdev, VirtQueue *vq) { VirtQueueElement elem; VirtIOCrypto *crdev; uint8_t *buf; size_t len; FUNC_IN; crdev = DO_UPCAST(VirtIOCrypto, vdev, vdev); len = 0; buf = NULL; if (!virtqueue_pop(vq, &elem)) return ; len = iov_size(elem.out_sg, elem.out_num); buf = g_malloc(len); iov_to_buf(elem.out_sg, elem.out_num, 0, buf, len); handle_control_message(crdev, buf, len); virtqueue_push(vq, &elem, 0); virtio_notify(vdev, vq); g_free(buf); FUNC_OUT; }
void net_rx_pkt_set_vhdr_iovec(struct NetRxPkt *pkt, const struct iovec *iov, int iovcnt) { assert(pkt); iov_to_buf(iov, iovcnt, 0, &pkt->virt_hdr, sizeof pkt->virt_hdr); }
static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = VIRTIO_BALLOON(vdev); VirtQueueElement *elem; MemoryRegionSection section; for (;;) { size_t offset = 0; uint32_t pfn; elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { return; } while (iov_to_buf(elem->out_sg, elem->out_num, offset, &pfn, 4) == 4) { hwaddr pa; int p = virtio_ldl_p(vdev, &pfn); pa = (hwaddr) p << VIRTIO_BALLOON_PFN_SHIFT; offset += 4; section = memory_region_find(get_system_memory(), pa, BALLOON_PAGE_SIZE); if (!section.mr) { trace_virtio_balloon_bad_addr(pa); continue; } if (!memory_region_is_ram(section.mr) || memory_region_is_rom(section.mr) || memory_region_is_romd(section.mr)) { trace_virtio_balloon_bad_addr(pa); memory_region_unref(section.mr); continue; } trace_virtio_balloon_handle_output(memory_region_name(section.mr), pa); if (!qemu_balloon_is_inhibited()) { if (vq == s->ivq) { balloon_inflate_page(s, section.mr, section.offset_within_region); } else if (vq == s->dvq) { balloon_deflate_page(s, section.mr, section.offset_within_region); } else { g_assert_not_reached(); } } memory_region_unref(section.mr); } virtqueue_push(vq, elem, offset); virtio_notify(vdev, vq); g_free(elem); } }
static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) { V9fsVirtioState *v = (V9fsVirtioState *)vdev; V9fsState *s = &v->state; V9fsPDU *pdu; ssize_t len; VirtQueueElement *elem; while ((pdu = pdu_alloc(s))) { struct { uint32_t size_le; uint8_t id; uint16_t tag_le; } QEMU_PACKED out; elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { goto out_free_pdu; } if (elem->in_num == 0) { virtio_error(vdev, "The guest sent a VirtFS request without space for " "the reply"); goto out_free_req; } QEMU_BUILD_BUG_ON(sizeof(out) != 7); v->elems[pdu->idx] = elem; len = iov_to_buf(elem->out_sg, elem->out_num, 0, &out, sizeof(out)); if (len != sizeof(out)) { virtio_error(vdev, "The guest sent a malformed VirtFS request: " "header size is %zd, should be 7", len); goto out_free_req; } pdu->size = le32_to_cpu(out.size_le); pdu->id = out.id; pdu->tag = le16_to_cpu(out.tag_le); qemu_co_queue_init(&pdu->complete); pdu_submit(pdu); } return; out_free_req: virtqueue_detach_element(vq, elem, 0); g_free(elem); out_free_pdu: pdu_free(pdu); }
static bool get_free_page_hints(VirtIOBalloon *dev) { VirtQueueElement *elem; VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtQueue *vq = dev->free_page_vq; bool ret = true; while (dev->block_iothread) { qemu_cond_wait(&dev->free_page_cond, &dev->free_page_lock); } elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { return false; } if (elem->out_num) { uint32_t id; size_t size = iov_to_buf(elem->out_sg, elem->out_num, 0, &id, sizeof(id)); virtio_tswap32s(vdev, &id); if (unlikely(size != sizeof(id))) { virtio_error(vdev, "received an incorrect cmd id"); ret = false; goto out; } if (id == dev->free_page_report_cmd_id) { dev->free_page_report_status = FREE_PAGE_REPORT_S_START; } else { /* * Stop the optimization only when it has started. This * avoids a stale stop sign for the previous command. */ if (dev->free_page_report_status == FREE_PAGE_REPORT_S_START) { dev->free_page_report_status = FREE_PAGE_REPORT_S_STOP; } } } if (elem->in_num) { if (dev->free_page_report_status == FREE_PAGE_REPORT_S_START) { qemu_guest_free_page_hint(elem->in_sg[0].iov_base, elem->in_sg[0].iov_len); } } out: virtqueue_push(vq, elem, 1); g_free(elem); return ret; }
BlockDriverAIOCB *win32_aio_submit(BlockDriverState *bs, QEMUWin32AIOState *aio, HANDLE hfile, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque, int type) { struct QEMUWin32AIOCB *waiocb; uint64_t offset = sector_num * 512; DWORD rc; waiocb = qemu_aio_get(&win32_aiocb_info, bs, cb, opaque); waiocb->nbytes = nb_sectors * 512; waiocb->qiov = qiov; waiocb->is_read = (type == QEMU_AIO_READ); if (qiov->niov > 1) { waiocb->buf = qemu_try_blockalign(bs, qiov->size); if (waiocb->buf == NULL) { goto out; } if (type & QEMU_AIO_WRITE) { iov_to_buf(qiov->iov, qiov->niov, 0, waiocb->buf, qiov->size); } waiocb->is_linear = false; } else { waiocb->buf = qiov->iov[0].iov_base; waiocb->is_linear = true; } memset(&waiocb->ov, 0, sizeof(waiocb->ov)); waiocb->ov.Offset = (DWORD)offset; waiocb->ov.OffsetHigh = (DWORD)(offset >> 32); waiocb->ov.hEvent = event_notifier_get_handle(&aio->e); aio->count++; if (type & QEMU_AIO_READ) { rc = ReadFile(hfile, waiocb->buf, waiocb->nbytes, NULL, &waiocb->ov); } else { rc = WriteFile(hfile, waiocb->buf, waiocb->nbytes, NULL, &waiocb->ov); } if(rc == 0 && GetLastError() != ERROR_IO_PENDING) { goto out_dec_count; } return &waiocb->common; out_dec_count: aio->count--; out: qemu_aio_release(waiocb); return NULL; }
static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = VIRTIO_BALLOON(vdev); VirtQueueElement *elem; VirtIOBalloonStat stat; size_t offset = 0; qemu_timeval tv; elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { goto out; } if (s->stats_vq_elem != NULL) { /* This should never happen if the driver follows the spec. */ virtqueue_push(vq, s->stats_vq_elem, 0); virtio_notify(vdev, vq); g_free(s->stats_vq_elem); } s->stats_vq_elem = elem; /* Initialize the stats to get rid of any stale values. This is only * needed to handle the case where a guest supports fewer stats than it * used to (ie. it has booted into an old kernel). */ reset_stats(s); while (iov_to_buf(elem->out_sg, elem->out_num, offset, &stat, sizeof(stat)) == sizeof(stat)) { uint16_t tag = virtio_tswap16(vdev, stat.tag); uint64_t val = virtio_tswap64(vdev, stat.val); offset += sizeof(stat); if (tag < VIRTIO_BALLOON_S_NR) s->stats[tag] = val; } s->stats_vq_offset = offset; if (qemu_gettimeofday(&tv) < 0) { fprintf(stderr, "warning: %s: failed to get time of day\n", __func__); goto out; } s->stats_last_update = tv.tv_sec; out: if (balloon_stats_enabled(s)) { balloon_stats_change_timer(s, s->stats_poll_interval); } }
static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = VIRTIO_BALLOON(vdev); VirtQueueElement *elem; MemoryRegionSection section; for (;;) { size_t offset = 0; uint32_t pfn; elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { return; } while (iov_to_buf(elem->out_sg, elem->out_num, offset, &pfn, 4) == 4) { ram_addr_t pa; ram_addr_t addr; int p = virtio_ldl_p(vdev, &pfn); pa = (ram_addr_t) p << VIRTIO_BALLOON_PFN_SHIFT; offset += 4; /* FIXME: remove get_system_memory(), but how? */ section = memory_region_find(get_system_memory(), pa, 1); if (!int128_nz(section.size) || !memory_region_is_ram(section.mr) || memory_region_is_rom(section.mr) || memory_region_is_romd(section.mr)) { trace_virtio_balloon_bad_addr(pa); memory_region_unref(section.mr); continue; } trace_virtio_balloon_handle_output(memory_region_name(section.mr), pa); /* Using memory_region_get_ram_ptr is bending the rules a bit, but should be OK because we only want a single page. */ addr = section.offset_within_region; balloon_page(memory_region_get_ram_ptr(section.mr) + addr, !!(vq == s->dvq)); memory_region_unref(section.mr); } virtqueue_push(vq, elem, offset); virtio_notify(vdev, vq); g_free(elem); } }
void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, FILE *fp, const char *prefix, size_t limit) { int v; size_t size = 0; char *buf; for (v = 0; v < iov_cnt; v++) { size += iov[v].iov_len; } size = size > limit ? limit : size; buf = g_malloc(size); iov_to_buf(iov, iov_cnt, 0, buf, size); qemu_hexdump(buf, fp, prefix, size); g_free(buf); }
void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) { assert(p->result >= 0); assert(p->result + bytes <= p->iov.size); switch (p->pid) { case USB_TOKEN_SETUP: case USB_TOKEN_OUT: iov_to_buf(p->iov.iov, p->iov.niov, p->result, ptr, bytes); break; case USB_TOKEN_IN: iov_from_buf(p->iov.iov, p->iov.niov, p->result, ptr, bytes); break; default: fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid); abort(); } p->result += bytes; }
static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) { V9fsVirtioState *v = (V9fsVirtioState *)vdev; V9fsState *s = &v->state; V9fsPDU *pdu; ssize_t len; VirtQueueElement *elem; while ((pdu = pdu_alloc(s))) { P9MsgHeader out; elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { goto out_free_pdu; } if (elem->in_num == 0) { virtio_error(vdev, "The guest sent a VirtFS request without space for " "the reply"); goto out_free_req; } QEMU_BUILD_BUG_ON(sizeof(out) != 7); v->elems[pdu->idx] = elem; len = iov_to_buf(elem->out_sg, elem->out_num, 0, &out, sizeof(out)); if (len != sizeof(out)) { virtio_error(vdev, "The guest sent a malformed VirtFS request: " "header size is %zd, should be 7", len); goto out_free_req; } pdu_submit(pdu, &out); } return; out_free_req: virtqueue_detach_element(vq, elem, 0); g_free(elem); out_free_pdu: pdu_free(pdu); }
static int virtio_crypto_cipher_session_helper(VirtIODevice *vdev, CryptoDevBackendSymSessionInfo *info, struct virtio_crypto_cipher_session_para *cipher_para, struct iovec **iov, unsigned int *out_num) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); unsigned int num = *out_num; info->cipher_alg = ldl_le_p(&cipher_para->algo); info->key_len = ldl_le_p(&cipher_para->keylen); info->direction = ldl_le_p(&cipher_para->op); DPRINTF("cipher_alg=%" PRIu32 ", info->direction=%" PRIu32 "\n", info->cipher_alg, info->direction); if (info->key_len > vcrypto->conf.max_cipher_key_len) { error_report("virtio-crypto length of cipher key is too big: %u", info->key_len); return -VIRTIO_CRYPTO_ERR; } /* Get cipher key */ if (info->key_len > 0) { size_t s; DPRINTF("keylen=%" PRIu32 "\n", info->key_len); info->cipher_key = g_malloc(info->key_len); s = iov_to_buf(*iov, num, 0, info->cipher_key, info->key_len); if (unlikely(s != info->key_len)) { virtio_error(vdev, "virtio-crypto cipher key incorrect"); return -EFAULT; } iov_discard_front(iov, &num, info->key_len); *out_num = num; } return 0; }
static CryptoDevBackendSymOpInfo * virtio_crypto_sym_op_helper(VirtIODevice *vdev, struct virtio_crypto_cipher_para *cipher_para, struct virtio_crypto_alg_chain_data_para *alg_chain_para, struct iovec *iov, unsigned int out_num) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); CryptoDevBackendSymOpInfo *op_info; uint32_t src_len = 0, dst_len = 0; uint32_t iv_len = 0; uint32_t aad_len = 0, hash_result_len = 0; uint32_t hash_start_src_offset = 0, len_to_hash = 0; uint32_t cipher_start_src_offset = 0, len_to_cipher = 0; uint64_t max_len, curr_size = 0; size_t s; /* Plain cipher */ if (cipher_para) { iv_len = ldl_le_p(&cipher_para->iv_len); src_len = ldl_le_p(&cipher_para->src_data_len); dst_len = ldl_le_p(&cipher_para->dst_data_len); } else if (alg_chain_para) { /* Algorithm chain */ iv_len = ldl_le_p(&alg_chain_para->iv_len); src_len = ldl_le_p(&alg_chain_para->src_data_len); dst_len = ldl_le_p(&alg_chain_para->dst_data_len); aad_len = ldl_le_p(&alg_chain_para->aad_len); hash_result_len = ldl_le_p(&alg_chain_para->hash_result_len); hash_start_src_offset = ldl_le_p( &alg_chain_para->hash_start_src_offset); cipher_start_src_offset = ldl_le_p( &alg_chain_para->cipher_start_src_offset); len_to_cipher = ldl_le_p(&alg_chain_para->len_to_cipher); len_to_hash = ldl_le_p(&alg_chain_para->len_to_hash); } else { return NULL; } max_len = (uint64_t)iv_len + aad_len + src_len + dst_len + hash_result_len; if (unlikely(max_len > vcrypto->conf.max_size)) { virtio_error(vdev, "virtio-crypto too big length"); return NULL; } op_info = g_malloc0(sizeof(CryptoDevBackendSymOpInfo) + max_len); op_info->iv_len = iv_len; op_info->src_len = src_len; op_info->dst_len = dst_len; op_info->aad_len = aad_len; op_info->digest_result_len = hash_result_len; op_info->hash_start_src_offset = hash_start_src_offset; op_info->len_to_hash = len_to_hash; op_info->cipher_start_src_offset = cipher_start_src_offset; op_info->len_to_cipher = len_to_cipher; /* Handle the initilization vector */ if (op_info->iv_len > 0) { DPRINTF("iv_len=%" PRIu32 "\n", op_info->iv_len); op_info->iv = op_info->data + curr_size; s = iov_to_buf(iov, out_num, 0, op_info->iv, op_info->iv_len); if (unlikely(s != op_info->iv_len)) { virtio_error(vdev, "virtio-crypto iv incorrect"); goto err; } iov_discard_front(&iov, &out_num, op_info->iv_len); curr_size += op_info->iv_len; } /* Handle additional authentication data if exists */ if (op_info->aad_len > 0) { DPRINTF("aad_len=%" PRIu32 "\n", op_info->aad_len); op_info->aad_data = op_info->data + curr_size; s = iov_to_buf(iov, out_num, 0, op_info->aad_data, op_info->aad_len); if (unlikely(s != op_info->aad_len)) { virtio_error(vdev, "virtio-crypto additional auth data incorrect"); goto err; } iov_discard_front(&iov, &out_num, op_info->aad_len); curr_size += op_info->aad_len; } /* Handle the source data */ if (op_info->src_len > 0) { DPRINTF("src_len=%" PRIu32 "\n", op_info->src_len); op_info->src = op_info->data + curr_size; s = iov_to_buf(iov, out_num, 0, op_info->src, op_info->src_len); if (unlikely(s != op_info->src_len)) { virtio_error(vdev, "virtio-crypto source data incorrect"); goto err; } iov_discard_front(&iov, &out_num, op_info->src_len); curr_size += op_info->src_len; } /* Handle the destination data */ op_info->dst = op_info->data + curr_size; curr_size += op_info->dst_len; DPRINTF("dst_len=%" PRIu32 "\n", op_info->dst_len); /* Handle the hash digest result */ if (hash_result_len > 0) { DPRINTF("hash_result_len=%" PRIu32 "\n", hash_result_len); op_info->digest_result = op_info->data + curr_size; } return op_info; err: g_free(op_info); return NULL; }
size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset, void *buf, size_t bytes) { return iov_to_buf(qiov->iov, qiov->niov, offset, buf, bytes); }
static void test_to_from_buf_1(void) { unsigned niov; struct iovec *iov; size_t sz; unsigned char *ibuf, *obuf; unsigned i, j, n; iov_random(&iov, &niov); sz = iov_size(iov, niov); ibuf = g_malloc(sz + 8) + 4; memcpy(ibuf-4, "aaaa", 4); memcpy(ibuf + sz, "bbbb", 4); obuf = g_malloc(sz + 8) + 4; memcpy(obuf-4, "xxxx", 4); memcpy(obuf + sz, "yyyy", 4); /* fill in ibuf with 0123456... */ for (i = 0; i < sz; ++i) { ibuf[i] = i & 255; } for (i = 0; i <= sz; ++i) { /* Test from/to buf for offset(i) in [0..sz] up to the end of buffer. * For last iteration with offset == sz, the procedure should * skip whole vector and process exactly 0 bytes */ /* first set bytes [i..sz) to some "random" value */ n = iov_memset(iov, niov, 0, 0xff, -1); g_assert(n == sz); /* next copy bytes [i..sz) from ibuf to iovec */ n = iov_from_buf(iov, niov, i, ibuf + i, -1); g_assert(n == sz - i); /* clear part of obuf */ memset(obuf + i, 0, sz - i); /* and set this part of obuf to values from iovec */ n = iov_to_buf(iov, niov, i, obuf + i, -1); g_assert(n == sz - i); /* now compare resulting buffers */ g_assert(memcmp(ibuf, obuf, sz) == 0); /* test just one char */ n = iov_to_buf(iov, niov, i, obuf + i, 1); g_assert(n == (i < sz)); if (n) { g_assert(obuf[i] == (i & 255)); } for (j = i; j <= sz; ++j) { /* now test num of bytes cap up to byte no. j, * with j in [i..sz]. */ /* clear iovec */ n = iov_memset(iov, niov, 0, 0xff, -1); g_assert(n == sz); /* copy bytes [i..j) from ibuf to iovec */ n = iov_from_buf(iov, niov, i, ibuf + i, j - i); g_assert(n == j - i); /* clear part of obuf */ memset(obuf + i, 0, j - i); /* copy bytes [i..j) from iovec to obuf */ n = iov_to_buf(iov, niov, i, obuf + i, j - i); g_assert(n == j - i); /* verify result */ g_assert(memcmp(ibuf, obuf, sz) == 0); /* now actually check if the iovec contains the right data */ test_iov_bytes(iov, niov, i, j - i); } } g_assert(!memcmp(ibuf-4, "aaaa", 4) && !memcmp(ibuf+sz, "bbbb", 4)); g_free(ibuf-4); g_assert(!memcmp(obuf-4, "xxxx", 4) && !memcmp(obuf+sz, "yyyy", 4)); g_free(obuf-4); iov_free(iov, niov); }
void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) { uint32_t type; struct iovec *in_iov = req->elem->in_sg; struct iovec *iov = req->elem->out_sg; unsigned in_num = req->elem->in_num; unsigned out_num = req->elem->out_num; if (req->elem->out_num < 1 || req->elem->in_num < 1) { error_report("virtio-blk missing headers"); exit(1); } if (unlikely(iov_to_buf(iov, out_num, 0, &req->out, sizeof(req->out)) != sizeof(req->out))) { error_report("virtio-blk request outhdr too short"); exit(1); } iov_discard_front(&iov, &out_num, sizeof(req->out)); if (in_num < 1 || in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { error_report("virtio-blk request inhdr too short"); exit(1); } req->in = (void *)in_iov[in_num - 1].iov_base + in_iov[in_num - 1].iov_len - sizeof(struct virtio_blk_inhdr); iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); type = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); if (type & VIRTIO_BLK_T_FLUSH) { virtio_blk_handle_flush(req, mrb); } else if (type & VIRTIO_BLK_T_SCSI_CMD) { virtio_blk_handle_scsi(req); } else if (type & VIRTIO_BLK_T_GET_ID) { VirtIOBlock *s = req->dev; /* * NB: per existing s/n string convention the string is * terminated by '\0' only when shorter than buffer. */ strncpy(req->elem->in_sg[0].iov_base, s->blk.serial ? s->blk.serial : "", MIN(req->elem->in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES)); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); virtio_blk_free_request(req); } else if (type & VIRTIO_BLK_T_OUT) { qemu_iovec_init_external(&req->qiov, &req->elem->out_sg[1], req->elem->out_num - 1); virtio_blk_handle_write(req, mrb); } else if (type == VIRTIO_BLK_T_IN || type == VIRTIO_BLK_T_BARRIER) { /* VIRTIO_BLK_T_IN is 0, so we can't just & it. */ qemu_iovec_init_external(&req->qiov, &req->elem->in_sg[0], req->elem->in_num - 1); virtio_blk_handle_read(req); } else { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); virtio_blk_free_request(req); } }
static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); struct virtio_crypto_op_ctrl_req ctrl; VirtQueueElement *elem; struct iovec *in_iov; struct iovec *out_iov; unsigned in_num; unsigned out_num; uint32_t queue_id; uint32_t opcode; struct virtio_crypto_session_input input; int64_t session_id; uint8_t status; size_t s; for (;;) { elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { break; } if (elem->out_num < 1 || elem->in_num < 1) { virtio_error(vdev, "virtio-crypto ctrl missing headers"); virtqueue_detach_element(vq, elem, 0); g_free(elem); break; } out_num = elem->out_num; out_iov = elem->out_sg; in_num = elem->in_num; in_iov = elem->in_sg; if (unlikely(iov_to_buf(out_iov, out_num, 0, &ctrl, sizeof(ctrl)) != sizeof(ctrl))) { virtio_error(vdev, "virtio-crypto request ctrl_hdr too short"); virtqueue_detach_element(vq, elem, 0); g_free(elem); break; } iov_discard_front(&out_iov, &out_num, sizeof(ctrl)); opcode = ldl_le_p(&ctrl.header.opcode); queue_id = ldl_le_p(&ctrl.header.queue_id); switch (opcode) { case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: memset(&input, 0, sizeof(input)); session_id = virtio_crypto_create_sym_session(vcrypto, &ctrl.u.sym_create_session, queue_id, opcode, out_iov, out_num); /* Serious errors, need to reset virtio crypto device */ if (session_id == -EFAULT) { virtqueue_detach_element(vq, elem, 0); break; } else if (session_id == -VIRTIO_CRYPTO_NOTSUPP) { stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP); } else if (session_id == -VIRTIO_CRYPTO_ERR) { stl_le_p(&input.status, VIRTIO_CRYPTO_ERR); } else { /* Set the session id */ stq_le_p(&input.session_id, session_id); stl_le_p(&input.status, VIRTIO_CRYPTO_OK); } s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input)); if (unlikely(s != sizeof(input))) { virtio_error(vdev, "virtio-crypto input incorrect"); virtqueue_detach_element(vq, elem, 0); break; } virtqueue_push(vq, elem, sizeof(input)); virtio_notify(vdev, vq); break; case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION: case VIRTIO_CRYPTO_HASH_DESTROY_SESSION: case VIRTIO_CRYPTO_MAC_DESTROY_SESSION: case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION: status = virtio_crypto_handle_close_session(vcrypto, &ctrl.u.destroy_session, queue_id); /* The status only occupy one byte, we can directly use it */ s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status)); if (unlikely(s != sizeof(status))) { virtio_error(vdev, "virtio-crypto status incorrect"); virtqueue_detach_element(vq, elem, 0); break; } virtqueue_push(vq, elem, sizeof(status)); virtio_notify(vdev, vq); break; case VIRTIO_CRYPTO_HASH_CREATE_SESSION: case VIRTIO_CRYPTO_MAC_CREATE_SESSION: case VIRTIO_CRYPTO_AEAD_CREATE_SESSION: default: error_report("virtio-crypto unsupported ctrl opcode: %d", opcode); memset(&input, 0, sizeof(input)); stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP); s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input)); if (unlikely(s != sizeof(input))) { virtio_error(vdev, "virtio-crypto input incorrect"); virtqueue_detach_element(vq, elem, 0); break; } virtqueue_push(vq, elem, sizeof(input)); virtio_notify(vdev, vq); break; } /* end switch case */ g_free(elem); } /* end for loop */ }
static int64_t virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto, struct virtio_crypto_sym_create_session_req *sess_req, uint32_t queue_id, uint32_t opcode, struct iovec *iov, unsigned int out_num) { VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); CryptoDevBackendSymSessionInfo info; int64_t session_id; int queue_index; uint32_t op_type; Error *local_err = NULL; int ret; memset(&info, 0, sizeof(info)); op_type = ldl_le_p(&sess_req->op_type); info.op_type = op_type; info.op_code = opcode; if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) { ret = virtio_crypto_cipher_session_helper(vdev, &info, &sess_req->u.cipher.para, &iov, &out_num); if (ret < 0) { goto err; } } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { size_t s; /* cipher part */ ret = virtio_crypto_cipher_session_helper(vdev, &info, &sess_req->u.chain.para.cipher_param, &iov, &out_num); if (ret < 0) { goto err; } /* hash part */ info.alg_chain_order = ldl_le_p( &sess_req->u.chain.para.alg_chain_order); info.add_len = ldl_le_p(&sess_req->u.chain.para.aad_len); info.hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode); if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) { info.hash_alg = ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo); info.auth_key_len = ldl_le_p( &sess_req->u.chain.para.u.mac_param.auth_key_len); info.hash_result_len = ldl_le_p( &sess_req->u.chain.para.u.mac_param.hash_result_len); if (info.auth_key_len > vcrypto->conf.max_auth_key_len) { error_report("virtio-crypto length of auth key is too big: %u", info.auth_key_len); ret = -VIRTIO_CRYPTO_ERR; goto err; } /* get auth key */ if (info.auth_key_len > 0) { DPRINTF("auth_keylen=%" PRIu32 "\n", info.auth_key_len); info.auth_key = g_malloc(info.auth_key_len); s = iov_to_buf(iov, out_num, 0, info.auth_key, info.auth_key_len); if (unlikely(s != info.auth_key_len)) { virtio_error(vdev, "virtio-crypto authenticated key incorrect"); ret = -EFAULT; goto err; } iov_discard_front(&iov, &out_num, info.auth_key_len); } } else if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) { info.hash_alg = ldl_le_p( &sess_req->u.chain.para.u.hash_param.algo); info.hash_result_len = ldl_le_p( &sess_req->u.chain.para.u.hash_param.hash_result_len); } else { /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */ error_report("unsupported hash mode"); ret = -VIRTIO_CRYPTO_NOTSUPP; goto err; } } else { /* VIRTIO_CRYPTO_SYM_OP_NONE */ error_report("unsupported cipher op_type: VIRTIO_CRYPTO_SYM_OP_NONE"); ret = -VIRTIO_CRYPTO_NOTSUPP; goto err; } queue_index = virtio_crypto_vq2q(queue_id); session_id = cryptodev_backend_sym_create_session( vcrypto->cryptodev, &info, queue_index, &local_err); if (session_id >= 0) { DPRINTF("create session_id=%" PRIu64 " successfully\n", session_id); ret = session_id; } else { if (local_err) { error_report_err(local_err); } ret = -VIRTIO_CRYPTO_ERR; } err: g_free(info.cipher_key); g_free(info.auth_key); return ret; }
static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, unsigned long int req, void *buf, BlockDriverCompletionFunc *cb, void *opaque) { IscsiLun *iscsilun = bs->opaque; struct iscsi_context *iscsi = iscsilun->iscsi; struct iscsi_data data; IscsiAIOCB *acb; assert(req == SG_IO); acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); acb->iscsilun = iscsilun; acb->canceled = 0; acb->bh = NULL; acb->status = -EINPROGRESS; acb->buf = NULL; acb->ioh = buf; acb->task = malloc(sizeof(struct scsi_task)); if (acb->task == NULL) { error_report("iSCSI: Failed to allocate task for scsi command. %s", iscsi_get_error(iscsi)); qemu_aio_release(acb); return NULL; } memset(acb->task, 0, sizeof(struct scsi_task)); switch (acb->ioh->dxfer_direction) { case SG_DXFER_TO_DEV: acb->task->xfer_dir = SCSI_XFER_WRITE; break; case SG_DXFER_FROM_DEV: acb->task->xfer_dir = SCSI_XFER_READ; break; default: acb->task->xfer_dir = SCSI_XFER_NONE; break; } acb->task->cdb_size = acb->ioh->cmd_len; memcpy(&acb->task->cdb[0], acb->ioh->cmdp, acb->ioh->cmd_len); acb->task->expxferlen = acb->ioh->dxfer_len; data.size = 0; if (acb->task->xfer_dir == SCSI_XFER_WRITE) { if (acb->ioh->iovec_count == 0) { data.data = acb->ioh->dxferp; data.size = acb->ioh->dxfer_len; } else { #if defined(LIBISCSI_FEATURE_IOVECTOR) scsi_task_set_iov_out(acb->task, (struct scsi_iovec *) acb->ioh->dxferp, acb->ioh->iovec_count); #else struct iovec *iov = (struct iovec *)acb->ioh->dxferp; acb->buf = g_malloc(acb->ioh->dxfer_len); data.data = acb->buf; data.size = iov_to_buf(iov, acb->ioh->iovec_count, 0, acb->buf, acb->ioh->dxfer_len); #endif } } if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task, iscsi_aio_ioctl_cb, (data.size > 0) ? &data : NULL, acb) != 0) { scsi_free_scsi_task(acb->task); qemu_aio_release(acb); return NULL; } /* tell libiscsi to read straight into the buffer we got from ioctl */ if (acb->task->xfer_dir == SCSI_XFER_READ) { if (acb->ioh->iovec_count == 0) { scsi_task_add_data_in_buffer(acb->task, acb->ioh->dxfer_len, acb->ioh->dxferp); } else { #if defined(LIBISCSI_FEATURE_IOVECTOR) scsi_task_set_iov_in(acb->task, (struct scsi_iovec *) acb->ioh->dxferp, acb->ioh->iovec_count); #else int i; for (i = 0; i < acb->ioh->iovec_count; i++) { struct iovec *iov = (struct iovec *)acb->ioh->dxferp; scsi_task_add_data_in_buffer(acb->task, iov[i].iov_len, iov[i].iov_base); } #endif } } iscsi_set_events(iscsilun); return &acb->common; }
static int virtio_crypto_handle_request(VirtIOCryptoReq *request) { VirtIOCrypto *vcrypto = request->vcrypto; VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); VirtQueueElement *elem = &request->elem; int queue_index = virtio_crypto_vq2q(virtio_get_queue_index(request->vq)); struct virtio_crypto_op_data_req req; int ret; struct iovec *in_iov; struct iovec *out_iov; unsigned in_num; unsigned out_num; uint32_t opcode; uint8_t status = VIRTIO_CRYPTO_ERR; uint64_t session_id; CryptoDevBackendSymOpInfo *sym_op_info = NULL; Error *local_err = NULL; if (elem->out_num < 1 || elem->in_num < 1) { virtio_error(vdev, "virtio-crypto dataq missing headers"); return -1; } out_num = elem->out_num; out_iov = elem->out_sg; in_num = elem->in_num; in_iov = elem->in_sg; if (unlikely(iov_to_buf(out_iov, out_num, 0, &req, sizeof(req)) != sizeof(req))) { virtio_error(vdev, "virtio-crypto request outhdr too short"); return -1; } iov_discard_front(&out_iov, &out_num, sizeof(req)); if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_crypto_inhdr)) { virtio_error(vdev, "virtio-crypto request inhdr too short"); return -1; } /* We always touch the last byte, so just see how big in_iov is. */ request->in_len = iov_size(in_iov, in_num); request->in = (void *)in_iov[in_num - 1].iov_base + in_iov[in_num - 1].iov_len - sizeof(struct virtio_crypto_inhdr); iov_discard_back(in_iov, &in_num, sizeof(struct virtio_crypto_inhdr)); /* * The length of operation result, including dest_data * and digest_result if exists. */ request->in_num = in_num; request->in_iov = in_iov; opcode = ldl_le_p(&req.header.opcode); session_id = ldq_le_p(&req.header.session_id); switch (opcode) { case VIRTIO_CRYPTO_CIPHER_ENCRYPT: case VIRTIO_CRYPTO_CIPHER_DECRYPT: ret = virtio_crypto_handle_sym_req(vcrypto, &req.u.sym_req, &sym_op_info, out_iov, out_num); /* Serious errors, need to reset virtio crypto device */ if (ret == -EFAULT) { return -1; } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) { virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP); virtio_crypto_free_request(request); } else { sym_op_info->session_id = session_id; /* Set request's parameter */ request->flags = CRYPTODEV_BACKEND_ALG_SYM; request->u.sym_op_info = sym_op_info; ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev, request, queue_index, &local_err); if (ret < 0) { status = -ret; if (local_err) { error_report_err(local_err); } } else { /* ret == VIRTIO_CRYPTO_OK */ status = ret; } virtio_crypto_req_complete(request, status); virtio_crypto_free_request(request); } break; case VIRTIO_CRYPTO_HASH: case VIRTIO_CRYPTO_MAC: case VIRTIO_CRYPTO_AEAD_ENCRYPT: case VIRTIO_CRYPTO_AEAD_DECRYPT: default: error_report("virtio-crypto unsupported dataq opcode: %u", opcode); virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP); virtio_crypto_free_request(request); } return 0; }