Implement the bus structure for PAPR virtual IO
This extends the "pseries" (PAPR) machine to include a virtual IO bus supporting the PAPR defined hypercall based virtual IO mechanisms. So far only one VIO device is provided, the vty / vterm, providing a full console (polled only, for now). Signed-off-by: David Gibson <dwg@au1.ibm.com> Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
parent
9fdf0c2995
commit
4040ab7237
@ -231,9 +231,10 @@ obj-ppc-y += ppc_prep.o
|
||||
obj-ppc-y += ppc_oldworld.o
|
||||
# NewWorld PowerMac
|
||||
obj-ppc-y += ppc_newworld.o
|
||||
# IBM pSeries (sPAPR)i
|
||||
# IBM pSeries (sPAPR)
|
||||
ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
|
||||
obj-ppc-y += spapr.o spapr_hcall.o
|
||||
obj-ppc-y += spapr.o spapr_hcall.o spapr_vio.o
|
||||
obj-ppc-y += spapr_vty.o
|
||||
endif
|
||||
# PowerPC 4xx boards
|
||||
obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
|
||||
|
48
hw/spapr.c
48
hw/spapr.c
@ -25,7 +25,6 @@
|
||||
*
|
||||
*/
|
||||
#include "sysemu.h"
|
||||
#include "qemu-char.h"
|
||||
#include "hw.h"
|
||||
#include "elf.h"
|
||||
|
||||
@ -34,6 +33,7 @@
|
||||
#include "hw/loader.h"
|
||||
|
||||
#include "hw/spapr.h"
|
||||
#include "hw/spapr_vio.h"
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
@ -60,6 +60,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
|
||||
uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
|
||||
int i;
|
||||
char *modelname;
|
||||
int ret;
|
||||
|
||||
#define _FDT(exp) \
|
||||
do { \
|
||||
@ -159,9 +160,30 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
|
||||
|
||||
_FDT((fdt_end_node(fdt)));
|
||||
|
||||
/* vdevice */
|
||||
_FDT((fdt_begin_node(fdt, "vdevice")));
|
||||
|
||||
_FDT((fdt_property_string(fdt, "device_type", "vdevice")));
|
||||
_FDT((fdt_property_string(fdt, "compatible", "IBM,vdevice")));
|
||||
_FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
|
||||
_FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
|
||||
|
||||
_FDT((fdt_end_node(fdt)));
|
||||
|
||||
_FDT((fdt_end_node(fdt))); /* close root node */
|
||||
_FDT((fdt_finish(fdt)));
|
||||
|
||||
/* re-expand to allow for further tweaks */
|
||||
_FDT((fdt_open_into(fdt, fdt, FDT_MAX_SIZE)));
|
||||
|
||||
ret = spapr_populate_vdevice(spapr->vio_bus, fdt);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "couldn't setup vio devices in fdt\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
_FDT((fdt_pack(fdt)));
|
||||
|
||||
*fdt_size = fdt_totalsize(fdt);
|
||||
|
||||
return fdt;
|
||||
@ -177,21 +199,6 @@ static void emulate_spapr_hypercall(CPUState *env)
|
||||
env->gpr[3] = spapr_hypercall(env, env->gpr[3], &env->gpr[4]);
|
||||
}
|
||||
|
||||
/* FIXME: hack until we implement the proper VIO console */
|
||||
static target_ulong h_put_term_char(CPUState *env, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
uint8_t buf[16];
|
||||
|
||||
stq_p(buf, args[2]);
|
||||
stq_p(buf + 8, args[3]);
|
||||
|
||||
qemu_chr_write(serial_hds[0], buf, args[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* pSeries LPAR / sPAPR hardware init */
|
||||
static void ppc_spapr_init(ram_addr_t ram_size,
|
||||
const char *boot_device,
|
||||
@ -243,7 +250,13 @@ static void ppc_spapr_init(ram_addr_t ram_size,
|
||||
ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size);
|
||||
cpu_register_physical_memory(0, ram_size, ram_offset);
|
||||
|
||||
spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char);
|
||||
spapr->vio_bus = spapr_vio_bus_init();
|
||||
|
||||
for (i = 0; i < MAX_SERIAL_PORTS; i++) {
|
||||
if (serial_hds[i]) {
|
||||
spapr_vty_create(spapr->vio_bus, i, serial_hds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (kernel_filename) {
|
||||
uint64_t lowaddr = 0;
|
||||
@ -276,7 +289,6 @@ static void ppc_spapr_init(ram_addr_t ram_size,
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "pSeries machine needs -kernel for now");
|
||||
exit(1);
|
||||
|
@ -1,7 +1,10 @@
|
||||
#if !defined(__HW_SPAPR_H__)
|
||||
#define __HW_SPAPR_H__
|
||||
|
||||
struct VIOsPAPRBus;
|
||||
|
||||
typedef struct sPAPREnvironment {
|
||||
struct VIOsPAPRBus *vio_bus;
|
||||
} sPAPREnvironment;
|
||||
|
||||
#define H_SUCCESS 0
|
||||
|
214
hw/spapr_vio.c
Normal file
214
hw/spapr_vio.c
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* QEMU sPAPR VIO code
|
||||
*
|
||||
* Copyright (c) 2010 David Gibson, IBM Corporation <dwg@au1.ibm.com>
|
||||
* Based on the s390 virtio bus code:
|
||||
* Copyright (c) 2009 Alexander Graf <agraf@suse.de>
|
||||
*
|
||||
* 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.h"
|
||||
#include "sysemu.h"
|
||||
#include "boards.h"
|
||||
#include "monitor.h"
|
||||
#include "loader.h"
|
||||
#include "elf.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "kvm.h"
|
||||
#include "device_tree.h"
|
||||
|
||||
#include "hw/spapr.h"
|
||||
#include "hw/spapr_vio.h"
|
||||
|
||||
#ifdef CONFIG_FDT
|
||||
#include <libfdt.h>
|
||||
#endif /* CONFIG_FDT */
|
||||
|
||||
/* #define DEBUG_SPAPR */
|
||||
|
||||
#ifdef DEBUG_SPAPR
|
||||
#define dprintf(fmt, ...) \
|
||||
do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define dprintf(fmt, ...) \
|
||||
do { } while (0)
|
||||
#endif
|
||||
|
||||
static struct BusInfo spapr_vio_bus_info = {
|
||||
.name = "spapr-vio",
|
||||
.size = sizeof(VIOsPAPRBus),
|
||||
};
|
||||
|
||||
VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg)
|
||||
{
|
||||
DeviceState *qdev;
|
||||
VIOsPAPRDevice *dev = NULL;
|
||||
|
||||
QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
|
||||
dev = (VIOsPAPRDevice *)qdev;
|
||||
if (dev->reg == reg) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FDT
|
||||
static int vio_make_devnode(VIOsPAPRDevice *dev,
|
||||
void *fdt)
|
||||
{
|
||||
VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)dev->qdev.info;
|
||||
int vdevice_off, node_off;
|
||||
int ret;
|
||||
|
||||
vdevice_off = fdt_path_offset(fdt, "/vdevice");
|
||||
if (vdevice_off < 0) {
|
||||
return vdevice_off;
|
||||
}
|
||||
|
||||
node_off = fdt_add_subnode(fdt, vdevice_off, dev->qdev.id);
|
||||
if (node_off < 0) {
|
||||
return node_off;
|
||||
}
|
||||
|
||||
ret = fdt_setprop_cell(fdt, node_off, "reg", dev->reg);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (info->dt_type) {
|
||||
ret = fdt_setprop_string(fdt, node_off, "device_type",
|
||||
info->dt_type);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->dt_compatible) {
|
||||
ret = fdt_setprop_string(fdt, node_off, "compatible",
|
||||
info->dt_compatible);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->devnode) {
|
||||
ret = (info->devnode)(dev, fdt, node_off);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return node_off;
|
||||
}
|
||||
#endif /* CONFIG_FDT */
|
||||
|
||||
static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo)
|
||||
{
|
||||
VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
|
||||
VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
|
||||
char *id;
|
||||
|
||||
if (asprintf(&id, "%s@%x", info->dt_name, dev->reg) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->qdev.id = id;
|
||||
|
||||
return info->init(dev);
|
||||
}
|
||||
|
||||
void spapr_vio_bus_register_withprop(VIOsPAPRDeviceInfo *info)
|
||||
{
|
||||
info->qdev.init = spapr_vio_busdev_init;
|
||||
info->qdev.bus_info = &spapr_vio_bus_info;
|
||||
|
||||
assert(info->qdev.size >= sizeof(VIOsPAPRDevice));
|
||||
qdev_register(&info->qdev);
|
||||
}
|
||||
|
||||
VIOsPAPRBus *spapr_vio_bus_init(void)
|
||||
{
|
||||
VIOsPAPRBus *bus;
|
||||
BusState *qbus;
|
||||
DeviceState *dev;
|
||||
DeviceInfo *qinfo;
|
||||
|
||||
/* Create bridge device */
|
||||
dev = qdev_create(NULL, "spapr-vio-bridge");
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
/* Create bus on bridge device */
|
||||
|
||||
qbus = qbus_create(&spapr_vio_bus_info, dev, "spapr-vio");
|
||||
bus = DO_UPCAST(VIOsPAPRBus, bus, qbus);
|
||||
|
||||
for (qinfo = device_info_list; qinfo; qinfo = qinfo->next) {
|
||||
VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
|
||||
|
||||
if (qinfo->bus_info != &spapr_vio_bus_info) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (info->hcalls) {
|
||||
info->hcalls(bus);
|
||||
}
|
||||
}
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
||||
/* Represents sPAPR hcall VIO devices */
|
||||
|
||||
static int spapr_vio_bridge_init(SysBusDevice *dev)
|
||||
{
|
||||
/* nothing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SysBusDeviceInfo spapr_vio_bridge_info = {
|
||||
.init = spapr_vio_bridge_init,
|
||||
.qdev.name = "spapr-vio-bridge",
|
||||
.qdev.size = sizeof(SysBusDevice),
|
||||
.qdev.no_user = 1,
|
||||
};
|
||||
|
||||
static void spapr_vio_register_devices(void)
|
||||
{
|
||||
sysbus_register_withprop(&spapr_vio_bridge_info);
|
||||
}
|
||||
|
||||
device_init(spapr_vio_register_devices)
|
||||
|
||||
#ifdef CONFIG_FDT
|
||||
int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt)
|
||||
{
|
||||
DeviceState *qdev;
|
||||
int ret = 0;
|
||||
|
||||
QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
|
||||
VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
|
||||
|
||||
ret = vio_make_devnode(dev, fdt);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_FDT */
|
50
hw/spapr_vio.h
Normal file
50
hw/spapr_vio.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef _HW_SPAPR_VIO_H
|
||||
#define _HW_SPAPR_VIO_H
|
||||
/*
|
||||
* QEMU sPAPR VIO bus definitions
|
||||
*
|
||||
* Copyright (c) 2010 David Gibson, IBM Corporation <david@gibson.dropbear.id.au>
|
||||
* Based on the s390 virtio bus definitions:
|
||||
* Copyright (c) 2009 Alexander Graf <agraf@suse.de>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
typedef struct VIOsPAPRDevice {
|
||||
DeviceState qdev;
|
||||
uint32_t reg;
|
||||
} VIOsPAPRDevice;
|
||||
|
||||
typedef struct VIOsPAPRBus {
|
||||
BusState bus;
|
||||
} VIOsPAPRBus;
|
||||
|
||||
typedef struct {
|
||||
DeviceInfo qdev;
|
||||
const char *dt_name, *dt_type, *dt_compatible;
|
||||
int (*init)(VIOsPAPRDevice *dev);
|
||||
void (*hcalls)(VIOsPAPRBus *bus);
|
||||
int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off);
|
||||
} VIOsPAPRDeviceInfo;
|
||||
|
||||
extern VIOsPAPRBus *spapr_vio_bus_init(void);
|
||||
extern VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg);
|
||||
extern void spapr_vio_bus_register_withprop(VIOsPAPRDeviceInfo *info);
|
||||
extern int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt);
|
||||
|
||||
void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len);
|
||||
void spapr_vty_create(VIOsPAPRBus *bus,
|
||||
uint32_t reg, CharDriverState *chardev);
|
||||
|
||||
#endif /* _HW_SPAPR_VIO_H */
|
150
hw/spapr_vty.c
Normal file
150
hw/spapr_vty.c
Normal file
@ -0,0 +1,150 @@
|
||||
#include "qdev.h"
|
||||
#include "qemu-char.h"
|
||||
#include "hw/spapr.h"
|
||||
#include "hw/spapr_vio.h"
|
||||
|
||||
#define VTERM_BUFSIZE 16
|
||||
|
||||
typedef struct VIOsPAPRVTYDevice {
|
||||
VIOsPAPRDevice sdev;
|
||||
CharDriverState *chardev;
|
||||
uint32_t in, out;
|
||||
uint8_t buf[VTERM_BUFSIZE];
|
||||
} VIOsPAPRVTYDevice;
|
||||
|
||||
static int vty_can_receive(void *opaque)
|
||||
{
|
||||
VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque;
|
||||
|
||||
return (dev->in - dev->out) < VTERM_BUFSIZE;
|
||||
}
|
||||
|
||||
static void vty_receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
assert((dev->in - dev->out) < VTERM_BUFSIZE);
|
||||
dev->buf[dev->in++ % VTERM_BUFSIZE] = buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, int max)
|
||||
{
|
||||
VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev;
|
||||
int n = 0;
|
||||
|
||||
while ((n < max) && (dev->out != dev->in)) {
|
||||
buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE];
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len)
|
||||
{
|
||||
VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev;
|
||||
|
||||
/* FIXME: should check the qemu_chr_write() return value */
|
||||
qemu_chr_write(dev->chardev, buf, len);
|
||||
}
|
||||
|
||||
static int spapr_vty_init(VIOsPAPRDevice *sdev)
|
||||
{
|
||||
VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev;
|
||||
|
||||
qemu_chr_add_handlers(dev->chardev, vty_can_receive,
|
||||
vty_receive, NULL, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static target_ulong h_put_term_char(CPUState *env, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
target_ulong reg = args[0];
|
||||
target_ulong len = args[1];
|
||||
target_ulong char0_7 = args[2];
|
||||
target_ulong char8_15 = args[3];
|
||||
VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
|
||||
uint8_t buf[16];
|
||||
|
||||
if (!sdev) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
if (len > 16) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
*((uint64_t *)buf) = cpu_to_be64(char0_7);
|
||||
*((uint64_t *)buf + 1) = cpu_to_be64(char8_15);
|
||||
|
||||
vty_putchars(sdev, buf, len);
|
||||
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_get_term_char(CPUState *env, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
target_ulong reg = args[0];
|
||||
target_ulong *len = args + 0;
|
||||
target_ulong *char0_7 = args + 1;
|
||||
target_ulong *char8_15 = args + 2;
|
||||
VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
|
||||
uint8_t buf[16];
|
||||
|
||||
if (!sdev) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
*len = vty_getchars(sdev, buf, sizeof(buf));
|
||||
if (*len < 16) {
|
||||
memset(buf + *len, 0, 16 - *len);
|
||||
}
|
||||
|
||||
*char0_7 = be64_to_cpu(*((uint64_t *)buf));
|
||||
*char8_15 = be64_to_cpu(*((uint64_t *)buf + 1));
|
||||
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
void spapr_vty_create(VIOsPAPRBus *bus,
|
||||
uint32_t reg, CharDriverState *chardev)
|
||||
{
|
||||
DeviceState *dev;
|
||||
|
||||
dev = qdev_create(&bus->bus, "spapr-vty");
|
||||
qdev_prop_set_uint32(dev, "reg", reg);
|
||||
qdev_prop_set_chr(dev, "chardev", chardev);
|
||||
qdev_init_nofail(dev);
|
||||
}
|
||||
|
||||
static void vty_hcalls(VIOsPAPRBus *bus)
|
||||
{
|
||||
spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char);
|
||||
spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char);
|
||||
}
|
||||
|
||||
static VIOsPAPRDeviceInfo spapr_vty = {
|
||||
.init = spapr_vty_init,
|
||||
.dt_name = "vty",
|
||||
.dt_type = "serial",
|
||||
.dt_compatible = "hvterm1",
|
||||
.hcalls = vty_hcalls,
|
||||
.qdev.name = "spapr-vty",
|
||||
.qdev.size = sizeof(VIOsPAPRVTYDevice),
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0),
|
||||
DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
},
|
||||
};
|
||||
|
||||
static void spapr_vty_register(void)
|
||||
{
|
||||
spapr_vio_bus_register_withprop(&spapr_vty);
|
||||
}
|
||||
device_init(spapr_vty_register);
|
Loading…
Reference in New Issue
Block a user