import Unicorn2
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += intc/
|
||||
devices-dirs-y += core/
|
||||
common-obj-y += $(devices-dirs-y)
|
||||
obj-y += $(devices-dirs-y)
|
||||
@@ -1,2 +0,0 @@
|
||||
obj-y += tosa.o
|
||||
obj-y += virt.o
|
||||
@@ -1,45 +0,0 @@
|
||||
/* vim:set shiftwidth=4 ts=4 et: */
|
||||
/*
|
||||
* PXA255 Sharp Zaurus SL-6000 PDA platform
|
||||
*
|
||||
* Copyright (c) 2008 Dmitry Baryshkov
|
||||
*
|
||||
* Code based on spitz platform by Andrzej Zaborowski <balrog@zabor.org>
|
||||
* This code is licensed under the GNU GPL v2.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/arm/arm.h"
|
||||
#include "hw/boards.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
|
||||
static int tosa_init(struct uc_struct *uc, MachineState *machine)
|
||||
{
|
||||
if (uc->mode & UC_MODE_MCLASS)
|
||||
uc->cpu = (CPUState *)cpu_arm_init(uc, "cortex-m3");
|
||||
else if (uc->mode & UC_MODE_ARM926)
|
||||
uc->cpu = (CPUState *)cpu_arm_init(uc, "arm926");
|
||||
else if (uc->mode & UC_MODE_ARM946)
|
||||
uc->cpu = (CPUState *)cpu_arm_init(uc, "arm946");
|
||||
else if (uc->mode & UC_MODE_ARM1176)
|
||||
uc->cpu = (CPUState *)cpu_arm_init(uc, "arm1176");
|
||||
else
|
||||
uc->cpu = (CPUState *)cpu_arm_init(uc, "cortex-a15");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tosa_machine_init(struct uc_struct *uc)
|
||||
{
|
||||
static QEMUMachine tosapda_machine = { 0 };
|
||||
tosapda_machine.name = "tosa",
|
||||
tosapda_machine.init = tosa_init,
|
||||
tosapda_machine.is_default = 1,
|
||||
tosapda_machine.arch = UC_ARCH_ARM,
|
||||
|
||||
qemu_register_machine(uc, &tosapda_machine, TYPE_MACHINE, NULL);
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* ARM mach-virt emulation
|
||||
*
|
||||
* Copyright (c) 2013 Linaro Limited
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
* Emulate a virtual board which works by passing Linux all the information
|
||||
* it needs about what devices are present via the device tree.
|
||||
* There are some restrictions about what we can do here:
|
||||
* + we can only present devices whose Linux drivers will work based
|
||||
* purely on the device tree with no platform data at all
|
||||
* + we want to present a very stripped-down minimalist platform,
|
||||
* both because this reduces the security attack surface from the guest
|
||||
* and also because it reduces our exposure to being broken when
|
||||
* the kernel updates its device tree bindings and requires further
|
||||
* information in a device binding that we aren't providing.
|
||||
* This is essentially the same approach kvmtool uses.
|
||||
*/
|
||||
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh, 2015 */
|
||||
|
||||
#include "hw/arm/arm.h"
|
||||
#include "hw/boards.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
|
||||
static int machvirt_init(struct uc_struct *uc, MachineState *machine)
|
||||
{
|
||||
const char *cpu_model = machine->cpu_model;
|
||||
int n;
|
||||
|
||||
if (!cpu_model) {
|
||||
cpu_model = "cortex-a57"; // ARM64
|
||||
}
|
||||
|
||||
for (n = 0; n < smp_cpus; n++) {
|
||||
Object *cpuobj;
|
||||
ObjectClass *oc = cpu_class_by_name(uc, TYPE_ARM_CPU, cpu_model);
|
||||
|
||||
if (!oc) {
|
||||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cpuobj = object_new(uc, object_class_get_name(oc));
|
||||
uc->cpu = (CPUState *)cpuobj;
|
||||
object_property_set_bool(uc, cpuobj, true, "realized", NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void machvirt_machine_init(struct uc_struct *uc)
|
||||
{
|
||||
static QEMUMachine machvirt_a15_machine = { 0 };
|
||||
machvirt_a15_machine.name = "virt",
|
||||
machvirt_a15_machine.init = machvirt_init,
|
||||
machvirt_a15_machine.is_default = 1,
|
||||
machvirt_a15_machine.arch = UC_ARCH_ARM64,
|
||||
|
||||
qemu_register_machine(uc, &machvirt_a15_machine, TYPE_MACHINE, NULL);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# core qdev-related obj files, also used by *-user:
|
||||
common-obj-y += qdev.o
|
||||
common-obj-$(CONFIG_SOFTMMU) += machine.o
|
||||
154
qemu/hw/core/cpu.c
Normal file
154
qemu/hw/core/cpu.c
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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 "uc_priv.h"
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "sysemu/tcg.h"
|
||||
|
||||
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)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
|
||||
cc->get_memory_mapping(cpu, list);
|
||||
}
|
||||
|
||||
static void cpu_common_get_memory_mapping(CPUState *cpu,
|
||||
MemoryMappingList *list)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
cpu->interrupt_request &= ~mask;
|
||||
}
|
||||
|
||||
void cpu_exit(CPUState *cpu)
|
||||
{
|
||||
cpu->exit_request = 1;
|
||||
cpu->tcg_exit_req = 1;
|
||||
cpu->icount_decr_ptr->u16.high = -1;
|
||||
}
|
||||
|
||||
static void cpu_common_noop(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
static bool cpu_common_exec_interrupt(CPUState *cpu, int int_req)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void cpu_reset(CPUState *cpu)
|
||||
{
|
||||
CPUClass *klass = CPU_GET_CLASS(cpu);
|
||||
|
||||
if (klass->reset != NULL) {
|
||||
(*klass->reset)(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static void cpu_common_reset(CPUState *dev)
|
||||
{
|
||||
CPUState *cpu = CPU(dev);
|
||||
|
||||
cpu->interrupt_request = 0;
|
||||
cpu->halted = 0;
|
||||
cpu->mem_io_pc = 0;
|
||||
cpu->icount_extra = 0;
|
||||
cpu->can_do_io = 1;
|
||||
cpu->exception_index = -1;
|
||||
cpu->crash_occurred = false;
|
||||
cpu->cflags_next_tb = -1;
|
||||
|
||||
cpu_tb_jmp_cache_clear(cpu);
|
||||
|
||||
cpu->uc->tcg_flush_tlb(cpu->uc);
|
||||
}
|
||||
|
||||
static bool cpu_common_has_work(CPUState *cs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static int64_t cpu_common_get_arch_id(CPUState *cpu)
|
||||
{
|
||||
return cpu->cpu_index;
|
||||
}
|
||||
|
||||
void cpu_class_init(struct uc_struct *uc, CPUClass *k)
|
||||
{
|
||||
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->debug_excp_handler = cpu_common_noop;
|
||||
k->cpu_exec_enter = cpu_common_noop;
|
||||
k->cpu_exec_exit = cpu_common_noop;
|
||||
k->cpu_exec_interrupt = cpu_common_exec_interrupt;
|
||||
/* instead of dc->reset. */
|
||||
k->reset = cpu_common_reset;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void cpu_common_initfn(struct uc_struct *uc, CPUState *cs)
|
||||
{
|
||||
CPUState *cpu = CPU(cs);
|
||||
|
||||
cpu->cpu_index = UNASSIGNED_CPU_INDEX;
|
||||
cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX;
|
||||
/* *-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;
|
||||
|
||||
QTAILQ_INIT(&cpu->breakpoints);
|
||||
QTAILQ_INIT(&cpu->watchpoints);
|
||||
|
||||
/* cpu_exec_initfn(cpu); */
|
||||
cpu->num_ases = 1;
|
||||
cpu->as = &(cpu->uc->address_space_memory);
|
||||
cpu->memory = cpu->uc->system_memory;
|
||||
}
|
||||
|
||||
void cpu_stop(struct uc_struct *uc)
|
||||
{
|
||||
if (uc->cpu) {
|
||||
uc->cpu->stop = false;
|
||||
uc->cpu->stopped = true;
|
||||
cpu_exit(uc->cpu);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* QEMU Machine
|
||||
*
|
||||
* Copyright (C) 2014 Red Hat Inc
|
||||
*
|
||||
* Authors:
|
||||
* Marcel Apfelbaum <marcel.a@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.
|
||||
*/
|
||||
|
||||
#include "hw/boards.h"
|
||||
|
||||
static void machine_initfn(struct uc_struct *uc, Object *obj, void *opaque)
|
||||
{
|
||||
}
|
||||
|
||||
static void machine_finalize(struct uc_struct *uc, Object *obj, void *opaque)
|
||||
{
|
||||
}
|
||||
|
||||
static const TypeInfo machine_info = {
|
||||
TYPE_MACHINE,
|
||||
TYPE_OBJECT,
|
||||
|
||||
sizeof(MachineClass),
|
||||
sizeof(MachineState),
|
||||
NULL,
|
||||
|
||||
machine_initfn,
|
||||
NULL,
|
||||
machine_finalize,
|
||||
|
||||
NULL,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
true,
|
||||
};
|
||||
|
||||
void machine_register_types(struct uc_struct *uc)
|
||||
{
|
||||
type_register_static(uc, &machine_info);
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
/*
|
||||
* Dynamic device configuration and creation.
|
||||
*
|
||||
* Copyright (c) 2009 CodeSourcery
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* The theory here is that it should be possible to create a machine without
|
||||
knowledge of specific devices. Historically board init routines have
|
||||
passed a bunch of arguments to each device, requiring the board know
|
||||
exactly which device it is dealing with. This file provides an abstract
|
||||
API for device configuration and initialization. Devices will generally
|
||||
inherit from a particular bus (e.g. PCI or I2C) rather than
|
||||
this API directly. */
|
||||
|
||||
#include "hw/qdev.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
|
||||
|
||||
static void bus_add_child(BusState *bus, DeviceState *child)
|
||||
{
|
||||
char name[32];
|
||||
BusChild *kid = g_malloc0(sizeof(*kid));
|
||||
|
||||
kid->index = bus->max_index++;
|
||||
kid->child = child;
|
||||
object_ref(OBJECT(kid->child));
|
||||
|
||||
QTAILQ_INSERT_HEAD(&bus->children, kid, sibling);
|
||||
|
||||
/* This transfers ownership of kid->child to the property. */
|
||||
snprintf(name, sizeof(name), "child[%d]", kid->index);
|
||||
object_property_add_link(OBJECT(bus), name,
|
||||
object_get_typename(OBJECT(child)),
|
||||
(Object **)&kid->child,
|
||||
NULL, /* read-only property */
|
||||
0, /* return ownership on prop deletion */
|
||||
NULL);
|
||||
}
|
||||
|
||||
void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
|
||||
{
|
||||
dev->parent_bus = bus;
|
||||
object_ref(OBJECT(bus));
|
||||
bus_add_child(bus, dev);
|
||||
}
|
||||
|
||||
/* Create a new device. This only initializes the device state structure
|
||||
and allows properties to be set. qdev_init should be called to
|
||||
initialize the actual device emulation. */
|
||||
DeviceState *qdev_create(BusState *bus, const char *name)
|
||||
{
|
||||
DeviceState *dev;
|
||||
|
||||
dev = qdev_try_create(bus, name);
|
||||
if (!dev) {
|
||||
abort();
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
DeviceState *qdev_try_create(BusState *bus, const char *type)
|
||||
{
|
||||
#if 0
|
||||
DeviceState *dev;
|
||||
|
||||
if (object_class_by_name(NULL, type) == NULL) { // no need to fix. aq
|
||||
return NULL;
|
||||
}
|
||||
dev = DEVICE(object_new(NULL, type)); // no need to fix. aq
|
||||
if (!dev) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!bus) {
|
||||
bus = sysbus_get_default();
|
||||
}
|
||||
|
||||
qdev_set_parent_bus(dev, bus);
|
||||
object_unref(OBJECT(dev));
|
||||
return dev;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize a device. Device properties should be set before calling
|
||||
this function. IRQs and MMIO regions should be connected/mapped after
|
||||
calling this function.
|
||||
On failure, destroy the device and return negative value.
|
||||
Return 0 on success. */
|
||||
int qdev_init(DeviceState *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
BusState *qdev_get_parent_bus(DeviceState *dev)
|
||||
{
|
||||
return dev->parent_bus;
|
||||
}
|
||||
|
||||
static void qbus_realize(BusState *bus, DeviceState *parent, const char *name)
|
||||
{
|
||||
}
|
||||
|
||||
static void bus_unparent(struct uc_struct *uc, Object *obj)
|
||||
{
|
||||
BusState *bus = BUS(uc, obj);
|
||||
BusChild *kid;
|
||||
|
||||
while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) {
|
||||
DeviceState *dev = kid->child;
|
||||
object_unparent(uc, OBJECT(dev));
|
||||
}
|
||||
if (bus->parent) {
|
||||
QLIST_REMOVE(bus, sibling);
|
||||
bus->parent->num_child_bus--;
|
||||
bus->parent = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void qbus_create_inplace(void *bus, size_t size, const char *typename,
|
||||
DeviceState *parent, const char *name)
|
||||
{
|
||||
object_initialize(NULL, bus, size, typename); // unused, so no need to fix. aq
|
||||
qbus_realize(bus, parent, name);
|
||||
}
|
||||
|
||||
BusState *qbus_create(const char *typename, DeviceState *parent, const char *name)
|
||||
{
|
||||
BusState *bus;
|
||||
|
||||
bus = BUS(NULL, object_new(NULL, typename)); // no need to fix. aq
|
||||
qbus_realize(bus, parent, name);
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
||||
static bool device_get_realized(struct uc_struct *uc, Object *obj, Error **errp)
|
||||
{
|
||||
DeviceState *dev = DEVICE(uc, obj);
|
||||
return dev->realized;
|
||||
}
|
||||
|
||||
static int device_set_realized(struct uc_struct *uc, Object *obj, bool value, Error **errp)
|
||||
{
|
||||
DeviceState *dev = DEVICE(uc, obj);
|
||||
DeviceClass *dc = DEVICE_GET_CLASS(uc, dev);
|
||||
BusState *bus;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (dev->hotplugged && !dc->hotpluggable) {
|
||||
error_set(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (value && !dev->realized) {
|
||||
#if 0
|
||||
if (!obj->parent) {
|
||||
static int unattached_count;
|
||||
gchar *name = g_strdup_printf("device[%d]", unattached_count++);
|
||||
|
||||
object_property_add_child(container_get(qdev_get_machine(),
|
||||
"/unattached"),
|
||||
name, obj, &error_abort);
|
||||
g_free(name);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dc->realize) {
|
||||
if (dc->realize(uc, dev, &local_err))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (local_err != NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (local_err != NULL) {
|
||||
goto post_realize_fail;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
|
||||
object_property_set_bool(uc, OBJECT(bus), true, "realized",
|
||||
&local_err);
|
||||
if (local_err != NULL) {
|
||||
goto child_realize_fail;
|
||||
}
|
||||
}
|
||||
if (dev->hotplugged) {
|
||||
device_reset(dev);
|
||||
}
|
||||
dev->pending_deleted_event = false;
|
||||
} else if (!value && dev->realized) {
|
||||
Error **local_errp = NULL;
|
||||
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
|
||||
local_errp = local_err ? NULL : &local_err;
|
||||
object_property_set_bool(uc, OBJECT(bus), false, "realized",
|
||||
local_errp);
|
||||
}
|
||||
if (dc->unrealize) {
|
||||
local_errp = local_err ? NULL : &local_err;
|
||||
dc->unrealize(dev, local_errp);
|
||||
}
|
||||
dev->pending_deleted_event = true;
|
||||
}
|
||||
|
||||
if (local_err != NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dev->realized = value;
|
||||
return 0;
|
||||
|
||||
child_realize_fail:
|
||||
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
|
||||
object_property_set_bool(uc, OBJECT(bus), false, "realized",
|
||||
NULL);
|
||||
}
|
||||
|
||||
post_realize_fail:
|
||||
if (dc->unrealize) {
|
||||
dc->unrealize(dev, NULL);
|
||||
}
|
||||
|
||||
fail:
|
||||
error_propagate(errp, local_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void device_initfn(struct uc_struct *uc, Object *obj, void *opaque)
|
||||
{
|
||||
DeviceState *dev = DEVICE(uc, obj);
|
||||
|
||||
dev->instance_id_alias = -1;
|
||||
dev->realized = false;
|
||||
|
||||
object_property_add_bool(uc, obj, "realized",
|
||||
device_get_realized, device_set_realized, NULL);
|
||||
}
|
||||
|
||||
static void device_post_init(struct uc_struct *uc, Object *obj)
|
||||
{
|
||||
}
|
||||
|
||||
/* Unlink device from bus and free the structure. */
|
||||
static void device_finalize(struct uc_struct *uc, Object *obj, void *opaque)
|
||||
{
|
||||
}
|
||||
|
||||
static void device_class_base_init(ObjectClass *class, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void device_class_init(struct uc_struct *uc, ObjectClass *class, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
void device_reset(DeviceState *dev)
|
||||
{
|
||||
}
|
||||
|
||||
Object *qdev_get_machine(struct uc_struct *uc)
|
||||
{
|
||||
return container_get(uc, object_get_root(uc), "/machine");
|
||||
}
|
||||
|
||||
static const TypeInfo device_type_info = {
|
||||
TYPE_DEVICE,
|
||||
TYPE_OBJECT,
|
||||
|
||||
sizeof(DeviceClass),
|
||||
sizeof(DeviceState),
|
||||
NULL,
|
||||
|
||||
device_initfn,
|
||||
device_post_init,
|
||||
device_finalize,
|
||||
|
||||
NULL,
|
||||
|
||||
device_class_init,
|
||||
device_class_base_init,
|
||||
NULL,
|
||||
|
||||
true,
|
||||
};
|
||||
|
||||
static void qbus_initfn(struct uc_struct *uc, Object *obj, void *opaque)
|
||||
{
|
||||
}
|
||||
|
||||
static void bus_class_init(struct uc_struct *uc, ObjectClass *class, void *data)
|
||||
{
|
||||
class->unparent = bus_unparent;
|
||||
}
|
||||
|
||||
static void qbus_finalize(struct uc_struct *uc, Object *obj, void *opaque)
|
||||
{
|
||||
BusState *bus = BUS(uc, obj);
|
||||
|
||||
g_free((char *)bus->name);
|
||||
}
|
||||
|
||||
static const TypeInfo bus_info = {
|
||||
TYPE_BUS,
|
||||
TYPE_OBJECT,
|
||||
|
||||
sizeof(BusClass),
|
||||
sizeof(BusState),
|
||||
NULL,
|
||||
|
||||
qbus_initfn,
|
||||
NULL,
|
||||
qbus_finalize,
|
||||
|
||||
NULL,
|
||||
|
||||
bus_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
true,
|
||||
};
|
||||
|
||||
void qdev_register_types(struct uc_struct *uc)
|
||||
{
|
||||
type_register_static(uc, &bus_info);
|
||||
type_register_static(uc, &device_type_info);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
obj-y += pc.o pc_piix.o
|
||||
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
* QEMU PC System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
/* Modified for Unicorn Engine by Nguyen Anh Quynh, 2015 */
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qapi-visit.h"
|
||||
|
||||
|
||||
/* XXX: add IGNNE support */
|
||||
void cpu_set_ferr(CPUX86State *s)
|
||||
{
|
||||
// qemu_irq_raise(ferr_irq);
|
||||
}
|
||||
|
||||
/* TSC handling */
|
||||
uint64_t cpu_get_tsc(CPUX86State *env)
|
||||
{
|
||||
return cpu_get_ticks();
|
||||
}
|
||||
|
||||
/* SMM support */
|
||||
|
||||
static cpu_set_smm_t smm_set;
|
||||
static void *smm_arg;
|
||||
|
||||
void cpu_smm_register(cpu_set_smm_t callback, void *arg)
|
||||
{
|
||||
assert(smm_set == NULL);
|
||||
assert(smm_arg == NULL);
|
||||
smm_set = callback;
|
||||
smm_arg = arg;
|
||||
}
|
||||
|
||||
void cpu_smm_update(CPUX86State *env)
|
||||
{
|
||||
struct uc_struct *uc = x86_env_get_cpu(env)->parent_obj.uc;
|
||||
|
||||
if (smm_set && smm_arg && CPU(x86_env_get_cpu(env)) == uc->cpu) {
|
||||
smm_set(!!(env->hflags & HF_SMM_MASK), smm_arg);
|
||||
}
|
||||
}
|
||||
|
||||
/* IRQ handling */
|
||||
int cpu_get_pic_interrupt(CPUX86State *env)
|
||||
{
|
||||
X86CPU *cpu = x86_env_get_cpu(env);
|
||||
int intno;
|
||||
|
||||
intno = apic_get_interrupt(cpu->apic_state);
|
||||
if (intno >= 0) {
|
||||
return intno;
|
||||
}
|
||||
/* read the irq from the PIC */
|
||||
if (!apic_accept_pic_intr(cpu->apic_state)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DeviceState *cpu_get_current_apic(struct uc_struct *uc)
|
||||
{
|
||||
if (uc->current_cpu) {
|
||||
X86CPU *cpu = X86_CPU(uc, uc->current_cpu);
|
||||
return cpu->apic_state;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static X86CPU *pc_new_cpu(struct uc_struct *uc, const char *cpu_model, int64_t apic_id,
|
||||
Error **errp)
|
||||
{
|
||||
X86CPU *cpu;
|
||||
Error *local_err = NULL;
|
||||
|
||||
cpu = cpu_x86_create(uc, cpu_model, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
object_property_set_int(uc, OBJECT(cpu), apic_id, "apic-id", &local_err);
|
||||
object_property_set_bool(uc, OBJECT(cpu), true, "realized", &local_err);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
object_unref(uc, OBJECT(cpu));
|
||||
cpu = NULL;
|
||||
}
|
||||
return cpu;
|
||||
}
|
||||
|
||||
int pc_cpus_init(struct uc_struct *uc, const char *cpu_model)
|
||||
{
|
||||
int i;
|
||||
Error *error = NULL;
|
||||
|
||||
/* init CPUs */
|
||||
if (cpu_model == NULL) {
|
||||
#ifdef TARGET_X86_64
|
||||
cpu_model = "qemu64";
|
||||
#else
|
||||
cpu_model = "qemu32";
|
||||
#endif
|
||||
}
|
||||
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
uc->cpu = (CPUState *)pc_new_cpu(uc, cpu_model, x86_cpu_apic_id_from_index(i), &error);
|
||||
if (error) {
|
||||
//error_report("%s", error_get_pretty(error));
|
||||
error_free(error);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pc_machine_initfn(struct uc_struct *uc, Object *obj, void *opaque)
|
||||
{
|
||||
}
|
||||
|
||||
static void pc_machine_class_init(struct uc_struct *uc, ObjectClass *oc, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static const TypeInfo pc_machine_info = {
|
||||
TYPE_PC_MACHINE,
|
||||
TYPE_MACHINE,
|
||||
|
||||
sizeof(PCMachineClass),
|
||||
sizeof(PCMachineState),
|
||||
NULL,
|
||||
|
||||
pc_machine_initfn,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
NULL,
|
||||
|
||||
pc_machine_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
true,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
// should this be added somehow?
|
||||
//.interfaces = (InterfaceInfo[]) { { } },
|
||||
};
|
||||
|
||||
void pc_machine_register_types(struct uc_struct *uc)
|
||||
{
|
||||
type_register_static(uc, &pc_machine_info);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* QEMU PC System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
/* Modified for Unicorn Engine by Nguyen Anh Quynh, 2015 */
|
||||
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/boards.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "uc_priv.h"
|
||||
|
||||
|
||||
/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
|
||||
* host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte
|
||||
* pages in the host.
|
||||
*/
|
||||
#define GIGABYTE_ALIGN true
|
||||
|
||||
/* PC hardware initialisation */
|
||||
static int pc_init1(struct uc_struct *uc, MachineState *machine)
|
||||
{
|
||||
return pc_cpus_init(uc, machine->cpu_model);
|
||||
}
|
||||
|
||||
static int pc_init_pci(struct uc_struct *uc, MachineState *machine)
|
||||
{
|
||||
return pc_init1(uc, machine);
|
||||
}
|
||||
|
||||
static QEMUMachine pc_i440fx_machine_v2_2 = {
|
||||
"pc_piix",
|
||||
"pc-i440fx-2.2",
|
||||
pc_init_pci,
|
||||
NULL,
|
||||
255,
|
||||
1,
|
||||
UC_ARCH_X86, // X86
|
||||
};
|
||||
|
||||
static void pc_generic_machine_class_init(struct uc_struct *uc, ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(uc, oc);
|
||||
QEMUMachine *qm = data;
|
||||
|
||||
mc->family = qm->family;
|
||||
mc->name = qm->name;
|
||||
mc->init = qm->init;
|
||||
mc->reset = qm->reset;
|
||||
mc->max_cpus = qm->max_cpus;
|
||||
mc->is_default = qm->is_default;
|
||||
mc->arch = qm->arch;
|
||||
}
|
||||
|
||||
void pc_machine_init(struct uc_struct *uc);
|
||||
void pc_machine_init(struct uc_struct *uc)
|
||||
{
|
||||
qemu_register_machine(uc, &pc_i440fx_machine_v2_2,
|
||||
TYPE_PC_MACHINE, pc_generic_machine_class_init);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* QEMU MIPS address translation support
|
||||
* QEMU PC System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -19,21 +21,17 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
/* Modified for Unicorn Engine by Nguyen Anh Quynh, 2015 */
|
||||
/* Modified for Unicorn Engine by Chen Huitao<chenhuitao@hfmrit.com>, 2020 */
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/mips/cpudevs.h"
|
||||
#include "qemu/compiler.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "target/i386/cpu.h"
|
||||
|
||||
uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr)
|
||||
|
||||
/* TSC handling */
|
||||
uint64_t cpu_get_tsc(CPUX86State *env)
|
||||
{
|
||||
return addr & 0x1fffffffll;
|
||||
return cpu_get_ticks();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
obj-$(CONFIG_APIC) += apic.o apic_common.o
|
||||
@@ -1,230 +0,0 @@
|
||||
/*
|
||||
* APIC support
|
||||
*
|
||||
* Copyright (c) 2004-2005 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
#include "qemu/thread.h"
|
||||
#include "hw/i386/apic_internal.h"
|
||||
#include "hw/i386/apic.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "hw/i386/pc.h"
|
||||
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
#define MAX_APIC_WORDS 8
|
||||
|
||||
#define SYNC_FROM_VAPIC 0x1
|
||||
#define SYNC_TO_VAPIC 0x2
|
||||
#define SYNC_ISR_IRR_TO_VAPIC 0x4
|
||||
|
||||
static void apic_update_irq(APICCommonState *s);
|
||||
|
||||
/* Find first bit starting from msb */
|
||||
static int apic_fls_bit(uint32_t value)
|
||||
{
|
||||
return 31 - clz32(value);
|
||||
}
|
||||
|
||||
/* return -1 if no bit is set */
|
||||
static int get_highest_priority_int(uint32_t *tab)
|
||||
{
|
||||
int i;
|
||||
for (i = 7; i >= 0; i--) {
|
||||
if (tab[i] != 0) {
|
||||
return i * 32 + apic_fls_bit(tab[i]);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void apic_sync_vapic(APICCommonState *s, int sync_type)
|
||||
{
|
||||
VAPICState vapic_state;
|
||||
//size_t length;
|
||||
//off_t start;
|
||||
int vector;
|
||||
|
||||
if (!s->vapic_paddr) {
|
||||
return;
|
||||
}
|
||||
if (sync_type & SYNC_FROM_VAPIC) {
|
||||
cpu_physical_memory_read(NULL, s->vapic_paddr, &vapic_state,
|
||||
sizeof(vapic_state));
|
||||
s->tpr = vapic_state.tpr;
|
||||
}
|
||||
if (sync_type & (SYNC_TO_VAPIC | SYNC_ISR_IRR_TO_VAPIC)) {
|
||||
//start = offsetof(VAPICState, isr);
|
||||
//length = offsetof(VAPICState, enabled) - offsetof(VAPICState, isr);
|
||||
|
||||
if (sync_type & SYNC_TO_VAPIC) {
|
||||
|
||||
vapic_state.tpr = s->tpr;
|
||||
vapic_state.enabled = 1;
|
||||
//start = 0;
|
||||
//length = sizeof(VAPICState);
|
||||
}
|
||||
|
||||
vector = get_highest_priority_int(s->isr);
|
||||
if (vector < 0) {
|
||||
vector = 0;
|
||||
}
|
||||
vapic_state.isr = vector & 0xf0;
|
||||
|
||||
vapic_state.zero = 0;
|
||||
|
||||
vector = get_highest_priority_int(s->irr);
|
||||
if (vector < 0) {
|
||||
vector = 0;
|
||||
}
|
||||
vapic_state.irr = vector & 0xff;
|
||||
|
||||
//cpu_physical_memory_write_rom(&address_space_memory,
|
||||
// s->vapic_paddr + start,
|
||||
// ((void *)&vapic_state) + start, length);
|
||||
// FIXME qq
|
||||
}
|
||||
}
|
||||
|
||||
static void apic_vapic_base_update(APICCommonState *s)
|
||||
{
|
||||
apic_sync_vapic(s, SYNC_TO_VAPIC);
|
||||
}
|
||||
|
||||
#define foreach_apic(apic, deliver_bitmask, code) \
|
||||
{\
|
||||
int __i, __j;\
|
||||
for(__i = 0; __i < MAX_APIC_WORDS; __i++) {\
|
||||
uint32_t __mask = deliver_bitmask[__i];\
|
||||
if (__mask) {\
|
||||
for(__j = 0; __j < 32; __j++) {\
|
||||
if (__mask & (1U << __j)) {\
|
||||
apic = local_apics[__i * 32 + __j];\
|
||||
if (apic) {\
|
||||
code;\
|
||||
}\
|
||||
}\
|
||||
}\
|
||||
}\
|
||||
}\
|
||||
}
|
||||
|
||||
static void apic_set_base(APICCommonState *s, uint64_t val)
|
||||
{
|
||||
s->apicbase = (val & 0xfffff000) |
|
||||
(s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
|
||||
/* if disabled, cannot be enabled again */
|
||||
if (!(val & MSR_IA32_APICBASE_ENABLE)) {
|
||||
s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
|
||||
cpu_clear_apic_feature(&s->cpu->env);
|
||||
s->spurious_vec &= ~APIC_SV_ENABLE;
|
||||
}
|
||||
}
|
||||
|
||||
static void apic_set_tpr(APICCommonState *s, uint8_t val)
|
||||
{
|
||||
/* Updates from cr8 are ignored while the VAPIC is active */
|
||||
if (!s->vapic_paddr) {
|
||||
s->tpr = val << 4;
|
||||
apic_update_irq(s);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t apic_get_tpr(APICCommonState *s)
|
||||
{
|
||||
apic_sync_vapic(s, SYNC_FROM_VAPIC);
|
||||
return s->tpr >> 4;
|
||||
}
|
||||
|
||||
/* signal the CPU if an irq is pending */
|
||||
static void apic_update_irq(APICCommonState *s)
|
||||
{
|
||||
}
|
||||
|
||||
void apic_poll_irq(DeviceState *dev)
|
||||
{
|
||||
}
|
||||
|
||||
void apic_sipi(DeviceState *dev)
|
||||
{
|
||||
}
|
||||
|
||||
int apic_get_interrupt(DeviceState *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int apic_accept_pic_intr(DeviceState *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apic_pre_save(APICCommonState *s)
|
||||
{
|
||||
apic_sync_vapic(s, SYNC_FROM_VAPIC);
|
||||
}
|
||||
|
||||
static void apic_post_load(APICCommonState *s)
|
||||
{
|
||||
#if 0
|
||||
if (s->timer_expiry != -1) {
|
||||
timer_mod(s->timer, s->timer_expiry);
|
||||
} else {
|
||||
timer_del(s->timer);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int apic_realize(struct uc_struct *uc, DeviceState *dev, Error **errp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apic_class_init(struct uc_struct *uc, ObjectClass *klass, void *data)
|
||||
{
|
||||
APICCommonClass *k = APIC_COMMON_CLASS(uc, klass);
|
||||
|
||||
k->realize = apic_realize;
|
||||
k->set_base = apic_set_base;
|
||||
k->set_tpr = apic_set_tpr;
|
||||
k->get_tpr = apic_get_tpr;
|
||||
k->vapic_base_update = apic_vapic_base_update;
|
||||
k->pre_save = apic_pre_save;
|
||||
k->post_load = apic_post_load;
|
||||
//printf("... init apic class\n");
|
||||
}
|
||||
|
||||
static const TypeInfo apic_info = {
|
||||
"apic",
|
||||
TYPE_APIC_COMMON,
|
||||
|
||||
0,
|
||||
sizeof(APICCommonState),
|
||||
NULL,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
NULL,
|
||||
|
||||
apic_class_init,
|
||||
};
|
||||
|
||||
void apic_register_types(struct uc_struct *uc)
|
||||
{
|
||||
//printf("... register apic types\n");
|
||||
type_register_static(uc, &apic_info);
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
/*
|
||||
* APIC support - common bits of emulated and KVM kernel model
|
||||
*
|
||||
* Copyright (c) 2004-2005 Fabrice Bellard
|
||||
* Copyright (c) 2011 Jan Kiszka, Siemens AG
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
#include "hw/i386/apic.h"
|
||||
#include "hw/i386/apic_internal.h"
|
||||
#include "hw/qdev.h"
|
||||
|
||||
#include "uc_priv.h"
|
||||
|
||||
|
||||
void cpu_set_apic_base(struct uc_struct *uc, DeviceState *dev, uint64_t val)
|
||||
{
|
||||
if (dev) {
|
||||
APICCommonState *s = APIC_COMMON(uc, dev);
|
||||
APICCommonClass *info = APIC_COMMON_GET_CLASS(uc, s);
|
||||
info->set_base(s, val);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t cpu_get_apic_base(struct uc_struct *uc, DeviceState *dev)
|
||||
{
|
||||
if (dev) {
|
||||
APICCommonState *s = APIC_COMMON(uc, dev);
|
||||
return s->apicbase;
|
||||
} else {
|
||||
return MSR_IA32_APICBASE_BSP;
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_set_apic_tpr(struct uc_struct *uc, DeviceState *dev, uint8_t val)
|
||||
{
|
||||
APICCommonState *s;
|
||||
APICCommonClass *info;
|
||||
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
s = APIC_COMMON(uc, dev);
|
||||
info = APIC_COMMON_GET_CLASS(uc, s);
|
||||
|
||||
info->set_tpr(s, val);
|
||||
}
|
||||
|
||||
uint8_t cpu_get_apic_tpr(struct uc_struct *uc, DeviceState *dev)
|
||||
{
|
||||
APICCommonState *s;
|
||||
APICCommonClass *info;
|
||||
|
||||
if (!dev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s = APIC_COMMON(uc, dev);
|
||||
info = APIC_COMMON_GET_CLASS(uc, s);
|
||||
|
||||
return info->get_tpr(s);
|
||||
}
|
||||
|
||||
void apic_enable_vapic(struct uc_struct *uc, DeviceState *dev, hwaddr paddr)
|
||||
{
|
||||
APICCommonState *s = APIC_COMMON(uc, dev);
|
||||
APICCommonClass *info = APIC_COMMON_GET_CLASS(uc, s);
|
||||
|
||||
s->vapic_paddr = paddr;
|
||||
info->vapic_base_update(s);
|
||||
}
|
||||
|
||||
void apic_handle_tpr_access_report(DeviceState *dev, target_ulong ip,
|
||||
TPRAccess access)
|
||||
{
|
||||
//APICCommonState *s = APIC_COMMON(NULL, dev);
|
||||
|
||||
//vapic_report_tpr_access(s->vapic, CPU(s->cpu), ip, access);
|
||||
}
|
||||
|
||||
bool apic_next_timer(APICCommonState *s, int64_t current_time)
|
||||
{
|
||||
int64_t d;
|
||||
|
||||
/* We need to store the timer state separately to support APIC
|
||||
* implementations that maintain a non-QEMU timer, e.g. inside the
|
||||
* host kernel. This open-coded state allows us to migrate between
|
||||
* both models. */
|
||||
s->timer_expiry = -1;
|
||||
|
||||
if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d = (current_time - s->initial_count_load_time) >> s->count_shift;
|
||||
|
||||
if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
|
||||
if (!s->initial_count) {
|
||||
return false;
|
||||
}
|
||||
d = ((d / ((uint64_t)s->initial_count + 1)) + 1) *
|
||||
((uint64_t)s->initial_count + 1);
|
||||
} else {
|
||||
if (d >= s->initial_count) {
|
||||
return false;
|
||||
}
|
||||
d = (uint64_t)s->initial_count + 1;
|
||||
}
|
||||
s->next_time = s->initial_count_load_time + (d << s->count_shift);
|
||||
s->timer_expiry = s->next_time;
|
||||
return true;
|
||||
}
|
||||
|
||||
void apic_init_reset(struct uc_struct *uc, DeviceState *dev)
|
||||
{
|
||||
APICCommonState *s = APIC_COMMON(uc, dev);
|
||||
APICCommonClass *info = APIC_COMMON_GET_CLASS(uc, s);
|
||||
int i;
|
||||
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
s->tpr = 0;
|
||||
s->spurious_vec = 0xff;
|
||||
s->log_dest = 0;
|
||||
s->dest_mode = 0xf;
|
||||
memset(s->isr, 0, sizeof(s->isr));
|
||||
memset(s->tmr, 0, sizeof(s->tmr));
|
||||
memset(s->irr, 0, sizeof(s->irr));
|
||||
for (i = 0; i < APIC_LVT_NB; i++) {
|
||||
s->lvt[i] = APIC_LVT_MASKED;
|
||||
}
|
||||
s->esr = 0;
|
||||
memset(s->icr, 0, sizeof(s->icr));
|
||||
s->divide_conf = 0;
|
||||
s->count_shift = 0;
|
||||
s->initial_count = 0;
|
||||
s->initial_count_load_time = 0;
|
||||
s->next_time = 0;
|
||||
s->wait_for_sipi = !cpu_is_bsp(s->cpu);
|
||||
|
||||
if (s->timer) {
|
||||
// timer_del(s->timer);
|
||||
}
|
||||
s->timer_expiry = -1;
|
||||
|
||||
if (info->reset) {
|
||||
info->reset(s);
|
||||
}
|
||||
}
|
||||
|
||||
void apic_designate_bsp(struct uc_struct *uc, DeviceState *dev)
|
||||
{
|
||||
APICCommonState *s;
|
||||
|
||||
if (dev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
s = APIC_COMMON(uc, dev);
|
||||
s->apicbase |= MSR_IA32_APICBASE_BSP;
|
||||
}
|
||||
|
||||
static void apic_reset_common(struct uc_struct *uc, DeviceState *dev)
|
||||
{
|
||||
APICCommonState *s = APIC_COMMON(uc, dev);
|
||||
APICCommonClass *info = APIC_COMMON_GET_CLASS(uc, s);
|
||||
bool bsp;
|
||||
|
||||
bsp = cpu_is_bsp(s->cpu);
|
||||
s->apicbase = APIC_DEFAULT_ADDRESS |
|
||||
(bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE;
|
||||
|
||||
s->vapic_paddr = 0;
|
||||
info->vapic_base_update(s);
|
||||
|
||||
apic_init_reset(uc, dev);
|
||||
|
||||
if (bsp) {
|
||||
/*
|
||||
* LINT0 delivery mode on CPU #0 is set to ExtInt at initialization
|
||||
* time typically by BIOS, so PIC interrupt can be delivered to the
|
||||
* processor when local APIC is enabled.
|
||||
*/
|
||||
s->lvt[APIC_LVT_LINT0] = 0x700;
|
||||
}
|
||||
}
|
||||
|
||||
static int apic_common_realize(struct uc_struct *uc, DeviceState *dev, Error **errp)
|
||||
{
|
||||
APICCommonState *s = APIC_COMMON(uc, dev);
|
||||
APICCommonClass *info;
|
||||
|
||||
if (uc->apic_no >= MAX_APICS) {
|
||||
error_setg(errp, "%s initialization failed.",
|
||||
object_get_typename(OBJECT(dev)));
|
||||
return -1;
|
||||
}
|
||||
s->idx = uc->apic_no++;
|
||||
|
||||
info = APIC_COMMON_GET_CLASS(uc, s);
|
||||
info->realize(uc, dev, errp);
|
||||
if (!uc->mmio_registered) {
|
||||
ICCBus *b = ICC_BUS(uc, qdev_get_parent_bus(dev));
|
||||
memory_region_add_subregion(b->apic_address_space, 0, &s->io_memory);
|
||||
uc->mmio_registered = true;
|
||||
}
|
||||
|
||||
/* Note: We need at least 1M to map the VAPIC option ROM */
|
||||
if (!uc->vapic && s->vapic_control & VAPIC_ENABLE_MASK) {
|
||||
// ram_size >= 1024 * 1024) { // FIXME
|
||||
uc->vapic = NULL;
|
||||
}
|
||||
s->vapic = uc->vapic;
|
||||
if (uc->apic_report_tpr_access && info->enable_tpr_reporting) {
|
||||
info->enable_tpr_reporting(s, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apic_common_class_init(struct uc_struct *uc, ObjectClass *klass, void *data)
|
||||
{
|
||||
ICCDeviceClass *idc = ICC_DEVICE_CLASS(uc, klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(uc, klass);
|
||||
|
||||
dc->reset = apic_reset_common;
|
||||
idc->realize = apic_common_realize;
|
||||
/*
|
||||
* Reason: APIC and CPU need to be wired up by
|
||||
* x86_cpu_apic_create()
|
||||
*/
|
||||
dc->cannot_instantiate_with_device_add_yet = true;
|
||||
//printf("... init apic common class\n");
|
||||
}
|
||||
|
||||
static const TypeInfo apic_common_type = {
|
||||
TYPE_APIC_COMMON,
|
||||
TYPE_DEVICE,
|
||||
|
||||
sizeof(APICCommonClass),
|
||||
sizeof(APICCommonState),
|
||||
NULL,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
NULL,
|
||||
|
||||
apic_common_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
true,
|
||||
};
|
||||
|
||||
void apic_common_register_types(struct uc_struct *uc)
|
||||
{
|
||||
//printf("... register apic common\n");
|
||||
type_register_static(uc, &apic_common_type);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
obj-y += dummy_m68k.o
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Dummy board with just RAM and CPU for use as an ISS.
|
||||
*
|
||||
* Copyright (c) 2007 CodeSourcery.
|
||||
*
|
||||
* This code is licensed under the GPL
|
||||
*/
|
||||
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh, 2015 */
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/m68k/m68k.h"
|
||||
#include "hw/boards.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
|
||||
/* Board init. */
|
||||
static int dummy_m68k_init(struct uc_struct *uc, MachineState *machine)
|
||||
{
|
||||
const char *cpu_model = machine->cpu_model;
|
||||
CPUM68KState *env;
|
||||
|
||||
if (!cpu_model)
|
||||
cpu_model = "cfv4e";
|
||||
|
||||
env = cpu_init(uc, cpu_model);
|
||||
if (!env) {
|
||||
fprintf(stderr, "Unable to find m68k CPU definition\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize CPU registers. */
|
||||
env->vbr = 0;
|
||||
env->pc = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dummy_m68k_machine_init(struct uc_struct *uc)
|
||||
{
|
||||
static QEMUMachine dummy_m68k_machine = { 0 };
|
||||
dummy_m68k_machine.name = "dummy",
|
||||
dummy_m68k_machine.init = dummy_m68k_init,
|
||||
dummy_m68k_machine.is_default = 1,
|
||||
dummy_m68k_machine.arch = UC_ARCH_M68K,
|
||||
|
||||
//printf(">>> dummy_m68k_machine_init\n");
|
||||
qemu_register_machine(uc, &dummy_m68k_machine, TYPE_MACHINE, NULL);
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
obj-y += mips_r4k.o
|
||||
obj-y += addr.o cputimer.o
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* QEMU MIPS timer support
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/mips/cpudevs.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
#define TIMER_FREQ 100 * 1000 * 1000
|
||||
|
||||
/* XXX: do not use a global */
|
||||
uint32_t cpu_mips_get_random (CPUMIPSState *env)
|
||||
{
|
||||
static uint32_t lfsr = 1;
|
||||
static uint32_t prev_idx = 0;
|
||||
uint32_t idx;
|
||||
/* Don't return same value twice, so get another value */
|
||||
do {
|
||||
lfsr = (lfsr >> 1) ^ ((0-(lfsr & 1u)) & 0xd0000001u);
|
||||
idx = lfsr % (env->tlb->nb_tlb - env->CP0_Wired) + env->CP0_Wired;
|
||||
} while (idx == prev_idx);
|
||||
prev_idx = idx;
|
||||
return idx;
|
||||
}
|
||||
|
||||
/* MIPS R4K timer */
|
||||
static void cpu_mips_timer_update(CPUMIPSState *env)
|
||||
{
|
||||
#if 0
|
||||
uint64_t now, next;
|
||||
uint32_t wait;
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
wait = env->CP0_Compare - env->CP0_Count -
|
||||
(uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
|
||||
next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ);
|
||||
timer_mod(env->timer, next);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Expire the timer. */
|
||||
static void cpu_mips_timer_expire(CPUMIPSState *env)
|
||||
{
|
||||
cpu_mips_timer_update(env);
|
||||
if (env->insn_flags & ISA_MIPS32R2) {
|
||||
env->CP0_Cause |= 1 << CP0Ca_TI;
|
||||
}
|
||||
//qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t cpu_mips_get_count (CPUMIPSState *env)
|
||||
{
|
||||
if (env->CP0_Cause & (1 << CP0Ca_DC)) {
|
||||
return env->CP0_Count;
|
||||
} else {
|
||||
uint64_t now;
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
//if (timer_pending(env->timer)
|
||||
// && timer_expired(env->timer, now)) {
|
||||
// /* The timer has already expired. */
|
||||
// cpu_mips_timer_expire(env);
|
||||
//}
|
||||
|
||||
return env->CP0_Count +
|
||||
(uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_mips_store_count (CPUMIPSState *env, uint32_t count)
|
||||
{
|
||||
#if 0
|
||||
/*
|
||||
* 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 */
|
||||
env->CP0_Count =
|
||||
count - (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
|
||||
TIMER_FREQ, get_ticks_per_sec());
|
||||
/* Update timer timer */
|
||||
cpu_mips_timer_update(env);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void cpu_mips_store_compare (CPUMIPSState *env, uint32_t value)
|
||||
{
|
||||
env->CP0_Compare = value;
|
||||
if (!(env->CP0_Cause & (1 << CP0Ca_DC)))
|
||||
cpu_mips_timer_update(env);
|
||||
if (env->insn_flags & ISA_MIPS32R2)
|
||||
env->CP0_Cause &= ~(1 << CP0Ca_TI);
|
||||
//qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
|
||||
}
|
||||
|
||||
void cpu_mips_start_count(CPUMIPSState *env)
|
||||
{
|
||||
cpu_mips_store_count(env, env->CP0_Count);
|
||||
}
|
||||
|
||||
void cpu_mips_stop_count(CPUMIPSState *env)
|
||||
{
|
||||
/* Store the current value */
|
||||
env->CP0_Count += (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
|
||||
TIMER_FREQ, get_ticks_per_sec());
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* QEMU/MIPS pseudo-board
|
||||
*
|
||||
* emulates a simple machine with ISA-like bus.
|
||||
* ISA IO space mapped to the 0x14000000 (PHYS) and
|
||||
* ISA memory at the 0x10000000 (PHYS, 16Mb in size).
|
||||
* All peripherial devices are attached to this "bus" with
|
||||
* the standard PC ISA addresses.
|
||||
*/
|
||||
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh, 2015 */
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/mips/mips.h"
|
||||
#include "hw/mips/cpudevs.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/boards.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
|
||||
static int mips_r4k_init(struct uc_struct *uc, MachineState *machine)
|
||||
{
|
||||
const char *cpu_model = machine->cpu_model;
|
||||
|
||||
/* init CPUs */
|
||||
if (cpu_model == NULL) {
|
||||
#ifdef TARGET_MIPS64
|
||||
cpu_model = "R4000";
|
||||
#else
|
||||
cpu_model = "24Kf";
|
||||
#endif
|
||||
}
|
||||
|
||||
uc->cpu = (void*) cpu_mips_init(uc, cpu_model);
|
||||
if (uc->cpu == NULL) {
|
||||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mips_machine_init(struct uc_struct *uc)
|
||||
{
|
||||
static QEMUMachine mips_machine = {
|
||||
NULL,
|
||||
"mips",
|
||||
mips_r4k_init,
|
||||
NULL,
|
||||
0,
|
||||
1,
|
||||
UC_ARCH_MIPS,
|
||||
};
|
||||
|
||||
qemu_register_machine(uc, &mips_machine, TYPE_MACHINE, NULL);
|
||||
}
|
||||
1569
qemu/hw/ppc/ppc.c
Normal file
1569
qemu/hw/ppc/ppc.c
Normal file
File diff suppressed because it is too large
Load Diff
373
qemu/hw/ppc/ppc_booke.c
Normal file
373
qemu/hw/ppc/ppc_booke.c
Normal file
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* QEMU PowerPC Booke hardware System Emulator
|
||||
*
|
||||
* Copyright (c) 2011 AdaCore
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/ppc/ppc.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/log.h"
|
||||
// #include "kvm_ppc.h"
|
||||
|
||||
|
||||
/* Timer Control Register */
|
||||
|
||||
#define TCR_WP_SHIFT 30 /* Watchdog Timer Period */
|
||||
#define TCR_WP_MASK (0x3U << TCR_WP_SHIFT)
|
||||
#define TCR_WRC_SHIFT 28 /* Watchdog Timer Reset Control */
|
||||
#define TCR_WRC_MASK (0x3U << TCR_WRC_SHIFT)
|
||||
#define TCR_WIE (1U << 27) /* Watchdog Timer Interrupt Enable */
|
||||
#define TCR_DIE (1U << 26) /* Decrementer Interrupt Enable */
|
||||
#define TCR_FP_SHIFT 24 /* Fixed-Interval Timer Period */
|
||||
#define TCR_FP_MASK (0x3U << TCR_FP_SHIFT)
|
||||
#define TCR_FIE (1U << 23) /* Fixed-Interval Timer Interrupt Enable */
|
||||
#define TCR_ARE (1U << 22) /* Auto-Reload Enable */
|
||||
|
||||
/* Timer Control Register (e500 specific fields) */
|
||||
|
||||
#define TCR_E500_FPEXT_SHIFT 13 /* Fixed-Interval Timer Period Extension */
|
||||
#define TCR_E500_FPEXT_MASK (0xf << TCR_E500_FPEXT_SHIFT)
|
||||
#define TCR_E500_WPEXT_SHIFT 17 /* Watchdog Timer Period Extension */
|
||||
#define TCR_E500_WPEXT_MASK (0xf << TCR_E500_WPEXT_SHIFT)
|
||||
|
||||
/* Timer Status Register */
|
||||
|
||||
#define TSR_FIS (1U << 26) /* Fixed-Interval Timer Interrupt Status */
|
||||
#define TSR_DIS (1U << 27) /* Decrementer Interrupt Status */
|
||||
#define TSR_WRS_SHIFT 28 /* Watchdog Timer Reset Status */
|
||||
#define TSR_WRS_MASK (0x3U << TSR_WRS_SHIFT)
|
||||
#define TSR_WIS (1U << 30) /* Watchdog Timer Interrupt Status */
|
||||
#define TSR_ENW (1U << 31) /* Enable Next Watchdog Timer */
|
||||
|
||||
typedef struct booke_timer_t booke_timer_t;
|
||||
struct booke_timer_t {
|
||||
|
||||
uint64_t fit_next;
|
||||
QEMUTimer *fit_timer;
|
||||
|
||||
uint64_t wdt_next;
|
||||
QEMUTimer *wdt_timer;
|
||||
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
static void booke_update_irq(PowerPCCPU *cpu)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
ppc_set_irq(cpu, PPC_INTERRUPT_DECR,
|
||||
(env->spr[SPR_BOOKE_TSR] & TSR_DIS
|
||||
&& env->spr[SPR_BOOKE_TCR] & TCR_DIE));
|
||||
|
||||
ppc_set_irq(cpu, PPC_INTERRUPT_WDT,
|
||||
(env->spr[SPR_BOOKE_TSR] & TSR_WIS
|
||||
&& env->spr[SPR_BOOKE_TCR] & TCR_WIE));
|
||||
|
||||
ppc_set_irq(cpu, PPC_INTERRUPT_FIT,
|
||||
(env->spr[SPR_BOOKE_TSR] & TSR_FIS
|
||||
&& env->spr[SPR_BOOKE_TCR] & TCR_FIE));
|
||||
}
|
||||
|
||||
/* Return the location of the bit of time base at which the FIT will raise an
|
||||
interrupt */
|
||||
static uint8_t booke_get_fit_target(CPUPPCState *env, ppc_tb_t *tb_env)
|
||||
{
|
||||
uint8_t fp = (env->spr[SPR_BOOKE_TCR] & TCR_FP_MASK) >> TCR_FP_SHIFT;
|
||||
|
||||
if (tb_env->flags & PPC_TIMER_E500) {
|
||||
/* e500 Fixed-interval timer period extension */
|
||||
uint32_t fpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_FPEXT_MASK)
|
||||
>> TCR_E500_FPEXT_SHIFT;
|
||||
fp = 63 - (fp | fpext << 2);
|
||||
} else {
|
||||
fp = env->fit_period[fp];
|
||||
}
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
/* Return the location of the bit of time base at which the WDT will raise an
|
||||
interrupt */
|
||||
static uint8_t booke_get_wdt_target(CPUPPCState *env, ppc_tb_t *tb_env)
|
||||
{
|
||||
uint8_t wp = (env->spr[SPR_BOOKE_TCR] & TCR_WP_MASK) >> TCR_WP_SHIFT;
|
||||
|
||||
if (tb_env->flags & PPC_TIMER_E500) {
|
||||
/* e500 Watchdog timer period extension */
|
||||
uint32_t wpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_WPEXT_MASK)
|
||||
>> TCR_E500_WPEXT_SHIFT;
|
||||
wp = 63 - (wp | wpext << 2);
|
||||
} else {
|
||||
wp = env->wdt_period[wp];
|
||||
}
|
||||
|
||||
return wp;
|
||||
}
|
||||
|
||||
static void booke_update_fixed_timer(CPUPPCState *env,
|
||||
uint8_t target_bit,
|
||||
uint64_t *next,
|
||||
QEMUTimer *timer,
|
||||
int tsr_bit)
|
||||
{
|
||||
#if 0
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
uint64_t delta_tick, ticks = 0;
|
||||
uint64_t tb;
|
||||
uint64_t period;
|
||||
uint64_t now;
|
||||
|
||||
if (!(env->spr[SPR_BOOKE_TSR] & tsr_bit)) {
|
||||
/*
|
||||
* Don't arm the timer again when the guest has the current
|
||||
* interrupt still pending. Wait for it to ack it.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
tb = cpu_ppc_get_tb(tb_env, now, tb_env->tb_offset);
|
||||
period = 1ULL << target_bit;
|
||||
delta_tick = period - (tb & (period - 1));
|
||||
|
||||
/* the timer triggers only when the selected bit toggles from 0 to 1 */
|
||||
if (tb & period) {
|
||||
ticks = period;
|
||||
}
|
||||
|
||||
if (ticks + delta_tick < ticks) {
|
||||
/* Overflow, so assume the biggest number we can express. */
|
||||
ticks = UINT64_MAX;
|
||||
} else {
|
||||
ticks += delta_tick;
|
||||
}
|
||||
|
||||
*next = now + muldiv64(ticks, NANOSECONDS_PER_SECOND, tb_env->tb_freq);
|
||||
if ((*next < now) || (*next > INT64_MAX)) {
|
||||
/* Overflow, so assume the biggest number the qemu timer supports. */
|
||||
*next = INT64_MAX;
|
||||
}
|
||||
|
||||
/* XXX: If expire time is now. We can't run the callback because we don't
|
||||
* have access to it. So we just set the timer one nanosecond later.
|
||||
*/
|
||||
|
||||
if (*next == now) {
|
||||
(*next)++;
|
||||
} else {
|
||||
/*
|
||||
* There's no point to fake any granularity that's more fine grained
|
||||
* than milliseconds. Anything beyond that just overloads the system.
|
||||
*/
|
||||
*next = MAX(*next, now + SCALE_MS);
|
||||
}
|
||||
|
||||
/* Fire the next timer */
|
||||
timer_mod(timer, *next);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void booke_decr_cb(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
env->spr[SPR_BOOKE_TSR] |= TSR_DIS;
|
||||
booke_update_irq(cpu);
|
||||
|
||||
if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) {
|
||||
/* Do not reload 0, it is already there. It would just trigger
|
||||
* the timer again and lead to infinite loop */
|
||||
if (env->spr[SPR_BOOKE_DECAR] != 0) {
|
||||
/* Auto Reload */
|
||||
cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void booke_fit_cb(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
ppc_tb_t *tb_env;
|
||||
booke_timer_t *booke_timer;
|
||||
|
||||
tb_env = env->tb_env;
|
||||
booke_timer = tb_env->opaque;
|
||||
env->spr[SPR_BOOKE_TSR] |= TSR_FIS;
|
||||
|
||||
booke_update_irq(cpu);
|
||||
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_fit_target(env, tb_env),
|
||||
&booke_timer->fit_next,
|
||||
booke_timer->fit_timer,
|
||||
TSR_FIS);
|
||||
}
|
||||
|
||||
static void booke_wdt_cb(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
ppc_tb_t *tb_env;
|
||||
booke_timer_t *booke_timer;
|
||||
|
||||
tb_env = env->tb_env;
|
||||
booke_timer = tb_env->opaque;
|
||||
|
||||
/* TODO: There's lots of complicated stuff to do here */
|
||||
|
||||
booke_update_irq(cpu);
|
||||
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_wdt_target(env, tb_env),
|
||||
&booke_timer->wdt_next,
|
||||
booke_timer->wdt_timer,
|
||||
TSR_WIS);
|
||||
}
|
||||
|
||||
void store_booke_tsr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
PowerPCCPU *cpu = env_archcpu(env);
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
booke_timer_t *booke_timer = tb_env->opaque;
|
||||
|
||||
env->spr[SPR_BOOKE_TSR] &= ~val;
|
||||
// kvmppc_clear_tsr_bits(cpu, val);
|
||||
|
||||
if (val & TSR_FIS) {
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_fit_target(env, tb_env),
|
||||
&booke_timer->fit_next,
|
||||
booke_timer->fit_timer,
|
||||
TSR_FIS);
|
||||
}
|
||||
|
||||
if (val & TSR_WIS) {
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_wdt_target(env, tb_env),
|
||||
&booke_timer->wdt_next,
|
||||
booke_timer->wdt_timer,
|
||||
TSR_WIS);
|
||||
}
|
||||
|
||||
booke_update_irq(cpu);
|
||||
}
|
||||
|
||||
void store_booke_tcr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
PowerPCCPU *cpu = env_archcpu(env);
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
booke_timer_t *booke_timer = tb_env->opaque;
|
||||
|
||||
env->spr[SPR_BOOKE_TCR] = val;
|
||||
// kvmppc_set_tcr(cpu);
|
||||
|
||||
booke_update_irq(cpu);
|
||||
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_fit_target(env, tb_env),
|
||||
&booke_timer->fit_next,
|
||||
booke_timer->fit_timer,
|
||||
TSR_FIS);
|
||||
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_wdt_target(env, tb_env),
|
||||
&booke_timer->wdt_next,
|
||||
booke_timer->wdt_timer,
|
||||
TSR_WIS);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void ppc_booke_timer_reset_handle(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
store_booke_tcr(env, 0);
|
||||
store_booke_tsr(env, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function will be called whenever the CPU state changes.
|
||||
* CPU states are defined "typedef enum RunState".
|
||||
* Regarding timer, When CPU state changes to running after debug halt
|
||||
* or similar cases which takes time then in between final watchdog
|
||||
* expiry happenes. This will cause exit to QEMU and configured watchdog
|
||||
* action will be taken. To avoid this we always clear the watchdog state when
|
||||
* state changes to running.
|
||||
*/
|
||||
static void cpu_state_change_handler(void *opaque, int running, RunState state)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear watchdog interrupt condition by clearing TSR.
|
||||
*/
|
||||
store_booke_tsr(env, TSR_ENW | TSR_WIS | TSR_WRS_MASK);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ppc_booke_timers_init(PowerPCCPU *cpu, uint32_t freq, uint32_t flags)
|
||||
{
|
||||
ppc_tb_t *tb_env;
|
||||
booke_timer_t *booke_timer;
|
||||
|
||||
tb_env = g_malloc0(sizeof(ppc_tb_t));
|
||||
booke_timer = g_malloc0(sizeof(booke_timer_t));
|
||||
|
||||
cpu->env.tb_env = tb_env;
|
||||
tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED;
|
||||
|
||||
tb_env->tb_freq = freq;
|
||||
tb_env->decr_freq = freq;
|
||||
tb_env->opaque = booke_timer;
|
||||
tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &booke_decr_cb, cpu);
|
||||
|
||||
booke_timer->fit_timer =
|
||||
timer_new_ns(QEMU_CLOCK_VIRTUAL, &booke_fit_cb, cpu);
|
||||
booke_timer->wdt_timer =
|
||||
timer_new_ns(QEMU_CLOCK_VIRTUAL, &booke_wdt_cb, cpu);
|
||||
|
||||
#if 0
|
||||
int ret = 0;
|
||||
ret = kvmppc_booke_watchdog_enable(cpu);
|
||||
|
||||
if (ret) {
|
||||
/* TODO: Start the QEMU emulated watchdog if not running on KVM.
|
||||
* Also start the QEMU emulated watchdog if KVM does not support
|
||||
* emulated watchdog or somehow it is not enabled (supported but
|
||||
* not enabled is though some bug and requires debugging :)).
|
||||
*/
|
||||
}
|
||||
|
||||
qemu_add_vm_change_state_handler(cpu_state_change_handler, cpu);
|
||||
|
||||
qemu_register_reset(ppc_booke_timer_reset_handle, cpu);
|
||||
#endif
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
obj-y += leon3.o
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* QEMU Leon3 System Emulator
|
||||
*
|
||||
* Copyright (c) 2010-2011 AdaCore
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh, 2015 */
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sparc/sparc.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/boards.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
|
||||
static int leon3_generic_hw_init(struct uc_struct *uc, MachineState *machine)
|
||||
{
|
||||
const char *cpu_model = machine->cpu_model;
|
||||
SPARCCPU *cpu;
|
||||
|
||||
/* Init CPU */
|
||||
if (!cpu_model) {
|
||||
cpu_model = "LEON3";
|
||||
}
|
||||
|
||||
cpu = cpu_sparc_init(uc, cpu_model);
|
||||
uc->cpu = CPU(cpu);
|
||||
if (cpu == NULL) {
|
||||
fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cpu_sparc_set_id(&cpu->env, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void leon3_machine_init(struct uc_struct *uc)
|
||||
{
|
||||
static QEMUMachine leon3_generic_machine = {
|
||||
NULL,
|
||||
"leon3_generic",
|
||||
leon3_generic_hw_init,
|
||||
NULL,
|
||||
0,
|
||||
1,
|
||||
UC_ARCH_SPARC,
|
||||
};
|
||||
|
||||
//printf(">>> leon3_machine_init\n");
|
||||
qemu_register_machine(uc, &leon3_generic_machine, TYPE_MACHINE, NULL);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
obj-y += sun4u.o
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* QEMU Sun4u/Sun4v System Emulator
|
||||
*
|
||||
* Copyright (c) 2005 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sparc/sparc.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/boards.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
|
||||
/* Sun4u hardware initialisation */
|
||||
static int sun4u_init(struct uc_struct *uc, MachineState *machine)
|
||||
{
|
||||
const char *cpu_model = machine->cpu_model;
|
||||
SPARCCPU *cpu;
|
||||
|
||||
if (cpu_model == NULL)
|
||||
cpu_model = "Sun UltraSparc IV";
|
||||
|
||||
cpu = cpu_sparc_init(uc, cpu_model);
|
||||
if (cpu == NULL) {
|
||||
fprintf(stderr, "Unable to find Sparc CPU definition\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sun4u_machine_init(struct uc_struct *uc)
|
||||
{
|
||||
static QEMUMachine sun4u_machine = {
|
||||
NULL,
|
||||
"sun4u",
|
||||
sun4u_init,
|
||||
NULL,
|
||||
1, // XXX for now
|
||||
1,
|
||||
UC_ARCH_SPARC,
|
||||
};
|
||||
|
||||
qemu_register_machine(uc, &sun4u_machine, TYPE_MACHINE, NULL);
|
||||
}
|
||||
Reference in New Issue
Block a user