Browse Source
Add a system controller for the m68k-virt machine. This controller allows the kernel to power off or reset the machine. Signed-off-by: Laurent Vivier <laurent@vivier.eu> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Message-Id: <20210312214145.2936082-5-laurent@vivier.eu>pull/113/head
6 changed files with 212 additions and 0 deletions
@ -0,0 +1,26 @@ |
|||
Virtual System Controller |
|||
========================= |
|||
|
|||
This device is a simple interface defined for the pure virtual machine with no |
|||
hardware reference implementation to allow the guest kernel to send command |
|||
to the host hypervisor. |
|||
|
|||
The specification can evolve, the current state is defined as below. |
|||
|
|||
This is a MMIO mapped device using 256 bytes. |
|||
|
|||
Two 32bit registers are defined: |
|||
|
|||
1- the features register (read-only, address 0x00) |
|||
|
|||
This register allows the device to report features supported by the |
|||
controller. |
|||
The only feature supported for the moment is power control (0x01). |
|||
|
|||
2- the command register (write-only, address 0x04) |
|||
|
|||
This register allows the kernel to send the commands to the hypervisor. |
|||
The implemented commands are part of the power control feature and |
|||
are reset (1), halt (2) and panic (3). |
|||
A basic command, no-op (0), is always present and can be used to test the |
|||
register access. This command has no effect. |
|||
@ -0,0 +1,151 @@ |
|||
/*
|
|||
* SPDX-License-Identifer: GPL-2.0-or-later |
|||
* |
|||
* Virt system Controller |
|||
*/ |
|||
|
|||
#include "qemu/osdep.h" |
|||
#include "hw/irq.h" |
|||
#include "hw/qdev-properties.h" |
|||
#include "hw/sysbus.h" |
|||
#include "migration/vmstate.h" |
|||
#include "qemu/log.h" |
|||
#include "trace.h" |
|||
#include "sysemu/runstate.h" |
|||
#include "hw/misc/virt_ctrl.h" |
|||
|
|||
enum { |
|||
REG_FEATURES = 0x00, |
|||
REG_CMD = 0x04, |
|||
}; |
|||
|
|||
#define FEAT_POWER_CTRL 0x00000001 |
|||
|
|||
enum { |
|||
CMD_NOOP, |
|||
CMD_RESET, |
|||
CMD_HALT, |
|||
CMD_PANIC, |
|||
}; |
|||
|
|||
static uint64_t virt_ctrl_read(void *opaque, hwaddr addr, unsigned size) |
|||
{ |
|||
VirtCtrlState *s = opaque; |
|||
uint64_t value = 0; |
|||
|
|||
switch (addr) { |
|||
case REG_FEATURES: |
|||
value = FEAT_POWER_CTRL; |
|||
break; |
|||
default: |
|||
qemu_log_mask(LOG_UNIMP, |
|||
"%s: unimplemented register read 0x%02"HWADDR_PRIx"\n", |
|||
__func__, addr); |
|||
break; |
|||
} |
|||
|
|||
trace_virt_ctrl_write(s, addr, size, value); |
|||
|
|||
return value; |
|||
} |
|||
|
|||
static void virt_ctrl_write(void *opaque, hwaddr addr, uint64_t value, |
|||
unsigned size) |
|||
{ |
|||
VirtCtrlState *s = opaque; |
|||
|
|||
trace_virt_ctrl_write(s, addr, size, value); |
|||
|
|||
switch (addr) { |
|||
case REG_CMD: |
|||
switch (value) { |
|||
case CMD_NOOP: |
|||
break; |
|||
case CMD_RESET: |
|||
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
|||
break; |
|||
case CMD_HALT: |
|||
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); |
|||
break; |
|||
case CMD_PANIC: |
|||
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_PANIC); |
|||
break; |
|||
} |
|||
break; |
|||
default: |
|||
qemu_log_mask(LOG_UNIMP, |
|||
"%s: unimplemented register write 0x%02"HWADDR_PRIx"\n", |
|||
__func__, addr); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
static const MemoryRegionOps virt_ctrl_ops = { |
|||
.read = virt_ctrl_read, |
|||
.write = virt_ctrl_write, |
|||
.endianness = DEVICE_NATIVE_ENDIAN, |
|||
.valid.max_access_size = 4, |
|||
.impl.max_access_size = 4, |
|||
}; |
|||
|
|||
static void virt_ctrl_reset(DeviceState *dev) |
|||
{ |
|||
VirtCtrlState *s = VIRT_CTRL(dev); |
|||
|
|||
trace_virt_ctrl_reset(s); |
|||
} |
|||
|
|||
static void virt_ctrl_realize(DeviceState *dev, Error **errp) |
|||
{ |
|||
VirtCtrlState *s = VIRT_CTRL(dev); |
|||
|
|||
trace_virt_ctrl_instance_init(s); |
|||
|
|||
memory_region_init_io(&s->iomem, OBJECT(s), &virt_ctrl_ops, s, |
|||
"virt-ctrl", 0x100); |
|||
} |
|||
|
|||
static const VMStateDescription vmstate_virt_ctrl = { |
|||
.name = "virt-ctrl", |
|||
.version_id = 1, |
|||
.minimum_version_id = 1, |
|||
.fields = (VMStateField[]) { |
|||
VMSTATE_UINT32(irq_enabled, VirtCtrlState), |
|||
VMSTATE_END_OF_LIST() |
|||
} |
|||
}; |
|||
|
|||
static void virt_ctrl_instance_init(Object *obj) |
|||
{ |
|||
SysBusDevice *dev = SYS_BUS_DEVICE(obj); |
|||
VirtCtrlState *s = VIRT_CTRL(obj); |
|||
|
|||
trace_virt_ctrl_instance_init(s); |
|||
|
|||
sysbus_init_mmio(dev, &s->iomem); |
|||
sysbus_init_irq(dev, &s->irq); |
|||
} |
|||
|
|||
static void virt_ctrl_class_init(ObjectClass *oc, void *data) |
|||
{ |
|||
DeviceClass *dc = DEVICE_CLASS(oc); |
|||
|
|||
dc->reset = virt_ctrl_reset; |
|||
dc->realize = virt_ctrl_realize; |
|||
dc->vmsd = &vmstate_virt_ctrl; |
|||
} |
|||
|
|||
static const TypeInfo virt_ctrl_info = { |
|||
.name = TYPE_VIRT_CTRL, |
|||
.parent = TYPE_SYS_BUS_DEVICE, |
|||
.class_init = virt_ctrl_class_init, |
|||
.instance_init = virt_ctrl_instance_init, |
|||
.instance_size = sizeof(VirtCtrlState), |
|||
}; |
|||
|
|||
static void virt_ctrl_register_types(void) |
|||
{ |
|||
type_register_static(&virt_ctrl_info); |
|||
} |
|||
|
|||
type_init(virt_ctrl_register_types) |
|||
@ -0,0 +1,22 @@ |
|||
/*
|
|||
* SPDX-License-Identifer: GPL-2.0-or-later |
|||
* |
|||
* Virt system Controller |
|||
*/ |
|||
|
|||
#ifndef VIRT_CTRL_H |
|||
#define VIRT_CTRL_H |
|||
|
|||
#define TYPE_VIRT_CTRL "virt-ctrl" |
|||
OBJECT_DECLARE_SIMPLE_TYPE(VirtCtrlState, VIRT_CTRL) |
|||
|
|||
struct VirtCtrlState { |
|||
SysBusDevice parent_obj; |
|||
|
|||
MemoryRegion iomem; |
|||
qemu_irq irq; |
|||
|
|||
uint32_t irq_enabled; |
|||
}; |
|||
|
|||
#endif |
|||
Loading…
Reference in new issue