From patchwork Wed Mar 23 05:30:36 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Gibson X-Patchwork-Id: 88033 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id C2DF7B6F14 for ; Wed, 23 Mar 2011 17:43:02 +1100 (EST) Received: from localhost ([127.0.0.1]:52734 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Q2HmH-0001TQ-8q for incoming@patchwork.ozlabs.org; Wed, 23 Mar 2011 02:42:45 -0400 Received: from [140.186.70.92] (port=36282 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Q2GfE-0006Hl-HK for qemu-devel@nongnu.org; Wed, 23 Mar 2011 01:31:26 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Q2Gf9-0006Io-Dh for qemu-devel@nongnu.org; Wed, 23 Mar 2011 01:31:24 -0400 Received: from ozlabs.org ([203.10.76.45]:41518) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Q2Gf8-0006Es-Of for qemu-devel@nongnu.org; Wed, 23 Mar 2011 01:31:19 -0400 Received: by ozlabs.org (Postfix, from userid 1007) id 6AD8C1007DE; Wed, 23 Mar 2011 16:31:07 +1100 (EST) From: David Gibson To: agraf@suse.de, qemu-devel@nongnu.org Date: Wed, 23 Mar 2011 16:30:36 +1100 Message-Id: <1300858247-8197-17-git-send-email-david@gibson.dropbear.id.au> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1300858247-8197-1-git-send-email-david@gibson.dropbear.id.au> References: <1300858247-8197-1-git-send-email-david@gibson.dropbear.id.au> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 203.10.76.45 Cc: paulus@samba.org Subject: [Qemu-devel] [PATCH 16/27] Implement hcall based RTAS for pSeries machines X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- Makefile | 3 +- Makefile.target | 2 +- configure | 4 +- hw/spapr.c | 26 +++++++- hw/spapr.h | 22 +++++++ hw/spapr_hcall.c | 15 +++++ hw/spapr_rtas.c | 131 +++++++++++++++++++++++++++++++++++++++ pc-bios/spapr-rtas.bin | Bin 0 -> 20 bytes pc-bios/spapr-rtas/Makefile | 24 +++++++ pc-bios/spapr-rtas/spapr-rtas.S | 36 +++++++++++ 10 files changed, 257 insertions(+), 6 deletions(-) create mode 100644 hw/spapr_rtas.c create mode 100644 pc-bios/spapr-rtas.bin create mode 100644 pc-bios/spapr-rtas/Makefile create mode 100644 pc-bios/spapr-rtas/spapr-rtas.S diff --git a/Makefile b/Makefile index 89e88b4..e0b3fea 100644 --- a/Makefile +++ b/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 diff --git a/Makefile.target b/Makefile.target index 2cea6ad..e52b058 100644 --- a/Makefile.target +++ b/Makefile.target @@ -232,7 +232,7 @@ obj-ppc-y += ppc_oldworld.o # NewWorld PowerMac obj-ppc-y += ppc_newworld.o # IBM pSeries (sPAPR) -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 # PowerPC 4xx boards obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o diff --git a/configure b/configure index 5a5827f..7d8d890 100755 --- a/configure +++ b/configure @@ -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`" diff --git a/hw/spapr.c b/hw/spapr.c index 15c1509..59e07d7 100644 --- a/hw/spapr.c +++ b/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; @@ -194,6 +197,12 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize, fprintf(stderr, "couldn't setup vio devices in fdt\n"); } + /* 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))); if (fdt_size) { @@ -225,11 +234,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; @@ -238,6 +248,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) { @@ -277,6 +289,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); if (!fdt) { hw_error("Couldn't create pSeries device tree\n"); exit(1); diff --git a/hw/spapr.h b/hw/spapr.h index 06cca15..62a040f 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -237,6 +237,8 @@ typedef struct sPAPREnvironment { #define H_GET_MPP 0x2D4 #define MAX_HCALL_OPCODE H_GET_MPP +#define H_RTAS 0x72746173 + extern sPAPREnvironment *spapr; /*#define DEBUG_SPAPR_HCALLS*/ @@ -257,4 +259,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__) */ diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index 5c2dd88..594e27d 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -248,6 +248,16 @@ static target_ulong h_protect(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } +static target_ulong h_rtas(sPAPREnvironment *spapr, target_ulong rtas_r3) +{ + 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); +} + struct hypercall { spapr_hcall_fn fn; } hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; @@ -283,6 +293,11 @@ target_ulong spapr_hypercall(CPUState *env, target_ulong opcode, } } + if (opcode == H_RTAS) { + /* H_RTAS is a special case outside the normal range */ + return h_rtas(spapr, args[0]); + } + hcall_dprintf("Unimplemented hcall 0x" TARGET_FMT_lx "\n", opcode); return H_FUNCTION; } diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c new file mode 100644 index 0000000..3f090f5 --- /dev/null +++ b/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 + +#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; +} diff --git a/pc-bios/spapr-rtas.bin b/pc-bios/spapr-rtas.bin new file mode 100644 index 0000000000000000000000000000000000000000..eade9c0e8ff0fd3071e3a6638a11c1a2e9a47152 GIT binary patch literal 20 bcmb