Implement hcall based RTAS for pSeries machines
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>
This commit is contained in:
parent
f43e35255c
commit
39ac845510
3
Makefile
3
Makefile
@ -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
|
||||
|
@ -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
|
||||
|
4
configure
vendored
4
configure
vendored
@ -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`"
|
||||
|
26
hw/spapr.c
26
hw/spapr.c
@ -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
|
||||
|
||||
@ -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;
|
||||
@ -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);
|
||||
@ -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;
|
||||
@ -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) {
|
||||
@ -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++) {
|
||||
@ -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);
|
||||
|
32
hw/spapr.h
32
hw/spapr.h
@ -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*/
|
||||
@ -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__) */
|
||||
|
@ -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;
|
||||
|
||||
assert(opcode <= MAX_HCALL_OPCODE);
|
||||
assert((opcode & 0x3) == 0);
|
||||
if (opcode <= MAX_HCALL_OPCODE) {
|
||||
assert((opcode & 0x3) == 0);
|
||||
|
||||
old_fn = hypercall_table[opcode / 4];
|
||||
slot = &papr_hypercall_table[opcode / 4];
|
||||
} else {
|
||||
assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX));
|
||||
|
||||
assert(!old_fn || (fn == old_fn));
|
||||
|
||||
hypercall_table[opcode / 4] = fn;
|
||||
slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE];
|
||||
}
|
||||
|
||||
assert(!(*slot) || (fn == *slot));
|
||||
*slot = fn;
|
||||
}
|
||||
|
||||
target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
|
||||
@ -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);
|
||||
@ -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
hw/spapr_rtas.c
Normal file
131
hw/spapr_rtas.c
Normal file
@ -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;
|
||||
}
|
BIN
pc-bios/spapr-rtas.bin
Executable file
BIN
pc-bios/spapr-rtas.bin
Executable file
Binary file not shown.
24
pc-bios/spapr-rtas/Makefile
Normal file
24
pc-bios/spapr-rtas/Makefile
Normal file
@ -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 *~
|
37
pc-bios/spapr-rtas/spapr-rtas.S
Normal file
37
pc-bios/spapr-rtas/spapr-rtas.S
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
|
||||
*
|
||||
* Trivial in-partition RTAS implementation, based on a hypercall
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#define KVMPPC_HCALL_BASE 0xf000
|
||||
#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0)
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
mr 4,3
|
||||
lis 3,KVMPPC_H_RTAS@h
|
||||
ori 3,3,KVMPPC_H_RTAS@l
|
||||
sc 1
|
||||
blr
|
Loading…
Reference in New Issue
Block a user