Skip to content

Commit 39ac845

Browse files
dgibsonagraf
authored andcommitted
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 <[email protected]> Signed-off-by: Alexander Graf <[email protected]>
1 parent f43e352 commit 39ac845

10 files changed

+289
-14
lines changed

Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,8 @@ pxe-ne2k_pci.bin pxe-pcnet.bin \
213213
pxe-rtl8139.bin pxe-virtio.bin \
214214
bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
215215
multiboot.bin linuxboot.bin \
216-
s390-zipl.rom
216+
s390-zipl.rom \
217+
spapr-rtas.bin
217218
else
218219
BLOBS=
219220
endif

Makefile.target

+1-1
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ obj-ppc-y += ppc_oldworld.o
233233
obj-ppc-y += ppc_newworld.o
234234
# IBM pSeries (sPAPR)
235235
ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
236-
obj-ppc-y += spapr.o spapr_hcall.o spapr_vio.o
236+
obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
237237
obj-ppc-y += spapr_vty.o
238238
endif
239239
# PowerPC 4xx boards

configure

+3-1
Original file line numberDiff line numberDiff line change
@@ -2461,7 +2461,9 @@ if test \( "$cpu" = "i386" -o "$cpu" = "x86_64" \) -a \
24612461
"$softmmu" = yes ; then
24622462
roms="optionrom"
24632463
fi
2464-
2464+
if test "$cpu" = "ppc64" ; then
2465+
roms="$roms spapr-rtas"
2466+
fi
24652467

24662468
echo "Install prefix $prefix"
24672469
echo "BIOS directory `eval echo $datadir`"

hw/spapr.c

+23-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#define KERNEL_LOAD_ADDR 0x00000000
4141
#define INITRD_LOAD_ADDR 0x02800000
4242
#define FDT_MAX_SIZE 0x10000
43+
#define RTAS_MAX_SIZE 0x10000
4344

4445
#define TIMEBASE_FREQ 512000000ULL
4546

@@ -53,6 +54,8 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
5354
target_phys_addr_t initrd_base,
5455
target_phys_addr_t initrd_size,
5556
const char *kernel_cmdline,
57+
target_phys_addr_t rtas_addr,
58+
target_phys_addr_t rtas_size,
5659
long hash_shift)
5760
{
5861
void *fdt;
@@ -195,6 +198,12 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
195198
exit(1);
196199
}
197200

201+
/* RTAS */
202+
ret = spapr_rtas_device_tree_setup(fdt, rtas_addr, rtas_size);
203+
if (ret < 0) {
204+
fprintf(stderr, "Couldn't set up RTAS device tree properties\n");
205+
}
206+
198207
_FDT((fdt_pack(fdt)));
199208

