diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/9pfs/9p.c | 8 | ||||
-rw-r--r-- | hw/audio/ac97.c | 11 | ||||
-rw-r--r-- | hw/audio/es1370.c | 14 | ||||
-rw-r--r-- | hw/char/serial.c | 10 | ||||
-rw-r--r-- | hw/display/cirrus_vga.c | 5 | ||||
-rw-r--r-- | hw/display/virtio-gpu-3d.c | 20 | ||||
-rw-r--r-- | hw/display/virtio-gpu.c | 6 | ||||
-rw-r--r-- | hw/net/net_rx_pkt.c | 35 | ||||
-rw-r--r-- | hw/scsi/megasas.c | 11 | ||||
-rw-r--r-- | hw/sd/sdhci.c | 14 | ||||
-rw-r--r-- | hw/usb/dev-smartcard-reader.c | 2 | ||||
-rw-r--r-- | hw/usb/hcd-ohci.c | 11 | ||||
-rw-r--r-- | hw/usb/hcd-xhci.c | 23 | ||||
-rw-r--r-- | hw/usb/trace-events | 1 | ||||
-rw-r--r-- | hw/virtio/virtio-crypto.c | 4 | ||||
-rw-r--r-- | hw/watchdog/wdt_i6300esb.c | 9 |
16 files changed, 146 insertions, 38 deletions
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index faebd91f5f..d01ade8421 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1539,6 +1539,10 @@ static void coroutine_fn v9fs_lcreate(void *opaque) err = -ENOENT; goto out_nofid; } + if (fidp->fid_type != P9_FID_NONE) { + err = -EINVAL; + goto out; + } flags = get_dotl_openflags(pdu->s, flags); err = v9fs_co_open2(pdu, fidp, &name, gid, @@ -2137,6 +2141,10 @@ static void coroutine_fn v9fs_create(void *opaque) err = -EINVAL; goto out_nofid; } + if (fidp->fid_type != P9_FID_NONE) { + err = -EINVAL; + goto out; + } if (perm & P9_STAT_MODE_DIR) { err = v9fs_co_mkdir(pdu, fidp, &name, perm & 0777, fidp->uid, -1, &stbuf); diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index cbd959e0bd..c30657501c 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -1387,6 +1387,16 @@ static void ac97_realize(PCIDevice *dev, Error **errp) ac97_on_reset (&s->dev.qdev); } +static void ac97_exit(PCIDevice *dev) +{ + AC97LinkState *s = DO_UPCAST(AC97LinkState, dev, dev); + + AUD_close_in(&s->card, s->voice_pi); + AUD_close_out(&s->card, s->voice_po); + AUD_close_in(&s->card, s->voice_mc); + AUD_remove_card(&s->card); +} + static int ac97_init (PCIBus *bus) { pci_create_simple (bus, -1, "AC97"); @@ -1404,6 +1414,7 @@ static void ac97_class_init (ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS (klass); k->realize = ac97_realize; + k->exit = ac97_exit; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82801AA_5; k->revision = 0x01; diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 8449b5f436..883ec69b30 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -1041,6 +1041,19 @@ static void es1370_realize(PCIDevice *dev, Error **errp) es1370_reset (s); } +static void es1370_exit(PCIDevice *dev) +{ + ES1370State *s = ES1370(dev); + int i; + + for (i = 0; i < 2; ++i) { + AUD_close_out(&s->card, s->dac_voice[i]); + } + + AUD_close_in(&s->card, s->adc_voice); + AUD_remove_card(&s->card); +} + static int es1370_init (PCIBus *bus) { pci_create_simple (bus, -1, TYPE_ES1370); @@ -1053,6 +1066,7 @@ static void es1370_class_init (ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS (klass); k->realize = es1370_realize; + k->exit = es1370_exit; k->vendor_id = PCI_VENDOR_ID_ENSONIQ; k->device_id = PCI_DEVICE_ID_ENSONIQ_ES1370; k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO; diff --git a/hw/char/serial.c b/hw/char/serial.c index ffbacd8227..67b18eda12 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -906,6 +906,16 @@ void serial_realize_core(SerialState *s, Error **errp) void serial_exit_core(SerialState *s) { qemu_chr_fe_deinit(&s->chr); + + timer_del(s->modem_status_poll); + timer_free(s->modem_status_poll); + + timer_del(s->fifo_timeout_timer); + timer_free(s->fifo_timeout_timer); + + fifo8_destroy(&s->recv_fifo); + fifo8_destroy(&s->xmit_fifo); + qemu_unregister_reset(serial_reset, s); } diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index bdb092ee9d..be5bf1c821 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -658,9 +658,14 @@ static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin, int off_cur; int off_cur_end; + if (off_pitch < 0) { + off_begin -= bytesperline - 1; + } + for (y = 0; y < lines; y++) { off_cur = off_begin; off_cur_end = (off_cur + bytesperline) & s->cirrus_addr_mask; + assert(off_cur_end >= off_cur); memory_region_set_dirty(&s->vga.vram, off_cur, off_cur_end - off_cur); off_begin += off_pitch; } diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c index 23f39de94d..8e6fd4fa7c 100644 --- a/hw/display/virtio-gpu-3d.c +++ b/hw/display/virtio-gpu-3d.c @@ -77,10 +77,18 @@ static void virgl_cmd_resource_unref(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { struct virtio_gpu_resource_unref unref; + struct iovec *res_iovs = NULL; + int num_iovs = 0; VIRTIO_GPU_FILL_CMD(unref); trace_virtio_gpu_cmd_res_unref(unref.resource_id); + virgl_renderer_resource_detach_iov(unref.resource_id, + &res_iovs, + &num_iovs); + if (res_iovs != NULL && num_iovs != 0) { + virtio_gpu_cleanup_mapping_iov(res_iovs, num_iovs); + } virgl_renderer_resource_unref(unref.resource_id); } @@ -291,8 +299,11 @@ static void virgl_resource_attach_backing(VirtIOGPU *g, return; } - virgl_renderer_resource_attach_iov(att_rb.resource_id, - res_iovs, att_rb.nr_entries); + ret = virgl_renderer_resource_attach_iov(att_rb.resource_id, + res_iovs, att_rb.nr_entries); + + if (ret != 0) + virtio_gpu_cleanup_mapping_iov(res_iovs, att_rb.nr_entries); } static void virgl_resource_detach_backing(VirtIOGPU *g, @@ -371,6 +382,11 @@ static void virgl_cmd_get_capset(VirtIOGPU *g, virgl_renderer_get_cap_set(gc.capset_id, &max_ver, &max_size); + if (!max_size) { + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + resp = g_malloc(sizeof(*resp) + max_size); resp->hdr.type = VIRTIO_GPU_RESP_OK_CAPSET; diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 5f32e1aae9..bcc9bde2cd 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -599,6 +599,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; return; } + pixman_image_unref(rect); dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds); } @@ -705,6 +706,11 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g, return; } + if (res->iov) { + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + return; + } + ret = virtio_gpu_create_mapping_iov(&ab, cmd, &res->addrs, &res->iov); if (ret != 0) { cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; diff --git a/hw/net/net_rx_pkt.c b/hw/net/net_rx_pkt.c index 1019b50c18..cdf2dc4391 100644 --- a/hw/net/net_rx_pkt.c +++ b/hw/net/net_rx_pkt.c @@ -23,13 +23,13 @@ struct NetRxPkt { struct virtio_net_hdr virt_hdr; - uint8_t ehdr_buf[sizeof(struct eth_header)]; + uint8_t ehdr_buf[sizeof(struct eth_header) + sizeof(struct vlan_header)]; struct iovec *vec; uint16_t vec_len_total; uint16_t vec_len; uint32_t tot_len; uint16_t tci; - bool vlan_stripped; + size_t ehdr_buf_len; bool has_virt_hdr; eth_pkt_types_e packet_type; @@ -88,14 +88,13 @@ net_rx_pkt_pull_data(struct NetRxPkt *pkt, const struct iovec *iov, int iovcnt, size_t ploff) { - if (pkt->vlan_stripped) { + if (pkt->ehdr_buf_len) { net_rx_pkt_iovec_realloc(pkt, iovcnt + 1); pkt->vec[0].iov_base = pkt->ehdr_buf; - pkt->vec[0].iov_len = sizeof(pkt->ehdr_buf); - - pkt->tot_len = - iov_size(iov, iovcnt) - ploff + sizeof(struct eth_header); + pkt->vec[0].iov_len = pkt->ehdr_buf_len; + + pkt->tot_len = iov_size(iov, iovcnt) - ploff + pkt->ehdr_buf_len; pkt->vec_len = iov_copy(pkt->vec + 1, pkt->vec_len_total - 1, iov, iovcnt, ploff, pkt->tot_len); @@ -123,11 +122,12 @@ void net_rx_pkt_attach_iovec(struct NetRxPkt *pkt, uint16_t tci = 0; uint16_t ploff = iovoff; assert(pkt); - pkt->vlan_stripped = false; if (strip_vlan) { - pkt->vlan_stripped = eth_strip_vlan(iov, iovcnt, iovoff, pkt->ehdr_buf, - &ploff, &tci); + pkt->ehdr_buf_len = eth_strip_vlan(iov, iovcnt, iovoff, pkt->ehdr_buf, + &ploff, &tci); + } else { + pkt->ehdr_buf_len = 0; } pkt->tci = tci; @@ -143,12 +143,13 @@ void net_rx_pkt_attach_iovec_ex(struct NetRxPkt *pkt, uint16_t tci = 0; uint16_t ploff = iovoff; assert(pkt); - pkt->vlan_stripped = false; if (strip_vlan) { - pkt->vlan_stripped = eth_strip_vlan_ex(iov, iovcnt, iovoff, vet, - pkt->ehdr_buf, - &ploff, &tci); + pkt->ehdr_buf_len = eth_strip_vlan_ex(iov, iovcnt, iovoff, vet, + pkt->ehdr_buf, + &ploff, &tci); + } else { + pkt->ehdr_buf_len = 0; } pkt->tci = tci; @@ -162,8 +163,8 @@ void net_rx_pkt_dump(struct NetRxPkt *pkt) NetRxPkt *pkt = (NetRxPkt *)pkt; assert(pkt); - printf("RX PKT: tot_len: %d, vlan_stripped: %d, vlan_tag: %d\n", - pkt->tot_len, pkt->vlan_stripped, pkt->tci); + printf("RX PKT: tot_len: %d, ehdr_buf_len: %lu, vlan_tag: %d\n", + pkt->tot_len, pkt->ehdr_buf_len, pkt->tci); #endif } @@ -426,7 +427,7 @@ bool net_rx_pkt_is_vlan_stripped(struct NetRxPkt *pkt) { assert(pkt); - return pkt->vlan_stripped; + return pkt->ehdr_buf_len ? true : false; } bool net_rx_pkt_has_virt_hdr(struct NetRxPkt *pkt) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 67fc1e7893..6233865494 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -683,14 +683,14 @@ static int megasas_map_dcmd(MegasasState *s, MegasasCmd *cmd) trace_megasas_dcmd_invalid_sge(cmd->index, cmd->frame->header.sge_count); cmd->iov_size = 0; - return -1; + return -EINVAL; } iov_pa = megasas_sgl_get_addr(cmd, &cmd->frame->dcmd.sgl); iov_size = megasas_sgl_get_len(cmd, &cmd->frame->dcmd.sgl); pci_dma_sglist_init(&cmd->qsg, PCI_DEVICE(s), 1); qemu_sglist_add(&cmd->qsg, iov_pa, iov_size); cmd->iov_size = iov_size; - return cmd->iov_size; + return 0; } static void megasas_finish_dcmd(MegasasCmd *cmd, uint32_t iov_size) @@ -1559,19 +1559,20 @@ static const struct dcmd_cmd_tbl_t { static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd) { - int opcode, len; + int opcode; int retval = 0; + size_t len; const struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl; opcode = le32_to_cpu(cmd->frame->dcmd.opcode); trace_megasas_handle_dcmd(cmd->index, opcode); - len = megasas_map_dcmd(s, cmd); - if (len < 0) { + if (megasas_map_dcmd(s, cmd) < 0) { return MFI_STAT_MEMORY_NOT_AVAILABLE; } while (cmdptr->opcode != -1 && cmdptr->opcode != opcode) { cmdptr++; } + len = cmd->iov_size; if (cmdptr->opcode == -1) { trace_megasas_dcmd_unhandled(cmd->index, opcode, len); retval = megasas_dcmd_dummy(s, cmd); diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 01fbf228be..09d6cb51d9 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -486,6 +486,11 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12); uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk); + if (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || !s->blkcnt) { + qemu_log_mask(LOG_UNIMP, "infinite transfer is not supported\n"); + return; + } + /* XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for * possible stop at page boundary if initial address is not page aligned, * allow them to work properly */ @@ -536,7 +541,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) boundary_count -= block_size - begin; } dma_memory_read(&address_space_memory, s->sdmasysad, - &s->fifo_buffer[begin], s->data_count); + &s->fifo_buffer[begin], s->data_count - begin); s->sdmasysad += s->data_count - begin; if (s->data_count == block_size) { for (n = 0; n < block_size; n++) { @@ -797,12 +802,7 @@ static void sdhci_data_transfer(void *opaque) if (s->trnmod & SDHC_TRNS_DMA) { switch (SDHC_DMA_TYPE(s->hostctl)) { case SDHC_CTRL_SDMA: - if ((s->trnmod & SDHC_TRNS_MULTI) && - (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || s->blkcnt == 0)) { - break; - } - - if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) { + if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) { sdhci_sdma_transfer_single_block(s); } else { sdhci_sdma_transfer_multi_blocks(s); diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 89e11b68c4..1325ea1659 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -967,7 +967,7 @@ static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv) DPRINTF(s, 1, "%s: seq %d, len %d\n", __func__, recv->hdr.bSeq, len); ccid_add_pending_answer(s, (CCID_Header *)recv); - if (s->card) { + if (s->card && len <= BULK_OUT_DATA_SIZE) { ccid_card_apdu_from_guest(s->card, recv->abData, len); } else { DPRINTF(s, D_WARN, "warning: discarded apdu\n"); diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index c82a92fff7..21c93e0372 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -42,6 +42,8 @@ #define OHCI_MAX_PORTS 15 +#define ED_LINK_LIMIT 4 + static int64_t usb_frame_time; static int64_t usb_bit_time; @@ -725,7 +727,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, if (ohci_read_iso_td(ohci, addr, &iso_td)) { trace_usb_ohci_iso_td_read_failed(addr); ohci_die(ohci); - return 0; + return 1; } starting_frame = OHCI_BM(iso_td.flags, TD_SF); @@ -1184,7 +1186,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) uint32_t next_ed; uint32_t cur; int active; - + uint32_t link_cnt = 0; active = 0; if (head == 0) @@ -1199,6 +1201,11 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) next_ed = ed.next & OHCI_DPTR_MASK; + if (++link_cnt > ED_LINK_LIMIT) { + ohci_die(ohci); + return 0; + } + if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) { uint32_t addr; /* Cancel pending packets for ED that have been paused. */ diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 4acf0c6dd8..f1ff9e60e7 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -54,6 +54,8 @@ #define ER_FULL_HACK #define TRB_LINK_LIMIT 4 +#define COMMAND_LIMIT 256 +#define TRANSFER_LIMIT 256 #define LEN_CAP 0x40 #define LEN_OPER (0x400 + 0x10 * MAXPORTS) @@ -390,6 +392,7 @@ struct XHCIEPContext { dma_addr_t pctx; unsigned int max_psize; uint32_t state; + uint32_t kick_active; /* streams */ unsigned int max_pstreams; @@ -1031,6 +1034,7 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb, } ring->dequeue = xhci_mask64(trb->parameter); if (trb->control & TRB_LK_TC) { + trace_usb_xhci_enforced_limit("trb-link"); ring->ccs = !ring->ccs; } } @@ -2138,7 +2142,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, epid, slotid); return; } - + if (epctx->kick_active) { + return; + } xhci_kick_epctx(epctx, streamid); } @@ -2150,10 +2156,12 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) XHCIRing *ring; USBEndpoint *ep = NULL; uint64_t mfindex; + unsigned int count = 0; int length; int i; trace_usb_xhci_ep_kick(epctx->slotid, epctx->epid, streamid); + assert(!epctx->kick_active); /* If the device has been detached, but the guest has not noticed this yet the 2 above checks will succeed, but we must NOT continue */ @@ -2223,6 +2231,7 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) } assert(ring->dequeue != 0); + epctx->kick_active++; while (1) { length = xhci_ring_chain_length(xhci, ring); if (length <= 0) { @@ -2258,7 +2267,12 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) epctx->retry = xfer; break; } + if (count++ > TRANSFER_LIMIT) { + trace_usb_xhci_enforced_limit("transfers"); + break; + } } + epctx->kick_active--; ep = xhci_epid_to_usbep(epctx); if (ep) { @@ -2729,7 +2743,7 @@ static void xhci_process_commands(XHCIState *xhci) TRBType type; XHCIEvent event = {ER_COMMAND_COMPLETE, CC_SUCCESS}; dma_addr_t addr; - unsigned int i, slotid = 0; + unsigned int i, slotid = 0, count = 0; DPRINTF("xhci_process_commands()\n"); if (!xhci_running(xhci)) { @@ -2843,6 +2857,11 @@ static void xhci_process_commands(XHCIState *xhci) } event.slotid = slotid; xhci_event(xhci, &event, 0); + + if (count++ > COMMAND_LIMIT) { + trace_usb_xhci_enforced_limit("commands"); + return; + } } } diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 2d42fd45da..a2c85f0576 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -175,6 +175,7 @@ usb_xhci_xfer_retry(void *xfer) "%p" usb_xhci_xfer_success(void *xfer, uint32_t bytes) "%p: len %d" usb_xhci_xfer_error(void *xfer, uint32_t ret) "%p: ret %d" usb_xhci_unimplemented(const char *item, int nr) "%s (0x%x)" +usb_xhci_enforced_limit(const char *item) "%s" # hw/usb/desc.c usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d" diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 2f2467e859..c23e1ad458 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -416,7 +416,7 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev, uint32_t hash_start_src_offset = 0, len_to_hash = 0; uint32_t cipher_start_src_offset = 0, len_to_cipher = 0; - size_t max_len, curr_size = 0; + uint64_t max_len, curr_size = 0; size_t s; /* Plain cipher */ @@ -441,7 +441,7 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev, return NULL; } - max_len = iv_len + aad_len + src_len + dst_len + hash_result_len; + 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; diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index a83d951213..49b3cd188a 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -428,6 +428,14 @@ static void i6300esb_realize(PCIDevice *dev, Error **errp) /* qemu_register_coalesced_mmio (addr, 0x10); ? */ } +static void i6300esb_exit(PCIDevice *dev) +{ + I6300State *d = WATCHDOG_I6300ESB_DEVICE(dev); + + timer_del(d->timer); + timer_free(d->timer); +} + static WatchdogTimerModel model = { .wdt_name = "i6300esb", .wdt_description = "Intel 6300ESB", @@ -441,6 +449,7 @@ static void i6300esb_class_init(ObjectClass *klass, void *data) k->config_read = i6300esb_config_read; k->config_write = i6300esb_config_write; k->realize = i6300esb_realize; + k->exit = i6300esb_exit; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_ESB_9; k->class_id = PCI_CLASS_SYSTEM_OTHER; |