Skip to content

Commit

Permalink
Implement hcall based RTAS for pSeries machines
Browse files Browse the repository at this point in the history
On pSeries machines, operating systems can instantiate "RTAS" (Run-Time
Abstraction Services), a runtime component of the firmware which implements
a number of low-level, infrequently used operations.  On logical partitions
under a hypervisor, many of the RTAS functions require hypervisor
privilege.  For simplicity, therefore, hypervisor systems typically
implement the in-partition RTAS as just a tiny wrapper around a hypercall
which actually implements the various RTAS functions.

This patch implements such a hypercall based RTAS for our emulated pSeries
machine.  A tiny in-partition "firmware" calls a new hypercall, which
looks up available RTAS services in a table.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
Signed-off-by: Alexander Graf <agraf@suse.de>
  • Loading branch information
dgibson authored and agraf committed Apr 1, 2011
1 parent f43e352 commit 39ac845
Show file tree
Hide file tree
Showing 10 changed files with 289 additions and 14 deletions.
3 changes: 2 additions & 1 deletion Makefile
Expand Up @@ -213,7 +213,8 @@ pxe-ne2k_pci.bin pxe-pcnet.bin \
pxe-rtl8139.bin pxe-virtio.bin \
bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
multiboot.bin linuxboot.bin \
s390-zipl.rom
s390-zipl.rom \
spapr-rtas.bin
else
BLOBS=
endif
Expand Down
2 changes: 1 addition & 1 deletion Makefile.target
Expand Up @@ -233,7 +233,7 @@ obj-ppc-y += ppc_oldworld.o
obj-ppc-y += ppc_newworld.o
# IBM pSeries (sPAPR)
ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
obj-ppc-y += spapr.o spapr_hcall.o spapr_vio.o
obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
obj-ppc-y += spapr_vty.o
endif
# PowerPC 4xx boards
Expand Down
4 changes: 3 additions & 1 deletion configure
Expand Up @@ -2461,7 +2461,9 @@ if test \( "$cpu" = "i386" -o "$cpu" = "x86_64" \) -a \
"$softmmu" = yes ; then
roms="optionrom"
fi

if test "$cpu" = "ppc64" ; then
roms="$roms spapr-rtas"
fi

echo "Install prefix $prefix"
echo "BIOS directory `eval echo $datadir`"
Expand Down
26 changes: 23 additions & 3 deletions hw/spapr.c
Expand Up @@ -40,6 +40,7 @@
#define KERNEL_LOAD_ADDR 0x00000000
#define INITRD_LOAD_ADDR 0x02800000
#define FDT_MAX_SIZE 0x10000
#define RTAS_MAX_SIZE 0x10000

#define TIMEBASE_FREQ 512000000ULL

Expand All @@ -53,6 +54,8 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
target_phys_addr_t initrd_base,
target_phys_addr_t initrd_size,
const char *kernel_cmdline,
target_phys_addr_t rtas_addr,
target_phys_addr_t rtas_size,
long hash_shift)
{
void *fdt;
Expand Down Expand Up @@ -195,6 +198,12 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
exit(1);
}

/* RTAS */
ret = spapr_rtas_device_tree_setup(fdt, rtas_addr, rtas_size);
if (ret < 0) {
fprintf(stderr, "Couldn't set up RTAS device tree properties\n");
}

_FDT((fdt_pack(fdt)));

