From 4b69c7e265a2c2fd1120c431c5d8d0809d4ec10a Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 17 Jun 2014 23:10:26 +0100 Subject: target-mips: Reset CPU timer consistently The MIPS CPU timer (CP0 Count/Compare registers & QEMU timer) is reset at machine initialisation, including starting the timeout. Both registers however are placed before mvp in CPUMIPSState so they will both be zeroed on reset by the memset in mips_cpu_reset() including soon after init. This doesn't take into account that the timer may be running, in which case env->CP0_Count will represent the delta against the VM clock and the timeout will need updating. At init time (cpu_mips_clock_init()), lets only create the timer. Setting Count = 1 and starting the timer (cpu_mips_store_count()) can be done at reset time from cpu_state_reset(), which is after the memset. There is also no need to set CP0_Compare = 0 as that is already handled by the memset. Note that a reset occurs from mips_cpu_realizefn() which is before the machine init callback has had a chance to set up the CPU interrupts and the CPU timer, so env->timer will be NULL. This case is handled explicitly in cpu_mips_store_count(), treating the timer as disabled (which will also be the right thing to do when KVM support is added). Reported-by: Paolo Bonzini Signed-off-by: James Hogan Cc: Aurelien Jarno Signed-off-by: Paolo Bonzini --- hw/mips/cputimer.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'hw') diff --git a/hw/mips/cputimer.c b/hw/mips/cputimer.c index c8b4b00..6900a74 100644 --- a/hw/mips/cputimer.c +++ b/hw/mips/cputimer.c @@ -85,7 +85,12 @@ uint32_t cpu_mips_get_count (CPUMIPSState *env) void cpu_mips_store_count (CPUMIPSState *env, uint32_t count) { - if (env->CP0_Cause & (1 << CP0Ca_DC)) + /* + * This gets called from cpu_state_reset(), potentially before timer init. + * So env->timer may be NULL, which is also the case with KVM enabled so + * treat timer as disabled in that case. + */ + if (env->CP0_Cause & (1 << CP0Ca_DC) || !env->timer) env->CP0_Count = count; else { /* Store new count register */ @@ -142,6 +147,4 @@ static void mips_timer_cb (void *opaque) void cpu_mips_clock_init (CPUMIPSState *env) { env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &mips_timer_cb, env); - env->CP0_Compare = 0; - cpu_mips_store_count(env, 1); } -- cgit v1.1 From 353a243e22fae2b993ab7f2d123c2b81013b3c5d Mon Sep 17 00:00:00 2001 From: Sanjay Lal Date: Tue, 17 Jun 2014 23:10:27 +0100 Subject: hw/mips/cputimer: Don't start periodic timer in KVM mode Compare/Count timer interrupts are handled in-kernel for KVM. Therefore don't bother creating the timer at init time if KVM is enabled. This will conveniently avoid attempts to set the timeout when cpu_mips_store_count() is called at reset with KVM enabled, treating the timer as stopped so that CP0_Count is modified directly. Signed-off-by: Sanjay Lal [james.hogan@imgtec.com: Update after "target-mips: Reset CPU timer consistently" which moves timer start to reset time] Signed-off-by: James Hogan Cc: Aurelien Jarno Cc: Paolo Bonzini Signed-off-by: Paolo Bonzini --- hw/mips/cputimer.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/mips/cputimer.c b/hw/mips/cputimer.c index 6900a74..577c9ae 100644 --- a/hw/mips/cputimer.c +++ b/hw/mips/cputimer.c @@ -23,6 +23,7 @@ #include "hw/hw.h" #include "hw/mips/cpudevs.h" #include "qemu/timer.h" +#include "sysemu/kvm.h" #define TIMER_FREQ 100 * 1000 * 1000 @@ -146,5 +147,11 @@ static void mips_timer_cb (void *opaque) void cpu_mips_clock_init (CPUMIPSState *env) { - env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &mips_timer_cb, env); + /* + * If we're in KVM mode, don't create the periodic timer, that is handled in + * kernel. + */ + if (!kvm_enabled()) { + env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &mips_timer_cb, env); + } } -- cgit v1.1 From 253fffe725e8ecc0147a4c6f0893493c1393f0f7 Mon Sep 17 00:00:00 2001 From: Sanjay Lal Date: Tue, 17 Jun 2014 23:10:28 +0100 Subject: hw/mips: Add API to convert KVM guest KSEG0 <-> GPA Add API for converting physical addresses to KVM guest KSEG0 addresses, and fix the existing API for converting KSEG0 addresses to physical addresses to work in the KVM case. Both have the same sized KSEG0, so it's just a case of fixing the mask. In KVM trap and emulate mode both the guest kernel and guest userspace execute in useg: Guest User address space: 0x00000000..0x3fffffff Guest Kernel Unmapped: 0x40000000..0x5fffffff Guest Kernel Mapped: 0x60000000..0x7fffffff Signed-off-by: Sanjay Lal Signed-off-by: James Hogan Cc: Aurelien Jarno Signed-off-by: Paolo Bonzini --- hw/mips/addr.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/mips/addr.c b/hw/mips/addr.c index 99488f1..ff3b952 100644 --- a/hw/mips/addr.c +++ b/hw/mips/addr.c @@ -25,10 +25,15 @@ uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr) { - return addr & 0x7fffffffll; + return addr & 0x1fffffffll; } uint64_t cpu_mips_phys_to_kseg0(void *opaque, uint64_t addr) { return addr | ~0x7fffffffll; } + +uint64_t cpu_mips_kvm_um_phys_to_kseg0(void *opaque, uint64_t addr) +{ + return addr | 0x40000000ll; +} -- cgit v1.1 From b1bd8b28ccad3a9813466895794216a02326876c Mon Sep 17 00:00:00 2001 From: Sanjay Lal Date: Tue, 17 Jun 2014 23:10:34 +0100 Subject: hw/mips: In KVM mode, inject IRQ2 (I/O) interrupts via ioctls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit COP0 emulation is in-kernel for KVM, so inject IRQ2 (I/O) interrupts via ioctls. Signed-off-by: Sanjay Lal Signed-off-by: James Hogan Reviewed-by: Aurelien Jarno Reviewed-by: Andreas Färber Signed-off-by: Paolo Bonzini --- hw/mips/mips_int.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'hw') diff --git a/hw/mips/mips_int.c b/hw/mips/mips_int.c index 7dbd24d..d740046 100644 --- a/hw/mips/mips_int.c +++ b/hw/mips/mips_int.c @@ -23,6 +23,8 @@ #include "hw/hw.h" #include "hw/mips/cpudevs.h" #include "cpu.h" +#include "sysemu/kvm.h" +#include "kvm_mips.h" static void cpu_mips_irq_request(void *opaque, int irq, int level) { @@ -35,8 +37,17 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level) if (level) { env->CP0_Cause |= 1 << (irq + CP0Ca_IP); + + if (kvm_enabled() && irq == 2) { + kvm_mips_set_interrupt(cpu, irq, level); + } + } else { env->CP0_Cause &= ~(1 << (irq + CP0Ca_IP)); + + if (kvm_enabled() && irq == 2) { + kvm_mips_set_interrupt(cpu, irq, level); + } } if (env->CP0_Cause & CP0Ca_IP_mask) { -- cgit v1.1 From b03118114d461a681eaf5fc1d07e99e228d45260 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 17 Jun 2014 23:10:35 +0100 Subject: hw/mips: malta: Add KVM support In KVM mode the bootrom is loaded and executed from the last 1MB of DRAM. Based on "[PATCH 12/12] KVM/MIPS: General KVM support and support for SMP Guests" by Sanjay Lal . Signed-off-by: James Hogan Reviewed-by: Aurelien Jarno Cc: Peter Maydell Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- hw/mips/mips_malta.c | 73 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 20 deletions(-) (limited to 'hw') diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index f4a7d47..8bc5392 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -51,6 +51,7 @@ #include "sysemu/qtest.h" #include "qemu/error-report.h" #include "hw/empty_slot.h" +#include "sysemu/kvm.h" //#define DEBUG_BOARD_INIT @@ -603,29 +604,31 @@ static void network_init(PCIBus *pci_bus) */ static void write_bootloader (CPUMIPSState *env, uint8_t *base, - int64_t kernel_entry) + int64_t run_addr, int64_t kernel_entry) { uint32_t *p; /* Small bootloader */ p = (uint32_t *)base; - stl_p(p++, 0x0bf00160); /* j 0x1fc00580 */ + + stl_p(p++, 0x08000000 | /* j 0x1fc00580 */ + ((run_addr + 0x580) & 0x0fffffff) >> 2); stl_p(p++, 0x00000000); /* nop */ /* YAMON service vector */ - stl_p(base + 0x500, 0xbfc00580); /* start: */ - stl_p(base + 0x504, 0xbfc0083c); /* print_count: */ - stl_p(base + 0x520, 0xbfc00580); /* start: */ - stl_p(base + 0x52c, 0xbfc00800); /* flush_cache: */ - stl_p(base + 0x534, 0xbfc00808); /* print: */ - stl_p(base + 0x538, 0xbfc00800); /* reg_cpu_isr: */ - stl_p(base + 0x53c, 0xbfc00800); /* unred_cpu_isr: */ - stl_p(base + 0x540, 0xbfc00800); /* reg_ic_isr: */ - stl_p(base + 0x544, 0xbfc00800); /* unred_ic_isr: */ - stl_p(base + 0x548, 0xbfc00800); /* reg_esr: */ - stl_p(base + 0x54c, 0xbfc00800); /* unreg_esr: */ - stl_p(base + 0x550, 0xbfc00800); /* getchar: */ - stl_p(base + 0x554, 0xbfc00800); /* syscon_read: */ + stl_p(base + 0x500, run_addr + 0x0580); /* start: */ + stl_p(base + 0x504, run_addr + 0x083c); /* print_count: */ + stl_p(base + 0x520, run_addr + 0x0580); /* start: */ + stl_p(base + 0x52c, run_addr + 0x0800); /* flush_cache: */ + stl_p(base + 0x534, run_addr + 0x0808); /* print: */ + stl_p(base + 0x538, run_addr + 0x0800); /* reg_cpu_isr: */ + stl_p(base + 0x53c, run_addr + 0x0800); /* unred_cpu_isr: */ + stl_p(base + 0x540, run_addr + 0x0800); /* reg_ic_isr: */ + stl_p(base + 0x544, run_addr + 0x0800); /* unred_ic_isr: */ + stl_p(base + 0x548, run_addr + 0x0800); /* reg_esr: */ + stl_p(base + 0x54c, run_addr + 0x0800); /* unreg_esr: */ + stl_p(base + 0x550, run_addr + 0x0800); /* getchar: */ + stl_p(base + 0x554, run_addr + 0x0800); /* syscon_read: */ /* Second part of the bootloader */ @@ -701,7 +704,7 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base, p = (uint32_t *) (base + 0x800); stl_p(p++, 0x03e00008); /* jr ra */ stl_p(p++, 0x24020000); /* li v0,0 */ - /* 808 YAMON print */ + /* 808 YAMON print */ stl_p(p++, 0x03e06821); /* move t5,ra */ stl_p(p++, 0x00805821); /* move t3,a0 */ stl_p(p++, 0x00a05021); /* move t2,a1 */ @@ -774,6 +777,7 @@ static int64_t load_kernel (void) uint32_t *prom_buf; long prom_size; int prom_index = 0; + uint64_t (*xlate_to_kseg0) (void *opaque, uint64_t addr); #ifdef TARGET_WORDS_BIGENDIAN big_endian = 1; @@ -788,6 +792,11 @@ static int64_t load_kernel (void) loaderparams.kernel_filename); exit(1); } + if (kvm_enabled()) { + xlate_to_kseg0 = cpu_mips_kvm_um_phys_to_kseg0; + } else { + xlate_to_kseg0 = cpu_mips_phys_to_kseg0; + } /* load initrd */ initrd_size = 0; @@ -820,7 +829,7 @@ static int64_t load_kernel (void) prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_filename); if (initrd_size > 0) { prom_set(prom_buf, prom_index++, "rd_start=0x%" PRIx64 " rd_size=%li %s", - cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size, + xlate_to_kseg0(NULL, initrd_offset), initrd_size, loaderparams.kernel_cmdline); } else { prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_cmdline); @@ -829,6 +838,7 @@ static int64_t load_kernel (void) prom_set(prom_buf, prom_index++, "memsize"); prom_set(prom_buf, prom_index++, "%i", MIN(loaderparams.ram_size, 256 << 20)); + prom_set(prom_buf, prom_index++, "modetty0"); prom_set(prom_buf, prom_index++, "38400n8r"); prom_set(prom_buf, prom_index++, NULL); @@ -863,6 +873,11 @@ static void main_cpu_reset(void *opaque) } malta_mips_config(cpu); + + if (kvm_enabled()) { + /* Start running from the bootloader we wrote to end of RAM */ + env->active_tc.PC = 0x40000000 + loaderparams.ram_size; + } } static void cpu_request_exit(void *opaque, int irq, int level) @@ -878,6 +893,7 @@ static void mips_malta_init(MachineState *machine) { ram_addr_t ram_size = machine->ram_size; + ram_addr_t ram_low_size; const char *cpu_model = machine->cpu_model; const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; @@ -892,7 +908,7 @@ void mips_malta_init(MachineState *machine) target_long bios_size = FLASH_SIZE; const size_t smbus_eeprom_size = 8 * 256; uint8_t *smbus_eeprom_buf = g_malloc0(smbus_eeprom_size); - int64_t kernel_entry; + int64_t kernel_entry, bootloader_run_addr; PCIBus *pci_bus; ISABus *isa_bus; MIPSCPU *cpu; @@ -1011,13 +1027,30 @@ void mips_malta_init(MachineState *machine) bios = pflash_cfi01_get_memory(fl); fl_idx++; if (kernel_filename) { + ram_low_size = MIN(ram_size, 256 << 20); + /* For KVM T&E we reserve 1MB of RAM for running bootloader */ + if (kvm_enabled()) { + ram_low_size -= 0x100000; + bootloader_run_addr = 0x40000000 + ram_low_size; + } else { + bootloader_run_addr = 0xbfc00000; + } + /* Write a small bootloader to the flash location. */ - loaderparams.ram_size = MIN(ram_size, 256 << 20); + loaderparams.ram_size = ram_low_size; loaderparams.kernel_filename = kernel_filename; loaderparams.kernel_cmdline = kernel_cmdline; loaderparams.initrd_filename = initrd_filename; kernel_entry = load_kernel(); - write_bootloader(env, memory_region_get_ram_ptr(bios), kernel_entry); + + write_bootloader(env, memory_region_get_ram_ptr(bios), + bootloader_run_addr, kernel_entry); + if (kvm_enabled()) { + /* Write the bootloader code @ the end of RAM, 1MB reserved */ + write_bootloader(env, memory_region_get_ram_ptr(ram_low_preio) + + ram_low_size, + bootloader_run_addr, kernel_entry); + } } else { /* Load firmware from flash. */ if (!dinfo) { -- cgit v1.1 From 3c5d0be553b18d47361ac3a701e2bff86b8256b0 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 20 Jun 2014 12:47:59 +0100 Subject: hw/mips: malta: Don't boot from flash with KVM T&E In KVM trap & emulate (T&E) mode the flash reset region at 0xbfc00000 isn't executable, which is why the minimal kernel bootloader is loaded and executed from the last 1MB of DRAM instead. Therefore if no kernel is provided on the command line and KVM is enabled, exit with an error since booting from flash will fail. Reported-by: Aurelien Jarno Signed-off-by: James Hogan Cc: Paolo Bonzini Signed-off-by: Paolo Bonzini --- hw/mips/mips_malta.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'hw') diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 8bc5392..91b0ce5 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1052,6 +1052,12 @@ void mips_malta_init(MachineState *machine) bootloader_run_addr, kernel_entry); } } else { + /* The flash region isn't executable from a KVM T&E guest */ + if (kvm_enabled()) { + error_report("KVM enabled but no -kernel argument was specified. " + "Booting from flash is not supported with KVM T&E."); + exit(1); + } /* Load firmware from flash. */ if (!dinfo) { /* Load a BIOS image. */ -- cgit v1.1