From patchwork Tue Feb 15 04:56:28 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: qemu@gibson.dropbear.id.au X-Patchwork-Id: 83176 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 A496AB70A4 for ; Tue, 15 Feb 2011 16:07:11 +1100 (EST) Received: from localhost ([127.0.0.1]:56486 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PpD6i-0004FT-Lq for incoming@patchwork.ozlabs.org; Tue, 15 Feb 2011 00:05:48 -0500 Received: from [140.186.70.92] (port=53514 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PpCyM-00007d-N6 for qemu-devel@nongnu.org; Mon, 14 Feb 2011 23:57:13 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PpCyK-0001ht-Ml for qemu-devel@nongnu.org; Mon, 14 Feb 2011 23:57:10 -0500 Received: from ozlabs.org ([203.10.76.45]:34737) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PpCyK-0001aL-1i for qemu-devel@nongnu.org; Mon, 14 Feb 2011 23:57:08 -0500 Received: by ozlabs.org (Postfix, from userid 1007) id 99BCBB712B; Tue, 15 Feb 2011 15:56:53 +1100 (EST) From: qemu@gibson.dropbear.id.au To: qemu-devel@nongnu.org Date: Tue, 15 Feb 2011 15:56:28 +1100 Message-Id: <1297745799-26148-18-git-send-email-qemu@gibson.dropbear.id.au> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1297745799-26148-1-git-send-email-qemu@gibson.dropbear.id.au> References: <1297745799-26148-1-git-send-email-qemu@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, agraf@suse.de, anton@samba.org Subject: [Qemu-devel] [PATCH 17/28] 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 From: David Gibson 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 +- hw/spapr.c | 27 +++++++++++-- hw/spapr.h | 21 ++++++++++ hw/spapr_hcall.c | 15 +++++++ hw/spapr_rtas.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ pc-bios/spapr-rtas.bin | Bin 0 -> 20 bytes 7 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 hw/spapr_rtas.c create mode 100644 pc-bios/spapr-rtas.bin 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 bcmbhtab_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++) { @@ -317,7 +336,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 47bf2ef..7a7c319 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 + typedef target_ulong (*spapr_hcall_fn)(CPUState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args); @@ -245,5 +247,24 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn); target_ulong spapr_hypercall(CPUState *env, sPAPREnvironment *spapr, 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 2b14000..7b8e17c 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -241,6 +241,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]; @@ -276,6 +286,11 @@ target_ulong spapr_hypercall(CPUState *env, sPAPREnvironment *spapr, return hc->fn(env, spapr, opcode, args); } + if (opcode == H_RTAS) { + /* H_RTAS is a special case outside the normal range */ + return h_rtas(spapr, args[0]); + } + fprintf(stderr, "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..c606018 --- /dev/null +++ b/hw/spapr_rtas.c @@ -0,0 +1,104 @@ +#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; + } + } + + fprintf(stderr, "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; +}