*fdt_size = fdt_totalsize(fdt);
Expand Down Expand Up @@ -224,11 +233,12 @@ static void ppc_spapr_init(ram_addr_t ram_size,
void *fdt, *htab;
int i;
ram_addr_t ram_offset;
target_phys_addr_t fdt_addr;
target_phys_addr_t fdt_addr, rtas_addr;
uint32_t kernel_base, initrd_base;
long kernel_size, initrd_size, htab_size;
long kernel_size, initrd_size, htab_size, rtas_size;
long pteg_shift = 17;
int fdt_size;
char *filename;

spapr = qemu_malloc(sizeof(*spapr));
cpu_ppc_hypercall = emulate_spapr_hypercall;
Expand All @@ -237,6 +247,8 @@ static void ppc_spapr_init(ram_addr_t ram_size,
* 2GB, so that it can be processed with 32-bit code if
* necessary */
fdt_addr = MIN(ram_size, 0x80000000) - FDT_MAX_SIZE;
/* RTAS goes just below that */
rtas_addr = fdt_addr - RTAS_MAX_SIZE;

/* init CPUs */
if (cpu_model == NULL) {
Expand Down Expand Up @@ -276,6 +288,14 @@ static void ppc_spapr_init(ram_addr_t ram_size,
envs[i]->htab_mask = htab_size - 1;
}

filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
rtas_size = load_image_targphys(filename, rtas_addr, ram_size - rtas_addr);
if (rtas_size < 0) {
hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
exit(1);
}
qemu_free(filename);

spapr->vio_bus = spapr_vio_bus_init();

for (i = 0; i < MAX_SERIAL_PORTS; i++) {
Expand Down Expand Up @@ -323,7 +343,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
/* Prepare the device tree */
fdt = spapr_create_fdt(&fdt_size, ram_size, cpu_model, envs, spapr,
initrd_base, initrd_size, kernel_cmdline,
pteg_shift + 7);
rtas_addr, rtas_size, pteg_shift + 7);
assert(fdt != NULL);

cpu_physical_memory_write(fdt_addr, fdt, fdt_size);
Expand Down
32 changes: 32 additions & 0 deletions hw/spapr.h
Expand Up @@ -237,6 +237,18 @@ typedef struct sPAPREnvironment {
#define H_GET_MPP 0x2D4
#define MAX_HCALL_OPCODE H_GET_MPP

/* The hcalls above are standardized in PAPR and implemented by pHyp
* as well.
*
* We also need some hcalls which are specific to qemu / KVM-on-POWER.
* So far we just need one for H_RTAS, but in future we'll need more
* for extensions like virtio. We put those into the 0xf000-0xfffc
* range which is reserved by PAPR for "platform-specific" hcalls.
*/
#define KVMPPC_HCALL_BASE 0xf000
#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0)
#define KVMPPC_HCALL_MAX KVMPPC_H_RTAS

extern sPAPREnvironment *spapr;

/*#define DEBUG_SPAPR_HCALLS*/
Expand All @@ -257,4 +269,24 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn);
target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
target_ulong *args);

static inline uint32_t rtas_ld(target_ulong phys, int n)
{
return ldl_phys(phys + 4*n);
}

static inline void rtas_st(target_ulong phys, int n, uint32_t val)
{
stl_phys(phys + 4*n, val);
}

typedef void (*spapr_rtas_fn)(sPAPREnvironment *spapr, uint32_t token,
uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets);
void spapr_rtas_register(const char *name, spapr_rtas_fn fn);
target_ulong spapr_rtas_call(sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets);
int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr,
target_phys_addr_t rtas_size);

#endif /* !defined (__HW_SPAPR_H__) */
44 changes: 36 additions & 8 deletions hw/spapr_hcall.c
Expand Up @@ -248,20 +248,38 @@ static target_ulong h_protect(CPUState *env, sPAPREnvironment *spapr,
return H_SUCCESS;
}

spapr_hcall_fn hypercall_table[(MAX_HCALL_OPCODE / 4) + 1];
static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong rtas_r3 = args[0];
uint32_t token = ldl_phys(rtas_r3);
uint32_t nargs = ldl_phys(rtas_r3 + 4);
uint32_t nret = ldl_phys(rtas_r3 + 8);

return spapr_rtas_call(spapr, token, nargs, rtas_r3 + 12,
nret, rtas_r3 + 12 + 4*nargs);
}

spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1];
spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE];

void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn)
{
spapr_hcall_fn old_fn;
spapr_hcall_fn *slot;

if (opcode <= MAX_HCALL_OPCODE) {
assert((opcode & 0x3) == 0);

assert(opcode <= MAX_HCALL_OPCODE);
assert((opcode & 0x3) == 0);
slot = &papr_hypercall_table[opcode / 4];
} else {
assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX));

old_fn = hypercall_table[opcode / 4];

assert(!old_fn || (fn == old_fn));
slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE];
}

hypercall_table[opcode / 4] = fn;
assert(!(*slot) || (fn == *slot));
*slot = fn;
}

target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
Expand All @@ -274,7 +292,14 @@ target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,

