diff options
Diffstat (limited to 'hw/core')
| -rw-r--r-- | hw/core/Makefile.objs | 1 | ||||
| -rw-r--r-- | hw/core/cpu.c | 458 | ||||
| -rw-r--r-- | hw/core/generic-loader.c | 2 | ||||
| -rw-r--r-- | hw/core/loader.c | 48 | ||||
| -rw-r--r-- | hw/core/machine-qmp-cmds.c | 10 | ||||
| -rw-r--r-- | hw/core/machine.c | 3 | ||||
| -rw-r--r-- | hw/core/null-machine.c | 2 | ||||
| -rw-r--r-- | hw/core/numa.c | 2 | ||||
| -rw-r--r-- | hw/core/qdev-properties-system.c | 57 |
9 files changed, 572 insertions, 11 deletions
diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index b49f880a0c..fd0550d1d9 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -8,6 +8,7 @@ common-obj-y += irq.o common-obj-y += hotplug.o common-obj-$(CONFIG_SOFTMMU) += nmi.o common-obj-$(CONFIG_SOFTMMU) += vm-change-state-handler.o +common-obj-y += cpu.o common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o common-obj-$(CONFIG_XILINX_AXI) += stream.o diff --git a/hw/core/cpu.c b/hw/core/cpu.c new file mode 100644 index 0000000000..0035845511 --- /dev/null +++ b/hw/core/cpu.c @@ -0,0 +1,458 @@ +/* + * QEMU CPU model + * + * Copyright (c) 2012-2014 SUSE LINUX Products GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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/gpl-2.0.html> + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/core/cpu.h" +#include "sysemu/hw_accel.h" +#include "qemu/notify.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "exec/log.h" +#include "qemu/error-report.h" +#include "qemu/qemu-print.h" +#include "sysemu/tcg.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "trace-root.h" + +CPUInterruptHandler cpu_interrupt_handler; + +CPUState *cpu_by_arch_id(int64_t id) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (cc->get_arch_id(cpu) == id) { + return cpu; + } + } + return NULL; +} + +bool cpu_exists(int64_t id) +{ + return !!cpu_by_arch_id(id); +} + +CPUState *cpu_create(const char *typename) +{ + Error *err = NULL; + CPUState *cpu = CPU(object_new(typename)); + object_property_set_bool(OBJECT(cpu), true, "realized", &err); + if (err != NULL) { + error_report_err(err); + object_unref(OBJECT(cpu)); + exit(EXIT_FAILURE); + } + return cpu; +} + +bool cpu_paging_enabled(const CPUState *cpu) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + return cc->get_paging_enabled(cpu); +} + +static bool cpu_common_get_paging_enabled(const CPUState *cpu) +{ + return false; +} + +void cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, + Error **errp) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + cc->get_memory_mapping(cpu, list, errp); +} + +static void cpu_common_get_memory_mapping(CPUState *cpu, + MemoryMappingList *list, + Error **errp) +{ + error_setg(errp, "Obtaining memory mappings is unsupported on this CPU."); +} + +/* Resetting the IRQ comes from across the code base so we take the + * BQL here if we need to. cpu_interrupt assumes it is held.*/ +void cpu_reset_interrupt(CPUState *cpu, int mask) +{ + bool need_lock = !qemu_mutex_iothread_locked(); + + if (need_lock) { + qemu_mutex_lock_iothread(); + } + cpu->interrupt_request &= ~mask; + if (need_lock) { + qemu_mutex_unlock_iothread(); + } +} + +void cpu_exit(CPUState *cpu) +{ + atomic_set(&cpu->exit_request, 1); + /* Ensure cpu_exec will see the exit request after TCG has exited. */ + smp_wmb(); + atomic_set(&cpu->icount_decr_ptr->u16.high, -1); +} + +int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, + void *opaque) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + return (*cc->write_elf32_qemunote)(f, cpu, opaque); +} + +static int cpu_common_write_elf32_qemunote(WriteCoreDumpFunction f, + CPUState *cpu, void *opaque) +{ + return 0; +} + +int cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, + int cpuid, void *opaque) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + return (*cc->write_elf32_note)(f, cpu, cpuid, opaque); +} + +static int cpu_common_write_elf32_note(WriteCoreDumpFunction f, + CPUState *cpu, int cpuid, + void *opaque) +{ + return -1; +} + +int cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, + void *opaque) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + return (*cc->write_elf64_qemunote)(f, cpu, opaque); +} + +static int cpu_common_write_elf64_qemunote(WriteCoreDumpFunction f, + CPUState *cpu, void *opaque) +{ + return 0; +} + +int cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, + int cpuid, void *opaque) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + return (*cc->write_elf64_note)(f, cpu, cpuid, opaque); +} + +static int cpu_common_write_elf64_note(WriteCoreDumpFunction f, + CPUState *cpu, int cpuid, + void *opaque) +{ + return -1; +} + + +static int cpu_common_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg) +{ + return 0; +} + +static int cpu_common_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg) +{ + return 0; +} + +static bool cpu_common_debug_check_watchpoint(CPUState *cpu, CPUWatchpoint *wp) +{ + /* If no extra check is required, QEMU watchpoint match can be considered + * as an architectural match. + */ + return true; +} + +static bool cpu_common_virtio_is_big_endian(CPUState *cpu) +{ + return target_words_bigendian(); +} + +static void cpu_common_noop(CPUState *cpu) +{ +} + +static bool cpu_common_exec_interrupt(CPUState *cpu, int int_req) +{ + return false; +} + +GuestPanicInformation *cpu_get_crash_info(CPUState *cpu) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + GuestPanicInformation *res = NULL; + + if (cc->get_crash_info) { + res = cc->get_crash_info(cpu); + } + return res; +} + +void cpu_dump_state(CPUState *cpu, FILE *f, int flags) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (cc->dump_state) { + cpu_synchronize_state(cpu); + cc->dump_state(cpu, f, flags); + } +} + +void cpu_dump_statistics(CPUState *cpu, int flags) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (cc->dump_statistics) { + cc->dump_statistics(cpu, flags); + } +} + +void cpu_reset(CPUState *cpu) +{ + CPUClass *klass = CPU_GET_CLASS(cpu); + + if (klass->reset != NULL) { + (*klass->reset)(cpu); + } + + trace_guest_cpu_reset(cpu); +} + +static void cpu_common_reset(CPUState *cpu) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (qemu_loglevel_mask(CPU_LOG_RESET)) { + qemu_log("CPU Reset (CPU %d)\n", cpu->cpu_index); + log_cpu_state(cpu, cc->reset_dump_flags); + } + + cpu->interrupt_request = 0; + cpu->halted = 0; + cpu->mem_io_pc = 0; + cpu->mem_io_vaddr = 0; + cpu->icount_extra = 0; + atomic_set(&cpu->icount_decr_ptr->u32, 0); + cpu->can_do_io = 1; + cpu->exception_index = -1; + cpu->crash_occurred = false; + cpu->cflags_next_tb = -1; + + if (tcg_enabled()) { + cpu_tb_jmp_cache_clear(cpu); + + tcg_flush_softmmu_tlb(cpu); + } +} + +static bool cpu_common_has_work(CPUState *cs) +{ + return false; +} + +ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) +{ + CPUClass *cc = CPU_CLASS(object_class_by_name(typename)); + + assert(cpu_model && cc->class_by_name); + return cc->class_by_name(cpu_model); +} + +static void cpu_common_parse_features(const char *typename, char *features, + Error **errp) +{ + char *val; + static bool cpu_globals_initialized; + /* Single "key=value" string being parsed */ + char *featurestr = features ? strtok(features, ",") : NULL; + + /* should be called only once, catch invalid users */ + assert(!cpu_globals_initialized); + cpu_globals_initialized = true; + + while (featurestr) { + val = strchr(featurestr, '='); + if (val) { + GlobalProperty *prop = g_new0(typeof(*prop), 1); + *val = 0; + val++; + prop->driver = typename; + prop->property = g_strdup(featurestr); + prop->value = g_strdup(val); + qdev_prop_register_global(prop); + } else { + error_setg(errp, "Expected key=value format, found %s.", + featurestr); + return; + } + featurestr = strtok(NULL, ","); + } +} + +static void cpu_common_realizefn(DeviceState *dev, Error **errp) +{ + CPUState *cpu = CPU(dev); + Object *machine = qdev_get_machine(); + + /* qdev_get_machine() can return something that's not TYPE_MACHINE + * if this is one of the user-only emulators; in that case there's + * no need to check the ignore_memory_transaction_failures board flag. + */ + if (object_dynamic_cast(machine, TYPE_MACHINE)) { + ObjectClass *oc = object_get_class(machine); + MachineClass *mc = MACHINE_CLASS(oc); + + if (mc) { + cpu->ignore_memory_transaction_failures = + mc->ignore_memory_transaction_failures; + } + } + + if (dev->hotplugged) { + cpu_synchronize_post_init(cpu); + cpu_resume(cpu); + } + + /* NOTE: latest generic point where the cpu is fully realized */ + trace_init_vcpu(cpu); +} + +static void cpu_common_unrealizefn(DeviceState *dev, Error **errp) +{ + CPUState *cpu = CPU(dev); + /* NOTE: latest generic point before the cpu is fully unrealized */ + trace_fini_vcpu(cpu); + cpu_exec_unrealizefn(cpu); +} + +static void cpu_common_initfn(Object *obj) +{ + CPUState *cpu = CPU(obj); + CPUClass *cc = CPU_GET_CLASS(obj); + + cpu->cpu_index = UNASSIGNED_CPU_INDEX; + cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX; + cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs; + /* *-user doesn't have configurable SMP topology */ + /* the default value is changed by qemu_init_vcpu() for softmmu */ + cpu->nr_cores = 1; + cpu->nr_threads = 1; + + qemu_mutex_init(&cpu->work_mutex); + QTAILQ_INIT(&cpu->breakpoints); + QTAILQ_INIT(&cpu->watchpoints); + + cpu_exec_initfn(cpu); +} + +static void cpu_common_finalize(Object *obj) +{ + CPUState *cpu = CPU(obj); + + qemu_mutex_destroy(&cpu->work_mutex); +} + +static int64_t cpu_common_get_arch_id(CPUState *cpu) +{ + return cpu->cpu_index; +} + +static vaddr cpu_adjust_watchpoint_address(CPUState *cpu, vaddr addr, int len) +{ + return addr; +} + +static void generic_handle_interrupt(CPUState *cpu, int mask) +{ + cpu->interrupt_request |= mask; + + if (!qemu_cpu_is_self(cpu)) { + qemu_cpu_kick(cpu); + } +} + +CPUInterruptHandler cpu_interrupt_handler = generic_handle_interrupt; + +static void cpu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + CPUClass *k = CPU_CLASS(klass); + + k->parse_features = cpu_common_parse_features; + k->reset = cpu_common_reset; + k->get_arch_id = cpu_common_get_arch_id; + k->has_work = cpu_common_has_work; + k->get_paging_enabled = cpu_common_get_paging_enabled; + k->get_memory_mapping = cpu_common_get_memory_mapping; + k->write_elf32_qemunote = cpu_common_write_elf32_qemunote; + k->write_elf32_note = cpu_common_write_elf32_note; + k->write_elf64_qemunote = cpu_common_write_elf64_qemunote; + k->write_elf64_note = cpu_common_write_elf64_note; + k->gdb_read_register = cpu_common_gdb_read_register; + k->gdb_write_register = cpu_common_gdb_write_register; + k->virtio_is_big_endian = cpu_common_virtio_is_big_endian; + k->debug_excp_handler = cpu_common_noop; + k->debug_check_watchpoint = cpu_common_debug_check_watchpoint; + k->cpu_exec_enter = cpu_common_noop; + k->cpu_exec_exit = cpu_common_noop; + k->cpu_exec_interrupt = cpu_common_exec_interrupt; + k->adjust_watchpoint_address = cpu_adjust_watchpoint_address; + set_bit(DEVICE_CATEGORY_CPU, dc->categories); + dc->realize = cpu_common_realizefn; + dc->unrealize = cpu_common_unrealizefn; + dc->props = cpu_common_props; + /* + * Reason: CPUs still need special care by board code: wiring up + * IRQs, adding reset handlers, halting non-first CPUs, ... + */ + dc->user_creatable = false; +} + +static const TypeInfo cpu_type_info = { + .name = TYPE_CPU, + .parent = TYPE_DEVICE, + .instance_size = sizeof(CPUState), + .instance_init = cpu_common_initfn, + .instance_finalize = cpu_common_finalize, + .abstract = true, + .class_size = sizeof(CPUClass), + .class_init = cpu_class_init, +}; + +static void cpu_register_types(void) +{ + type_register_static(&cpu_type_info); +} + +type_init(cpu_register_types) diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index 06d8d6466e..4b1fc86a06 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -31,7 +31,7 @@ */ #include "qemu/osdep.h" -#include "qom/cpu.h" +#include "hw/core/cpu.h" #include "hw/sysbus.h" #include "sysemu/dma.h" #include "sysemu/reset.h" diff --git a/hw/core/loader.c b/hw/core/loader.c index 84e4f3efac..32f7cc7c33 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -58,6 +58,7 @@ #include "exec/address-spaces.h" #include "hw/boards.h" #include "qemu/cutils.h" +#include "sysemu/runstate.h" #include <zlib.h> @@ -838,6 +839,7 @@ struct Rom { int isrom; char *fw_dir; char *fw_file; + GMappedFile *mapped_file; bool committed; @@ -848,10 +850,25 @@ struct Rom { static FWCfgState *fw_cfg; static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms); -/* rom->data must be heap-allocated (do not use with rom_add_elf_program()) */ +/* + * rom->data can be heap-allocated or memory-mapped (e.g. when added with + * rom_add_elf_program()) + */ +static void rom_free_data(Rom *rom) +{ + if (rom->mapped_file) { + g_mapped_file_unref(rom->mapped_file); + rom->mapped_file = NULL; + } else { + g_free(rom->data); + } + + rom->data = NULL; +} + static void rom_free(Rom *rom) { - g_free(rom->data); + rom_free_data(rom); g_free(rom->path); g_free(rom->name); g_free(rom->fw_dir); @@ -1058,11 +1075,12 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, /* This function is specific for elf program because we don't need to allocate * all the rom. We just allocate the first part and the rest is just zeros. This - * is why romsize and datasize are different. Also, this function seize the - * memory ownership of "data", so we don't have to allocate and copy the buffer. + * is why romsize and datasize are different. Also, this function takes its own + * reference to "mapped_file", so we don't have to allocate and copy the buffer. */ -int rom_add_elf_program(const char *name, void *data, size_t datasize, - size_t romsize, hwaddr addr, AddressSpace *as) +int rom_add_elf_program(const char *name, GMappedFile *mapped_file, void *data, + size_t datasize, size_t romsize, hwaddr addr, + AddressSpace *as) { Rom *rom; @@ -1073,6 +1091,12 @@ int rom_add_elf_program(const char *name, void *data, size_t datasize, rom->romsize = romsize; rom->data = data; rom->as = as; + + if (mapped_file && data) { + g_mapped_file_ref(mapped_file); + rom->mapped_file = mapped_file; + } + rom_insert(rom); return 0; } @@ -1091,6 +1115,15 @@ static void rom_reset(void *unused) { Rom *rom; + /* + * We don't need to fill in the RAM with ROM data because we'll fill + * the data in during the next incoming migration in all cases. Note + * that some of those RAMs can actually be modified by the guest on ARM + * so this is probably the only right thing to do here. + */ + if (runstate_check(RUN_STATE_INMIGRATE)) + return; + QTAILQ_FOREACH(rom, &roms, next) { if (rom->fw_file) { continue; @@ -1107,8 +1140,7 @@ static void rom_reset(void *unused) } if (rom->isrom) { /* rom needs to be written only once */ - g_free(rom->data); - rom->data = NULL; + rom_free_data(rom); } /* * The rom loader is really on the same level as firmware in the guest diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 526fbd5ced..15cf7c62e3 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -249,6 +249,16 @@ CurrentMachineParams *qmp_query_current_machine(Error **errp) return params; } +TargetInfo *qmp_query_target(Error **errp) +{ + TargetInfo *info = g_malloc0(sizeof(*info)); + + info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1, + &error_abort); + + return info; +} + HotpluggableCPUList *qmp_query_hotpluggable_cpus(Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); diff --git a/hw/core/machine.c b/hw/core/machine.c index 32d1ca9abc..83cd1bfeec 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -27,6 +27,9 @@ #include "hw/pci/pci.h" #include "hw/mem/nvdimm.h" +GlobalProperty hw_compat_4_1[] = {}; +const size_t hw_compat_4_1_len = G_N_ELEMENTS(hw_compat_4_1); + GlobalProperty hw_compat_4_0[] = { { "VGA", "edid", "false" }, { "secondary-vga", "edid", "false" }, diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c index 30b1991b52..1aa0a9a01a 100644 --- a/hw/core/null-machine.c +++ b/hw/core/null-machine.c @@ -16,7 +16,7 @@ #include "hw/boards.h" #include "sysemu/sysemu.h" #include "exec/address-spaces.h" -#include "qom/cpu.h" +#include "hw/core/cpu.h" static void machine_none_init(MachineState *mch) { diff --git a/hw/core/numa.c b/hw/core/numa.c index 7a63ddc4c6..4f7e4628a0 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -34,7 +34,7 @@ #include "qapi/opts-visitor.h" #include "qapi/qapi-visit-machine.h" #include "sysemu/qtest.h" -#include "qom/cpu.h" +#include "hw/core/cpu.h" #include "hw/mem/pc-dimm.h" #include "migration/vmstate.h" #include "hw/boards.h" diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index fceab9afd5..70bfd4809b 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "audio/audio.h" #include "net/net.h" #include "hw/qdev-properties.h" #include "qapi/error.h" @@ -353,6 +354,62 @@ const PropertyInfo qdev_prop_netdev = { }; +/* --- audiodev --- */ +static void get_audiodev(Object *obj, Visitor *v, const char* name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + QEMUSoundCard *card = qdev_get_prop_ptr(dev, prop); + char *p = g_strdup(audio_get_id(card)); + + visit_type_str(v, name, &p, errp); + g_free(p); +} + +static void set_audiodev(Object *obj, Visitor *v, const char* name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + QEMUSoundCard *card = qdev_get_prop_ptr(dev, prop); + AudioState *state; + Error *local_err = NULL; + int err = 0; + char *str; + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_str(v, name, &str, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + state = audio_state_by_name(str); + + if (!state) { + err = -ENOENT; + goto out; + } + card->state = state; + +out: + error_set_from_qdev_prop_error(errp, err, dev, prop, str); + g_free(str); +} + +const PropertyInfo qdev_prop_audiodev = { + .name = "str", + .description = "ID of an audiodev to use as a backend", + /* release done on shutdown */ + .get = get_audiodev, + .set = set_audiodev, +}; + void qdev_prop_set_drive(DeviceState *dev, const char *name, BlockBackend *value, Error **errp) { |