mirror of
https://github.com/qemu/qemu.git
synced 2024-11-25 20:03:37 +08:00
m68k pull request 20210315
Add m68k virt machine -----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEEzS913cjjpNwuT1Fz8ww4vT8vvjwFAmBPxo8SHGxhdXJlbnRA dml2aWVyLmV1AAoJEPMMOL0/L748LnYQAKX0IV5AzSU04fq1o6AA5MfgVjsF3v0/ FdvudfqSl6QjBfE6uqM2YjvgNRNPkSYPoTmoE476l0WcFwCH42lBDs7vt7oqSMtW lSc2aXUM6MEUfkPCZ5WLUjAT7R4+pT2C9f0kPnEs2eepr4/ho9rXsfPpKkag5GLs MvzHOK2ecHh0HDNPekG74yEbrn4k0yl3miyUimIG5Fk2NUP7dJ5J+ue+5IDHrFOu ZU/0wXb01rJjp1xEZSv/9RqINvLA+xzlK0NExEGeYCwnhQi7aRCw+peTIuCW2RCy +Y3TEyPrWpplVGuJjgpFS9ZTx9Qd4tOrGe9+QfurYCWlq6yp9QCRuX0Ee5As8471 jLW6wjiMGE3cK5uiZxx0sqplTI5SBuYIznXf/wruTHOajGuNsMFN4HfzJYtCTCOt aytd3Xibm+OxojJ5xasjva7U4l8ojEdeSxbq8r5FiFc5ezCNhLQzAANQZHaHNXY+ 9pQteKVJq05DdBz5LZ18wInINdFWbS68UANmVR9FEdqpDWmOiKD7cF8U3U7ikVz5 AcfcqfY2At3FUSmmy6Wy0BRkgBiRjSU9q5QnqIMjkzcZ6aONLA11cziR5PHZD5ZO LqTXEngrwo/c/jNLDfsJhCCC7Lwpx5DUSzl5XnqWdLpVt5fF9GX0j9UxWC8ZCBNC SZXvG3oDSSBB =1YJ9 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/vivier/tags/m68k-for-6.0-pull-request' into staging m68k pull request 20210315 Add m68k virt machine # gpg: Signature made Mon 15 Mar 2021 20:41:51 GMT # gpg: using RSA key CD2F75DDC8E3A4DC2E4F5173F30C38BD3F2FBE3C # gpg: issuer "laurent@vivier.eu" # gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>" [full] # gpg: aka "Laurent Vivier <laurent@vivier.eu>" [full] # gpg: aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>" [full] # Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F 5173 F30C 38BD 3F2F BE3C * remotes/vivier/tags/m68k-for-6.0-pull-request: m68k: add Virtual M68k Machine m68k: add a system controller m68k: add an interrupt controller hw/intc: add goldfish-pic hw/char: add goldfish-tty Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
ff81439aaf
13
MAINTAINERS
13
MAINTAINERS
@ -1130,6 +1130,19 @@ F: include/hw/nubus/*
|
||||
F: include/hw/display/macfb.h
|
||||
F: include/hw/block/swim.h
|
||||
|
||||
virt
|
||||
M: Laurent Vivier <laurent@vivier.eu>
|
||||
S: Maintained
|
||||
F: hw/m68k/virt.c
|
||||
F: hw/char/goldfish_tty.c
|
||||
F: hw/intc/goldfish_pic.c
|
||||
F: hw/intc/m68k_irqc.c
|
||||
F: hw/misc/virt_ctrl.c
|
||||
F: include/hw/char/goldfish_tty.h
|
||||
F: include/hw/intc/goldfish_pic.h
|
||||
F: include/hw/intc/m68k_irqc.h
|
||||
F: include/hw/misc/virt_ctrl.h
|
||||
|
||||
MicroBlaze Machines
|
||||
-------------------
|
||||
petalogix_s3adsp1800
|
||||
|
@ -8,3 +8,4 @@ CONFIG_AN5206=y
|
||||
CONFIG_MCF5208=y
|
||||
CONFIG_NEXTCUBE=y
|
||||
CONFIG_Q800=y
|
||||
CONFIG_M68K_VIRT=y
|
||||
|
26
docs/specs/virt-ctlr.txt
Normal file
26
docs/specs/virt-ctlr.txt
Normal file
@ -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.
|
@ -64,3 +64,6 @@ config MCHP_PFSOC_MMUART
|
||||
|
||||
config SIFIVE_UART
|
||||
bool
|
||||
|
||||
config GOLDFISH_TTY
|
||||
bool
|
||||
|
285
hw/char/goldfish_tty.c
Normal file
285
hw/char/goldfish_tty.c
Normal file
@ -0,0 +1,285 @@
|
||||
/*
|
||||
* SPDX-License-Identifer: GPL-2.0-or-later
|
||||
*
|
||||
* Goldfish TTY
|
||||
*
|
||||
* (c) 2020 Laurent Vivier <laurent@vivier.eu>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties-system.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "qemu/log.h"
|
||||
#include "trace.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/char/goldfish_tty.h"
|
||||
|
||||
#define GOLDFISH_TTY_VERSION 1
|
||||
|
||||
/* registers */
|
||||
|
||||
enum {
|
||||
REG_PUT_CHAR = 0x00,
|
||||
REG_BYTES_READY = 0x04,
|
||||
REG_CMD = 0x08,
|
||||
REG_DATA_PTR = 0x10,
|
||||
REG_DATA_LEN = 0x14,
|
||||
REG_DATA_PTR_HIGH = 0x18,
|
||||
REG_VERSION = 0x20,
|
||||
};
|
||||
|
||||
/* commands */
|
||||
|
||||
enum {
|
||||
CMD_INT_DISABLE = 0x00,
|
||||
CMD_INT_ENABLE = 0x01,
|
||||
CMD_WRITE_BUFFER = 0x02,
|
||||
CMD_READ_BUFFER = 0x03,
|
||||
};
|
||||
|
||||
static uint64_t goldfish_tty_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
GoldfishTTYState *s = opaque;
|
||||
uint64_t value = 0;
|
||||
|
||||
switch (addr) {
|
||||
case REG_BYTES_READY:
|
||||
value = fifo8_num_used(&s->rx_fifo);
|
||||
break;
|
||||
case REG_VERSION:
|
||||
value = GOLDFISH_TTY_VERSION;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: unimplemented register read 0x%02"HWADDR_PRIx"\n",
|
||||
__func__, addr);
|
||||
break;
|
||||
}
|
||||
|
||||
trace_goldfish_tty_read(s, addr, size, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void goldfish_tty_cmd(GoldfishTTYState *s, uint32_t cmd)
|
||||
{
|
||||
uint32_t to_copy;
|
||||
uint8_t *buf;
|
||||
uint8_t data_out[GOLFISH_TTY_BUFFER_SIZE];
|
||||
int len;
|
||||
uint64_t ptr;
|
||||
|
||||
switch (cmd) {
|
||||
case CMD_INT_DISABLE:
|
||||
if (s->int_enabled) {
|
||||
if (!fifo8_is_empty(&s->rx_fifo)) {
|
||||
qemu_set_irq(s->irq, 0);
|
||||
}
|
||||
s->int_enabled = false;
|
||||
}
|
||||
break;
|
||||
case CMD_INT_ENABLE:
|
||||
if (!s->int_enabled) {
|
||||
if (!fifo8_is_empty(&s->rx_fifo)) {
|
||||
qemu_set_irq(s->irq, 1);
|
||||
}
|
||||
s->int_enabled = true;
|
||||
}
|
||||
break;
|
||||
case CMD_WRITE_BUFFER:
|
||||
len = s->data_len;
|
||||
ptr = s->data_ptr;
|
||||
while (len) {
|
||||
to_copy = MIN(GOLFISH_TTY_BUFFER_SIZE, len);
|
||||
|
||||
address_space_rw(&address_space_memory, ptr,
|
||||
MEMTXATTRS_UNSPECIFIED, data_out, to_copy, 0);
|
||||
qemu_chr_fe_write_all(&s->chr, data_out, to_copy);
|
||||
|
||||
len -= to_copy;
|
||||
ptr += to_copy;
|
||||
}
|
||||
break;
|
||||
case CMD_READ_BUFFER:
|
||||
len = s->data_len;
|
||||
ptr = s->data_ptr;
|
||||
while (len && !fifo8_is_empty(&s->rx_fifo)) {
|
||||
buf = (uint8_t *)fifo8_pop_buf(&s->rx_fifo, len, &to_copy);
|
||||
address_space_rw(&address_space_memory, ptr,
|
||||
MEMTXATTRS_UNSPECIFIED, buf, to_copy, 1);
|
||||
|
||||
len -= to_copy;
|
||||
ptr += to_copy;
|
||||
}
|
||||
if (s->int_enabled && fifo8_is_empty(&s->rx_fifo)) {
|
||||
qemu_set_irq(s->irq, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void goldfish_tty_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
GoldfishTTYState *s = opaque;
|
||||
unsigned char c;
|
||||
|
||||
trace_goldfish_tty_write(s, addr, size, value);
|
||||
|
||||
switch (addr) {
|
||||
case REG_PUT_CHAR:
|
||||
c = value;
|
||||
qemu_chr_fe_write_all(&s->chr, &c, sizeof(c));
|
||||
break;
|
||||
case REG_CMD:
|
||||
goldfish_tty_cmd(s, value);
|
||||
break;
|
||||
case REG_DATA_PTR:
|
||||
s->data_ptr = value;
|
||||
break;
|
||||
case REG_DATA_PTR_HIGH:
|
||||
s->data_ptr = deposit64(s->data_ptr, 32, 32, value);
|
||||
break;
|
||||
case REG_DATA_LEN:
|
||||
s->data_len = value;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: unimplemented register write 0x%02"HWADDR_PRIx"\n",
|
||||
__func__, addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps goldfish_tty_ops = {
|
||||
.read = goldfish_tty_read,
|
||||
.write = goldfish_tty_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid.max_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
.impl.min_access_size = 4,
|
||||
};
|
||||
|
||||
static int goldfish_tty_can_receive(void *opaque)
|
||||
{
|
||||
GoldfishTTYState *s = opaque;
|
||||
int available = fifo8_num_free(&s->rx_fifo);
|
||||
|
||||
trace_goldfish_tty_can_receive(s, available);
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
static void goldfish_tty_receive(void *opaque, const uint8_t *buffer, int size)
|
||||
{
|
||||
GoldfishTTYState *s = opaque;
|
||||
|
||||
trace_goldfish_tty_receive(s, size);
|
||||
|
||||
g_assert(size <= fifo8_num_free(&s->rx_fifo));
|
||||
|
||||
fifo8_push_all(&s->rx_fifo, buffer, size);
|
||||
|
||||
if (s->int_enabled && !fifo8_is_empty(&s->rx_fifo)) {
|
||||
qemu_set_irq(s->irq, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void goldfish_tty_reset(DeviceState *dev)
|
||||
{
|
||||
GoldfishTTYState *s = GOLDFISH_TTY(dev);
|
||||
|
||||
trace_goldfish_tty_reset(s);
|
||||
|
||||
fifo8_reset(&s->rx_fifo);
|
||||
s->int_enabled = false;
|
||||
s->data_ptr = 0;
|
||||
s->data_len = 0;
|
||||
}
|
||||
|
||||
static void goldfish_tty_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
GoldfishTTYState *s = GOLDFISH_TTY(dev);
|
||||
|
||||
trace_goldfish_tty_realize(s);
|
||||
|
||||
fifo8_create(&s->rx_fifo, GOLFISH_TTY_BUFFER_SIZE);
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_tty_ops, s,
|
||||
"goldfish_tty", 0x24);
|
||||
|
||||
if (qemu_chr_fe_backend_connected(&s->chr)) {
|
||||
qemu_chr_fe_set_handlers(&s->chr, goldfish_tty_can_receive,
|
||||
goldfish_tty_receive, NULL, NULL,
|
||||
s, NULL, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void goldfish_tty_unrealize(DeviceState *dev)
|
||||
{
|
||||
GoldfishTTYState *s = GOLDFISH_TTY(dev);
|
||||
|
||||
trace_goldfish_tty_unrealize(s);
|
||||
|
||||
fifo8_destroy(&s->rx_fifo);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_goldfish_tty = {
|
||||
.name = "goldfish_tty",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(data_len, GoldfishTTYState),
|
||||
VMSTATE_UINT64(data_ptr, GoldfishTTYState),
|
||||
VMSTATE_BOOL(int_enabled, GoldfishTTYState),
|
||||
VMSTATE_FIFO8(rx_fifo, GoldfishTTYState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property goldfish_tty_properties[] = {
|
||||
DEFINE_PROP_CHR("chardev", GoldfishTTYState, chr),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void goldfish_tty_instance_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
GoldfishTTYState *s = GOLDFISH_TTY(obj);
|
||||
|
||||
trace_goldfish_tty_instance_init(s);
|
||||
|
||||
sysbus_init_mmio(dev, &s->iomem);
|
||||
sysbus_init_irq(dev, &s->irq);
|
||||
}
|
||||
|
||||
static void goldfish_tty_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
device_class_set_props(dc, goldfish_tty_properties);
|
||||
dc->reset = goldfish_tty_reset;
|
||||
dc->realize = goldfish_tty_realize;
|
||||
dc->unrealize = goldfish_tty_unrealize;
|
||||
dc->vmsd = &vmstate_goldfish_tty;
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
}
|
||||
|
||||
static const TypeInfo goldfish_tty_info = {
|
||||
.name = TYPE_GOLDFISH_TTY,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.class_init = goldfish_tty_class_init,
|
||||
.instance_init = goldfish_tty_instance_init,
|
||||
.instance_size = sizeof(GoldfishTTYState),
|
||||
};
|
||||
|
||||
static void goldfish_tty_register_types(void)
|
||||
{
|
||||
type_register_static(&goldfish_tty_info);
|
||||
}
|
||||
|
||||
type_init(goldfish_tty_register_types)
|
@ -39,3 +39,5 @@ specific_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c'))
|
||||
specific_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('terminal3270.c'))
|
||||
specific_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-serial-bus.c'))
|
||||
specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_vty.c'))
|
||||
|
||||
specific_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c'))
|
||||
|
@ -20,6 +20,16 @@ virtio_console_flush_buf(unsigned int port, size_t len, ssize_t ret) "port %u, i
|
||||
virtio_console_chr_read(unsigned int port, int size) "port %u, size %d"
|
||||
virtio_console_chr_event(unsigned int port, int event) "port %u, event %d"
|
||||
|
||||
# goldfish_tty.c
|
||||
goldfish_tty_read(void *dev, unsigned int addr, unsigned int size, uint64_t value) "tty: %p reg: 0x%02x size: %d value: 0x%"PRIx64
|
||||
goldfish_tty_write(void *dev, unsigned int addr, unsigned int size, uint64_t value) "tty: %p reg: 0x%02x size: %d value: 0x%"PRIx64
|
||||
goldfish_tty_can_receive(void *dev, unsigned int available) "tty: %p available: %u"
|
||||
goldfish_tty_receive(void *dev, unsigned int size) "tty: %p size: %u"
|
||||
goldfish_tty_reset(void *dev) "tty: %p"
|
||||
goldfish_tty_realize(void *dev) "tty: %p"
|
||||
goldfish_tty_unrealize(void *dev) "tty: %p"
|
||||
goldfish_tty_instance_init(void *dev) "tty: %p"
|
||||
|
||||
# grlib_apbuart.c
|
||||
grlib_apbuart_event(int event) "event:%d"
|
||||
grlib_apbuart_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" value 0x%x"
|
||||
|
@ -67,3 +67,9 @@ config SIFIVE_CLINT
|
||||
|
||||
config SIFIVE_PLIC
|
||||
bool
|
||||
|
||||
config GOLDFISH_PIC
|
||||
bool
|
||||
|
||||
config M68K_IRQC
|
||||
bool
|
||||
|
219
hw/intc/goldfish_pic.c
Normal file
219
hw/intc/goldfish_pic.c
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* SPDX-License-Identifer: GPL-2.0-or-later
|
||||
*
|
||||
* Goldfish PIC
|
||||
*
|
||||
* (c) 2020 Laurent Vivier <laurent@vivier.eu>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qemu/log.h"
|
||||
#include "trace.h"
|
||||
#include "hw/intc/intc.h"
|
||||
#include "hw/intc/goldfish_pic.h"
|
||||
|
||||
/* registers */
|
||||
|
||||
enum {
|
||||
REG_STATUS = 0x00,
|
||||
REG_IRQ_PENDING = 0x04,
|
||||
REG_IRQ_DISABLE_ALL = 0x08,
|
||||
REG_DISABLE = 0x0c,
|
||||
REG_ENABLE = 0x10,
|
||||
};
|
||||
|
||||
static bool goldfish_pic_get_statistics(InterruptStatsProvider *obj,
|
||||
uint64_t **irq_counts,
|
||||
unsigned int *nb_irqs)
|
||||
{
|
||||
GoldfishPICState *s = GOLDFISH_PIC(obj);
|
||||
|
||||
*irq_counts = s->stats_irq_count;
|
||||
*nb_irqs = ARRAY_SIZE(s->stats_irq_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void goldfish_pic_print_info(InterruptStatsProvider *obj, Monitor *mon)
|
||||
{
|
||||
GoldfishPICState *s = GOLDFISH_PIC(obj);
|
||||
monitor_printf(mon, "goldfish-pic.%d: pending=0x%08x enabled=0x%08x\n",
|
||||
s->idx, s->pending, s->enabled);
|
||||
}
|
||||
|
||||
static void goldfish_pic_update(GoldfishPICState *s)
|
||||
{
|
||||
if (s->pending & s->enabled) {
|
||||
qemu_irq_raise(s->irq);
|
||||
} else {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
}
|
||||
|
||||
static void goldfish_irq_request(void *opaque, int irq, int level)
|
||||
{
|
||||
GoldfishPICState *s = opaque;
|
||||
|
||||
trace_goldfish_irq_request(s, s->idx, irq, level);
|
||||
|
||||
if (level) {
|
||||
s->pending |= 1 << irq;
|
||||
s->stats_irq_count[irq]++;
|
||||
} else {
|
||||
s->pending &= ~(1 << irq);
|
||||
}
|
||||
goldfish_pic_update(s);
|
||||
}
|
||||
|
||||
static uint64_t goldfish_pic_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
GoldfishPICState *s = opaque;
|
||||
uint64_t value = 0;
|
||||
|
||||
switch (addr) {
|
||||
case REG_STATUS:
|
||||
/* The number of pending interrupts (0 to 32) */
|
||||
value = ctpop32(s->pending & s->enabled);
|
||||
break;
|
||||
case REG_IRQ_PENDING:
|
||||
/* The pending interrupt mask */
|
||||
value = s->pending & s->enabled;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: unimplemented register read 0x%02"HWADDR_PRIx"\n",
|
||||
__func__, addr);
|
||||
break;
|
||||
}
|
||||
|
||||
trace_goldfish_pic_read(s, s->idx, addr, size, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void goldfish_pic_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
GoldfishPICState *s = opaque;
|
||||
|
||||
trace_goldfish_pic_write(s, s->idx, addr, size, value);
|
||||
|
||||
switch (addr) {
|
||||
case REG_IRQ_DISABLE_ALL:
|
||||
s->enabled = 0;
|
||||
s->pending = 0;
|
||||
break;
|
||||
case REG_DISABLE:
|
||||
s->enabled &= ~value;
|
||||
break;
|
||||
case REG_ENABLE:
|
||||
s->enabled |= value;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: unimplemented register write 0x%02"HWADDR_PRIx"\n",
|
||||
__func__, addr);
|
||||
break;
|
||||
}
|
||||
goldfish_pic_update(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps goldfish_pic_ops = {
|
||||
.read = goldfish_pic_read,
|
||||
.write = goldfish_pic_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid.max_access_size = 4,
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
};
|
||||
|
||||
static void goldfish_pic_reset(DeviceState *dev)
|
||||
{
|
||||
GoldfishPICState *s = GOLDFISH_PIC(dev);
|
||||
int i;
|
||||
|
||||
trace_goldfish_pic_reset(s, s->idx);
|
||||
s->pending = 0;
|
||||
s->enabled = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s->stats_irq_count); i++) {
|
||||
s->stats_irq_count[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void goldfish_pic_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
GoldfishPICState *s = GOLDFISH_PIC(dev);
|
||||
|
||||
trace_goldfish_pic_realize(s, s->idx);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_pic_ops, s,
|
||||
"goldfish_pic", 0x24);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_goldfish_pic = {
|
||||
.name = "goldfish_pic",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(pending, GoldfishPICState),
|
||||
VMSTATE_UINT32(enabled, GoldfishPICState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void goldfish_pic_instance_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
GoldfishPICState *s = GOLDFISH_PIC(obj);
|
||||
|
||||
trace_goldfish_pic_instance_init(s);
|
||||
|
||||
sysbus_init_mmio(dev, &s->iomem);
|
||||
sysbus_init_irq(dev, &s->irq);
|
||||
|
||||
qdev_init_gpio_in(DEVICE(obj), goldfish_irq_request, GOLDFISH_PIC_IRQ_NB);
|
||||
}
|
||||
|
||||
static Property goldfish_pic_properties[] = {
|
||||
DEFINE_PROP_UINT8("index", GoldfishPICState, idx, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void goldfish_pic_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc);
|
||||
|
||||
dc->reset = goldfish_pic_reset;
|
||||
dc->realize = goldfish_pic_realize;
|
||||
dc->vmsd = &vmstate_goldfish_pic;
|
||||
ic->get_statistics = goldfish_pic_get_statistics;
|
||||
ic->print_info = goldfish_pic_print_info;
|
||||
device_class_set_props(dc, goldfish_pic_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo goldfish_pic_info = {
|
||||
.name = TYPE_GOLDFISH_PIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.class_init = goldfish_pic_class_init,
|
||||
.instance_init = goldfish_pic_instance_init,
|
||||
.instance_size = sizeof(GoldfishPICState),
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_INTERRUPT_STATS_PROVIDER },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void goldfish_pic_register_types(void)
|
||||
{
|
||||
type_register_static(&goldfish_pic_info);
|
||||
}
|
||||
|
||||
type_init(goldfish_pic_register_types)
|
119
hw/intc/m68k_irqc.c
Normal file
119
hw/intc/m68k_irqc.c
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* SPDX-License-Identifer: GPL-2.0-or-later
|
||||
*
|
||||
* QEMU Motorola 680x0 IRQ Controller
|
||||
*
|
||||
* (c) 2020 Laurent Vivier <laurent@vivier.eu>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "hw/nmi.h"
|
||||
#include "hw/intc/intc.h"
|
||||
#include "hw/intc/m68k_irqc.h"
|
||||
|
||||
|
||||
static bool m68k_irqc_get_statistics(InterruptStatsProvider *obj,
|
||||
uint64_t **irq_counts, unsigned int *nb_irqs)
|
||||
{
|
||||
M68KIRQCState *s = M68K_IRQC(obj);
|
||||
|
||||
*irq_counts = s->stats_irq_count;
|
||||
*nb_irqs = ARRAY_SIZE(s->stats_irq_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void m68k_irqc_print_info(InterruptStatsProvider *obj, Monitor *mon)
|
||||
{
|
||||
M68KIRQCState *s = M68K_IRQC(obj);
|
||||
monitor_printf(mon, "m68k-irqc: ipr=0x%x\n", s->ipr);
|
||||
}
|
||||
|
||||
static void m68k_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
M68KIRQCState *s = opaque;
|
||||
M68kCPU *cpu = M68K_CPU(first_cpu);
|
||||
int i;
|
||||
|
||||
if (level) {
|
||||
s->ipr |= 1 << irq;
|
||||
s->stats_irq_count[irq]++;
|
||||
} else {
|
||||
s->ipr &= ~(1 << irq);
|
||||
}
|
||||
|
||||
for (i = M68K_IRQC_LEVEL_7; i >= M68K_IRQC_LEVEL_1; i--) {
|
||||
if ((s->ipr >> i) & 1) {
|
||||
m68k_set_irq_level(cpu, i + 1, i + M68K_IRQC_AUTOVECTOR_BASE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
m68k_set_irq_level(cpu, 0, 0);
|
||||
}
|
||||
|
||||
static void m68k_irqc_reset(DeviceState *d)
|
||||
{
|
||||
M68KIRQCState *s = M68K_IRQC(d);
|
||||
int i;
|
||||
|
||||
s->ipr = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(s->stats_irq_count); i++) {
|
||||
s->stats_irq_count[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void m68k_irqc_instance_init(Object *obj)
|
||||
{
|
||||
qdev_init_gpio_in(DEVICE(obj), m68k_set_irq, M68K_IRQC_LEVEL_NUM);
|
||||
}
|
||||
|
||||
static void m68k_nmi(NMIState *n, int cpu_index, Error **errp)
|
||||
{
|
||||
m68k_set_irq(n, M68K_IRQC_LEVEL_7, 1);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_m68k_irqc = {
|
||||
.name = "m68k-irqc",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(ipr, M68KIRQCState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void m68k_irqc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
NMIClass *nc = NMI_CLASS(oc);
|
||||
InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc);
|
||||
|
||||
nc->nmi_monitor_handler = m68k_nmi;
|
||||
dc->reset = m68k_irqc_reset;
|
||||
dc->vmsd = &vmstate_m68k_irqc;
|
||||
ic->get_statistics = m68k_irqc_get_statistics;
|
||||
ic->print_info = m68k_irqc_print_info;
|
||||
}
|
||||
|
||||
static const TypeInfo m68k_irqc_type_info = {
|
||||
.name = TYPE_M68K_IRQC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(M68KIRQCState),
|
||||
.instance_init = m68k_irqc_instance_init,
|
||||
.class_init = m68k_irqc_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_NMI },
|
||||
{ TYPE_INTERRUPT_STATS_PROVIDER },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void q800_irq_register_types(void)
|
||||
{
|
||||
type_register_static(&m68k_irqc_type_info);
|
||||
}
|
||||
|
||||
type_init(q800_irq_register_types);
|
@ -57,3 +57,5 @@ specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xi
|
||||
specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c'))
|
||||
specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
|
||||
if_true: files('spapr_xive_kvm.c'))
|
||||
specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c'))
|
||||
specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
|
||||
|
@ -239,3 +239,11 @@ xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x
|
||||
|
||||
# pnv_xive.c
|
||||
pnv_xive_ic_hw_trigger(uint64_t addr, uint64_t val) "@0x%"PRIx64" val=0x%"PRIx64
|
||||
|
||||
# goldfish_pic.c
|
||||
goldfish_irq_request(void *dev, int idx, int irq, int level) "pic: %p goldfish-irq.%d irq: %d level: %d"
|
||||
goldfish_pic_read(void *dev, int idx, unsigned int addr, unsigned int size, uint64_t value) "pic: %p goldfish-irq.%d reg: 0x%02x size: %d value: 0x%"PRIx64
|
||||
goldfish_pic_write(void *dev, int idx, unsigned int addr, unsigned int size, uint64_t value) "pic: %p goldfish-irq.%d reg: 0x%02x size: %d value: 0x%"PRIx64
|
||||
goldfish_pic_reset(void *dev, int idx) "pic: %p goldfish-irq.%d"
|
||||
goldfish_pic_realize(void *dev, int idx) "pic: %p goldfish-irq.%d"
|
||||
goldfish_pic_instance_init(void *dev) "pic: %p goldfish-irq"
|
||||
|
@ -23,3 +23,12 @@ config Q800
|
||||
select ESP
|
||||
select DP8393X
|
||||
select OR_IRQ
|
||||
|
||||
config M68K_VIRT
|
||||
bool
|
||||
select M68K_IRQC
|
||||
select VIRT_CTRL
|
||||
select GOLDFISH_PIC
|
||||
select GOLDFISH_TTY
|
||||
select GOLDFISH_RTC
|
||||
select VIRTIO_MMIO
|
||||
|
@ -3,5 +3,6 @@ m68k_ss.add(when: 'CONFIG_AN5206', if_true: files('an5206.c', 'mcf5206.c'))
|
||||
m68k_ss.add(when: 'CONFIG_MCF5208', if_true: files('mcf5208.c', 'mcf_intc.c'))
|
||||
m68k_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-kbd.c', 'next-cube.c'))
|
||||
m68k_ss.add(when: 'CONFIG_Q800', if_true: files('q800.c'))
|
||||
m68k_ss.add(when: 'CONFIG_M68K_VIRT', if_true: files('virt.c'))
|
||||
|
||||
hw_arch += {'m68k': m68k_ss}
|
||||
|
313
hw/m68k/virt.c
Normal file
313
hw/m68k/virt.c
Normal file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* SPDX-License-Identifer: GPL-2.0-or-later
|
||||
*
|
||||
* QEMU Vitual M68K Machine
|
||||
*
|
||||
* (c) 2020 Laurent Vivier <laurent@vivier.eu>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "elf.h"
|
||||
#include "hw/loader.h"
|
||||
#include "ui/console.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "standard-headers/asm-m68k/bootinfo.h"
|
||||
#include "standard-headers/asm-m68k/bootinfo-virt.h"
|
||||
#include "bootinfo.h"
|
||||
#include "net/net.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/reset.h"
|
||||
|
||||
#include "hw/intc/m68k_irqc.h"
|
||||
#include "hw/misc/virt_ctrl.h"
|
||||
#include "hw/char/goldfish_tty.h"
|
||||
#include "hw/rtc/goldfish_rtc.h"
|
||||
#include "hw/intc/goldfish_pic.h"
|
||||
#include "hw/virtio/virtio-mmio.h"
|
||||
#include "hw/virtio/virtio-blk.h"
|
||||
|
||||
/*
|
||||
* 6 goldfish-pic for CPU IRQ #1 to IRQ #6
|
||||
* CPU IRQ #1 -> PIC #1
|
||||
* IRQ #1 to IRQ #31 -> unused
|
||||
* IRQ #32 -> goldfish-tty
|
||||
* CPU IRQ #2 -> PIC #2
|
||||
* IRQ #1 to IRQ #32 -> virtio-mmio from 1 to 32
|
||||
* CPU IRQ #3 -> PIC #3
|
||||
* IRQ #1 to IRQ #32 -> virtio-mmio from 33 to 64
|
||||
* CPU IRQ #4 -> PIC #4
|
||||
* IRQ #1 to IRQ #32 -> virtio-mmio from 65 to 96
|
||||
* CPU IRQ #5 -> PIC #5
|
||||
* IRQ #1 to IRQ #32 -> virtio-mmio from 97 to 128
|
||||
* CPU IRQ #6 -> PIC #6
|
||||
* IRQ #1 -> goldfish-rtc
|
||||
* IRQ #2 to IRQ #32 -> unused
|
||||
* CPU IRQ #7 -> NMI
|
||||
*/
|
||||
|
||||
#define PIC_IRQ_BASE(num) (8 + (num - 1) * 32)
|
||||
#define PIC_IRQ(num, irq) (PIC_IRQ_BASE(num) + irq - 1)
|
||||
#define PIC_GPIO(pic_irq) (qdev_get_gpio_in(pic_dev[(pic_irq - 8) / 32], \
|
||||
(pic_irq - 8) % 32))
|
||||
|
||||
#define VIRT_GF_PIC_MMIO_BASE 0xff000000 /* MMIO: 0xff000000 - 0xff005fff */
|
||||
#define VIRT_GF_PIC_IRQ_BASE 1 /* IRQ: #1 -> #6 */
|
||||
#define VIRT_GF_PIC_NB 6
|
||||
|
||||
/* 2 goldfish-rtc (and timer) */
|
||||
#define VIRT_GF_RTC_MMIO_BASE 0xff006000 /* MMIO: 0xff006000 - 0xff007fff */
|
||||
#define VIRT_GF_RTC_IRQ_BASE PIC_IRQ(6, 1) /* PIC: #6, IRQ: #1 */
|
||||
#define VIRT_GF_RTC_NB 2
|
||||
|
||||
/* 1 goldfish-tty */
|
||||
#define VIRT_GF_TTY_MMIO_BASE 0xff008000 /* MMIO: 0xff008000 - 0xff008fff */
|
||||
#define VIRT_GF_TTY_IRQ_BASE PIC_IRQ(1, 32) /* PIC: #1, IRQ: #32 */
|
||||
|
||||
/* 1 virt-ctrl */
|
||||
#define VIRT_CTRL_MMIO_BASE 0xff009000 /* MMIO: 0xff009000 - 0xff009fff */
|
||||
#define VIRT_CTRL_IRQ_BASE PIC_IRQ(1, 1) /* PIC: #1, IRQ: #1 */
|
||||
|
||||
/*
|
||||
* virtio-mmio size is 0x200 bytes
|
||||
* we use 4 goldfish-pic to attach them,
|
||||
* we can attach 32 virtio devices / goldfish-pic
|
||||
* -> we can manage 32 * 4 = 128 virtio devices
|
||||
*/
|
||||
#define VIRT_VIRTIO_MMIO_BASE 0xff010000 /* MMIO: 0xff010000 - 0xff01ffff */
|
||||
#define VIRT_VIRTIO_IRQ_BASE PIC_IRQ(2, 1) /* PIC: 2, 3, 4, 5, IRQ: ALL */
|
||||
|
||||
static void main_cpu_reset(void *opaque)
|
||||
{
|
||||
M68kCPU *cpu = opaque;
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
cpu_reset(cs);
|
||||
cpu->env.aregs[7] = ldl_phys(cs->as, 0);
|
||||
cpu->env.pc = ldl_phys(cs->as, 4);
|
||||
}
|
||||
|
||||
static void virt_init(MachineState *machine)
|
||||
{
|
||||
M68kCPU *cpu = NULL;
|
||||
int32_t kernel_size;
|
||||
uint64_t elf_entry;
|
||||
ram_addr_t initrd_base;
|
||||
int32_t initrd_size;
|
||||
ram_addr_t ram_size = machine->ram_size;
|
||||
const char *kernel_filename = machine->kernel_filename;
|
||||
const char *initrd_filename = machine->initrd_filename;
|
||||
const char *kernel_cmdline = machine->kernel_cmdline;
|
||||
hwaddr parameters_base;
|
||||
DeviceState *dev;
|
||||
DeviceState *irqc_dev;
|
||||
DeviceState *pic_dev[VIRT_GF_PIC_NB];
|
||||
SysBusDevice *sysbus;
|
||||
hwaddr io_base;
|
||||
int i;
|
||||
|
||||
if (ram_size > 3399672 * KiB) {
|
||||
/*
|
||||
* The physical memory can be up to 4 GiB - 16 MiB, but linux
|
||||
* kernel crashes after this limit (~ 3.2 GiB)
|
||||
*/
|
||||
error_report("Too much memory for this machine: %" PRId64 " KiB, "
|
||||
"maximum 3399672 KiB", ram_size / KiB);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* init CPUs */
|
||||
cpu = M68K_CPU(cpu_create(machine->cpu_type));
|
||||
qemu_register_reset(main_cpu_reset, cpu);
|
||||
|
||||
/* RAM */
|
||||
memory_region_add_subregion(get_system_memory(), 0, machine->ram);
|
||||
|
||||
/* IRQ Controller */
|
||||
|
||||
irqc_dev = qdev_new(TYPE_M68K_IRQC);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(irqc_dev), &error_fatal);
|
||||
|
||||
/*
|
||||
* 6 goldfish-pic
|
||||
*
|
||||
* map: 0xff000000 - 0xff006fff = 28 KiB
|
||||
* IRQ: #1 (lower priority) -> #6 (higher priority)
|
||||
*
|
||||
*/
|
||||
io_base = VIRT_GF_PIC_MMIO_BASE;
|
||||
for (i = 0; i < VIRT_GF_PIC_NB; i++) {
|
||||
pic_dev[i] = qdev_new(TYPE_GOLDFISH_PIC);
|
||||
sysbus = SYS_BUS_DEVICE(pic_dev[i]);
|
||||
qdev_prop_set_uint8(pic_dev[i], "index", i);
|
||||
sysbus_realize_and_unref(sysbus, &error_fatal);
|
||||
|
||||
sysbus_mmio_map(sysbus, 0, io_base);
|
||||
sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(irqc_dev, i));
|
||||
|
||||
io_base += 0x1000;
|
||||
}
|
||||
|
||||
/* goldfish-rtc */
|
||||
io_base = VIRT_GF_RTC_MMIO_BASE;
|
||||
for (i = 0; i < VIRT_GF_RTC_NB; i++) {
|
||||
dev = qdev_new(TYPE_GOLDFISH_RTC);
|
||||
sysbus = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize_and_unref(sysbus, &error_fatal);
|
||||
sysbus_mmio_map(sysbus, 0, io_base);
|
||||
sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_RTC_IRQ_BASE + i));
|
||||
|
||||
io_base += 0x1000;
|
||||
}
|
||||
|
||||
/* goldfish-tty */
|
||||
dev = qdev_new(TYPE_GOLDFISH_TTY);
|
||||
sysbus = SYS_BUS_DEVICE(dev);
|
||||
qdev_prop_set_chr(dev, "chardev", serial_hd(0));
|
||||
sysbus_realize_and_unref(sysbus, &error_fatal);
|
||||
sysbus_mmio_map(sysbus, 0, VIRT_GF_TTY_MMIO_BASE);
|
||||
sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_TTY_IRQ_BASE));
|
||||
|
||||
/* virt controller */
|
||||
dev = qdev_new(TYPE_VIRT_CTRL);
|
||||
sysbus = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize_and_unref(sysbus, &error_fatal);
|
||||
sysbus_mmio_map(sysbus, 0, VIRT_CTRL_MMIO_BASE);
|
||||
sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_CTRL_IRQ_BASE));
|
||||
|
||||
/* virtio-mmio */
|
||||
io_base = VIRT_VIRTIO_MMIO_BASE;
|
||||
for (i = 0; i < 128; i++) {
|
||||
dev = qdev_new(TYPE_VIRTIO_MMIO);
|
||||
qdev_prop_set_bit(dev, "force-legacy", false);
|
||||
sysbus = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize_and_unref(sysbus, &error_fatal);
|
||||
sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_VIRTIO_IRQ_BASE + i));
|
||||
sysbus_mmio_map(sysbus, 0, io_base);
|
||||
io_base += 0x200;
|
||||
}
|
||||
|
||||
if (kernel_filename) {
|
||||
CPUState *cs = CPU(cpu);
|
||||
uint64_t high;
|
||||
|
||||
kernel_size = load_elf(kernel_filename, NULL, NULL, NULL,
|
||||
&elf_entry, NULL, &high, NULL, 1,
|
||||
EM_68K, 0, 0);
|
||||
if (kernel_size < 0) {
|
||||
error_report("could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
stl_phys(cs->as, 4, elf_entry); /* reset initial PC */
|
||||
parameters_base = (high + 1) & ~1;
|
||||
|
||||
BOOTINFO1(cs->as, parameters_base, BI_MACHTYPE, MACH_VIRT);
|
||||
BOOTINFO1(cs->as, parameters_base, BI_FPUTYPE, FPU_68040);
|
||||
BOOTINFO1(cs->as, parameters_base, BI_MMUTYPE, MMU_68040);
|
||||
BOOTINFO1(cs->as, parameters_base, BI_CPUTYPE, CPU_68040);
|
||||
BOOTINFO2(cs->as, parameters_base, BI_MEMCHUNK, 0, ram_size);
|
||||
|
||||
BOOTINFO1(cs->as, parameters_base, BI_VIRT_QEMU_VERSION,
|
||||
((QEMU_VERSION_MAJOR << 24) | (QEMU_VERSION_MINOR << 16) |
|
||||
(QEMU_VERSION_MICRO << 8)));
|
||||
BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_PIC_BASE,
|
||||
VIRT_GF_PIC_MMIO_BASE, VIRT_GF_PIC_IRQ_BASE);
|
||||
BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_RTC_BASE,
|
||||
VIRT_GF_RTC_MMIO_BASE, VIRT_GF_RTC_IRQ_BASE);
|
||||
BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_TTY_BASE,
|
||||
VIRT_GF_TTY_MMIO_BASE, VIRT_GF_TTY_IRQ_BASE);
|
||||
BOOTINFO2(cs->as, parameters_base, BI_VIRT_CTRL_BASE,
|
||||
VIRT_CTRL_MMIO_BASE, VIRT_CTRL_IRQ_BASE);
|
||||
BOOTINFO2(cs->as, parameters_base, BI_VIRT_VIRTIO_BASE,
|
||||
VIRT_VIRTIO_MMIO_BASE, VIRT_VIRTIO_IRQ_BASE);
|
||||
|
||||
if (kernel_cmdline) {
|
||||
BOOTINFOSTR(cs->as, parameters_base, BI_COMMAND_LINE,
|
||||
kernel_cmdline);
|
||||
}
|
||||
|
||||
/* load initrd */
|
||||
if (initrd_filename) {
|
||||
initrd_size = get_image_size(initrd_filename);
|
||||
if (initrd_size < 0) {
|
||||
error_report("could not load initial ram disk '%s'",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK;
|
||||
load_image_targphys(initrd_filename, initrd_base,
|
||||
ram_size - initrd_base);
|
||||
BOOTINFO2(cs->as, parameters_base, BI_RAMDISK, initrd_base,
|
||||
initrd_size);
|
||||
} else {
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
BOOTINFO0(cs->as, parameters_base, BI_LAST);
|
||||
}
|
||||
}
|
||||
|
||||
static void virt_machine_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
mc->desc = "QEMU M68K Virtual Machine";
|
||||
mc->init = virt_init;
|
||||
mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040");
|
||||
mc->max_cpus = 1;
|
||||
mc->no_floppy = 1;
|
||||
mc->no_parallel = 1;
|
||||
mc->default_ram_id = "m68k_virt.ram";
|
||||
}
|
||||
|
||||
static const TypeInfo virt_machine_info = {
|
||||
.name = MACHINE_TYPE_NAME("virt"),
|
||||
.parent = TYPE_MACHINE,
|
||||
.abstract = true,
|
||||
.class_init = virt_machine_class_init,
|
||||
};
|
||||
|
||||
static void virt_machine_register_types(void)
|
||||
{
|
||||
type_register_static(&virt_machine_info);
|
||||
}
|
||||
|
||||
type_init(virt_machine_register_types)
|
||||
|
||||
#define DEFINE_VIRT_MACHINE(major, minor, latest) \
|
||||
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
|
||||
void *data) \
|
||||
{ \
|
||||
MachineClass *mc = MACHINE_CLASS(oc); \
|
||||
virt_machine_##major##_##minor##_options(mc); \
|
||||
mc->desc = "QEMU " # major "." # minor " M68K Virtual Machine"; \
|
||||
if (latest) { \
|
||||
mc->alias = "virt"; \
|
||||
} \
|
||||
} \
|
||||
static const TypeInfo machvirt_##major##_##minor##_info = { \
|
||||
.name = MACHINE_TYPE_NAME("virt-" # major "." # minor), \
|
||||
.parent = MACHINE_TYPE_NAME("virt"), \
|
||||
.class_init = virt_##major##_##minor##_class_init, \
|
||||
}; \
|
||||
static void machvirt_machine_##major##_##minor##_init(void) \
|
||||
{ \
|
||||
type_register_static(&machvirt_##major##_##minor##_info); \
|
||||
} \
|
||||
type_init(machvirt_machine_##major##_##minor##_init);
|
||||
|
||||
static void virt_machine_6_0_options(MachineClass *mc)
|
||||
{
|
||||
}
|
||||
DEFINE_VIRT_MACHINE(6, 0, true)
|
@ -183,4 +183,7 @@ config SIFIVE_U_OTP
|
||||
config SIFIVE_U_PRCI
|
||||
bool
|
||||
|
||||
config VIRT_CTRL
|
||||
bool
|
||||
|
||||
source macio/Kconfig
|
||||
|
@ -24,6 +24,9 @@ softmmu_ss.add(when: 'CONFIG_ARM11SCU', if_true: files('arm11scu.c'))
|
||||
# Mac devices
|
||||
softmmu_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c'))
|
||||
|
||||
# virt devices
|
||||
softmmu_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c'))
|
||||
|
||||
# RISC-V devices
|
||||
softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_DMC', if_true: files('mchp_pfsoc_dmc.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.c'))
|
||||
|
@ -255,3 +255,10 @@ pca955x_gpio_change(const char *description, unsigned id, unsigned prev_state, u
|
||||
bcm2835_cprman_read(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
|
||||
bcm2835_cprman_write(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
|
||||
bcm2835_cprman_write_invalid_magic(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
|
||||
|
||||
# virt_ctrl.c
|
||||
virt_ctrl_read(void *dev, unsigned int addr, unsigned int size, uint64_t value) "ctrl: %p reg: 0x%02x size: %d value: 0x%"PRIx64
|
||||
virt_ctrl_write(void *dev, unsigned int addr, unsigned int size, uint64_t value) "ctrl: %p reg: 0x%02x size: %d value: 0x%"PRIx64
|
||||
virt_ctrl_reset(void *dev) "ctrl: %p"
|
||||
virt_ctrl_realize(void *dev) "ctrl: %p"
|
||||
virt_ctrl_instance_init(void *dev) "ctrl: %p"
|
||||
|
151
hw/misc/virt_ctrl.c
Normal file
151
hw/misc/virt_ctrl.c
Normal file
@ -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)
|
35
include/hw/char/goldfish_tty.h
Normal file
35
include/hw/char/goldfish_tty.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* SPDX-License-Identifer: GPL-2.0-or-later
|
||||
*
|
||||
* Goldfish TTY
|
||||
*
|
||||
* (c) 2020 Laurent Vivier <laurent@vivier.eu>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HW_CHAR_GOLDFISH_TTY_H
|
||||
#define HW_CHAR_GOLDFISH_TTY_H
|
||||
|
||||
#include "qemu/fifo8.h"
|
||||
#include "chardev/char-fe.h"
|
||||
|
||||
#define TYPE_GOLDFISH_TTY "goldfish_tty"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(GoldfishTTYState, GOLDFISH_TTY)
|
||||
|
||||
#define GOLFISH_TTY_BUFFER_SIZE 128
|
||||
|
||||
struct GoldfishTTYState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
CharBackend chr;
|
||||
|
||||
uint32_t data_len;
|
||||
uint64_t data_ptr;
|
||||
bool int_enabled;
|
||||
|
||||
Fifo8 rx_fifo;
|
||||
};
|
||||
|
||||
#endif
|
33
include/hw/intc/goldfish_pic.h
Normal file
33
include/hw/intc/goldfish_pic.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* SPDX-License-Identifer: GPL-2.0-or-later
|
||||
*
|
||||
* Goldfish PIC
|
||||
*
|
||||
* (c) 2020 Laurent Vivier <laurent@vivier.eu>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HW_INTC_GOLDFISH_PIC_H
|
||||
#define HW_INTC_GOLDFISH_PIC_H
|
||||
|
||||
#define TYPE_GOLDFISH_PIC "goldfish_pic"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(GoldfishPICState, GOLDFISH_PIC)
|
||||
|
||||
#define GOLDFISH_PIC_IRQ_NB 32
|
||||
|
||||
struct GoldfishPICState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
|
||||
uint32_t pending;
|
||||
uint32_t enabled;
|
||||
|
||||
/* statistics */
|
||||
uint64_t stats_irq_count[32];
|
||||
/* for tracing */
|
||||
uint8_t idx;
|
||||
};
|
||||
|
||||
#endif
|
41
include/hw/intc/m68k_irqc.h
Normal file
41
include/hw/intc/m68k_irqc.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* SPDX-License-Identifer: GPL-2.0-or-later
|
||||
*
|
||||
* QEMU Motorola 680x0 IRQ Controller
|
||||
*
|
||||
* (c) 2020 Laurent Vivier <laurent@vivier.eu>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef M68K_IRQC_H
|
||||
#define M68K_IRQC_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_M68K_IRQC "m68k-irq-controller"
|
||||
#define M68K_IRQC(obj) OBJECT_CHECK(M68KIRQCState, (obj), \
|
||||
TYPE_M68K_IRQC)
|
||||
|
||||
#define M68K_IRQC_AUTOVECTOR_BASE 25
|
||||
|
||||
enum {
|
||||
M68K_IRQC_LEVEL_1 = 0,
|
||||
M68K_IRQC_LEVEL_2,
|
||||
M68K_IRQC_LEVEL_3,
|
||||
M68K_IRQC_LEVEL_4,
|
||||
M68K_IRQC_LEVEL_5,
|
||||
M68K_IRQC_LEVEL_6,
|
||||
M68K_IRQC_LEVEL_7,
|
||||
};
|
||||
#define M68K_IRQC_LEVEL_NUM (M68K_IRQC_LEVEL_7 - M68K_IRQC_LEVEL_1 + 1)
|
||||
|
||||
typedef struct M68KIRQCState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
uint8_t ipr;
|
||||
|
||||
/* statistics */
|
||||
uint64_t stats_irq_count[M68K_IRQC_LEVEL_NUM];
|
||||
} M68KIRQCState;
|
||||
|
||||
#endif
|
22
include/hw/misc/virt_ctrl.h
Normal file
22
include/hw/misc/virt_ctrl.h
Normal file
@ -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
|
18
include/standard-headers/asm-m68k/bootinfo-virt.h
Normal file
18
include/standard-headers/asm-m68k/bootinfo-virt.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
** asm/bootinfo-virt.h -- Virtual-m68k-specific boot information definitions
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_ASM_M68K_BOOTINFO_VIRT_H
|
||||
#define _UAPI_ASM_M68K_BOOTINFO_VIRT_H
|
||||
|
||||
#define BI_VIRT_QEMU_VERSION 0x8000
|
||||
#define BI_VIRT_GF_PIC_BASE 0x8001
|
||||
#define BI_VIRT_GF_RTC_BASE 0x8002
|
||||
#define BI_VIRT_GF_TTY_BASE 0x8003
|
||||
#define BI_VIRT_VIRTIO_BASE 0x8004
|
||||
#define BI_VIRT_CTRL_BASE 0x8005
|
||||
|
||||
#define VIRT_BOOTI_VERSION MK_BI_VERSION(2, 0)
|
||||
|
||||
#endif /* _UAPI_ASM_M68K_BOOTINFO_MAC_H */
|
Loading…
Reference in New Issue
Block a user