200209
*fdt_size = fdt_totalsize(fdt);
@@ -224,11 +233,12 @@ static void ppc_spapr_init(ram_addr_t ram_size,
224233
void *fdt, *htab;
225234
int i;
226235
ram_addr_t ram_offset;
227-
target_phys_addr_t fdt_addr;
236+
target_phys_addr_t fdt_addr, rtas_addr;
228237
uint32_t kernel_base, initrd_base;
229-
long kernel_size, initrd_size, htab_size;
238+
long kernel_size, initrd_size, htab_size, rtas_size;
230239
long pteg_shift = 17;
231240
int fdt_size;
241+
char *filename;
232242

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

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

291+
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
292+
rtas_size = load_image_targphys(filename, rtas_addr, ram_size - rtas_addr);
293+
if (rtas_size < 0) {
294+
hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
295+
exit(1);
296+
}
297+
qemu_free(filename);
298+
279299
spapr->vio_bus = spapr_vio_bus_init();
280300

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

329349
cpu_physical_memory_write(fdt_addr, fdt, fdt_size);

hw/spapr.h

+32
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,18 @@ typedef struct sPAPREnvironment {
237237
#define H_GET_MPP 0x2D4
238238
#define MAX_HCALL_OPCODE H_GET_MPP
239239

240+
/* The hcalls above are standardized in PAPR and implemented by pHyp
241+
* as well.
242+
*
243+
* We also need some hcalls which are specific to qemu / KVM-on-POWER.
244+
* So far we just need one for H_RTAS, but in future we'll need more
245+
* for extensions like virtio. We put those into the 0xf000-0xfffc
246+
* range which is reserved by PAPR for "platform-specific" hcalls.
247+
*/
248+
#define KVMPPC_HCALL_BASE 0xf000
249+
#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0)
250+
#define KVMPPC_HCALL_MAX KVMPPC_H_RTAS
251+
240252
extern sPAPREnvironment *spapr;
241253

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

272+
static inline uint32_t rtas_ld(target_ulong phys, int n)
273+
{
274+
return ldl_phys(phys + 4*n);
275+
}
276+
277+
static inline void rtas_st(target_ulong phys, int n, uint32_t val)
278+
{
279+
stl_phys(phys + 4*n, val);
280+
}
281+
282+
typedef void (*spapr_rtas_fn)(sPAPREnvironment *spapr, uint32_t token,
283+
uint32_t nargs, target_ulong args,
284+
uint32_t nret, target_ulong rets);
285+
void spapr_rtas_register(const char *name, spapr_rtas_fn fn);
286+
target_ulong spapr_rtas_call(sPAPREnvironment *spapr,
287+
uint32_t token, uint32_t nargs, target_ulong args,
288+
uint32_t nret, target_ulong rets);
289+
int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr,
290+
target_phys_addr_t rtas_size);
291+
260292
#endif /* !defined (__HW_SPAPR_H__) */

hw/spapr_hcall.c

+36-8
Original file line numberDiff line numberDiff line change
@@ -248,20 +248,38 @@ static target_ulong h_protect(CPUState *env, sPAPREnvironment *spapr,
248248
return H_SUCCESS;
249249
}
250250

251-
spapr_hcall_fn hypercall_table[(MAX_HCALL_OPCODE / 4) + 1];
251+
static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr,
252+
target_ulong opcode, target_ulong *args)
253+
{
254+
target_ulong rtas_r3 = args[0];
255+
uint32_t token = ldl_phys(rtas_r3);
256+
uint32_t nargs = ldl_phys(rtas_r3 + 4);
257+
uint32_t nret = ldl_phys(rtas_r3 + 8);
258+
259+
return spapr_rtas_call(spapr, token, nargs, rtas_r3 + 12,
260+
nret, rtas_r3 + 12 + 4*nargs);
261+
}
262+
263+
spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1];
264+
spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE];
252265

253266
void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn)
254267
{
255-
spapr_hcall_fn old_fn;
268+
spapr_hcall_fn *slot;
269+
270+
if (opcode <= MAX_HCALL_OPCODE) {
271+
assert((opcode & 0x3) == 0);
256272

257-
assert(opcode <= MAX_HCALL_OPCODE);
258-
assert((opcode & 0x3) == 0);
273+
slot = &papr_hypercall_table[opcode / 4];
274+
} else {
275+
assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX));
259276

260-
old_fn = hypercall_table[opcode / 4];
261277

262-
assert(!old_fn || (fn == old_fn));
278+
slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE];
279+
}
263280

264-
hypercall_table[opcode / 4] = fn;
281+
assert(!(*slot) || (fn == *slot));
282+
*slot = fn;
265283
}
266284

267285
target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
@@ -274,7 +292,14 @@ target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
274292

275293
if ((opcode <= MAX_HCALL_OPCODE)
276294
&& ((opcode & 0x3) == 0)) {
277-
spapr_hcall_fn fn = hypercall_table[opcode / 4];
295+
spapr_hcall_fn fn = papr_hypercall_table[opcode / 4];
296+
297+
if (fn) {
298+
return fn(env, spapr, opcode, args);
299+
}
300+
} else if ((opcode >= KVMPPC_HCALL_BASE) &&
301+
(opcode <= KVMPPC_HCALL_MAX)) {
302+
spapr_hcall_fn fn = kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE];
278303

279304
if (fn) {
280305
return fn(env, spapr, opcode, args);
@@ -291,5 +316,8 @@ static void hypercall_init(void)
291316
spapr_register_hypercall(H_ENTER, h_enter);
292317
spapr_register_hypercall(H_REMOVE, h_remove);
293318
spapr_register_hypercall(H_PROTECT, h_protect);
319+
320+
/* qemu/KVM-PPC specific hcalls */
321+
spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas);
294322
}
295323
device_init(hypercall_init);

