diff options
Diffstat (limited to 'hw')
33 files changed, 1613 insertions, 185 deletions
diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index 8f255e91c0..18082dffe8 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -19,6 +19,7 @@ #include "qemu/rcu.h" #include "qemu/rcu_queue.h" #include "qemu/cutils.h" +#include "sysemu/qtest.h" /* Root node for synth file system */ static V9fsSynthNode synth_root = { @@ -514,6 +515,26 @@ static int synth_unlinkat(FsContext *ctx, V9fsPath *dir, return -1; } +static ssize_t v9fs_synth_qtest_write(void *buf, int len, off_t offset, + void *arg) +{ + return 1; +} + +static ssize_t v9fs_synth_qtest_flush_write(void *buf, int len, off_t offset, + void *arg) +{ + bool should_block = !!*(uint8_t *)buf; + + if (should_block) { + /* This will cause the server to call us again until we're cancelled */ + errno = EINTR; + return -1; + } + + return 1; +} + static int synth_init(FsContext *ctx, Error **errp) { QLIST_INIT(&synth_root.child); @@ -527,6 +548,37 @@ static int synth_init(FsContext *ctx, Error **errp) /* Mark the subsystem is ready for use */ synth_fs = 1; + + if (qtest_enabled()) { + V9fsSynthNode *node = NULL; + int i, ret; + + /* Directory hierarchy for WALK test */ + for (i = 0; i < P9_MAXWELEM; i++) { + char *name = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i); + + ret = qemu_v9fs_synth_mkdir(node, 0700, name, &node); + assert(!ret); + g_free(name); + } + + /* File for LOPEN test */ + ret = qemu_v9fs_synth_add_file(NULL, 0, QTEST_V9FS_SYNTH_LOPEN_FILE, + NULL, NULL, ctx); + assert(!ret); + + /* File for WRITE test */ + ret = qemu_v9fs_synth_add_file(NULL, 0, QTEST_V9FS_SYNTH_WRITE_FILE, + NULL, v9fs_synth_qtest_write, ctx); + assert(!ret); + + /* File for FLUSH test */ + ret = qemu_v9fs_synth_add_file(NULL, 0, QTEST_V9FS_SYNTH_FLUSH_FILE, + NULL, v9fs_synth_qtest_flush_write, + ctx); + assert(!ret); + } + return 0; } diff --git a/hw/9pfs/9p-synth.h b/hw/9pfs/9p-synth.h index 49c2fc7b27..af7a993a1e 100644 --- a/hw/9pfs/9p-synth.h +++ b/hw/9pfs/9p-synth.h @@ -49,4 +49,17 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, const char *name, v9fs_synth_read read, v9fs_synth_write write, void *arg); +/* qtest stuff */ + +#define QTEST_V9FS_SYNTH_WALK_FILE "WALK%d" +#define QTEST_V9FS_SYNTH_LOPEN_FILE "LOPEN" +#define QTEST_V9FS_SYNTH_WRITE_FILE "WRITE" + +/* Any write to the "FLUSH" file is handled one byte at a time by the + * backend. If the byte is zero, the backend returns success (ie, 1), + * otherwise it forces the server to try again forever. Thus allowing + * the client to cancel the request. + */ +#define QTEST_V9FS_SYNTH_FLUSH_FILE "FLUSH" + #endif diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 909a611394..85a1ed8171 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -24,6 +24,7 @@ #include "coth.h" #include "trace.h" #include "migration/blocker.h" +#include "sysemu/qtest.h" int open_fd_hw; int total_open_fd; @@ -630,6 +631,24 @@ static void coroutine_fn pdu_complete(V9fsPDU *pdu, ssize_t len) V9fsState *s = pdu->s; int ret; + /* + * The 9p spec requires that successfully cancelled pdus receive no reply. + * Sending a reply would confuse clients because they would + * assume that any EINTR is the actual result of the operation, + * rather than a consequence of the cancellation. However, if + * the operation completed (succesfully or with an error other + * than caused be cancellation), we do send out that reply, both + * for efficiency and to avoid confusing the rest of the state machine + * that assumes passing a non-error here will mean a successful + * transmission of the reply. + */ + bool discard = pdu->cancelled && len == -EINTR; + if (discard) { + trace_v9fs_rcancel(pdu->tag, pdu->id); + pdu->size = 0; + goto out_notify; + } + if (len < 0) { int err = -len; len = 7; @@ -3485,7 +3504,8 @@ void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr) } /* Returns 0 on success, 1 on failure. */ -int v9fs_device_realize_common(V9fsState *s, Error **errp) +int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t, + Error **errp) { int i, len; struct stat stat; @@ -3493,6 +3513,9 @@ int v9fs_device_realize_common(V9fsState *s, Error **errp) V9fsPath path; int rc = 1; + assert(!s->transport); + s->transport = t; + /* initialize pdu allocator */ QLIST_INIT(&s->free_list); QLIST_INIT(&s->active_list); diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index ffe658ab89..5ced427d86 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -346,7 +346,8 @@ void v9fs_path_sprintf(V9fsPath *path, const char *fmt, ...); void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs); int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath, const char *name, V9fsPath *path); -int v9fs_device_realize_common(V9fsState *s, Error **errp); +int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t, + Error **errp); void v9fs_device_unrealize_common(V9fsState *s, Error **errp); V9fsPDU *pdu_alloc(V9fsState *s); @@ -366,11 +367,4 @@ struct V9fsTransport { void (*push_and_notify)(V9fsPDU *pdu); }; -static inline int v9fs_register_transport(V9fsState *s, const V9fsTransport *t) -{ - assert(!s->transport); - s->transport = t; - return 0; -} - #endif diff --git a/hw/9pfs/trace-events b/hw/9pfs/trace-events index 08a4abf22e..1aee350c42 100644 --- a/hw/9pfs/trace-events +++ b/hw/9pfs/trace-events @@ -1,6 +1,7 @@ # See docs/devel/tracing.txt for syntax documentation. # hw/9pfs/virtio-9p.c +v9fs_rcancel(uint16_t tag, uint8_t id) "tag %d id %d" v9fs_rerror(uint16_t tag, uint8_t id, int err) "tag %d id %d err %d" v9fs_version(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s" v9fs_version_return(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s" diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 43f4e53f33..775e8ff766 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -198,17 +198,13 @@ static void virtio_9p_device_realize(DeviceState *dev, Error **errp) V9fsVirtioState *v = VIRTIO_9P(dev); V9fsState *s = &v->state; - if (v9fs_device_realize_common(s, errp)) { - goto out; + if (v9fs_device_realize_common(s, &virtio_9p_transport, errp)) { + return; } v->config_size = sizeof(struct virtio_9p_config) + strlen(s->fsconf.tag); virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P, v->config_size); v->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output); - v9fs_register_transport(s, &virtio_9p_transport); - -out: - return; } static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp) diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index df2a4100bf..14f0d6a50e 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -446,7 +446,6 @@ static int xen_9pfs_connect(struct XenDevice *xendev) xen_9pdev->id = s->fsconf.fsdev_id = g_strdup_printf("xen9p%d", xendev->dev); xen_9pdev->tag = s->fsconf.tag = xenstore_read_fe_str(xendev, "tag"); - v9fs_register_transport(s, &xen_9p_transport); fsdev = qemu_opts_create(qemu_find_opts("fsdev"), s->fsconf.tag, 1, NULL); @@ -455,7 +454,7 @@ static int xen_9pfs_connect(struct XenDevice *xendev) qemu_opt_set(fsdev, "security_model", xen_9pdev->security_model, NULL); qemu_opts_set_id(fsdev, s->fsconf.fsdev_id); qemu_fsdev_add(fsdev); - v9fs_device_realize_common(s, NULL); + v9fs_device_realize_common(s, &xen_9p_transport, NULL); return 0; diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index b648770882..4172caf5db 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -25,6 +25,7 @@ #include "hw/block/flash.h" #include "ui/console.h" #include "hw/i2c/i2c.h" +#include "hw/audio/wm8750.h" #include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "ui/pixel_ops.h" @@ -1691,7 +1692,7 @@ static void musicpal_init(MachineState *machine) qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15)); } - wm8750_dev = i2c_create_slave(i2c, "wm8750", MP_WM_ADDR); + wm8750_dev = i2c_create_slave(i2c, TYPE_WM8750, MP_WM_ADDR); dev = qdev_create(NULL, "mv88w8618_audio"); s = SYS_BUS_DEVICE(dev); qdev_prop_set_ptr(dev, "wm8750", wm8750_dev); diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index ac1e15cbbc..e419e3c00e 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -24,6 +24,7 @@ #include "hw/devices.h" #include "hw/arm/sharpsl.h" #include "ui/console.h" +#include "hw/audio/wm8750.h" #include "audio/audio.h" #include "hw/boards.h" #include "sysemu/block-backend.h" @@ -745,7 +746,7 @@ static void spitz_i2c_setup(PXA2xxState *cpu) DeviceState *wm; /* Attach a WM8750 to the bus */ - wm = i2c_create_slave(bus, "wm8750", 0); + wm = i2c_create_slave(bus, TYPE_WM8750, 0); spitz_wm8750_addr(wm, 0, 0); qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_WM, diff --git a/hw/arm/z2.c b/hw/arm/z2.c index 60561c7b7c..300e933c82 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -23,6 +23,7 @@ #include "hw/block/flash.h" #include "sysemu/block-backend.h" #include "ui/console.h" +#include "hw/audio/wm8750.h" #include "audio/audio.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" @@ -346,7 +347,7 @@ static void z2_init(MachineState *machine) z2_lcd = ssi_create_slave(mpu->ssp[1], "zipit-lcd"); bus = pxa2xx_i2c_bus(mpu->i2c[0]); i2c_create_slave(bus, TYPE_AER915, 0x55); - wm = i2c_create_slave(bus, "wm8750", 0x1b); + wm = i2c_create_slave(bus, TYPE_WM8750, 0x1b); mpu->i2s->opaque = wm; mpu->i2s->codec_out = wm8750_dac_dat; mpu->i2s->codec_in = wm8750_adc_dat; diff --git a/hw/audio/marvell_88w8618.c b/hw/audio/marvell_88w8618.c index 4f65f8c199..e546892d3c 100644 --- a/hw/audio/marvell_88w8618.c +++ b/hw/audio/marvell_88w8618.c @@ -13,6 +13,7 @@ #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/i2c/i2c.h" +#include "hw/audio/wm8750.h" #include "audio/audio.h" #define MP_AUDIO_SIZE 0x00001000 diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index 6ab2f6f89a..31de264ab7 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -29,6 +29,8 @@ #include "hw/qdev.h" #include "qemu/timer.h" #include "qemu/host-utils.h" +#include "qemu/log.h" +#include "qapi/error.h" #define dolog(...) AUD_log ("sb16", __VA_ARGS__) @@ -123,7 +125,7 @@ static int magic_of_irq (int irq) case 10: return 8; default: - dolog ("bad irq %d\n", irq); + qemu_log_mask(LOG_GUEST_ERROR, "bad irq %d\n", irq); return 2; } } @@ -140,7 +142,7 @@ static int irq_of_magic (int magic) case 8: return 10; default: - dolog ("bad irq magic %d\n", magic); + qemu_log_mask(LOG_GUEST_ERROR, "bad irq magic %d\n", magic); return -1; } } @@ -258,8 +260,8 @@ static void dma_cmd8 (SB16State *s, int mask, int dma_len) s->align = (1 << s->fmt_stereo) - 1; if (s->block_size & s->align) { - dolog ("warning: misaligned block size %d, alignment %d\n", - s->block_size, s->align + 1); + qemu_log_mask(LOG_GUEST_ERROR, "warning: misaligned block size %d," + " alignment %d\n", s->block_size, s->align + 1); } ldebug ("freq %d, stereo %d, sign %d, bits %d, " @@ -338,8 +340,8 @@ static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len) s->highspeed = 0; s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1; if (s->block_size & s->align) { - dolog ("warning: misaligned block size %d, alignment %d\n", - s->block_size, s->align + 1); + qemu_log_mask(LOG_GUEST_ERROR, "warning: misaligned block size %d," + " alignment %d\n", s->block_size, s->align + 1); } if (s->freq) { @@ -391,7 +393,8 @@ static void command (SB16State *s, uint8_t cmd) if (cmd > 0xaf && cmd < 0xd0) { if (cmd & 8) { - dolog ("ADC not yet supported (command %#x)\n", cmd); + qemu_log_mask(LOG_UNIMP, "ADC not yet supported (command %#x)\n", + cmd); } switch (cmd >> 4) { @@ -399,7 +402,7 @@ static void command (SB16State *s, uint8_t cmd) case 12: break; default: - dolog ("%#x wrong bits\n", cmd); + qemu_log_mask(LOG_GUEST_ERROR, "%#x wrong bits\n", cmd); } s->needed_bytes = 3; } @@ -453,7 +456,7 @@ static void command (SB16State *s, uint8_t cmd) goto warn; case 0x35: - dolog ("0x35 - MIDI command not implemented\n"); + qemu_log_mask(LOG_UNIMP, "0x35 - MIDI command not implemented\n"); break; case 0x40: @@ -487,34 +490,38 @@ static void command (SB16State *s, uint8_t cmd) case 0x74: s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */ - dolog ("0x75 - DMA DAC, 4-bit ADPCM not implemented\n"); + qemu_log_mask(LOG_UNIMP, "0x75 - DMA DAC, 4-bit ADPCM not" + " implemented\n"); break; case 0x75: /* DMA DAC, 4-bit ADPCM Reference */ s->needed_bytes = 2; - dolog ("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n"); + qemu_log_mask(LOG_UNIMP, "0x74 - DMA DAC, 4-bit ADPCM Reference not" + " implemented\n"); break; case 0x76: /* DMA DAC, 2.6-bit ADPCM */ s->needed_bytes = 2; - dolog ("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n"); + qemu_log_mask(LOG_UNIMP, "0x74 - DMA DAC, 2.6-bit ADPCM not" + " implemented\n"); break; case 0x77: /* DMA DAC, 2.6-bit ADPCM Reference */ s->needed_bytes = 2; - dolog ("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n"); + qemu_log_mask(LOG_UNIMP, "0x74 - DMA DAC, 2.6-bit ADPCM Reference" + " not implemented\n"); break; case 0x7d: - dolog ("0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n"); - dolog ("not implemented\n"); + qemu_log_mask(LOG_UNIMP, "0x7d - Autio-Initialize DMA DAC, 4-bit" + " ADPCM Reference\n"); + qemu_log_mask(LOG_UNIMP, "not implemented\n"); break; case 0x7f: - dolog ( - "0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n" - ); - dolog ("not implemented\n"); + qemu_log_mask(LOG_UNIMP, "0x7d - Autio-Initialize DMA DAC, 2.6-bit" + " ADPCM Reference\n"); + qemu_log_mask(LOG_UNIMP, "not implemented\n"); break; case 0x80: @@ -586,7 +593,7 @@ static void command (SB16State *s, uint8_t cmd) break; case 0xe7: - dolog ("Attempt to probe for ESS (0xe7)?\n"); + qemu_log_mask(LOG_UNIMP, "Attempt to probe for ESS (0xe7)?\n"); break; case 0xe8: /* read test reg */ @@ -613,7 +620,7 @@ static void command (SB16State *s, uint8_t cmd) goto warn; default: - dolog ("Unrecognized command %#x\n", cmd); + qemu_log_mask(LOG_UNIMP, "Unrecognized command %#x\n", cmd); break; } } @@ -632,8 +639,8 @@ static void command (SB16State *s, uint8_t cmd) return; warn: - dolog ("warning: command %#x,%d is not truly understood yet\n", - cmd, s->needed_bytes); + qemu_log_mask(LOG_UNIMP, "warning: command %#x,%d is not truly understood" + " yet\n", cmd, s->needed_bytes); goto exit; } @@ -735,9 +742,8 @@ static void complete (SB16State *s) break; case 0x42: /* FT2 sets output freq with this, go figure */ -#if 0 - dolog ("cmd 0x42 might not do what it think it should\n"); -#endif + qemu_log_mask(LOG_UNIMP, "cmd 0x42 might not do what it think it" + " should\n"); case 0x41: s->freq = dsp_get_hilo (s); ldebug ("set freq %d\n", s->freq); @@ -820,7 +826,8 @@ static void complete (SB16State *s) break; default: - dolog ("complete: unrecognized command %#x\n", s->cmd); + qemu_log_mask(LOG_UNIMP, "complete: unrecognized command %#x\n", + s->cmd); return; } } @@ -1095,10 +1102,9 @@ static void mixer_write_datab(void *opaque, uint32_t nport, uint32_t val) dma = ctz32 (val & 0xf); hdma = ctz32 (val & 0xf0); if (dma != s->dma || hdma != s->hdma) { - dolog ( - "attempt to change DMA " - "8bit %d(%d), 16bit %d(%d) (val=%#x)\n", - dma, s->dma, hdma, s->hdma, val); + qemu_log_mask(LOG_GUEST_ERROR, "attempt to change DMA 8bit" + " %d(%d), 16bit %d(%d) (val=%#x)\n", dma, s->dma, + hdma, s->hdma, val); } #if 0 s->dma = dma; @@ -1108,8 +1114,8 @@ static void mixer_write_datab(void *opaque, uint32_t nport, uint32_t val) break; case 0x82: - dolog ("attempt to write into IRQ status register (val=%#x)\n", - val); + qemu_log_mask(LOG_GUEST_ERROR, "attempt to write into IRQ status" + " register (val=%#x)\n", val); return; default: @@ -1181,8 +1187,9 @@ static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) int till, copy, written, free; if (s->block_size <= 0) { - dolog ("invalid block size=%d nchan=%d dma_pos=%d dma_len=%d\n", - s->block_size, nchan, dma_pos, dma_len); + qemu_log_mask(LOG_GUEST_ERROR, "invalid block size=%d nchan=%d" + " dma_pos=%d dma_len=%d\n", s->block_size, nchan, + dma_pos, dma_len); return dma_pos; } @@ -1376,7 +1383,7 @@ static void sb16_realizefn (DeviceState *dev, Error **errp) reset_mixer (s); s->aux_ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, aux_timer, s); if (!s->aux_ts) { - dolog ("warning: Could not create auxiliary timer\n"); + error_setg(errp, "warning: Could not create auxiliary timer"); } isa_register_portio_list(isadev, &s->portio_list, s->port, diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c index 6d8b728923..416a78e869 100644 --- a/hw/audio/wm8750.c +++ b/hw/audio/wm8750.c @@ -8,8 +8,8 @@ */ #include "qemu/osdep.h" -#include "hw/hw.h" #include "hw/i2c/i2c.h" +#include "hw/audio/wm8750.h" #include "audio/audio.h" #define IN_PORT_N 3 @@ -24,7 +24,6 @@ typedef struct { int dac_hz; } WMRate; -#define TYPE_WM8750 "wm8750" #define WM8750(obj) OBJECT_CHECK(WM8750State, (obj), TYPE_WM8750) typedef struct WM8750State { @@ -639,8 +638,7 @@ static void wm8750_fini(I2CSlave *i2c) } #endif -void wm8750_data_req_set(DeviceState *dev, - void (*data_req)(void *, int, int), void *opaque) +void wm8750_data_req_set(DeviceState *dev, data_req_cb *data_req, void *opaque) { WM8750State *s = WM8750(dev); diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index ef92c4ad6f..3519dc80b1 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -28,10 +28,16 @@ static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) VirtIOGPU *g = &vgpu->vdev; DeviceState *vdev = DEVICE(&vgpu->vdev); int i; + Error *local_error = NULL; qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); virtio_pci_force_virtio_1(vpci_dev); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); + object_property_set_bool(OBJECT(vdev), true, "realized", &local_error); + + if (local_error) { + error_propagate(errp, local_error); + return; + } for (i = 0; i < g->conf.max_outputs; i++) { object_property_set_link(OBJECT(g->scanout[i].con), diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 274e365713..6658f6c6a6 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1173,6 +1173,11 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) Error *local_err = NULL; int i; + if (virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { + error_setg(errp, "virtio-gpu does not support vIOMMU yet"); + return; + } + if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) { error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOUTS); return; diff --git a/hw/hppa/Makefile.objs b/hw/hppa/Makefile.objs new file mode 100644 index 0000000000..bef241ed25 --- /dev/null +++ b/hw/hppa/Makefile.objs @@ -0,0 +1 @@ +obj-y += machine.o pci.o dino.o diff --git a/hw/hppa/dino.c b/hw/hppa/dino.c new file mode 100644 index 0000000000..15aefde09c --- /dev/null +++ b/hw/hppa/dino.c @@ -0,0 +1,518 @@ +/* + * HP-PARISC Dino PCI chipset emulation. + * + * (C) 2017 by Helge Deller <deller@gmx.de> + * + * This work is licensed under the GNU GPL license version 2 or later. + * + * Documentation available at: + * https://parisc.wiki.kernel.org/images-parisc/9/91/Dino_ers.pdf + * https://parisc.wiki.kernel.org/images-parisc/7/70/Dino_3_1_Errata.pdf + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "hw/hw.h" +#include "hw/devices.h" +#include "sysemu/sysemu.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "hppa_sys.h" +#include "exec/address-spaces.h" + + +#define TYPE_DINO_PCI_HOST_BRIDGE "dino-pcihost" + +#define DINO_IAR0 0x004 +#define DINO_IODC 0x008 +#define DINO_IRR0 0x00C /* RO */ +#define DINO_IAR1 0x010 +#define DINO_IRR1 0x014 /* RO */ +#define DINO_IMR 0x018 +#define DINO_IPR 0x01C +#define DINO_TOC_ADDR 0x020 +#define DINO_ICR 0x024 +#define DINO_ILR 0x028 /* RO */ +#define DINO_IO_COMMAND 0x030 /* WO */ +#define DINO_IO_STATUS 0x034 /* RO */ +#define DINO_IO_CONTROL 0x038 +#define DINO_IO_GSC_ERR_RESP 0x040 /* RO */ +#define DINO_IO_ERR_INFO 0x044 /* RO */ +#define DINO_IO_PCI_ERR_RESP 0x048 /* RO */ +#define DINO_IO_FBB_EN 0x05c +#define DINO_IO_ADDR_EN 0x060 +#define DINO_PCI_CONFIG_ADDR 0x064 +#define DINO_PCI_CONFIG_DATA 0x068 +#define DINO_PCI_IO_DATA 0x06c +#define DINO_PCI_MEM_DATA 0x070 /* Dino 3.x only */ +#define DINO_GSC2X_CONFIG 0x7b4 /* RO */ +#define DINO_GMASK 0x800 +#define DINO_PAMR 0x804 +#define DINO_PAPR 0x808 +#define DINO_DAMODE 0x80c +#define DINO_PCICMD 0x810 +#define DINO_PCISTS 0x814 /* R/WC */ +#define DINO_MLTIM 0x81c +#define DINO_BRDG_FEAT 0x820 +#define DINO_PCIROR 0x824 +#define DINO_PCIWOR 0x828 +#define DINO_TLTIM 0x830 + +#define DINO_IRQS 11 /* bits 0-10 are architected */ +#define DINO_IRR_MASK 0x5ff /* only 10 bits are implemented */ +#define DINO_LOCAL_IRQS (DINO_IRQS + 1) +#define DINO_MASK_IRQ(x) (1 << (x)) + +#define PCIINTA 0x001 +#define PCIINTB 0x002 +#define PCIINTC 0x004 +#define PCIINTD 0x008 +#define PCIINTE 0x010 +#define PCIINTF 0x020 +#define GSCEXTINT 0x040 +/* #define xxx 0x080 - bit 7 is "default" */ +/* #define xxx 0x100 - bit 8 not used */ +/* #define xxx 0x200 - bit 9 not used */ +#define RS232INT 0x400 + +#define DINO_MEM_CHUNK_SIZE (8 * 1024 * 1024) /* 8MB */ + +#define DINO_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(DinoState, (obj), TYPE_DINO_PCI_HOST_BRIDGE) + +typedef struct DinoState { + PCIHostState parent_obj; + + /* PCI_CONFIG_ADDR is parent_obj.config_reg, via pci_host_conf_be_ops, + so that we can map PCI_CONFIG_DATA to pci_host_data_be_ops. */ + + uint32_t iar0; + uint32_t iar1; + uint32_t imr; + uint32_t ipr; + uint32_t icr; + uint32_t ilr; + uint32_t io_addr_en; + uint32_t io_control; + + MemoryRegion this_mem; + MemoryRegion pci_mem; + MemoryRegion pci_mem_alias[32]; + + AddressSpace bm_as; + MemoryRegion bm; + MemoryRegion bm_ram_alias; + MemoryRegion bm_pci_alias; + + MemoryRegion cpu0_eir_mem; +} DinoState; + +/* + * Dino can forward memory accesses from the CPU in the range between + * 0xf0800000 and 0xff000000 to the PCI bus. + */ +static void gsc_to_pci_forwarding(DinoState *s) +{ + uint32_t io_addr_en, tmp; + int enabled, i; + + tmp = extract32(s->io_control, 7, 2); + enabled = (tmp == 0x01); + io_addr_en = s->io_addr_en; + + memory_region_transaction_begin(); + for (i = 1; i < 31; i++) { + MemoryRegion *mem = &s->pci_mem_alias[i]; + if (enabled && (io_addr_en & (1U << i))) { + if (!memory_region_is_mapped(mem)) { + uint32_t addr = 0xf0000000 + i * DINO_MEM_CHUNK_SIZE; + memory_region_add_subregion(get_system_memory(), addr, mem); + } + } else if (memory_region_is_mapped(mem)) { + memory_region_del_subregion(get_system_memory(), mem); + } + } + memory_region_transaction_commit(); +} + +static bool dino_chip_mem_valid(void *opaque, hwaddr addr, + unsigned size, bool is_write) +{ + switch (addr) { + case DINO_IAR0: + case DINO_IAR1: + case DINO_IRR0: + case DINO_IRR1: + case DINO_IMR: + case DINO_IPR: + case DINO_ICR: + case DINO_ILR: + case DINO_IO_CONTROL: + case DINO_IO_ADDR_EN: + case DINO_PCI_IO_DATA: + return true; + case DINO_PCI_IO_DATA + 2: + return size <= 2; + case DINO_PCI_IO_DATA + 1: + case DINO_PCI_IO_DATA + 3: + return size == 1; + } + return false; +} + +static MemTxResult dino_chip_read_with_attrs(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + DinoState *s = opaque; + MemTxResult ret = MEMTX_OK; + AddressSpace *io; + uint16_t ioaddr; + uint32_t val; + + switch (addr) { + case DINO_PCI_IO_DATA ... DINO_PCI_IO_DATA + 3: + /* Read from PCI IO space. */ + io = &address_space_io; + ioaddr = s->parent_obj.config_reg; + switch (size) { + case 1: + val = address_space_ldub(io, ioaddr, attrs, &ret); + break; + case 2: + val = address_space_lduw_be(io, ioaddr, attrs, &ret); + break; + case 4: + val = address_space_ldl_be(io, ioaddr, attrs, &ret); + break; + default: + g_assert_not_reached(); + } + break; + + case DINO_IO_ADDR_EN: + val = s->io_addr_en; + break; + case DINO_IO_CONTROL: + val = s->io_control; + break; + + case DINO_IAR0: + val = s->iar0; + break; + case DINO_IAR1: + val = s->iar1; + break; + case DINO_IMR: + val = s->imr; + break; + case DINO_ICR: + val = s->icr; + break; + case DINO_IPR: + val = s->ipr; + /* Any read to IPR clears the register. */ + s->ipr = 0; + break; + case DINO_ILR: + val = s->ilr; + break; + case DINO_IRR0: + val = s->ilr & s->imr & ~s->icr; + break; + case DINO_IRR1: + val = s->ilr & s->imr & s->icr; + break; + + default: + /* Controlled by dino_chip_mem_valid above. */ + g_assert_not_reached(); + } + + *data = val; + return ret; +} + +static MemTxResult dino_chip_write_with_attrs(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + DinoState *s = opaque; + AddressSpace *io; + MemTxResult ret; + uint16_t ioaddr; + + switch (addr) { + case DINO_IO_DATA ... DINO_PCI_IO_DATA + 3: + /* Write into PCI IO space. */ + io = &address_space_io; + ioaddr = s->parent_obj.config_reg; + switch (size) { + case 1: + address_space_stb(io, ioaddr, val, attrs, &ret); + break; + case 2: + address_space_stw_be(io, ioaddr, val, attrs, &ret); + break; + case 4: + address_space_stl_be(io, ioaddr, val, attrs, &ret); + break; + default: + g_assert_not_reached(); + } + return ret; + + case DINO_IO_ADDR_EN: + /* Never allow first (=firmware) and last (=Dino) areas. */ + s->io_addr_en = val & 0x7ffffffe; + gsc_to_pci_forwarding(s); + break; + case DINO_IO_CONTROL: + s->io_control = val; + gsc_to_pci_forwarding(s); + break; + + case DINO_IAR0: + s->iar0 = val; + break; + case DINO_IAR1: + s->iar1 = val; + break; + case DINO_IMR: + s->imr = val; + break; + case DINO_ICR: + s->icr = val; + break; + case DINO_IPR: + /* Any write to IPR clears the register. */ + s->ipr = 0; + break; + + case DINO_ILR: + case DINO_IRR0: + case DINO_IRR1: + /* These registers are read-only. */ + break; + + default: + /* Controlled by dino_chip_mem_valid above. */ + g_assert_not_reached(); + } + return MEMTX_OK; +} + +static const MemoryRegionOps dino_chip_ops = { + .read_with_attrs = dino_chip_read_with_attrs, + .write_with_attrs = dino_chip_write_with_attrs, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + .accepts = dino_chip_mem_valid, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static const VMStateDescription vmstate_dino = { + .name = "Dino", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(iar0, DinoState), + VMSTATE_UINT32(iar1, DinoState), + VMSTATE_UINT32(imr, DinoState), + VMSTATE_UINT32(ipr, DinoState), + VMSTATE_UINT32(icr, DinoState), + VMSTATE_UINT32(ilr, DinoState), + VMSTATE_UINT32(io_addr_en, DinoState), + VMSTATE_UINT32(io_control, DinoState), + VMSTATE_END_OF_LIST() + } +}; + + +/* Unlike pci_config_data_le_ops, no check of high bit set in config_reg. */ + +static uint64_t dino_config_data_read(void *opaque, hwaddr addr, unsigned len) +{ + PCIHostState *s = opaque; + return pci_data_read(s->bus, s->config_reg | (addr & 3), len); +} + +static void dino_config_data_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + PCIHostState *s = opaque; + pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); +} + +static const MemoryRegionOps dino_config_data_ops = { + .read = dino_config_data_read, + .write = dino_config_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static AddressSpace *dino_pcihost_set_iommu(PCIBus *bus, void *opaque, + int devfn) +{ + DinoState *s = opaque; + + return &s->bm_as; +} + +/* + * Dino interrupts are connected as shown on Page 78, Table 23 + * (Little-endian bit numbers) + * 0 PCI INTA + * 1 PCI INTB + * 2 PCI INTC + * 3 PCI INTD + * 4 PCI INTE + * 5 PCI INTF + * 6 GSC External Interrupt + * 7 Bus Error for "less than fatal" mode + * 8 PS2 + * 9 Unused + * 10 RS232 + */ + +static void dino_set_irq(void *opaque, int irq, int level) +{ + DinoState *s = opaque; + uint32_t bit = 1u << irq; + uint32_t old_ilr = s->ilr; + + if (level) { + uint32_t ena = bit & ~old_ilr; + s->ipr |= ena; + s->ilr = old_ilr | bit; + if (ena & s->imr) { + uint32_t iar = (ena & s->icr ? s->iar1 : s->iar0); + stl_be_phys(&address_space_memory, iar & -32, iar & 31); + } + } else { + s->ilr = old_ilr & ~bit; + } +} + +static int dino_pci_map_irq(PCIDevice *d, int irq_num) +{ + int slot = d->devfn >> 3; + int local_irq; + + assert(irq_num >= 0 && irq_num <= 3); + + local_irq = slot & 0x03; + + return local_irq; +} + +static void dino_set_timer_irq(void *opaque, int irq, int level) +{ + /* ??? Not connected. */ +} + +static void dino_set_serial_irq(void *opaque, int irq, int level) +{ + dino_set_irq(opaque, 10, level); +} + +PCIBus *dino_init(MemoryRegion *addr_space, + qemu_irq *p_rtc_irq, qemu_irq *p_ser_irq) +{ + DeviceState *dev; + DinoState *s; + PCIBus *b; + int i; + + dev = qdev_create(NULL, TYPE_DINO_PCI_HOST_BRIDGE); + s = DINO_PCI_HOST_BRIDGE(dev); + + /* Dino PCI access from main memory. */ + memory_region_init_io(&s->this_mem, OBJECT(s), &dino_chip_ops, + s, "dino", 4096); + memory_region_add_subregion(addr_space, DINO_HPA, &s->this_mem); + + /* Dino PCI config. */ + memory_region_init_io(&s->parent_obj.conf_mem, OBJECT(&s->parent_obj), + &pci_host_conf_be_ops, dev, "pci-conf-idx", 4); + memory_region_init_io(&s->parent_obj.data_mem, OBJECT(&s->parent_obj), + &dino_config_data_ops, dev, "pci-conf-data", 4); + memory_region_add_subregion(&s->this_mem, DINO_PCI_CONFIG_ADDR, + &s->parent_obj.conf_mem); + memory_region_add_subregion(&s->this_mem, DINO_CONFIG_DATA, + &s->parent_obj.data_mem); + + /* Dino PCI bus memory. */ + memory_region_init(&s->pci_mem, OBJECT(s), "pci-memory", 1ull << 32); + + b = pci_register_root_bus(dev, "pci", dino_set_irq, dino_pci_map_irq, s, + &s->pci_mem, get_system_io(), + PCI_DEVFN(0, 0), 32, TYPE_PCI_BUS); + s->parent_obj.bus = b; + qdev_init_nofail(dev); + + /* Set up windows into PCI bus memory. */ + for (i = 1; i < 31; i++) { + uint32_t addr = 0xf0000000 + i * DINO_MEM_CHUNK_SIZE; + char *name = g_strdup_printf("PCI Outbound Window %d", i); + memory_region_init_alias(&s->pci_mem_alias[i], OBJECT(s), + name, &s->pci_mem, addr, + DINO_MEM_CHUNK_SIZE); + } + + /* Set up PCI view of memory: Bus master address space. */ + memory_region_init(&s->bm, OBJECT(s), "bm-dino", 1ull << 32); + memory_region_init_alias(&s->bm_ram_alias, OBJECT(s), + "bm-system", addr_space, 0, + 0xf0000000 + DINO_MEM_CHUNK_SIZE); + memory_region_init_alias(&s->bm_pci_alias, OBJECT(s), + "bm-pci", &s->pci_mem, + 0xf0000000 + DINO_MEM_CHUNK_SIZE, + 31 * DINO_MEM_CHUNK_SIZE); + memory_region_add_subregion(&s->bm, 0, + &s->bm_ram_alias); + memory_region_add_subregion(&s->bm, + 0xf0000000 + DINO_MEM_CHUNK_SIZE, + &s->bm_pci_alias); + address_space_init(&s->bm_as, &s->bm, "pci-bm"); + pci_setup_iommu(b, dino_pcihost_set_iommu, s); + + *p_rtc_irq = qemu_allocate_irq(dino_set_timer_irq, s, 0); + *p_ser_irq = qemu_allocate_irq(dino_set_serial_irq, s, 0); + + return b; +} + +static int dino_pcihost_init(SysBusDevice *dev) +{ + return 0; +} + +static void dino_pcihost_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = dino_pcihost_init; + dc->vmsd = &vmstate_dino; +} + +static const TypeInfo dino_pcihost_info = { + .name = TYPE_DINO_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(DinoState), + .class_init = dino_pcihost_class_init, +}; + +static void dino_register_types(void) +{ + type_register_static(&dino_pcihost_info); +} + +type_init(dino_register_types) diff --git a/hw/hppa/hppa_hardware.h b/hw/hppa/hppa_hardware.h new file mode 100644 index 0000000000..2c61b1f77c --- /dev/null +++ b/hw/hppa/hppa_hardware.h @@ -0,0 +1,40 @@ +/* HPPA cores and system support chips. */ + +#define FIRMWARE_START 0xf0000000 +#define FIRMWARE_END 0xf0800000 + +#define DEVICE_HPA_LEN 0x00100000 + +#define GSC_HPA 0xffc00000 +#define DINO_HPA 0xfff80000 +#define DINO_UART_HPA 0xfff83000 +#define DINO_UART_BASE 0xfff83800 +#define DINO_SCSI_HPA 0xfff8c000 +#define LASI_HPA 0xffd00000 +#define LASI_UART_HPA 0xffd05000 +#define LASI_SCSI_HPA 0xffd06000 +#define LASI_LAN_HPA 0xffd07000 +#define LASI_LPT_HPA 0xffd02000 +#define LASI_AUDIO_HPA 0xffd04000 +#define LASI_PS2KBD_HPA 0xffd08000 +#define LASI_PS2MOU_HPA 0xffd08100 +#define LASI_GFX_HPA 0xf8000000 +#define CPU_HPA 0xfff10000 +#define MEMORY_HPA 0xfffbf000 + +#define PCI_HPA DINO_HPA /* PCI bus */ +#define IDE_HPA 0xf9000000 /* Boot disc controller */ + +/* offsets to DINO HPA: */ +#define DINO_PCI_ADDR 0x064 +#define DINO_CONFIG_DATA 0x068 +#define DINO_IO_DATA 0x06c + +#define PORT_PCI_CMD (PCI_HPA + DINO_PCI_ADDR) +#define PORT_PCI_DATA (PCI_HPA + DINO_CONFIG_DATA) + +#define PORT_SERIAL1 (DINO_UART_HPA + 0x800) +#define PORT_SERIAL2 (LASI_UART_HPA + 0x800) + +#define HPPA_MAX_CPUS 32 /* max. number of SMP CPUs */ +#define CPU_CLOCK_MHZ 250 /* emulate a 250 MHz CPU */ diff --git a/hw/hppa/hppa_sys.h b/hw/hppa/hppa_sys.h new file mode 100644 index 0000000000..a182d1f34e --- /dev/null +++ b/hw/hppa/hppa_sys.h @@ -0,0 +1,24 @@ +/* HPPA cores and system support chips. */ + +#ifndef HW_HPPA_SYS_H +#define HW_HPPA_SYS_H + +#include "target/hppa/cpu-qom.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/ide.h" +#include "hw/i386/pc.h" +#include "hw/irq.h" + +#include "hw/hppa/hppa_hardware.h" + +PCIBus *dino_init(MemoryRegion *, qemu_irq *, qemu_irq *); + +#define TYPE_DINO_PCI_HOST_BRIDGE "dino-pcihost" + +/* hppa_pci.c. */ +extern const MemoryRegionOps hppa_pci_ignore_ops; +extern const MemoryRegionOps hppa_pci_conf1_ops; +extern const MemoryRegionOps hppa_pci_iack_ops; + +#endif diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c new file mode 100644 index 0000000000..afd3867313 --- /dev/null +++ b/hw/hppa/machine.c @@ -0,0 +1,283 @@ +/* + * QEMU HPPA hardware system emulator. + * Copyright 2018 Helge Deller <deller@gmx.de> + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/hw.h" +#include "elf.h" +#include "hw/loader.h" +#include "hw/boards.h" +#include "qemu/error-report.h" +#include "sysemu/sysemu.h" +#include "hw/timer/mc146818rtc.h" +#include "hw/ide.h" +#include "hw/timer/i8254.h" +#include "hw/char/serial.h" +#include "hw/hppa/hppa_sys.h" +#include "qemu/cutils.h" +#include "qapi/error.h" + +#define MAX_IDE_BUS 2 + +static ISABus *hppa_isa_bus(void) +{ + ISABus *isa_bus; + qemu_irq *isa_irqs; + MemoryRegion *isa_region; + + isa_region = g_new(MemoryRegion, 1); + memory_region_init_io(isa_region, NULL, &hppa_pci_ignore_ops, + NULL, "isa-io", 0x800); + memory_region_add_subregion(get_system_memory(), IDE_HPA, + isa_region); + + isa_bus = isa_bus_new(NULL, get_system_memory(), isa_region, + &error_abort); + isa_irqs = i8259_init(isa_bus, + /* qemu_allocate_irq(dino_set_isa_irq, s, 0)); */ + NULL); + isa_bus_irqs(isa_bus, isa_irqs); + + return isa_bus; +} + +static uint64_t cpu_hppa_to_phys(void *opaque, uint64_t addr) +{ + addr &= (0x10000000 - 1); + return addr; +} + +static HPPACPU *cpu[HPPA_MAX_CPUS]; +static uint64_t firmware_entry; + +static void machine_hppa_init(MachineState *machine) +{ + const char *kernel_filename = machine->kernel_filename; + const char *kernel_cmdline = machine->kernel_cmdline; + const char *initrd_filename = machine->initrd_filename; + PCIBus *pci_bus; + ISABus *isa_bus; + qemu_irq rtc_irq, serial_irq; + char *firmware_filename; + uint64_t firmware_low, firmware_high; + long size; + uint64_t kernel_entry = 0, kernel_low, kernel_high; + MemoryRegion *addr_space = get_system_memory(); + MemoryRegion *rom_region; + MemoryRegion *ram_region; + MemoryRegion *cpu_region; + long i; + + ram_size = machine->ram_size; + + /* Create CPUs. */ + for (i = 0; i < smp_cpus; i++) { + cpu[i] = HPPA_CPU(cpu_create(machine->cpu_type)); + + cpu_region = g_new(MemoryRegion, 1); + memory_region_init_io(cpu_region, OBJECT(cpu[i]), &hppa_io_eir_ops, + cpu[i], g_strdup_printf("cpu%ld-io-eir", i), 4); + memory_region_add_subregion(addr_space, CPU_HPA + i * 0x1000, + cpu_region); + } + + /* Limit main memory. */ + if (ram_size > FIRMWARE_START) { + machine->ram_size = ram_size = FIRMWARE_START; + } + + /* Main memory region. */ + ram_region = g_new(MemoryRegion, 1); + memory_region_allocate_system_memory(ram_region, OBJECT(machine), + "ram", ram_size); + memory_region_add_subregion(addr_space, 0, ram_region); + + /* Init Dino (PCI host bus chip). */ + pci_bus = dino_init(addr_space, &rtc_irq, &serial_irq); + assert(pci_bus); + + /* Create ISA bus. */ + isa_bus = hppa_isa_bus(); + assert(isa_bus); + + /* Realtime clock, used by firmware for PDC_TOD call. */ + mc146818_rtc_init(isa_bus, 2000, rtc_irq); + + /* Serial code setup. */ + if (serial_hds[0]) { + uint32_t addr = DINO_UART_HPA + 0x800; + serial_mm_init(addr_space, addr, 0, serial_irq, + 115200, serial_hds[0], DEVICE_BIG_ENDIAN); + fprintf(stderr, "Serial port created at 0x%x\n", addr); + } + + /* SCSI disk setup. */ + lsi53c895a_create(pci_bus); + + /* Network setup. e1000 is good enough, failing Tulip support. */ + for (i = 0; i < nb_nics; i++) { + pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL); + } + + /* Load firmware. Given that this is not "real" firmware, + but one explicitly written for the emulation, we might as + well load it directly from an ELF image. */ + firmware_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, + bios_name ? bios_name : + "hppa-firmware.img"); + if (firmware_filename == NULL) { + error_report("no firmware provided"); + exit(1); + } + + size = load_elf(firmware_filename, NULL, + NULL, &firmware_entry, &firmware_low, &firmware_high, + true, EM_PARISC, 0, 0); + + /* Unfortunately, load_elf sign-extends reading elf32. */ + firmware_entry = (target_ureg)firmware_entry; + firmware_low = (target_ureg)firmware_low; + firmware_high = (target_ureg)firmware_high; + + if (size < 0) { + error_report("could not load firmware '%s'", firmware_filename); + exit(1); + } + fprintf(stderr, "Firmware loaded at 0x%08" PRIx64 "-0x%08" PRIx64 + ", entry at 0x%08" PRIx64 ".\n", + firmware_low, firmware_high, firmware_entry); + if (firmware_low < ram_size || firmware_high >= FIRMWARE_END) { + error_report("Firmware overlaps with memory or IO space"); + exit(1); + } + g_free(firmware_filename); + + rom_region = g_new(MemoryRegion, 1); + memory_region_allocate_system_memory(rom_region, OBJECT(machine), + "firmware", + (FIRMWARE_END - FIRMWARE_START)); + memory_region_add_subregion(addr_space, FIRMWARE_START, rom_region); + + /* Load kernel */ + if (kernel_filename) { + fprintf(stderr, "LOADING kernel '%s'\n", kernel_filename); + size = load_elf(kernel_filename, &cpu_hppa_to_phys, + NULL, &kernel_entry, &kernel_low, &kernel_high, + true, EM_PARISC, 0, 0); + + /* Unfortunately, load_elf sign-extends reading elf32. */ + kernel_entry = (target_ureg) cpu_hppa_to_phys(NULL, kernel_entry); + kernel_low = (target_ureg)kernel_low; + kernel_high = (target_ureg)kernel_high; + + if (size < 0) { + error_report("could not load kernel '%s'", kernel_filename); + exit(1); + } + + fprintf(stderr, "Kernel loaded at 0x%08" PRIx64 "-0x%08" PRIx64 + ", entry at 0x%08" PRIx64 ", size %ld kB.\n", + kernel_low, kernel_high, kernel_entry, size / 1024); + + if (kernel_cmdline) { + cpu[0]->env.gr[24] = 0x4000; + pstrcpy_targphys("cmdline", cpu[0]->env.gr[24], + TARGET_PAGE_SIZE, kernel_cmdline); + } + + if (initrd_filename) { + ram_addr_t initrd_base; + long initrd_size; + + initrd_size = get_image_size(initrd_filename); + if (initrd_size < 0) { + error_report("could not load initial ram disk '%s'", + initrd_filename); + exit(1); + } + + /* Load the initrd image high in memory. + Mirror the algorithm used by palo: + (1) Due to sign-extension problems and PDC, + put the initrd no higher than 1G. + (2) Reserve 64k for stack. */ + initrd_base = MIN(ram_size, 1024 * 1024 * 1024); + initrd_base = initrd_base - 64 * 1024; + initrd_base = (initrd_base - initrd_size) & TARGET_PAGE_MASK; + + if (initrd_base < kernel_high) { + error_report("kernel and initial ram disk too large!"); + exit(1); + } + + load_image_targphys(initrd_filename, initrd_base, initrd_size); + cpu[0]->env.gr[23] = initrd_base; + cpu[0]->env.gr[22] = initrd_base + initrd_size; + } + } + + if (!kernel_entry) { + /* When booting via firmware, tell firmware if we want interactive + * mode (kernel_entry=1), and to boot from CD (gr[24]='d') + * or hard disc * (gr[24]='c'). + */ + kernel_entry = boot_menu ? 1 : 0; + cpu[0]->env.gr[24] = machine->boot_order[0]; + } + + /* We jump to the firmware entry routine and pass the + * various parameters in registers. After firmware initialization, + * firmware will start the Linux kernel with ramdisk and cmdline. + */ + cpu[0]->env.gr[26] = ram_size; + cpu[0]->env.gr[25] = kernel_entry; + + /* tell firmware how many SMP CPUs to present in inventory table */ + cpu[0]->env.gr[21] = smp_cpus; +} + +static void hppa_machine_reset(void) +{ + int i; + + qemu_devices_reset(); + + /* Start all CPUs at the firmware entry point. + * Monarch CPU will initialize firmware, secondary CPUs + * will enter a small idle look and wait for rendevouz. */ + for (i = 0; i < smp_cpus; i++) { + cpu_set_pc(CPU(cpu[i]), firmware_entry); + cpu[i]->env.gr[5] = CPU_HPA + i * 0x1000; + } + + /* already initialized by machine_hppa_init()? */ + if (cpu[0]->env.gr[26] == ram_size) { + return; + } + + cpu[0]->env.gr[26] = ram_size; + cpu[0]->env.gr[25] = 0; /* no firmware boot menu */ + cpu[0]->env.gr[24] = 'c'; + /* gr22/gr23 unused, no initrd while reboot. */ + cpu[0]->env.gr[21] = smp_cpus; +} + + +static void machine_hppa_machine_init(MachineClass *mc) +{ + mc->desc = "HPPA generic machine"; + mc->default_cpu_type = TYPE_HPPA_CPU; + mc->init = machine_hppa_init; + mc->reset = hppa_machine_reset; + mc->block_default_type = IF_SCSI; + mc->max_cpus = HPPA_MAX_CPUS; + mc->default_cpus = 1; + mc->is_default = 1; + mc->default_ram_size = 512 * M_BYTE; + mc->default_boot_order = "cd"; +} + +DEFINE_MACHINE("hppa", machine_hppa_machine_init) diff --git a/hw/hppa/pci.c b/hw/hppa/pci.c new file mode 100644 index 0000000000..766420254e --- /dev/null +++ b/hw/hppa/pci.c @@ -0,0 +1,90 @@ +/* + * QEMU HP-PARISC PCI support functions. + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "hppa_sys.h" +#include "qemu/log.h" +#include "sysemu/sysemu.h" +#include "trace.h" + + +/* Fallback for unassigned PCI I/O operations. Avoids MCHK. */ + +static uint64_t ignore_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0; +} + +static void ignore_write(void *opaque, hwaddr addr, uint64_t v, unsigned size) +{ +} + +const MemoryRegionOps hppa_pci_ignore_ops = { + .read = ignore_read, + .write = ignore_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + + +/* PCI config space reads/writes, to byte-word addressable memory. */ +static uint64_t bw_conf1_read(void *opaque, hwaddr addr, + unsigned size) +{ + PCIBus *b = opaque; + return pci_data_read(b, addr, size); +} + +static void bw_conf1_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PCIBus *b = opaque; + pci_data_write(b, addr, val, size); +} + +const MemoryRegionOps hppa_pci_conf1_ops = { + .read = bw_conf1_read, + .write = bw_conf1_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +/* PCI/EISA Interrupt Acknowledge Cycle. */ + +static uint64_t iack_read(void *opaque, hwaddr addr, unsigned size) +{ + return pic_read_irq(isa_pic); +} + +static void special_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + trace_hppa_pci_iack_write(); +} + +const MemoryRegionOps hppa_pci_iack_ops = { + .read = iack_read, + .write = special_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; diff --git a/hw/hppa/trace-events b/hw/hppa/trace-events new file mode 100644 index 0000000000..14c67937e1 --- /dev/null +++ b/hw/hppa/trace-events @@ -0,0 +1,4 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# hw/hppa/pci.c +hppa_pci_iack_write(void) "" diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index dc4b2b9ffe..ed78c4ed9f 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2224,6 +2224,22 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(sb_scope, scope); } } + + if (TPM_IS_CRB(tpm_find())) { + dev = aml_device("TPM"); + aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101"))); + crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(TPM_CRB_ADDR_BASE, + TPM_CRB_ADDR_SIZE, AML_READ_WRITE)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_int(0x0f))); + aml_append(dev, method); + + aml_append(sb_scope, dev); + } + aml_append(dsdt, sb_scope); /* copy AML table into ACPI tables blob and patch header there */ @@ -2285,18 +2301,20 @@ build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog) if (TPM_IS_TIS(tpm_find())) { tpm2_ptr->control_area_address = cpu_to_le64(0); tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO); - - tpm2_ptr->log_area_minimum_length = - cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE); - - /* log area start address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, - ACPI_BUILD_TABLE_FILE, log_addr_offset, log_addr_size, - ACPI_BUILD_TPMLOG_FILE, 0); + } else if (TPM_IS_CRB(tpm_find())) { + tpm2_ptr->control_area_address = cpu_to_le64(TPM_CRB_ADDR_CTRL); + tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_CRB); } else { g_warn_if_reached(); } + tpm2_ptr->log_area_minimum_length = + cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE); + + /* log area start address to be filled by Guest linker */ + bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, + log_addr_offset, log_addr_size, + ACPI_BUILD_TPMLOG_FILE, 0); build_header(linker, table_data, (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL); } diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index 8c0535d3dd..bf0b7ee0f5 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -425,6 +425,11 @@ static void chr_event(void *opaque, int event) return; } ibe->connected = false; + /* + * Don't hang the OS trying to handle the ATN bit, other end will + * resend on a reconnect. + */ + k->set_atn(s, 0, 0); if (ibe->waiting_rsp) { ibe->waiting_rsp = false; ibe->inbuf[1] = ibe->outbuf[1] | 0x04; diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 277c28cb40..9b509f829b 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -38,6 +38,7 @@ #define IPMI_NETFN_SENSOR_EVENT 0x04 +#define IPMI_CMD_PLATFORM_EVENT_MSG 0x02 #define IPMI_CMD_SET_SENSOR_EVT_ENABLE 0x28 #define IPMI_CMD_GET_SENSOR_EVT_ENABLE 0x29 #define IPMI_CMD_REARM_SENSOR_EVTS 0x2a @@ -213,8 +214,8 @@ struct IPMIBmcSim { uint8_t device_rev; uint8_t fwrev1; uint8_t fwrev2; - uint8_t mfg_id[3]; - uint8_t product_id[2]; + uint32_t mfg_id; + uint16_t product_id; uint8_t restart_cause; @@ -443,16 +444,21 @@ static void sel_inc_reservation(IPMISel *sel) /* Returns 1 if the SEL is full and can't hold the event. */ static int sel_add_event(IPMIBmcSim *ibs, uint8_t *event) { + uint8_t ts[4]; + event[0] = 0xff; event[1] = 0xff; - set_timestamp(ibs, event + 3); + set_timestamp(ibs, ts); + if (event[2] < 0xe0) { /* Don't set timestamps for type 0xe0-0xff. */ + memcpy(event + 3, ts, 4); + } if (ibs->sel.next_free == MAX_SEL_SIZE) { ibs->sel.overflow = 1; return 1; } event[0] = ibs->sel.next_free & 0xff; event[1] = (ibs->sel.next_free >> 8) & 0xff; - memcpy(ibs->sel.last_addition, event + 3, 4); + memcpy(ibs->sel.last_addition, ts, 4); memcpy(ibs->sel.sel[ibs->sel.next_free], event, 16); ibs->sel.next_free++; sel_inc_reservation(&ibs->sel); @@ -861,11 +867,11 @@ static void get_device_id(IPMIBmcSim *ibs, rsp_buffer_push(rsp, ibs->fwrev2); rsp_buffer_push(rsp, ibs->ipmi_version); rsp_buffer_push(rsp, 0x07); /* sensor, SDR, and SEL. */ - rsp_buffer_push(rsp, ibs->mfg_id[0]); - rsp_buffer_push(rsp, ibs->mfg_id[1]); - rsp_buffer_push(rsp, ibs->mfg_id[2]); - rsp_buffer_push(rsp, ibs->product_id[0]); - rsp_buffer_push(rsp, ibs->product_id[1]); + rsp_buffer_push(rsp, ibs->mfg_id & 0xff); + rsp_buffer_push(rsp, (ibs->mfg_id >> 8) & 0xff); + rsp_buffer_push(rsp, (ibs->mfg_id >> 16) & 0xff); + rsp_buffer_push(rsp, ibs->product_id & 0xff); + rsp_buffer_push(rsp, (ibs->product_id >> 8) & 0xff); } static void set_global_enables(IPMIBmcSim *ibs, uint8_t val) @@ -1576,6 +1582,28 @@ static void set_sel_time(IPMIBmcSim *ibs, ibs->sel.time_offset = now.tv_sec - ((long) val); } +static void platform_event_msg(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint8_t event[16]; + + event[2] = 2; /* System event record */ + event[7] = cmd[2]; /* Generator ID */ + event[8] = 0; + event[9] = cmd[3]; /* EvMRev */ + event[10] = cmd[4]; /* Sensor type */ + event[11] = cmd[5]; /* Sensor number */ + event[12] = cmd[6]; /* Event dir / Event type */ + event[13] = cmd[7]; /* Event data 1 */ + event[14] = cmd[8]; /* Event data 2 */ + event[15] = cmd[9]; /* Event data 3 */ + + if (sel_add_event(ibs, event)) { + rsp_buffer_set_error(rsp, IPMI_CC_OUT_OF_SPACE); + } +} + static void set_sensor_evt_enable(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, RspBuffer *rsp) @@ -1752,6 +1780,7 @@ static const IPMINetfn chassis_netfn = { }; static const IPMICmdHandler sensor_event_cmds[] = { + [IPMI_CMD_PLATFORM_EVENT_MSG] = { platform_event_msg, 10 }, [IPMI_CMD_SET_SENSOR_EVT_ENABLE] = { set_sensor_evt_enable, 4 }, [IPMI_CMD_GET_SENSOR_EVT_ENABLE] = { get_sensor_evt_enable, 3 }, [IPMI_CMD_REARM_SENSOR_EVTS] = { rearm_sensor_evts, 4 }, @@ -1802,8 +1831,8 @@ static const IPMICmdHandler storage_cmds[] = { [IPMI_CMD_GET_SEL_ENTRY] = { get_sel_entry, 8 }, [IPMI_CMD_ADD_SEL_ENTRY] = { add_sel_entry, 18 }, [IPMI_CMD_CLEAR_SEL] = { clear_sel, 8 }, - [IPMI_CMD_GET_SEL_TIME] = { get_sel_time, 6 }, - [IPMI_CMD_SET_SEL_TIME] = { set_sel_time }, + [IPMI_CMD_GET_SEL_TIME] = { get_sel_time }, + [IPMI_CMD_SET_SEL_TIME] = { set_sel_time, 6 }, }; static const IPMINetfn storage_netfn = { @@ -1968,6 +1997,13 @@ static Property ipmi_sim_properties[] = { DEFINE_PROP_UINT16("fruareasize", IPMIBmcSim, fru.areasize, 1024), DEFINE_PROP_STRING("frudatafile", IPMIBmcSim, fru.filename), DEFINE_PROP_STRING("sdrfile", IPMIBmcSim, sdr_filename), + DEFINE_PROP_UINT8("device_id", IPMIBmcSim, device_id, 0x20), + DEFINE_PROP_UINT8("ipmi_version", IPMIBmcSim, ipmi_version, 0x02), + DEFINE_PROP_UINT8("device_rev", IPMIBmcSim, device_rev, 0), + DEFINE_PROP_UINT8("fwrev1", IPMIBmcSim, fwrev1, 0), + DEFINE_PROP_UINT8("fwrev2", IPMIBmcSim, fwrev2, 0), + DEFINE_PROP_UINT32("mfg_id", IPMIBmcSim, mfg_id, 0), + DEFINE_PROP_UINT16("product_id", IPMIBmcSim, product_id, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index e098fd5206..e946030e84 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -45,21 +45,21 @@ #define IPMI_BT_B2H_ATN_MASK (1 << IPMI_BT_B2H_ATN_BIT) #define IPMI_BT_GET_B2H_ATN(d) (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1) #define IPMI_BT_SET_B2H_ATN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \ - (((v) & 1) << IPMI_BT_B2H_ATN_BIT))) + (!!(v) << IPMI_BT_B2H_ATN_BIT))) #define IPMI_BT_SMS_ATN_MASK (1 << IPMI_BT_SMS_ATN_BIT) #define IPMI_BT_GET_SMS_ATN(d) (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1) #define IPMI_BT_SET_SMS_ATN(d, v) ((d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \ - (((v) & 1) << IPMI_BT_SMS_ATN_BIT))) + (!!(v) << IPMI_BT_SMS_ATN_BIT))) #define IPMI_BT_HBUSY_MASK (1 << IPMI_BT_HBUSY_BIT) #define IPMI_BT_GET_HBUSY(d) (((d) >> IPMI_BT_HBUSY_BIT) & 0x1) #define IPMI_BT_SET_HBUSY(d, v) ((d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \ - (((v) & 1) << IPMI_BT_HBUSY_BIT))) + (!!(v) << IPMI_BT_HBUSY_BIT))) #define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT) #define IPMI_BT_SET_BBUSY(d, v) ((d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \ - (((v) & 1) << IPMI_BT_BBUSY_BIT))) + (!!(v) << IPMI_BT_BBUSY_BIT))) /* Mask register */ @@ -69,12 +69,12 @@ #define IPMI_BT_B2H_IRQ_EN_MASK (1 << IPMI_BT_B2H_IRQ_EN_BIT) #define IPMI_BT_GET_B2H_IRQ_EN(d) (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1) #define IPMI_BT_SET_B2H_IRQ_EN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) |\ - (((v) & 1) << IPMI_BT_B2H_IRQ_EN_BIT))) + (!!(v) << IPMI_BT_B2H_IRQ_EN_BIT))) #define IPMI_BT_B2H_IRQ_MASK (1 << IPMI_BT_B2H_IRQ_BIT) #define IPMI_BT_GET_B2H_IRQ(d) (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1) #define IPMI_BT_SET_B2H_IRQ(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \ - (((v) & 1) << IPMI_BT_B2H_IRQ_BIT))) + (!!(v) << IPMI_BT_B2H_IRQ_BIT))) typedef struct IPMIBT { IPMIBmc *bmc; diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs index 7a93b24636..1dc9f8bf2c 100644 --- a/hw/tpm/Makefile.objs +++ b/hw/tpm/Makefile.objs @@ -1,4 +1,5 @@ common-obj-y += tpm_util.o common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o +common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c new file mode 100644 index 0000000000..687d2557b7 --- /dev/null +++ b/hw/tpm/tpm_crb.c @@ -0,0 +1,303 @@ +/* + * tpm_crb.c - QEMU's TPM CRB interface emulator + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * Marc-André Lureau <marcandre.lureau@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * tpm_crb is a device for TPM 2.0 Command Response Buffer (CRB) Interface + * as defined in TCG PC Client Platform TPM Profile (PTP) Specification + * Family “2.0” Level 00 Revision 01.03 v22 + */ + +#include "qemu/osdep.h" + +#include "qemu-common.h" +#include "qapi/error.h" +#include "exec/address-spaces.h" + +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "hw/pci/pci_ids.h" +#include "hw/acpi/tpm.h" +#include "migration/vmstate.h" +#include "sysemu/tpm_backend.h" +#include "tpm_int.h" +#include "tpm_util.h" + +typedef struct CRBState { + DeviceState parent_obj; + + TPMBackend *tpmbe; + TPMBackendCmd cmd; + uint32_t regs[TPM_CRB_R_MAX]; + MemoryRegion mmio; + MemoryRegion cmdmem; + + size_t be_buffer_size; +} CRBState; + +#define CRB(obj) OBJECT_CHECK(CRBState, (obj), TYPE_TPM_CRB) + +#define DEBUG_CRB 0 + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_CRB) { \ + printf(fmt, ## __VA_ARGS__); \ + } \ + } while (0) + +#define CRB_INTF_TYPE_CRB_ACTIVE 0b1 +#define CRB_INTF_VERSION_CRB 0b1 +#define CRB_INTF_CAP_LOCALITY_0_ONLY 0b0 +#define CRB_INTF_CAP_IDLE_FAST 0b0 +#define CRB_INTF_CAP_XFER_SIZE_64 0b11 +#define CRB_INTF_CAP_FIFO_NOT_SUPPORTED 0b0 +#define CRB_INTF_CAP_CRB_SUPPORTED 0b1 +#define CRB_INTF_IF_SELECTOR_CRB 0b1 + +#define CRB_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER) + +enum crb_loc_ctrl { + CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0), + CRB_LOC_CTRL_RELINQUISH = BIT(1), + CRB_LOC_CTRL_SEIZE = BIT(2), + CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT = BIT(3), +}; + +enum crb_ctrl_req { + CRB_CTRL_REQ_CMD_READY = BIT(0), + CRB_CTRL_REQ_GO_IDLE = BIT(1), +}; + +enum crb_start { + CRB_START_INVOKE = BIT(0), +}; + +enum crb_cancel { + CRB_CANCEL_INVOKE = BIT(0), +}; + +static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + CRBState *s = CRB(opaque); + void *regs = (void *)&s->regs + (addr & ~3); + unsigned offset = addr & 3; + uint32_t val = *(uint32_t *)regs >> (8 * offset); + + DPRINTF("CRB read 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32 "\n", + addr, size, val); + return val; +} + +static void tpm_crb_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + CRBState *s = CRB(opaque); + DPRINTF("CRB write 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx64 "\n", + addr, size, val); + + switch (addr) { + case A_CRB_CTRL_REQ: + switch (val) { + case CRB_CTRL_REQ_CMD_READY: + ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, + tpmIdle, 0); + break; + case CRB_CTRL_REQ_GO_IDLE: + ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, + tpmIdle, 1); + break; + } + break; + case A_CRB_CTRL_CANCEL: + if (val == CRB_CANCEL_INVOKE && + s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) { + tpm_backend_cancel_cmd(s->tpmbe); + } + break; + case A_CRB_CTRL_START: + if (val == CRB_START_INVOKE && + !(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE)) { + void *mem = memory_region_get_ram_ptr(&s->cmdmem); + + s->regs[R_CRB_CTRL_START] |= CRB_START_INVOKE; + s->cmd = (TPMBackendCmd) { + .in = mem, + .in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size), + .out = mem, + .out_len = s->be_buffer_size, + }; + + tpm_backend_deliver_request(s->tpmbe, &s->cmd); + } + break; + case A_CRB_LOC_CTRL: + switch (val) { + case CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT: + /* not loc 3 or 4 */ + break; + case CRB_LOC_CTRL_RELINQUISH: + break; + case CRB_LOC_CTRL_REQUEST_ACCESS: + ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS, + Granted, 1); + ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS, + beenSeized, 0); + ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE, + locAssigned, 1); + ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE, + tpmRegValidSts, 1); + break; + } + break; + } +} + +static const MemoryRegionOps tpm_crb_memory_ops = { + .read = tpm_crb_mmio_read, + .write = tpm_crb_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void tpm_crb_request_completed(TPMIf *ti, int ret) +{ + CRBState *s = CRB(ti); + + s->regs[R_CRB_CTRL_START] &= ~CRB_START_INVOKE; + if (ret != 0) { + ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, + tpmSts, 1); /* fatal error */ + } +} + +static enum TPMVersion tpm_crb_get_version(TPMIf *ti) +{ + CRBState *s = CRB(ti); + + return tpm_backend_get_tpm_version(s->tpmbe); +} + +static int tpm_crb_pre_save(void *opaque) +{ + CRBState *s = opaque; + + tpm_backend_finish_sync(s->tpmbe); + + return 0; +} + +static const VMStateDescription vmstate_tpm_crb = { + .name = "tpm-crb", + .pre_save = tpm_crb_pre_save, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, CRBState, TPM_CRB_R_MAX), + VMSTATE_END_OF_LIST(), + } +}; + +static Property tpm_crb_properties[] = { + DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tpm_crb_realize(DeviceState *dev, Error **errp) +{ + CRBState *s = CRB(dev); + + if (!tpm_find()) { + error_setg(errp, "at most one TPM device is permitted"); + return; + } + if (!s->tpmbe) { + error_setg(errp, "'tpmdev' property is required"); + return; + } + + memory_region_init_io(&s->mmio, OBJECT(s), &tpm_crb_memory_ops, s, + "tpm-crb-mmio", sizeof(s->regs)); + memory_region_init_ram(&s->cmdmem, OBJECT(s), + "tpm-crb-cmd", CRB_CTRL_CMD_SIZE, errp); + + memory_region_add_subregion(get_system_memory(), + TPM_CRB_ADDR_BASE, &s->mmio); + memory_region_add_subregion(get_system_memory(), + TPM_CRB_ADDR_BASE + sizeof(s->regs), &s->cmdmem); + + tpm_backend_reset(s->tpmbe); + + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, + InterfaceType, CRB_INTF_TYPE_CRB_ACTIVE); + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, + InterfaceVersion, CRB_INTF_VERSION_CRB); + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, + CapLocality, CRB_INTF_CAP_LOCALITY_0_ONLY); + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, + CapCRBIdleBypass, CRB_INTF_CAP_IDLE_FAST); + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, + CapDataXferSizeSupport, CRB_INTF_CAP_XFER_SIZE_64); + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, + CapFIFO, CRB_INTF_CAP_FIFO_NOT_SUPPORTED); + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, + CapCRB, CRB_INTF_CAP_CRB_SUPPORTED); + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, + InterfaceSelector, CRB_INTF_IF_SELECTOR_CRB); + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, + RID, 0b0000); + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID2, + VID, PCI_VENDOR_ID_IBM); + + s->regs[R_CRB_CTRL_CMD_SIZE] = CRB_CTRL_CMD_SIZE; + s->regs[R_CRB_CTRL_CMD_LADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER; + s->regs[R_CRB_CTRL_RSP_SIZE] = CRB_CTRL_CMD_SIZE; + s->regs[R_CRB_CTRL_RSP_ADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER; + + s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->tpmbe), + CRB_CTRL_CMD_SIZE); + + tpm_backend_startup_tpm(s->tpmbe, s->be_buffer_size); +} + +static void tpm_crb_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + TPMIfClass *tc = TPM_IF_CLASS(klass); + + dc->realize = tpm_crb_realize; + dc->props = tpm_crb_properties; + dc->vmsd = &vmstate_tpm_crb; + dc->user_creatable = true; + tc->model = TPM_MODEL_TPM_CRB; + tc->get_version = tpm_crb_get_version; + tc->request_completed = tpm_crb_request_completed; + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo tpm_crb_info = { + .name = TYPE_TPM_CRB, + /* could be TYPE_SYS_BUS_DEVICE (or LPC etc) */ + .parent = TYPE_DEVICE, + .instance_size = sizeof(CRBState), + .class_init = tpm_crb_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_TPM_IF }, + { } + } +}; + +static void tpm_crb_register(void) +{ + type_register_static(&tpm_crb_info); +} + +type_init(tpm_crb_register) diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c index 35c78de5a9..710a9ec718 100644 --- a/hw/tpm/tpm_emulator.c +++ b/hw/tpm/tpm_emulator.c @@ -120,7 +120,6 @@ static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, { ssize_t ret; bool is_selftest = false; - const struct tpm_resp_hdr *hdr = NULL; if (selftest_done) { *selftest_done = false; @@ -132,22 +131,21 @@ static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, return -1; } - ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, sizeof(*hdr), - err); + ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, + sizeof(struct tpm_resp_hdr), err); if (ret != 0) { return -1; } - hdr = (struct tpm_resp_hdr *)out; - out += sizeof(*hdr); - ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, - be32_to_cpu(hdr->len) - sizeof(*hdr) , err); + ret = qio_channel_read_all(tpm_emu->data_ioc, + (char *)out + sizeof(struct tpm_resp_hdr), + tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), err); if (ret != 0) { return -1; } if (is_selftest) { - *selftest_done = (be32_to_cpu(hdr->errcode) == 0); + *selftest_done = tpm_cmd_get_errcode(out) == 0; } return 0; @@ -185,28 +183,19 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number, return 0; } -static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd) +static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, + Error **errp) { TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - Error *err = NULL; DPRINTF("processing TPM command"); - if (tpm_emulator_set_locality(tpm_emu, cmd->locty, &err) < 0) { - goto error; - } - - if (tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len, + if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 || + tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len, cmd->out, cmd->out_len, - &cmd->selftest_done, &err) < 0) { - goto error; + &cmd->selftest_done, errp) < 0) { + tpm_util_write_fatal_error_response(cmd->out, cmd->out_len); } - - return; - -error: - tpm_util_write_fatal_error_response(cmd->out, cmd->out_len); - error_report_err(err); } static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) @@ -320,7 +309,9 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb, static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) { TPMEmulator *tpm_emu = TPM_EMULATOR(tb); - ptm_init init; + ptm_init init = { + .u.req.init_flags = 0, + }; ptm_res res; if (buffersize != 0 && diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index 29142f38bb..a495fe07f4 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -80,14 +80,14 @@ static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) } return ret; } -static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, - const uint8_t *in, uint32_t in_len, - uint8_t *out, uint32_t out_len, - bool *selftest_done) + +static void tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, + const uint8_t *in, uint32_t in_len, + uint8_t *out, uint32_t out_len, + bool *selftest_done, Error **errp) { ssize_t ret; bool is_selftest; - const struct tpm_resp_hdr *hdr; /* FIXME: protect shared variables or use other sync mechanism */ tpm_pt->tpm_op_canceled = false; @@ -99,9 +99,8 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, ret = qemu_write_full(tpm_pt->tpm_fd, in, in_len); if (ret != in_len) { if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { - error_report("tpm_passthrough: error while transmitting data " - "to TPM: %s (%i)", - strerror(errno), errno); + error_setg_errno(errp, errno, "tpm_passthrough: error while " + "transmitting data to TPM"); } goto err_exit; } @@ -111,20 +110,18 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); if (ret < 0) { if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { - error_report("tpm_passthrough: error while reading data from " - "TPM: %s (%i)", - strerror(errno), errno); + error_setg_errno(errp, errno, "tpm_passthrough: error while " + "reading data from TPM"); } } else if (ret < sizeof(struct tpm_resp_hdr) || - be32_to_cpu(((struct tpm_resp_hdr *)out)->len) != ret) { + tpm_cmd_get_size(out) != ret) { ret = -1; - error_report("tpm_passthrough: received invalid response " - "packet from TPM"); + error_setg_errno(errp, errno, "tpm_passthrough: received invalid " + "response packet from TPM"); } if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) { - hdr = (struct tpm_resp_hdr *)out; - *selftest_done = (be32_to_cpu(hdr->errcode) == 0); + *selftest_done = tpm_cmd_get_errcode(out) == 0; } err_exit: @@ -133,18 +130,18 @@ err_exit: } tpm_pt->tpm_executing = false; - - return ret; } -static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd) +static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, + Error **errp) { TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); DPRINTF("tpm_passthrough: processing command %p\n", cmd); tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len, - cmd->out, cmd->out_len, &cmd->selftest_done); + cmd->out, cmd->out_len, &cmd->selftest_done, + errp); } static void tpm_passthrough_reset(TPMBackend *tb) @@ -216,7 +213,8 @@ static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb) * Unless path or file descriptor set has been provided by user, * determine the sysfs cancel file following kernel documentation * in Documentation/ABI/stable/sysfs-class-tpm. - * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel + * From /dev/tpm0 create /sys/class/tpm/tpm0/device/cancel + * before 4.0: /sys/class/misc/tpm0/device/cancel */ static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt) { @@ -227,26 +225,35 @@ static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt) if (tpm_pt->options->cancel_path) { fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY); if (fd < 0) { - error_report("Could not open TPM cancel path : %s", + error_report("tpm_passthrough: Could not open TPM cancel path: %s", strerror(errno)); } return fd; } dev = strrchr(tpm_pt->tpm_dev, '/'); - if (dev) { - dev++; - if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel", - dev) < sizeof(path)) { - fd = qemu_open(path, O_WRONLY); - if (fd < 0) { - error_report("tpm_passthrough: Could not open TPM cancel " - "path %s : %s", path, strerror(errno)); + if (!dev) { + error_report("tpm_passthrough: Bad TPM device path %s", + tpm_pt->tpm_dev); + return -1; + } + + dev++; + if (snprintf(path, sizeof(path), "/sys/class/tpm/%s/device/cancel", + dev) < sizeof(path)) { + fd = qemu_open(path, O_WRONLY); + if (fd < 0) { + if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel", + dev) < sizeof(path)) { + fd = qemu_open(path, O_WRONLY); } } + } + + if (fd < 0) { + error_report("tpm_passthrough: Could not guess TPM cancel path"); } else { - error_report("tpm_passthrough: Bad TPM device path %s", - tpm_pt->tpm_dev); + tpm_pt->options->cancel_path = g_strdup(path); } return fd; diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c index 8b5eb01a2c..08f41d2707 100644 --- a/hw/tpm/tpm_tis.c +++ b/hw/tpm/tpm_tis.c @@ -393,7 +393,7 @@ static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) /* * Callback from the TPM to indicate that the response was received. */ -static void tpm_tis_request_completed(TPMIf *ti) +static void tpm_tis_request_completed(TPMIf *ti, int ret) { TPMState *s = TPM(ti); uint8_t locty = s->cmd.locty; @@ -405,6 +405,7 @@ static void tpm_tis_request_completed(TPMIf *ti) } } + /* FIXME: report error if ret != 0 */ tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); s->loc[locty].state = TPM_TIS_STATE_COMPLETION; diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c index 747075e244..8abde59915 100644 --- a/hw/tpm/tpm_util.c +++ b/hw/tpm/tpm_util.c @@ -106,20 +106,16 @@ const PropertyInfo qdev_prop_tpm = { void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len) { if (out_len >= sizeof(struct tpm_resp_hdr)) { - struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out; - - resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND); - resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr)); - resp->errcode = cpu_to_be32(TPM_FAIL); + stw_be_p(out, TPM_TAG_RSP_COMMAND); + stl_be_p(out + 2, sizeof(struct tpm_resp_hdr)); + stl_be_p(out + 6, TPM_FAIL); } } bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len) { - struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in; - - if (in_len >= sizeof(*hdr)) { - return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest); + if (in_len >= sizeof(struct tpm_req_hdr)) { + return tpm_cmd_get_ordinal(in) == TPM_ORD_ContinueSelfTest; } return false; @@ -129,12 +125,11 @@ bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len) * Send request to a TPM device. We expect a response within one second. */ static int tpm_util_request(int fd, - unsigned char *request, + const void *request, size_t requestlen, - unsigned char *response, + void *response, size_t responselen) { - struct tpm_resp_hdr *resp; fd_set readfds; int n; struct timeval tv = { @@ -164,9 +159,8 @@ static int tpm_util_request(int fd, return -EFAULT; } - resp = (struct tpm_resp_hdr *)response; /* check the header */ - if (be32_to_cpu(resp->len) != n) { + if (tpm_cmd_get_size(response) != n) { return -EMSGSIZE; } @@ -178,12 +172,11 @@ static int tpm_util_request(int fd, * (error response is fine). */ static int tpm_util_test(int fd, - unsigned char *request, + const void *request, size_t requestlen, uint16_t *return_tag) { - struct tpm_resp_hdr *resp; - unsigned char buf[1024]; + char buf[1024]; ssize_t ret; ret = tpm_util_request(fd, request, requestlen, @@ -192,8 +185,7 @@ static int tpm_util_test(int fd, return ret; } - resp = (struct tpm_resp_hdr *)buf; - *return_tag = be16_to_cpu(resp->tag); + *return_tag = tpm_cmd_get_tag(buf); return 0; } @@ -228,7 +220,7 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version) int ret; /* Send TPM 2 command */ - ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req_tpm2, + ret = tpm_util_test(tpm_fd, &test_req_tpm2, sizeof(test_req_tpm2), &return_tag); /* TPM 2 would respond with a tag of TPM2_ST_NO_SESSIONS */ if (!ret && return_tag == TPM2_ST_NO_SESSIONS) { @@ -237,7 +229,7 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version) } /* Send TPM 1.2 command */ - ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req, + ret = tpm_util_test(tpm_fd, &test_req, sizeof(test_req), &return_tag); if (!ret && return_tag == TPM_TAG_RSP_COMMAND) { *tpm_version = TPM_VERSION_1_2; @@ -253,7 +245,6 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version) int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version, size_t *buffersize) { - unsigned char buf[1024]; int ret; switch (tpm_version) { @@ -277,26 +268,27 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version, struct tpm_resp_hdr hdr; uint32_t len; uint32_t buffersize; - } QEMU_PACKED *tpm_resp = (struct tpm_resp_get_buffer_size *)buf; + } QEMU_PACKED tpm_resp; - ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm_get_buffer_size, - sizeof(tpm_get_buffer_size), buf, sizeof(buf)); + ret = tpm_util_request(tpm_fd, &tpm_get_buffer_size, + sizeof(tpm_get_buffer_size), + &tpm_resp, sizeof(tpm_resp)); if (ret < 0) { return ret; } - if (be32_to_cpu(tpm_resp->hdr.len) != sizeof(*tpm_resp) || - be32_to_cpu(tpm_resp->len) != sizeof(uint32_t)) { + if (be32_to_cpu(tpm_resp.hdr.len) != sizeof(tpm_resp) || + be32_to_cpu(tpm_resp.len) != sizeof(uint32_t)) { DPRINTF("tpm_resp->hdr.len = %u, expected = %zu\n", - be32_to_cpu(tpm_resp->hdr.len), sizeof(*tpm_resp)); + be32_to_cpu(tpm_resp.hdr.len), sizeof(tpm_resp)); DPRINTF("tpm_resp->len = %u, expected = %zu\n", - be32_to_cpu(tpm_resp->len), sizeof(uint32_t)); + be32_to_cpu(tpm_resp.len), sizeof(uint32_t)); error_report("tpm_util: Got unexpected response to " "TPM_GetCapability; errcode: 0x%x", - be32_to_cpu(tpm_resp->hdr.errcode)); + be32_to_cpu(tpm_resp.hdr.errcode)); return -EFAULT; } - *buffersize = be32_to_cpu(tpm_resp->buffersize); + *buffersize = be32_to_cpu(tpm_resp.buffersize); break; } case TPM_VERSION_2_0: { @@ -324,27 +316,28 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version, uint32_t value1; uint32_t property2; uint32_t value2; - } QEMU_PACKED *tpm2_resp = (struct tpm2_resp_get_buffer_size *)buf; + } QEMU_PACKED tpm2_resp; - ret = tpm_util_request(tpm_fd, (unsigned char *)&tpm2_get_buffer_size, - sizeof(tpm2_get_buffer_size), buf, sizeof(buf)); + ret = tpm_util_request(tpm_fd, &tpm2_get_buffer_size, + sizeof(tpm2_get_buffer_size), + &tpm2_resp, sizeof(tpm2_resp)); if (ret < 0) { return ret; } - if (be32_to_cpu(tpm2_resp->hdr.len) != sizeof(*tpm2_resp) || - be32_to_cpu(tpm2_resp->count) != 2) { + if (be32_to_cpu(tpm2_resp.hdr.len) != sizeof(tpm2_resp) || + be32_to_cpu(tpm2_resp.count) != 2) { DPRINTF("tpm2_resp->hdr.len = %u, expected = %zu\n", - be32_to_cpu(tpm2_resp->hdr.len), sizeof(*tpm2_resp)); + be32_to_cpu(tpm2_resp.hdr.len), sizeof(tpm2_resp)); DPRINTF("tpm2_resp->len = %u, expected = %u\n", - be32_to_cpu(tpm2_resp->count), 2); + be32_to_cpu(tpm2_resp.count), 2); error_report("tpm_util: Got unexpected response to " "TPM2_GetCapability; errcode: 0x%x", - be32_to_cpu(tpm2_resp->hdr.errcode)); + be32_to_cpu(tpm2_resp.hdr.errcode)); return -EFAULT; } - *buffersize = MAX(be32_to_cpu(tpm2_resp->value1), - be32_to_cpu(tpm2_resp->value2)); + *buffersize = MAX(be32_to_cpu(tpm2_resp.value1), + be32_to_cpu(tpm2_resp.value2)); break; } case TPM_VERSION_UNSPEC: diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h index 19b28474ae..f003d15615 100644 --- a/hw/tpm/tpm_util.h +++ b/hw/tpm/tpm_util.h @@ -31,9 +31,24 @@ bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len); int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version); +static inline uint16_t tpm_cmd_get_tag(const void *b) +{ + return lduw_be_p(b); +} + static inline uint32_t tpm_cmd_get_size(const void *b) { - return be32_to_cpu(*(const uint32_t *)(b + 2)); + return ldl_be_p(b + 2); +} + +static inline uint32_t tpm_cmd_get_ordinal(const void *b) +{ + return ldl_be_p(b + 6); +} + +static inline uint32_t tpm_cmd_get_errcode(const void *b) +{ + return ldl_be_p(b + 6); } int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version, |