qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] Re: [PATCH 16/26] Implement hcall based RTAS for pSeries ma


From: Alexander Graf
Subject: [Qemu-devel] Re: [PATCH 16/26] Implement hcall based RTAS for pSeries machines
Date: Wed, 16 Mar 2011 16:08:30 +0100
User-agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101206 SUSE/3.1.7 Thunderbird/3.1.7

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<address@hidden>
---
  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<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;
+        }
+    }
+
+    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<address@hidden)vPAqm|U{LaFU{C-6M#cr<

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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]