hw/spapr_rtas.c

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
3+
*
4+
* Hypercall based emulated RTAS
5+
*
6+
* Copyright (c) 2010-2011 David Gibson, IBM Corporation.
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21+
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*
26+
*/
27+
#include "cpu.h"
28+
#include "sysemu.h"
29+
#include "qemu-char.h"
30+
#include "hw/qdev.h"
31+
#include "device_tree.h"
32+
33+
#include "hw/spapr.h"
34+
#include "hw/spapr_vio.h"
35+
36+
#include <libfdt.h>
37+
38+
#define TOKEN_BASE 0x2000
39+
#define TOKEN_MAX 0x100
40+
41+
static struct rtas_call {
42+
const char *name;
43+
spapr_rtas_fn fn;
44+
} rtas_table[TOKEN_MAX];
45+
46+
struct rtas_call *rtas_next = rtas_table;
47+
48+
target_ulong spapr_rtas_call(sPAPREnvironment *spapr,
49+
uint32_t token, uint32_t nargs, target_ulong args,
50+
uint32_t nret, target_ulong rets)
51+
{
52+
if ((token >= TOKEN_BASE)
53+
&& ((token - TOKEN_BASE) < TOKEN_MAX)) {
54+
struct rtas_call *call = rtas_table + (token - TOKEN_BASE);
55+
56+
if (call->fn) {
57+
call->fn(spapr, token, nargs, args, nret, rets);
58+
return H_SUCCESS;
59+
}
60+
}
61+
62+
hcall_dprintf("Unknown RTAS token 0x%x\n", token);
63+
rtas_st(rets, 0, -3);
64+
return H_PARAMETER;
65+
}
66+
67+
void spapr_rtas_register(const char *name, spapr_rtas_fn fn)
68+
{
69+
assert(rtas_next < (rtas_table + TOKEN_MAX));
70+
71+
rtas_next->name = name;
72+
rtas_next->fn = fn;
73+
74+
rtas_next++;
75+
}
76+
77+
int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr,
78+
target_phys_addr_t rtas_size)
79+
{
80+
int ret;
81+
int i;
82+
83+
ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size);
84+
if (ret < 0) {
85+
fprintf(stderr, "Couldn't add RTAS reserve entry: %s\n",
86+
fdt_strerror(ret));
87+
return ret;
88+
}
89+
90+
ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-base",
91+
rtas_addr);
92+
if (ret < 0) {
93+
fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n",
94+
fdt_strerror(ret));
95+
return ret;
96+
}
97+
98+
ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-entry",
99+
rtas_addr);
100+
if (ret < 0) {
101+
fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n",
102+
fdt_strerror(ret));
103+
return ret;
104+
}
105+
106+
ret = qemu_devtree_setprop_cell(fdt, "/rtas", "rtas-size",
107+
rtas_size);
108+
if (ret < 0) {
109+
fprintf(stderr, "Couldn't add rtas-size property: %s\n",
110+
fdt_strerror(ret));
111+
return ret;
112+
}
113+
114+
for (i = 0; i < TOKEN_MAX; i++) {
115+
struct rtas_call *call = &rtas_table[i];
116+
117+
if (!call->fn) {
118+
continue;
119+
}
120+
121+
ret = qemu_devtree_setprop_cell(fdt, "/rtas", call->name,
122+
i + TOKEN_BASE);
123+
if (ret < 0) {
124+
fprintf(stderr, "Couldn't add rtas token for %s: %s\n",
125+
call->name, fdt_strerror(ret));
126+
return ret;
127+
}
128+
129+
}
130+
return 0;
131+
}

pc-bios/spapr-rtas.bin

20 Bytes
Binary file not shown.

pc-bios/spapr-rtas/Makefile

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
all: build-all
2+
# Dummy command so that make thinks it has done something
3+
@true
4+
5+
include ../../config-host.mak
6+
include $(SRC_PATH)/rules.mak
7+
8+
$(call set-vpath, $(SRC_PATH)/pc-bios/spapr-rtas)
9+
10+
.PHONY : all clean build-all
11+
12+
#CFLAGS += -I$(SRC_PATH)
13+
#QEMU_CFLAGS = $(CFLAGS)
14+
15+
build-all: spapr-rtas.bin
16+
17+
%.img: %.o
18+
$(call quiet-command,$(CC) -nostdlib -o $@ $<," Building $(TARGET_DIR)$@")
19+
20+
%.bin: %.img
21+
$(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@," Building $(TARGET_DIR)$@")
22+
23+
clean:
24+
rm -f *.o *.d *.img *.bin *~

0 commit comments

Comments
 (0)