qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

hw/timer/bcm2835: Add the BCM2835 SYS_timer

Add the 64-bit free running timer. Do not model the COMPARE register
(no IRQ generated).
This timer is used by Linux kernel and recently U-Boot:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/clocksource/bcm2835_timer.c?h=v3.7
https://github.com/u-boot/u-boot/blob/v2019.07/include/configs/rpi.h#L19

Datasheet used:
https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-id: 20191019234715.25750-4-f4bug@amsat.org
[PMM: squashed in switch to using memset in reset]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

authored by

Philippe Mathieu-Daudé and committed by
Peter Maydell
d05be883 d442d95f

+202
+1
hw/timer/Makefile.objs
··· 47 47 common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o 48 48 common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o 49 49 common-obj-$(CONFIG_MSF2) += mss-timer.o 50 + common-obj-$(CONFIG_RASPI) += bcm2835_systmr.o
+163
hw/timer/bcm2835_systmr.c
··· 1 + /* 2 + * BCM2835 SYS timer emulation 3 + * 4 + * Copyright (C) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org> 5 + * 6 + * SPDX-License-Identifier: GPL-2.0-or-later 7 + * 8 + * Datasheet: BCM2835 ARM Peripherals (C6357-M-1398) 9 + * https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf 10 + * 11 + * Only the free running 64-bit counter is implemented. 12 + * The 4 COMPARE registers and the interruption are not implemented. 13 + */ 14 + 15 + #include "qemu/osdep.h" 16 + #include "qemu/log.h" 17 + #include "qemu/timer.h" 18 + #include "hw/timer/bcm2835_systmr.h" 19 + #include "hw/registerfields.h" 20 + #include "migration/vmstate.h" 21 + #include "trace.h" 22 + 23 + REG32(CTRL_STATUS, 0x00) 24 + REG32(COUNTER_LOW, 0x04) 25 + REG32(COUNTER_HIGH, 0x08) 26 + REG32(COMPARE0, 0x0c) 27 + REG32(COMPARE1, 0x10) 28 + REG32(COMPARE2, 0x14) 29 + REG32(COMPARE3, 0x18) 30 + 31 + static void bcm2835_systmr_update_irq(BCM2835SystemTimerState *s) 32 + { 33 + bool enable = !!s->reg.status; 34 + 35 + trace_bcm2835_systmr_irq(enable); 36 + qemu_set_irq(s->irq, enable); 37 + } 38 + 39 + static void bcm2835_systmr_update_compare(BCM2835SystemTimerState *s, 40 + unsigned timer_index) 41 + { 42 + /* TODO fow now, since neither Linux nor U-boot use these timers. */ 43 + qemu_log_mask(LOG_UNIMP, "COMPARE register %u not implemented\n", 44 + timer_index); 45 + } 46 + 47 + static uint64_t bcm2835_systmr_read(void *opaque, hwaddr offset, 48 + unsigned size) 49 + { 50 + BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque); 51 + uint64_t r = 0; 52 + 53 + switch (offset) { 54 + case A_CTRL_STATUS: 55 + r = s->reg.status; 56 + break; 57 + case A_COMPARE0 ... A_COMPARE3: 58 + r = s->reg.compare[(offset - A_COMPARE0) >> 2]; 59 + break; 60 + case A_COUNTER_LOW: 61 + case A_COUNTER_HIGH: 62 + /* Free running counter at 1MHz */ 63 + r = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL); 64 + r >>= 8 * (offset - A_COUNTER_LOW); 65 + r &= UINT32_MAX; 66 + break; 67 + default: 68 + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%" HWADDR_PRIx "\n", 69 + __func__, offset); 70 + break; 71 + } 72 + trace_bcm2835_systmr_read(offset, r); 73 + 74 + return r; 75 + } 76 + 77 + static void bcm2835_systmr_write(void *opaque, hwaddr offset, 78 + uint64_t value, unsigned size) 79 + { 80 + BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque); 81 + 82 + trace_bcm2835_systmr_write(offset, value); 83 + switch (offset) { 84 + case A_CTRL_STATUS: 85 + s->reg.status &= ~value; /* Ack */ 86 + bcm2835_systmr_update_irq(s); 87 + break; 88 + case A_COMPARE0 ... A_COMPARE3: 89 + s->reg.compare[(offset - A_COMPARE0) >> 2] = value; 90 + bcm2835_systmr_update_compare(s, (offset - A_COMPARE0) >> 2); 91 + break; 92 + case A_COUNTER_LOW: 93 + case A_COUNTER_HIGH: 94 + qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only ofs 0x%" HWADDR_PRIx "\n", 95 + __func__, offset); 96 + break; 97 + default: 98 + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%" HWADDR_PRIx "\n", 99 + __func__, offset); 100 + break; 101 + } 102 + } 103 + 104 + static const MemoryRegionOps bcm2835_systmr_ops = { 105 + .read = bcm2835_systmr_read, 106 + .write = bcm2835_systmr_write, 107 + .endianness = DEVICE_LITTLE_ENDIAN, 108 + .impl = { 109 + .min_access_size = 4, 110 + .max_access_size = 4, 111 + }, 112 + }; 113 + 114 + static void bcm2835_systmr_reset(DeviceState *dev) 115 + { 116 + BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev); 117 + 118 + memset(&s->reg, 0, sizeof(s->reg)); 119 + } 120 + 121 + static void bcm2835_systmr_realize(DeviceState *dev, Error **errp) 122 + { 123 + BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev); 124 + 125 + memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_systmr_ops, 126 + s, "bcm2835-sys-timer", 0x20); 127 + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); 128 + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); 129 + } 130 + 131 + static const VMStateDescription bcm2835_systmr_vmstate = { 132 + .name = "bcm2835_sys_timer", 133 + .version_id = 1, 134 + .minimum_version_id = 1, 135 + .fields = (VMStateField[]) { 136 + VMSTATE_UINT32(reg.status, BCM2835SystemTimerState), 137 + VMSTATE_UINT32_ARRAY(reg.compare, BCM2835SystemTimerState, 4), 138 + VMSTATE_END_OF_LIST() 139 + } 140 + }; 141 + 142 + static void bcm2835_systmr_class_init(ObjectClass *klass, void *data) 143 + { 144 + DeviceClass *dc = DEVICE_CLASS(klass); 145 + 146 + dc->realize = bcm2835_systmr_realize; 147 + dc->reset = bcm2835_systmr_reset; 148 + dc->vmsd = &bcm2835_systmr_vmstate; 149 + } 150 + 151 + static const TypeInfo bcm2835_systmr_info = { 152 + .name = TYPE_BCM2835_SYSTIMER, 153 + .parent = TYPE_SYS_BUS_DEVICE, 154 + .instance_size = sizeof(BCM2835SystemTimerState), 155 + .class_init = bcm2835_systmr_class_init, 156 + }; 157 + 158 + static void bcm2835_systmr_register_types(void) 159 + { 160 + type_register_static(&bcm2835_systmr_info); 161 + } 162 + 163 + type_init(bcm2835_systmr_register_types);
+5
hw/timer/trace-events
··· 87 87 pl031_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" 88 88 pl031_alarm_raised(void) "alarm raised" 89 89 pl031_set_alarm(uint32_t ticks) "alarm set for %u ticks" 90 + 91 + # bcm2835_systmr.c 92 + bcm2835_systmr_irq(bool enable) "timer irq state %u" 93 + bcm2835_systmr_read(uint64_t offset, uint64_t data) "timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 94 + bcm2835_systmr_write(uint64_t offset, uint64_t data) "timer write: offset 0x%" PRIx64 " data 0x%" PRIx64
+33
include/hw/timer/bcm2835_systmr.h
··· 1 + /* 2 + * BCM2835 SYS timer emulation 3 + * 4 + * Copyright (c) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org> 5 + * 6 + * SPDX-License-Identifier: GPL-2.0-or-later 7 + */ 8 + 9 + #ifndef BCM2835_SYSTIMER_H 10 + #define BCM2835_SYSTIMER_H 11 + 12 + #include "hw/sysbus.h" 13 + #include "hw/irq.h" 14 + 15 + #define TYPE_BCM2835_SYSTIMER "bcm2835-sys-timer" 16 + #define BCM2835_SYSTIMER(obj) \ 17 + OBJECT_CHECK(BCM2835SystemTimerState, (obj), TYPE_BCM2835_SYSTIMER) 18 + 19 + typedef struct { 20 + /*< private >*/ 21 + SysBusDevice parent_obj; 22 + 23 + /*< public >*/ 24 + MemoryRegion iomem; 25 + qemu_irq irq; 26 + 27 + struct { 28 + uint32_t status; 29 + uint32_t compare[4]; 30 + } reg; 31 + } BCM2835SystemTimerState; 32 + 33 + #endif