From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=38302 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PzsKx-0001yy-Uh for qemu-devel@nongnu.org; Wed, 16 Mar 2011 11:08:37 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PzsKu-0007hW-Ts for qemu-devel@nongnu.org; Wed, 16 Mar 2011 11:08:35 -0400 Received: from cantor2.suse.de ([195.135.220.15]:50362 helo=mx2.suse.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PzsKu-0007gp-AE for qemu-devel@nongnu.org; Wed, 16 Mar 2011 11:08:32 -0400 Message-ID: <4D80D26E.1030206@suse.de> Date: Wed, 16 Mar 2011 16:08:30 +0100 From: Alexander Graf MIME-Version: 1.0 References: <1300251423-6715-1-git-send-email-david@gibson.dropbear.id.au> <1300251423-6715-17-git-send-email-david@gibson.dropbear.id.au> In-Reply-To: <1300251423-6715-17-git-send-email-david@gibson.dropbear.id.au> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Subject: [Qemu-devel] Re: [PATCH 16/26] Implement hcall based RTAS for pSeries machines List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: David Gibson Cc: paulus@samba.org, qemu-devel@nongnu.org, anton@samba.org On 03/16/2011 05:56 AM, David Gibson wrote: > 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/Makefile b/Makefile > index eca4c76..fc4bd24 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 \ > 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 3f2b235..e333225 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/hw/spapr.c b/hw/spapr.c > index c3d9286..f41451b 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 > > @@ -51,6 +52,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; > @@ -162,7 +165,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize, > > _FDT((fdt_property(fdt, "ibm,hypertas-functions", hypertas_prop, > sizeof(hypertas_prop)))); > - > + Ahem... > _FDT((fdt_end_node(fdt))); > > /* vdevice */ > @@ -186,6 +189,11 @@ 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) Braces > + fprintf(stderr, "Couldn't set up RTAS device tree properties\n"); > + > _FDT((fdt_pack(fdt))); > > if (fdt_size) { > @@ -218,12 +226,13 @@ 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; > sPAPREnvironment *spapr; > + char *filename; > > spapr = qemu_malloc(sizeof(*spapr)); > > @@ -231,6 +240,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) { > @@ -271,6 +282,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++) { > @@ -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; > +} > 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 > literal 0 > HcmV?d00001 Despite being very simple, this is missing source code. There needs to at least be a reference on where to find it in some text file in pc-bios. Alex