diff options
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rwxr-xr-x | configure | 4 | ||||
-rw-r--r-- | hw/riscv/Kconfig | 1 | ||||
-rw-r--r-- | hw/riscv/virt.c | 42 | ||||
-rw-r--r-- | hw/rtc/Kconfig | 3 | ||||
-rw-r--r-- | hw/rtc/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/rtc/goldfish_rtc.c | 285 | ||||
-rw-r--r-- | hw/rtc/trace-events | 4 | ||||
-rw-r--r-- | include/hw/riscv/virt.h | 2 | ||||
-rw-r--r-- | include/hw/rtc/goldfish_rtc.h | 46 | ||||
-rw-r--r-- | target/riscv/gdbstub.c | 20 |
11 files changed, 401 insertions, 15 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index c7717df720..1740a4fddc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -562,6 +562,14 @@ F: include/hw/arm/digic.h F: hw/*/digic* F: include/hw/*/digic* +Goldfish RTC +M: Anup Patel <anup.patel@wdc.com> +M: Alistair Francis <Alistair.Francis@wdc.com> +L: qemu-riscv@nongnu.org +S: Maintained +F: hw/rtc/goldfish_rtc.c +F: include/hw/rtc/goldfish_rtc.h + Gumstix M: Peter Maydell <peter.maydell@linaro.org> R: Philippe Mathieu-Daudé <f4bug@amsat.org> @@ -7739,13 +7739,13 @@ case "$target_name" in TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv mttcg=yes - gdb_xml_files="riscv-32bit-cpu.xml riscv-32bit-fpu.xml riscv-32bit-csr.xml riscv-32bit-virtual.xml" + gdb_xml_files="riscv-32bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-32bit-csr.xml riscv-32bit-virtual.xml" ;; riscv64) TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv mttcg=yes - gdb_xml_files="riscv-64bit-cpu.xml riscv-64bit-fpu.xml riscv-64bit-csr.xml riscv-64bit-virtual.xml" + gdb_xml_files="riscv-64bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-64bit-csr.xml riscv-64bit-virtual.xml" ;; sh4|sh4eb) TARGET_ARCH=sh4 diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index b12660b9f8..ff9fbe958a 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -34,6 +34,7 @@ config RISCV_VIRT select PCI select HART select SERIAL + select GOLDFISH_RTC select VIRTIO_MMIO select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index c44b865959..7f9e1e5176 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -58,6 +58,7 @@ static const struct MemmapEntry { [VIRT_DEBUG] = { 0x0, 0x100 }, [VIRT_MROM] = { 0x1000, 0x11000 }, [VIRT_TEST] = { 0x100000, 0x1000 }, + [VIRT_RTC] = { 0x101000, 0x1000 }, [VIRT_CLINT] = { 0x2000000, 0x10000 }, [VIRT_PLIC] = { 0xc000000, 0x4000000 }, [VIRT_UART0] = { 0x10000000, 0x100 }, @@ -182,11 +183,10 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, uint64_t mem_size, const char *cmdline) { void *fdt; - int cpu; + int cpu, i; uint32_t *cells; char *nodename; - uint32_t plic_phandle, phandle = 1; - int i; + uint32_t plic_phandle, test_phandle, phandle = 1; hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2; hwaddr flashbase = virt_memmap[VIRT_FLASH].base; @@ -356,16 +356,35 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, create_pcie_irq_map(fdt, nodename, plic_phandle); g_free(nodename); + test_phandle = phandle++; nodename = g_strdup_printf("/test@%lx", (long)memmap[VIRT_TEST].base); qemu_fdt_add_subnode(fdt, nodename); { - const char compat[] = "sifive,test1\0sifive,test0"; + const char compat[] = "sifive,test1\0sifive,test0\0syscon"; qemu_fdt_setprop(fdt, nodename, "compatible", compat, sizeof(compat)); } qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0, memmap[VIRT_TEST].base, 0x0, memmap[VIRT_TEST].size); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", test_phandle); + test_phandle = qemu_fdt_get_phandle(fdt, nodename); + g_free(nodename); + + nodename = g_strdup_printf("/reboot"); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "syscon-reboot"); + qemu_fdt_setprop_cell(fdt, nodename, "regmap", test_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "offset", 0x0); + qemu_fdt_setprop_cell(fdt, nodename, "value", FINISHER_RESET); + g_free(nodename); + + nodename = g_strdup_printf("/poweroff"); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "syscon-poweroff"); + qemu_fdt_setprop_cell(fdt, nodename, "regmap", test_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "offset", 0x0); + qemu_fdt_setprop_cell(fdt, nodename, "value", FINISHER_PASS); g_free(nodename); nodename = g_strdup_printf("/uart@%lx", @@ -386,6 +405,18 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, } g_free(nodename); + nodename = g_strdup_printf("/rtc@%lx", + (long)memmap[VIRT_RTC].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "google,goldfish-rtc"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[VIRT_RTC].base, + 0x0, memmap[VIRT_RTC].size); + qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", RTC_IRQ); + g_free(nodename); + nodename = g_strdup_printf("/flash@%" PRIx64, flashbase); qemu_fdt_add_subnode(s->fdt, nodename); qemu_fdt_setprop_string(s->fdt, nodename, "compatible", "cfi-flash"); @@ -583,6 +614,9 @@ static void riscv_virt_board_init(MachineState *machine) 0, qdev_get_gpio_in(DEVICE(s->plic), UART0_IRQ), 399193, serial_hd(0), DEVICE_LITTLE_ENDIAN); + sysbus_create_simple("goldfish_rtc", memmap[VIRT_RTC].base, + qdev_get_gpio_in(DEVICE(s->plic), RTC_IRQ)); + virt_flash_create(s); for (i = 0; i < ARRAY_SIZE(s->flash); i++) { diff --git a/hw/rtc/Kconfig b/hw/rtc/Kconfig index 3dc2dd6888..f06e133b8a 100644 --- a/hw/rtc/Kconfig +++ b/hw/rtc/Kconfig @@ -22,3 +22,6 @@ config MC146818RTC config SUN4V_RTC bool + +config GOLDFISH_RTC + bool diff --git a/hw/rtc/Makefile.objs b/hw/rtc/Makefile.objs index 8dc9fcd3a9..aa208d0d10 100644 --- a/hw/rtc/Makefile.objs +++ b/hw/rtc/Makefile.objs @@ -11,3 +11,4 @@ common-obj-$(CONFIG_EXYNOS4) += exynos4210_rtc.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_rtc.o +common-obj-$(CONFIG_GOLDFISH_RTC) += goldfish_rtc.o diff --git a/hw/rtc/goldfish_rtc.c b/hw/rtc/goldfish_rtc.c new file mode 100644 index 0000000000..01e9d2b083 --- /dev/null +++ b/hw/rtc/goldfish_rtc.c @@ -0,0 +1,285 @@ +/* + * Goldfish virtual platform RTC + * + * Copyright (C) 2019 Western Digital Corporation or its affiliates. + * + * For more details on Google Goldfish virtual platform refer: + * https://android.googlesource.com/platform/external/qemu/+/refs/heads/emu-2.0-release/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "hw/rtc/goldfish_rtc.h" +#include "migration/vmstate.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "qemu/bitops.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "qemu/cutils.h" +#include "qemu/log.h" + +#include "trace.h" + +#define RTC_TIME_LOW 0x00 +#define RTC_TIME_HIGH 0x04 +#define RTC_ALARM_LOW 0x08 +#define RTC_ALARM_HIGH 0x0c +#define RTC_IRQ_ENABLED 0x10 +#define RTC_CLEAR_ALARM 0x14 +#define RTC_ALARM_STATUS 0x18 +#define RTC_CLEAR_INTERRUPT 0x1c + +static void goldfish_rtc_update(GoldfishRTCState *s) +{ + qemu_set_irq(s->irq, (s->irq_pending & s->irq_enabled) ? 1 : 0); +} + +static void goldfish_rtc_interrupt(void *opaque) +{ + GoldfishRTCState *s = (GoldfishRTCState *)opaque; + + s->alarm_running = 0; + s->irq_pending = 1; + goldfish_rtc_update(s); +} + +static uint64_t goldfish_rtc_get_count(GoldfishRTCState *s) +{ + return s->tick_offset + (uint64_t)qemu_clock_get_ns(rtc_clock); +} + +static void goldfish_rtc_clear_alarm(GoldfishRTCState *s) +{ + timer_del(s->timer); + s->alarm_running = 0; +} + +static void goldfish_rtc_set_alarm(GoldfishRTCState *s) +{ + uint64_t ticks = goldfish_rtc_get_count(s); + uint64_t event = s->alarm_next; + + if (event <= ticks) { + goldfish_rtc_clear_alarm(s); + goldfish_rtc_interrupt(s); + } else { + /* + * We should be setting timer expiry to: + * qemu_clock_get_ns(rtc_clock) + (event - ticks) + * but this is equivalent to: + * event - s->tick_offset + */ + timer_mod(s->timer, event - s->tick_offset); + s->alarm_running = 1; + } +} + +static uint64_t goldfish_rtc_read(void *opaque, hwaddr offset, + unsigned size) +{ + GoldfishRTCState *s = opaque; + uint64_t r = 0; + + switch (offset) { + case RTC_TIME_LOW: + r = goldfish_rtc_get_count(s) & 0xffffffff; + break; + case RTC_TIME_HIGH: + r = goldfish_rtc_get_count(s) >> 32; + break; + case RTC_ALARM_LOW: + r = s->alarm_next & 0xffffffff; + break; + case RTC_ALARM_HIGH: + r = s->alarm_next >> 32; + break; + case RTC_IRQ_ENABLED: + r = s->irq_enabled; + break; + case RTC_ALARM_STATUS: + r = s->alarm_running; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%x is UNIMP.\n", __func__, (uint32_t)offset); + break; + } + + trace_goldfish_rtc_read(offset, r); + + return r; +} + +static void goldfish_rtc_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + GoldfishRTCState *s = opaque; + uint64_t current_tick, new_tick; + + switch (offset) { + case RTC_TIME_LOW: + current_tick = goldfish_rtc_get_count(s); + new_tick = deposit64(current_tick, 0, 32, value); + s->tick_offset += new_tick - current_tick; + break; + case RTC_TIME_HIGH: + current_tick = goldfish_rtc_get_count(s); + new_tick = deposit64(current_tick, 32, 32, value); + s->tick_offset += new_tick - current_tick; + break; + case RTC_ALARM_LOW: + s->alarm_next = deposit64(s->alarm_next, 0, 32, value); + goldfish_rtc_set_alarm(s); + break; + case RTC_ALARM_HIGH: + s->alarm_next = deposit64(s->alarm_next, 32, 32, value); + break; + case RTC_IRQ_ENABLED: + s->irq_enabled = (uint32_t)(value & 0x1); + goldfish_rtc_update(s); + break; + case RTC_CLEAR_ALARM: + goldfish_rtc_clear_alarm(s); + break; + case RTC_CLEAR_INTERRUPT: + s->irq_pending = 0; + goldfish_rtc_update(s); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%x is UNIMP.\n", __func__, (uint32_t)offset); + break; + } + + trace_goldfish_rtc_write(offset, value); +} + +static int goldfish_rtc_pre_save(void *opaque) +{ + uint64_t delta; + GoldfishRTCState *s = opaque; + + /* + * We want to migrate this offset, which sounds straightforward. + * Unfortunately, we cannot directly pass tick_offset because + * rtc_clock on destination Host might not be same source Host. + * + * To tackle, this we pass tick_offset relative to vm_clock from + * source Host and make it relative to rtc_clock at destination Host. + */ + delta = qemu_clock_get_ns(rtc_clock) - + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->tick_offset_vmstate = s->tick_offset + delta; + + return 0; +} + +static int goldfish_rtc_post_load(void *opaque, int version_id) +{ + uint64_t delta; + GoldfishRTCState *s = opaque; + + /* + * We extract tick_offset from tick_offset_vmstate by doing + * reverse math compared to pre_save() function. + */ + delta = qemu_clock_get_ns(rtc_clock) - + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->tick_offset = s->tick_offset_vmstate - delta; + + return 0; +} + +static const MemoryRegionOps goldfish_rtc_ops = { + .read = goldfish_rtc_read, + .write = goldfish_rtc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static const VMStateDescription goldfish_rtc_vmstate = { + .name = TYPE_GOLDFISH_RTC, + .version_id = 1, + .pre_save = goldfish_rtc_pre_save, + .post_load = goldfish_rtc_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT64(tick_offset_vmstate, GoldfishRTCState), + VMSTATE_UINT64(alarm_next, GoldfishRTCState), + VMSTATE_UINT32(alarm_running, GoldfishRTCState), + VMSTATE_UINT32(irq_pending, GoldfishRTCState), + VMSTATE_UINT32(irq_enabled, GoldfishRTCState), + VMSTATE_END_OF_LIST() + } +}; + +static void goldfish_rtc_reset(DeviceState *dev) +{ + GoldfishRTCState *s = GOLDFISH_RTC(dev); + struct tm tm; + + timer_del(s->timer); + + qemu_get_timedate(&tm, 0); + s->tick_offset = mktimegm(&tm); + s->tick_offset *= NANOSECONDS_PER_SECOND; + s->tick_offset -= qemu_clock_get_ns(rtc_clock); + s->tick_offset_vmstate = 0; + s->alarm_next = 0; + s->alarm_running = 0; + s->irq_pending = 0; + s->irq_enabled = 0; +} + +static void goldfish_rtc_realize(DeviceState *d, Error **errp) +{ + SysBusDevice *dev = SYS_BUS_DEVICE(d); + GoldfishRTCState *s = GOLDFISH_RTC(d); + + memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_rtc_ops, s, + "goldfish_rtc", 0x24); + sysbus_init_mmio(dev, &s->iomem); + + sysbus_init_irq(dev, &s->irq); + + s->timer = timer_new_ns(rtc_clock, goldfish_rtc_interrupt, s); +} + +static void goldfish_rtc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = goldfish_rtc_realize; + dc->reset = goldfish_rtc_reset; + dc->vmsd = &goldfish_rtc_vmstate; +} + +static const TypeInfo goldfish_rtc_info = { + .name = TYPE_GOLDFISH_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(GoldfishRTCState), + .class_init = goldfish_rtc_class_init, +}; + +static void goldfish_rtc_register_types(void) +{ + type_register_static(&goldfish_rtc_info); +} + +type_init(goldfish_rtc_register_types) diff --git a/hw/rtc/trace-events b/hw/rtc/trace-events index 52c1566198..c9894e1747 100644 --- a/hw/rtc/trace-events +++ b/hw/rtc/trace-events @@ -23,3 +23,7 @@ m48txx_nvram_io_read(uint64_t addr, uint64_t value) "io read addr:0x%04" PRIx64 m48txx_nvram_io_write(uint64_t addr, uint64_t value) "io write addr:0x%04" PRIx64 " value:0x%02" PRIx64 m48txx_nvram_mem_read(uint32_t addr, uint32_t value) "mem read addr:0x%04x value:0x%02x" m48txx_nvram_mem_write(uint32_t addr, uint32_t value) "mem write addr:0x%04x value:0x%02x" + +# goldfish_rtc.c +goldfish_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 +goldfish_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index b17048a93a..e69355efaf 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -44,6 +44,7 @@ enum { VIRT_DEBUG, VIRT_MROM, VIRT_TEST, + VIRT_RTC, VIRT_CLINT, VIRT_PLIC, VIRT_UART0, @@ -57,6 +58,7 @@ enum { enum { UART0_IRQ = 10, + RTC_IRQ = 11, VIRTIO_IRQ = 1, /* 1 to 8 */ VIRTIO_COUNT = 8, PCIE_IRQ = 0x20, /* 32 to 35 */ diff --git a/include/hw/rtc/goldfish_rtc.h b/include/hw/rtc/goldfish_rtc.h new file mode 100644 index 0000000000..16f9f9e29d --- /dev/null +++ b/include/hw/rtc/goldfish_rtc.h @@ -0,0 +1,46 @@ +/* + * Goldfish virtual platform RTC + * + * Copyright (C) 2019 Western Digital Corporation or its affiliates. + * + * For more details on Google Goldfish virtual platform refer: + * https://android.googlesource.com/platform/external/qemu/+/master/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_RTC_GOLDFISH_RTC_H +#define HW_RTC_GOLDFISH_RTC_H + +#include "hw/sysbus.h" + +#define TYPE_GOLDFISH_RTC "goldfish_rtc" +#define GOLDFISH_RTC(obj) \ + OBJECT_CHECK(GoldfishRTCState, (obj), TYPE_GOLDFISH_RTC) + +typedef struct GoldfishRTCState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + QEMUTimer *timer; + qemu_irq irq; + + uint64_t tick_offset; + uint64_t tick_offset_vmstate; + uint64_t alarm_next; + uint32_t alarm_running; + uint32_t irq_pending; + uint32_t irq_enabled; +} GoldfishRTCState; + +#endif diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index 1a7947e019..1a72f7be9c 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -303,7 +303,12 @@ int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) static int riscv_gdb_get_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) { if (n < 32) { - return gdb_get_reg64(mem_buf, env->fpr[n]); + if (env->misa & RVD) { + return gdb_get_reg64(mem_buf, env->fpr[n]); + } + if (env->misa & RVF) { + return gdb_get_reg32(mem_buf, env->fpr[n]); + } /* there is hole between ft11 and fflags in fpu.xml */ } else if (n < 36 && n > 32) { target_ulong val = 0; @@ -403,23 +408,20 @@ void riscv_cpu_register_gdb_regs_for_features(CPUState *cs) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; -#if defined(TARGET_RISCV32) - if (env->misa & RVF) { + if (env->misa & RVD) { + gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu, + 36, "riscv-64bit-fpu.xml", 0); + } else if (env->misa & RVF) { gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu, 36, "riscv-32bit-fpu.xml", 0); } - +#if defined(TARGET_RISCV32) gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr, 240, "riscv-32bit-csr.xml", 0); gdb_register_coprocessor(cs, riscv_gdb_get_virtual, riscv_gdb_set_virtual, 1, "riscv-32bit-virtual.xml", 0); #elif defined(TARGET_RISCV64) - if (env->misa & RVF) { - gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu, - 36, "riscv-64bit-fpu.xml", 0); - } - gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr, 240, "riscv-64bit-csr.xml", 0); |