if ((opcode <= MAX_HCALL_OPCODE)
&& ((opcode & 0x3) == 0)) {
spapr_hcall_fn fn = hypercall_table[opcode / 4];
spapr_hcall_fn fn = papr_hypercall_table[opcode / 4];

if (fn) {
return fn(env, spapr, opcode, args);
}
} else if ((opcode >= KVMPPC_HCALL_BASE) &&
(opcode <= KVMPPC_HCALL_MAX)) {
spapr_hcall_fn fn = kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE];

if (fn) {
return fn(env, spapr, opcode, args);
Expand All @@ -291,5 +316,8 @@ static void hypercall_init(void)
spapr_register_hypercall(H_ENTER, h_enter);
spapr_register_hypercall(H_REMOVE, h_remove);
spapr_register_hypercall(H_PROTECT, h_protect);

/* qemu/KVM-PPC specific hcalls */
spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas);
}
device_init(hypercall_init);
131 changes: 131 additions & 0 deletions hw/spapr_rtas.c
@@ -0,0 +1,131 @@
/*
* QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
*
* Hypercall based emulated RTAS
*
* Copyright (c) 2010-2011 David Gibson, IBM Corporation.
*
* 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 "cpu.h"
#include "sysemu.h"
#include "qemu-char.h"
#include "hw/qdev.h"
#include "device_tree.h"

#include "hw/spapr.h"
#include "hw/spapr_vio.h"

#include <libfdt.h>

#define TOKEN_BASE 0x2000
#define TOKEN_MAX 0x100

static struct rtas_call {
const char *name;
spapr_rtas_fn fn;
} rtas_table[TOKEN_MAX];

struct rtas_call *rtas_next = rtas_table;

target_ulong spapr_rtas_call(sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
{
if ((token >= TOKEN_BASE)
&& ((token - TOKEN_BASE) < TOKEN_MAX)) {
struct rtas_call *call = rtas_table + (token - TOKEN_BASE);

if (call->fn) {
call->fn(spapr, token, nargs, args, nret, rets);
return H_SUCCESS;
}
}

hcall_dprintf("Unknown RTAS token 0x%x\n", token);
rtas_st(rets, 0, -3);
return H_PARAMETER;
}

void spapr_rtas_register(const char *name, spapr_rtas_fn fn)
{
assert(rtas_next < (rtas_table + TOKEN_MAX));

rtas_next->name = name;
rtas_next->fn = fn;

rtas_next++;
}

int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr,
target_phys_addr_t rtas_size)
{
int ret;
int i;

ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size);
if (ret < 0) {
fprintf(stderr, "Couldn't add RTAS reserve entry: %s\n",
fdt_strerror(ret));
return ret;
}

ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-base",
rtas_addr);
if (ret < 0) {
fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n",
fdt_strerror(ret));
return ret;
}

ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-entry",
rtas_addr);
if (ret < 0) {
fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n",
fdt_strerror(ret));
return ret;
}

ret = qemu_devtree_setprop_cell(fdt, "/rtas", "rtas-size",
rtas_size);
if (ret < 0) {
fprintf(stderr, "Couldn't add rtas-size property: %s\n",
fdt_strerror(ret));
return ret;
}

for (i = 0; i < TOKEN_MAX; i++) {
struct rtas_call *call = &rtas_table[i];

if (!call->fn) {
continue;
}

ret = qemu_devtree_setprop_cell(fdt, "/rtas", call->name,
i + TOKEN_BASE);
if (ret < 0) {
fprintf(stderr, "Couldn't add rtas token for %s: %s\n",
call->name, fdt_strerror(ret));
return ret;
}

}
return 0;
}
Binary file added pc-bios/spapr-rtas.bin
Binary file not shown.
24 changes: 24 additions & 0 deletions pc-bios/spapr-rtas/Makefile
@@ -0,0 +1,24 @@
all: build-all
# Dummy command so that make thinks it has done something
@true

include ../../config-host.mak
include $(SRC_PATH)/rules.mak

$(call set-vpath, $(SRC_PATH)/pc-bios/spapr-rtas)

.PHONY : all clean build-all

#CFLAGS += -I$(SRC_PATH)
#QEMU_CFLAGS = $(CFLAGS)

build-all: spapr-rtas.bin

%.img: %.o
$(call quiet-command,$(CC) -nostdlib -o $@ $<," Building $(TARGET_DIR)$@")

%.bin: %.img
$(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@," Building $(TARGET_DIR)$@")

clean:
rm -f *.o *.d *.img *.bin *~

0 comments on commit 39ac845

Please sign in to comment.