All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5)
@ 2011-03-25  3:21 David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 01/27] Clean up PowerPC SLB handling code David Gibson
                   ` (27 more replies)
  0 siblings, 28 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

This patch series adds a "pseries" machine to qemu, allowing it to
emulate IBM pSeries logical partitions.  More specifically it
implements the interface defined by the "PowerPC Architecture Platform
Requirements" document (PAPR, or sPAPR for short).

Along the way we add a bunch of support for more modern ppc CPUs than
are currently supported.  It also makes some significant cleanups to
the translation code for hash page table based ppc MMUs.

Please apply.

---

Note that I haven't implemented (yet) a min_ram field in the machine
structure.  There are a number of places where the pseries platform
would benefit from more participation of the machine description in
command line validation.  In want to think a bit more about these
before sending some patches.  For now I've taken the simpler approach
of just adding a meaningful error message to the machine init function
if ram_size is too small.

Changes since v4 of this series:
 * Fix build breakages for powerpc targets other than ppc64 full system.
 * Since the pseries platform requires libfdt, only compile it when
   configured with --enable-fdt
 * Give an informative error if invoked with insufficient guest RAM to
   run the partition firmware.  Without this, giving insufficient RAM
   - such as qemu's default 64M - would lead to the firmware failing
   cryptically partway through boot.

Changes since v3 of this series:
 * Many, many checkpatch fixups
 * Integrated feedback from qemu-devel list
 * Added in-partition SLOF firmware

Changes since v2 of this series:
 * Assorted bugfixes and cleanups.

Changes since v1 of this series:
 * numerous coding style fixups
 * incorporated most review comments from initial version
 * moved to a wholly dynamic hypercall registration scheme
 * assorted other cleanups
 * many more patches implementing VIO devices

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 01/27] Clean up PowerPC SLB handling code
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-05-19  5:35   ` Andreas Färber
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 02/27] Allow qemu_devtree_setprop() to take arbitrary values David Gibson
                   ` (26 subsequent siblings)
  27 siblings, 1 reply; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

Currently the SLB information when emulating a PowerPC 970 is
storeed in a structure with the unhelpfully named fields 'tmp'
and 'tmp64'.  While the layout in these fields does match the
description of the SLB in the architecture document, it is not
convenient either for looking up the SLB, or for emulating the
slbmte instruction.

This patch, therefore, reorganizes the SLB entry structure to be
divided in the the "ESID related" and "VSID related" fields as
they are divided in instructions accessing the SLB.

In addition to making the code smaller and more readable, this will
make it easier to implement for the 1TB segments used in more
recent PowerPC chips.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 target-ppc/cpu.h       |   29 +++++++-
 target-ppc/helper.c    |  178 ++++++++++++++----------------------------------
 target-ppc/helper.h    |    1 -
 target-ppc/op_helper.c |    9 +--
 4 files changed, 80 insertions(+), 137 deletions(-)

diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index deb8d7c..124bbbf 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -43,6 +43,8 @@
 # define TARGET_VIRT_ADDR_SPACE_BITS 64
 #endif
 
+#define TARGET_PAGE_BITS_16M 24
+
 #else /* defined (TARGET_PPC64) */
 /* PowerPC 32 definitions */
 #define TARGET_LONG_BITS 32
@@ -359,10 +361,31 @@ union ppc_tlb_t {
 
 typedef struct ppc_slb_t ppc_slb_t;
 struct ppc_slb_t {
-    uint64_t tmp64;
-    uint32_t tmp;
+    uint64_t esid;
+    uint64_t vsid;
 };
 
+/* Bits in the SLB ESID word */
+#define SLB_ESID_ESID           0xFFFFFFFFF0000000ULL
+#define SLB_ESID_V              0x0000000008000000ULL /* valid */
+
+/* Bits in the SLB VSID word */
+#define SLB_VSID_SHIFT          12
+#define SLB_VSID_SSIZE_SHIFT    62
+#define SLB_VSID_B              0xc000000000000000ULL
+#define SLB_VSID_B_256M         0x0000000000000000ULL
+#define SLB_VSID_VSID           0x3FFFFFFFFFFFF000ULL
+#define SLB_VSID_KS             0x0000000000000800ULL
+#define SLB_VSID_KP             0x0000000000000400ULL
+#define SLB_VSID_N              0x0000000000000200ULL /* no-execute */
+#define SLB_VSID_L              0x0000000000000100ULL
+#define SLB_VSID_C              0x0000000000000080ULL /* class */
+#define SLB_VSID_LP             0x0000000000000030ULL
+#define SLB_VSID_ATTR           0x0000000000000FFFULL
+
+#define SEGMENT_SHIFT_256M      28
+#define SEGMENT_MASK_256M       (~((1ULL << SEGMENT_SHIFT_256M) - 1))
+
 /*****************************************************************************/
 /* Machine state register bits definition                                    */
 #define MSR_SF   63 /* Sixty-four-bit mode                            hflags */
@@ -755,7 +778,7 @@ void ppc_store_sdr1 (CPUPPCState *env, target_ulong value);
 void ppc_store_asr (CPUPPCState *env, target_ulong value);
 target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr);
 target_ulong ppc_load_sr (CPUPPCState *env, int sr_nr);
-void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
+int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
 #endif /* defined(TARGET_PPC64) */
 void ppc_store_sr (CPUPPCState *env, int srnum, target_ulong value);
 #endif /* !defined(CONFIG_USER_ONLY) */
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 4b49101..2094ca3 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -672,85 +672,36 @@ static inline int find_pte(CPUState *env, mmu_ctx_t *ctx, int h, int rw,
 }
 
 #if defined(TARGET_PPC64)
-static ppc_slb_t *slb_get_entry(CPUPPCState *env, int nr)
-{
-    ppc_slb_t *retval = &env->slb[nr];
-
-#if 0 // XXX implement bridge mode?
-    if (env->spr[SPR_ASR] & 1) {
-        target_phys_addr_t sr_base;
-
-        sr_base = env->spr[SPR_ASR] & 0xfffffffffffff000;
-        sr_base += (12 * nr);
-
-        retval->tmp64 = ldq_phys(sr_base);
-        retval->tmp = ldl_phys(sr_base + 8);
-    }
-#endif
-
-    return retval;
-}
-
-static void slb_set_entry(CPUPPCState *env, int nr, ppc_slb_t *slb)
-{
-    ppc_slb_t *entry = &env->slb[nr];
-
-    if (slb == entry)
-        return;
-
-    entry->tmp64 = slb->tmp64;
-    entry->tmp = slb->tmp;
-}
-
-static inline int slb_is_valid(ppc_slb_t *slb)
-{
-    return (int)(slb->tmp64 & 0x0000000008000000ULL);
-}
-
-static inline void slb_invalidate(ppc_slb_t *slb)
-{
-    slb->tmp64 &= ~0x0000000008000000ULL;
-}
-
 static inline int slb_lookup(CPUPPCState *env, target_ulong eaddr,
                              target_ulong *vsid, target_ulong *page_mask,
                              int *attr, int *target_page_bits)
 {
-    target_ulong mask;
-    int n, ret;
+    uint64_t esid;
+    int n;
 
-    ret = -5;
     LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr);
-    mask = 0x0000000000000000ULL; /* Avoid gcc warning */
+
+    esid = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V;
+
     for (n = 0; n < env->slb_nr; n++) {
-        ppc_slb_t *slb = slb_get_entry(env, n);
-
-        LOG_SLB("%s: seg %d %016" PRIx64 " %08"
-                    PRIx32 "\n", __func__, n, slb->tmp64, slb->tmp);
-        if (slb_is_valid(slb)) {
-            /* SLB entry is valid */
-            mask = 0xFFFFFFFFF0000000ULL;
-            if (slb->tmp & 0x8) {
-                /* 16 MB PTEs */
-                if (target_page_bits)
-                    *target_page_bits = 24;
-            } else {
-                /* 4 KB PTEs */
-                if (target_page_bits)
-                    *target_page_bits = TARGET_PAGE_BITS;
-            }
-            if ((eaddr & mask) == (slb->tmp64 & mask)) {
-                /* SLB match */
-                *vsid = ((slb->tmp64 << 24) | (slb->tmp >> 8)) & 0x0003FFFFFFFFFFFFULL;
-                *page_mask = ~mask;
-                *attr = slb->tmp & 0xFF;
-                ret = n;
-                break;
+        ppc_slb_t *slb = &env->slb[n];
+
+        LOG_SLB("%s: slot %d %016" PRIx64 " %016"
+                    PRIx64 "\n", __func__, n, slb->esid, slb->vsid);
+        if (slb->esid == esid) {
+            *vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
+            *page_mask = ~SEGMENT_MASK_256M;
+            *attr = slb->vsid & SLB_VSID_ATTR;
+            if (target_page_bits) {
+                *target_page_bits = (slb->vsid & SLB_VSID_L)
+                    ? TARGET_PAGE_BITS_16M
+                    : TARGET_PAGE_BITS;
             }
+            return n;
         }
     }
 
-    return ret;
+    return -5;
 }
 
 void ppc_slb_invalidate_all (CPUPPCState *env)
@@ -760,11 +711,10 @@ void ppc_slb_invalidate_all (CPUPPCState *env)
     do_invalidate = 0;
     /* XXX: Warning: slbia never invalidates the first segment */
     for (n = 1; n < env->slb_nr; n++) {
-        ppc_slb_t *slb = slb_get_entry(env, n);
+        ppc_slb_t *slb = &env->slb[n];
 
-        if (slb_is_valid(slb)) {
-            slb_invalidate(slb);
-            slb_set_entry(env, n, slb);
+        if (slb->esid & SLB_ESID_V) {
+            slb->esid &= ~SLB_ESID_V;
             /* XXX: given the fact that segment size is 256 MB or 1TB,
              *      and we still don't have a tlb_flush_mask(env, n, mask)
              *      in Qemu, we just invalidate all TLBs
@@ -781,68 +731,44 @@ void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0)
     target_ulong vsid, page_mask;
     int attr;
     int n;
+    ppc_slb_t *slb;
 
     n = slb_lookup(env, T0, &vsid, &page_mask, &attr, NULL);
-    if (n >= 0) {
-        ppc_slb_t *slb = slb_get_entry(env, n);
-
-        if (slb_is_valid(slb)) {
-            slb_invalidate(slb);
-            slb_set_entry(env, n, slb);
-            /* XXX: given the fact that segment size is 256 MB or 1TB,
-             *      and we still don't have a tlb_flush_mask(env, n, mask)
-             *      in Qemu, we just invalidate all TLBs
-             */
-            tlb_flush(env, 1);
-        }
+    if (n < 0) {
+        return;
     }
-}
 
-target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr)
-{
-    target_ulong rt;
-    ppc_slb_t *slb = slb_get_entry(env, slb_nr);
+    slb = &env->slb[n];
 
-    if (slb_is_valid(slb)) {
-        /* SLB entry is valid */
-        /* Copy SLB bits 62:88 to Rt 37:63 (VSID 23:49) */
-        rt = slb->tmp >> 8;             /* 65:88 => 40:63 */
-        rt |= (slb->tmp64 & 0x7) << 24; /* 62:64 => 37:39 */
-        /* Copy SLB bits 89:92 to Rt 33:36 (KsKpNL) */
-        rt |= ((slb->tmp >> 4) & 0xF) << 27;
-    } else {
-        rt = 0;
-    }
-    LOG_SLB("%s: %016" PRIx64 " %08" PRIx32 " => %d "
-            TARGET_FMT_lx "\n", __func__, slb->tmp64, slb->tmp, slb_nr, rt);
+    if (slb->esid & SLB_ESID_V) {
+        slb->esid &= ~SLB_ESID_V;
 
-    return rt;
+        /* XXX: given the fact that segment size is 256 MB or 1TB,
+         *      and we still don't have a tlb_flush_mask(env, n, mask)
+         *      in Qemu, we just invalidate all TLBs
+         */
+        tlb_flush(env, 1);
+    }
 }
 
-void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs)
+int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs)
 {
-    ppc_slb_t *slb;
-
-    uint64_t vsid;
-    uint64_t esid;
-    int flags, valid, slb_nr;
-
-    vsid = rs >> 12;
-    flags = ((rs >> 8) & 0xf);
+    int slot = rb & 0xfff;
+    uint64_t esid = rb & ~0xfff;
+    ppc_slb_t *slb = &env->slb[slot];
 
-    esid = rb >> 28;
-    valid = (rb & (1 << 27));
-    slb_nr = rb & 0xfff;
+    if (slot >= env->slb_nr) {
+        return -1;
+    }
 
-    slb = slb_get_entry(env, slb_nr);
-    slb->tmp64 = (esid << 28) | valid | (vsid >> 24);
-    slb->tmp = (vsid << 8) | (flags << 3);
+    slb->esid = esid;
+    slb->vsid = rs;
 
     LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64
-            " %08" PRIx32 "\n", __func__, slb_nr, rb, rs, slb->tmp64,
-            slb->tmp);
+            " %016" PRIx64 "\n", __func__, slot, rb, rs,
+            slb->esid, slb->vsid);
 
-    slb_set_entry(env, slb_nr, slb);
+    return 0;
 }
 #endif /* defined(TARGET_PPC64) */
 
@@ -860,24 +786,22 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
 {
     target_phys_addr_t sdr, hash, mask, sdr_mask, htab_mask;
     target_ulong sr, vsid, vsid_mask, pgidx, page_mask;
-#if defined(TARGET_PPC64)
-    int attr;
-#endif
     int ds, vsid_sh, sdr_sh, pr, target_page_bits;
     int ret, ret2;
 
     pr = msr_pr;
 #if defined(TARGET_PPC64)
     if (env->mmu_model & POWERPC_MMU_64) {
+        int attr;
+
         LOG_MMU("Check SLBs\n");
         ret = slb_lookup(env, eaddr, &vsid, &page_mask, &attr,
                          &target_page_bits);
         if (ret < 0)
             return ret;
-        ctx->key = ((attr & 0x40) && (pr != 0)) ||
-            ((attr & 0x80) && (pr == 0)) ? 1 : 0;
+        ctx->key = !!(pr ? (attr & SLB_VSID_KP) : (attr & SLB_VSID_KS));
         ds = 0;
-        ctx->nx = attr & 0x10 ? 1 : 0;
+        ctx->nx = !!(attr & SLB_VSID_N);
         ctx->eaddr = eaddr;
         vsid_mask = 0x00003FFFFFFFFF80ULL;
         vsid_sh = 7;
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index 2bf9283..d512cb0 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -340,7 +340,6 @@ DEF_HELPER_1(74xx_tlbi, void, tl)
 DEF_HELPER_FLAGS_0(tlbia, TCG_CALL_CONST, void)
 DEF_HELPER_FLAGS_1(tlbie, TCG_CALL_CONST, void, tl)
 #if defined(TARGET_PPC64)
-DEF_HELPER_FLAGS_1(load_slb, TCG_CALL_CONST, tl, tl)
 DEF_HELPER_FLAGS_2(store_slb, TCG_CALL_CONST, void, tl, tl)
 DEF_HELPER_FLAGS_0(slbia, TCG_CALL_CONST, void)
 DEF_HELPER_FLAGS_1(slbie, TCG_CALL_CONST, void, tl)
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index 17e070a..bf41627 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -3746,14 +3746,11 @@ void helper_store_sr (target_ulong sr_num, target_ulong val)
 
 /* SLB management */
 #if defined(TARGET_PPC64)
-target_ulong helper_load_slb (target_ulong slb_nr)
-{
-    return ppc_load_slb(env, slb_nr);
-}
-
 void helper_store_slb (target_ulong rb, target_ulong rs)
 {
-    ppc_store_slb(env, rb, rs);
+    if (ppc_store_slb(env, rb, rs) < 0) {
+        helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL);
+    }
 }
 
 void helper_slbia (void)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 02/27] Allow qemu_devtree_setprop() to take arbitrary values
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 01/27] Clean up PowerPC SLB handling code David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 03/27] Add a hook to allow hypercalls to be emulated on PowerPC David Gibson
                   ` (25 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

From: David Gibson <dwg@au1.ibm.com>

Currently qemu_devtree_setprop() expects the new property value to be
given as a uint32_t *.  While property values consisting of u32s are
common, in general they can have any bytestring value.

Therefore, this patch alters the function to take a void * instead,
allowing callers to easily give anything as the property value.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 device_tree.c |    2 +-
 device_tree.h |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/device_tree.c b/device_tree.c
index 426a631..21be070 100644
--- a/device_tree.c
+++ b/device_tree.c
@@ -74,7 +74,7 @@ fail:
 }
 
 int qemu_devtree_setprop(void *fdt, const char *node_path,
-                         const char *property, uint32_t *val_array, int size)
+                         const char *property, void *val_array, int size)
 {
     int offset;
 
diff --git a/device_tree.h b/device_tree.h
index f05c4e7..cecd98f 100644
--- a/device_tree.h
+++ b/device_tree.h
@@ -17,7 +17,7 @@
 void *load_device_tree(const char *filename_path, int *sizep);
 
 int qemu_devtree_setprop(void *fdt, const char *node_path,
-                         const char *property, uint32_t *val_array, int size);
+                         const char *property, void *val_array, int size);
 int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
                               const char *property, uint32_t val);
 int qemu_devtree_setprop_string(void *fdt, const char *node_path,
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 03/27] Add a hook to allow hypercalls to be emulated on PowerPC
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 01/27] Clean up PowerPC SLB handling code David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 02/27] Allow qemu_devtree_setprop() to take arbitrary values David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 04/27] Implement PowerPC slbmfee and slbmfev instructions David Gibson
                   ` (24 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

PowerPC and POWER chips since the POWER4 and 970 have a special
hypervisor mode, and a corresponding form of the system call
instruction which traps to the hypervisor.

qemu currently has stub implementations of hypervisor mode.  That
is, the outline is there to allow qemu to run a PowerPC hypervisor
under emulation.  There are a number of details missing so this
won't actually work at present, but the idea is there.

What there is no provision at all, is for qemu to instead emulate
the hypervisor itself.  That is to have hypercalls trap into qemu
and their result be emulated from qemu, rather than running
hypervisor code within the emulated system.

Hypervisor hardware aware KVM implementations are in the works and
it would  be useful for debugging and development to also allow
full emulation of the same para-virtualized guests as such a KVM.

Therefore, this patch adds a hook which will allow a machine to
set up emulation of hypervisor calls.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 target-ppc/cpu.h    |    2 ++
 target-ppc/helper.c |    8 ++++++++
 2 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 124bbbf..36ca342 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -1646,4 +1646,6 @@ static inline void cpu_set_tls(CPUState *env, target_ulong newtls)
 #endif
 }
 
+extern void (*cpu_ppc_hypercall)(CPUState *);
+
 #endif /* !defined (__CPU_PPC_H__) */
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 2094ca3..452a35c 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -70,6 +70,10 @@
 #  define LOG_EXCP(...) do { } while (0)
 #endif
 
+/*****************************************************************************/
+/* PowerPC Hypercall emulation */
+
+void (*cpu_ppc_hypercall)(CPUState *);
 
 /*****************************************************************************/
 /* PowerPC MMU emulation */
@@ -2152,6 +2156,10 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
     case POWERPC_EXCP_SYSCALL:   /* System call exception                    */
         dump_syscall(env);
         lev = env->error_code;
+        if ((lev == 1) && cpu_ppc_hypercall) {
+            cpu_ppc_hypercall(env);
+            return;
+        }
         if (lev == 1 || (lpes0 == 0 && lpes1 == 0))
             new_msr |= (target_ulong)MSR_HVB;
         goto store_next;
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 04/27] Implement PowerPC slbmfee and slbmfev instructions
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (2 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 03/27] Add a hook to allow hypercalls to be emulated on PowerPC David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 05/27] Implement missing parts of the logic for the POWER PURR David Gibson
                   ` (23 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

For a 64-bit PowerPC target, qemu correctly implements translation
through the segment lookaside buffer.  Likewise it supports the
slbmte instruction which is used to load entries into the SLB.

However, it does not emulate the slbmfee and slbmfev instructions
which read SLB entries back into registers.  Because these are
only occasionally used in guests (mostly for debugging) we get
away with it.

However, given the recent SLB cleanups, it becomes quite easy to
implement these, and thereby allow, amongst other things, a guest
Linux to use xmon's command to dump the SLB.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 target-ppc/cpu.h       |    2 ++
 target-ppc/helper.c    |   26 ++++++++++++++++++++++++++
 target-ppc/helper.h    |    2 ++
 target-ppc/op_helper.c |   20 ++++++++++++++++++++
 target-ppc/translate.c |   31 ++++++++++++++++++++++++++++++-
 5 files changed, 80 insertions(+), 1 deletions(-)

diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 36ca342..f293f85 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -779,6 +779,8 @@ void ppc_store_asr (CPUPPCState *env, target_ulong value);
 target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr);
 target_ulong ppc_load_sr (CPUPPCState *env, int sr_nr);
 int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
+int ppc_load_slb_esid (CPUPPCState *env, target_ulong rb, target_ulong *rt);
+int ppc_load_slb_vsid (CPUPPCState *env, target_ulong rb, target_ulong *rt);
 #endif /* defined(TARGET_PPC64) */
 void ppc_store_sr (CPUPPCState *env, int srnum, target_ulong value);
 #endif /* !defined(CONFIG_USER_ONLY) */
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 452a35c..b9621d2 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -774,6 +774,32 @@ int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs)
 
     return 0;
 }
+
+int ppc_load_slb_esid (CPUPPCState *env, target_ulong rb, target_ulong *rt)
+{
+    int slot = rb & 0xfff;
+    ppc_slb_t *slb = &env->slb[slot];
+
+    if (slot >= env->slb_nr) {
+        return -1;
+    }
+
+    *rt = slb->esid;
+    return 0;
+}
+
+int ppc_load_slb_vsid (CPUPPCState *env, target_ulong rb, target_ulong *rt)
+{
+    int slot = rb & 0xfff;
+    ppc_slb_t *slb = &env->slb[slot];
+
+    if (slot >= env->slb_nr) {
+        return -1;
+    }
+
+    *rt = slb->vsid;
+    return 0;
+}
 #endif /* defined(TARGET_PPC64) */
 
 /* Perform segment based translation */
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index d512cb0..1a69cf8 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -341,6 +341,8 @@ DEF_HELPER_FLAGS_0(tlbia, TCG_CALL_CONST, void)
 DEF_HELPER_FLAGS_1(tlbie, TCG_CALL_CONST, void, tl)
 #if defined(TARGET_PPC64)
 DEF_HELPER_FLAGS_2(store_slb, TCG_CALL_CONST, void, tl, tl)
+DEF_HELPER_1(load_slb_esid, tl, tl)
+DEF_HELPER_1(load_slb_vsid, tl, tl)
 DEF_HELPER_FLAGS_0(slbia, TCG_CALL_CONST, void)
 DEF_HELPER_FLAGS_1(slbie, TCG_CALL_CONST, void, tl)
 #endif
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index bf41627..bdb1f17 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -3753,6 +3753,26 @@ void helper_store_slb (target_ulong rb, target_ulong rs)
     }
 }
 
+target_ulong helper_load_slb_esid (target_ulong rb)
+{
+    target_ulong rt;
+
+    if (ppc_load_slb_esid(env, rb, &rt) < 0) {
+        helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL);
+    }
+    return rt;
+}
+
+target_ulong helper_load_slb_vsid (target_ulong rb)
+{
+    target_ulong rt;
+
+    if (ppc_load_slb_vsid(env, rb, &rt) < 0) {
+        helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL);
+    }
+    return rt;
+}
+
 void helper_slbia (void)
 {
     ppc_slb_invalidate_all(env);
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 3d265e3..0b6bfe7 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -4227,6 +4227,33 @@ static void gen_slbmte(DisasContext *ctx)
 #endif
 }
 
+static void gen_slbmfee(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+    if (unlikely(!ctx->mem_idx)) {
+        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+        return;
+    }
+    gen_helper_load_slb_esid(cpu_gpr[rS(ctx->opcode)],
+                             cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
+
+static void gen_slbmfev(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+    if (unlikely(!ctx->mem_idx)) {
+        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+        return;
+    }
+    gen_helper_load_slb_vsid(cpu_gpr[rS(ctx->opcode)],
+                             cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
 #endif /* defined(TARGET_PPC64) */
 
 /***                      Lookaside buffer management                      ***/
@@ -8300,7 +8327,9 @@ GEN_HANDLER2(mfsrin_64b, "mfsrin", 0x1F, 0x13, 0x14, 0x001F0001,
 GEN_HANDLER2(mtsr_64b, "mtsr", 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT_64B),
 GEN_HANDLER2(mtsrin_64b, "mtsrin", 0x1F, 0x12, 0x07, 0x001F0001,
              PPC_SEGMENT_64B),
-GEN_HANDLER2(slbmte, "slbmte", 0x1F, 0x12, 0x0C, 0x00000000, PPC_SEGMENT_64B),
+GEN_HANDLER2(slbmte, "slbmte", 0x1F, 0x12, 0x0C, 0x001F0001, PPC_SEGMENT_64B),
+GEN_HANDLER2(slbmfee, "slbmfee", 0x1F, 0x13, 0x1C, 0x001F0001, PPC_SEGMENT_64B),
+GEN_HANDLER2(slbmfev, "slbmfev", 0x1F, 0x13, 0x1A, 0x001F0001, PPC_SEGMENT_64B),
 #endif
 GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA),
 GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x03FF0001, PPC_MEM_TLBIE),
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 05/27] Implement missing parts of the logic for the POWER PURR
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (3 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 04/27] Implement PowerPC slbmfee and slbmfev instructions David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 06/27] Correct ppc popcntb logic, implement popcntw and popcntd David Gibson
                   ` (22 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

The PURR (Processor Utilization Resource Register) is a register found
on recent POWER CPUs.  The guts of implementing it at least enough to
get by are already present in qemu, however some of the helper
functions needed to actually wire it up are missing.

This patch adds the necessary glue, so that the PURR can be wired up
when we implement newer POWER CPU targets which include it.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 target-ppc/helper.h         |    1 +
 target-ppc/op_helper.c      |    7 +++++++
 target-ppc/translate_init.c |    8 ++++++++
 3 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index 1a69cf8..2b4744d 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -376,6 +376,7 @@ DEF_HELPER_0(load_601_rtcu, tl)
 #if !defined(CONFIG_USER_ONLY)
 #if defined(TARGET_PPC64)
 DEF_HELPER_1(store_asr, void, tl)
+DEF_HELPER_0(load_purr, tl)
 #endif
 DEF_HELPER_1(store_sdr1, void, tl)
 DEF_HELPER_1(store_tbl, void, tl)
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index bdb1f17..aa2e8ba 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -86,6 +86,13 @@ target_ulong helper_load_atbu (void)
     return cpu_ppc_load_atbu(env);
 }
 
+#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
+target_ulong helper_load_purr (void)
+{
+    return (target_ulong)cpu_ppc_load_purr(env);
+}
+#endif
+
 target_ulong helper_load_601_rtcl (void)
 {
     return cpu_ppc601_load_rtcl(env);
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 7c08b1c..bca85d5 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -251,6 +251,14 @@ static void spr_write_atbu (void *opaque, int sprn, int gprn)
 {
     gen_helper_store_atbu(cpu_gpr[gprn]);
 }
+
+#if defined(TARGET_PPC64)
+__attribute__ (( unused ))
+static void spr_read_purr (void *opaque, int gprn, int sprn)
+{
+    gen_helper_load_purr(cpu_gpr[gprn]);
+}
+#endif
 #endif
 
 #if !defined(CONFIG_USER_ONLY)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 06/27] Correct ppc popcntb logic, implement popcntw and popcntd
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (4 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 05/27] Implement missing parts of the logic for the POWER PURR David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 07/27] Clean up slb_lookup() function David Gibson
                   ` (21 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

From: David Gibson <dwg@au1.ibm.com>

qemu already includes support for the popcntb instruction introduced
in POWER5 (although it doesn't actually allow you to choose POWER5).

However, the logic is slightly incorrect: it will generate results
truncated to 32-bits when the CPU is in 32-bit mode.  This is not
normal for powerpc - generally arithmetic instructions on a 64-bit
powerpc cpu will generate full 64 bit results, it's just that only the
low 32 bits will be significant for condition codes.

This patch corrects this nit, which actually simplifies the code slightly.

In addition, this patch implements the popcntw and popcntd
instructions added in POWER7, in preparation for allowing POWER7 as an
emulated CPU.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 target-ppc/cpu.h       |    2 +
 target-ppc/helper.h    |    3 +-
 target-ppc/op_helper.c |   55 +++++++++++++++++++++++++++++++++++++++++++----
 target-ppc/translate.c |   20 +++++++++++++----
 4 files changed, 69 insertions(+), 11 deletions(-)

diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index f293f85..37dde39 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -1505,6 +1505,8 @@ enum {
     PPC_DCRX           = 0x2000000000000000ULL,
     /* user-mode DCR access, implemented in PowerPC 460                      */
     PPC_DCRUX          = 0x4000000000000000ULL,
+    /* popcntw and popcntd instructions                                      */
+    PPC_POPCNTWD       = 0x8000000000000000ULL,
 };
 
 /*****************************************************************************/
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index 2b4744d..7c02be9 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -38,10 +38,11 @@ DEF_HELPER_2(mulldo, i64, i64, i64)
 
 DEF_HELPER_FLAGS_1(cntlzw, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
 DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
 DEF_HELPER_2(sraw, tl, tl, tl)
 #if defined(TARGET_PPC64)
 DEF_HELPER_FLAGS_1(cntlzd, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
-DEF_HELPER_FLAGS_1(popcntb_64, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_FLAGS_1(popcntd, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
 DEF_HELPER_2(srad, tl, tl, tl)
 #endif
 
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index aa2e8ba..b1b883d 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -499,6 +499,50 @@ target_ulong helper_srad (target_ulong value, target_ulong shift)
 }
 #endif
 
+#if defined(TARGET_PPC64)
+target_ulong helper_popcntb (target_ulong val)
+{
+    val = (val & 0x5555555555555555ULL) + ((val >>  1) &
+                                           0x5555555555555555ULL);
+    val = (val & 0x3333333333333333ULL) + ((val >>  2) &
+                                           0x3333333333333333ULL);
+    val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >>  4) &
+                                           0x0f0f0f0f0f0f0f0fULL);
+    return val;
+}
+
+target_ulong helper_popcntw (target_ulong val)
+{
+    val = (val & 0x5555555555555555ULL) + ((val >>  1) &
+                                           0x5555555555555555ULL);
+    val = (val & 0x3333333333333333ULL) + ((val >>  2) &
+                                           0x3333333333333333ULL);
+    val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >>  4) &
+                                           0x0f0f0f0f0f0f0f0fULL);
+    val = (val & 0x00ff00ff00ff00ffULL) + ((val >>  8) &
+                                           0x00ff00ff00ff00ffULL);
+    val = (val & 0x0000ffff0000ffffULL) + ((val >> 16) &
+                                           0x0000ffff0000ffffULL);
+    return val;
+}
+
+target_ulong helper_popcntd (target_ulong val)
+{
+    val = (val & 0x5555555555555555ULL) + ((val >>  1) &
+                                           0x5555555555555555ULL);
+    val = (val & 0x3333333333333333ULL) + ((val >>  2) &
+                                           0x3333333333333333ULL);
+    val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >>  4) &
+                                           0x0f0f0f0f0f0f0f0fULL);
+    val = (val & 0x00ff00ff00ff00ffULL) + ((val >>  8) &
+                                           0x00ff00ff00ff00ffULL);
+    val = (val & 0x0000ffff0000ffffULL) + ((val >> 16) &
+                                           0x0000ffff0000ffffULL);
+    val = (val & 0x00000000ffffffffULL) + ((val >> 32) &
+                                           0x00000000ffffffffULL);
+    return val;
+}
+#else
 target_ulong helper_popcntb (target_ulong val)
 {
     val = (val & 0x55555555) + ((val >>  1) & 0x55555555);
@@ -507,12 +551,13 @@ target_ulong helper_popcntb (target_ulong val)
     return val;
 }
 
-#if defined(TARGET_PPC64)
-target_ulong helper_popcntb_64 (target_ulong val)
+target_ulong helper_popcntw (target_ulong val)
 {
-    val = (val & 0x5555555555555555ULL) + ((val >>  1) & 0x5555555555555555ULL);
-    val = (val & 0x3333333333333333ULL) + ((val >>  2) & 0x3333333333333333ULL);
-    val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >>  4) & 0x0f0f0f0f0f0f0f0fULL);
+    val = (val & 0x55555555) + ((val >>  1) & 0x55555555);
+    val = (val & 0x33333333) + ((val >>  2) & 0x33333333);
+    val = (val & 0x0f0f0f0f) + ((val >>  4) & 0x0f0f0f0f);
+    val = (val & 0x00ff00ff) + ((val >>  8) & 0x00ff00ff);
+    val = (val & 0x0000ffff) + ((val >> 16) & 0x0000ffff);
     return val;
 }
 #endif
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 0b6bfe7..0547047 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -1483,13 +1483,21 @@ static void gen_xoris(DisasContext *ctx)
 /* popcntb : PowerPC 2.03 specification */
 static void gen_popcntb(DisasContext *ctx)
 {
+    gen_helper_popcntb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+}
+
+static void gen_popcntw(DisasContext *ctx)
+{
+    gen_helper_popcntw(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+}
+
 #if defined(TARGET_PPC64)
-    if (ctx->sf_mode)
-        gen_helper_popcntb_64(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
-    else
-#endif
-        gen_helper_popcntb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+/* popcntd: PowerPC 2.06 specification */
+static void gen_popcntd(DisasContext *ctx)
+{
+    gen_helper_popcntd(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
 }
+#endif
 
 #if defined(TARGET_PPC64)
 /* extsw & extsw. */
@@ -8226,7 +8234,9 @@ GEN_HANDLER(oris, 0x19, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
 GEN_HANDLER(xori, 0x1A, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
 GEN_HANDLER(xoris, 0x1B, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
 GEN_HANDLER(popcntb, 0x1F, 0x03, 0x03, 0x0000F801, PPC_POPCNTB),
+GEN_HANDLER(popcntw, 0x1F, 0x1A, 0x0b, 0x0000F801, PPC_POPCNTWD),
 #if defined(TARGET_PPC64)
+GEN_HANDLER(popcntd, 0x1F, 0x1A, 0x0F, 0x0000F801, PPC_POPCNTWD),
 GEN_HANDLER(cntlzd, 0x1F, 0x1A, 0x01, 0x00000000, PPC_64B),
 #endif
 GEN_HANDLER(rlwimi, 0x14, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 07/27] Clean up slb_lookup() function
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (5 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 06/27] Correct ppc popcntb logic, implement popcntw and popcntd David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 08/27] Parse SDR1 on mtspr instead of at translate time David Gibson
                   ` (20 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

The slb_lookup() function, used in the ppc translation path returns a
number of slb entry fields in reference parameters.  However, only one
of the two callers of slb_lookup() actually wants this information.

This patch, therefore, makes slb_lookup() return a simple pointer to the
located SLB entry (or NULL), and the caller which needs the fields can
extract them itself.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 target-ppc/helper.c |   45 ++++++++++++++++++---------------------------
 1 files changed, 18 insertions(+), 27 deletions(-)

diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index b9621d2..7ca33cb 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -676,9 +676,7 @@ static inline int find_pte(CPUState *env, mmu_ctx_t *ctx, int h, int rw,
 }
 
 #if defined(TARGET_PPC64)
-static inline int slb_lookup(CPUPPCState *env, target_ulong eaddr,
-                             target_ulong *vsid, target_ulong *page_mask,
-                             int *attr, int *target_page_bits)
+static inline ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr)
 {
     uint64_t esid;
     int n;
@@ -693,19 +691,11 @@ static inline int slb_lookup(CPUPPCState *env, target_ulong eaddr,
         LOG_SLB("%s: slot %d %016" PRIx64 " %016"
                     PRIx64 "\n", __func__, n, slb->esid, slb->vsid);
         if (slb->esid == esid) {
-            *vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
-            *page_mask = ~SEGMENT_MASK_256M;
-            *attr = slb->vsid & SLB_VSID_ATTR;
-            if (target_page_bits) {
-                *target_page_bits = (slb->vsid & SLB_VSID_L)
-                    ? TARGET_PAGE_BITS_16M
-                    : TARGET_PAGE_BITS;
-            }
-            return n;
+            return slb;
         }
     }
 
-    return -5;
+    return NULL;
 }
 
 void ppc_slb_invalidate_all (CPUPPCState *env)
@@ -732,18 +722,13 @@ void ppc_slb_invalidate_all (CPUPPCState *env)
 
 void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0)
 {
-    target_ulong vsid, page_mask;
-    int attr;
-    int n;
     ppc_slb_t *slb;
 
-    n = slb_lookup(env, T0, &vsid, &page_mask, &attr, NULL);
-    if (n < 0) {
+    slb = slb_lookup(env, T0);
+    if (!slb) {
         return;
     }
 
-    slb = &env->slb[n];
-
     if (slb->esid & SLB_ESID_V) {
         slb->esid &= ~SLB_ESID_V;
 
@@ -822,16 +807,22 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
     pr = msr_pr;
 #if defined(TARGET_PPC64)
     if (env->mmu_model & POWERPC_MMU_64) {
-        int attr;
+        ppc_slb_t *slb;
 
         LOG_MMU("Check SLBs\n");
-        ret = slb_lookup(env, eaddr, &vsid, &page_mask, &attr,
-                         &target_page_bits);
-        if (ret < 0)
-            return ret;
-        ctx->key = !!(pr ? (attr & SLB_VSID_KP) : (attr & SLB_VSID_KS));
+        slb = slb_lookup(env, eaddr);
+        if (!slb) {
+            return -5;
+        }
+
+        vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
+        page_mask = ~SEGMENT_MASK_256M;
+        target_page_bits = (slb->vsid & SLB_VSID_L)
+            ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS;
+        ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP)
+                      : (slb->vsid & SLB_VSID_KS));
         ds = 0;
-        ctx->nx = !!(attr & SLB_VSID_N);
+        ctx->nx = !!(slb->vsid & SLB_VSID_N);
         ctx->eaddr = eaddr;
         vsid_mask = 0x00003FFFFFFFFF80ULL;
         vsid_sh = 7;
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 08/27] Parse SDR1 on mtspr instead of at translate time
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (6 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 07/27] Clean up slb_lookup() function David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 09/27] Use "hash" more consistently in ppc mmu code David Gibson
                   ` (19 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

On ppc machines with hash table MMUs, the special purpose register SDR1
contains both the base address of the encoded size (hashed) page tables.

At present, we interpret the SDR1 value within the address translation
path.  But because the encodings of the size for 32-bit and 64-bit are
different this makes for a confusing branch on the MMU type with a bunch
of curly shifts and masks in the middle of the translate path.

This patch cleans things up by moving the interpretation on SDR1 into the
helper function handling the write to the register.  This leaves a simple
pre-sanitized base address and mask for the hash table in the CPUState
structure which is easier to work with in the translation path.

This makes the translation path more readable.  It addresses the FIXME
comment currently in the mtsdr1 helper, by validating the SDR1 value during
interpretation.  Finally it opens the way for emulating a pSeries-style
partition where the hash table used for translation is not mapped into
the guests's RAM.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 monitor.c                   |    2 +-
 target-ppc/cpu.h            |   11 +++++-
 target-ppc/helper.c         |   80 ++++++++++++++++++++++++-------------------
 target-ppc/kvm.c            |    2 +-
 target-ppc/machine.c        |    6 ++-
 target-ppc/translate.c      |    2 +-
 target-ppc/translate_init.c |    7 +---
 7 files changed, 63 insertions(+), 47 deletions(-)

diff --git a/monitor.c b/monitor.c
index 76a8207..f1a08dc 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3462,7 +3462,7 @@ static const MonitorDef monitor_defs[] = {
     { "asr", offsetof(CPUState, asr) },
 #endif
     /* Segment registers */
-    { "sdr1", offsetof(CPUState, sdr1) },
+    { "sdr1", offsetof(CPUState, spr[SPR_SDR1]) },
     { "sr0", offsetof(CPUState, sr[0]) },
     { "sr1", offsetof(CPUState, sr[1]) },
     { "sr2", offsetof(CPUState, sr[2]) },
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 37dde39..ead4566 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -359,6 +359,14 @@ union ppc_tlb_t {
 };
 #endif
 
+#define SDR_32_HTABORG         0xFFFF0000UL
+#define SDR_32_HTABMASK        0x000001FFUL
+
+#if defined(TARGET_PPC64)
+#define SDR_64_HTABORG         0xFFFFFFFFFFFC0000ULL
+#define SDR_64_HTABSIZE        0x000000000000001FULL
+#endif /* defined(TARGET_PPC64 */
+
 typedef struct ppc_slb_t ppc_slb_t;
 struct ppc_slb_t {
     uint64_t esid;
@@ -642,7 +650,8 @@ struct CPUPPCState {
     int slb_nr;
 #endif
     /* segment registers */
-    target_ulong sdr1;
+    target_phys_addr_t htab_base;
+    target_phys_addr_t htab_mask;
     target_ulong sr[32];
     /* BATs */
     int nb_BATs;
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 7ca33cb..68d2d9c 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -788,20 +788,19 @@ int ppc_load_slb_vsid (CPUPPCState *env, target_ulong rb, target_ulong *rt)
 #endif /* defined(TARGET_PPC64) */
 
 /* Perform segment based translation */
-static inline target_phys_addr_t get_pgaddr(target_phys_addr_t sdr1,
-                                            int sdr_sh,
-                                            target_phys_addr_t hash,
-                                            target_phys_addr_t mask)
+static inline target_phys_addr_t get_pgaddr(target_phys_addr_t htab_base,
+                                            target_phys_addr_t htab_mask,
+                                            target_phys_addr_t hash)
 {
-    return (sdr1 & ((target_phys_addr_t)(-1ULL) << sdr_sh)) | (hash & mask);
+    return htab_base | (hash & htab_mask);
 }
 
 static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
                               target_ulong eaddr, int rw, int type)
 {
-    target_phys_addr_t sdr, hash, mask, sdr_mask, htab_mask;
+    target_phys_addr_t hash;
     target_ulong sr, vsid, vsid_mask, pgidx, page_mask;
-    int ds, vsid_sh, sdr_sh, pr, target_page_bits;
+    int ds, vsid_sh, pr, target_page_bits;
     int ret, ret2;
 
     pr = msr_pr;
@@ -826,8 +825,6 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
         ctx->eaddr = eaddr;
         vsid_mask = 0x00003FFFFFFFFF80ULL;
         vsid_sh = 7;
-        sdr_sh = 18;
-        sdr_mask = 0x3FF80;
     } else
 #endif /* defined(TARGET_PPC64) */
     {
@@ -840,8 +837,6 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
         vsid = sr & 0x00FFFFFF;
         vsid_mask = 0x01FFFFC0;
         vsid_sh = 6;
-        sdr_sh = 16;
-        sdr_mask = 0xFFC0;
         target_page_bits = TARGET_PAGE_BITS;
         LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip="
                 TARGET_FMT_lx " lr=" TARGET_FMT_lx
@@ -857,29 +852,26 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
         if (type != ACCESS_CODE || ctx->nx == 0) {
             /* Page address translation */
             /* Primary table address */
-            sdr = env->sdr1;
             pgidx = (eaddr & page_mask) >> target_page_bits;
 #if defined(TARGET_PPC64)
             if (env->mmu_model & POWERPC_MMU_64) {
-                htab_mask = 0x0FFFFFFF >> (28 - (sdr & 0x1F));
                 /* XXX: this is false for 1 TB segments */
                 hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask;
             } else
 #endif
             {
-                htab_mask = sdr & 0x000001FF;
                 hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask;
             }
-            mask = (htab_mask << sdr_sh) | sdr_mask;
-            LOG_MMU("sdr " TARGET_FMT_plx " sh %d hash " TARGET_FMT_plx
-                    " mask " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
-                    sdr, sdr_sh, hash, mask, page_mask);
-            ctx->pg_addr[0] = get_pgaddr(sdr, sdr_sh, hash, mask);
+            LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
+                    " hash " TARGET_FMT_plx "\n",
+                    env->htab_base, env->htab_mask, hash);
+            ctx->pg_addr[0] = get_pgaddr(env->htab_base, env->htab_mask, hash);
             /* Secondary table address */
             hash = (~hash) & vsid_mask;
-            LOG_MMU("sdr " TARGET_FMT_plx " sh %d hash " TARGET_FMT_plx
-                    " mask " TARGET_FMT_plx "\n", sdr, sdr_sh, hash, mask);
-            ctx->pg_addr[1] = get_pgaddr(sdr, sdr_sh, hash, mask);
+            LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
+                    " hash " TARGET_FMT_plx "\n",
+                    env->htab_base, env->htab_mask, hash);
+            ctx->pg_addr[1] = get_pgaddr(env->htab_base, env->htab_mask, hash);
 #if defined(TARGET_PPC64)
             if (env->mmu_model & POWERPC_MMU_64) {
                 /* Only 5 bits of the page index are used in the AVPN */
@@ -901,19 +893,22 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
                 /* Software TLB search */
                 ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type);
             } else {
-                LOG_MMU("0 sdr1=" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " "
-                        "api=" TARGET_FMT_lx " hash=" TARGET_FMT_plx
-                        " pg_addr=" TARGET_FMT_plx "\n",
-                        sdr, vsid, pgidx, hash, ctx->pg_addr[0]);
+                LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
+                        " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
+                        " hash=" TARGET_FMT_plx " pg_addr=" TARGET_FMT_plx "\n",
+                        env->htab_base, env->htab_mask, vsid, pgidx, hash,
+                        ctx->pg_addr[0]);
                 /* Primary table lookup */
                 ret = find_pte(env, ctx, 0, rw, type, target_page_bits);
                 if (ret < 0) {
                     /* Secondary table lookup */
                     if (eaddr != 0xEFFFFFFF)
-                        LOG_MMU("1 sdr1=" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " "
-                                "api=" TARGET_FMT_lx " hash=" TARGET_FMT_plx
-                                " pg_addr=" TARGET_FMT_plx "\n", sdr, vsid,
-                                pgidx, hash, ctx->pg_addr[1]);
+                        LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
+                                " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
+                                " hash=" TARGET_FMT_plx " pg_addr="
+                                TARGET_FMT_plx "\n", env->htab_base,
+                                env->htab_mask, vsid, pgidx, hash,
+                                ctx->pg_addr[1]);
                     ret2 = find_pte(env, ctx, 1, rw, type,
                                     target_page_bits);
                     if (ret2 != -1)
@@ -1919,11 +1914,26 @@ void ppc_store_asr (CPUPPCState *env, target_ulong value)
 void ppc_store_sdr1 (CPUPPCState *env, target_ulong value)
 {
     LOG_MMU("%s: " TARGET_FMT_lx "\n", __func__, value);
-    if (env->sdr1 != value) {
-        /* XXX: for PowerPC 64, should check that the HTABSIZE value
-         *      is <= 28
-         */
-        env->sdr1 = value;
+    if (env->spr[SPR_SDR1] != value) {
+        env->spr[SPR_SDR1] = value;
+#if defined(TARGET_PPC64)
+        if (env->mmu_model & POWERPC_MMU_64) {
+            target_ulong htabsize = value & SDR_64_HTABSIZE;
+
+            if (htabsize > 28) {
+                fprintf(stderr, "Invalid HTABSIZE 0x" TARGET_FMT_lx
+                        " stored in SDR1\n", htabsize);
+                htabsize = 28;
+            }
+            env->htab_mask = (1ULL << (htabsize + 18)) - 1;
+            env->htab_base = value & SDR_64_HTABORG;
+        } else
+#endif /* defined(TARGET_PPC64) */
+        {
+            /* FIXME: Should check for valid HTABMASK values */
+            env->htab_mask = ((value & SDR_32_HTABMASK) << 16) | 0xFFFF;
+            env->htab_base = value & SDR_32_HTABORG;
+        }
         tlb_flush(env, 1);
     }
 }
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index 0e2e67b..2cfb24b 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -169,7 +169,7 @@ int kvm_arch_get_registers(CPUState *env)
 
 #ifdef KVM_CAP_PPC_SEGSTATE
     if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_SEGSTATE)) {
-        env->sdr1 = sregs.u.s.sdr1;
+        ppc_store_sdr1(env, sregs.u.s.sdr1);
 
         /* Sync SLB */
 #ifdef TARGET_PPC64
diff --git a/target-ppc/machine.c b/target-ppc/machine.c
index 67de951..0c1986e 100644
--- a/target-ppc/machine.c
+++ b/target-ppc/machine.c
@@ -37,7 +37,7 @@ void cpu_save(QEMUFile *f, void *opaque)
     qemu_put_betls(f, &env->asr);
     qemu_put_sbe32s(f, &env->slb_nr);
 #endif
-    qemu_put_betls(f, &env->sdr1);
+    qemu_put_betls(f, &env->spr[SPR_SDR1]);
     for (i = 0; i < 32; i++)
         qemu_put_betls(f, &env->sr[i]);
     for (i = 0; i < 2; i++)
@@ -93,6 +93,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
 {
     CPUState *env = (CPUState *)opaque;
     unsigned int i, j;
+    target_ulong sdr1;
 
     for (i = 0; i < 32; i++)
         qemu_get_betls(f, &env->gpr[i]);
@@ -124,7 +125,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
     qemu_get_betls(f, &env->asr);
     qemu_get_sbe32s(f, &env->slb_nr);
 #endif
-    qemu_get_betls(f, &env->sdr1);
+    qemu_get_betls(f, &sdr1);
     for (i = 0; i < 32; i++)
         qemu_get_betls(f, &env->sr[i]);
     for (i = 0; i < 2; i++)
@@ -152,6 +153,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
 #endif
     for (i = 0; i < 1024; i++)
         qemu_get_betls(f, &env->spr[i]);
+    ppc_store_sdr1(env, sdr1);
     qemu_get_be32s(f, &env->vscr);
     qemu_get_be64s(f, &env->spe_acc);
     qemu_get_be32s(f, &env->spe_fscr);
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 0547047..b77f666 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -9126,7 +9126,7 @@ void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf,
 #if !defined(CONFIG_USER_ONLY)
     cpu_fprintf(f, "SRR0 " TARGET_FMT_lx " SRR1 " TARGET_FMT_lx " SDR1 "
                 TARGET_FMT_lx "\n", env->spr[SPR_SRR0], env->spr[SPR_SRR1],
-                env->sdr1);
+                env->spr[SPR_SDR1]);
 #endif
 
 #undef RGPL
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index bca85d5..6366424 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -343,11 +343,6 @@ static void spr_write_dbatl_h (void *opaque, int sprn, int gprn)
 }
 
 /* SDR1 */
-static void spr_read_sdr1 (void *opaque, int gprn, int sprn)
-{
-    tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUState, sdr1));
-}
-
 static void spr_write_sdr1 (void *opaque, int sprn, int gprn)
 {
     gen_helper_store_sdr1(cpu_gpr[gprn]);
@@ -671,7 +666,7 @@ static void gen_spr_ne_601 (CPUPPCState *env)
     /* Memory management */
     spr_register(env, SPR_SDR1, "SDR1",
                  SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_sdr1, &spr_write_sdr1,
+                 &spr_read_generic, &spr_write_sdr1,
                  0x00000000);
 }
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 09/27] Use "hash" more consistently in ppc mmu code
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (7 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 08/27] Parse SDR1 on mtspr instead of at translate time David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 10/27] Better factor the ppc hash translation path David Gibson
                   ` (18 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

Currently, get_segment() has a variable called hash.  However it doesn't
(quite) get the hash value for the ppc hashed page table.  Instead it
gets the hash shifted - effectively the offset of the hash bucket within
the hash page table.

As well, as being different to the normal use of plain "hash" in the
architecture documentation, this usage necessitates some awkward 32/64
dependent masks and shifts which clutter up the path in get_segment().

This patch alters the code to use raw hash values through get_segment()
including storing raw hashes instead of pte group offsets in the ctx
structure.  This cleans up the path noticeably.

This does necessitate 32/64 dependent shifts when the hash values are
taken out of the ctx structure and used, but those paths already have
32/64 bit variants so this is less awkward than it was in get_segment().

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 target-ppc/cpu.h    |    5 ++-
 target-ppc/helper.c |   95 ++++++++++++++++++++++++--------------------------
 2 files changed, 50 insertions(+), 50 deletions(-)

diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index ead4566..cee1057 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -367,6 +367,9 @@ union ppc_tlb_t {
 #define SDR_64_HTABSIZE        0x000000000000001FULL
 #endif /* defined(TARGET_PPC64 */
 
+#define HASH_PTE_SIZE_32       8
+#define HASH_PTE_SIZE_64       16
+
 typedef struct ppc_slb_t ppc_slb_t;
 struct ppc_slb_t {
     uint64_t esid;
@@ -744,7 +747,7 @@ struct mmu_ctx_t {
     target_phys_addr_t raddr;      /* Real address              */
     target_phys_addr_t eaddr;      /* Effective address         */
     int prot;                      /* Protection bits           */
-    target_phys_addr_t pg_addr[2]; /* PTE tables base addresses */
+    target_phys_addr_t hash[2];    /* Pagetable hash values     */
     target_ulong ptem;             /* Virtual segment ID | API  */
     int key;                       /* Access key                */
     int nx;                        /* Non-execute area          */
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 68d2d9c..0efa2a8 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -567,21 +567,30 @@ static inline int get_bat(CPUState *env, mmu_ctx_t *ctx, target_ulong virtual,
     return ret;
 }
 
+static inline target_phys_addr_t get_pteg_offset(CPUState *env,
+                                                 target_phys_addr_t hash,
+                                                 int pte_size)
+{
+    return (hash * pte_size * 8) & env->htab_mask;
+}
+
 /* PTE table lookup */
-static inline int _find_pte(mmu_ctx_t *ctx, int is_64b, int h, int rw,
-                            int type, int target_page_bits)
+static inline int _find_pte(CPUState *env, mmu_ctx_t *ctx, int is_64b, int h,
+                            int rw, int type, int target_page_bits)
 {
-    target_ulong base, pte0, pte1;
+    target_phys_addr_t pteg_off;
+    target_ulong pte0, pte1;
     int i, good = -1;
     int ret, r;
 
     ret = -1; /* No entry found */
-    base = ctx->pg_addr[h];
+    pteg_off = get_pteg_offset(env, ctx->hash[h],
+                               is_64b ? HASH_PTE_SIZE_64 : HASH_PTE_SIZE_32);
     for (i = 0; i < 8; i++) {
 #if defined(TARGET_PPC64)
         if (is_64b) {
-            pte0 = ldq_phys(base + (i * 16));
-            pte1 = ldq_phys(base + (i * 16) + 8);
+            pte0 = ldq_phys(env->htab_base + pteg_off + (i * 16));
+            pte1 = ldq_phys(env->htab_base + pteg_off + (i * 16) + 8);
 
             /* We have a TLB that saves 4K pages, so let's
              * split a huge page to 4k chunks */
@@ -592,17 +601,17 @@ static inline int _find_pte(mmu_ctx_t *ctx, int is_64b, int h, int rw,
             r = pte64_check(ctx, pte0, pte1, h, rw, type);
             LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " "
                     TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n",
-                    base + (i * 16), pte0, pte1, (int)(pte0 & 1), h,
+                    pteg_base + (i * 16), pte0, pte1, (int)(pte0 & 1), h,
                     (int)((pte0 >> 1) & 1), ctx->ptem);
         } else
 #endif
         {
-            pte0 = ldl_phys(base + (i * 8));
-            pte1 =  ldl_phys(base + (i * 8) + 4);
+            pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8));
+            pte1 =  ldl_phys(env->htab_base + pteg_off + (i * 8) + 4);
             r = pte32_check(ctx, pte0, pte1, h, rw, type);
             LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " "
                     TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n",
-                    base + (i * 8), pte0, pte1, (int)(pte0 >> 31), h,
+                    pteg_base + (i * 8), pte0, pte1, (int)(pte0 >> 31), h,
                     (int)((pte0 >> 6) & 1), ctx->ptem);
         }
         switch (r) {
@@ -638,11 +647,13 @@ static inline int _find_pte(mmu_ctx_t *ctx, int is_64b, int h, int rw,
         if (pte_update_flags(ctx, &pte1, ret, rw) == 1) {
 #if defined(TARGET_PPC64)
             if (is_64b) {
-                stq_phys_notdirty(base + (good * 16) + 8, pte1);
+                stq_phys_notdirty(env->htab_base + pteg_off + (good * 16) + 8,
+                                  pte1);
             } else
 #endif
             {
-                stl_phys_notdirty(base + (good * 8) + 4, pte1);
+                stl_phys_notdirty(env->htab_base + pteg_off + (good * 8) + 4,
+                                  pte1);
             }
         }
     }
@@ -650,17 +661,17 @@ static inline int _find_pte(mmu_ctx_t *ctx, int is_64b, int h, int rw,
     return ret;
 }
 
-static inline int find_pte32(mmu_ctx_t *ctx, int h, int rw, int type,
-                             int target_page_bits)
+static inline int find_pte32(CPUState *env, mmu_ctx_t *ctx, int h, int rw,
+                             int type, int target_page_bits)
 {
-    return _find_pte(ctx, 0, h, rw, type, target_page_bits);
+    return _find_pte(env, ctx, 0, h, rw, type, target_page_bits);
 }
 
 #if defined(TARGET_PPC64)
-static inline int find_pte64(mmu_ctx_t *ctx, int h, int rw, int type,
-                             int target_page_bits)
+static inline int find_pte64(CPUState *env, mmu_ctx_t *ctx, int h, int rw,
+                             int type, int target_page_bits)
 {
-    return _find_pte(ctx, 1, h, rw, type, target_page_bits);
+    return _find_pte(env, ctx, 1, h, rw, type, target_page_bits);
 }
 #endif
 
@@ -669,10 +680,10 @@ static inline int find_pte(CPUState *env, mmu_ctx_t *ctx, int h, int rw,
 {
 #if defined(TARGET_PPC64)
     if (env->mmu_model & POWERPC_MMU_64)
-        return find_pte64(ctx, h, rw, type, target_page_bits);
+        return find_pte64(env, ctx, h, rw, type, target_page_bits);
 #endif
 
-    return find_pte32(ctx, h, rw, type, target_page_bits);
+    return find_pte32(env, ctx, h, rw, type, target_page_bits);
 }
 
 #if defined(TARGET_PPC64)
@@ -788,19 +799,12 @@ int ppc_load_slb_vsid (CPUPPCState *env, target_ulong rb, target_ulong *rt)
 #endif /* defined(TARGET_PPC64) */
 
 /* Perform segment based translation */
-static inline target_phys_addr_t get_pgaddr(target_phys_addr_t htab_base,
-                                            target_phys_addr_t htab_mask,
-                                            target_phys_addr_t hash)
-{
-    return htab_base | (hash & htab_mask);
-}
-
 static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
                               target_ulong eaddr, int rw, int type)
 {
     target_phys_addr_t hash;
-    target_ulong sr, vsid, vsid_mask, pgidx, page_mask;
-    int ds, vsid_sh, pr, target_page_bits;
+    target_ulong sr, vsid, pgidx, page_mask;
+    int ds, pr, target_page_bits;
     int ret, ret2;
 
     pr = msr_pr;
@@ -823,8 +827,6 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
         ds = 0;
         ctx->nx = !!(slb->vsid & SLB_VSID_N);
         ctx->eaddr = eaddr;
-        vsid_mask = 0x00003FFFFFFFFF80ULL;
-        vsid_sh = 7;
     } else
 #endif /* defined(TARGET_PPC64) */
     {
@@ -835,8 +837,6 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
         ds = sr & 0x80000000 ? 1 : 0;
         ctx->nx = sr & 0x10000000 ? 1 : 0;
         vsid = sr & 0x00FFFFFF;
-        vsid_mask = 0x01FFFFC0;
-        vsid_sh = 6;
         target_page_bits = TARGET_PAGE_BITS;
         LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip="
                 TARGET_FMT_lx " lr=" TARGET_FMT_lx
@@ -851,27 +851,22 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
         /* Check if instruction fetch is allowed, if needed */
         if (type != ACCESS_CODE || ctx->nx == 0) {
             /* Page address translation */
-            /* Primary table address */
             pgidx = (eaddr & page_mask) >> target_page_bits;
 #if defined(TARGET_PPC64)
             if (env->mmu_model & POWERPC_MMU_64) {
                 /* XXX: this is false for 1 TB segments */
-                hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask;
+                hash = vsid ^ pgidx;
             } else
 #endif
             {
-                hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask;
+                hash = vsid ^ pgidx;
             }
             LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
                     " hash " TARGET_FMT_plx "\n",
                     env->htab_base, env->htab_mask, hash);
-            ctx->pg_addr[0] = get_pgaddr(env->htab_base, env->htab_mask, hash);
-            /* Secondary table address */
-            hash = (~hash) & vsid_mask;
-            LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
-                    " hash " TARGET_FMT_plx "\n",
-                    env->htab_base, env->htab_mask, hash);
-            ctx->pg_addr[1] = get_pgaddr(env->htab_base, env->htab_mask, hash);
+            ctx->hash[0] = hash;
+            ctx->hash[1] = ~hash;
+
 #if defined(TARGET_PPC64)
             if (env->mmu_model & POWERPC_MMU_64) {
                 /* Only 5 bits of the page index are used in the AVPN */
@@ -895,9 +890,9 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
             } else {
                 LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
                         " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
-                        " hash=" TARGET_FMT_plx " pg_addr=" TARGET_FMT_plx "\n",
-                        env->htab_base, env->htab_mask, vsid, pgidx, hash,
-                        ctx->pg_addr[0]);
+                        " hash=" TARGET_FMT_plx "\n",
+                        env->htab_base, env->htab_mask, vsid, pgidx,
+                        ctx->hash[0]);
                 /* Primary table lookup */
                 ret = find_pte(env, ctx, 0, rw, type, target_page_bits);
                 if (ret < 0) {
@@ -908,7 +903,7 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
                                 " hash=" TARGET_FMT_plx " pg_addr="
                                 TARGET_FMT_plx "\n", env->htab_base,
                                 env->htab_mask, vsid, pgidx, hash,
-                                ctx->pg_addr[1]);
+                                ctx->hash[1]);
                     ret2 = find_pte(env, ctx, 1, rw, type,
                                     target_page_bits);
                     if (ret2 != -1)
@@ -1460,8 +1455,10 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
                     env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem;
                 tlb_miss:
                     env->error_code |= ctx.key << 19;
-                    env->spr[SPR_HASH1] = ctx.pg_addr[0];
-                    env->spr[SPR_HASH2] = ctx.pg_addr[1];
+                    env->spr[SPR_HASH1] = env->htab_base +
+                        get_pteg_offset(env, ctx.hash[0], HASH_PTE_SIZE_32);
+                    env->spr[SPR_HASH2] = env->htab_base +
+                        get_pteg_offset(env, ctx.hash[1], HASH_PTE_SIZE_32);
                     break;
                 case POWERPC_MMU_SOFT_74xx:
                     if (rw == 1) {
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 10/27] Better factor the ppc hash translation path
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (8 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 09/27] Use "hash" more consistently in ppc mmu code David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 11/27] Support 1T segments on ppc David Gibson
                   ` (17 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

Currently the path handling hash page table translation in get_segment()
has a mix of common and 32 or 64 bit specific code.  However the
division is not done terribly well which results in a lot of messy code
flipping between common and divided paths.

This patch improves the organization, consolidating several divided paths
into one.  This in turn allows simplification of some code in
get_segment(), removing a number of ugly interim variables.

This new factorization will also make it easier to add support for the 1T
segments added in newer CPUs.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 target-ppc/cpu.h    |    1 +
 target-ppc/helper.c |   67 ++++++++++++++------------------------------------
 2 files changed, 20 insertions(+), 48 deletions(-)

diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index cee1057..fd2dfcd 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -386,6 +386,7 @@ struct ppc_slb_t {
 #define SLB_VSID_B              0xc000000000000000ULL
 #define SLB_VSID_B_256M         0x0000000000000000ULL
 #define SLB_VSID_VSID           0x3FFFFFFFFFFFF000ULL
+#define SLB_VSID_PTEM           (SLB_VSID_B | SLB_VSID_VSID)
 #define SLB_VSID_KS             0x0000000000000800ULL
 #define SLB_VSID_KP             0x0000000000000400ULL
 #define SLB_VSID_N              0x0000000000000200ULL /* no-execute */
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 0efa2a8..ae8001c 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -661,29 +661,15 @@ static inline int _find_pte(CPUState *env, mmu_ctx_t *ctx, int is_64b, int h,
     return ret;
 }
 
-static inline int find_pte32(CPUState *env, mmu_ctx_t *ctx, int h, int rw,
-                             int type, int target_page_bits)
-{
-    return _find_pte(env, ctx, 0, h, rw, type, target_page_bits);
-}
-
-#if defined(TARGET_PPC64)
-static inline int find_pte64(CPUState *env, mmu_ctx_t *ctx, int h, int rw,
-                             int type, int target_page_bits)
-{
-    return _find_pte(env, ctx, 1, h, rw, type, target_page_bits);
-}
-#endif
-
 static inline int find_pte(CPUState *env, mmu_ctx_t *ctx, int h, int rw,
                            int type, int target_page_bits)
 {
 #if defined(TARGET_PPC64)
     if (env->mmu_model & POWERPC_MMU_64)
-        return find_pte64(env, ctx, h, rw, type, target_page_bits);
+        return _find_pte(env, ctx, 1, h, rw, type, target_page_bits);
 #endif
 
-    return find_pte32(env, ctx, h, rw, type, target_page_bits);
+    return _find_pte(env, ctx, 0, h, rw, type, target_page_bits);
 }
 
 #if defined(TARGET_PPC64)
@@ -803,14 +789,16 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
                               target_ulong eaddr, int rw, int type)
 {
     target_phys_addr_t hash;
-    target_ulong sr, vsid, pgidx, page_mask;
+    target_ulong vsid;
     int ds, pr, target_page_bits;
     int ret, ret2;
 
     pr = msr_pr;
+    ctx->eaddr = eaddr;
 #if defined(TARGET_PPC64)
     if (env->mmu_model & POWERPC_MMU_64) {
         ppc_slb_t *slb;
+        target_ulong pageaddr;
 
         LOG_MMU("Check SLBs\n");
         slb = slb_lookup(env, eaddr);
@@ -819,19 +807,24 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
         }
 
         vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
-        page_mask = ~SEGMENT_MASK_256M;
         target_page_bits = (slb->vsid & SLB_VSID_L)
             ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS;
         ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP)
                       : (slb->vsid & SLB_VSID_KS));
         ds = 0;
         ctx->nx = !!(slb->vsid & SLB_VSID_N);
-        ctx->eaddr = eaddr;
+
+        pageaddr = eaddr & ((1ULL << 28) - (1ULL << target_page_bits));
+        /* XXX: this is false for 1 TB segments */
+        hash = vsid ^ (pageaddr >> target_page_bits);
+        /* Only 5 bits of the page index are used in the AVPN */
+        ctx->ptem = (slb->vsid & SLB_VSID_PTEM) | ((pageaddr >> 16) & 0x0F80);
     } else
 #endif /* defined(TARGET_PPC64) */
     {
+        target_ulong sr, pgidx;
+
         sr = env->sr[eaddr >> 28];
-        page_mask = 0x0FFFFFFF;
         ctx->key = (((sr & 0x20000000) && (pr != 0)) ||
                     ((sr & 0x40000000) && (pr == 0))) ? 1 : 0;
         ds = sr & 0x80000000 ? 1 : 0;
@@ -843,6 +836,9 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
                 " ir=%d dr=%d pr=%d %d t=%d\n",
                 eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir,
                 (int)msr_dr, pr != 0 ? 1 : 0, rw, type);
+        pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits;
+        hash = vsid ^ pgidx;
+        ctx->ptem = (vsid << 7) | (pgidx >> 10);
     }
     LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n",
             ctx->key, ds, ctx->nx, vsid);
@@ -851,36 +847,12 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
         /* Check if instruction fetch is allowed, if needed */
         if (type != ACCESS_CODE || ctx->nx == 0) {
             /* Page address translation */
-            pgidx = (eaddr & page_mask) >> target_page_bits;
-#if defined(TARGET_PPC64)
-            if (env->mmu_model & POWERPC_MMU_64) {
-                /* XXX: this is false for 1 TB segments */
-                hash = vsid ^ pgidx;
-            } else
-#endif
-            {
-                hash = vsid ^ pgidx;
-            }
             LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
                     " hash " TARGET_FMT_plx "\n",
                     env->htab_base, env->htab_mask, hash);
             ctx->hash[0] = hash;
             ctx->hash[1] = ~hash;
 
-#if defined(TARGET_PPC64)
-            if (env->mmu_model & POWERPC_MMU_64) {
-                /* Only 5 bits of the page index are used in the AVPN */
-                if (target_page_bits > 23) {
-                    ctx->ptem = (vsid << 12) |
-                                ((pgidx << (target_page_bits - 16)) & 0xF80);
-                } else {
-                    ctx->ptem = (vsid << 12) | ((pgidx >> 4) & 0x0F80);
-                }
-            } else
-#endif
-            {
-                ctx->ptem = (vsid << 7) | (pgidx >> 10);
-            }
             /* Initialize real address with an invalid value */
             ctx->raddr = (target_phys_addr_t)-1ULL;
             if (unlikely(env->mmu_model == POWERPC_MMU_SOFT_6xx ||
@@ -889,9 +861,9 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
                 ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type);
             } else {
                 LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
-                        " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
+                        " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx
                         " hash=" TARGET_FMT_plx "\n",
-                        env->htab_base, env->htab_mask, vsid, pgidx,
+                        env->htab_base, env->htab_mask, vsid, ctx->ptem,
                         ctx->hash[0]);
                 /* Primary table lookup */
                 ret = find_pte(env, ctx, 0, rw, type, target_page_bits);
@@ -902,8 +874,7 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
                                 " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
                                 " hash=" TARGET_FMT_plx " pg_addr="
                                 TARGET_FMT_plx "\n", env->htab_base,
-                                env->htab_mask, vsid, pgidx, hash,
-                                ctx->hash[1]);
+                                env->htab_mask, vsid, ctx->ptem, ctx->hash[1]);
                     ret2 = find_pte(env, ctx, 1, rw, type,
                                     target_page_bits);
                     if (ret2 != -1)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 11/27] Support 1T segments on ppc
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (9 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 10/27] Better factor the ppc hash translation path David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 12/27] Add POWER7 support for ppc David Gibson
                   ` (16 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

Traditionally, the "segments" used for the two-stage translation used on
powerpc MMUs were 256MB in size.  This was the only option on all hash
page table based 32-bit powerpc cpus, and on the earlier 64-bit hash page
table based cpus.  However, newer 64-bit cpus also permit 1TB segments

This patch adds support for 1TB segment translation to the qemu code.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 target-ppc/cpu.h    |    7 +++++++
 target-ppc/helper.c |   50 ++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index fd2dfcd..10341b3 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -114,6 +114,7 @@ enum powerpc_mmu_t {
     POWERPC_MMU_601        = 0x0000000A,
 #if defined(TARGET_PPC64)
 #define POWERPC_MMU_64       0x00010000
+#define POWERPC_MMU_1TSEG    0x00020000
     /* 64 bits PowerPC MMU                                     */
     POWERPC_MMU_64B        = POWERPC_MMU_64 | 0x00000001,
     /* 620 variant (no segment exceptions)                     */
@@ -382,9 +383,11 @@ struct ppc_slb_t {
 
 /* Bits in the SLB VSID word */
 #define SLB_VSID_SHIFT          12
+#define SLB_VSID_SHIFT_1T       24
 #define SLB_VSID_SSIZE_SHIFT    62
 #define SLB_VSID_B              0xc000000000000000ULL
 #define SLB_VSID_B_256M         0x0000000000000000ULL
+#define SLB_VSID_B_1T           0x4000000000000000ULL
 #define SLB_VSID_VSID           0x3FFFFFFFFFFFF000ULL
 #define SLB_VSID_PTEM           (SLB_VSID_B | SLB_VSID_VSID)
 #define SLB_VSID_KS             0x0000000000000800ULL
@@ -398,6 +401,10 @@ struct ppc_slb_t {
 #define SEGMENT_SHIFT_256M      28
 #define SEGMENT_MASK_256M       (~((1ULL << SEGMENT_SHIFT_256M) - 1))
 
+#define SEGMENT_SHIFT_1T        40
+#define SEGMENT_MASK_1T         (~((1ULL << SEGMENT_SHIFT_1T) - 1))
+
+
 /*****************************************************************************/
 /* Machine state register bits definition                                    */
 #define MSR_SF   63 /* Sixty-four-bit mode                            hflags */
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index ae8001c..6712fce 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -675,19 +675,26 @@ static inline int find_pte(CPUState *env, mmu_ctx_t *ctx, int h, int rw,
 #if defined(TARGET_PPC64)
 static inline ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr)
 {
-    uint64_t esid;
+    uint64_t esid_256M, esid_1T;
     int n;
 
     LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr);
 
-    esid = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V;
+    esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V;
+    esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V;
 
     for (n = 0; n < env->slb_nr; n++) {
         ppc_slb_t *slb = &env->slb[n];
 
         LOG_SLB("%s: slot %d %016" PRIx64 " %016"
                     PRIx64 "\n", __func__, n, slb->esid, slb->vsid);
-        if (slb->esid == esid) {
+        /* We check for 1T matches on all MMUs here - if the MMU
+         * doesn't have 1T segment support, we will have prevented 1T
+         * entries from being inserted in the slbmte code. */
+        if (((slb->esid == esid_256M) &&
+             ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_256M))
+            || ((slb->esid == esid_1T) &&
+                ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_1T))) {
             return slb;
         }
     }
@@ -740,14 +747,20 @@ void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0)
 int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs)
 {
     int slot = rb & 0xfff;
-    uint64_t esid = rb & ~0xfff;
     ppc_slb_t *slb = &env->slb[slot];
 
-    if (slot >= env->slb_nr) {
-        return -1;
+    if (rb & (0x1000 - env->slb_nr)) {
+        return -1; /* Reserved bits set or slot too high */
+    }
+    if (rs & (SLB_VSID_B & ~SLB_VSID_B_1T)) {
+        return -1; /* Bad segment size */
+    }
+    if ((rs & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) {
+        return -1; /* 1T segment on MMU that doesn't support it */
     }
 
-    slb->esid = esid;
+    /* Mask out the slot number as we store the entry */
+    slb->esid = rb & (SLB_ESID_ESID | SLB_ESID_V);
     slb->vsid = rs;
 
     LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64
@@ -799,6 +812,7 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
     if (env->mmu_model & POWERPC_MMU_64) {
         ppc_slb_t *slb;
         target_ulong pageaddr;
+        int segment_bits;
 
         LOG_MMU("Check SLBs\n");
         slb = slb_lookup(env, eaddr);
@@ -806,7 +820,14 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
             return -5;
         }
 
-        vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
+        if (slb->vsid & SLB_VSID_B) {
+            vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T;
+            segment_bits = 40;
+        } else {
+            vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
+            segment_bits = 28;
+        }
+
         target_page_bits = (slb->vsid & SLB_VSID_L)
             ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS;
         ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP)
@@ -814,11 +835,16 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
         ds = 0;
         ctx->nx = !!(slb->vsid & SLB_VSID_N);
 
-        pageaddr = eaddr & ((1ULL << 28) - (1ULL << target_page_bits));
-        /* XXX: this is false for 1 TB segments */
-        hash = vsid ^ (pageaddr >> target_page_bits);
+        pageaddr = eaddr & ((1ULL << segment_bits)
+                            - (1ULL << target_page_bits));
+        if (slb->vsid & SLB_VSID_B) {
+            hash = vsid ^ (vsid << 25) ^ (pageaddr >> target_page_bits);
+        } else {
+            hash = vsid ^ (pageaddr >> target_page_bits);
+        }
         /* Only 5 bits of the page index are used in the AVPN */
-        ctx->ptem = (slb->vsid & SLB_VSID_PTEM) | ((pageaddr >> 16) & 0x0F80);
+        ctx->ptem = (slb->vsid & SLB_VSID_PTEM) |
+            ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80));
     } else
 #endif /* defined(TARGET_PPC64) */
     {
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 12/27] Add POWER7 support for ppc
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (10 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 11/27] Support 1T segments on ppc David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 13/27] Start implementing pSeries logical partition machine David Gibson
                   ` (15 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

This adds emulation support for the recent POWER7 cpu to qemu.  It's far
from perfect - it's missing a number of POWER7 features so far, including
any support for VSX or decimal floating point instructions.  However, it's
close enough to boot a kernel with the POWER7 PVR.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 hw/ppc.c                    |   35 ++++++++++++++
 hw/ppc.h                    |    1 +
 target-ppc/cpu.h            |   16 ++++++
 target-ppc/helper.c         |    6 ++
 target-ppc/translate_init.c |  107 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 165 insertions(+), 0 deletions(-)

diff --git a/hw/ppc.c b/hw/ppc.c
index b55a848..dabb816 100644
--- a/hw/ppc.c
+++ b/hw/ppc.c
@@ -247,6 +247,41 @@ void ppc970_irq_init (CPUState *env)
     env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, env,
                                                   PPC970_INPUT_NB);
 }
+
+/* POWER7 internal IRQ controller */
+static void power7_set_irq (void *opaque, int pin, int level)
+{
+    CPUState *env = opaque;
+    int cur_level;
+
+    LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
+                env, pin, level);
+    cur_level = (env->irq_input_state >> pin) & 1;
+
+    switch (pin) {
+    case POWER7_INPUT_INT:
+        /* Level sensitive - active high */
+        LOG_IRQ("%s: set the external IRQ state to %d\n",
+                __func__, level);
+        ppc_set_irq(env, PPC_INTERRUPT_EXT, level);
+        break;
+    default:
+        /* Unknown pin - do nothing */
+        LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
+        return;
+    }
+    if (level) {
+        env->irq_input_state |= 1 << pin;
+    } else {
+        env->irq_input_state &= ~(1 << pin);
+    }
+}
+
+void ppcPOWER7_irq_init (CPUState *env)
+{
+    env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, env,
+                                                  POWER7_INPUT_NB);
+}
 #endif /* defined(TARGET_PPC64) */
 
 /* PowerPC 40x internal IRQ controller */
diff --git a/hw/ppc.h b/hw/ppc.h
index 34f54cf..3ccf134 100644
--- a/hw/ppc.h
+++ b/hw/ppc.h
@@ -36,6 +36,7 @@ void ppc40x_irq_init (CPUState *env);
 void ppce500_irq_init (CPUState *env);
 void ppc6xx_irq_init (CPUState *env);
 void ppc970_irq_init (CPUState *env);
+void ppcPOWER7_irq_init (CPUState *env);
 
 /* PPC machines for OpenBIOS */
 enum {
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 10341b3..25d0658 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -119,6 +119,8 @@ enum powerpc_mmu_t {
     POWERPC_MMU_64B        = POWERPC_MMU_64 | 0x00000001,
     /* 620 variant (no segment exceptions)                     */
     POWERPC_MMU_620        = POWERPC_MMU_64 | 0x00000002,
+    /* Architecture 2.06 variant                               */
+    POWERPC_MMU_2_06       = POWERPC_MMU_64 | POWERPC_MMU_1TSEG | 0x00000003,
 #endif /* defined(TARGET_PPC64) */
 };
 
@@ -154,6 +156,8 @@ enum powerpc_excp_t {
 #if defined(TARGET_PPC64)
     /* PowerPC 970 exception model      */
     POWERPC_EXCP_970,
+    /* POWER7 exception model           */
+    POWERPC_EXCP_POWER7,
 #endif /* defined(TARGET_PPC64) */
 };
 
@@ -289,6 +293,8 @@ enum powerpc_input_t {
     PPC_FLAGS_INPUT_405,
     /* PowerPC 970 bus                  */
     PPC_FLAGS_INPUT_970,
+    /* PowerPC POWER7 bus               */
+    PPC_FLAGS_INPUT_POWER7,
     /* PowerPC 401 bus                  */
     PPC_FLAGS_INPUT_401,
     /* Freescale RCPU bus               */
@@ -1001,6 +1007,7 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
 #define SPR_HSPRG1            (0x131)
 #define SPR_HDSISR            (0x132)
 #define SPR_HDAR              (0x133)
+#define SPR_SPURR             (0x134)
 #define SPR_BOOKE_DBCR0       (0x134)
 #define SPR_IBCR              (0x135)
 #define SPR_PURR              (0x135)
@@ -1625,6 +1632,15 @@ enum {
     PPC970_INPUT_THINT      = 6,
     PPC970_INPUT_NB,
 };
+
+enum {
+    /* POWER7 input pins */
+    POWER7_INPUT_INT        = 0,
+    /* POWER7 probably has other inputs, but we don't care about them
+     * for any existing machine.  We can wire these up when we need
+     * them */
+    POWER7_INPUT_NB,
+};
 #endif
 
 /* Hardware exceptions definitions */
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 6712fce..278bee4 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -1200,6 +1200,7 @@ static inline int check_physical(CPUState *env, mmu_ctx_t *ctx,
 #if defined(TARGET_PPC64)
     case POWERPC_MMU_620:
     case POWERPC_MMU_64B:
+    case POWERPC_MMU_2_06:
         /* Real address are 60 bits long */
         ctx->raddr &= 0x0FFFFFFFFFFFFFFFULL;
         ctx->prot |= PAGE_WRITE;
@@ -1277,6 +1278,7 @@ int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr,
 #if defined(TARGET_PPC64)
         case POWERPC_MMU_620:
         case POWERPC_MMU_64B:
+        case POWERPC_MMU_2_06:
 #endif
             if (ret < 0) {
                 /* We didn't match any BAT entry or don't have BATs */
@@ -1376,6 +1378,7 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
 #if defined(TARGET_PPC64)
                 case POWERPC_MMU_620:
                 case POWERPC_MMU_64B:
+                case POWERPC_MMU_2_06:
 #endif
                     env->exception_index = POWERPC_EXCP_ISI;
                     env->error_code = 0x40000000;
@@ -1485,6 +1488,7 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
 #if defined(TARGET_PPC64)
                 case POWERPC_MMU_620:
                 case POWERPC_MMU_64B:
+                case POWERPC_MMU_2_06:
 #endif
                     env->exception_index = POWERPC_EXCP_DSI;
                     env->error_code = 0;
@@ -1808,6 +1812,7 @@ void ppc_tlb_invalidate_all (CPUPPCState *env)
 #if defined(TARGET_PPC64)
     case POWERPC_MMU_620:
     case POWERPC_MMU_64B:
+    case POWERPC_MMU_2_06:
 #endif /* defined(TARGET_PPC64) */
         tlb_flush(env, 1);
         break;
@@ -1875,6 +1880,7 @@ void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr)
 #if defined(TARGET_PPC64)
     case POWERPC_MMU_620:
     case POWERPC_MMU_64B:
+    case POWERPC_MMU_2_06:
         /* tlbie invalidate TLBs for all segments */
         /* XXX: given the fact that there are too many segments to invalidate,
          *      and we still don't have a tlb_flush_mask(env, n, mask) in Qemu,
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 6366424..e2a83c5 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -61,6 +61,7 @@ void glue(glue(ppc, name),_irq_init) (CPUPPCState *env);
 PPC_IRQ_INIT_FN(40x);
 PPC_IRQ_INIT_FN(6xx);
 PPC_IRQ_INIT_FN(970);
+PPC_IRQ_INIT_FN(POWER7);
 PPC_IRQ_INIT_FN(e500);
 
 /* Generic callbacks:
@@ -3131,6 +3132,35 @@ static void init_excp_970 (CPUPPCState *env)
     env->hreset_vector = 0x0000000000000100ULL;
 #endif
 }
+
+static void init_excp_POWER7 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+    env->excp_vectors[POWERPC_EXCP_RESET]    = 0x00000100;
+    env->excp_vectors[POWERPC_EXCP_MCHECK]   = 0x00000200;
+    env->excp_vectors[POWERPC_EXCP_DSI]      = 0x00000300;
+    env->excp_vectors[POWERPC_EXCP_DSEG]     = 0x00000380;
+    env->excp_vectors[POWERPC_EXCP_ISI]      = 0x00000400;
+    env->excp_vectors[POWERPC_EXCP_ISEG]     = 0x00000480;
+    env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+    env->excp_vectors[POWERPC_EXCP_ALIGN]    = 0x00000600;
+    env->excp_vectors[POWERPC_EXCP_PROGRAM]  = 0x00000700;
+    env->excp_vectors[POWERPC_EXCP_FPU]      = 0x00000800;
+    env->excp_vectors[POWERPC_EXCP_DECR]     = 0x00000900;
+    env->excp_vectors[POWERPC_EXCP_HDECR]    = 0x00000980;
+    env->excp_vectors[POWERPC_EXCP_SYSCALL]  = 0x00000C00;
+    env->excp_vectors[POWERPC_EXCP_TRACE]    = 0x00000D00;
+    env->excp_vectors[POWERPC_EXCP_PERFM]    = 0x00000F00;
+    env->excp_vectors[POWERPC_EXCP_VPU]      = 0x00000F20;
+    env->excp_vectors[POWERPC_EXCP_IABR]     = 0x00001300;
+    env->excp_vectors[POWERPC_EXCP_MAINT]    = 0x00001600;
+    env->excp_vectors[POWERPC_EXCP_VPUA]     = 0x00001700;
+    env->excp_vectors[POWERPC_EXCP_THERM]    = 0x00001800;
+    env->hreset_excp_prefix = 0;
+    /* Hardware reset vector */
+    env->hreset_vector = 0x0000000000000100ULL;
+#endif
+}
 #endif
 
 /*****************************************************************************/
@@ -6312,6 +6342,78 @@ static void init_proc_970MP (CPUPPCState *env)
     vscr_init(env, 0x00010000);
 }
 
+#if defined(TARGET_PPC64)
+/* POWER7 */
+#define POWERPC_INSNS_POWER7  (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB |        \
+                              PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |   \
+                              PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |           \
+                              PPC_FLOAT_STFIWX |                              \
+                              PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT |  \
+                              PPC_MEM_SYNC | PPC_MEM_EIEIO |                  \
+                              PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |               \
+                              PPC_64B | PPC_ALTIVEC |                         \
+                              PPC_SEGMENT_64B | PPC_SLBI |                    \
+                              PPC_POPCNTB | PPC_POPCNTWD)
+#define POWERPC_MSRM_POWER7   (0x800000000204FF36ULL)
+#define POWERPC_MMU_POWER7    (POWERPC_MMU_2_06)
+#define POWERPC_EXCP_POWER7   (POWERPC_EXCP_POWER7)
+#define POWERPC_INPUT_POWER7  (PPC_FLAGS_INPUT_POWER7)
+#define POWERPC_BFDM_POWER7   (bfd_mach_ppc64)
+#define POWERPC_FLAG_POWER7   (POWERPC_FLAG_VRE | POWERPC_FLAG_SE |            \
+                              POWERPC_FLAG_BE | POWERPC_FLAG_PMM |            \
+                              POWERPC_FLAG_BUS_CLK)
+#define check_pow_POWER7    check_pow_nocheck
+
+static void init_proc_POWER7 (CPUPPCState *env)
+{
+    gen_spr_ne_601(env);
+    gen_spr_7xx(env);
+    /* Time base */
+    gen_tbl(env);
+#if !defined(CONFIG_USER_ONLY)
+    /* PURR & SPURR: Hack - treat these as aliases for the TB for now */
+    spr_register(env, SPR_PURR,   "PURR",
+                 &spr_read_purr, SPR_NOACCESS,
+                 &spr_read_purr, SPR_NOACCESS,
+                 0x00000000);
+    spr_register(env, SPR_SPURR,   "SPURR",
+                 &spr_read_purr, SPR_NOACCESS,
+                 &spr_read_purr, SPR_NOACCESS,
+                 0x00000000);
+#endif /* !CONFIG_USER_ONLY */
+    /* Memory management */
+    /* XXX : not implemented */
+    spr_register(env, SPR_MMUCFG, "MMUCFG",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, SPR_NOACCESS,
+                 0x00000000); /* TOFIX */
+    /* XXX : not implemented */
+    spr_register(env, SPR_CTRL, "SPR_CTRLT",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x80800000);
+    spr_register(env, SPR_UCTRL, "SPR_CTRLF",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x80800000);
+    spr_register(env, SPR_VRSAVE, "SPR_VRSAVE",
+                 &spr_read_generic, &spr_write_generic,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+#if !defined(CONFIG_USER_ONLY)
+    env->slb_nr = 32;
+#endif
+    init_excp_POWER7(env);
+    env->dcache_line_size = 128;
+    env->icache_line_size = 128;
+    /* Allocate hardware IRQ controller */
+    ppcPOWER7_irq_init(env);
+    /* Can't find information on what this should be on reset.  This
+     * value is the one used by 74xx processors. */
+    vscr_init(env, 0x00010000);
+}
+#endif /* TARGET_PPC64 */
+
 /* PowerPC 620                                                               */
 #define POWERPC_INSNS_620    (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB |        \
                               PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |   \
@@ -7034,6 +7136,8 @@ enum {
     CPU_POWERPC_POWER6             = 0x003E0000,
     CPU_POWERPC_POWER6_5           = 0x0F000001, /* POWER6 in POWER5 mode */
     CPU_POWERPC_POWER6A            = 0x0F000002,
+#define CPU_POWERPC_POWER7           CPU_POWERPC_POWER7_v20
+    CPU_POWERPC_POWER7_v20         = 0x003F0200,
     CPU_POWERPC_970                = 0x00390202,
 #define CPU_POWERPC_970FX            CPU_POWERPC_970FX_v31
     CPU_POWERPC_970FX_v10          = 0x00391100,
@@ -8836,6 +8940,9 @@ static const ppc_def_t ppc_defs[] = {
     /* POWER6A                                                               */
     POWERPC_DEF("POWER6A",       CPU_POWERPC_POWER6A,                POWER6),
 #endif
+    /* POWER7                                                                */
+    POWERPC_DEF("POWER7",        CPU_POWERPC_POWER7,                 POWER7),
+    POWERPC_DEF("POWER7_v2.0",   CPU_POWERPC_POWER7_v20,             POWER7),
     /* PowerPC 970                                                           */
     POWERPC_DEF("970",           CPU_POWERPC_970,                    970),
     /* PowerPC 970FX (G5)                                                    */
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 13/27] Start implementing pSeries logical partition machine
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (11 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 12/27] Add POWER7 support for ppc David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 14/27] Implement the bus structure for PAPR virtual IO David Gibson
                   ` (14 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

This patch adds a "pseries" machine to qemu.  This aims to emulate a
logical partition on an IBM pSeries machine, compliant to the
"PowerPC Architecture Platform Requirements" (PAPR) document.

This initial version is quite limited, it implements a basic machine
and PAPR hypercall emulation.  So far only one hypercall is present -
H_PUT_TERM_CHAR - so that a (write-only) console is available.

Multiple CPUs are permitted, with SMP entry handled kexec() style.

The machine so far more resembles an old POWER4 style "full system
partition" rather than a modern LPAR, in that the guest manages the
page tables directly, rather than via hypercalls.

The machine requires qemu to be configured with --enable-fdt.  The
machine can (so far) only be booted with -kernel - i.e. no partition
firmware is provided.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 Makefile.target  |    4 +
 hw/spapr.c       |  313 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/spapr.h       |  257 ++++++++++++++++++++++++++++++++++++++++++++
 hw/spapr_hcall.c |   43 ++++++++
 4 files changed, 617 insertions(+), 0 deletions(-)
 create mode 100644 hw/spapr.c
 create mode 100644 hw/spapr.h
 create mode 100644 hw/spapr_hcall.c

diff --git a/Makefile.target b/Makefile.target
index 62b102a..ccf090b 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -231,6 +231,10 @@ obj-ppc-y += ppc_prep.o
 obj-ppc-y += ppc_oldworld.o
 # NewWorld PowerMac
 obj-ppc-y += ppc_newworld.o
+# IBM pSeries (sPAPR)i
+ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
+obj-ppc-y += spapr.o spapr_hcall.o
+endif
 # PowerPC 4xx boards
 obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
 obj-ppc-y += ppc440.o ppc440_bamboo.o
diff --git a/hw/spapr.c b/hw/spapr.c
new file mode 100644
index 0000000..0deea1b
--- /dev/null
+++ b/hw/spapr.c
@@ -0,0 +1,313 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * Copyright (c) 2004-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ * Copyright (c) 2010 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 "sysemu.h"
+#include "qemu-char.h"
+#include "hw.h"
+#include "elf.h"
+
+#include "hw/boards.h"
+#include "hw/ppc.h"
+#include "hw/loader.h"
+
+#include "hw/spapr.h"
+
+#include <libfdt.h>
+
+#define KERNEL_LOAD_ADDR        0x00000000
+#define INITRD_LOAD_ADDR        0x02800000
+#define FDT_MAX_SIZE            0x10000
+
+#define TIMEBASE_FREQ           512000000ULL
+
+#define MAX_CPUS                32
+
+sPAPREnvironment *spapr;
+
+static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
+                              const char *cpu_model, CPUState *envs[],
+                              sPAPREnvironment *spapr,
+                              target_phys_addr_t initrd_base,
+                              target_phys_addr_t initrd_size,
+                              const char *kernel_cmdline)
+{
+    void *fdt;
+    uint64_t mem_reg_property[] = { 0, cpu_to_be64(ramsize) };
+    uint32_t start_prop = cpu_to_be32(initrd_base);
+    uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
+    int i;
+    char *modelname;
+
+#define _FDT(exp) \
+    do { \
+        int ret = (exp);                                           \
+        if (ret < 0) {                                             \
+            fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \
+                    #exp, fdt_strerror(ret));                      \
+            exit(1);                                               \
+        }                                                          \
+    } while (0)
+
+    fdt = qemu_mallocz(FDT_MAX_SIZE);
+    _FDT((fdt_create(fdt, FDT_MAX_SIZE)));
+
+    _FDT((fdt_finish_reservemap(fdt)));
+
+    /* Root node */
+    _FDT((fdt_begin_node(fdt, "")));
+    _FDT((fdt_property_string(fdt, "device_type", "chrp")));
+    _FDT((fdt_property_string(fdt, "model", "qemu,emulated-pSeries-LPAR")));
+
+    _FDT((fdt_property_cell(fdt, "#address-cells", 0x2)));
+    _FDT((fdt_property_cell(fdt, "#size-cells", 0x2)));
+
+    /* /chosen */
+    _FDT((fdt_begin_node(fdt, "chosen")));
+
+    _FDT((fdt_property_string(fdt, "bootargs", kernel_cmdline)));
+    _FDT((fdt_property(fdt, "linux,initrd-start",
+                       &start_prop, sizeof(start_prop))));
+    _FDT((fdt_property(fdt, "linux,initrd-end",
+                       &end_prop, sizeof(end_prop))));
+
+    _FDT((fdt_end_node(fdt)));
+
+    /* memory node */
+    _FDT((fdt_begin_node(fdt, "memory@0")));
+
+    _FDT((fdt_property_string(fdt, "device_type", "memory")));
+    _FDT((fdt_property(fdt, "reg",
+                       mem_reg_property, sizeof(mem_reg_property))));
+
+    _FDT((fdt_end_node(fdt)));
+
+    /* cpus */
+    _FDT((fdt_begin_node(fdt, "cpus")));
+
+    _FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
+    _FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
+
+    modelname = qemu_strdup(cpu_model);
+
+    for (i = 0; i < strlen(modelname); i++) {
+        modelname[i] = toupper(modelname[i]);
+    }
+
+    for (i = 0; i < smp_cpus; i++) {
+        CPUState *env = envs[i];
+        char *nodename;
+        uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
+                           0xffffffff, 0xffffffff};
+
+        if (asprintf(&nodename, "%s@%x", modelname, i) < 0) {
+            fprintf(stderr, "Allocation failure\n");
+            exit(1);
+        }
+
+        _FDT((fdt_begin_node(fdt, nodename)));
+
+        free(nodename);
+
+        _FDT((fdt_property_cell(fdt, "reg", i)));
+        _FDT((fdt_property_string(fdt, "device_type", "cpu")));
+
+        _FDT((fdt_property_cell(fdt, "cpu-version", env->spr[SPR_PVR])));
+        _FDT((fdt_property_cell(fdt, "dcache-block-size",
+                                env->dcache_line_size)));
+        _FDT((fdt_property_cell(fdt, "icache-block-size",
+                                env->icache_line_size)));
+        _FDT((fdt_property_cell(fdt, "timebase-frequency", TIMEBASE_FREQ)));
+        /* Hardcode CPU frequency for now.  It's kind of arbitrary on
+         * full emu, for kvm we should copy it from the host */
+        _FDT((fdt_property_cell(fdt, "clock-frequency", 1000000000)));
+        _FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr)));
+        _FDT((fdt_property_string(fdt, "status", "okay")));
+        _FDT((fdt_property(fdt, "64-bit", NULL, 0)));
+
+        if (envs[i]->mmu_model & POWERPC_MMU_1TSEG) {
+            _FDT((fdt_property(fdt, "ibm,processor-segment-sizes",
+                               segs, sizeof(segs))));
+        }
+
+        _FDT((fdt_end_node(fdt)));
+    }
+
+    qemu_free(modelname);
+
+    _FDT((fdt_end_node(fdt)));
+
+    _FDT((fdt_end_node(fdt))); /* close root node */
+    _FDT((fdt_finish(fdt)));
+
+    *fdt_size = fdt_totalsize(fdt);
+
+    return fdt;
+}
+
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+    return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR;
+}
+
+static void emulate_spapr_hypercall(CPUState *env)
+{
+    env->gpr[3] = spapr_hypercall(env, env->gpr[3], &env->gpr[4]);
+}
+
+/* FIXME: hack until we implement the proper VIO console */
+static target_ulong h_put_term_char(CPUState *env, sPAPREnvironment *spapr,
+                                    target_ulong opcode, target_ulong *args)
+{
+    uint8_t buf[16];
+
+    stq_p(buf, args[2]);
+    stq_p(buf + 8, args[3]);
+
+    qemu_chr_write(serial_hds[0], buf, args[1]);
+
+    return 0;
+}
+
+
+/* pSeries LPAR / sPAPR hardware init */
+static void ppc_spapr_init(ram_addr_t ram_size,
+                           const char *boot_device,
+                           const char *kernel_filename,
+                           const char *kernel_cmdline,
+                           const char *initrd_filename,
+                           const char *cpu_model)
+{
+    CPUState *envs[MAX_CPUS];
+    void *fdt;
+    int i;
+    ram_addr_t ram_offset;
+    target_phys_addr_t fdt_addr;
+    uint32_t kernel_base, initrd_base;
+    long kernel_size, initrd_size;
+    int fdt_size;
+
+    spapr = qemu_malloc(sizeof(*spapr));
+    cpu_ppc_hypercall = emulate_spapr_hypercall;
+
+    /* We place the device tree just below either the top of RAM, or
+     * 2GB, so that it can be processed with 32-bit code if
+     * necessary */
+    fdt_addr = MIN(ram_size, 0x80000000) - FDT_MAX_SIZE;
+
+    /* init CPUs */
+    if (cpu_model == NULL) {
+        cpu_model = "POWER7";
+    }
+    for (i = 0; i < smp_cpus; i++) {
+        CPUState *env =  cpu_init(cpu_model);
+
+        if (!env) {
+            fprintf(stderr, "Unable to find PowerPC CPU definition\n");
+            exit(1);
+        }
+        /* Set time-base frequency to 512 MHz */
+        cpu_ppc_tb_init(env, TIMEBASE_FREQ);
+        qemu_register_reset((QEMUResetHandler *)&cpu_reset, env);
+
+        env->hreset_vector = 0x60;
+        env->hreset_excp_prefix = 0;
+        env->gpr[3] = i;
+
+        envs[i] = env;
+    }
+
+    /* allocate RAM */
+    ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size);
+    cpu_register_physical_memory(0, ram_size, ram_offset);
+
+    spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char);
+
+    if (kernel_filename) {
+        uint64_t lowaddr = 0;
+
+        kernel_base = KERNEL_LOAD_ADDR;
+
+        kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
+                               NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
+        if (kernel_size < 0) {
+            kernel_size = load_image_targphys(kernel_filename, kernel_base,
+                                              ram_size - kernel_base);
+        }
+        if (kernel_size < 0) {
+            fprintf(stderr, "qemu: could not load kernel '%s'\n",
+                    kernel_filename);
+            exit(1);
+        }
+
+        /* load initrd */
+        if (initrd_filename) {
+            initrd_base = INITRD_LOAD_ADDR;
+            initrd_size = load_image_targphys(initrd_filename, initrd_base,
+                                              ram_size - initrd_base);
+            if (initrd_size < 0) {
+                fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+                        initrd_filename);
+                exit(1);
+            }
+        } else {
+            initrd_base = 0;
+            initrd_size = 0;
+        }
+
+    } else {
+        fprintf(stderr, "pSeries machine needs -kernel for now");
+        exit(1);
+    }
+
+    /* Prepare the device tree */
+    fdt = spapr_create_fdt(&fdt_size, ram_size, cpu_model, envs, spapr,
+                           initrd_base, initrd_size, kernel_cmdline);
+    assert(fdt != NULL);
+
+    cpu_physical_memory_write(fdt_addr, fdt, fdt_size);
+
+    qemu_free(fdt);
+
+    envs[0]->gpr[3] = fdt_addr;
+    envs[0]->gpr[5] = 0;
+    envs[0]->hreset_vector = kernel_base;
+}
+
+static QEMUMachine spapr_machine = {
+    .name = "pseries",
+    .desc = "pSeries Logical Partition (PAPR compliant)",
+    .init = ppc_spapr_init,
+    .max_cpus = MAX_CPUS,
+    .no_vga = 1,
+    .no_parallel = 1,
+};
+
+static void spapr_machine_init(void)
+{
+    qemu_register_machine(&spapr_machine);
+}
+
+machine_init(spapr_machine_init);
diff --git a/hw/spapr.h b/hw/spapr.h
new file mode 100644
index 0000000..685944b
--- /dev/null
+++ b/hw/spapr.h
@@ -0,0 +1,257 @@
+#if !defined(__HW_SPAPR_H__)
+#define __HW_SPAPR_H__
+
+typedef struct sPAPREnvironment {
+} sPAPREnvironment;
+
+#define H_SUCCESS         0
+#define H_BUSY            1        /* Hardware busy -- retry later */
+#define H_CLOSED          2        /* Resource closed */
+#define H_NOT_AVAILABLE   3
+#define H_CONSTRAINED     4        /* Resource request constrained to max allowed */
+#define H_PARTIAL         5
+#define H_IN_PROGRESS     14       /* Kind of like busy */
+#define H_PAGE_REGISTERED 15
+#define H_PARTIAL_STORE   16
+#define H_PENDING         17       /* returned from H_POLL_PENDING */
+#define H_CONTINUE        18       /* Returned from H_Join on success */
+#define H_LONG_BUSY_START_RANGE         9900  /* Start of long busy range */
+#define H_LONG_BUSY_ORDER_1_MSEC        9900  /* Long busy, hint that 1msec \
+                                                 is a good time to retry */
+#define H_LONG_BUSY_ORDER_10_MSEC       9901  /* Long busy, hint that 10msec \
+                                                 is a good time to retry */
+#define H_LONG_BUSY_ORDER_100_MSEC      9902  /* Long busy, hint that 100msec \
+                                                 is a good time to retry */
+#define H_LONG_BUSY_ORDER_1_SEC         9903  /* Long busy, hint that 1sec \
+                                                 is a good time to retry */
+#define H_LONG_BUSY_ORDER_10_SEC        9904  /* Long busy, hint that 10sec \
+                                                 is a good time to retry */
+#define H_LONG_BUSY_ORDER_100_SEC       9905  /* Long busy, hint that 100sec \
+                                                 is a good time to retry */
+#define H_LONG_BUSY_END_RANGE           9905  /* End of long busy range */
+#define H_HARDWARE        -1       /* Hardware error */
+#define H_FUNCTION        -2       /* Function not supported */
+#define H_PRIVILEGE       -3       /* Caller not privileged */
+#define H_PARAMETER       -4       /* Parameter invalid, out-of-range or conflicting */
+#define H_BAD_MODE        -5       /* Illegal msr value */
+#define H_PTEG_FULL       -6       /* PTEG is full */
+#define H_NOT_FOUND       -7       /* PTE was not found" */
+#define H_RESERVED_DABR   -8       /* DABR address is reserved by the hypervisor on this processor" */
+#define H_NO_MEM          -9
+#define H_AUTHORITY       -10
+#define H_PERMISSION      -11
+#define H_DROPPED         -12
+#define H_SOURCE_PARM     -13
+#define H_DEST_PARM       -14
+#define H_REMOTE_PARM     -15
+#define H_RESOURCE        -16
+#define H_ADAPTER_PARM    -17
+#define H_RH_PARM         -18
+#define H_RCQ_PARM        -19
+#define H_SCQ_PARM        -20
+#define H_EQ_PARM         -21
+#define H_RT_PARM         -22
+#define H_ST_PARM         -23
+#define H_SIGT_PARM       -24
+#define H_TOKEN_PARM      -25
+#define H_MLENGTH_PARM    -27
+#define H_MEM_PARM        -28
+#define H_MEM_ACCESS_PARM -29
+#define H_ATTR_PARM       -30
+#define H_PORT_PARM       -31
+#define H_MCG_PARM        -32
+#define H_VL_PARM         -33
+#define H_TSIZE_PARM      -34
+#define H_TRACE_PARM      -35
+
+#define H_MASK_PARM       -37
+#define H_MCG_FULL        -38
+#define H_ALIAS_EXIST     -39
+#define H_P_COUNTER       -40
+#define H_TABLE_FULL      -41
+#define H_ALT_TABLE       -42
+#define H_MR_CONDITION    -43
+#define H_NOT_ENOUGH_RESOURCES -44
+#define H_R_STATE         -45
+#define H_RESCINDEND      -46
+#define H_MULTI_THREADS_ACTIVE -9005
+
+
+/* Long Busy is a condition that can be returned by the firmware
+ * when a call cannot be completed now, but the identical call
+ * should be retried later.  This prevents calls blocking in the
+ * firmware for long periods of time.  Annoyingly the firmware can return
+ * a range of return codes, hinting at how long we should wait before
+ * retrying.  If you don't care for the hint, the macro below is a good
+ * way to check for the long_busy return codes
+ */
+#define H_IS_LONG_BUSY(x)  ((x >= H_LONG_BUSY_START_RANGE) \
+                            && (x <= H_LONG_BUSY_END_RANGE))
+
+/* Flags */
+#define H_LARGE_PAGE      (1ULL<<(63-16))
+#define H_EXACT           (1ULL<<(63-24))       /* Use exact PTE or return H_PTEG_FULL */
+#define H_R_XLATE         (1ULL<<(63-25))       /* include a valid logical page num in the pte if the valid bit is set */
+#define H_READ_4          (1ULL<<(63-26))       /* Return 4 PTEs */
+#define H_PAGE_STATE_CHANGE (1ULL<<(63-28))
+#define H_PAGE_UNUSED     ((1ULL<<(63-29)) | (1ULL<<(63-30)))
+#define H_PAGE_SET_UNUSED (H_PAGE_STATE_CHANGE | H_PAGE_UNUSED)
+#define H_PAGE_SET_LOANED (H_PAGE_SET_UNUSED | (1ULL<<(63-31)))
+#define H_PAGE_SET_ACTIVE H_PAGE_STATE_CHANGE
+#define H_AVPN            (1ULL<<(63-32))       /* An avpn is provided as a sanity test */
+#define H_ANDCOND         (1ULL<<(63-33))
+#define H_ICACHE_INVALIDATE (1ULL<<(63-40))     /* icbi, etc.  (ignored for IO pages) */
+#define H_ICACHE_SYNCHRONIZE (1ULL<<(63-41))    /* dcbst, icbi, etc (ignored for IO pages */
+#define H_ZERO_PAGE       (1ULL<<(63-48))       /* zero the page before mapping (ignored for IO pages) */
+#define H_COPY_PAGE       (1ULL<<(63-49))
+#define H_N               (1ULL<<(63-61))
+#define H_PP1             (1ULL<<(63-62))
+#define H_PP2             (1ULL<<(63-63))
+
+/* VASI States */
+#define H_VASI_INVALID    0
+#define H_VASI_ENABLED    1
+#define H_VASI_ABORTED    2
+#define H_VASI_SUSPENDING 3
+#define H_VASI_SUSPENDED  4
+#define H_VASI_RESUMED    5
+#define H_VASI_COMPLETED  6
+
+/* DABRX flags */
+#define H_DABRX_HYPERVISOR (1ULL<<(63-61))
+#define H_DABRX_KERNEL     (1ULL<<(63-62))
+#define H_DABRX_USER       (1ULL<<(63-63))
+
+/* Each control block has to be on a 4K bondary */
+#define H_CB_ALIGNMENT     4096
+
+/* pSeries hypervisor opcodes */
+#define H_REMOVE                0x04
+#define H_ENTER                 0x08
+#define H_READ                  0x0c
+#define H_CLEAR_MOD             0x10
+#define H_CLEAR_REF             0x14
+#define H_PROTECT               0x18
+#define H_GET_TCE               0x1c
+#define H_PUT_TCE               0x20
+#define H_SET_SPRG0             0x24
+#define H_SET_DABR              0x28
+#define H_PAGE_INIT             0x2c
+#define H_SET_ASR               0x30
+#define H_ASR_ON                0x34
+#define H_ASR_OFF               0x38
+#define H_LOGICAL_CI_LOAD       0x3c
+#define H_LOGICAL_CI_STORE      0x40
+#define H_LOGICAL_CACHE_LOAD    0x44
+#define H_LOGICAL_CACHE_STORE   0x48
+#define H_LOGICAL_ICBI          0x4c
+#define H_LOGICAL_DCBF          0x50
+#define H_GET_TERM_CHAR         0x54
+#define H_PUT_TERM_CHAR         0x58
+#define H_REAL_TO_LOGICAL       0x5c
+#define H_HYPERVISOR_DATA       0x60
+#define H_EOI                   0x64
+#define H_CPPR                  0x68
+#define H_IPI                   0x6c
+#define H_IPOLL                 0x70
+#define H_XIRR                  0x74
+#define H_PERFMON               0x7c
+#define H_MIGRATE_DMA           0x78
+#define H_REGISTER_VPA          0xDC
+#define H_CEDE                  0xE0
+#define H_CONFER                0xE4
+#define H_PROD                  0xE8
+#define H_GET_PPP               0xEC
+#define H_SET_PPP               0xF0
+#define H_PURR                  0xF4
+#define H_PIC                   0xF8
+#define H_REG_CRQ               0xFC
+#define H_FREE_CRQ              0x100
+#define H_VIO_SIGNAL            0x104
+#define H_SEND_CRQ              0x108
+#define H_COPY_RDMA             0x110
+#define H_REGISTER_LOGICAL_LAN  0x114
+#define H_FREE_LOGICAL_LAN      0x118
+#define H_ADD_LOGICAL_LAN_BUFFER 0x11C
+#define H_SEND_LOGICAL_LAN      0x120
+#define H_BULK_REMOVE           0x124
+#define H_MULTICAST_CTRL        0x130
+#define H_SET_XDABR             0x134
+#define H_STUFF_TCE             0x138
+#define H_PUT_TCE_INDIRECT      0x13C
+#define H_CHANGE_LOGICAL_LAN_MAC 0x14C
+#define H_VTERM_PARTNER_INFO    0x150
+#define H_REGISTER_VTERM        0x154
+#define H_FREE_VTERM            0x158
+#define H_RESET_EVENTS          0x15C
+#define H_ALLOC_RESOURCE        0x160
+#define H_FREE_RESOURCE         0x164
+#define H_MODIFY_QP             0x168
+#define H_QUERY_QP              0x16C
+#define H_REREGISTER_PMR        0x170
+#define H_REGISTER_SMR          0x174
+#define H_QUERY_MR              0x178
+#define H_QUERY_MW              0x17C
+#define H_QUERY_HCA             0x180
+#define H_QUERY_PORT            0x184
+#define H_MODIFY_PORT           0x188
+#define H_DEFINE_AQP1           0x18C
+#define H_GET_TRACE_BUFFER      0x190
+#define H_DEFINE_AQP0           0x194
+#define H_RESIZE_MR             0x198
+#define H_ATTACH_MCQP           0x19C
+#define H_DETACH_MCQP           0x1A0
+#define H_CREATE_RPT            0x1A4
+#define H_REMOVE_RPT            0x1A8
+#define H_REGISTER_RPAGES       0x1AC
+#define H_DISABLE_AND_GETC      0x1B0
+#define H_ERROR_DATA            0x1B4
+#define H_GET_HCA_INFO          0x1B8
+#define H_GET_PERF_COUNT        0x1BC
+#define H_MANAGE_TRACE          0x1C0
+#define H_FREE_LOGICAL_LAN_BUFFER 0x1D4
+#define H_QUERY_INT_STATE       0x1E4
+#define H_POLL_PENDING          0x1D8
+#define H_ILLAN_ATTRIBUTES      0x244
+#define H_MODIFY_HEA_QP         0x250
+#define H_QUERY_HEA_QP          0x254
+#define H_QUERY_HEA             0x258
+#define H_QUERY_HEA_PORT        0x25C
+#define H_MODIFY_HEA_PORT       0x260
+#define H_REG_BCMC              0x264
+#define H_DEREG_BCMC            0x268
+#define H_REGISTER_HEA_RPAGES   0x26C
+#define H_DISABLE_AND_GET_HEA   0x270
+#define H_GET_HEA_INFO          0x274
+#define H_ALLOC_HEA_RESOURCE    0x278
+#define H_ADD_CONN              0x284
+#define H_DEL_CONN              0x288
+#define H_JOIN                  0x298
+#define H_VASI_STATE            0x2A4
+#define H_ENABLE_CRQ            0x2B0
+#define H_GET_EM_PARMS          0x2B8
+#define H_SET_MPP               0x2D0
+#define H_GET_MPP               0x2D4
+#define MAX_HCALL_OPCODE        H_GET_MPP
+
+extern sPAPREnvironment *spapr;
+
+/*#define DEBUG_SPAPR_HCALLS*/
+
+#ifdef DEBUG_SPAPR_HCALLS
+#define hcall_dprintf(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define hcall_dprintf(fmt, ...) \
+    do { } while (0)
+#endif
+
+typedef target_ulong (*spapr_hcall_fn)(CPUState *env, sPAPREnvironment *spapr,
+                                       target_ulong opcode,
+                                       target_ulong *args);
+
+void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn);
+target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
+                             target_ulong *args);
+
+#endif /* !defined (__HW_SPAPR_H__) */
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c
new file mode 100644
index 0000000..7623969
--- /dev/null
+++ b/hw/spapr_hcall.c
@@ -0,0 +1,43 @@
+#include "sysemu.h"
+#include "cpu.h"
+#include "qemu-char.h"
+#include "hw/spapr.h"
+
+struct hypercall {
+    spapr_hcall_fn fn;
+} hypercall_table[(MAX_HCALL_OPCODE / 4) + 1];
+
+void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn)
+{
+    struct hypercall *hc;
+
+    assert(opcode <= MAX_HCALL_OPCODE);
+    assert((opcode & 0x3) == 0);
+
+    hc = hypercall_table + (opcode / 4);
+
+    assert(!hc->fn || (fn == hc->fn));
+
+    hc->fn = fn;
+}
+
+target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
+                             target_ulong *args)
+{
+    if (msr_pr) {
+        hcall_dprintf("Hypercall made with MSR[PR]=1\n");
+        return H_PRIVILEGE;
+    }
+
+    if ((opcode <= MAX_HCALL_OPCODE)
+        && ((opcode & 0x3) == 0)) {
+        struct hypercall *hc = hypercall_table + (opcode / 4);
+
+        if (hc->fn) {
+            return hc->fn(env, spapr, opcode, args);
+        }
+    }
+
+    hcall_dprintf("Unimplemented hcall 0x" TARGET_FMT_lx "\n", opcode);
+    return H_FUNCTION;
+}
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 14/27] Implement the bus structure for PAPR virtual IO
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (12 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 13/27] Start implementing pSeries logical partition machine David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 15/27] Virtual hash page table handling on pSeries machine David Gibson
                   ` (13 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

This extends the "pseries" (PAPR) machine to include a virtual IO bus
supporting the PAPR defined hypercall based virtual IO mechanisms.

So far only one VIO device is provided, the vty / vterm, providing
a full console (polled only, for now).

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 Makefile.target |    5 +-
 hw/spapr.c      |   48 ++++++++-----
 hw/spapr.h      |    3 +
 hw/spapr_vio.c  |  214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/spapr_vio.h  |   50 +++++++++++++
 hw/spapr_vty.c  |  150 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 450 insertions(+), 20 deletions(-)
 create mode 100644 hw/spapr_vio.c
 create mode 100644 hw/spapr_vio.h
 create mode 100644 hw/spapr_vty.c

diff --git a/Makefile.target b/Makefile.target
index ccf090b..cf12691 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -231,9 +231,10 @@ obj-ppc-y += ppc_prep.o
 obj-ppc-y += ppc_oldworld.o
 # NewWorld PowerMac
 obj-ppc-y += ppc_newworld.o
-# IBM pSeries (sPAPR)i
+# IBM pSeries (sPAPR)
 ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
-obj-ppc-y += spapr.o spapr_hcall.o
+obj-ppc-y += spapr.o spapr_hcall.o spapr_vio.o
+obj-ppc-y += spapr_vty.o
 endif
 # 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 0deea1b..f3d6125 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -25,7 +25,6 @@
  *
  */
 #include "sysemu.h"
-#include "qemu-char.h"
 #include "hw.h"
 #include "elf.h"
 
@@ -34,6 +33,7 @@
 #include "hw/loader.h"
 
 #include "hw/spapr.h"
+#include "hw/spapr_vio.h"
 
 #include <libfdt.h>
 
@@ -60,6 +60,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
     uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
     int i;
     char *modelname;
+    int ret;
 
 #define _FDT(exp) \
     do { \
@@ -159,9 +160,30 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
 
     _FDT((fdt_end_node(fdt)));
 
+    /* vdevice */
+    _FDT((fdt_begin_node(fdt, "vdevice")));
+
+    _FDT((fdt_property_string(fdt, "device_type", "vdevice")));
+    _FDT((fdt_property_string(fdt, "compatible", "IBM,vdevice")));
+    _FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
+    _FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
+
+    _FDT((fdt_end_node(fdt)));
+
     _FDT((fdt_end_node(fdt))); /* close root node */
     _FDT((fdt_finish(fdt)));
 
+    /* re-expand to allow for further tweaks */
+    _FDT((fdt_open_into(fdt, fdt, FDT_MAX_SIZE)));
+
+    ret = spapr_populate_vdevice(spapr->vio_bus, fdt);
+    if (ret < 0) {
+        fprintf(stderr, "couldn't setup vio devices in fdt\n");
+        exit(1);
+    }
+
+    _FDT((fdt_pack(fdt)));
+
     *fdt_size = fdt_totalsize(fdt);
 
     return fdt;
@@ -177,21 +199,6 @@ static void emulate_spapr_hypercall(CPUState *env)
     env->gpr[3] = spapr_hypercall(env, env->gpr[3], &env->gpr[4]);
 }
 
-/* FIXME: hack until we implement the proper VIO console */
-static target_ulong h_put_term_char(CPUState *env, sPAPREnvironment *spapr,
-                                    target_ulong opcode, target_ulong *args)
-{
-    uint8_t buf[16];
-
-    stq_p(buf, args[2]);
-    stq_p(buf + 8, args[3]);
-
-    qemu_chr_write(serial_hds[0], buf, args[1]);
-
-    return 0;
-}
-
-
 /* pSeries LPAR / sPAPR hardware init */
 static void ppc_spapr_init(ram_addr_t ram_size,
                            const char *boot_device,
@@ -243,7 +250,13 @@ static void ppc_spapr_init(ram_addr_t ram_size,
     ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size);
     cpu_register_physical_memory(0, ram_size, ram_offset);
 
-    spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char);
+    spapr->vio_bus = spapr_vio_bus_init();
+
+    for (i = 0; i < MAX_SERIAL_PORTS; i++) {
+        if (serial_hds[i]) {
+            spapr_vty_create(spapr->vio_bus, i, serial_hds[i]);
+        }
+    }
 
     if (kernel_filename) {
         uint64_t lowaddr = 0;
@@ -276,7 +289,6 @@ static void ppc_spapr_init(ram_addr_t ram_size,
             initrd_base = 0;
             initrd_size = 0;
         }
-
     } else {
         fprintf(stderr, "pSeries machine needs -kernel for now");
         exit(1);
diff --git a/hw/spapr.h b/hw/spapr.h
index 685944b..06cca15 100644
--- a/hw/spapr.h
+++ b/hw/spapr.h
@@ -1,7 +1,10 @@
 #if !defined(__HW_SPAPR_H__)
 #define __HW_SPAPR_H__
 
+struct VIOsPAPRBus;
+
 typedef struct sPAPREnvironment {
+    struct VIOsPAPRBus *vio_bus;
 } sPAPREnvironment;
 
 #define H_SUCCESS         0
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
new file mode 100644
index 0000000..10acb4c
--- /dev/null
+++ b/hw/spapr_vio.c
@@ -0,0 +1,214 @@
+/*
+ * QEMU sPAPR VIO code
+ *
+ * Copyright (c) 2010 David Gibson, IBM Corporation <dwg@au1.ibm.com>
+ * Based on the s390 virtio bus code:
+ * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "monitor.h"
+#include "loader.h"
+#include "elf.h"
+#include "hw/sysbus.h"
+#include "kvm.h"
+#include "device_tree.h"
+
+#include "hw/spapr.h"
+#include "hw/spapr_vio.h"
+
+#ifdef CONFIG_FDT
+#include <libfdt.h>
+#endif /* CONFIG_FDT */
+
+/* #define DEBUG_SPAPR */
+
+#ifdef DEBUG_SPAPR
+#define dprintf(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+    do { } while (0)
+#endif
+
+static struct BusInfo spapr_vio_bus_info = {
+    .name       = "spapr-vio",
+    .size       = sizeof(VIOsPAPRBus),
+};
+
+VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg)
+{
+    DeviceState *qdev;
+    VIOsPAPRDevice *dev = NULL;
+
+    QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
+        dev = (VIOsPAPRDevice *)qdev;
+        if (dev->reg == reg) {
+            break;
+        }
+    }
+
+    return dev;
+}
+
+#ifdef CONFIG_FDT
+static int vio_make_devnode(VIOsPAPRDevice *dev,
+                            void *fdt)
+{
+    VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)dev->qdev.info;
+    int vdevice_off, node_off;
+    int ret;
+
+    vdevice_off = fdt_path_offset(fdt, "/vdevice");
+    if (vdevice_off < 0) {
+        return vdevice_off;
+    }
+
+    node_off = fdt_add_subnode(fdt, vdevice_off, dev->qdev.id);
+    if (node_off < 0) {
+        return node_off;
+    }
+
+    ret = fdt_setprop_cell(fdt, node_off, "reg", dev->reg);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (info->dt_type) {
+        ret = fdt_setprop_string(fdt, node_off, "device_type",
+                                 info->dt_type);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    if (info->dt_compatible) {
+        ret = fdt_setprop_string(fdt, node_off, "compatible",
+                                 info->dt_compatible);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    if (info->devnode) {
+        ret = (info->devnode)(dev, fdt, node_off);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    return node_off;
+}
+#endif /* CONFIG_FDT */
+
+static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo)
+{
+    VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
+    VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
+    char *id;
+
+    if (asprintf(&id, "%s@%x", info->dt_name, dev->reg) < 0) {
+        return -1;
+    }
+
+    dev->qdev.id = id;
+
+    return info->init(dev);
+}
+
+void spapr_vio_bus_register_withprop(VIOsPAPRDeviceInfo *info)
+{
+    info->qdev.init = spapr_vio_busdev_init;
+    info->qdev.bus_info = &spapr_vio_bus_info;
+
+    assert(info->qdev.size >= sizeof(VIOsPAPRDevice));
+    qdev_register(&info->qdev);
+}
+
+VIOsPAPRBus *spapr_vio_bus_init(void)
+{
+    VIOsPAPRBus *bus;
+    BusState *qbus;
+    DeviceState *dev;
+    DeviceInfo *qinfo;
+
+    /* Create bridge device */
+    dev = qdev_create(NULL, "spapr-vio-bridge");
+    qdev_init_nofail(dev);
+
+    /* Create bus on bridge device */
+
+    qbus = qbus_create(&spapr_vio_bus_info, dev, "spapr-vio");
+    bus = DO_UPCAST(VIOsPAPRBus, bus, qbus);
+
+    for (qinfo = device_info_list; qinfo; qinfo = qinfo->next) {
+        VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
+
+        if (qinfo->bus_info != &spapr_vio_bus_info) {
+            continue;
+        }
+
+        if (info->hcalls) {
+            info->hcalls(bus);
+        }
+    }
+
+    return bus;
+}
+
+/* Represents sPAPR hcall VIO devices */
+
+static int spapr_vio_bridge_init(SysBusDevice *dev)
+{
+    /* nothing */
+    return 0;
+}
+
+static SysBusDeviceInfo spapr_vio_bridge_info = {
+    .init = spapr_vio_bridge_init,
+    .qdev.name  = "spapr-vio-bridge",
+    .qdev.size  = sizeof(SysBusDevice),
+    .qdev.no_user = 1,
+};
+
+static void spapr_vio_register_devices(void)
+{
+    sysbus_register_withprop(&spapr_vio_bridge_info);
+}
+
+device_init(spapr_vio_register_devices)
+
+#ifdef CONFIG_FDT
+int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt)
+{
+    DeviceState *qdev;
+    int ret = 0;
+
+    QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
+        VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
+
+        ret = vio_make_devnode(dev, fdt);
+
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    return 0;
+}
+#endif /* CONFIG_FDT */
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
new file mode 100644
index 0000000..b164ad3
--- /dev/null
+++ b/hw/spapr_vio.h
@@ -0,0 +1,50 @@
+#ifndef _HW_SPAPR_VIO_H
+#define _HW_SPAPR_VIO_H
+/*
+ * QEMU sPAPR VIO bus definitions
+ *
+ * Copyright (c) 2010 David Gibson, IBM Corporation <david@gibson.dropbear.id.au>
+ * Based on the s390 virtio bus definitions:
+ * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+typedef struct VIOsPAPRDevice {
+    DeviceState qdev;
+    uint32_t reg;
+} VIOsPAPRDevice;
+
+typedef struct VIOsPAPRBus {
+    BusState bus;
+} VIOsPAPRBus;
+
+typedef struct {
+    DeviceInfo qdev;
+    const char *dt_name, *dt_type, *dt_compatible;
+    int (*init)(VIOsPAPRDevice *dev);
+    void (*hcalls)(VIOsPAPRBus *bus);
+    int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off);
+} VIOsPAPRDeviceInfo;
+
+extern VIOsPAPRBus *spapr_vio_bus_init(void);
+extern VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg);
+extern void spapr_vio_bus_register_withprop(VIOsPAPRDeviceInfo *info);
+extern int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt);
+
+void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len);
+void spapr_vty_create(VIOsPAPRBus *bus,
+                      uint32_t reg, CharDriverState *chardev);
+
+#endif /* _HW_SPAPR_VIO_H */
diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c
new file mode 100644
index 0000000..b4da6a8
--- /dev/null
+++ b/hw/spapr_vty.c
@@ -0,0 +1,150 @@
+#include "qdev.h"
+#include "qemu-char.h"
+#include "hw/spapr.h"
+#include "hw/spapr_vio.h"
+
+#define VTERM_BUFSIZE   16
+
+typedef struct VIOsPAPRVTYDevice {
+    VIOsPAPRDevice sdev;
+    CharDriverState *chardev;
+    uint32_t in, out;
+    uint8_t buf[VTERM_BUFSIZE];
+} VIOsPAPRVTYDevice;
+
+static int vty_can_receive(void *opaque)
+{
+    VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque;
+
+    return (dev->in - dev->out) < VTERM_BUFSIZE;
+}
+
+static void vty_receive(void *opaque, const uint8_t *buf, int size)
+{
+    VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque;
+    int i;
+
+    for (i = 0; i < size; i++) {
+        assert((dev->in - dev->out) < VTERM_BUFSIZE);
+        dev->buf[dev->in++ % VTERM_BUFSIZE] = buf[i];
+    }
+}
+
+static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, int max)
+{
+    VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev;
+    int n = 0;
+
+    while ((n < max) && (dev->out != dev->in)) {
+        buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE];
+    }
+
+    return n;
+}
+
+void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len)
+{
+    VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev;
+
+    /* FIXME: should check the qemu_chr_write() return value */
+    qemu_chr_write(dev->chardev, buf, len);
+}
+
+static int spapr_vty_init(VIOsPAPRDevice *sdev)
+{
+    VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev;
+
+    qemu_chr_add_handlers(dev->chardev, vty_can_receive,
+                          vty_receive, NULL, dev);
+
+    return 0;
+}
+
+static target_ulong h_put_term_char(CPUState *env, sPAPREnvironment *spapr,
+                                    target_ulong opcode, target_ulong *args)
+{
+    target_ulong reg = args[0];
+    target_ulong len = args[1];
+    target_ulong char0_7 = args[2];
+    target_ulong char8_15 = args[3];
+    VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+    uint8_t buf[16];
+
+    if (!sdev) {
+        return H_PARAMETER;
+    }
+
+    if (len > 16) {
+        return H_PARAMETER;
+    }
+
+    *((uint64_t *)buf) = cpu_to_be64(char0_7);
+    *((uint64_t *)buf + 1) = cpu_to_be64(char8_15);
+
+    vty_putchars(sdev, buf, len);
+
+    return H_SUCCESS;
+}
+
+static target_ulong h_get_term_char(CPUState *env, sPAPREnvironment *spapr,
+                                    target_ulong opcode, target_ulong *args)
+{
+    target_ulong reg = args[0];
+    target_ulong *len = args + 0;
+    target_ulong *char0_7 = args + 1;
+    target_ulong *char8_15 = args + 2;
+    VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+    uint8_t buf[16];
+
+    if (!sdev) {
+        return H_PARAMETER;
+    }
+
+    *len = vty_getchars(sdev, buf, sizeof(buf));
+    if (*len < 16) {
+        memset(buf + *len, 0, 16 - *len);
+    }
+
+    *char0_7 = be64_to_cpu(*((uint64_t *)buf));
+    *char8_15 = be64_to_cpu(*((uint64_t *)buf + 1));
+
+    return H_SUCCESS;
+}
+
+void spapr_vty_create(VIOsPAPRBus *bus,
+                      uint32_t reg, CharDriverState *chardev)
+{
+    DeviceState *dev;
+
+    dev = qdev_create(&bus->bus, "spapr-vty");
+    qdev_prop_set_uint32(dev, "reg", reg);
+    qdev_prop_set_chr(dev, "chardev", chardev);
+    qdev_init_nofail(dev);
+}
+
+static void vty_hcalls(VIOsPAPRBus *bus)
+{
+    spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char);
+    spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char);
+}
+
+static VIOsPAPRDeviceInfo spapr_vty = {
+    .init = spapr_vty_init,
+    .dt_name = "vty",
+    .dt_type = "serial",
+    .dt_compatible = "hvterm1",
+    .hcalls = vty_hcalls,
+    .qdev.name = "spapr-vty",
+    .qdev.size = sizeof(VIOsPAPRVTYDevice),
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0),
+        DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static void spapr_vty_register(void)
+{
+    spapr_vio_bus_register_withprop(&spapr_vty);
+}
+device_init(spapr_vty_register);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 15/27] Virtual hash page table handling on pSeries machine
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (13 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 14/27] Implement the bus structure for PAPR virtual IO David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 16/27] Implement hcall based RTAS for pSeries machines David Gibson
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

On pSeries logical partitions, excepting the old POWER4-style full system
partitions, the guest does not have direct access to the hardware page
table.  Instead, the pagetable exists in hypervisor memory, and the guest
must manipulate it with hypercalls.

However, our current pSeries emulation more closely resembles the old
style where the guest must set up and handle the pagetables itself.  This
patch converts it to act like a modern partition.

This involves two things: first, the hash translation path is modified to
permit the has table to be stored externally to the emulated machine's
RAM.  The pSeries machine init code configures the CPUs to use this mode.

Secondly, we emulate the PAPR hypercalls for manipulating the external
hashed page table.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 hw/spapr.c          |   35 ++++++-
 hw/spapr_hcall.c    |  254 +++++++++++++++++++++++++++++++++++++++++++++++++++
 target-ppc/cpu.h    |    2 +
 target-ppc/helper.c |   36 ++++++--
 4 files changed, 315 insertions(+), 12 deletions(-)

diff --git a/hw/spapr.c b/hw/spapr.c
index f3d6125..cd05d3f 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -52,12 +52,15 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
                               sPAPREnvironment *spapr,
                               target_phys_addr_t initrd_base,
                               target_phys_addr_t initrd_size,
-                              const char *kernel_cmdline)
+                              const char *kernel_cmdline,
+                              long hash_shift)
 {
     void *fdt;
     uint64_t mem_reg_property[] = { 0, cpu_to_be64(ramsize) };
     uint32_t start_prop = cpu_to_be32(initrd_base);
     uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
+    uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
+    char hypertas_prop[] = "hcall-pft\0hcall-term";
     int i;
     char *modelname;
     int ret;
@@ -145,6 +148,8 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
          * full emu, for kvm we should copy it from the host */
         _FDT((fdt_property_cell(fdt, "clock-frequency", 1000000000)));
         _FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr)));
+        _FDT((fdt_property(fdt, "ibm,pft-size",
+                           pft_size_prop, sizeof(pft_size_prop))));
         _FDT((fdt_property_string(fdt, "status", "okay")));
         _FDT((fdt_property(fdt, "64-bit", NULL, 0)));
 
@@ -160,6 +165,14 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
 
     _FDT((fdt_end_node(fdt)));
 
+    /* RTAS */
+    _FDT((fdt_begin_node(fdt, "rtas")));
+
+    _FDT((fdt_property(fdt, "ibm,hypertas-functions", hypertas_prop,
+                       sizeof(hypertas_prop))));
+
+    _FDT((fdt_end_node(fdt)));
+
     /* vdevice */
     _FDT((fdt_begin_node(fdt, "vdevice")));
 
@@ -208,12 +221,13 @@ static void ppc_spapr_init(ram_addr_t ram_size,
                            const char *cpu_model)
 {
     CPUState *envs[MAX_CPUS];
-    void *fdt;
+    void *fdt, *htab;
     int i;
     ram_addr_t ram_offset;
     target_phys_addr_t fdt_addr;
     uint32_t kernel_base, initrd_base;
-    long kernel_size, initrd_size;
+    long kernel_size, initrd_size, htab_size;
+    long pteg_shift = 17;
     int fdt_size;
 
     spapr = qemu_malloc(sizeof(*spapr));
@@ -250,6 +264,18 @@ static void ppc_spapr_init(ram_addr_t ram_size,
     ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size);
     cpu_register_physical_memory(0, ram_size, ram_offset);
 
+    /* allocate hash page table.  For now we always make this 16mb,
+     * later we should probably make it scale to the size of guest
+     * RAM */
+    htab_size = 1ULL << (pteg_shift + 7);
+    htab = qemu_mallocz(htab_size);
+
+    for (i = 0; i < smp_cpus; i++) {
+        envs[i]->external_htab = htab;
+        envs[i]->htab_base = -1;
+        envs[i]->htab_mask = htab_size - 1;
+    }
+
     spapr->vio_bus = spapr_vio_bus_init();
 
     for (i = 0; i < MAX_SERIAL_PORTS; i++) {
@@ -296,7 +322,8 @@ 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);
+                           initrd_base, initrd_size, kernel_cmdline,
+                           pteg_shift + 7);
     assert(fdt != NULL);
 
     cpu_physical_memory_write(fdt_addr, fdt, fdt_size);
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c
index 7623969..5c2dd88 100644
--- a/hw/spapr_hcall.c
+++ b/hw/spapr_hcall.c
@@ -1,8 +1,253 @@
 #include "sysemu.h"
 #include "cpu.h"
 #include "qemu-char.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+#include "exec-all.h"
 #include "hw/spapr.h"
 
+#define HPTES_PER_GROUP 8
+
+#define HPTE_V_SSIZE_SHIFT      62
+#define HPTE_V_AVPN_SHIFT       7
+#define HPTE_V_AVPN             0x3fffffffffffff80ULL
+#define HPTE_V_AVPN_VAL(x)      (((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT)
+#define HPTE_V_COMPARE(x, y)    (!(((x) ^ (y)) & 0xffffffffffffff80UL))
+#define HPTE_V_BOLTED           0x0000000000000010ULL
+#define HPTE_V_LOCK             0x0000000000000008ULL
+#define HPTE_V_LARGE            0x0000000000000004ULL
+#define HPTE_V_SECONDARY        0x0000000000000002ULL
+#define HPTE_V_VALID            0x0000000000000001ULL
+
+#define HPTE_R_PP0              0x8000000000000000ULL
+#define HPTE_R_TS               0x4000000000000000ULL
+#define HPTE_R_KEY_HI           0x3000000000000000ULL
+#define HPTE_R_RPN_SHIFT        12
+#define HPTE_R_RPN              0x3ffffffffffff000ULL
+#define HPTE_R_FLAGS            0x00000000000003ffULL
+#define HPTE_R_PP               0x0000000000000003ULL
+#define HPTE_R_N                0x0000000000000004ULL
+#define HPTE_R_G                0x0000000000000008ULL
+#define HPTE_R_M                0x0000000000000010ULL
+#define HPTE_R_I                0x0000000000000020ULL
+#define HPTE_R_W                0x0000000000000040ULL
+#define HPTE_R_WIMG             0x0000000000000078ULL
+#define HPTE_R_C                0x0000000000000080ULL
+#define HPTE_R_R                0x0000000000000100ULL
+#define HPTE_R_KEY_LO           0x0000000000000e00ULL
+
+#define HPTE_V_1TB_SEG          0x4000000000000000ULL
+#define HPTE_V_VRMA_MASK        0x4001ffffff000000ULL
+
+#define HPTE_V_HVLOCK           0x40ULL
+
+static inline int lock_hpte(void *hpte, target_ulong bits)
+{
+    uint64_t pteh;
+
+    pteh = ldq_p(hpte);
+
+    /* We're protected by qemu's global lock here */
+    if (pteh & bits) {
+        return 0;
+    }
+    stq_p(hpte, pteh | HPTE_V_HVLOCK);
+    return 1;
+}
+
+static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
+                                     target_ulong pte_index)
+{
+    target_ulong rb, va_low;
+
+    rb = (v & ~0x7fULL) << 16; /* AVA field */
+    va_low = pte_index >> 3;
+    if (v & HPTE_V_SECONDARY) {
+        va_low = ~va_low;
+    }
+    /* xor vsid from AVA */
+    if (!(v & HPTE_V_1TB_SEG)) {
+        va_low ^= v >> 12;
+    } else {
+        va_low ^= v >> 24;
+    }
+    va_low &= 0x7ff;
+    if (v & HPTE_V_LARGE) {
+        rb |= 1;                         /* L field */
+#if 0 /* Disable that P7 specific bit for now */
+        if (r & 0xff000) {
+            /* non-16MB large page, must be 64k */
+            /* (masks depend on page size) */
+            rb |= 0x1000;                /* page encoding in LP field */
+            rb |= (va_low & 0x7f) << 16; /* 7b of VA in AVA/LP field */
+            rb |= (va_low & 0xfe);       /* AVAL field */
+        }
+#endif
+    } else {
+        /* 4kB page */
+        rb |= (va_low & 0x7ff) << 12;   /* remaining 11b of AVA */
+    }
+    rb |= (v >> 54) & 0x300;            /* B field */
+    return rb;
+}
+
+static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr,
+                            target_ulong opcode, target_ulong *args)
+{
+    target_ulong flags = args[0];
+    target_ulong pte_index = args[1];
+    target_ulong pteh = args[2];
+    target_ulong ptel = args[3];
+    target_ulong porder;
+    target_ulong i, pa;
+    uint8_t *hpte;
+
+    /* only handle 4k and 16M pages for now */
+    porder = 12;
+    if (pteh & HPTE_V_LARGE) {
+#if 0 /* We don't support 64k pages yet */
+        if ((ptel & 0xf000) == 0x1000) {
+            /* 64k page */
+            porder = 16;
+        } else
+#endif
+        if ((ptel & 0xff000) == 0) {
+            /* 16M page */
+            porder = 24;
+            /* lowest AVA bit must be 0 for 16M pages */
+            if (pteh & 0x80) {
+                return H_PARAMETER;
+            }
+        } else {
+            return H_PARAMETER;
+        }
+    }
+
+    pa = ptel & HPTE_R_RPN;
+    /* FIXME: bounds check the pa? */
+
+    /* Check WIMG */
+    if ((ptel & HPTE_R_WIMG) != HPTE_R_M) {
+        return H_PARAMETER;
+    }
+    pteh &= ~0x60ULL;
+
+    if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
+        return H_PARAMETER;
+    }
+    if (likely((flags & H_EXACT) == 0)) {
+        pte_index &= ~7ULL;
+        hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
+        for (i = 0; ; ++i) {
+            if (i == 8) {
+                return H_PTEG_FULL;
+            }
+            if (((ldq_p(hpte) & HPTE_V_VALID) == 0) &&
+                lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID)) {
+                break;
+            }
+            hpte += HASH_PTE_SIZE_64;
+        }
+    } else {
+        i = 0;
+        hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
+        if (!lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID)) {
+            return H_PTEG_FULL;
+        }
+    }
+    stq_p(hpte + (HASH_PTE_SIZE_64/2), ptel);
+    /* eieio();  FIXME: need some sort of barrier for smp? */
+    stq_p(hpte, pteh);
+
+    assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
+    args[0] = pte_index + i;
+    return H_SUCCESS;
+}
+
+static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr,
+                             target_ulong opcode, target_ulong *args)
+{
+    target_ulong flags = args[0];
+    target_ulong pte_index = args[1];
+    target_ulong avpn = args[2];
+    uint8_t *hpte;
+    target_ulong v, r, rb;
+
+    if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
+        return H_PARAMETER;
+    }
+
+    hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
+    while (!lock_hpte(hpte, HPTE_V_HVLOCK)) {
+        /* We have no real concurrency in qemu soft-emulation, so we
+         * will never actually have a contested lock */
+        assert(0);
+    }
+
+    v = ldq_p(hpte);
+    r = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
+
+    if ((v & HPTE_V_VALID) == 0 ||
+        ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) ||
+        ((flags & H_ANDCOND) && (v & avpn) != 0)) {
+        stq_p(hpte, v & ~HPTE_V_HVLOCK);
+        assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
+        return H_NOT_FOUND;
+    }
+    args[0] = v & ~HPTE_V_HVLOCK;
+    args[1] = r;
+    stq_p(hpte, 0);
+    rb = compute_tlbie_rb(v, r, pte_index);
+    ppc_tlb_invalidate_one(env, rb);
+    assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
+    return H_SUCCESS;
+}
+
+static target_ulong h_protect(CPUState *env, sPAPREnvironment *spapr,
+                              target_ulong opcode, target_ulong *args)
+{
+    target_ulong flags = args[0];
+    target_ulong pte_index = args[1];
+    target_ulong avpn = args[2];
+    uint8_t *hpte;
+    target_ulong v, r, rb;
+
+    if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
+        return H_PARAMETER;
+    }
+
+    hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
+    while (!lock_hpte(hpte, HPTE_V_HVLOCK)) {
+        /* We have no real concurrency in qemu soft-emulation, so we
+         * will never actually have a contested lock */
+        assert(0);
+    }
+
+    v = ldq_p(hpte);
+    r = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
+
+    if ((v & HPTE_V_VALID) == 0 ||
+        ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) {
+        stq_p(hpte, v & ~HPTE_V_HVLOCK);
+        assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
+        return H_NOT_FOUND;
+    }
+
+    r &= ~(HPTE_R_PP0 | HPTE_R_PP | HPTE_R_N |
+           HPTE_R_KEY_HI | HPTE_R_KEY_LO);
+    r |= (flags << 55) & HPTE_R_PP0;
+    r |= (flags << 48) & HPTE_R_KEY_HI;
+    r |= flags & (HPTE_R_PP | HPTE_R_N | HPTE_R_KEY_LO);
+    rb = compute_tlbie_rb(v, r, pte_index);
+    stq_p(hpte, v & ~HPTE_V_VALID);
+    ppc_tlb_invalidate_one(env, rb);
+    stq_p(hpte + (HASH_PTE_SIZE_64/2), r);
+    /* Don't need a memory barrier, due to qemu's global lock */
+    stq_p(hpte, v & ~HPTE_V_HVLOCK);
+    assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
+    return H_SUCCESS;
+}
+
 struct hypercall {
     spapr_hcall_fn fn;
 } hypercall_table[(MAX_HCALL_OPCODE / 4) + 1];
@@ -41,3 +286,12 @@ target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
     hcall_dprintf("Unimplemented hcall 0x" TARGET_FMT_lx "\n", opcode);
     return H_FUNCTION;
 }
+
+static void hypercall_init(void)
+{
+    /* hcall-pft */
+    spapr_register_hypercall(H_ENTER, h_enter);
+    spapr_register_hypercall(H_REMOVE, h_remove);
+    spapr_register_hypercall(H_PROTECT, h_protect);
+}
+device_init(hypercall_init);
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 25d0658..b4c2555 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -670,6 +670,8 @@ struct CPUPPCState {
     target_phys_addr_t htab_base;
     target_phys_addr_t htab_mask;
     target_ulong sr[32];
+    /* externally stored hash table */
+    uint8_t *external_htab;
     /* BATs */
     int nb_BATs;
     target_ulong DBAT[2][8];
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 278bee4..5e4030b 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -589,8 +589,13 @@ static inline int _find_pte(CPUState *env, mmu_ctx_t *ctx, int is_64b, int h,
     for (i = 0; i < 8; i++) {
 #if defined(TARGET_PPC64)
         if (is_64b) {
-            pte0 = ldq_phys(env->htab_base + pteg_off + (i * 16));
-            pte1 = ldq_phys(env->htab_base + pteg_off + (i * 16) + 8);
+            if (env->external_htab) {
+                pte0 = ldq_p(env->external_htab + pteg_off + (i * 16));
+                pte1 = ldq_p(env->external_htab + pteg_off + (i * 16) + 8);
+            } else {
+                pte0 = ldq_phys(env->htab_base + pteg_off + (i * 16));
+                pte1 = ldq_phys(env->htab_base + pteg_off + (i * 16) + 8);
+            }
 
             /* We have a TLB that saves 4K pages, so let's
              * split a huge page to 4k chunks */
@@ -606,8 +611,13 @@ static inline int _find_pte(CPUState *env, mmu_ctx_t *ctx, int is_64b, int h,
         } else
 #endif
         {
-            pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8));
-            pte1 =  ldl_phys(env->htab_base + pteg_off + (i * 8) + 4);
+            if (env->external_htab) {
+                pte0 = ldl_p(env->external_htab + pteg_off + (i * 8));
+                pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4);
+            } else {
+                pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8));
+                pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4);
+            }
             r = pte32_check(ctx, pte0, pte1, h, rw, type);
             LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " "
                     TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n",
@@ -647,13 +657,23 @@ static inline int _find_pte(CPUState *env, mmu_ctx_t *ctx, int is_64b, int h,
         if (pte_update_flags(ctx, &pte1, ret, rw) == 1) {
 #if defined(TARGET_PPC64)
             if (is_64b) {
-                stq_phys_notdirty(env->htab_base + pteg_off + (good * 16) + 8,
-                                  pte1);
+                if (env->external_htab) {
+                    stq_p(env->external_htab + pteg_off + (good * 16) + 8,
+                          pte1);
+                } else {
+                    stq_phys_notdirty(env->htab_base + pteg_off +
+                                      (good * 16) + 8, pte1);
+                }
             } else
 #endif
             {
-                stl_phys_notdirty(env->htab_base + pteg_off + (good * 8) + 4,
-                                  pte1);
+                if (env->external_htab) {
+                    stl_p(env->external_htab + pteg_off + (good * 8) + 4,
+                          pte1);
+                } else {
+                    stl_phys_notdirty(env->htab_base + pteg_off +
+                                      (good * 8) + 4, pte1);
+                }
             }
         }
     }
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 16/27] Implement hcall based RTAS for pSeries machines
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (14 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 15/27] Virtual hash page table handling on pSeries machine David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 17/27] Implement assorted pSeries hcalls and RTAS methods David Gibson
                   ` (11 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

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 <dwg@au1.ibm.com>
---
 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 cf12691..a53d99f 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -233,7 +233,7 @@ obj-ppc-y += ppc_oldworld.o
 obj-ppc-y += ppc_newworld.o
 # IBM pSeries (sPAPR)
 ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
-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
 endif
 # PowerPC 4xx boards
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 cd05d3f..ff1eb3b 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;
@@ -195,6 +198,12 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
         exit(1);
     }
 
+    /* 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)));
 
     *fdt_size = fdt_totalsize(fdt);
@@ -224,11 +233,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;
@@ -237,6 +247,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) {
@@ -276,6 +288,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);
     assert(fdt != NULL);
 
     cpu_physical_memory_write(fdt_addr, fdt, fdt_size);
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 <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;
+        }
+    }
+
+    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<Pk*=^wC@M)vPAqm|U{LaFU{C-6M#cr<

literal 0
HcmV?d00001

diff --git a/pc-bios/spapr-rtas/Makefile b/pc-bios/spapr-rtas/Makefile
new file mode 100644
index 0000000..dc8b23e
--- /dev/null
+++ b/pc-bios/spapr-rtas/Makefile
@@ -0,0 +1,24 @@
+all: build-all
+# Dummy command so that make thinks it has done something
+	@true
+
+include ../../config-host.mak
+include $(SRC_PATH)/rules.mak
+
+$(call set-vpath, $(SRC_PATH)/pc-bios/spapr-rtas)
+
+.PHONY : all clean build-all
+
+#CFLAGS += -I$(SRC_PATH)
+#QEMU_CFLAGS = $(CFLAGS)
+
+build-all: spapr-rtas.bin
+
+%.img: %.o
+	$(call quiet-command,$(CC) -nostdlib -o $@ $<,"  Building $(TARGET_DIR)$@")
+
+%.bin: %.img
+	$(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@,"  Building $(TARGET_DIR)$@")
+
+clean:
+	rm -f *.o *.d *.img *.bin *~
diff --git a/pc-bios/spapr-rtas/spapr-rtas.S b/pc-bios/spapr-rtas/spapr-rtas.S
new file mode 100644
index 0000000..71a8554
--- /dev/null
+++ b/pc-bios/spapr-rtas/spapr-rtas.S
@@ -0,0 +1,36 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * Trivial in-partition RTAS implementation, based on a hypercall
+ *
+ * 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.
+ *
+ */
+
+#define	H_RTAS	0x72746173
+
+.globl	_start
+_start:
+	mr	4,3
+	lis	3,H_RTAS@h
+	ori	3,3,H_RTAS@l
+	sc	1
+	blr
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 17/27] Implement assorted pSeries hcalls and RTAS methods
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (15 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 16/27] Implement hcall based RTAS for pSeries machines David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 18/27] Implement the PAPR (pSeries) virtualized interrupt controller (xics) David Gibson
                   ` (10 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

This patch adds several small utility hypercalls and RTAS methods to
the pSeries platform emulation.  Specifically:

* 'display-character' rtas call

This just prints a character to the console, it's occasionally used
for early debug of the OS.  The support includes a hack to make this
RTAS call respond on the normal token value present on real hardware,
since some early debugging tools just assume this value without
checking the device tree.

* 'get-time-of-day' rtas call

This one just takes the host real time, converts to the PAPR described
format and returns it to the guest.

* 'power-off' rtas call

This one shuts down the emulated system.

* H_DABR hypercall

On pSeries, the DABR debug register is usually a hypervisor resource
and virtualized through this hypercall.  If the hypercall is not
present, Linux will under some circumstances attempt to manipulate the
DABR directly which will fail on this emulated machine.

This stub implementation is enough to stop that behaviour, although it
doesn't actually implement the requested DABR operations as yet.

Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 hw/spapr.c       |    2 +-
 hw/spapr_hcall.c |   10 ++++++++
 hw/spapr_rtas.c  |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 80 insertions(+), 1 deletions(-)

diff --git a/hw/spapr.c b/hw/spapr.c
index ff1eb3b..21e3c86 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -63,7 +63,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
     uint32_t start_prop = cpu_to_be32(initrd_base);
     uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
     uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
-    char hypertas_prop[] = "hcall-pft\0hcall-term";
+    char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr";
     int i;
     char *modelname;
     int ret;
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c
index 594e27d..02ccafd 100644
--- a/hw/spapr_hcall.c
+++ b/hw/spapr_hcall.c
@@ -248,6 +248,13 @@ static target_ulong h_protect(CPUState *env, sPAPREnvironment *spapr,
     return H_SUCCESS;
 }
 
+static target_ulong h_set_dabr(CPUState *env, sPAPREnvironment *spapr,
+                               target_ulong opcode, target_ulong *args)
+{
+    /* FIXME: actually implement this */
+    return H_HARDWARE;
+}
+
 static target_ulong h_rtas(sPAPREnvironment *spapr, target_ulong rtas_r3)
 {
     uint32_t token = ldl_phys(rtas_r3);
@@ -308,5 +315,8 @@ static void hypercall_init(void)
     spapr_register_hypercall(H_ENTER, h_enter);
     spapr_register_hypercall(H_REMOVE, h_remove);
     spapr_register_hypercall(H_PROTECT, h_protect);
+
+    /* hcall-dabr */
+    spapr_register_hypercall(H_SET_DABR, h_set_dabr);
 }
 device_init(hypercall_init);
diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c
index 3f090f5..7226853 100644
--- a/hw/spapr_rtas.c
+++ b/hw/spapr_rtas.c
@@ -38,6 +38,58 @@
 #define TOKEN_BASE      0x2000
 #define TOKEN_MAX       0x100
 
+static void rtas_display_character(sPAPREnvironment *spapr,
+                                   uint32_t token, uint32_t nargs,
+                                   target_ulong args,
+                                   uint32_t nret, target_ulong rets)
+{
+    uint8_t c = rtas_ld(args, 0);
+    VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, 0);
+
+    if (!sdev) {
+        rtas_st(rets, 0, -1);
+    } else {
+        vty_putchars(sdev, &c, sizeof(c));
+        rtas_st(rets, 0, 0);
+    }
+}
+
+static void rtas_get_time_of_day(sPAPREnvironment *spapr,
+                                 uint32_t token, uint32_t nargs,
+                                 target_ulong args,
+                                 uint32_t nret, target_ulong rets)
+{
+    struct tm tm;
+
+    if (nret != 8) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+
+    qemu_get_timedate(&tm, 0);
+
+    rtas_st(rets, 0, 0); /* Success */
+    rtas_st(rets, 1, tm.tm_year + 1900);
+    rtas_st(rets, 2, tm.tm_mon + 1);
+    rtas_st(rets, 3, tm.tm_mday);
+    rtas_st(rets, 4, tm.tm_hour);
+    rtas_st(rets, 5, tm.tm_min);
+    rtas_st(rets, 6, tm.tm_sec);
+    rtas_st(rets, 7, 0); /* we don't do nanoseconds */
+}
+
+static void rtas_power_off(sPAPREnvironment *spapr,
+                           uint32_t token, uint32_t nargs, target_ulong args,
+                           uint32_t nret, target_ulong rets)
+{
+    if (nargs != 2 || nret != 1) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+    qemu_system_shutdown_request();
+    rtas_st(rets, 0, 0);
+}
+
 static struct rtas_call {
     const char *name;
     spapr_rtas_fn fn;
@@ -59,6 +111,15 @@ target_ulong spapr_rtas_call(sPAPREnvironment *spapr,
         }
     }
 
+    /* HACK: Some Linux early debug code uses RTAS display-character,
+     * but assumes the token value is 0xa (which it is on some real
+     * machines) without looking it up in the device tree.  This
+     * special case makes this work */
+    if (token == 0xa) {
+        rtas_display_character(spapr, 0xa, nargs, args, nret, rets);
+        return H_SUCCESS;
+    }
+
     hcall_dprintf("Unknown RTAS token 0x%x\n", token);
     rtas_st(rets, 0, -3);
     return H_PARAMETER;
@@ -129,3 +190,11 @@ int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr,
     }
     return 0;
 }
+
+static void register_core_rtas(void)
+{
+    spapr_rtas_register("display-character", rtas_display_character);
+    spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
+    spapr_rtas_register("power-off", rtas_power_off);
+}
+device_init(register_core_rtas);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 18/27] Implement the PAPR (pSeries) virtualized interrupt controller (xics)
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (16 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 17/27] Implement assorted pSeries hcalls and RTAS methods David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 19/27] Add PAPR H_VIO_SIGNAL hypercall and infrastructure for VIO interrupts David Gibson
                   ` (9 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

PAPR defines an interrupt control architecture which is logically divided
into ICS (Interrupt Control Presentation, each unit is responsible for
presenting interrupts to a particular "interrupt server", i.e. CPU) and
ICS (Interrupt Control Source, each unit responsible for one or more
hardware interrupts as numbered globally across the system).  All PAPR
virtual IO devices expect to deliver interrupts via this mechanism.  In
Linux, this interrupt controller system is handled by the "xics" driver.

On pSeries systems, access to the interrupt controller is virtualized via
hypercalls and RTAS methods.  However, the virtualized interface is very
similar to the underlying interrupt controller hardware, and similar PICs
exist un-virtualized in some other systems.

This patch implements both the ICP and ICS sides of the PAPR interrupt
controller.  For now, only the hypercall virtualized interface is provided,
however it would be relatively straightforward to graft an emulated
register interface onto the underlying interrupt logic if we want to add
a machine with a hardware ICS/ICP system in the future.

There are some limitations in this implementation: it is assumed for now
that only one instance of the ICS exists, although a full xics system can
have several, each responsible for a different group of hardware irqs.
ICP/ICS can handle both level-sensitve (LSI) and message signalled (MSI)
interrupt inputs.  For now, this implementation supports only MSI
interrupts, since that is used by PAPR virtual IO devices.

Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 Makefile.target |    2 +-
 hw/spapr.c      |   26 +++
 hw/spapr.h      |    2 +
 hw/xics.c       |  486 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xics.h       |   39 +++++
 5 files changed, 554 insertions(+), 1 deletions(-)
 create mode 100644 hw/xics.c
 create mode 100644 hw/xics.h

diff --git a/Makefile.target b/Makefile.target
index a53d99f..c795428 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -234,7 +234,7 @@ obj-ppc-y += ppc_newworld.o
 # IBM pSeries (sPAPR)
 ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
 obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
-obj-ppc-y += spapr_vty.o
+obj-ppc-y += xics.o spapr_vty.o
 endif
 # 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 21e3c86..b8244c9 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -34,6 +34,7 @@
 
 #include "hw/spapr.h"
 #include "hw/spapr_vio.h"
+#include "hw/xics.h"
 
 #include <libfdt.h>
 
@@ -64,6 +65,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
     uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
     uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
     char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr";
+    uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
     int i;
     char *modelname;
     int ret;
@@ -125,6 +127,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
 
     for (i = 0; i < smp_cpus; i++) {
         CPUState *env = envs[i];
+        uint32_t gserver_prop[] = {cpu_to_be32(i), 0}; /* HACK! */
         char *nodename;
         uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
                            0xffffffff, 0xffffffff};
@@ -155,6 +158,9 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
                            pft_size_prop, sizeof(pft_size_prop))));
         _FDT((fdt_property_string(fdt, "status", "okay")));
         _FDT((fdt_property(fdt, "64-bit", NULL, 0)));
+        _FDT((fdt_property_cell(fdt, "ibm,ppc-interrupt-server#s", i)));
+        _FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s",
+                           gserver_prop, sizeof(gserver_prop))));
 
         if (envs[i]->mmu_model & POWERPC_MMU_1TSEG) {
             _FDT((fdt_property(fdt, "ibm,processor-segment-sizes",
@@ -176,6 +182,20 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
 
     _FDT((fdt_end_node(fdt)));
 
+    /* interrupt controller */
+    _FDT((fdt_begin_node(fdt, "interrupt-controller@0")));
+
+    _FDT((fdt_property_string(fdt, "device_type",
+                              "PowerPC-External-Interrupt-Presentation")));
+    _FDT((fdt_property_string(fdt, "compatible", "IBM,ppc-xicp")));
+    _FDT((fdt_property_cell(fdt, "reg", 0)));
+    _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
+    _FDT((fdt_property(fdt, "ibm,interrupt-server-ranges",
+                       interrupt_server_ranges_prop,
+                       sizeof(interrupt_server_ranges_prop))));
+
+    _FDT((fdt_end_node(fdt)));
+
     /* vdevice */
     _FDT((fdt_begin_node(fdt, "vdevice")));
 
@@ -183,6 +203,8 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
     _FDT((fdt_property_string(fdt, "compatible", "IBM,vdevice")));
     _FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
     _FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
+    _FDT((fdt_property_cell(fdt, "#interrupt-cells", 0x2)));
+    _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
 
     _FDT((fdt_end_node(fdt)));
 
@@ -296,6 +318,10 @@ static void ppc_spapr_init(ram_addr_t ram_size,
     }
     qemu_free(filename);
 
+    /* Set up Interrupt Controller */
+    spapr->icp = xics_system_init(smp_cpus, envs, MAX_SERIAL_PORTS);
+
+    /* Set up VIO bus */
     spapr->vio_bus = spapr_vio_bus_init();
 
     for (i = 0; i < MAX_SERIAL_PORTS; i++) {
diff --git a/hw/spapr.h b/hw/spapr.h
index 62a040f..de5c48c 100644
--- a/hw/spapr.h
+++ b/hw/spapr.h
@@ -2,9 +2,11 @@
 #define __HW_SPAPR_H__
 
 struct VIOsPAPRBus;
+struct icp_state;
 
 typedef struct sPAPREnvironment {
     struct VIOsPAPRBus *vio_bus;
+    struct icp_state *icp;
 } sPAPREnvironment;
 
 #define H_SUCCESS         0
diff --git a/hw/xics.c b/hw/xics.c
new file mode 100644
index 0000000..66047a6
--- /dev/null
+++ b/hw/xics.c
@@ -0,0 +1,486 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics
+ *
+ * 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 "hw.h"
+#include "hw/spapr.h"
+#include "hw/xics.h"
+
+#include <pthread.h>
+
+/*
+ * ICP: Presentation layer
+ */
+
+struct icp_server_state {
+    uint32_t xirr;
+    uint8_t pending_priority;
+    uint8_t mfrr;
+    qemu_irq output;
+};
+
+#define XISR_MASK  0x00ffffff
+#define CPPR_MASK  0xff000000
+
+#define XISR(ss)   (((ss)->xirr) & XISR_MASK)
+#define CPPR(ss)   (((ss)->xirr) >> 24)
+
+struct ics_state;
+
+struct icp_state {
+    long nr_servers;
+    struct icp_server_state *ss;
+    struct ics_state *ics;
+};
+
+static void ics_reject(struct ics_state *ics, int nr);
+static void ics_resend(struct ics_state *ics);
+static void ics_eoi(struct ics_state *ics, int nr);
+
+static void icp_check_ipi(struct icp_state *icp, int server)
+{
+    struct icp_server_state *ss = icp->ss + server;
+
+    if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) {
+        return;
+    }
+
+    if (XISR(ss)) {
+        ics_reject(icp->ics, XISR(ss));
+    }
+
+    ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI;
+    ss->pending_priority = ss->mfrr;
+    qemu_irq_raise(ss->output);
+}
+
+static void icp_resend(struct icp_state *icp, int server)
+{
+    struct icp_server_state *ss = icp->ss + server;
+
+    if (ss->mfrr < CPPR(ss)) {
+        icp_check_ipi(icp, server);
+    }
+    ics_resend(icp->ics);
+}
+
+static void icp_set_cppr(struct icp_state *icp, int server, uint8_t cppr)
+{
+    struct icp_server_state *ss = icp->ss + server;
+    uint8_t old_cppr;
+    uint32_t old_xisr;
+
+    old_cppr = CPPR(ss);
+    ss->xirr = (ss->xirr & ~CPPR_MASK) | (cppr << 24);
+
+    if (cppr < old_cppr) {
+        if (XISR(ss) && (cppr <= ss->pending_priority)) {
+            old_xisr = XISR(ss);
+            ss->xirr &= ~XISR_MASK; /* Clear XISR */
+            qemu_irq_lower(ss->output);
+            ics_reject(icp->ics, old_xisr);
+        }
+    } else {
+        if (!XISR(ss)) {
+            icp_resend(icp, server);
+        }
+    }
+}
+
+static void icp_set_mfrr(struct icp_state *icp, int nr, uint8_t mfrr)
+{
+    struct icp_server_state *ss = icp->ss + nr;
+
+    ss->mfrr = mfrr;
+    if (mfrr < CPPR(ss)) {
+        icp_check_ipi(icp, nr);
+    }
+}
+
+static uint32_t icp_accept(struct icp_server_state *ss)
+{
+    uint32_t xirr;
+
+    qemu_irq_lower(ss->output);
+    xirr = ss->xirr;
+    ss->xirr = ss->pending_priority << 24;
+    return xirr;
+}
+
+static void icp_eoi(struct icp_state *icp, int server, uint32_t xirr)
+{
+    struct icp_server_state *ss = icp->ss + server;
+
+    ics_eoi(icp->ics, xirr & XISR_MASK);
+    /* Send EOI -> ICS */
+    ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK);
+    if (!XISR(ss)) {
+        icp_resend(icp, server);
+    }
+}
+
+static void icp_irq(struct icp_state *icp, int server, int nr, uint8_t priority)
+{
+    struct icp_server_state *ss = icp->ss + server;
+
+    if ((priority >= CPPR(ss))
+        || (XISR(ss) && (ss->pending_priority <= priority))) {
+        ics_reject(icp->ics, nr);
+    } else {
+        if (XISR(ss)) {
+            ics_reject(icp->ics, XISR(ss));
+        }
+        ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK);
+        ss->pending_priority = priority;
+        qemu_irq_raise(ss->output);
+    }
+}
+
+/*
+ * ICS: Source layer
+ */
+
+struct ics_irq_state {
+    int server;
+    uint8_t priority;
+    uint8_t saved_priority;
+    /* int pending:1; */
+    /* int presented:1; */
+    int rejected:1;
+    int masked_pending:1;
+};
+
+struct ics_state {
+    int nr_irqs;
+    int offset;
+    qemu_irq *qirqs;
+    struct ics_irq_state *irqs;
+    struct icp_state *icp;
+};
+
+static int ics_valid_irq(struct ics_state *ics, uint32_t nr)
+{
+    return (nr >= ics->offset)
+        && (nr < (ics->offset + ics->nr_irqs));
+}
+
+static void ics_set_irq_msi(void *opaque, int nr, int val)
+{
+    struct ics_state *ics = (struct ics_state *)opaque;
+    struct ics_irq_state *irq = ics->irqs + nr;
+
+    if (val) {
+        if (irq->priority == 0xff) {
+            irq->masked_pending = 1;
+            /* masked pending */ ;
+        } else  {
+            icp_irq(ics->icp, irq->server, nr + ics->offset, irq->priority);
+        }
+    }
+}
+
+static void ics_reject_msi(struct ics_state *ics, int nr)
+{
+    struct ics_irq_state *irq = ics->irqs + nr - ics->offset;
+
+    irq->rejected = 1;
+}
+
+static void ics_resend_msi(struct ics_state *ics)
+{
+    int i;
+
+    for (i = 0; i < ics->nr_irqs; i++) {
+        struct ics_irq_state *irq = ics->irqs + i;
+
+        /* FIXME: filter by server#? */
+        if (irq->rejected) {
+            irq->rejected = 0;
+            if (irq->priority != 0xff) {
+                icp_irq(ics->icp, irq->server, i + ics->offset, irq->priority);
+            }
+        }
+    }
+}
+
+static void ics_write_xive_msi(struct ics_state *ics, int nr, int server,
+                               uint8_t priority)
+{
+    struct ics_irq_state *irq = ics->irqs + nr;
+
+    irq->server = server;
+    irq->priority = priority;
+
+    if (!irq->masked_pending || (priority == 0xff)) {
+        return;
+    }
+
+    irq->masked_pending = 0;
+    icp_irq(ics->icp, server, nr + ics->offset, priority);
+}
+
+static void ics_reject(struct ics_state *ics, int nr)
+{
+    ics_reject_msi(ics, nr);
+}
+
+static void ics_resend(struct ics_state *ics)
+{
+    ics_resend_msi(ics);
+}
+
+static void ics_eoi(struct ics_state *ics, int nr)
+{
+}
+
+/*
+ * Exported functions
+ */
+
+qemu_irq xics_find_qirq(struct icp_state *icp, int irq)
+{
+    if ((irq < icp->ics->offset)
+        || (irq >= (icp->ics->offset + icp->ics->nr_irqs))) {
+        return NULL;
+    }
+
+    return icp->ics->qirqs[irq - icp->ics->offset];
+}
+
+static target_ulong h_cppr(CPUState *env, sPAPREnvironment *spapr,
+                           target_ulong opcode, target_ulong *args)
+{
+    target_ulong cppr = args[0];
+
+    icp_set_cppr(spapr->icp, env->cpu_index, cppr);
+    return H_SUCCESS;
+}
+
+static target_ulong h_ipi(CPUState *env, sPAPREnvironment *spapr,
+                          target_ulong opcode, target_ulong *args)
+{
+    target_ulong server = args[0];
+    target_ulong mfrr = args[1];
+
+    if (server >= spapr->icp->nr_servers) {
+        return H_PARAMETER;
+    }
+
+    icp_set_mfrr(spapr->icp, server, mfrr);
+    return H_SUCCESS;
+
+}
+
+static target_ulong h_xirr(CPUState *env, sPAPREnvironment *spapr,
+                           target_ulong opcode, target_ulong *args)
+{
+    uint32_t xirr = icp_accept(spapr->icp->ss + env->cpu_index);
+
+    args[0] = xirr;
+    return H_SUCCESS;
+}
+
+static target_ulong h_eoi(CPUState *env, sPAPREnvironment *spapr,
+                          target_ulong opcode, target_ulong *args)
+{
+    target_ulong xirr = args[0];
+
+    icp_eoi(spapr->icp, env->cpu_index, xirr);
+    return H_SUCCESS;
+}
+
+static void rtas_set_xive(sPAPREnvironment *spapr, uint32_t token,
+                          uint32_t nargs, target_ulong args,
+                          uint32_t nret, target_ulong rets)
+{
+    struct ics_state *ics = spapr->icp->ics;
+    uint32_t nr, server, priority;
+
+    if ((nargs != 3) || (nret != 1)) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+
+    nr = rtas_ld(args, 0);
+    server = rtas_ld(args, 1);
+    priority = rtas_ld(args, 2);
+
+    if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers)
+        || (priority > 0xff)) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+
+    ics_write_xive_msi(ics, nr - ics->offset, server, priority);
+
+    rtas_st(rets, 0, 0); /* Success */
+}
+
+static void rtas_get_xive(sPAPREnvironment *spapr, uint32_t token,
+                          uint32_t nargs, target_ulong args,
+                          uint32_t nret, target_ulong rets)
+{
+    struct ics_state *ics = spapr->icp->ics;
+    uint32_t nr;
+
+    if ((nargs != 1) || (nret != 3)) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+
+    nr = rtas_ld(args, 0);
+
+    if (!ics_valid_irq(ics, nr)) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+
+    rtas_st(rets, 0, 0); /* Success */
+    rtas_st(rets, 1, ics->irqs[nr - ics->offset].server);
+    rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
+}
+
+static void rtas_int_off(sPAPREnvironment *spapr, uint32_t token,
+                         uint32_t nargs, target_ulong args,
+                         uint32_t nret, target_ulong rets)
+{
+    struct ics_state *ics = spapr->icp->ics;
+    uint32_t nr;
+
+    if ((nargs != 1) || (nret != 1)) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+
+    nr = rtas_ld(args, 0);
+
+    if (!ics_valid_irq(ics, nr)) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+
+    /* This is a NOP for now, since the described PAPR semantics don't
+     * seem to gel with what Linux does */
+#if 0
+    struct ics_irq_state *irq = xics->irqs + (nr - xics->offset);
+
+    irq->saved_priority = irq->priority;
+    ics_write_xive_msi(xics, nr - xics->offset, irq->server, 0xff);
+#endif
+
+    rtas_st(rets, 0, 0); /* Success */
+}
+
+static void rtas_int_on(sPAPREnvironment *spapr, uint32_t token,
+                        uint32_t nargs, target_ulong args,
+                        uint32_t nret, target_ulong rets)
+{
+    struct ics_state *ics = spapr->icp->ics;
+    uint32_t nr;
+
+    if ((nargs != 1) || (nret != 1)) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+
+    nr = rtas_ld(args, 0);
+
+    if (!ics_valid_irq(ics, nr)) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+
+    /* This is a NOP for now, since the described PAPR semantics don't
+     * seem to gel with what Linux does */
+#if 0
+    struct ics_irq_state *irq = xics->irqs + (nr - xics->offset);
+
+    ics_write_xive_msi(xics, nr - xics->offset,
+                       irq->server, irq->saved_priority);
+#endif
+
+    rtas_st(rets, 0, 0); /* Success */
+}
+
+struct icp_state *xics_system_init(int nr_servers, CPUState *servers[],
+                                   int nr_irqs)
+{
+    int i;
+    struct icp_state *icp;
+    struct ics_state *ics;
+
+    icp = qemu_mallocz(sizeof(*icp));
+    icp->nr_servers = nr_servers;
+    icp->ss = qemu_mallocz(nr_servers * sizeof(struct icp_server_state));
+
+    for (i = 0; i < nr_servers; i++) {
+        servers[i]->cpu_index = i;
+
+        switch (PPC_INPUT(servers[i])) {
+        case PPC_FLAGS_INPUT_POWER7:
+            icp->ss[i].output = servers[i]->irq_inputs[POWER7_INPUT_INT];
+            break;
+
+        case PPC_FLAGS_INPUT_970:
+            icp->ss[i].output = servers[i]->irq_inputs[PPC970_INPUT_INT];
+            break;
+
+        default:
+            hw_error("XICS interrupt model does not support this CPU bus "
+                     "model\n");
+            exit(1);
+        }
+
+        icp->ss[i].mfrr = 0xff;
+    }
+
+    ics = qemu_mallocz(sizeof(*ics));
+    ics->nr_irqs = nr_irqs;
+    ics->offset = 16;
+    ics->irqs = qemu_mallocz(nr_irqs * sizeof(struct ics_irq_state));
+
+    icp->ics = ics;
+    ics->icp = icp;
+
+    for (i = 0; i < nr_irqs; i++) {
+        ics->irqs[i].priority = 0xff;
+        ics->irqs[i].saved_priority = 0xff;
+    }
+
+    ics->qirqs = qemu_allocate_irqs(ics_set_irq_msi, ics, nr_irqs);
+
+    spapr_register_hypercall(H_CPPR, h_cppr);
+    spapr_register_hypercall(H_IPI, h_ipi);
+    spapr_register_hypercall(H_XIRR, h_xirr);
+    spapr_register_hypercall(H_EOI, h_eoi);
+
+    spapr_rtas_register("ibm,set-xive", rtas_set_xive);
+    spapr_rtas_register("ibm,get-xive", rtas_get_xive);
+    spapr_rtas_register("ibm,int-off", rtas_int_off);
+    spapr_rtas_register("ibm,int-on", rtas_int_on);
+
+    return icp;
+}
diff --git a/hw/xics.h b/hw/xics.h
new file mode 100644
index 0000000..096eeb3
--- /dev/null
+++ b/hw/xics.h
@@ -0,0 +1,39 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics
+ *
+ * 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.
+ *
+ */
+#if !defined(__XICS_H__)
+#define __XICS_H__
+
+#define XICS_IPI        0x2
+
+struct icp_state;
+
+qemu_irq xics_find_qirq(struct icp_state *icp, int irq);
+
+struct icp_state *xics_system_init(int nr_servers, CPUState *servers[],
+                                   int nr_irqs);
+
+#endif /* __XICS_H__ */
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 19/27] Add PAPR H_VIO_SIGNAL hypercall and infrastructure for VIO interrupts
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (17 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 18/27] Implement the PAPR (pSeries) virtualized interrupt controller (xics) David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 20/27] Add (virtual) interrupt to PAPR virtual tty device David Gibson
                   ` (8 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

This patch adds infrastructure to support interrupts from PAPR virtual IO
devices.  This includes correctly advertising those interrupts in the
device tree, and implementing the H_VIO_SIGNAL hypercall, used to
enable and disable individual device interrupts.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 hw/spapr.c     |    2 +-
 hw/spapr_vio.c |   37 +++++++++++++++++++++++++++++++++++++
 hw/spapr_vio.h |    6 ++++++
 3 files changed, 44 insertions(+), 1 deletions(-)

diff --git a/hw/spapr.c b/hw/spapr.c
index b8244c9..0f6f40b 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -64,7 +64,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
     uint32_t start_prop = cpu_to_be32(initrd_base);
     uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
     uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
-    char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr";
+    char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt";
     uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
     int i;
     char *modelname;
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index 10acb4c..605079c 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -105,6 +105,16 @@ static int vio_make_devnode(VIOsPAPRDevice *dev,
         }
     }
 
+    if (dev->qirq) {
+        uint32_t ints_prop[] = {cpu_to_be32(dev->vio_irq_num), 0};
+
+        ret = fdt_setprop(fdt, node_off, "interrupts", ints_prop,
+                          sizeof(ints_prop));
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
     if (info->devnode) {
         ret = (info->devnode)(dev, fdt, node_off);
         if (ret < 0) {
@@ -140,6 +150,30 @@ void spapr_vio_bus_register_withprop(VIOsPAPRDeviceInfo *info)
     qdev_register(&info->qdev);
 }
 
+static target_ulong h_vio_signal(CPUState *env, sPAPREnvironment *spapr,
+                                 target_ulong opcode,
+                                 target_ulong *args)
+{
+    target_ulong reg = args[0];
+    target_ulong mode = args[1];
+    VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+    VIOsPAPRDeviceInfo *info;
+
+    if (!dev) {
+        return H_PARAMETER;
+    }
+
+    info = (VIOsPAPRDeviceInfo *)dev->qdev.info;
+
+    if (mode & ~info->signal_mask) {
+        return H_PARAMETER;
+    }
+
+    dev->signal_state = mode;
+
+    return H_SUCCESS;
+}
+
 VIOsPAPRBus *spapr_vio_bus_init(void)
 {
     VIOsPAPRBus *bus;
@@ -156,6 +190,9 @@ VIOsPAPRBus *spapr_vio_bus_init(void)
     qbus = qbus_create(&spapr_vio_bus_info, dev, "spapr-vio");
     bus = DO_UPCAST(VIOsPAPRBus, bus, qbus);
 
+    /* hcall-vio */
+    spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal);
+
     for (qinfo = device_info_list; qinfo; qinfo = qinfo->next) {
         VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
 
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
index b164ad3..8a000c6 100644
--- a/hw/spapr_vio.h
+++ b/hw/spapr_vio.h
@@ -24,6 +24,9 @@
 typedef struct VIOsPAPRDevice {
     DeviceState qdev;
     uint32_t reg;
+    qemu_irq qirq;
+    uint32_t vio_irq_num;
+    target_ulong signal_state;
 } VIOsPAPRDevice;
 
 typedef struct VIOsPAPRBus {
@@ -33,6 +36,7 @@ typedef struct VIOsPAPRBus {
 typedef struct {
     DeviceInfo qdev;
     const char *dt_name, *dt_type, *dt_compatible;
+    target_ulong signal_mask;
     int (*init)(VIOsPAPRDevice *dev);
     void (*hcalls)(VIOsPAPRBus *bus);
     int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off);
@@ -43,6 +47,8 @@ extern VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg);
 extern void spapr_vio_bus_register_withprop(VIOsPAPRDeviceInfo *info);
 extern int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt);
 
+extern int spapr_vio_signal(VIOsPAPRDevice *dev, target_ulong mode);
+
 void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len);
 void spapr_vty_create(VIOsPAPRBus *bus,
                       uint32_t reg, CharDriverState *chardev);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 20/27] Add (virtual) interrupt to PAPR virtual tty device
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (18 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 19/27] Add PAPR H_VIO_SIGNAL hypercall and infrastructure for VIO interrupts David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 21/27] Implement TCE translation for sPAPR VIO David Gibson
                   ` (7 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

Now that we have implemented the PAPR "xics" virtualized interrupt
controller, we can add interrupts in PAPR VIO devices.  This patch adds
interrupt support to the PAPR virtual tty/console device.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 hw/spapr.c     |    6 ++++--
 hw/spapr_vio.h |    3 ++-
 hw/spapr_vty.c |   11 ++++++++++-
 3 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/hw/spapr.c b/hw/spapr.c
index 0f6f40b..56dba8e 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -261,6 +261,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
     long pteg_shift = 17;
     int fdt_size;
     char *filename;
+    int irq = 16;
 
     spapr = qemu_malloc(sizeof(*spapr));
     cpu_ppc_hypercall = emulate_spapr_hypercall;
@@ -324,9 +325,10 @@ static void ppc_spapr_init(ram_addr_t ram_size,
     /* Set up VIO bus */
     spapr->vio_bus = spapr_vio_bus_init();
 
-    for (i = 0; i < MAX_SERIAL_PORTS; i++) {
+    for (i = 0; i < MAX_SERIAL_PORTS; i++, irq++) {
         if (serial_hds[i]) {
-            spapr_vty_create(spapr->vio_bus, i, serial_hds[i]);
+            spapr_vty_create(spapr->vio_bus, i, serial_hds[i],
+                             xics_find_qirq(spapr->icp, irq), irq);
         }
     }
 
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
index 8a000c6..2013927 100644
--- a/hw/spapr_vio.h
+++ b/hw/spapr_vio.h
@@ -51,6 +51,7 @@ extern int spapr_vio_signal(VIOsPAPRDevice *dev, target_ulong mode);
 
 void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len);
 void spapr_vty_create(VIOsPAPRBus *bus,
-                      uint32_t reg, CharDriverState *chardev);
+                      uint32_t reg, CharDriverState *chardev,
+                      qemu_irq qirq, uint32_t vio_irq_num);
 
 #endif /* _HW_SPAPR_VIO_H */
diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c
index b4da6a8..6fc0105 100644
--- a/hw/spapr_vty.c
+++ b/hw/spapr_vty.c
@@ -24,6 +24,10 @@ static void vty_receive(void *opaque, const uint8_t *buf, int size)
     VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque;
     int i;
 
+    if ((dev->in == dev->out) && size) {
+        /* toggle line to simulate edge interrupt */
+        qemu_irq_pulse(dev->sdev.qirq);
+    }
     for (i = 0; i < size; i++) {
         assert((dev->in - dev->out) < VTERM_BUFSIZE);
         dev->buf[dev->in++ % VTERM_BUFSIZE] = buf[i];
@@ -112,14 +116,19 @@ static target_ulong h_get_term_char(CPUState *env, sPAPREnvironment *spapr,
 }
 
 void spapr_vty_create(VIOsPAPRBus *bus,
-                      uint32_t reg, CharDriverState *chardev)
+                      uint32_t reg, CharDriverState *chardev,
+                      qemu_irq qirq, uint32_t vio_irq_num)
 {
     DeviceState *dev;
+    VIOsPAPRDevice *sdev;
 
     dev = qdev_create(&bus->bus, "spapr-vty");
     qdev_prop_set_uint32(dev, "reg", reg);
     qdev_prop_set_chr(dev, "chardev", chardev);
     qdev_init_nofail(dev);
+    sdev = (VIOsPAPRDevice *)dev;
+    sdev->qirq = qirq;
+    sdev->vio_irq_num = vio_irq_num;
 }
 
 static void vty_hcalls(VIOsPAPRBus *bus)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 21/27] Implement TCE translation for sPAPR VIO
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (19 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 20/27] Add (virtual) interrupt to PAPR virtual tty device David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 22/27] Implement sPAPR Virtual LAN (ibmveth) David Gibson
                   ` (6 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

This patch implements the necessary infrastructure and hypercalls for
sPAPR's TCE (Translation Control Entry) IOMMU mechanism.  This is necessary
for all virtual IO devices which do DMA (i.e. nearly all of them).

Signed-off-by: Ben Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 hw/spapr.c     |    3 +-
 hw/spapr_vio.c |  238 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/spapr_vio.h |   32 ++++++++
 3 files changed, 272 insertions(+), 1 deletions(-)

diff --git a/hw/spapr.c b/hw/spapr.c
index 56dba8e..69759c5 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -64,7 +64,8 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
     uint32_t start_prop = cpu_to_be32(initrd_base);
     uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
     uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
-    char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt";
+    char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt"
+        "\0hcall-tce";
     uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
     int i;
     char *modelname;
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index 605079c..39d77ee 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -37,6 +37,7 @@
 #endif /* CONFIG_FDT */
 
 /* #define DEBUG_SPAPR */
+/* #define DEBUG_TCE */
 
 #ifdef DEBUG_SPAPR
 #define dprintf(fmt, ...) \
@@ -115,6 +116,28 @@ static int vio_make_devnode(VIOsPAPRDevice *dev,
         }
     }
 
+    if (dev->rtce_window_size) {
+        uint32_t dma_prop[] = {cpu_to_be32(dev->reg),
+                               0, 0,
+                               0, cpu_to_be32(dev->rtce_window_size)};
+
+        ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2);
+        if (ret < 0) {
+            return ret;
+        }
+
+        ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2);
+        if (ret < 0) {
+            return ret;
+        }
+
+        ret = fdt_setprop(fdt, node_off, "ibm,my-dma-window", dma_prop,
+                          sizeof(dma_prop));
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
     if (info->devnode) {
         ret = (info->devnode)(dev, fdt, node_off);
         if (ret < 0) {
@@ -126,6 +149,216 @@ static int vio_make_devnode(VIOsPAPRDevice *dev,
 }
 #endif /* CONFIG_FDT */
 
+/*
+ * RTCE handling
+ */
+
+static void rtce_init(VIOsPAPRDevice *dev)
+{
+    size_t size = (dev->rtce_window_size >> SPAPR_VIO_TCE_PAGE_SHIFT)
+        * sizeof(VIOsPAPR_RTCE);
+
+    if (size) {
+        dev->rtce_table = qemu_mallocz(size);
+    }
+}
+
+static target_ulong h_put_tce(CPUState *env, sPAPREnvironment *spapr,
+                              target_ulong opcode, target_ulong *args)
+{
+    target_ulong liobn = args[0];
+    target_ulong ioba = args[1];
+    target_ulong tce = args[2];
+    VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, liobn);
+    VIOsPAPR_RTCE *rtce;
+
+    if (!dev) {
+        hcall_dprintf("spapr_vio_put_tce on non-existent LIOBN "
+                      TARGET_FMT_lx "\n", liobn);
+        return H_PARAMETER;
+    }
+
+    ioba &= ~(SPAPR_VIO_TCE_PAGE_SIZE - 1);
+
+#ifdef DEBUG_TCE
+    fprintf(stderr, "spapr_vio_put_tce on %s  ioba 0x" TARGET_FMT_lx
+            "  TCE 0x" TARGET_FMT_lx "\n", dev->qdev.id, ioba, tce);
+#endif
+
+    if (ioba >= dev->rtce_window_size) {
+        hcall_dprintf("spapr_vio_put_tce on out-of-boards IOBA 0x"
+                      TARGET_FMT_lx "\n", ioba);
+        return H_PARAMETER;
+    }
+
+    rtce = dev->rtce_table + (ioba >> SPAPR_VIO_TCE_PAGE_SHIFT);
+    rtce->tce = tce;
+
+    return H_SUCCESS;
+}
+
+int spapr_vio_check_tces(VIOsPAPRDevice *dev, target_ulong ioba,
+                         target_ulong len, enum VIOsPAPR_TCEAccess access)
+{
+    int start, end, i;
+
+    start = ioba >> SPAPR_VIO_TCE_PAGE_SHIFT;
+    end = (ioba + len - 1) >> SPAPR_VIO_TCE_PAGE_SHIFT;
+
+    for (i = start; i <= end; i++) {
+        if ((dev->rtce_table[i].tce & access) != access) {
+#ifdef DEBUG_TCE
+            fprintf(stderr, "FAIL on %d\n", i);
+#endif
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+int spapr_tce_dma_write(VIOsPAPRDevice *dev, uint64_t taddr, const void *buf,
+                        uint32_t size)
+{
+#ifdef DEBUG_TCE
+    fprintf(stderr, "spapr_tce_dma_write taddr=0x%llx size=0x%x\n",
+            (unsigned long long)taddr, size);
+#endif
+
+    while (size) {
+        uint64_t tce;
+        uint32_t lsize;
+        uint64_t txaddr;
+
+        /* Check if we are in bound */
+        if (taddr >= dev->rtce_window_size) {
+#ifdef DEBUG_TCE
+            fprintf(stderr, "spapr_tce_dma_write out of bounds\n");
+#endif
+            return H_DEST_PARM;
+        }
+        tce = dev->rtce_table[taddr >> SPAPR_VIO_TCE_PAGE_SHIFT].tce;
+
+        /* How much til end of page ? */
+        lsize = MIN(size, ((~taddr) & SPAPR_VIO_TCE_PAGE_MASK) + 1);
+
+        /* Check TCE */
+        if (!(tce & 2)) {
+            return H_DEST_PARM;
+        }
+
+        /* Translate */
+        txaddr = (tce & ~SPAPR_VIO_TCE_PAGE_MASK) |
+            (taddr & SPAPR_VIO_TCE_PAGE_MASK);
+
+#ifdef DEBUG_TCE
+        fprintf(stderr, " -> write to txaddr=0x%llx, size=0x%x\n",
+                (unsigned long long)txaddr, lsize);
+#endif
+
+        /* Do it */
+        cpu_physical_memory_write(txaddr, buf, lsize);
+        buf += lsize;
+        taddr += lsize;
+        size -= lsize;
+    }
+    return 0;
+}
+
+int spapr_tce_dma_zero(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t size)
+{
+    /* FIXME: allocating a temp buffer is nasty, but just stepping
+     * through writing zeroes is awkward.  This will do for now. */
+    uint8_t zeroes[size];
+
+#ifdef DEBUG_TCE
+    fprintf(stderr, "spapr_tce_dma_zero taddr=0x%llx size=0x%x\n",
+            (unsigned long long)taddr, size);
+#endif
+
+    memset(zeroes, 0, size);
+    return spapr_tce_dma_write(dev, taddr, zeroes, size);
+}
+
+void stb_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint8_t val)
+{
+    spapr_tce_dma_write(dev, taddr, &val, sizeof(val));
+}
+
+void sth_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint16_t val)
+{
+    val = tswap16(val);
+    spapr_tce_dma_write(dev, taddr, &val, sizeof(val));
+}
+
+
+void stw_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t val)
+{
+    val = tswap32(val);
+    spapr_tce_dma_write(dev, taddr, &val, sizeof(val));
+}
+
+void stq_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint64_t val)
+{
+    val = tswap64(val);
+    spapr_tce_dma_write(dev, taddr, &val, sizeof(val));
+}
+
+int spapr_tce_dma_read(VIOsPAPRDevice *dev, uint64_t taddr, void *buf,
+                       uint32_t size)
+{
+#ifdef DEBUG_TCE
+    fprintf(stderr, "spapr_tce_dma_write taddr=0x%llx size=0x%x\n",
+            (unsigned long long)taddr, size);
+#endif
+
+    while (size) {
+        uint64_t tce;
+        uint32_t lsize;
+        uint64_t txaddr;
+
+        /* Check if we are in bound */
+        if (taddr >= dev->rtce_window_size) {
+#ifdef DEBUG_TCE
+            fprintf(stderr, "spapr_tce_dma_read out of bounds\n");
+#endif
+            return H_DEST_PARM;
+        }
+        tce = dev->rtce_table[taddr >> SPAPR_VIO_TCE_PAGE_SHIFT].tce;
+
+        /* How much til end of page ? */
+        lsize = MIN(size, ((~taddr) & SPAPR_VIO_TCE_PAGE_MASK) + 1);
+
+        /* Check TCE */
+        if (!(tce & 1)) {
+            return H_DEST_PARM;
+        }
+
+        /* Translate */
+        txaddr = (tce & ~SPAPR_VIO_TCE_PAGE_MASK) |
+            (taddr & SPAPR_VIO_TCE_PAGE_MASK);
+
+#ifdef DEBUG_TCE
+        fprintf(stderr, " -> write to txaddr=0x%llx, size=0x%x\n",
+                (unsigned long long)txaddr, lsize);
+#endif
+        /* Do it */
+        cpu_physical_memory_read(txaddr, buf, lsize);
+        buf += lsize;
+        taddr += lsize;
+        size -= lsize;
+    }
+    return H_SUCCESS;
+}
+
+uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr)
+{
+    uint64_t val;
+
+    spapr_tce_dma_read(dev, taddr, &val, sizeof(val));
+    return tswap64(val);
+}
+
 static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo)
 {
     VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
@@ -138,6 +371,8 @@ static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo)
 
     dev->qdev.id = id;
 
+    rtce_init(dev);
+
     return info->init(dev);
 }
 
@@ -193,6 +428,9 @@ VIOsPAPRBus *spapr_vio_bus_init(void)
     /* hcall-vio */
     spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal);
 
+    /* hcall-tce */
+    spapr_register_hypercall(H_PUT_TCE, h_put_tce);
+
     for (qinfo = device_info_list; qinfo; qinfo = qinfo->next) {
         VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
 
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
index 2013927..9d864c2 100644
--- a/hw/spapr_vio.h
+++ b/hw/spapr_vio.h
@@ -21,12 +21,29 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#define SPAPR_VIO_TCE_PAGE_SHIFT   12
+#define SPAPR_VIO_TCE_PAGE_SIZE    (1ULL << SPAPR_VIO_TCE_PAGE_SHIFT)
+#define SPAPR_VIO_TCE_PAGE_MASK    (SPAPR_VIO_TCE_PAGE_SIZE - 1)
+
+enum VIOsPAPR_TCEAccess {
+    SPAPR_TCE_FAULT = 0,
+    SPAPR_TCE_RO = 1,
+    SPAPR_TCE_WO = 2,
+    SPAPR_TCE_RW = 3,
+};
+
+typedef struct VIOsPAPR_RTCE {
+    uint64_t tce;
+} VIOsPAPR_RTCE;
+
 typedef struct VIOsPAPRDevice {
     DeviceState qdev;
     uint32_t reg;
     qemu_irq qirq;
     uint32_t vio_irq_num;
     target_ulong signal_state;
+    uint32_t rtce_window_size;
+    VIOsPAPR_RTCE *rtce_table;
 } VIOsPAPRDevice;
 
 typedef struct VIOsPAPRBus {
@@ -49,6 +66,21 @@ extern int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt);
 
 extern int spapr_vio_signal(VIOsPAPRDevice *dev, target_ulong mode);
 
+int spapr_vio_check_tces(VIOsPAPRDevice *dev, target_ulong ioba,
+                         target_ulong len,
+                         enum VIOsPAPR_TCEAccess access);
+
+int spapr_tce_dma_read(VIOsPAPRDevice *dev, uint64_t taddr,
+                       void *buf, uint32_t size);
+int spapr_tce_dma_write(VIOsPAPRDevice *dev, uint64_t taddr,
+                        const void *buf, uint32_t size);
+int spapr_tce_dma_zero(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t size);
+void stb_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint8_t val);
+void sth_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint16_t val);
+void stw_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t val);
+void stq_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint64_t val);
+uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr);
+
 void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len);
 void spapr_vty_create(VIOsPAPRBus *bus,
                       uint32_t reg, CharDriverState *chardev,
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 22/27] Implement sPAPR Virtual LAN (ibmveth)
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (20 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 21/27] Implement TCE translation for sPAPR VIO David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 23/27] Implement PAPR CRQ hypercalls David Gibson
                   ` (5 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

This patch implements the PAPR specified Inter Virtual Machine Logical
LAN; that is the virtual hardware used by the Linux ibmveth driver.

Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: David Gibson <dwg@au1.ibm.com>

Conflicts:

	Makefile.target
---
 Makefile.target |    2 +-
 hw/spapr.c      |   21 ++-
 hw/spapr_llan.c |  521 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/spapr_vio.h  |    3 +
 4 files changed, 545 insertions(+), 2 deletions(-)
 create mode 100644 hw/spapr_llan.c

diff --git a/Makefile.target b/Makefile.target
index c795428..cd7bb41 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -234,7 +234,7 @@ obj-ppc-y += ppc_newworld.o
 # IBM pSeries (sPAPR)
 ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
 obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
-obj-ppc-y += xics.o spapr_vty.o
+obj-ppc-y += xics.o spapr_vty.o spapr_llan.o
 endif
 # 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 69759c5..18660dc 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -27,6 +27,7 @@
 #include "sysemu.h"
 #include "hw.h"
 #include "elf.h"
+#include "net.h"
 
 #include "hw/boards.h"
 #include "hw/ppc.h"
@@ -321,7 +322,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
     qemu_free(filename);
 
     /* Set up Interrupt Controller */
-    spapr->icp = xics_system_init(smp_cpus, envs, MAX_SERIAL_PORTS);
+    spapr->icp = xics_system_init(smp_cpus, envs, MAX_SERIAL_PORTS + nb_nics);
 
     /* Set up VIO bus */
     spapr->vio_bus = spapr_vio_bus_init();
@@ -333,6 +334,24 @@ static void ppc_spapr_init(ram_addr_t ram_size,
         }
     }
 
+    for (i = 0; i < nb_nics; i++, irq++) {
+        NICInfo *nd = &nd_table[i];
+
+        if (!nd->model) {
+            nd->model = qemu_strdup("ibmveth");
+        }
+
+        if (strcmp(nd->model, "ibmveth") == 0) {
+            spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd,
+                              xics_find_qirq(spapr->icp, irq), irq);
+        } else {
+            fprintf(stderr, "pSeries (sPAPR) platform does not support "
+                    "NIC model '%s' (only ibmveth is supported)\n",
+                    nd->model);
+            exit(1);
+        }
+    }
+
     if (kernel_filename) {
         uint64_t lowaddr = 0;
 
diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c
new file mode 100644
index 0000000..1d83fd5
--- /dev/null
+++ b/hw/spapr_llan.c
@@ -0,0 +1,521 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * PAPR Inter-VM Logical Lan, aka ibmveth
+ *
+ * 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 "hw.h"
+#include "net.h"
+#include "hw/qdev.h"
+#include "hw/spapr.h"
+#include "hw/spapr_vio.h"
+
+#include <libfdt.h>
+
+#define ETH_ALEN        6
+#define MAX_PACKET_SIZE 65536
+
+/*#define DEBUG*/
+
+#ifdef DEBUG
+#define dprintf(fmt...) do { fprintf(stderr, fmt); } while (0)
+#else
+#define dprintf(fmt...)
+#endif
+
+/*
+ * Virtual LAN device
+ */
+
+typedef uint64_t vlan_bd_t;
+
+#define VLAN_BD_VALID        0x8000000000000000ULL
+#define VLAN_BD_TOGGLE       0x4000000000000000ULL
+#define VLAN_BD_NO_CSUM      0x0200000000000000ULL
+#define VLAN_BD_CSUM_GOOD    0x0100000000000000ULL
+#define VLAN_BD_LEN_MASK     0x00ffffff00000000ULL
+#define VLAN_BD_LEN(bd)      (((bd) & VLAN_BD_LEN_MASK) >> 32)
+#define VLAN_BD_ADDR_MASK    0x00000000ffffffffULL
+#define VLAN_BD_ADDR(bd)     ((bd) & VLAN_BD_ADDR_MASK)
+
+#define VLAN_VALID_BD(addr, len) (VLAN_BD_VALID | \
+                                  (((len) << 32) & VLAN_BD_LEN_MASK) |  \
+                                  (addr & VLAN_BD_ADDR_MASK))
+
+#define VLAN_RXQC_TOGGLE     0x80
+#define VLAN_RXQC_VALID      0x40
+#define VLAN_RXQC_NO_CSUM    0x02
+#define VLAN_RXQC_CSUM_GOOD  0x01
+
+#define VLAN_RQ_ALIGNMENT    16
+#define VLAN_RXQ_BD_OFF      0
+#define VLAN_FILTER_BD_OFF   8
+#define VLAN_RX_BDS_OFF      16
+#define VLAN_MAX_BUFS        ((SPAPR_VIO_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF) / 8)
+
+typedef struct VIOsPAPRVLANDevice {
+    VIOsPAPRDevice sdev;
+    NICConf nicconf;
+    NICState *nic;
+    int isopen;
+    target_ulong buf_list;
+    int add_buf_ptr, use_buf_ptr, rx_bufs;
+    target_ulong rxq_ptr;
+} VIOsPAPRVLANDevice;
+
+static int spapr_vlan_can_receive(VLANClientState *nc)
+{
+    VIOsPAPRVLANDevice *dev = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    return (dev->isopen && dev->rx_bufs > 0);
+}
+
+static ssize_t spapr_vlan_receive(VLANClientState *nc, const uint8_t *buf,
+                                  size_t size)
+{
+    VIOsPAPRDevice *sdev = DO_UPCAST(NICState, nc, nc)->opaque;
+    VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
+    vlan_bd_t rxq_bd = ldq_tce(sdev, dev->buf_list + VLAN_RXQ_BD_OFF);
+    vlan_bd_t bd;
+    int buf_ptr = dev->use_buf_ptr;
+    uint64_t handle;
+    uint8_t control;
+
+    dprintf("spapr_vlan_receive() [%s] rx_bufs=%d\n", sdev->qdev.id,
+            dev->rx_bufs);
+
+    if (!dev->isopen) {
+        return -1;
+    }
+
+    if (!dev->rx_bufs) {
+        return -1;
+    }
+
+    do {
+        buf_ptr += 8;
+        if (buf_ptr >= SPAPR_VIO_TCE_PAGE_SIZE) {
+            buf_ptr = VLAN_RX_BDS_OFF;
+        }
+
+        bd = ldq_tce(sdev, dev->buf_list + buf_ptr);
+        dprintf("use_buf_ptr=%d bd=0x%016llx\n",
+                buf_ptr, (unsigned long long)bd);
+    } while ((!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8)))
+             && (buf_ptr != dev->use_buf_ptr));
+
+    if (!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) {
+        /* Failed to find a suitable buffer */
+        return -1;
+    }
+
+    /* Remove the buffer from the pool */
+    dev->rx_bufs--;
+    dev->use_buf_ptr = buf_ptr;
+    stq_tce(sdev, dev->buf_list + dev->use_buf_ptr, 0);
+
+    dprintf("Found buffer: ptr=%d num=%d\n", dev->use_buf_ptr, dev->rx_bufs);
+
+    /* Transfer the packet data */
+    if (spapr_tce_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) {
+        return -1;
+    }
+
+    dprintf("spapr_vlan_receive: DMA write completed\n");
+
+    /* Update the receive queue */
+    control = VLAN_RXQC_TOGGLE | VLAN_RXQC_VALID;
+    if (rxq_bd & VLAN_BD_TOGGLE) {
+        control ^= VLAN_RXQC_TOGGLE;
+    }
+
+    handle = ldq_tce(sdev, VLAN_BD_ADDR(bd));
+    stq_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8, handle);
+    stw_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 4, size);
+    sth_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 2, 8);
+    stb_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr, control);
+
+    dprintf("wrote rxq entry (ptr=0x%llx): 0x%016llx 0x%016llx\n",
+            (unsigned long long)dev->rxq_ptr,
+            (unsigned long long)ldq_tce(sdev, VLAN_BD_ADDR(rxq_bd) +
+                                        dev->rxq_ptr),
+            (unsigned long long)ldq_tce(sdev, VLAN_BD_ADDR(rxq_bd) +
+                                        dev->rxq_ptr + 8));
+
+    dev->rxq_ptr += 16;
+    if (dev->rxq_ptr >= VLAN_BD_LEN(rxq_bd)) {
+        dev->rxq_ptr = 0;
+        stq_tce(sdev, dev->buf_list + VLAN_RXQ_BD_OFF, rxq_bd ^ VLAN_BD_TOGGLE);
+    }
+
+    if (sdev->signal_state & 1) {
+        qemu_irq_pulse(sdev->qirq);
+    }
+
+    return size;
+}
+
+static NetClientInfo net_spapr_vlan_info = {
+    .type = NET_CLIENT_TYPE_NIC,
+    .size = sizeof(NICState),
+    .can_receive = spapr_vlan_can_receive,
+    .receive = spapr_vlan_receive,
+};
+
+static int spapr_vlan_init(VIOsPAPRDevice *sdev)
+{
+    VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
+    VIOsPAPRBus *bus;
+
+    bus = DO_UPCAST(VIOsPAPRBus, bus, sdev->qdev.parent_bus);
+
+    qemu_macaddr_default_if_unset(&dev->nicconf.macaddr);
+
+    dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf,
+                            sdev->qdev.info->name, sdev->qdev.id, dev);
+    qemu_format_nic_info_str(&dev->nic->nc, dev->nicconf.macaddr.a);
+
+    return 0;
+}
+
+void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd,
+                       qemu_irq qirq, uint32_t vio_irq_num)
+{
+    DeviceState *dev;
+    VIOsPAPRDevice *sdev;
+
+    dev = qdev_create(&bus->bus, "spapr-vlan");
+    qdev_prop_set_uint32(dev, "reg", reg);
+
+    qdev_set_nic_properties(dev, nd);
+
+    qdev_init_nofail(dev);
+    sdev = (VIOsPAPRDevice *)dev;
+    sdev->qirq = qirq;
+    sdev->vio_irq_num = vio_irq_num;
+}
+
+static int spapr_vlan_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
+{
+    VIOsPAPRVLANDevice *vdev = (VIOsPAPRVLANDevice *)dev;
+    uint8_t padded_mac[8] = {0, 0};
+    int ret;
+
+    /* Some old phyp versions give the mac address in an 8-byte
+     * property.  The kernel driver has an insane workaround for this;
+     * rather than doing the obvious thing and checking the property
+     * length, it checks whether the first byte has 0b10 in the low
+     * bits.  If a correct 6-byte property has a different first byte
+     * the kernel will get the wrong mac address, overrunning its
+     * buffer in the process (read only, thank goodness).
+     *
+     * Here we workaround the kernel workaround by always supplying an
+     * 8-byte property, with the mac address in the last six bytes */
+    memcpy(&padded_mac[2], &vdev->nicconf.macaddr, ETH_ALEN);
+    ret = fdt_setprop(fdt, node_off, "local-mac-address",
+                      padded_mac, sizeof(padded_mac));
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = fdt_setprop_cell(fdt, node_off, "ibm,mac-address-filters", 0);
+    if (ret < 0) {
+        return ret;
+    }
+
+    return 0;
+}
+
+static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd,
+                    target_ulong alignment)
+{
+    if ((VLAN_BD_ADDR(bd) % alignment)
+        || (VLAN_BD_LEN(bd) % alignment)) {
+        return -1;
+    }
+
+    if (spapr_vio_check_tces(&dev->sdev, VLAN_BD_ADDR(bd),
+                             VLAN_BD_LEN(bd), SPAPR_TCE_RW) != 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static target_ulong h_register_logical_lan(CPUState *env,
+                                           sPAPREnvironment *spapr,
+                                           target_ulong opcode,
+                                           target_ulong *args)
+{
+    target_ulong reg = args[0];
+    target_ulong buf_list = args[1];
+    target_ulong rec_queue = args[2];
+    target_ulong filter_list = args[3];
+    VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+    VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
+    vlan_bd_t filter_list_bd;
+#ifdef DEBUG
+    target_ulong mac_address = args[4];
+#endif
+
+    if (!dev) {
+        return H_PARAMETER;
+    }
+
+    if (dev->isopen) {
+        hcall_dprintf("H_REGISTER_LOGICAL_LAN called twice without "
+                      "H_FREE_LOGICAL_LAN\n");
+        return H_RESOURCE;
+    }
+
+    if (check_bd(dev, VLAN_VALID_BD(buf_list, SPAPR_VIO_TCE_PAGE_SIZE),
+                 SPAPR_VIO_TCE_PAGE_SIZE) < 0) {
+        hcall_dprintf("Bad buf_list 0x" TARGET_FMT_lx " for "
+                      "H_REGISTER_LOGICAL_LAN\n", buf_list);
+        return H_PARAMETER;
+    }
+
+    filter_list_bd = VLAN_VALID_BD(filter_list, SPAPR_VIO_TCE_PAGE_SIZE);
+    if (check_bd(dev, filter_list_bd, SPAPR_VIO_TCE_PAGE_SIZE) < 0) {
+        hcall_dprintf("Bad filter_list 0x" TARGET_FMT_lx " for "
+                      "H_REGISTER_LOGICAL_LAN\n", filter_list);
+        return H_PARAMETER;
+    }
+
+    if (!(rec_queue & VLAN_BD_VALID)
+        || (check_bd(dev, rec_queue, VLAN_RQ_ALIGNMENT) < 0)) {
+        hcall_dprintf("Bad receive queue for H_REGISTER_LOGICAL_LAN\n");
+        return H_PARAMETER;
+    }
+
+    dev->buf_list = buf_list;
+    sdev->signal_state = 0;
+
+    rec_queue &= ~VLAN_BD_TOGGLE;
+
+    /* Initialize the buffer list */
+    stq_tce(sdev, buf_list, rec_queue);
+    stq_tce(sdev, buf_list + 8, filter_list_bd);
+    spapr_tce_dma_zero(sdev, buf_list + VLAN_RX_BDS_OFF,
+                       SPAPR_VIO_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF);
+    dev->add_buf_ptr = VLAN_RX_BDS_OFF - 8;
+    dev->use_buf_ptr = VLAN_RX_BDS_OFF - 8;
+    dev->rx_bufs = 0;
+    dev->rxq_ptr = 0;
+
+    /* Initialize the receive queue */
+    spapr_tce_dma_zero(sdev, VLAN_BD_ADDR(rec_queue), VLAN_BD_LEN(rec_queue));
+
+    dev->isopen = 1;
+    return H_SUCCESS;
+}
+
+
+static target_ulong h_free_logical_lan(CPUState *env, sPAPREnvironment *spapr,
+                                       target_ulong opcode, target_ulong *args)
+{
+    target_ulong reg = args[0];
+    VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+    VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
+
+    if (!dev) {
+        return H_PARAMETER;
+    }
+
+    if (!dev->isopen) {
+        hcall_dprintf("H_FREE_LOGICAL_LAN called without "
+                      "H_REGISTER_LOGICAL_LAN\n");
+        return H_RESOURCE;
+    }
+
+    dev->buf_list = 0;
+    dev->rx_bufs = 0;
+    dev->isopen = 0;
+    return H_SUCCESS;
+}
+
+static target_ulong h_add_logical_lan_buffer(CPUState *env,
+                                             sPAPREnvironment *spapr,
+                                             target_ulong opcode,
+                                             target_ulong *args)
+{
+    target_ulong reg = args[0];
+    target_ulong buf = args[1];
+    VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+    VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
+    vlan_bd_t bd;
+
+    dprintf("H_ADD_LOGICAL_LAN_BUFFER(0x" TARGET_FMT_lx
+            ", 0x" TARGET_FMT_lx ")\n", reg, buf);
+
+    if (!sdev) {
+        hcall_dprintf("Wrong device in h_add_logical_lan_buffer\n");
+        return H_PARAMETER;
+    }
+
+    if ((check_bd(dev, buf, 4) < 0)
+        || (VLAN_BD_LEN(buf) < 16)) {
+        hcall_dprintf("Bad buffer enqueued in h_add_logical_lan_buffer\n");
+        return H_PARAMETER;
+    }
+
+    if (!dev->isopen || dev->rx_bufs >= VLAN_MAX_BUFS) {
+        return H_RESOURCE;
+    }
+
+    do {
+        dev->add_buf_ptr += 8;
+        if (dev->add_buf_ptr >= SPAPR_VIO_TCE_PAGE_SIZE) {
+            dev->add_buf_ptr = VLAN_RX_BDS_OFF;
+        }
+
+        bd = ldq_tce(sdev, dev->buf_list + dev->add_buf_ptr);
+    } while (bd & VLAN_BD_VALID);
+
+    stq_tce(sdev, dev->buf_list + dev->add_buf_ptr, buf);
+
+    dev->rx_bufs++;
+
+    dprintf("h_add_logical_lan_buffer():  Added buf  ptr=%d  rx_bufs=%d"
+            " bd=0x%016llx\n", dev->add_buf_ptr, dev->rx_bufs,
+            (unsigned long long)buf);
+
+    return H_SUCCESS;
+}
+
+static target_ulong h_send_logical_lan(CPUState *env, sPAPREnvironment *spapr,
+                                       target_ulong opcode, target_ulong *args)
+{
+    target_ulong reg = args[0];
+    target_ulong *bufs = args + 1;
+    target_ulong continue_token = args[7];
+    VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+    VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
+    unsigned total_len;
+    uint8_t *lbuf, *p;
+    int i, nbufs;
+    int ret;
+
+    dprintf("H_SEND_LOGICAL_LAN(0x" TARGET_FMT_lx ", <bufs>, 0x"
+            TARGET_FMT_lx ")\n", reg, continue_token);
+
+    if (!sdev) {
+        return H_PARAMETER;
+    }
+
+    dprintf("rxbufs = %d\n", dev->rx_bufs);
+
+    if (!dev->isopen) {
+        return H_DROPPED;
+    }
+
+    if (continue_token) {
+        return H_HARDWARE; /* FIXME actually handle this */
+    }
+
+    total_len = 0;
+    for (i = 0; i < 6; i++) {
+        dprintf("   buf desc: 0x" TARGET_FMT_lx "\n", bufs[i]);
+        if (!(bufs[i] & VLAN_BD_VALID)) {
+            break;
+        }
+        total_len += VLAN_BD_LEN(bufs[i]);
+    }
+
+    nbufs = i;
+    dprintf("h_send_logical_lan() %d buffers, total length 0x%x\n",
+            nbufs, total_len);
+
+    if (total_len == 0) {
+        return H_SUCCESS;
+    }
+
+    if (total_len > MAX_PACKET_SIZE) {
+        /* Don't let the guest force too large an allocation */
+        return H_RESOURCE;
+    }
+
+    lbuf = alloca(total_len);
+    p = lbuf;
+    for (i = 0; i < nbufs; i++) {
+        ret = spapr_tce_dma_read(sdev, VLAN_BD_ADDR(bufs[i]),
+                                 p, VLAN_BD_LEN(bufs[i]));
+        if (ret < 0) {
+            return ret;
+        }
+
+        p += VLAN_BD_LEN(bufs[i]);
+    }
+
+    qemu_send_packet(&dev->nic->nc, lbuf, total_len);
+
+    return H_SUCCESS;
+}
+
+static target_ulong h_multicast_ctrl(CPUState *env, sPAPREnvironment *spapr,
+                                     target_ulong opcode, target_ulong *args)
+{
+    target_ulong reg = args[0];
+    VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+
+    if (!dev) {
+        return H_PARAMETER;
+    }
+
+    return H_SUCCESS;
+}
+
+static void vlan_hcalls(VIOsPAPRBus *bus)
+{
+    spapr_register_hypercall(H_REGISTER_LOGICAL_LAN, h_register_logical_lan);
+    spapr_register_hypercall(H_FREE_LOGICAL_LAN, h_free_logical_lan);
+    spapr_register_hypercall(H_SEND_LOGICAL_LAN, h_send_logical_lan);
+    spapr_register_hypercall(H_ADD_LOGICAL_LAN_BUFFER,
+                             h_add_logical_lan_buffer);
+    spapr_register_hypercall(H_MULTICAST_CTRL, h_multicast_ctrl);
+}
+
+static VIOsPAPRDeviceInfo spapr_vlan = {
+    .init = spapr_vlan_init,
+    .devnode = spapr_vlan_devnode,
+    .dt_name = "l-lan",
+    .dt_type = "network",
+    .dt_compatible = "IBM,l-lan",
+    .signal_mask = 0x1,
+    .hcalls = vlan_hcalls,
+    .qdev.name = "spapr-vlan",
+    .qdev.size = sizeof(VIOsPAPRVLANDevice),
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x1000),
+        DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice, rtce_window_size,
+                           0x10000000),
+        DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static void spapr_vlan_register(void)
+{
+    spapr_vio_bus_register_withprop(&spapr_vlan);
+}
+device_init(spapr_vlan_register);
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
index 9d864c2..4cfaf55 100644
--- a/hw/spapr_vio.h
+++ b/hw/spapr_vio.h
@@ -86,4 +86,7 @@ void spapr_vty_create(VIOsPAPRBus *bus,
                       uint32_t reg, CharDriverState *chardev,
                       qemu_irq qirq, uint32_t vio_irq_num);
 
+void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd,
+                       qemu_irq qirq, uint32_t vio_irq_num);
+
 #endif /* _HW_SPAPR_VIO_H */
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 23/27] Implement PAPR CRQ hypercalls
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (21 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 22/27] Implement sPAPR Virtual LAN (ibmveth) David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 24/27] Implement PAPR virtual SCSI interface (ibmvscsi) David Gibson
                   ` (4 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

This patch implements the infrastructure and hypercalls necessary for the
PAPR specified CRQ (Command Request Queue) mechanism.  This general
request queueing system is used by many of the PAPR virtual IO devices,
including the virtual scsi adapter.

Signed-off-by: Ben Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 hw/spapr.c           |    2 +-
 hw/spapr_vio.c       |  160 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/spapr_vio.h       |   12 ++++
 target-ppc/kvm_ppc.h |   11 ++++
 4 files changed, 184 insertions(+), 1 deletions(-)

diff --git a/hw/spapr.c b/hw/spapr.c
index 18660dc..02a3bbe 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -66,7 +66,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
     uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
     uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
     char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt"
-        "\0hcall-tce";
+        "\0hcall-tce\0hcall-vio";
     uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
     int i;
     char *modelname;
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index 39d77ee..8f14fcc 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -28,6 +28,7 @@
 #include "hw/sysbus.h"
 #include "kvm.h"
 #include "device_tree.h"
+#include "kvm_ppc.h"
 
 #include "hw/spapr.h"
 #include "hw/spapr_vio.h"
@@ -359,6 +360,159 @@ uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr)
     return tswap64(val);
 }
 
+/*
+ * CRQ handling
+ */
+static target_ulong h_reg_crq(CPUState *env, sPAPREnvironment *spapr,
+                              target_ulong opcode, target_ulong *args)
+{
+    target_ulong reg = args[0];
+    target_ulong queue_addr = args[1];
+    target_ulong queue_len = args[2];
+    VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+
+    if (!dev) {
+        hcall_dprintf("h_reg_crq on non-existent unit 0x"
+                      TARGET_FMT_lx "\n", reg);
+        return H_PARAMETER;
+    }
+
+    /* We can't grok a queue size bigger than 256M for now */
+    if (queue_len < 0x1000 || queue_len > 0x10000000) {
+        hcall_dprintf("h_reg_crq, queue size too small or too big (0x%llx)\n",
+                      (unsigned long long)queue_len);
+        return H_PARAMETER;
+    }
+
+    /* Check queue alignment */
+    if (queue_addr & 0xfff) {
+        hcall_dprintf("h_reg_crq, queue not aligned (0x%llx)\n",
+                      (unsigned long long)queue_addr);
+        return H_PARAMETER;
+    }
+
+    /* Check if device supports CRQs */
+    if (!dev->crq.SendFunc) {
+        return H_NOT_FOUND;
+    }
+
+
+    /* Already a queue ? */
+    if (dev->crq.qsize) {
+        return H_RESOURCE;
+    }
+    dev->crq.qladdr = queue_addr;
+    dev->crq.qsize = queue_len;
+    dev->crq.qnext = 0;
+
+    dprintf("CRQ for dev 0x" TARGET_FMT_lx " registered at 0x"
+            TARGET_FMT_lx "/0x" TARGET_FMT_lx "\n",
+            reg, queue_addr, queue_len);
+    return H_SUCCESS;
+}
+
+static target_ulong h_free_crq(CPUState *env, sPAPREnvironment *spapr,
+                               target_ulong opcode, target_ulong *args)
+{
+    target_ulong reg = args[0];
+    VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+
+    if (!dev) {
+        hcall_dprintf("h_free_crq on non-existent unit 0x"
+                      TARGET_FMT_lx "\n", reg);
+        return H_PARAMETER;
+    }
+
+    dev->crq.qladdr = 0;
+    dev->crq.qsize = 0;
+    dev->crq.qnext = 0;
+
+    dprintf("CRQ for dev 0x" TARGET_FMT_lx " freed\n", reg);
+
+    return H_SUCCESS;
+}
+
+static target_ulong h_send_crq(CPUState *env, sPAPREnvironment *spapr,
+                               target_ulong opcode, target_ulong *args)
+{
+    target_ulong reg = args[0];
+    target_ulong msg_hi = args[1];
+    target_ulong msg_lo = args[2];
+    VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+    uint64_t crq_mangle[2];
+
+    if (!dev) {
+        hcall_dprintf("h_send_crq on non-existent unit 0x"
+                      TARGET_FMT_lx "\n", reg);
+        return H_PARAMETER;
+    }
+    crq_mangle[0] = cpu_to_be64(msg_hi);
+    crq_mangle[1] = cpu_to_be64(msg_lo);
+
+    if (dev->crq.SendFunc) {
+        return dev->crq.SendFunc(dev, (uint8_t *)crq_mangle);
+    }
+
+    return H_HARDWARE;
+}
+
+static target_ulong h_enable_crq(CPUState *env, sPAPREnvironment *spapr,
+                                 target_ulong opcode, target_ulong *args)
+{
+    target_ulong reg = args[0];
+    VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+
+    if (!dev) {
+        hcall_dprintf("h_enable_crq on non-existent unit 0x"
+                      TARGET_FMT_lx "\n", reg);
+        return H_PARAMETER;
+    }
+
+    return 0;
+}
+
+/* Returns negative error, 0 success, or positive: queue full */
+int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq)
+{
+    int rc;
+    uint8_t byte;
+
+    if (!dev->crq.qsize) {
+        fprintf(stderr, "spapr_vio_send_creq on uninitialized queue\n");
+        return -1;
+    }
+
+    /* Maybe do a fast path for KVM just writing to the pages */
+    rc = spapr_tce_dma_read(dev, dev->crq.qladdr + dev->crq.qnext, &byte, 1);
+    if (rc) {
+        return rc;
+    }
+    if (byte != 0) {
+        return 1;
+    }
+
+    rc = spapr_tce_dma_write(dev, dev->crq.qladdr + dev->crq.qnext + 8,
+                             &crq[8], 8);
+    if (rc) {
+        return rc;
+    }
+
+    kvmppc_eieio();
+
+    rc = spapr_tce_dma_write(dev, dev->crq.qladdr + dev->crq.qnext, crq, 8);
+    if (rc) {
+        return rc;
+    }
+
+    dev->crq.qnext = (dev->crq.qnext + 16) % dev->crq.qsize;
+
+    if (dev->signal_state & 1) {
+        qemu_irq_pulse(dev->qirq);
+    }
+
+    return 0;
+}
+
 static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo)
 {
     VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
@@ -431,6 +585,12 @@ VIOsPAPRBus *spapr_vio_bus_init(void)
     /* hcall-tce */
     spapr_register_hypercall(H_PUT_TCE, h_put_tce);
 
+    /* hcall-crq */
+    spapr_register_hypercall(H_REG_CRQ, h_reg_crq);
+    spapr_register_hypercall(H_FREE_CRQ, h_free_crq);
+    spapr_register_hypercall(H_SEND_CRQ, h_send_crq);
+    spapr_register_hypercall(H_ENABLE_CRQ, h_enable_crq);
+
     for (qinfo = device_info_list; qinfo; qinfo = qinfo->next) {
         VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
 
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
index 4cfaf55..ba16795 100644
--- a/hw/spapr_vio.h
+++ b/hw/spapr_vio.h
@@ -32,10 +32,19 @@ enum VIOsPAPR_TCEAccess {
     SPAPR_TCE_RW = 3,
 };
 
+struct VIOsPAPRDevice;
+
 typedef struct VIOsPAPR_RTCE {
     uint64_t tce;
 } VIOsPAPR_RTCE;
 
+typedef struct VIOsPAPR_CRQ {
+    uint64_t qladdr;
+    uint32_t qsize;
+    uint32_t qnext;
+    int(*SendFunc)(struct VIOsPAPRDevice *vdev, uint8_t *crq);
+} VIOsPAPR_CRQ;
+
 typedef struct VIOsPAPRDevice {
     DeviceState qdev;
     uint32_t reg;
@@ -44,6 +53,7 @@ typedef struct VIOsPAPRDevice {
     target_ulong signal_state;
     uint32_t rtce_window_size;
     VIOsPAPR_RTCE *rtce_table;
+    VIOsPAPR_CRQ crq;
 } VIOsPAPRDevice;
 
 typedef struct VIOsPAPRBus {
@@ -81,6 +91,8 @@ void stw_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t val);
 void stq_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint64_t val);
 uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr);
 
+int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq);
+
 void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len);
 void spapr_vty_create(VIOsPAPRBus *bus,
                       uint32_t reg, CharDriverState *chardev,
diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h
index 911b19e..5afb308 100644
--- a/target-ppc/kvm_ppc.h
+++ b/target-ppc/kvm_ppc.h
@@ -18,6 +18,17 @@ uint32_t kvmppc_get_tbfreq(void);
 int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len);
 int kvmppc_set_interrupt(CPUState *env, int irq, int level);
 
+#ifndef CONFIG_KVM
+#define kvmppc_eieio() do { } while (0)
+#else
+#define kvmppc_eieio() \
+    do {                                          \
+        if (kvm_enabled()) {                          \
+            asm volatile("eieio" : : : "memory"); \
+        } \
+    } while (0)
+#endif
+
 #ifndef KVM_INTERRUPT_SET
 #define KVM_INTERRUPT_SET -1
 #endif
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 24/27] Implement PAPR virtual SCSI interface (ibmvscsi)
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (22 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 23/27] Implement PAPR CRQ hypercalls David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 25/27] Add a PAPR TCE-bypass mechanism for the pSeries machine David Gibson
                   ` (3 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

This patch implements the infrastructure and hypercalls necessary for
the PAPR specified Virtual SCSI interface.  This is the normal method
for providing (virtual) disks to PAPR partitions.

Signed-off-by: Ben Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: David Gibson <dwg@au1.ibm.com>

Conflicts:

	Makefile.target
---
 Makefile.target  |    2 +-
 hw/ppc-viosrp.h  |  216 ++++++++++++
 hw/spapr.c       |   11 +-
 hw/spapr_vio.h   |    3 +
 hw/spapr_vscsi.c |  988 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/srp.h         |  240 +++++++++++++
 6 files changed, 1458 insertions(+), 2 deletions(-)
 create mode 100644 hw/ppc-viosrp.h
 create mode 100644 hw/spapr_vscsi.c
 create mode 100644 hw/srp.h

diff --git a/Makefile.target b/Makefile.target
index cd7bb41..565e1fb 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -234,7 +234,7 @@ obj-ppc-y += ppc_newworld.o
 # IBM pSeries (sPAPR)
 ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
 obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
-obj-ppc-y += xics.o spapr_vty.o spapr_llan.o
+obj-ppc-y += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
 endif
 # PowerPC 4xx boards
 obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
diff --git a/hw/ppc-viosrp.h b/hw/ppc-viosrp.h
new file mode 100644
index 0000000..d8e365d
--- /dev/null
+++ b/hw/ppc-viosrp.h
@@ -0,0 +1,216 @@
+/*****************************************************************************/
+/* srp.h -- SCSI RDMA Protocol definitions                                   */
+/*                                                                           */
+/* Written By: Colin Devilbis, IBM Corporation                               */
+/*                                                                           */
+/* Copyright (C) 2003 IBM Corporation                                        */
+/*                                                                           */
+/* This program is free software; you can redistribute it and/or modify      */
+/* it under the terms of the GNU General Public License as published by      */
+/* the Free Software Foundation; either version 2 of the License, or         */
+/* (at your option) any later version.                                       */
+/*                                                                           */
+/* This program is distributed in the hope that it will be useful,           */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             */
+/* GNU General Public License for more details.                              */
+/*                                                                           */
+/* You should have received a copy of the GNU General Public License         */
+/* along with this program; if not, write to the Free Software               */
+/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+/*                                                                           */
+/*                                                                           */
+/* This file contains structures and definitions for IBM RPA (RS/6000        */
+/* platform architecture) implementation of the SRP (SCSI RDMA Protocol)     */
+/* standard.  SRP is used on IBM iSeries and pSeries platforms to send SCSI  */
+/* commands between logical partitions.                                      */
+/*                                                                           */
+/* SRP Information Units (IUs) are sent on a "Command/Response Queue" (CRQ)  */
+/* between partitions.  The definitions in this file are architected,        */
+/* and cannot be changed without breaking compatibility with other versions  */
+/* of Linux and other operating systems (AIX, OS/400) that talk this protocol*/
+/* between logical partitions                                                */
+/*****************************************************************************/
+#ifndef PPC_VIOSRP_H
+#define PPC_VIOSRP_H
+
+#define SRP_VERSION "16.a"
+#define SRP_MAX_IU_LEN    256
+#define SRP_MAX_LOC_LEN 32
+
+union srp_iu {
+    struct srp_login_req login_req;
+    struct srp_login_rsp login_rsp;
+    struct srp_login_rej login_rej;
+    struct srp_i_logout i_logout;
+    struct srp_t_logout t_logout;
+    struct srp_tsk_mgmt tsk_mgmt;
+    struct srp_cmd cmd;
+    struct srp_rsp rsp;
+    uint8_t reserved[SRP_MAX_IU_LEN];
+};
+
+enum viosrp_crq_formats {
+    VIOSRP_SRP_FORMAT = 0x01,
+    VIOSRP_MAD_FORMAT = 0x02,
+    VIOSRP_OS400_FORMAT = 0x03,
+    VIOSRP_AIX_FORMAT = 0x04,
+    VIOSRP_LINUX_FORMAT = 0x06,
+    VIOSRP_INLINE_FORMAT = 0x07
+};
+
+enum viosrp_crq_status {
+    VIOSRP_OK = 0x0,
+    VIOSRP_NONRECOVERABLE_ERR = 0x1,
+    VIOSRP_VIOLATES_MAX_XFER = 0x2,
+    VIOSRP_PARTNER_PANIC = 0x3,
+    VIOSRP_DEVICE_BUSY = 0x8,
+    VIOSRP_ADAPTER_FAIL = 0x10,
+    VIOSRP_OK2 = 0x99,
+};
+
+struct viosrp_crq {
+    uint8_t valid;        /* used by RPA */
+    uint8_t format;        /* SCSI vs out-of-band */
+    uint8_t reserved;
+    uint8_t status;        /* non-scsi failure? (e.g. DMA failure) */
+    uint16_t timeout;        /* in seconds */
+    uint16_t IU_length;        /* in bytes */
+    uint64_t IU_data_ptr;    /* the TCE for transferring data */
+};
+
+/* MADs are Management requests above and beyond the IUs defined in the SRP
+ * standard.
+ */
+enum viosrp_mad_types {
+    VIOSRP_EMPTY_IU_TYPE = 0x01,
+    VIOSRP_ERROR_LOG_TYPE = 0x02,
+    VIOSRP_ADAPTER_INFO_TYPE = 0x03,
+    VIOSRP_HOST_CONFIG_TYPE = 0x04,
+    VIOSRP_CAPABILITIES_TYPE = 0x05,
+    VIOSRP_ENABLE_FAST_FAIL = 0x08,
+};
+
+enum viosrp_mad_status {
+    VIOSRP_MAD_SUCCESS = 0x00,
+    VIOSRP_MAD_NOT_SUPPORTED = 0xF1,
+    VIOSRP_MAD_FAILED = 0xF7,
+};
+
+enum viosrp_capability_type {
+    MIGRATION_CAPABILITIES = 0x01,
+    RESERVATION_CAPABILITIES = 0x02,
+};
+
+enum viosrp_capability_support {
+    SERVER_DOES_NOT_SUPPORTS_CAP = 0x0,
+    SERVER_SUPPORTS_CAP = 0x01,
+    SERVER_CAP_DATA = 0x02,
+};
+
+enum viosrp_reserve_type {
+    CLIENT_RESERVE_SCSI_2 = 0x01,
+};
+
+enum viosrp_capability_flag {
+    CLIENT_MIGRATED = 0x01,
+    CLIENT_RECONNECT = 0x02,
+    CAP_LIST_SUPPORTED = 0x04,
+    CAP_LIST_DATA = 0x08,
+};
+
+/*
+ * Common MAD header
+ */
+struct mad_common {
+    uint32_t type;
+    uint16_t status;
+    uint16_t length;
+    uint64_t tag;
+};
+
+/*
+ * All SRP (and MAD) requests normally flow from the
+ * client to the server.  There is no way for the server to send
+ * an asynchronous message back to the client.  The Empty IU is used
+ * to hang out a meaningless request to the server so that it can respond
+ * asynchrouously with something like a SCSI AER
+ */
+struct viosrp_empty_iu {
+    struct mad_common common;
+    uint64_t buffer;
+    uint32_t port;
+};
+
+struct viosrp_error_log {
+    struct mad_common common;
+    uint64_t buffer;
+};
+
+struct viosrp_adapter_info {
+    struct mad_common common;
+    uint64_t buffer;
+};
+
+struct viosrp_host_config {
+    struct mad_common common;
+    uint64_t buffer;
+};
+
+struct viosrp_fast_fail {
+    struct mad_common common;
+};
+
+struct viosrp_capabilities {
+    struct mad_common common;
+    uint64_t buffer;
+};
+
+struct mad_capability_common {
+    uint32_t cap_type;
+    uint16_t length;
+    uint16_t server_support;
+};
+
+struct mad_reserve_cap {
+    struct mad_capability_common common;
+    uint32_t type;
+};
+
+struct mad_migration_cap {
+    struct mad_capability_common common;
+    uint32_t ecl;
+};
+
+struct capabilities {
+    uint32_t flags;
+    char name[SRP_MAX_LOC_LEN];
+    char loc[SRP_MAX_LOC_LEN];
+    struct mad_migration_cap migration;
+    struct mad_reserve_cap reserve;
+};
+
+union mad_iu {
+    struct viosrp_empty_iu empty_iu;
+    struct viosrp_error_log error_log;
+    struct viosrp_adapter_info adapter_info;
+    struct viosrp_host_config host_config;
+    struct viosrp_fast_fail fast_fail;
+    struct viosrp_capabilities capabilities;
+};
+
+union viosrp_iu {
+    union srp_iu srp;
+    union mad_iu mad;
+};
+
+struct mad_adapter_info_data {
+    char srp_version[8];
+    char partition_name[96];
+    uint32_t partition_number;
+    uint32_t mad_version;
+    uint32_t os_type;
+    uint32_t port_max_txu[8];    /* per-port maximum transfer */
+};
+
+#endif
diff --git a/hw/spapr.c b/hw/spapr.c
index 02a3bbe..d16e499 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -28,6 +28,7 @@
 #include "hw.h"
 #include "elf.h"
 #include "net.h"
+#include "blockdev.h"
 
 #include "hw/boards.h"
 #include "hw/ppc.h"
@@ -322,7 +323,8 @@ static void ppc_spapr_init(ram_addr_t ram_size,
     qemu_free(filename);
 
     /* Set up Interrupt Controller */
-    spapr->icp = xics_system_init(smp_cpus, envs, MAX_SERIAL_PORTS + nb_nics);
+    spapr->icp = xics_system_init(smp_cpus, envs, MAX_SERIAL_PORTS + nb_nics +
+                                  drive_get_max_bus(IF_SCSI) + 1);
 
     /* Set up VIO bus */
     spapr->vio_bus = spapr_vio_bus_init();
@@ -352,6 +354,12 @@ static void ppc_spapr_init(ram_addr_t ram_size,
         }
     }
 
+    for (i = 0; i <= drive_get_max_bus(IF_SCSI); i++) {
+        spapr_vscsi_create(spapr->vio_bus, 0x2000 + i,
+                           xics_find_qirq(spapr->icp, irq), irq);
+        irq++;
+    }
+
     if (kernel_filename) {
         uint64_t lowaddr = 0;
 
@@ -410,6 +418,7 @@ static QEMUMachine spapr_machine = {
     .max_cpus = MAX_CPUS,
     .no_vga = 1,
     .no_parallel = 1,
+    .use_scsi = 1,
 };
 
 static void spapr_machine_init(void)
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
index ba16795..b7d0daa 100644
--- a/hw/spapr_vio.h
+++ b/hw/spapr_vio.h
@@ -101,4 +101,7 @@ void spapr_vty_create(VIOsPAPRBus *bus,
 void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd,
                        qemu_irq qirq, uint32_t vio_irq_num);
 
+void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg,
+                        qemu_irq qirq, uint32_t vio_irq_num);
+
 #endif /* _HW_SPAPR_VIO_H */
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
new file mode 100644
index 0000000..e142dae
--- /dev/null
+++ b/hw/spapr_vscsi.c
@@ -0,0 +1,988 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * PAPR Virtual SCSI, aka ibmvscsi
+ *
+ * Copyright (c) 2010,2011 Benjamin Herrenschmidt, 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.
+ *
+ * TODO:
+ *
+ *  - Cleanups :-)
+ *  - Sort out better how to assign devices to VSCSI instances
+ *  - Fix residual counts
+ *  - Add indirect descriptors support
+ *  - Maybe do autosense (PAPR seems to mandate it, linux doesn't care)
+ */
+#include "hw.h"
+#include "scsi.h"
+#include "scsi-defs.h"
+#include "net.h" /* Remove that when we can */
+#include "srp.h"
+#include "hw/qdev.h"
+#include "hw/spapr.h"
+#include "hw/spapr_vio.h"
+#include "hw/ppc-viosrp.h"
+
+#include <libfdt.h>
+
+/*#define DEBUG_VSCSI*/
+
+#ifdef DEBUG_VSCSI
+#define dprintf(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+    do { } while (0)
+#endif
+
+/*
+ * Virtual SCSI device
+ */
+
+/* Random numbers */
+#define VSCSI_MAX_SECTORS       4096
+#define VSCSI_REQ_LIMIT         24
+
+#define SCSI_SENSE_BUF_SIZE     96
+#define SRP_RSP_SENSE_DATA_LEN  18
+
+typedef union vscsi_crq {
+    struct viosrp_crq s;
+    uint8_t raw[16];
+} vscsi_crq;
+
+typedef struct vscsi_req {
+    vscsi_crq               crq;
+    union viosrp_iu         iu;
+
+    /* SCSI request tracking */
+    SCSIDevice              *sdev;
+    uint32_t                qtag; /* qemu tag != srp tag */
+    int                     lun;
+    int                     active;
+    long                    data_len;
+    int                     writing;
+    int                     sensing;
+    int                     senselen;
+    uint8_t                 sense[SCSI_SENSE_BUF_SIZE];
+
+    /* RDMA related bits */
+    uint8_t                 dma_fmt;
+    struct srp_direct_buf   ext_desc;
+    struct srp_direct_buf   *cur_desc;
+    struct srp_indirect_buf *ind_desc;
+    int                     local_desc;
+    int                     total_desc;
+} vscsi_req;
+
+
+typedef struct {
+    VIOsPAPRDevice vdev;
+    SCSIBus bus;
+    vscsi_req reqs[VSCSI_REQ_LIMIT];
+} VSCSIState;
+
+/* XXX Debug only */
+static VSCSIState *dbg_vscsi_state;
+
+
+static struct vscsi_req *vscsi_get_req(VSCSIState *s)
+{
+    vscsi_req *req;
+    int i;
+
+    for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
+        req = &s->reqs[i];
+        if (!req->active) {
+            memset(req, 0, sizeof(*req));
+            req->qtag = i;
+            req->active = 1;
+            return req;
+        }
+    }
+    return NULL;
+}
+
+static void vscsi_put_req(VSCSIState *s, vscsi_req *req)
+{
+    req->active = 0;
+}
+
+static vscsi_req *vscsi_find_req(VSCSIState *s, uint32_t tag)
+{
+    if (tag >= VSCSI_REQ_LIMIT || !s->reqs[tag].active) {
+        return NULL;
+    }
+    return &s->reqs[tag];
+}
+
+static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun)
+{
+    /* XXX Figure that one out properly ! This is crackpot */
+    *id = (srp_lun >> 56) & 0x7f;
+    *lun = (srp_lun >> 48) & 0xff;
+}
+
+static int vscsi_send_iu(VSCSIState *s, vscsi_req *req,
+                         uint64_t length, uint8_t format)
+{
+    long rc, rc1;
+
+    /* First copy the SRP */
+    rc = spapr_tce_dma_write(&s->vdev, req->crq.s.IU_data_ptr,
+                             &req->iu, length);
+    if (rc) {
+        fprintf(stderr, "vscsi_send_iu: DMA write failure !\n");
+    }
+
+    req->crq.s.valid = 0x80;
+    req->crq.s.format = format;
+    req->crq.s.reserved = 0x00;
+    req->crq.s.timeout = cpu_to_be16(0x0000);
+    req->crq.s.IU_length = cpu_to_be16(length);
+    req->crq.s.IU_data_ptr = req->iu.srp.rsp.tag; /* right byte order */
+
+    if (rc == 0) {
+        req->crq.s.status = 0x99; /* Just needs to be non-zero */
+    } else {
+        req->crq.s.status = 0x00;
+    }
+
+    rc1 = spapr_vio_send_crq(&s->vdev, req->crq.raw);
+    if (rc1) {
+        fprintf(stderr, "vscsi_send_iu: Error sending response\n");
+        return rc1;
+    }
+
+    return rc;
+}
+
+static void vscsi_makeup_sense(VSCSIState *s, vscsi_req *req,
+                               uint8_t key, uint8_t asc, uint8_t ascq)
+{
+    req->senselen = SRP_RSP_SENSE_DATA_LEN;
+
+    /* Valid bit and 'current errors' */
+    req->sense[0] = (0x1 << 7 | 0x70);
+    /* Sense key */
+    req->sense[2] = key;
+    /* Additional sense length */
+    req->sense[7] = 0xa; /* 10 bytes */
+    /* Additional sense code */
+    req->sense[12] = asc;
+    req->sense[13] = ascq;
+}
+
+static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req,
+                          uint8_t status, int32_t res_in, int32_t res_out)
+{
+    union viosrp_iu *iu = &req->iu;
+    uint64_t tag = iu->srp.rsp.tag;
+    int total_len = sizeof(iu->srp.rsp);
+
+    dprintf("VSCSI: Sending resp status: 0x%x, "
+            "res_in: %d, res_out: %d\n", status, res_in, res_out);
+
+    memset(iu, 0, sizeof(struct srp_rsp));
+    iu->srp.rsp.opcode = SRP_RSP;
+    iu->srp.rsp.req_lim_delta = cpu_to_be32(1);
+    iu->srp.rsp.tag = tag;
+
+    /* Handle residuals */
+    if (res_in < 0) {
+        iu->srp.rsp.flags |= SRP_RSP_FLAG_DIUNDER;
+        res_in = -res_in;
+    } else if (res_in) {
+        iu->srp.rsp.flags |= SRP_RSP_FLAG_DIOVER;
+    }
+    if (res_out < 0) {
+        iu->srp.rsp.flags |= SRP_RSP_FLAG_DOUNDER;
+        res_out = -res_out;
+    } else if (res_out) {
+        iu->srp.rsp.flags |= SRP_RSP_FLAG_DOOVER;
+    }
+    iu->srp.rsp.data_in_res_cnt = cpu_to_be32(res_in);
+    iu->srp.rsp.data_out_res_cnt = cpu_to_be32(res_out);
+
+    /* We don't do response data */
+    /* iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID; */
+    iu->srp.rsp.resp_data_len = cpu_to_be32(0);
+
+    /* Handle success vs. failure */
+    iu->srp.rsp.status = status;
+    if (status) {
+        iu->srp.rsp.sol_not = (iu->srp.cmd.sol_not & 0x04) >> 2;
+        if (req->senselen) {
+            req->iu.srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID;
+            req->iu.srp.rsp.sense_data_len = cpu_to_be32(req->senselen);
+            memcpy(req->iu.srp.rsp.data, req->sense, req->senselen);
+            total_len += req->senselen;
+        }
+    } else {
+        iu->srp.rsp.sol_not = (iu->srp.cmd.sol_not & 0x02) >> 1;
+    }
+
+    vscsi_send_iu(s, req, total_len, VIOSRP_SRP_FORMAT);
+    return 0;
+}
+
+static inline void vscsi_swap_desc(struct srp_direct_buf *desc)
+{
+    desc->va = be64_to_cpu(desc->va);
+    desc->len = be32_to_cpu(desc->len);
+}
+
+static int vscsi_srp_direct_data(VSCSIState *s, vscsi_req *req,
+                                 uint8_t *buf, uint32_t len)
+{
+    struct srp_direct_buf *md = req->cur_desc;
+    uint32_t llen;
+    int rc;
+
+    dprintf("VSCSI: direct segment 0x%x bytes, va=0x%llx desc len=0x%x\n",
+            len, (unsigned long long)md->va, md->len);
+
+    llen = MIN(len, md->len);
+    if (llen) {
+        if (req->writing) { /* writing = to device = reading from memory */
+            rc = spapr_tce_dma_read(&s->vdev, md->va, buf, llen);
+        } else {
+            rc = spapr_tce_dma_write(&s->vdev, md->va, buf, llen);
+        }
+    }
+    md->len -= llen;
+    md->va += llen;
+
+    if (rc) {
+        return -1;
+    }
+    return llen;
+}
+
+static int vscsi_srp_indirect_data(VSCSIState *s, vscsi_req *req,
+                                   uint8_t *buf, uint32_t len)
+{
+    struct srp_direct_buf *td = &req->ind_desc->table_desc;
+    struct srp_direct_buf *md = req->cur_desc;
+    int rc = 0;
+    uint32_t llen, total = 0;
+
+    dprintf("VSCSI: indirect segment 0x%x bytes, td va=0x%llx len=0x%x\n",
+            len, (unsigned long long)td->va, td->len);
+
+    /* While we have data ... */
+    while (len) {
+        /* If we have a descriptor but it's empty, go fetch a new one */
+        if (md && md->len == 0) {
+            /* More local available, use one */
+            if (req->local_desc) {
+                md = ++req->cur_desc;
+                --req->local_desc;
+                --req->total_desc;
+                td->va += sizeof(struct srp_direct_buf);
+            } else {
+                md = req->cur_desc = NULL;
+            }
+        }
+        /* No descriptor at hand, fetch one */
+        if (!md) {
+            if (!req->total_desc) {
+                dprintf("VSCSI:   Out of descriptors !\n");
+                break;
+            }
+            md = req->cur_desc = &req->ext_desc;
+            dprintf("VSCSI:   Reading desc from 0x%llx\n",
+                    (unsigned long long)td->va);
+            rc = spapr_tce_dma_read(&s->vdev, td->va, md,
+                                    sizeof(struct srp_direct_buf));
+            if (rc) {
+                dprintf("VSCSI: tce_dma_read -> %d reading ext_desc\n", rc);
+                break;
+            }
+            vscsi_swap_desc(md);
+            td->va += sizeof(struct srp_direct_buf);
+            --req->total_desc;
+        }
+        dprintf("VSCSI:   [desc va=0x%llx,len=0x%x] remaining=0x%x\n",
+                (unsigned long long)md->va, md->len, len);
+
+        /* Perform transfer */
+        llen = MIN(len, md->len);
+        if (req->writing) { /* writing = to device = reading from memory */
+            rc = spapr_tce_dma_read(&s->vdev, md->va, buf, llen);
+        } else {
+            rc = spapr_tce_dma_write(&s->vdev, md->va, buf, llen);
+        }
+        if (rc) {
+            dprintf("VSCSI: tce_dma_r/w(%d) -> %d\n", req->writing, rc);
+            break;
+        }
+        dprintf("VSCSI:     data: %02x %02x %02x %02x...\n",
+                buf[0], buf[1], buf[2], buf[3]);
+
+        len -= llen;
+        buf += llen;
+        total += llen;
+        md->va += llen;
+        md->len -= llen;
+    }
+    return rc ? -1 : total;
+}
+
+static int vscsi_srp_transfer_data(VSCSIState *s, vscsi_req *req,
+                                   int writing, uint8_t *buf, uint32_t len)
+{
+    int err = 0;
+
+    switch (req->dma_fmt) {
+    case SRP_NO_DATA_DESC:
+        dprintf("VSCSI: no data desc transfer, skipping 0x%x bytes\n", len);
+        break;
+    case SRP_DATA_DESC_DIRECT:
+        err = vscsi_srp_direct_data(s, req, buf, len);
+        break;
+    case SRP_DATA_DESC_INDIRECT:
+        err = vscsi_srp_indirect_data(s, req, buf, len);
+        break;
+    }
+    return err;
+}
+
+/* Bits from linux srp */
+static int data_out_desc_size(struct srp_cmd *cmd)
+{
+    int size = 0;
+    uint8_t fmt = cmd->buf_fmt >> 4;
+
+    switch (fmt) {
+    case SRP_NO_DATA_DESC:
+        break;
+    case SRP_DATA_DESC_DIRECT:
+        size = sizeof(struct srp_direct_buf);
+        break;
+    case SRP_DATA_DESC_INDIRECT:
+        size = sizeof(struct srp_indirect_buf) +
+            sizeof(struct srp_direct_buf)*cmd->data_out_desc_cnt;
+        break;
+    default:
+        break;
+    }
+    return size;
+}
+
+static int vscsi_preprocess_desc(vscsi_req *req)
+{
+    struct srp_cmd *cmd = &req->iu.srp.cmd;
+    int offset, i;
+
+    offset = cmd->add_cdb_len & ~3;
+
+    if (req->writing) {
+        req->dma_fmt = cmd->buf_fmt >> 4;
+    } else {
+        offset += data_out_desc_size(cmd);
+        req->dma_fmt = cmd->buf_fmt & ((1U << 4) - 1);
+    }
+
+    switch (req->dma_fmt) {
+    case SRP_NO_DATA_DESC:
+        break;
+    case SRP_DATA_DESC_DIRECT:
+        req->cur_desc = (struct srp_direct_buf *)(cmd->add_data + offset);
+        req->total_desc = req->local_desc = 1;
+        vscsi_swap_desc(req->cur_desc);
+        dprintf("VSCSI: using direct RDMA %s, 0x%x bytes MD: 0x%llx\n",
+                req->writing ? "write" : "read",
+                req->cur_desc->len, (unsigned long long)req->cur_desc->va);
+        break;
+    case SRP_DATA_DESC_INDIRECT:
+        req->ind_desc = (struct srp_indirect_buf *)(cmd->add_data + offset);
+        vscsi_swap_desc(&req->ind_desc->table_desc);
+        req->total_desc = req->ind_desc->table_desc.len /
+            sizeof(struct srp_direct_buf);
+        req->local_desc = req->writing ? cmd->data_out_desc_cnt :
+            cmd->data_in_desc_cnt;
+        for (i = 0; i < req->local_desc; i++) {
+            vscsi_swap_desc(&req->ind_desc->desc_list[i]);
+        }
+        req->cur_desc = req->local_desc ? &req->ind_desc->desc_list[0] : NULL;
+        dprintf("VSCSI: using indirect RDMA %s, 0x%x bytes %d descs "
+                "(%d local) VA: 0x%llx\n",
+                req->writing ? "read" : "write",
+                be32_to_cpu(req->ind_desc->len),
+                req->total_desc, req->local_desc,
+                (unsigned long long)req->ind_desc->table_desc.va);
+        break;
+    default:
+        fprintf(stderr,
+                "vscsi_preprocess_desc: Unknown format %x\n", req->dma_fmt);
+        return -1;
+    }
+
+    return 0;
+}
+
+static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
+{
+    SCSIDevice *sdev = req->sdev;
+    uint8_t *cdb = req->iu.srp.cmd.cdb;
+    int n;
+
+    cdb[0] = 3;
+    cdb[1] = 0;
+    cdb[2] = 0;
+    cdb[3] = 0;
+    cdb[4] = 96;
+    cdb[5] = 0;
+    req->sensing = 1;
+    n = sdev->info->send_command(sdev, req->qtag, cdb, req->lun);
+    dprintf("VSCSI: Queued request sense tag 0x%x\n", req->qtag);
+    if (n < 0) {
+        fprintf(stderr, "VSCSI: REQUEST_SENSE wants write data !?!?!?\n");
+        sdev->info->cancel_io(sdev, req->qtag);
+        vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
+        vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
+        vscsi_put_req(s, req);
+        return;
+    } else if (n == 0) {
+        return;
+    }
+    sdev->info->read_data(sdev, req->qtag);
+}
+
+/* Callback to indicate that the SCSI layer has completed a transfer.  */
+static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
+                                   uint32_t arg)
+{
+    VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, bus->qbus.parent);
+    vscsi_req *req = vscsi_find_req(s, tag);
+    SCSIDevice *sdev;
+    uint8_t *buf;
+    int32_t res_in = 0, res_out = 0;
+    int len, rc = 0;
+
+    dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x arg=0x%x, req=%p\n",
+            reason, tag, arg, req);
+    if (req == NULL) {
+        fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", tag);
+        return;
+    }
+    sdev = req->sdev;
+
+    if (req->sensing) {
+        if (reason == SCSI_REASON_DONE) {
+            dprintf("VSCSI: Sense done !\n");
+            vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
+            vscsi_put_req(s, req);
+        } else {
+            uint8_t *buf = sdev->info->get_buf(sdev, tag);
+
+            len = MIN(arg, SCSI_SENSE_BUF_SIZE);
+            dprintf("VSCSI: Sense data, %d bytes:\n", len);
+            dprintf("       %02x  %02x  %02x  %02x  %02x  %02x  %02x  %02x\n",
+                    buf[0], buf[1], buf[2], buf[3],
+                    buf[4], buf[5], buf[6], buf[7]);
+            dprintf("       %02x  %02x  %02x  %02x  %02x  %02x  %02x  %02x\n",
+                    buf[8], buf[9], buf[10], buf[11],
+                    buf[12], buf[13], buf[14], buf[15]);
+            memcpy(req->sense, buf, len);
+            req->senselen = len;
+            sdev->info->read_data(sdev, req->qtag);
+        }
+        return;
+    }
+
+    if (reason == SCSI_REASON_DONE) {
+        dprintf("VSCSI: Command complete err=%d\n", arg);
+        if (arg == 0) {
+            /* We handle overflows, not underflows for normal commands,
+             * but hopefully nobody cares
+             */
+            if (req->writing) {
+                res_out = req->data_len;
+            } else {
+                res_in = req->data_len;
+            }
+            vscsi_send_rsp(s, req, 0, res_in, res_out);
+        } else if (arg == CHECK_CONDITION) {
+            dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n");
+            vscsi_send_request_sense(s, req);
+            return;
+        } else {
+            vscsi_send_rsp(s, req, arg, 0, 0);
+        }
+        vscsi_put_req(s, req);
+        return;
+    }
+
+    /* "arg" is how much we have read for reads and how much we want
+     * to write for writes (ie, how much is to be DMA'd)
+     */
+    if (arg) {
+        buf = sdev->info->get_buf(sdev, tag);
+        rc = vscsi_srp_transfer_data(s, req, req->writing, buf, arg);
+    }
+    if (rc < 0) {
+        fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc);
+        sdev->info->cancel_io(sdev, req->qtag);
+        vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
+        vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
+        vscsi_put_req(s, req);
+        return;
+    }
+
+    /* Start next chunk */
+    req->data_len -= rc;
+    if (req->writing) {
+        sdev->info->write_data(sdev, req->qtag);
+    } else {
+        sdev->info->read_data(sdev, req->qtag);
+    }
+}
+
+static void vscsi_process_login(VSCSIState *s, vscsi_req *req)
+{
+    union viosrp_iu *iu = &req->iu;
+    struct srp_login_rsp *rsp = &iu->srp.login_rsp;
+    uint64_t tag = iu->srp.rsp.tag;
+
+    dprintf("VSCSI: Got login, sendin response !\n");
+
+    /* TODO handle case that requested size is wrong and
+     * buffer format is wrong
+     */
+    memset(iu, 0, sizeof(struct srp_login_rsp));
+    rsp->opcode = SRP_LOGIN_RSP;
+    /* Don't advertise quite as many request as we support to
+     * keep room for management stuff etc...
+     */
+    rsp->req_lim_delta = cpu_to_be32(VSCSI_REQ_LIMIT-2);
+    rsp->tag = tag;
+    rsp->max_it_iu_len = cpu_to_be32(sizeof(union srp_iu));
+    rsp->max_ti_iu_len = cpu_to_be32(sizeof(union srp_iu));
+    /* direct and indirect */
+    rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT);
+
+    vscsi_send_iu(s, req, sizeof(*rsp), VIOSRP_SRP_FORMAT);
+}
+
+static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req)
+{
+    uint8_t *cdb = req->iu.srp.cmd.cdb;
+    uint8_t resp_data[36];
+    int rc, len, alen;
+
+    /* We dont do EVPD. Also check that page_code is 0 */
+    if ((cdb[1] & 0x01) || (cdb[1] & 0x01) || cdb[2] != 0) {
+        /* Send INVALID FIELD IN CDB */
+        vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x24, 0);
+        vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
+        return;
+    }
+    alen = cdb[3];
+    alen = (alen << 8) | cdb[4];
+    len = MIN(alen, 36);
+
+    /* Fake up inquiry using PQ=3 */
+    memset(resp_data, 0, 36);
+    resp_data[0] = 0x7f;   /* Not capable of supporting a device here */
+    resp_data[2] = 0x06;   /* SPS-4 */
+    resp_data[3] = 0x02;   /* Resp data format */
+    resp_data[4] = 36 - 5; /* Additional length */
+    resp_data[7] = 0x10;   /* Sync transfers */
+    memcpy(&resp_data[16], "QEMU EMPTY      ", 16);
+    memcpy(&resp_data[8], "QEMU    ", 8);
+
+    req->writing = 0;
+    vscsi_preprocess_desc(req);
+    rc = vscsi_srp_transfer_data(s, req, 0, resp_data, len);
+    if (rc < 0) {
+        vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
+        vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
+    } else {
+        vscsi_send_rsp(s, req, 0, 36 - rc, 0);
+    }
+}
+
+static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
+{
+    union srp_iu *srp = &req->iu.srp;
+    SCSIDevice *sdev;
+    int n, id, lun;
+
+    vscsi_decode_id_lun(be64_to_cpu(srp->cmd.lun), &id, &lun);
+
+    /* Qemu vs. linux issue with LUNs to be sorted out ... */
+    sdev = (id < 8 && lun < 16) ? s->bus.devs[id] : NULL;
+    if (!sdev) {
+        dprintf("VSCSI: Command for id %d with no drive\n", id);
+        if (srp->cmd.cdb[0] == INQUIRY) {
+            vscsi_inquiry_no_target(s, req);
+        } else {
+            vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x24, 0x00);
+            vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
+        } return 1;
+    }
+
+    req->sdev = sdev;
+    req->lun = lun;
+    n = sdev->info->send_command(sdev, req->qtag, srp->cmd.cdb, lun);
+
+    dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n",
+            req->qtag, srp->cmd.cdb[0], id, lun, n);
+
+    if (n) {
+        /* Transfer direction must be set before preprocessing the
+         * descriptors
+         */
+        req->writing = (n < 1);
+
+        /* Preprocess RDMA descriptors */
+        vscsi_preprocess_desc(req);
+    }
+
+    /* Get transfer direction and initiate transfer */
+    if (n > 0) {
+        req->data_len = n;
+        sdev->info->read_data(sdev, req->qtag);
+    } else if (n < 0) {
+        req->data_len = -n;
+        sdev->info->write_data(sdev, req->qtag);
+    }
+    /* Don't touch req here, it may have been recycled already */
+
+    return 0;
+}
+
+static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
+{
+    union viosrp_iu *iu = &req->iu;
+    int fn;
+
+    fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n",
+            iu->srp.tsk_mgmt.tsk_mgmt_func);
+
+    switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
+#if 0 /* We really don't deal with these for now */
+    case SRP_TSK_ABORT_TASK:
+        fn = ABORT_TASK;
+        break;
+    case SRP_TSK_ABORT_TASK_SET:
+        fn = ABORT_TASK_SET;
+        break;
+    case SRP_TSK_CLEAR_TASK_SET:
+        fn = CLEAR_TASK_SET;
+        break;
+    case SRP_TSK_LUN_RESET:
+        fn = LOGICAL_UNIT_RESET;
+        break;
+    case SRP_TSK_CLEAR_ACA:
+        fn = CLEAR_ACA;
+        break;
+#endif
+    default:
+        fn = 0;
+    }
+    if (fn) {
+        /* XXX Send/Handle target task management */
+        ;
+    } else {
+        vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x20, 0);
+        vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
+    }
+    return !fn;
+}
+
+static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req)
+{
+    union srp_iu *srp = &req->iu.srp;
+    int done = 1;
+    uint8_t opcode = srp->rsp.opcode;
+
+    switch (opcode) {
+    case SRP_LOGIN_REQ:
+        vscsi_process_login(s, req);
+        break;
+    case SRP_TSK_MGMT:
+        done = vscsi_process_tsk_mgmt(s, req);
+        break;
+    case SRP_CMD:
+        done = vscsi_queue_cmd(s, req);
+        break;
+    case SRP_LOGIN_RSP:
+    case SRP_I_LOGOUT:
+    case SRP_T_LOGOUT:
+    case SRP_RSP:
+    case SRP_CRED_REQ:
+    case SRP_CRED_RSP:
+    case SRP_AER_REQ:
+    case SRP_AER_RSP:
+        fprintf(stderr, "VSCSI: Unsupported opcode %02x\n", opcode);
+        break;
+    default:
+        fprintf(stderr, "VSCSI: Unknown type %02x\n", opcode);
+    }
+
+    return done;
+}
+
+static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req)
+{
+    struct viosrp_adapter_info *sinfo;
+    struct mad_adapter_info_data info;
+    int rc;
+
+    sinfo = &req->iu.mad.adapter_info;
+
+#if 0 /* What for ? */
+    rc = spapr_tce_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer),
+                            &info, be16_to_cpu(sinfo->common.length));
+    if (rc) {
+        fprintf(stderr, "vscsi_send_adapter_info: DMA read failure !\n");
+    }
+#endif
+    memset(&info, 0, sizeof(info));
+    strcpy(info.srp_version, SRP_VERSION);
+    strncpy(info.partition_name, "qemu", sizeof("qemu"));
+    info.partition_number = cpu_to_be32(0);
+    info.mad_version = cpu_to_be32(1);
+    info.os_type = cpu_to_be32(2);
+    info.port_max_txu[0] = cpu_to_be32(VSCSI_MAX_SECTORS << 9);
+
+    rc = spapr_tce_dma_write(&s->vdev, be64_to_cpu(sinfo->buffer),
+                             &info, be16_to_cpu(sinfo->common.length));
+    if (rc)  {
+        fprintf(stderr, "vscsi_send_adapter_info: DMA write failure !\n");
+    }
+
+    sinfo->common.status = rc ? cpu_to_be32(1) : 0;
+
+    return vscsi_send_iu(s, req, sizeof(*sinfo), VIOSRP_MAD_FORMAT);
+}
+
+static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req)
+{
+    union mad_iu *mad = &req->iu.mad;
+
+    switch (be32_to_cpu(mad->empty_iu.common.type)) {
+    case VIOSRP_EMPTY_IU_TYPE:
+        fprintf(stderr, "Unsupported EMPTY MAD IU\n");
+        break;
+    case VIOSRP_ERROR_LOG_TYPE:
+        fprintf(stderr, "Unsupported ERROR LOG MAD IU\n");
+        mad->error_log.common.status = cpu_to_be16(1);
+        vscsi_send_iu(s, req, sizeof(mad->error_log), VIOSRP_MAD_FORMAT);
+        break;
+    case VIOSRP_ADAPTER_INFO_TYPE:
+        vscsi_send_adapter_info(s, req);
+        break;
+    case VIOSRP_HOST_CONFIG_TYPE:
+        mad->host_config.common.status = cpu_to_be16(1);
+        vscsi_send_iu(s, req, sizeof(mad->host_config), VIOSRP_MAD_FORMAT);
+        break;
+    default:
+        fprintf(stderr, "VSCSI: Unknown MAD type %02x\n",
+                be32_to_cpu(mad->empty_iu.common.type));
+    }
+
+    return 1;
+}
+
+static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq)
+{
+    vscsi_req *req;
+    int done;
+
+    req = vscsi_get_req(s);
+    if (req == NULL) {
+        fprintf(stderr, "VSCSI: Failed to get a request !\n");
+        return;
+    }
+
+    /* We only support a limited number of descriptors, we know
+     * the ibmvscsi driver uses up to 10 max, so it should fit
+     * in our 256 bytes IUs. If not we'll have to increase the size
+     * of the structure.
+     */
+    if (crq->s.IU_length > sizeof(union viosrp_iu)) {
+        fprintf(stderr, "VSCSI: SRP IU too long (%d bytes) !\n",
+                crq->s.IU_length);
+        return;
+    }
+
+    /* XXX Handle failure differently ? */
+    if (spapr_tce_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu,
+                           crq->s.IU_length)) {
+        fprintf(stderr, "vscsi_got_payload: DMA read failure !\n");
+        qemu_free(req);
+    }
+    memcpy(&req->crq, crq, sizeof(vscsi_crq));
+
+    if (crq->s.format == VIOSRP_MAD_FORMAT) {
+        done = vscsi_handle_mad_req(s, req);
+    } else {
+        done = vscsi_handle_srp_req(s, req);
+    }
+
+    if (done) {
+        vscsi_put_req(s, req);
+    }
+}
+
+
+static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data)
+{
+    VSCSIState *s = DO_UPCAST(VSCSIState, vdev, dev);
+    vscsi_crq crq;
+
+    memcpy(crq.raw, crq_data, 16);
+    crq.s.timeout = be16_to_cpu(crq.s.timeout);
+    crq.s.IU_length = be16_to_cpu(crq.s.IU_length);
+    crq.s.IU_data_ptr = be64_to_cpu(crq.s.IU_data_ptr);
+
+    dprintf("VSCSI: do_crq %02x %02x ...\n", crq.raw[0], crq.raw[1]);
+
+    switch (crq.s.valid) {
+    case 0xc0: /* Init command/response */
+
+        /* Respond to initialization request */
+        if (crq.s.format == 0x01) {
+            memset(crq.raw, 0, 16);
+            crq.s.valid = 0xc0;
+            crq.s.format = 0x02;
+            spapr_vio_send_crq(dev, crq.raw);
+        }
+
+        /* Note that in hotplug cases, we might get a 0x02
+         * as a result of us emitting the init request
+         */
+
+        break;
+    case 0xff: /* Link event */
+
+        /* Not handled for now */
+
+        break;
+    case 0x80: /* Payloads */
+        switch (crq.s.format) {
+        case VIOSRP_SRP_FORMAT: /* AKA VSCSI request */
+        case VIOSRP_MAD_FORMAT: /* AKA VSCSI response */
+            vscsi_got_payload(s, &crq);
+            break;
+        case VIOSRP_OS400_FORMAT:
+        case VIOSRP_AIX_FORMAT:
+        case VIOSRP_LINUX_FORMAT:
+        case VIOSRP_INLINE_FORMAT:
+            fprintf(stderr, "vscsi_do_srq: Unsupported payload format %02x\n",
+                    crq.s.format);
+            break;
+        default:
+            fprintf(stderr, "vscsi_do_srq: Unknown payload format %02x\n",
+                    crq.s.format);
+        }
+        break;
+    default:
+        fprintf(stderr, "vscsi_do_crq: unknown CRQ %02x %02x ...\n",
+                crq.raw[0], crq.raw[1]);
+    };
+
+    return 0;
+}
+
+static int spapr_vscsi_init(VIOsPAPRDevice *dev)
+{
+    VSCSIState *s = DO_UPCAST(VSCSIState, vdev, dev);
+    int i;
+
+    dbg_vscsi_state = s;
+
+    /* Initialize qemu request tags */
+    memset(s->reqs, 0, sizeof(s->reqs));
+    for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
+        s->reqs[i].qtag = i;
+    }
+
+    dev->crq.SendFunc = vscsi_do_crq;
+
+    scsi_bus_new(&s->bus, &dev->qdev, 1, VSCSI_REQ_LIMIT,
+                 vscsi_command_complete);
+    if (!dev->qdev.hotplugged) {
+        scsi_bus_legacy_handle_cmdline(&s->bus);
+    }
+
+    return 0;
+}
+
+void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg,
+                        qemu_irq qirq, uint32_t vio_irq_num)
+{
+    DeviceState *dev;
+    VIOsPAPRDevice *sdev;
+
+    dev = qdev_create(&bus->bus, "spapr-vscsi");
+    qdev_prop_set_uint32(dev, "reg", reg);
+
+    qdev_init_nofail(dev);
+
+    sdev = (VIOsPAPRDevice *)dev;
+    sdev->qirq = qirq;
+    sdev->vio_irq_num = vio_irq_num;
+}
+
+static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
+{
+    int ret;
+
+    ret = fdt_setprop_cell(fdt, node_off, "#address-cells", 2);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = fdt_setprop_cell(fdt, node_off, "#size-cells", 0);
+    if (ret < 0) {
+        return ret;
+    }
+
+    return 0;
+}
+
+static VIOsPAPRDeviceInfo spapr_vscsi = {
+    .init = spapr_vscsi_init,
+    .devnode = spapr_vscsi_devnode,
+    .dt_name = "v-scsi",
+    .dt_type = "vscsi",
+    .dt_compatible = "IBM,v-scsi",
+    .signal_mask = 0x00000001,
+    .qdev.name = "spapr-vscsi",
+    .qdev.size = sizeof(VSCSIState),
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x2000),
+        DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice,
+                           rtce_window_size, 0x10000000),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static void spapr_vscsi_register(void)
+{
+    spapr_vio_bus_register_withprop(&spapr_vscsi);
+}
+device_init(spapr_vscsi_register);
diff --git a/hw/srp.h b/hw/srp.h
new file mode 100644
index 0000000..afcd135
--- /dev/null
+++ b/hw/srp.h
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2005 Cisco Systems.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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.
+ *
+ */
+
+#ifndef SCSI_SRP_H
+#define SCSI_SRP_H
+
+/*
+ * Structures and constants for the SCSI RDMA Protocol (SRP) as
+ * defined by the INCITS T10 committee.  This file was written using
+ * draft Revision 16a of the SRP standard.
+ */
+
+enum {
+
+    SRP_LOGIN_REQ = 0x00,
+    SRP_TSK_MGMT  = 0x01,
+    SRP_CMD       = 0x02,
+    SRP_I_LOGOUT  = 0x03,
+    SRP_LOGIN_RSP = 0xc0,
+    SRP_RSP       = 0xc1,
+    SRP_LOGIN_REJ = 0xc2,
+    SRP_T_LOGOUT  = 0x80,
+    SRP_CRED_REQ  = 0x81,
+    SRP_AER_REQ   = 0x82,
+    SRP_CRED_RSP  = 0x41,
+    SRP_AER_RSP   = 0x42
+};
+
+enum {
+    SRP_BUF_FORMAT_DIRECT   = 1 << 1,
+    SRP_BUF_FORMAT_INDIRECT = 1 << 2
+};
+
+enum {
+    SRP_NO_DATA_DESC       = 0,
+    SRP_DATA_DESC_DIRECT   = 1,
+    SRP_DATA_DESC_INDIRECT = 2
+};
+
+enum {
+    SRP_TSK_ABORT_TASK     = 0x01,
+    SRP_TSK_ABORT_TASK_SET = 0x02,
+    SRP_TSK_CLEAR_TASK_SET = 0x04,
+    SRP_TSK_LUN_RESET      = 0x08,
+    SRP_TSK_CLEAR_ACA      = 0x40
+};
+
+enum srp_login_rej_reason {
+    SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL   = 0x00010000,
+    SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES     = 0x00010001,
+    SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE = 0x00010002,
+    SRP_LOGIN_REJ_UNABLE_ASSOCIATE_CHANNEL   = 0x00010003,
+    SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT = 0x00010004,
+    SRP_LOGIN_REJ_MULTI_CHANNEL_UNSUPPORTED  = 0x00010005,
+    SRP_LOGIN_REJ_CHANNEL_LIMIT_REACHED      = 0x00010006
+};
+
+enum {
+    SRP_REV10_IB_IO_CLASS  = 0xff00,
+    SRP_REV16A_IB_IO_CLASS = 0x0100
+};
+
+struct srp_direct_buf {
+    uint64_t    va;
+    uint32_t    key;
+    uint32_t    len;
+};
+
+/*
+ * We need the packed attribute because the SRP spec puts the list of
+ * descriptors at an offset of 20, which is not aligned to the size of
+ * struct srp_direct_buf.  The whole structure must be packed to avoid
+ * having the 20-byte structure padded to 24 bytes on 64-bit architectures.
+ */
+struct srp_indirect_buf {
+    struct srp_direct_buf    table_desc;
+    uint32_t                 len;
+    struct srp_direct_buf    desc_list[0];
+} __attribute__((packed));
+
+enum {
+    SRP_MULTICHAN_SINGLE = 0,
+    SRP_MULTICHAN_MULTI  = 1
+};
+
+struct srp_login_req {
+    uint8_t    opcode;
+    uint8_t    reserved1[7];
+    uint64_t   tag;
+    uint32_t   req_it_iu_len;
+    uint8_t    reserved2[4];
+    uint16_t   req_buf_fmt;
+    uint8_t    req_flags;
+    uint8_t    reserved3[5];
+    uint8_t    initiator_port_id[16];
+    uint8_t    target_port_id[16];
+};
+
+/*
+ * The SRP spec defines the size of the LOGIN_RSP structure to be 52
+ * bytes, so it needs to be packed to avoid having it padded to 56
+ * bytes on 64-bit architectures.
+ */
+struct srp_login_rsp {
+    uint8_t    opcode;
+    uint8_t    reserved1[3];
+    uint32_t   req_lim_delta;
+    uint64_t   tag;
+    uint32_t   max_it_iu_len;
+    uint32_t   max_ti_iu_len;
+    uint16_t   buf_fmt;
+    uint8_t    rsp_flags;
+    uint8_t    reserved2[25];
+} __attribute__((packed));
+
+struct srp_login_rej {
+    uint8_t    opcode;
+    uint8_t    reserved1[3];
+    uint32_t   reason;
+    uint64_t   tag;
+    uint8_t    reserved2[8];
+    uint16_t   buf_fmt;
+    uint8_t    reserved3[6];
+};
+
+struct srp_i_logout {
+    uint8_t    opcode;
+    uint8_t    reserved[7];
+    uint64_t   tag;
+};
+
+struct srp_t_logout {
+    uint8_t    opcode;
+    uint8_t    sol_not;
+    uint8_t    reserved[2];
+    uint32_t   reason;
+    uint64_t   tag;
+};
+
+/*
+ * We need the packed attribute because the SRP spec only aligns the
+ * 8-byte LUN field to 4 bytes.
+ */
+struct srp_tsk_mgmt {
+    uint8_t    opcode;
+    uint8_t    sol_not;
+    uint8_t    reserved1[6];
+    uint64_t   tag;
+    uint8_t    reserved2[4];
+    uint64_t   lun __attribute__((packed));
+    uint8_t    reserved3[2];
+    uint8_t    tsk_mgmt_func;
+    uint8_t    reserved4;
+    uint64_t   task_tag;
+    uint8_t    reserved5[8];
+};
+
+/*
+ * We need the packed attribute because the SRP spec only aligns the
+ * 8-byte LUN field to 4 bytes.
+ */
+struct srp_cmd {
+    uint8_t    opcode;
+    uint8_t    sol_not;
+    uint8_t    reserved1[3];
+    uint8_t    buf_fmt;
+    uint8_t    data_out_desc_cnt;
+    uint8_t    data_in_desc_cnt;
+    uint64_t   tag;
+    uint8_t    reserved2[4];
+    uint64_t   lun __attribute__((packed));
+    uint8_t    reserved3;
+    uint8_t    task_attr;
+    uint8_t    reserved4;
+    uint8_t    add_cdb_len;
+    uint8_t    cdb[16];
+    uint8_t    add_data[0];
+};
+
+enum {
+    SRP_RSP_FLAG_RSPVALID = 1 << 0,
+    SRP_RSP_FLAG_SNSVALID = 1 << 1,
+    SRP_RSP_FLAG_DOOVER   = 1 << 2,
+    SRP_RSP_FLAG_DOUNDER  = 1 << 3,
+    SRP_RSP_FLAG_DIOVER   = 1 << 4,
+    SRP_RSP_FLAG_DIUNDER  = 1 << 5
+};
+
+/*
+ * The SRP spec defines the size of the RSP structure to be 36 bytes,
+ * so it needs to be packed to avoid having it padded to 40 bytes on
+ * 64-bit architectures.
+ */
+struct srp_rsp {
+    uint8_t    opcode;
+    uint8_t    sol_not;
+    uint8_t    reserved1[2];
+    uint32_t   req_lim_delta;
+    uint64_t   tag;
+    uint8_t    reserved2[2];
+    uint8_t    flags;
+    uint8_t    status;
+    uint32_t   data_out_res_cnt;
+    uint32_t   data_in_res_cnt;
+    uint32_t   sense_data_len;
+    uint32_t   resp_data_len;
+    uint8_t    data[0];
+} __attribute__((packed));
+
+#endif /* SCSI_SRP_H */
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 25/27] Add a PAPR TCE-bypass mechanism for the pSeries machine
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (23 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 24/27] Implement PAPR virtual SCSI interface (ibmvscsi) David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 26/27] Implement PAPR VPA functions for pSeries shared processor partitions David Gibson
                   ` (2 subsequent siblings)
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

From: Ben Herrenschmidt <benh@kernel.crashing.org>

Usually, PAPR virtual IO devices use a virtual IOMMU mechanism, TCEs,
to mediate all DMA transfers.  While this is necessary for some sorts of
operation, it can be complex to program and slow for others.

This patch implements a mechanism for bypassing TCE translation, treating
"IO" addresses as plain (guest) physical memory addresses.  This has two
main uses:
 * Simple, but 64-bit aware programs like firmwares can use the VIO devices
without the complexity of TCE setup.
 * The guest OS can optionally use the TCE bypass to improve performance in
suitable situations.

The mechanism used is a per-device flag which disables TCE translation.
The flag is toggled with some (hypervisor-implemented) RTAS methods.

Signed-off-by: Ben Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 hw/spapr_vio.c |   82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/spapr_vio.h |    5 +++
 2 files changed, 87 insertions(+), 0 deletions(-)

diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index 8f14fcc..481a804 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -226,6 +226,12 @@ int spapr_tce_dma_write(VIOsPAPRDevice *dev, uint64_t taddr, const void *buf,
             (unsigned long long)taddr, size);
 #endif
 
+    /* Check for bypass */
+    if (dev->flags & VIO_PAPR_FLAG_DMA_BYPASS) {
+        cpu_physical_memory_write(taddr, buf, size);
+        return 0;
+    }
+
     while (size) {
         uint64_t tce;
         uint32_t lsize;
@@ -313,6 +319,12 @@ int spapr_tce_dma_read(VIOsPAPRDevice *dev, uint64_t taddr, void *buf,
             (unsigned long long)taddr, size);
 #endif
 
+    /* Check for bypass */
+    if (dev->flags & VIO_PAPR_FLAG_DMA_BYPASS) {
+        cpu_physical_memory_read(taddr, buf, size);
+        return 0;
+    }
+
     while (size) {
         uint64_t tce;
         uint32_t lsize;
@@ -513,6 +525,72 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq)
     return 0;
 }
 
+/* "quiesce" handling */
+
+static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev)
+{
+    dev->flags &= ~VIO_PAPR_FLAG_DMA_BYPASS;
+
+    if (dev->rtce_table) {
+        size_t size = (dev->rtce_window_size >> SPAPR_VIO_TCE_PAGE_SHIFT)
+            * sizeof(VIOsPAPR_RTCE);
+        memset(dev->rtce_table, 0, size);
+    }
+
+    dev->crq.qladdr = 0;
+    dev->crq.qsize = 0;
+    dev->crq.qnext = 0;
+}
+
+static void rtas_set_tce_bypass(sPAPREnvironment *spapr, uint32_t token,
+                                uint32_t nargs, target_ulong args,
+                                uint32_t nret, target_ulong rets)
+{
+    VIOsPAPRBus *bus = spapr->vio_bus;
+    VIOsPAPRDevice *dev;
+    uint32_t unit, enable;
+
+    if (nargs != 2) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+    unit = rtas_ld(args, 0);
+    enable = rtas_ld(args, 1);
+    dev = spapr_vio_find_by_reg(bus, unit);
+    if (!dev) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+    if (enable) {
+        dev->flags |= VIO_PAPR_FLAG_DMA_BYPASS;
+    } else {
+        dev->flags &= ~VIO_PAPR_FLAG_DMA_BYPASS;
+    }
+
+    rtas_st(rets, 0, 0);
+}
+
+static void rtas_quiesce(sPAPREnvironment *spapr, uint32_t token,
+                         uint32_t nargs, target_ulong args,
+                         uint32_t nret, target_ulong rets)
+{
+    VIOsPAPRBus *bus = spapr->vio_bus;
+    DeviceState *qdev;
+    VIOsPAPRDevice *dev = NULL;
+
+    if (nargs != 0) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+
+    QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
+        dev = (VIOsPAPRDevice *)qdev;
+        spapr_vio_quiesce_one(dev);
+    }
+
+    rtas_st(rets, 0, 0);
+}
+
 static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo)
 {
     VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
@@ -591,6 +669,10 @@ VIOsPAPRBus *spapr_vio_bus_init(void)
     spapr_register_hypercall(H_SEND_CRQ, h_send_crq);
     spapr_register_hypercall(H_ENABLE_CRQ, h_enable_crq);
 
+    /* RTAS calls */
+    spapr_rtas_register("ibm,set-tce-bypass", rtas_set_tce_bypass);
+    spapr_rtas_register("quiesce", rtas_quiesce);
+
     for (qinfo = device_info_list; qinfo; qinfo = qinfo->next) {
         VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
 
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
index b7d0daa..841b043 100644
--- a/hw/spapr_vio.h
+++ b/hw/spapr_vio.h
@@ -48,6 +48,8 @@ typedef struct VIOsPAPR_CRQ {
 typedef struct VIOsPAPRDevice {
     DeviceState qdev;
     uint32_t reg;
+    uint32_t flags;
+#define VIO_PAPR_FLAG_DMA_BYPASS        0x1
     qemu_irq qirq;
     uint32_t vio_irq_num;
     target_ulong signal_state;
@@ -104,4 +106,7 @@ void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd,
 void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg,
                         qemu_irq qirq, uint32_t vio_irq_num);
 
+int spapr_tce_set_bypass(uint32_t unit, uint32_t enable);
+void spapr_vio_quiesce(void);
+
 #endif /* _HW_SPAPR_VIO_H */
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 26/27] Implement PAPR VPA functions for pSeries shared processor partitions
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (24 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 25/27] Add a PAPR TCE-bypass mechanism for the pSeries machine David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options David Gibson
  2011-03-28 11:22 ` [Qemu-devel] Re: [0/27] Implement emulation of pSeries logical partitions (v5) Alexander Graf
  27 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

Shared-processor partitions are those where a CPU is time-sliced between
partitions, rather than being permanently dedicated to a single
partition.  qemu emulated partitions, since they are just scheduled with
the qemu user process, behave mostly like shared processor partitions.

In order to better support shared processor partitions (splpar), PAPR
defines the "VPA" (Virtual Processor Area), a shared memory communication
channel between the hypervisor and partitions.  There are also two
additional shared memory communication areas for specialized purposes
associated with the VPA.

A VPA is not essential for operating an splpar, though it can be necessary
for obtaining accurate performance measurements in the presence of
runtime partition switching.

Most importantly, however, the VPA is a prerequisite for PAPR's H_CEDE,
hypercall, which allows a partition OS to give up it's shared processor
timeslices to other partitions when idle.

This patch implements the VPA and H_CEDE hypercalls in qemu.  We don't
implement any of the more advanced statistics which can be communicated
through the VPA.  However, this is enough to make normal pSeries kernels
do an effective power-save idle on an emulated pSeries, significantly
reducing the host load of a qemu emulated pSeries running an idle guest OS.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 hw/spapr.c       |    2 +-
 hw/spapr_hcall.c |  192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 target-ppc/cpu.h |    7 ++
 3 files changed, 200 insertions(+), 1 deletions(-)

diff --git a/hw/spapr.c b/hw/spapr.c
index d16e499..9d611a7 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -67,7 +67,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
     uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
     uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
     char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt"
-        "\0hcall-tce\0hcall-vio";
+        "\0hcall-tce\0hcall-vio\0hcall-splpar";
     uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
     int i;
     char *modelname;
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c
index 02ccafd..6cc101d 100644
--- a/hw/spapr_hcall.c
+++ b/hw/spapr_hcall.c
@@ -4,6 +4,8 @@
 #include "sysemu.h"
 #include "qemu-char.h"
 #include "exec-all.h"
+#include "exec.h"
+#include "helper_regs.h"
 #include "hw/spapr.h"
 
 #define HPTES_PER_GROUP 8
@@ -255,6 +257,192 @@ static target_ulong h_set_dabr(CPUState *env, sPAPREnvironment *spapr,
     return H_HARDWARE;
 }
 
+#define FLAGS_REGISTER_VPA         0x0000200000000000ULL
+#define FLAGS_REGISTER_DTL         0x0000400000000000ULL
+#define FLAGS_REGISTER_SLBSHADOW   0x0000600000000000ULL
+#define FLAGS_DEREGISTER_VPA       0x0000a00000000000ULL
+#define FLAGS_DEREGISTER_DTL       0x0000c00000000000ULL
+#define FLAGS_DEREGISTER_SLBSHADOW 0x0000e00000000000ULL
+
+#define VPA_MIN_SIZE           640
+#define VPA_SIZE_OFFSET        0x4
+#define VPA_SHARED_PROC_OFFSET 0x9
+#define VPA_SHARED_PROC_VAL    0x2
+
+static target_ulong register_vpa(CPUState *env, target_ulong vpa)
+{
+    uint16_t size;
+    uint8_t tmp;
+
+    if (vpa == 0) {
+        hcall_dprintf("Can't cope with registering a VPA at logical 0\n");
+        return H_HARDWARE;
+    }
+
+    if (vpa % env->dcache_line_size) {
+        return H_PARAMETER;
+    }
+    /* FIXME: bounds check the address */
+
+    size = lduw_phys(vpa + 0x4);
+
+    if (size < VPA_MIN_SIZE) {
+        return H_PARAMETER;
+    }
+
+    /* VPA is not allowed to cross a page boundary */
+    if ((vpa / 4096) != ((vpa + size - 1) / 4096)) {
+        return H_PARAMETER;
+    }
+
+    env->vpa = vpa;
+
+    tmp = ldub_phys(env->vpa + VPA_SHARED_PROC_OFFSET);
+    tmp |= VPA_SHARED_PROC_VAL;
+    stb_phys(env->vpa + VPA_SHARED_PROC_OFFSET, tmp);
+
+    return H_SUCCESS;
+}
+
+static target_ulong deregister_vpa(CPUState *env, target_ulong vpa)
+{
+    if (env->slb_shadow) {
+        return H_RESOURCE;
+    }
+
+    if (env->dispatch_trace_log) {
+        return H_RESOURCE;
+    }
+
+    env->vpa = 0;
+    return H_SUCCESS;
+}
+
+static target_ulong register_slb_shadow(CPUState *env, target_ulong addr)
+{
+    uint32_t size;
+
+    if (addr == 0) {
+        hcall_dprintf("Can't cope with SLB shadow at logical 0\n");
+        return H_HARDWARE;
+    }
+
+    size = ldl_phys(addr + 0x4);
+    if (size < 0x8) {
+        return H_PARAMETER;
+    }
+
+    if ((addr / 4096) != ((addr + size - 1) / 4096)) {
+        return H_PARAMETER;
+    }
+
+    if (!env->vpa) {
+        return H_RESOURCE;
+    }
+
+    env->slb_shadow = addr;
+
+    return H_SUCCESS;
+}
+
+static target_ulong deregister_slb_shadow(CPUState *env, target_ulong addr)
+{
+    env->slb_shadow = 0;
+    return H_SUCCESS;
+}
+
+static target_ulong register_dtl(CPUState *env, target_ulong addr)
+{
+    uint32_t size;
+
+    if (addr == 0) {
+        hcall_dprintf("Can't cope with DTL at logical 0\n");
+        return H_HARDWARE;
+    }
+
+    size = ldl_phys(addr + 0x4);
+
+    if (size < 48) {
+        return H_PARAMETER;
+    }
+
+    if (!env->vpa) {
+        return H_RESOURCE;
+    }
+
+    env->dispatch_trace_log = addr;
+    env->dtl_size = size;
+
+    return H_SUCCESS;
+}
+
+static target_ulong deregister_dtl(CPUState *emv, target_ulong addr)
+{
+    env->dispatch_trace_log = 0;
+    env->dtl_size = 0;
+
+    return H_SUCCESS;
+}
+
+static target_ulong h_register_vpa(CPUState *env, sPAPREnvironment *spapr,
+                                   target_ulong opcode, target_ulong *args)
+{
+    target_ulong flags = args[0];
+    target_ulong procno = args[1];
+    target_ulong vpa = args[2];
+    target_ulong ret = H_PARAMETER;
+    CPUState *tenv;
+
+    for (tenv = first_cpu; tenv; tenv = tenv->next_cpu) {
+        if (tenv->cpu_index == procno) {
+            break;
+        }
+    }
+
+    if (!tenv) {
+        return H_PARAMETER;
+    }
+
+    switch (flags) {
+    case FLAGS_REGISTER_VPA:
+        ret = register_vpa(tenv, vpa);
+        break;
+
+    case FLAGS_DEREGISTER_VPA:
+        ret = deregister_vpa(tenv, vpa);
+        break;
+
+    case FLAGS_REGISTER_SLBSHADOW:
+        ret = register_slb_shadow(tenv, vpa);
+        break;
+
+    case FLAGS_DEREGISTER_SLBSHADOW:
+        ret = deregister_slb_shadow(tenv, vpa);
+        break;
+
+    case FLAGS_REGISTER_DTL:
+        ret = register_dtl(tenv, vpa);
+        break;
+
+    case FLAGS_DEREGISTER_DTL:
+        ret = deregister_dtl(tenv, vpa);
+        break;
+    }
+
+    return ret;
+}
+
+static target_ulong h_cede(CPUState *env, sPAPREnvironment *spapr,
+                           target_ulong opcode, target_ulong *args)
+{
+    env->msr |= (1ULL << MSR_EE);
+    hreg_compute_hflags(env);
+    if (!cpu_has_work(env)) {
+        env->halted = 1;
+    }
+    return H_SUCCESS;
+}
+
 static target_ulong h_rtas(sPAPREnvironment *spapr, target_ulong rtas_r3)
 {
     uint32_t token = ldl_phys(rtas_r3);
@@ -318,5 +506,9 @@ static void hypercall_init(void)
 
     /* hcall-dabr */
     spapr_register_hypercall(H_SET_DABR, h_set_dabr);
+
+    /* hcall-splpar */
+    spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa);
+    spapr_register_hypercall(H_CEDE, h_cede);
 }
 device_init(hypercall_init);
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index b4c2555..04b1259 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -721,6 +721,13 @@ struct CPUPPCState {
     uint32_t flags;
     uint64_t insns_flags;
 
+#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
+    target_phys_addr_t vpa;
+    target_phys_addr_t slb_shadow;
+    target_phys_addr_t dispatch_trace_log;
+    uint32_t dtl_size;
+#endif /* TARGET_PPC64 */
+
     int error_code;
     uint32_t pending_interrupts;
 #if !defined(CONFIG_USER_ONLY)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (25 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 26/27] Implement PAPR VPA functions for pSeries shared processor partitions David Gibson
@ 2011-03-25  3:21 ` David Gibson
  2011-03-25 18:29   ` Anthony Liguori
  2011-03-28 10:30   ` [Qemu-devel] " Alexander Graf
  2011-03-28 11:22 ` [Qemu-devel] Re: [0/27] Implement emulation of pSeries logical partitions (v5) Alexander Graf
  27 siblings, 2 replies; 55+ messages in thread
From: David Gibson @ 2011-03-25  3:21 UTC (permalink / raw)
  To: agraf, qemu-devel; +Cc: paulus, anton

Currently, the emulated pSeries machine requires the use of the
-kernel parameter in order to explicitly load a guest kernel.  This
means booting from the virtual disk, cdrom or network is not possible.

This patch addresses this limitation by inserting a within-partition
firmware image (derived from the "SLOF" free Open Firmware project).
If -kernel is not specified, qemu will now load the SLOF image, which
has access to the qemu boot device list through the device tree, and
can boot from any of the usual virtual devices.

In order to support the new firmware, an extension to the emulated
machine/hypervisor is necessary.  Unlike Linux, which expects
multi-CPU entry to be handled kexec() style, the SLOF firmware expects
only one CPU to be active at entry, and to use a hypervisor RTAS
method to enable the other CPUs one by one.

This patch also implements this 'start-cpu' method, so that SLOF can
start the secondary CPUs and marshal them into the kexec() holding
pattern ready for entry into the guest OS.  Linux should, and in the
future might directly use the start-cpu method to enable initially
disabled CPUs, but for now it does require kexec() entry.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: David Gibson <dwg@au1.ibm.com>
---
 Makefile         |    2 +-
 hw/spapr.c       |   35 +++++++++++++++++++++---
 hw/spapr_rtas.c  |   78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 pc-bios/README   |    5 +++
 pc-bios/slof.bin |  Bin 0 -> 579072 bytes
 5 files changed, 115 insertions(+), 5 deletions(-)
 create mode 100644 pc-bios/slof.bin

diff --git a/Makefile b/Makefile
index e0b3fea..989622b 100644
--- a/Makefile
+++ b/Makefile
@@ -214,7 +214,7 @@ pxe-rtl8139.bin pxe-virtio.bin \
 bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
 multiboot.bin linuxboot.bin \
 s390-zipl.rom \
-spapr-rtas.bin
+spapr-rtas.bin slof.bin
 else
 BLOBS=
 endif
diff --git a/hw/spapr.c b/hw/spapr.c
index 9d611a7..c6454e6 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -44,6 +44,10 @@
 #define INITRD_LOAD_ADDR        0x02800000
 #define FDT_MAX_SIZE            0x10000
 #define RTAS_MAX_SIZE           0x10000
+#define FW_MAX_SIZE             0x400000
+#define FW_FILE_NAME            "slof.bin"
+
+#define MIN_RAM_SLOF		512UL
 
 #define TIMEBASE_FREQ           512000000ULL
 
@@ -56,6 +60,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
                               sPAPREnvironment *spapr,
                               target_phys_addr_t initrd_base,
                               target_phys_addr_t initrd_size,
+                              const char *boot_device,
                               const char *kernel_cmdline,
                               target_phys_addr_t rtas_addr,
                               target_phys_addr_t rtas_size,
@@ -104,6 +109,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
                        &start_prop, sizeof(start_prop))));
     _FDT((fdt_property(fdt, "linux,initrd-end",
                        &end_prop, sizeof(end_prop))));
+    _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device)));
 
     _FDT((fdt_end_node(fdt)));
 
@@ -260,7 +266,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
     ram_addr_t ram_offset;
     target_phys_addr_t fdt_addr, rtas_addr;
     uint32_t kernel_base, initrd_base;
-    long kernel_size, initrd_size, htab_size, rtas_size;
+    long kernel_size, initrd_size, htab_size, rtas_size, fw_size;
     long pteg_shift = 17;
     int fdt_size;
     char *filename;
@@ -392,13 +398,33 @@ static void ppc_spapr_init(ram_addr_t ram_size,
             initrd_size = 0;
         }
     } else {
-        fprintf(stderr, "pSeries machine needs -kernel for now");
-        exit(1);
+        if (ram_size < (MIN_RAM_SLOF << 20)) {
+            fprintf(stderr, "qemu: pSeries SLOF firmware requires >= "
+                    "%ldM guest RAM\n", MIN_RAM_SLOF);
+            exit(1);
+        }
+        filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "slof.bin");
+        fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
+        if (fw_size < 0) {
+            hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
+            exit(1);
+        }
+        qemu_free(filename);
+        kernel_base = 0x100;
+        initrd_base = 0;
+        initrd_size = 0;
+
+        /* SLOF will startup the secondary CPUs using RTAS,
+           rather than expecting a kexec() style entry */
+        for (i = 0; i < smp_cpus; i++) {
+            envs[i]->halted = 1;
+        }
     }
 
     /* Prepare the device tree */
     fdt = spapr_create_fdt(&fdt_size, ram_size, cpu_model, envs, spapr,
-                           initrd_base, initrd_size, kernel_cmdline,
+                           initrd_base, initrd_size,
+                           boot_device, kernel_cmdline,
                            rtas_addr, rtas_size, pteg_shift + 7);
     assert(fdt != NULL);
 
@@ -409,6 +435,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
     envs[0]->gpr[3] = fdt_addr;
     envs[0]->gpr[5] = 0;
     envs[0]->hreset_vector = kernel_base;
+    envs[0]->halted = 0;
 }
 
 static QEMUMachine spapr_machine = {
diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c
index 7226853..16b6542 100644
--- a/hw/spapr_rtas.c
+++ b/hw/spapr_rtas.c
@@ -90,6 +90,81 @@ static void rtas_power_off(sPAPREnvironment *spapr,
     rtas_st(rets, 0, 0);
 }
 
+static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr,
+                                         uint32_t token, uint32_t nargs,
+                                         target_ulong args,
+                                         uint32_t nret, target_ulong rets)
+{
+    target_ulong id;
+    CPUState *env;
+
+    if (nargs != 1 || nret != 2) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+
+    id = rtas_ld(args, 0);
+    for (env = first_cpu; env; env = env->next_cpu) {
+        if (env->cpu_index != id) {
+            continue;
+        }
+
+        if (env->halted) {
+            rtas_st(rets, 1, 0);
+        } else {
+            rtas_st(rets, 1, 2);
+        }
+
+        rtas_st(rets, 0, 0);
+        return;
+    }
+
+    /* Didn't find a matching cpu */
+    rtas_st(rets, 0, -3);
+}
+
+static void rtas_start_cpu(sPAPREnvironment *spapr,
+                           uint32_t token, uint32_t nargs,
+                           target_ulong args,
+                           uint32_t nret, target_ulong rets)
+{
+    target_ulong id, start, r3;
+    CPUState *env;
+
+    if (nargs != 3 || nret != 1) {
+        rtas_st(rets, 0, -3);
+        return;
+    }
+
+    id = rtas_ld(args, 0);
+    start = rtas_ld(args, 1);
+    r3 = rtas_ld(args, 2);
+
+    for (env = first_cpu; env; env = env->next_cpu) {
+        if (env->cpu_index != id) {
+            continue;
+        }
+
+        if (!env->halted) {
+            rtas_st(rets, 0, -1);
+            return;
+        }
+
+        env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME);
+        env->nip = start;
+        env->gpr[3] = r3;
+        env->halted = 0;
+
+        qemu_cpu_kick(env);
+
+        rtas_st(rets, 0, 0);
+        return;
+    }
+
+    /* Didn't find a matching cpu */
+    rtas_st(rets, 0, -3);
+}
+
 static struct rtas_call {
     const char *name;
     spapr_rtas_fn fn;
@@ -196,5 +271,8 @@ static void register_core_rtas(void)
     spapr_rtas_register("display-character", rtas_display_character);
     spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
     spapr_rtas_register("power-off", rtas_power_off);
+    spapr_rtas_register("query-cpu-stopped-state",
+                        rtas_query_cpu_stopped_state);
+    spapr_rtas_register("start-cpu", rtas_start_cpu);
 }
 device_init(register_core_rtas);
diff --git a/pc-bios/README b/pc-bios/README
index 3fc0944..646a31a 100644
--- a/pc-bios/README
+++ b/pc-bios/README
@@ -13,6 +13,11 @@
   The included image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32
   and Sparc64 are built from OpenBIOS SVN revision 1018.
 
+- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
+  implementation for certain IBM POWER hardware.  The sources are at
+  https://github.com/dgibson/SLOF, and the image currently in qemu is
+  built from git tag qemu-slof-20110323.
+
 - The PXE roms come from Rom-o-Matic gPXE 0.9.9 with BANNER_TIMEOUT=0
 
   e1000 8086:100E
diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin
new file mode 100644
index 0000000000000000000000000000000000000000..22c4c7f5c448e3002aefecf3438f5d080586d666
GIT binary patch
literal 579072
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

literal 0
HcmV?d00001

-- 
1.7.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options David Gibson
@ 2011-03-25 18:29   ` Anthony Liguori
  2011-03-28  1:19     ` David Gibson
  2011-03-28 10:30   ` [Qemu-devel] " Alexander Graf
  1 sibling, 1 reply; 55+ messages in thread
From: Anthony Liguori @ 2011-03-25 18:29 UTC (permalink / raw)
  To: David Gibson; +Cc: paulus, agraf, anton, qemu-devel

On 03/24/2011 10:21 PM, David Gibson wrote:
> Currently, the emulated pSeries machine requires the use of the
> -kernel parameter in order to explicitly load a guest kernel.  This
> means booting from the virtual disk, cdrom or network is not possible.
>
> This patch addresses this limitation by inserting a within-partition
> firmware image (derived from the "SLOF" free Open Firmware project).
> If -kernel is not specified, qemu will now load the SLOF image, which
> has access to the qemu boot device list through the device tree, and
> can boot from any of the usual virtual devices.
>
> In order to support the new firmware, an extension to the emulated
> machine/hypervisor is necessary.  Unlike Linux, which expects
> multi-CPU entry to be handled kexec() style, the SLOF firmware expects
> only one CPU to be active at entry, and to use a hypervisor RTAS
> method to enable the other CPUs one by one.
>
> This patch also implements this 'start-cpu' method, so that SLOF can
> start the secondary CPUs and marshal them into the kexec() holding
> pattern ready for entry into the guest OS.  Linux should, and in the
> future might directly use the start-cpu method to enable initially
> disabled CPUs, but for now it does require kexec() entry.
>
> Signed-off-by: Benjamin Herrenschmidt<benh@kernel.crashing.org>
> Signed-off-by: Paul Mackerras<paulus@samba.org>
> Signed-off-by: David Gibson<dwg@au1.ibm.com>

We should pull in SLOF via a git submodule.  That ensures we ship the 
source code along with the binary.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-25 18:29   ` Anthony Liguori
@ 2011-03-28  1:19     ` David Gibson
  2011-03-28  9:03       ` Alexander Graf
  2011-03-28 13:13       ` Anthony Liguori
  0 siblings, 2 replies; 55+ messages in thread
From: David Gibson @ 2011-03-28  1:19 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: paulus, agraf, anton, qemu-devel

On Fri, Mar 25, 2011 at 01:29:17PM -0500, Anthony Liguori wrote:
> On 03/24/2011 10:21 PM, David Gibson wrote:
> >Currently, the emulated pSeries machine requires the use of the
> >-kernel parameter in order to explicitly load a guest kernel.  This
> >means booting from the virtual disk, cdrom or network is not possible.
> >
> >This patch addresses this limitation by inserting a within-partition
> >firmware image (derived from the "SLOF" free Open Firmware project).
> >If -kernel is not specified, qemu will now load the SLOF image, which
> >has access to the qemu boot device list through the device tree, and
> >can boot from any of the usual virtual devices.
> >
> >In order to support the new firmware, an extension to the emulated
> >machine/hypervisor is necessary.  Unlike Linux, which expects
> >multi-CPU entry to be handled kexec() style, the SLOF firmware expects
> >only one CPU to be active at entry, and to use a hypervisor RTAS
> >method to enable the other CPUs one by one.
> >
> >This patch also implements this 'start-cpu' method, so that SLOF can
> >start the secondary CPUs and marshal them into the kexec() holding
> >pattern ready for entry into the guest OS.  Linux should, and in the
> >future might directly use the start-cpu method to enable initially
> >disabled CPUs, but for now it does require kexec() entry.
> >
> >Signed-off-by: Benjamin Herrenschmidt<benh@kernel.crashing.org>
> >Signed-off-by: Paul Mackerras<paulus@samba.org>
> >Signed-off-by: David Gibson<dwg@au1.ibm.com>
> 
> We should pull in SLOF via a git submodule.  That ensures we ship
> the source code along with the binary.

Um, ok.  Do I need to do anything about this?

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28  1:19     ` David Gibson
@ 2011-03-28  9:03       ` Alexander Graf
  2011-03-28 12:49         ` Avi Kivity
  2011-03-28 13:16         ` Anthony Liguori
  2011-03-28 13:13       ` Anthony Liguori
  1 sibling, 2 replies; 55+ messages in thread
From: Alexander Graf @ 2011-03-28  9:03 UTC (permalink / raw)
  To: David Gibson; +Cc: paulus, qemu-devel, anton


On 28.03.2011, at 03:19, David Gibson wrote:

> On Fri, Mar 25, 2011 at 01:29:17PM -0500, Anthony Liguori wrote:
>> On 03/24/2011 10:21 PM, David Gibson wrote:
>>> Currently, the emulated pSeries machine requires the use of the
>>> -kernel parameter in order to explicitly load a guest kernel.  This
>>> means booting from the virtual disk, cdrom or network is not possible.
>>> 
>>> This patch addresses this limitation by inserting a within-partition
>>> firmware image (derived from the "SLOF" free Open Firmware project).
>>> If -kernel is not specified, qemu will now load the SLOF image, which
>>> has access to the qemu boot device list through the device tree, and
>>> can boot from any of the usual virtual devices.
>>> 
>>> In order to support the new firmware, an extension to the emulated
>>> machine/hypervisor is necessary.  Unlike Linux, which expects
>>> multi-CPU entry to be handled kexec() style, the SLOF firmware expects
>>> only one CPU to be active at entry, and to use a hypervisor RTAS
>>> method to enable the other CPUs one by one.
>>> 
>>> This patch also implements this 'start-cpu' method, so that SLOF can
>>> start the secondary CPUs and marshal them into the kexec() holding
>>> pattern ready for entry into the guest OS.  Linux should, and in the
>>> future might directly use the start-cpu method to enable initially
>>> disabled CPUs, but for now it does require kexec() entry.
>>> 
>>> Signed-off-by: Benjamin Herrenschmidt<benh@kernel.crashing.org>
>>> Signed-off-by: Paul Mackerras<paulus@samba.org>
>>> Signed-off-by: David Gibson<dwg@au1.ibm.com>
>> 
>> We should pull in SLOF via a git submodule.  That ensures we ship
>> the source code along with the binary.
> 
> Um, ok.  Do I need to do anything about this?

I'm also not sure this is too important. Most of our firmware blobs come from svn repos which can't be submoduled. And as long as we don't have a consistent policy about it, we can just as well stick with the README file.


Alex

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [Qemu-devel] Re: [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options David Gibson
  2011-03-25 18:29   ` Anthony Liguori
@ 2011-03-28 10:30   ` Alexander Graf
  2011-03-28 10:51     ` Paolo Bonzini
  2011-03-28 10:51     ` Paolo Bonzini
  1 sibling, 2 replies; 55+ messages in thread
From: Alexander Graf @ 2011-03-28 10:30 UTC (permalink / raw)
  To: David Gibson; +Cc: paulus, qemu-devel, anton


On 25.03.2011, at 04:21, David Gibson wrote:

> Currently, the emulated pSeries machine requires the use of the
> -kernel parameter in order to explicitly load a guest kernel.  This
> means booting from the virtual disk, cdrom or network is not possible.
> 
> This patch addresses this limitation by inserting a within-partition
> firmware image (derived from the "SLOF" free Open Firmware project).
> If -kernel is not specified, qemu will now load the SLOF image, which
> has access to the qemu boot device list through the device tree, and
> can boot from any of the usual virtual devices.
> 
> In order to support the new firmware, an extension to the emulated
> machine/hypervisor is necessary.  Unlike Linux, which expects
> multi-CPU entry to be handled kexec() style, the SLOF firmware expects
> only one CPU to be active at entry, and to use a hypervisor RTAS
> method to enable the other CPUs one by one.
> 
> This patch also implements this 'start-cpu' method, so that SLOF can
> start the secondary CPUs and marshal them into the kexec() holding
> pattern ready for entry into the guest OS.  Linux should, and in the
> future might directly use the start-cpu method to enable initially
> disabled CPUs, but for now it does require kexec() entry.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Signed-off-by: Paul Mackerras <paulus@samba.org>
> Signed-off-by: David Gibson <dwg@au1.ibm.com>
> ---
> Makefile         |    2 +-
> hw/spapr.c       |   35 +++++++++++++++++++++---
> hw/spapr_rtas.c  |   78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> pc-bios/README   |    5 +++
> pc-bios/slof.bin |  Bin 0 -> 579072 bytes
> 5 files changed, 115 insertions(+), 5 deletions(-)
> create mode 100644 pc-bios/slof.bin
> 
> diff --git a/Makefile b/Makefile
> index e0b3fea..989622b 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -214,7 +214,7 @@ pxe-rtl8139.bin pxe-virtio.bin \
> bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
> multiboot.bin linuxboot.bin \
> s390-zipl.rom \
> -spapr-rtas.bin
> +spapr-rtas.bin slof.bin
> else
> BLOBS=
> endif
> diff --git a/hw/spapr.c b/hw/spapr.c
> index 9d611a7..c6454e6 100644
> --- a/hw/spapr.c
> +++ b/hw/spapr.c
> @@ -44,6 +44,10 @@
> #define INITRD_LOAD_ADDR        0x02800000
> #define FDT_MAX_SIZE            0x10000
> #define RTAS_MAX_SIZE           0x10000
> +#define FW_MAX_SIZE             0x400000
> +#define FW_FILE_NAME            "slof.bin"
> +
> +#define MIN_RAM_SLOF		512UL
> 
> #define TIMEBASE_FREQ           512000000ULL
> 
> @@ -56,6 +60,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
>                               sPAPREnvironment *spapr,
>                               target_phys_addr_t initrd_base,
>                               target_phys_addr_t initrd_size,
> +                              const char *boot_device,
>                               const char *kernel_cmdline,
>                               target_phys_addr_t rtas_addr,
>                               target_phys_addr_t rtas_size,
> @@ -104,6 +109,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
>                        &start_prop, sizeof(start_prop))));
>     _FDT((fdt_property(fdt, "linux,initrd-end",
>                        &end_prop, sizeof(end_prop))));
> +    _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device)));
> 
>     _FDT((fdt_end_node(fdt)));
> 
> @@ -260,7 +266,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
>     ram_addr_t ram_offset;
>     target_phys_addr_t fdt_addr, rtas_addr;
>     uint32_t kernel_base, initrd_base;
> -    long kernel_size, initrd_size, htab_size, rtas_size;
> +    long kernel_size, initrd_size, htab_size, rtas_size, fw_size;
>     long pteg_shift = 17;
>     int fdt_size;
>     char *filename;
> @@ -392,13 +398,33 @@ static void ppc_spapr_init(ram_addr_t ram_size,
>             initrd_size = 0;
>         }
>     } else {
> -        fprintf(stderr, "pSeries machine needs -kernel for now");
> -        exit(1);
> +        if (ram_size < (MIN_RAM_SLOF << 20)) {
> +            fprintf(stderr, "qemu: pSeries SLOF firmware requires >= "
> +                    "%ldM guest RAM\n", MIN_RAM_SLOF);
> +            exit(1);
> +        }
> +        filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "slof.bin");
> +        fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
> +        if (fw_size < 0) {
> +            hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
> +            exit(1);
> +        }
> +        qemu_free(filename);
> +        kernel_base = 0x100;
> +        initrd_base = 0;
> +        initrd_size = 0;
> +
> +        /* SLOF will startup the secondary CPUs using RTAS,
> +           rather than expecting a kexec() style entry */
> +        for (i = 0; i < smp_cpus; i++) {
> +            envs[i]->halted = 1;
> +        }
>     }
> 
>     /* Prepare the device tree */
>     fdt = spapr_create_fdt(&fdt_size, ram_size, cpu_model, envs, spapr,
> -                           initrd_base, initrd_size, kernel_cmdline,
> +                           initrd_base, initrd_size,
> +                           boot_device, kernel_cmdline,
>                            rtas_addr, rtas_size, pteg_shift + 7);
>     assert(fdt != NULL);
> 
> @@ -409,6 +435,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
>     envs[0]->gpr[3] = fdt_addr;
>     envs[0]->gpr[5] = 0;
>     envs[0]->hreset_vector = kernel_base;
> +    envs[0]->halted = 0;
> }
> 
> static QEMUMachine spapr_machine = {
> diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c
> index 7226853..16b6542 100644
> --- a/hw/spapr_rtas.c
> +++ b/hw/spapr_rtas.c
> @@ -90,6 +90,81 @@ static void rtas_power_off(sPAPREnvironment *spapr,
>     rtas_st(rets, 0, 0);
> }
> 
> +static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr,
> +                                         uint32_t token, uint32_t nargs,
> +                                         target_ulong args,
> +                                         uint32_t nret, target_ulong rets)
> +{
> +    target_ulong id;
> +    CPUState *env;
> +
> +    if (nargs != 1 || nret != 2) {
> +        rtas_st(rets, 0, -3);
> +        return;
> +    }
> +
> +    id = rtas_ld(args, 0);
> +    for (env = first_cpu; env; env = env->next_cpu) {
> +        if (env->cpu_index != id) {
> +            continue;
> +        }
> +
> +        if (env->halted) {
> +            rtas_st(rets, 1, 0);
> +        } else {
> +            rtas_st(rets, 1, 2);
> +        }
> +
> +        rtas_st(rets, 0, 0);
> +        return;
> +    }
> +
> +    /* Didn't find a matching cpu */
> +    rtas_st(rets, 0, -3);
> +}
> +
> +static void rtas_start_cpu(sPAPREnvironment *spapr,
> +                           uint32_t token, uint32_t nargs,
> +                           target_ulong args,
> +                           uint32_t nret, target_ulong rets)
> +{
> +    target_ulong id, start, r3;
> +    CPUState *env;
> +
> +    if (nargs != 3 || nret != 1) {
> +        rtas_st(rets, 0, -3);
> +        return;
> +    }
> +
> +    id = rtas_ld(args, 0);
> +    start = rtas_ld(args, 1);
> +    r3 = rtas_ld(args, 2);
> +
> +    for (env = first_cpu; env; env = env->next_cpu) {
> +        if (env->cpu_index != id) {
> +            continue;
> +        }
> +
> +        if (!env->halted) {
> +            rtas_st(rets, 0, -1);
> +            return;
> +        }
> +
> +        env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME);
> +        env->nip = start;
> +        env->gpr[3] = r3;
> +        env->halted = 0;
> +
> +        qemu_cpu_kick(env);
> +
> +        rtas_st(rets, 0, 0);
> +        return;
> +    }
> +
> +    /* Didn't find a matching cpu */
> +    rtas_st(rets, 0, -3);
> +}
> +
> static struct rtas_call {
>     const char *name;
>     spapr_rtas_fn fn;
> @@ -196,5 +271,8 @@ static void register_core_rtas(void)
>     spapr_rtas_register("display-character", rtas_display_character);
>     spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
>     spapr_rtas_register("power-off", rtas_power_off);
> +    spapr_rtas_register("query-cpu-stopped-state",
> +                        rtas_query_cpu_stopped_state);
> +    spapr_rtas_register("start-cpu", rtas_start_cpu);
> }
> device_init(register_core_rtas);
> diff --git a/pc-bios/README b/pc-bios/README
> index 3fc0944..646a31a 100644
> --- a/pc-bios/README
> +++ b/pc-bios/README
> @@ -13,6 +13,11 @@
>   The included image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32
>   and Sparc64 are built from OpenBIOS SVN revision 1018.
> 
> +- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
> +  implementation for certain IBM POWER hardware.  The sources are at
> +  https://github.com/dgibson/SLOF, and the image currently in qemu is
> +  built from git tag qemu-slof-20110323.
> +
> - The PXE roms come from Rom-o-Matic gPXE 0.9.9 with BANNER_TIMEOUT=0

Is this a line removal?


Alex

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [Qemu-devel] Re: [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 10:30   ` [Qemu-devel] " Alexander Graf
@ 2011-03-28 10:51     ` Paolo Bonzini
  2011-03-28 10:51     ` Paolo Bonzini
  1 sibling, 0 replies; 55+ messages in thread
From: Paolo Bonzini @ 2011-03-28 10:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: paulus, anton, David Gibson

On 03/28/2011 12:30 PM, Alexander Graf wrote:
>
> On 25.03.2011, at 04:21, David Gibson wrote:
>
>> Currently, the emulated pSeries machine requires the use of the
>> -kernel parameter in order to explicitly load a guest kernel.  This
>> means booting from the virtual disk, cdrom or network is not possible.
>>
>> This patch addresses this limitation by inserting a within-partition
>> firmware image (derived from the "SLOF" free Open Firmware project).
>> If -kernel is not specified, qemu will now load the SLOF image, which
>> has access to the qemu boot device list through the device tree, and
>> can boot from any of the usual virtual devices.
>>
>> In order to support the new firmware, an extension to the emulated
>> machine/hypervisor is necessary.  Unlike Linux, which expects
>> multi-CPU entry to be handled kexec() style, the SLOF firmware expects
>> only one CPU to be active at entry, and to use a hypervisor RTAS
>> method to enable the other CPUs one by one.
>>
>> This patch also implements this 'start-cpu' method, so that SLOF can
>> start the secondary CPUs and marshal them into the kexec() holding
>> pattern ready for entry into the guest OS.  Linux should, and in the
>> future might directly use the start-cpu method to enable initially
>> disabled CPUs, but for now it does require kexec() entry.
>>
>> Signed-off-by: Benjamin Herrenschmidt<benh@kernel.crashing.org>
>> Signed-off-by: Paul Mackerras<paulus@samba.org>
>> Signed-off-by: David Gibson<dwg@au1.ibm.com>
>> ---
>> Makefile         |    2 +-
>> hw/spapr.c       |   35 +++++++++++++++++++++---
>> hw/spapr_rtas.c  |   78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> pc-bios/README   |    5 +++
>> pc-bios/slof.bin |  Bin 0 ->  579072 bytes
>> 5 files changed, 115 insertions(+), 5 deletions(-)
>> create mode 100644 pc-bios/slof.bin
>>
>> diff --git a/Makefile b/Makefile
>> index e0b3fea..989622b 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -214,7 +214,7 @@ pxe-rtl8139.bin pxe-virtio.bin \
>> bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
>> multiboot.bin linuxboot.bin \
>> s390-zipl.rom \
>> -spapr-rtas.bin
>> +spapr-rtas.bin slof.bin
>> else
>> BLOBS=
>> endif
>> diff --git a/hw/spapr.c b/hw/spapr.c
>> index 9d611a7..c6454e6 100644
>> --- a/hw/spapr.c
>> +++ b/hw/spapr.c
>> @@ -44,6 +44,10 @@
>> #define INITRD_LOAD_ADDR        0x02800000
>> #define FDT_MAX_SIZE            0x10000
>> #define RTAS_MAX_SIZE           0x10000
>> +#define FW_MAX_SIZE             0x400000
>> +#define FW_FILE_NAME            "slof.bin"
>> +
>> +#define MIN_RAM_SLOF		512UL
>>
>> #define TIMEBASE_FREQ           512000000ULL
>>
>> @@ -56,6 +60,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
>>                                sPAPREnvironment *spapr,
>>                                target_phys_addr_t initrd_base,
>>                                target_phys_addr_t initrd_size,
>> +                              const char *boot_device,
>>                                const char *kernel_cmdline,
>>                                target_phys_addr_t rtas_addr,
>>                                target_phys_addr_t rtas_size,
>> @@ -104,6 +109,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
>>                         &start_prop, sizeof(start_prop))));
>>      _FDT((fdt_property(fdt, "linux,initrd-end",
>>                         &end_prop, sizeof(end_prop))));
>> +    _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device)));
>>
>>      _FDT((fdt_end_node(fdt)));
>>
>> @@ -260,7 +266,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
>>      ram_addr_t ram_offset;
>>      target_phys_addr_t fdt_addr, rtas_addr;
>>      uint32_t kernel_base, initrd_base;
>> -    long kernel_size, initrd_size, htab_size, rtas_size;
>> +    long kernel_size, initrd_size, htab_size, rtas_size, fw_size;
>>      long pteg_shift = 17;
>>      int fdt_size;
>>      char *filename;
>> @@ -392,13 +398,33 @@ static void ppc_spapr_init(ram_addr_t ram_size,
>>              initrd_size = 0;
>>          }
>>      } else {
>> -        fprintf(stderr, "pSeries machine needs -kernel for now");
>> -        exit(1);
>> +        if (ram_size<  (MIN_RAM_SLOF<<  20)) {
>> +            fprintf(stderr, "qemu: pSeries SLOF firmware requires>= "
>> +                    "%ldM guest RAM\n", MIN_RAM_SLOF);
>> +            exit(1);
>> +        }
>> +        filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "slof.bin");
>> +        fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
>> +        if (fw_size<  0) {
>> +            hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
>> +            exit(1);
>> +        }
>> +        qemu_free(filename);
>> +        kernel_base = 0x100;
>> +        initrd_base = 0;
>> +        initrd_size = 0;
>> +
>> +        /* SLOF will startup the secondary CPUs using RTAS,
>> +           rather than expecting a kexec() style entry */
>> +        for (i = 0; i<  smp_cpus; i++) {
>> +            envs[i]->halted = 1;
>> +        }
>>      }
>>
>>      /* Prepare the device tree */
>>      fdt = spapr_create_fdt(&fdt_size, ram_size, cpu_model, envs, spapr,
>> -                           initrd_base, initrd_size, kernel_cmdline,
>> +                           initrd_base, initrd_size,
>> +                           boot_device, kernel_cmdline,
>>                             rtas_addr, rtas_size, pteg_shift + 7);
>>      assert(fdt != NULL);
>>
>> @@ -409,6 +435,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
>>      envs[0]->gpr[3] = fdt_addr;
>>      envs[0]->gpr[5] = 0;
>>      envs[0]->hreset_vector = kernel_base;
>> +    envs[0]->halted = 0;
>> }
>>
>> static QEMUMachine spapr_machine = {
>> diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c
>> index 7226853..16b6542 100644
>> --- a/hw/spapr_rtas.c
>> +++ b/hw/spapr_rtas.c
>> @@ -90,6 +90,81 @@ static void rtas_power_off(sPAPREnvironment *spapr,
>>      rtas_st(rets, 0, 0);
>> }
>>
>> +static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr,
>> +                                         uint32_t token, uint32_t nargs,
>> +                                         target_ulong args,
>> +                                         uint32_t nret, target_ulong rets)
>> +{
>> +    target_ulong id;
>> +    CPUState *env;
>> +
>> +    if (nargs != 1 || nret != 2) {
>> +        rtas_st(rets, 0, -3);
>> +        return;
>> +    }
>> +
>> +    id = rtas_ld(args, 0);
>> +    for (env = first_cpu; env; env = env->next_cpu) {
>> +        if (env->cpu_index != id) {
>> +            continue;
>> +        }
>> +
>> +        if (env->halted) {
>> +            rtas_st(rets, 1, 0);
>> +        } else {
>> +            rtas_st(rets, 1, 2);
>> +        }
>> +
>> +        rtas_st(rets, 0, 0);
>> +        return;
>> +    }
>> +
>> +    /* Didn't find a matching cpu */
>> +    rtas_st(rets, 0, -3);
>> +}
>> +
>> +static void rtas_start_cpu(sPAPREnvironment *spapr,
>> +                           uint32_t token, uint32_t nargs,
>> +                           target_ulong args,
>> +                           uint32_t nret, target_ulong rets)
>> +{
>> +    target_ulong id, start, r3;
>> +    CPUState *env;
>> +
>> +    if (nargs != 3 || nret != 1) {
>> +        rtas_st(rets, 0, -3);
>> +        return;
>> +    }
>> +
>> +    id = rtas_ld(args, 0);
>> +    start = rtas_ld(args, 1);
>> +    r3 = rtas_ld(args, 2);
>> +
>> +    for (env = first_cpu; env; env = env->next_cpu) {
>> +        if (env->cpu_index != id) {
>> +            continue;
>> +        }
>> +
>> +        if (!env->halted) {
>> +            rtas_st(rets, 0, -1);
>> +            return;
>> +        }
>> +
>> +        env->msr = (1ULL<<  MSR_SF) | (1ULL<<  MSR_ME);
>> +        env->nip = start;
>> +        env->gpr[3] = r3;
>> +        env->halted = 0;
>> +
>> +        qemu_cpu_kick(env);
>> +
>> +        rtas_st(rets, 0, 0);
>> +        return;
>> +    }
>> +
>> +    /* Didn't find a matching cpu */
>> +    rtas_st(rets, 0, -3);
>> +}
>> +
>> static struct rtas_call {
>>      const char *name;
>>      spapr_rtas_fn fn;
>> @@ -196,5 +271,8 @@ static void register_core_rtas(void)
>>      spapr_rtas_register("display-character", rtas_display_character);
>>      spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
>>      spapr_rtas_register("power-off", rtas_power_off);
>> +    spapr_rtas_register("query-cpu-stopped-state",
>> +                        rtas_query_cpu_stopped_state);
>> +    spapr_rtas_register("start-cpu", rtas_start_cpu);
>> }
>> device_init(register_core_rtas);
>> diff --git a/pc-bios/README b/pc-bios/README
>> index 3fc0944..646a31a 100644
>> --- a/pc-bios/README
>> +++ b/pc-bios/README
>> @@ -13,6 +13,11 @@
>>    The included image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32
>>    and Sparc64 are built from OpenBIOS SVN revision 1018.
>>
>> +- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
>> +  implementation for certain IBM POWER hardware.  The sources are at
>> +  https://github.com/dgibson/SLOF, and the image currently in qemu is
>> +  built from git tag qemu-slof-20110323.
>> +
>> - The PXE roms come from Rom-o-Matic gPXE 0.9.9 with BANNER_TIMEOUT=0
>
> Is this a line removal?

No, it's a bug in your mailer. :)

Glad to see Thunderbird isn't the only one that butcher replies to 
patches (in addition to patches of course).

Paolo

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [Qemu-devel] Re: [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 10:30   ` [Qemu-devel] " Alexander Graf
  2011-03-28 10:51     ` Paolo Bonzini
@ 2011-03-28 10:51     ` Paolo Bonzini
  2011-03-28 11:19       ` Alexander Graf
  1 sibling, 1 reply; 55+ messages in thread
From: Paolo Bonzini @ 2011-03-28 10:51 UTC (permalink / raw)
  To: Alexander Graf; +Cc: paulus, qemu-devel, anton, David Gibson

On 03/28/2011 12:30 PM, Alexander Graf wrote:
>> diff --git a/pc-bios/README b/pc-bios/README
>> index 3fc0944..646a31a 100644
>> --- a/pc-bios/README
>> +++ b/pc-bios/README
>> @@ -13,6 +13,11 @@
>>    The included image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32
>>    and Sparc64 are built from OpenBIOS SVN revision 1018.
>>
>> +- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
>> +  implementation for certain IBM POWER hardware.  The sources are at
>> +  https://github.com/dgibson/SLOF, and the image currently in qemu is
>> +  built from git tag qemu-slof-20110323.
>> +
>> - The PXE roms come from Rom-o-Matic gPXE 0.9.9 with BANNER_TIMEOUT=0
>
> Is this a line removal?

No, it's a bug in your mailer. :)

Glad to see Thunderbird isn't the only one that butcher replies to 
patches (in addition to patches of course).

Paolo

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [Qemu-devel] Re: [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 10:51     ` Paolo Bonzini
@ 2011-03-28 11:19       ` Alexander Graf
  0 siblings, 0 replies; 55+ messages in thread
From: Alexander Graf @ 2011-03-28 11:19 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: paulus, qemu-devel, anton, David Gibson


On 28.03.2011, at 12:51, Paolo Bonzini wrote:

> On 03/28/2011 12:30 PM, Alexander Graf wrote:
>>> diff --git a/pc-bios/README b/pc-bios/README
>>> index 3fc0944..646a31a 100644
>>> --- a/pc-bios/README
>>> +++ b/pc-bios/README
>>> @@ -13,6 +13,11 @@
>>>   The included image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32
>>>   and Sparc64 are built from OpenBIOS SVN revision 1018.
>>> 
>>> +- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
>>> +  implementation for certain IBM POWER hardware.  The sources are at
>>> +  https://github.com/dgibson/SLOF, and the image currently in qemu is
>>> +  built from git tag qemu-slof-20110323.
>>> +
>>> - The PXE roms come from Rom-o-Matic gPXE 0.9.9 with BANNER_TIMEOUT=0
>> 
>> Is this a line removal?
> 
> No, it's a bug in your mailer. :)
> 
> Glad to see Thunderbird isn't the only one that butcher replies to patches (in addition to patches of course).

Glad to head that it's a bug O_o. Phew :)


Alex

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [Qemu-devel] Re: [0/27] Implement emulation of pSeries logical partitions (v5)
  2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
                   ` (26 preceding siblings ...)
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options David Gibson
@ 2011-03-28 11:22 ` Alexander Graf
  27 siblings, 0 replies; 55+ messages in thread
From: Alexander Graf @ 2011-03-28 11:22 UTC (permalink / raw)
  To: David Gibson; +Cc: paulus, qemu-devel, anton


On 25.03.2011, at 04:21, David Gibson wrote:

> This patch series adds a "pseries" machine to qemu, allowing it to
> emulate IBM pSeries logical partitions.  More specifically it
> implements the interface defined by the "PowerPC Architecture Platform
> Requirements" document (PAPR, or sPAPR for short).
> 
> Along the way we add a bunch of support for more modern ppc CPUs than
> are currently supported.  It also makes some significant cleanups to
> the translation code for hash page table based ppc MMUs.
> 
> Please apply.

Acked-by: Alexander Graf <agraf@suse.de>


Alex

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28  9:03       ` Alexander Graf
@ 2011-03-28 12:49         ` Avi Kivity
  2011-03-28 12:53           ` Alexander Graf
  2011-03-28 13:16         ` Anthony Liguori
  1 sibling, 1 reply; 55+ messages in thread
From: Avi Kivity @ 2011-03-28 12:49 UTC (permalink / raw)
  To: Alexander Graf; +Cc: paulus, qemu-devel, anton, David Gibson

On 03/28/2011 11:03 AM, Alexander Graf wrote:
> I'm also not sure this is too important. Most of our firmware blobs come from svn repos which can't be submoduled. And as long as we don't have a consistent policy about it, we can just as well stick with the README file.
>

We can have a git mirror of the subversion repository hosted on 
git.qemu.org, and submodule that.

-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 12:49         ` Avi Kivity
@ 2011-03-28 12:53           ` Alexander Graf
  2011-03-28 13:02             ` Avi Kivity
  0 siblings, 1 reply; 55+ messages in thread
From: Alexander Graf @ 2011-03-28 12:53 UTC (permalink / raw)
  To: Avi Kivity; +Cc: paulus, qemu-devel, anton, David Gibson


On 28.03.2011, at 14:49, Avi Kivity wrote:

> On 03/28/2011 11:03 AM, Alexander Graf wrote:
>> I'm also not sure this is too important. Most of our firmware blobs come from svn repos which can't be submoduled. And as long as we don't have a consistent policy about it, we can just as well stick with the README file.
>> 
> 
> We can have a git mirror of the subversion repository hosted on git.qemu.org, and submodule that.

*shrug* I'm fairly indifferent on that side. But whatever we do, it's out of scope of this patch set :). I personally don't mind the listing in the README file.


Alex

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 12:53           ` Alexander Graf
@ 2011-03-28 13:02             ` Avi Kivity
  2011-03-28 13:08               ` Alexander Graf
  0 siblings, 1 reply; 55+ messages in thread
From: Avi Kivity @ 2011-03-28 13:02 UTC (permalink / raw)
  To: Alexander Graf; +Cc: paulus, qemu-devel, anton, David Gibson

On 03/28/2011 02:53 PM, Alexander Graf wrote:
> On 28.03.2011, at 14:49, Avi Kivity wrote:
>
> >  On 03/28/2011 11:03 AM, Alexander Graf wrote:
> >>  I'm also not sure this is too important. Most of our firmware blobs come from svn repos which can't be submoduled. And as long as we don't have a consistent policy about it, we can just as well stick with the README file.
> >>
> >
> >  We can have a git mirror of the subversion repository hosted on git.qemu.org, and submodule that.
>
> *shrug* I'm fairly indifferent on that side. But whatever we do, it's out of scope of this patch set :). I personally don't mind the listing in the README file.

It depends on how often the code changes.  If it changes regularly and 
qemu is expected to take in newer versions, then we need to record which 
slof version comes with which qemu version.  Submodules do just that.

-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 13:02             ` Avi Kivity
@ 2011-03-28 13:08               ` Alexander Graf
  2011-03-28 13:20                 ` Anthony Liguori
  0 siblings, 1 reply; 55+ messages in thread
From: Alexander Graf @ 2011-03-28 13:08 UTC (permalink / raw)
  To: Avi Kivity; +Cc: paulus, qemu-devel, anton, David Gibson


On 28.03.2011, at 15:02, Avi Kivity wrote:

> On 03/28/2011 02:53 PM, Alexander Graf wrote:
>> On 28.03.2011, at 14:49, Avi Kivity wrote:
>> 
>> >  On 03/28/2011 11:03 AM, Alexander Graf wrote:
>> >>  I'm also not sure this is too important. Most of our firmware blobs come from svn repos which can't be submoduled. And as long as we don't have a consistent policy about it, we can just as well stick with the README file.
>> >>
>> >
>> >  We can have a git mirror of the subversion repository hosted on git.qemu.org, and submodule that.
>> 
>> *shrug* I'm fairly indifferent on that side. But whatever we do, it's out of scope of this patch set :). I personally don't mind the listing in the README file.
> 
> It depends on how often the code changes.  If it changes regularly and qemu is expected to take in newer versions, then we need to record which slof version comes with which qemu version.  Submodules do just that.

A commit id / tag in the README document it pretty well, no? Also, a README file is human readable. Submodules don't really buy anyone anything.

Normal users don't care about the source - they use the bundled binaries. Distributers will have to bundle the source code anyways, because the build process is always offline. You can't always build everything either, as there are targets in qemu that people don't have cross-compilers for.

It feels like the submodule idea is more of a feature that's "cool to play with" rather than of benefit for anyone.


Alex

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28  1:19     ` David Gibson
  2011-03-28  9:03       ` Alexander Graf
@ 2011-03-28 13:13       ` Anthony Liguori
  2011-03-29  7:21         ` David Gibson
  1 sibling, 1 reply; 55+ messages in thread
From: Anthony Liguori @ 2011-03-28 13:13 UTC (permalink / raw)
  To: agraf, qemu-devel, paulus, anton

On 03/27/2011 08:19 PM, David Gibson wrote:
>> We should pull in SLOF via a git submodule.  That ensures we ship
>> the source code along with the binary.
> Um, ok.  Do I need to do anything about this?

We should introduce SLOF as one patch that adds the git submodule and 
the binary.

The best way to do this is for me to pull as binary diffs on the list 
kind of suck.

But before we do the git submodule, I need to mirror SLOF on qemu.org 
such that everything can be fetched from one place.  Ping me later today 
when you get online and I'll explain how to do the git submodule part.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28  9:03       ` Alexander Graf
  2011-03-28 12:49         ` Avi Kivity
@ 2011-03-28 13:16         ` Anthony Liguori
  2011-03-28 14:07           ` David Gibson
  2011-03-28 17:42           ` Blue Swirl
  1 sibling, 2 replies; 55+ messages in thread
From: Anthony Liguori @ 2011-03-28 13:16 UTC (permalink / raw)
  To: Alexander Graf
  Cc: qemu-devel, Blue Swirl, paulus, anton, Aurelien Jarno, David Gibson

On 03/28/2011 04:03 AM, Alexander Graf wrote:
>
>> Um, ok.  Do I need to do anything about this?
> I'm also not sure this is too important.

It's GPL compliance so yes, it's very important.

>   Most of our firmware blobs come from svn repos which can't be submoduled.

The only firmware blob we're not currently including as a git submodule 
is OpenBIOS.  I believe the main reason is that different boards use 
different commits so a single submodule is a bit challenge.  We probably 
ought to figure something out here though for the next release.

Can anyone comment a bit more about OpenBIOS?

BTW, OpenBIOS is already actively mirrored on git.qemu.org so all that's 
needed is a patch that does a git submodule add with the appropriate commit.

>   And as long as we don't have a consistent policy about it, we can just as well stick with the README file.

We do have a consistent policy :-)  We're just not enforcing it as 
tightly as we should.

Any binary we ship in the release tgz's should also have corresponding 
source in a submodule.

Regards,

Anthony Liguori

>
> Alex
>
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 13:08               ` Alexander Graf
@ 2011-03-28 13:20                 ` Anthony Liguori
  0 siblings, 0 replies; 55+ messages in thread
From: Anthony Liguori @ 2011-03-28 13:20 UTC (permalink / raw)
  To: Alexander Graf; +Cc: David Gibson, paulus, Avi Kivity, anton, qemu-devel

On 03/28/2011 08:08 AM, Alexander Graf wrote:
>
>> It depends on how often the code changes.  If it changes regularly and qemu is expected to take in newer versions, then we need to record which slof version comes with which qemu version.  Submodules do just that.
> A commit id / tag in the README document it pretty well, no? Also, a README file is human readable. Submodules don't really buy anyone anything.

When I do a release, I do the equivalent of:

git submodule update --init
rm -rf roms/*/.git
rm -rf .git

Having the information is submodules makes this process automated and 
repeatable.

The main motivation is that we need to ship source for any binary we 
include in our tarball.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 13:16         ` Anthony Liguori
@ 2011-03-28 14:07           ` David Gibson
  2011-03-28 17:42           ` Blue Swirl
  1 sibling, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-28 14:07 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: Alexander Graf, qemu-devel, Blue Swirl, paulus, anton, Aurelien Jarno

On Mon, Mar 28, 2011 at 08:16:51AM -0500, Anthony Liguori wrote:
> On 03/28/2011 04:03 AM, Alexander Graf wrote:
> >
> >>Um, ok.  Do I need to do anything about this?
> >I'm also not sure this is too important.
> 
> It's GPL compliance so yes, it's very important.
> 
> >  Most of our firmware blobs come from svn repos which can't be submoduled.
> 
> The only firmware blob we're not currently including as a git
> submodule is OpenBIOS.  I believe the main reason is that different
> boards use different commits so a single submodule is a bit
> challenge.  We probably ought to figure something out here though
> for the next release.

Um.. where?  I don't see these sources.

> Can anyone comment a bit more about OpenBIOS?
> 
> BTW, OpenBIOS is already actively mirrored on git.qemu.org so all
> that's needed is a patch that does a git submodule add with the
> appropriate commit.
> 
> >  And as long as we don't have a consistent policy about it, we can
> >  just as well stick with the README file.
> 
> We do have a consistent policy :-)  We're just not enforcing it as
> tightly as we should.
> 
> Any binary we ship in the release tgz's should also have
> corresponding source in a submodule.

So, again, what do I need to do?

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 13:16         ` Anthony Liguori
  2011-03-28 14:07           ` David Gibson
@ 2011-03-28 17:42           ` Blue Swirl
  2011-03-28 18:02             ` Anthony Liguori
  1 sibling, 1 reply; 55+ messages in thread
From: Blue Swirl @ 2011-03-28 17:42 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: qemu-devel, Alexander Graf, paulus, anton, Aurelien Jarno, David Gibson

On Mon, Mar 28, 2011 at 4:16 PM, Anthony Liguori <anthony@codemonkey.ws> wrote:
> On 03/28/2011 04:03 AM, Alexander Graf wrote:
>>
>>> Um, ok.  Do I need to do anything about this?
>>
>> I'm also not sure this is too important.
>
> It's GPL compliance so yes, it's very important.
>
>>  Most of our firmware blobs come from svn repos which can't be submoduled.
>
> The only firmware blob we're not currently including as a git submodule is
> OpenBIOS.

No, there's also OpenHack'Ware (ppc_rom.bin) and s390-zipl.rom.

> I believe the main reason is that different boards use different
> commits so a single submodule is a bit challenge.  We probably ought to
> figure something out here though for the next release.
>
> Can anyone comment a bit more about OpenBIOS?
>
> BTW, OpenBIOS is already actively mirrored on git.qemu.org so all that's
> needed is a patch that does a git submodule add with the appropriate commit.

That would be an improvement. Though building various OpenBIOS images
depends on appropriate cross compilers. The situation is actually same
as with SeaBIOS.

>>  And as long as we don't have a consistent policy about it, we can just as
>> well stick with the README file.
>
> We do have a consistent policy :-)  We're just not enforcing it as tightly
> as we should.
>
> Any binary we ship in the release tgz's should also have corresponding
> source in a submodule.

What about OpenHack'Ware (and PReP machine), should it be deleted?

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 17:42           ` Blue Swirl
@ 2011-03-28 18:02             ` Anthony Liguori
  2011-03-28 18:24               ` Aurelien Jarno
  2011-03-29  9:07               ` Alexander Graf
  0 siblings, 2 replies; 55+ messages in thread
From: Anthony Liguori @ 2011-03-28 18:02 UTC (permalink / raw)
  To: Blue Swirl
  Cc: qemu-devel, Alexander Graf, paulus, anton, Aurelien Jarno, David Gibson

On 03/28/2011 12:42 PM, Blue Swirl wrote:
> On Mon, Mar 28, 2011 at 4:16 PM, Anthony Liguori<anthony@codemonkey.ws>  wrote:
>> On 03/28/2011 04:03 AM, Alexander Graf wrote:
>>>> Um, ok.  Do I need to do anything about this?
>>> I'm also not sure this is too important.
>> It's GPL compliance so yes, it's very important.
>>
>>>   Most of our firmware blobs come from svn repos which can't be submoduled.
>> The only firmware blob we're not currently including as a git submodule is
>> OpenBIOS.
> No, there's also OpenHack'Ware (ppc_rom.bin) and s390-zipl.rom.

Alex, what's the source of zipl?

>>   I believe the main reason is that different boards use different
>> commits so a single submodule is a bit challenge.  We probably ought to
>> figure something out here though for the next release.
>>
>> Can anyone comment a bit more about OpenBIOS?
>>
>> BTW, OpenBIOS is already actively mirrored on git.qemu.org so all that's
>> needed is a patch that does a git submodule add with the appropriate commit.
> That would be an improvement. Though building various OpenBIOS images
> depends on appropriate cross compilers. The situation is actually same
> as with SeaBIOS.

Can you do a git submodule add then?

>>>   And as long as we don't have a consistent policy about it, we can just as
>>> well stick with the README file.
>> We do have a consistent policy :-)  We're just not enforcing it as tightly
>> as we should.
>>
>> Any binary we ship in the release tgz's should also have corresponding
>> source in a submodule.
> What about OpenHack'Ware (and PReP machine), should it be deleted?

Yes.  I don't think the source for that is available, correct?  I don't 
think we have any other choice.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 18:02             ` Anthony Liguori
@ 2011-03-28 18:24               ` Aurelien Jarno
  2011-03-28 18:50                 ` Anthony Liguori
  2011-03-29  9:07               ` Alexander Graf
  1 sibling, 1 reply; 55+ messages in thread
From: Aurelien Jarno @ 2011-03-28 18:24 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: qemu-devel, Alexander Graf, Blue Swirl, paulus, anton, David Gibson

On Mon, Mar 28, 2011 at 01:02:45PM -0500, Anthony Liguori wrote:
> On 03/28/2011 12:42 PM, Blue Swirl wrote:
> >On Mon, Mar 28, 2011 at 4:16 PM, Anthony Liguori<anthony@codemonkey.ws>  wrote:
> >>On 03/28/2011 04:03 AM, Alexander Graf wrote:
> >>>>Um, ok.  Do I need to do anything about this?
> >>>I'm also not sure this is too important.
> >>It's GPL compliance so yes, it's very important.
> >>
> >>>  Most of our firmware blobs come from svn repos which can't be submoduled.
> >>The only firmware blob we're not currently including as a git submodule is
> >>OpenBIOS.
> >No, there's also OpenHack'Ware (ppc_rom.bin) and s390-zipl.rom.
> 
> Alex, what's the source of zipl?
> 
> >>  I believe the main reason is that different boards use different
> >>commits so a single submodule is a bit challenge.  We probably ought to
> >>figure something out here though for the next release.
> >>
> >>Can anyone comment a bit more about OpenBIOS?
> >>
> >>BTW, OpenBIOS is already actively mirrored on git.qemu.org so all that's
> >>needed is a patch that does a git submodule add with the appropriate commit.
> >That would be an improvement. Though building various OpenBIOS images
> >depends on appropriate cross compilers. The situation is actually same
> >as with SeaBIOS.
> 
> Can you do a git submodule add then?
> 
> >>>  And as long as we don't have a consistent policy about it, we can just as
> >>>well stick with the README file.
> >>We do have a consistent policy :-)  We're just not enforcing it as tightly
> >>as we should.
> >>
> >>Any binary we ship in the release tgz's should also have corresponding
> >>source in a submodule.
> >What about OpenHack'Ware (and PReP machine), should it be deleted?
> 
> Yes.  I don't think the source for that is available, correct?  I
> don't think we have any other choice.
> 

Debian still holds a copy of the code. People have worked recently to
restore prep support that has been broken by various patches, it would
be a pitty to remove it without before asking them.

-- 
Aurelien Jarno	                        GPG: 1024D/F1BCDB73
aurelien@aurel32.net                 http://www.aurel32.net

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 18:24               ` Aurelien Jarno
@ 2011-03-28 18:50                 ` Anthony Liguori
  2011-03-28 19:52                   ` Aurelien Jarno
  0 siblings, 1 reply; 55+ messages in thread
From: Anthony Liguori @ 2011-03-28 18:50 UTC (permalink / raw)
  To: Aurelien Jarno
  Cc: qemu-devel, Alexander Graf, Blue Swirl, paulus, anton, David Gibson

On 03/28/2011 01:24 PM, Aurelien Jarno wrote:
> On Mon, Mar 28, 2011 at 01:02:45PM -0500, Anthony Liguori wrote:
>> On 03/28/2011 12:42 PM, Blue Swirl wrote:
>>> On Mon, Mar 28, 2011 at 4:16 PM, Anthony Liguori<anthony@codemonkey.ws>   wrote:
>>>> On 03/28/2011 04:03 AM, Alexander Graf wrote:
>>>>>> Um, ok.  Do I need to do anything about this?
>>>>> I'm also not sure this is too important.
>>>> It's GPL compliance so yes, it's very important.
>>>>
>>>>>   Most of our firmware blobs come from svn repos which can't be submoduled.
>>>> The only firmware blob we're not currently including as a git submodule is
>>>> OpenBIOS.
>>> No, there's also OpenHack'Ware (ppc_rom.bin) and s390-zipl.rom.
>> Alex, what's the source of zipl?
>>
>>>>   I believe the main reason is that different boards use different
>>>> commits so a single submodule is a bit challenge.  We probably ought to
>>>> figure something out here though for the next release.
>>>>
>>>> Can anyone comment a bit more about OpenBIOS?
>>>>
>>>> BTW, OpenBIOS is already actively mirrored on git.qemu.org so all that's
>>>> needed is a patch that does a git submodule add with the appropriate commit.
>>> That would be an improvement. Though building various OpenBIOS images
>>> depends on appropriate cross compilers. The situation is actually same
>>> as with SeaBIOS.
>> Can you do a git submodule add then?
>>
>>>>>   And as long as we don't have a consistent policy about it, we can just as
>>>>> well stick with the README file.
>>>> We do have a consistent policy :-)  We're just not enforcing it as tightly
>>>> as we should.
>>>>
>>>> Any binary we ship in the release tgz's should also have corresponding
>>>> source in a submodule.
>>> What about OpenHack'Ware (and PReP machine), should it be deleted?
>> Yes.  I don't think the source for that is available, correct?  I
>> don't think we have any other choice.
>>
> Debian still holds a copy of the code.

I had thought that the actual binary was from Jocelyn and contains 
patches that noone else has.  In fact, the last commit is:

commit 55aa45ddde3283cdd781326d001f7456bf02f684
Author: j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Date:   Mon Oct 1 06:44:33 2007 +0000

     Quickly hack PowerPC BIOS able to boot on CDROM again.

>   People have worked recently to
> restore prep support that has been broken by various patches, it would
> be a pitty to remove it without before asking them.

I'd be very happy to just submodule whatever sources Debian is using.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 18:50                 ` Anthony Liguori
@ 2011-03-28 19:52                   ` Aurelien Jarno
  2011-03-29  9:09                     ` Alexander Graf
  0 siblings, 1 reply; 55+ messages in thread
From: Aurelien Jarno @ 2011-03-28 19:52 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: qemu-devel, Alexander Graf, Blue Swirl, paulus, anton, David Gibson

On Mon, Mar 28, 2011 at 01:50:40PM -0500, Anthony Liguori wrote:
> On 03/28/2011 01:24 PM, Aurelien Jarno wrote:
> >On Mon, Mar 28, 2011 at 01:02:45PM -0500, Anthony Liguori wrote:
> >>On 03/28/2011 12:42 PM, Blue Swirl wrote:
> >>>On Mon, Mar 28, 2011 at 4:16 PM, Anthony Liguori<anthony@codemonkey.ws>   wrote:
> >>>>On 03/28/2011 04:03 AM, Alexander Graf wrote:
> >>>>>>Um, ok.  Do I need to do anything about this?
> >>>>>I'm also not sure this is too important.
> >>>>It's GPL compliance so yes, it's very important.
> >>>>
> >>>>>  Most of our firmware blobs come from svn repos which can't be submoduled.
> >>>>The only firmware blob we're not currently including as a git submodule is
> >>>>OpenBIOS.
> >>>No, there's also OpenHack'Ware (ppc_rom.bin) and s390-zipl.rom.
> >>Alex, what's the source of zipl?
> >>
> >>>>  I believe the main reason is that different boards use different
> >>>>commits so a single submodule is a bit challenge.  We probably ought to
> >>>>figure something out here though for the next release.
> >>>>
> >>>>Can anyone comment a bit more about OpenBIOS?
> >>>>
> >>>>BTW, OpenBIOS is already actively mirrored on git.qemu.org so all that's
> >>>>needed is a patch that does a git submodule add with the appropriate commit.
> >>>That would be an improvement. Though building various OpenBIOS images
> >>>depends on appropriate cross compilers. The situation is actually same
> >>>as with SeaBIOS.
> >>Can you do a git submodule add then?
> >>
> >>>>>  And as long as we don't have a consistent policy about it, we can just as
> >>>>>well stick with the README file.
> >>>>We do have a consistent policy :-)  We're just not enforcing it as tightly
> >>>>as we should.
> >>>>
> >>>>Any binary we ship in the release tgz's should also have corresponding
> >>>>source in a submodule.
> >>>What about OpenHack'Ware (and PReP machine), should it be deleted?
> >>Yes.  I don't think the source for that is available, correct?  I
> >>don't think we have any other choice.
> >>
> >Debian still holds a copy of the code.
> 
> I had thought that the actual binary was from Jocelyn and contains
> patches that noone else has.  In fact, the last commit is:
> 
> commit 55aa45ddde3283cdd781326d001f7456bf02f684
> Author: j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
> Date:   Mon Oct 1 06:44:33 2007 +0000
> 
>     Quickly hack PowerPC BIOS able to boot on CDROM again.
> 
> >  People have worked recently to
> >restore prep support that has been broken by various patches, it would
> >be a pitty to remove it without before asking them.
> 
> I'd be very happy to just submodule whatever sources Debian is using.
> 

I am not sure that it corresponds to the latest code, so it might have
some issues, but at least it is something that is usable. The code is a
vailable from:

http://ftp.debian.org/debian/pool/main/o/openhackware/

Note that the .diff.gz contains a few patches needed to fix build
issues.

-- 
Aurelien Jarno	                        GPG: 1024D/F1BCDB73
aurelien@aurel32.net                 http://www.aurel32.net

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 13:13       ` Anthony Liguori
@ 2011-03-29  7:21         ` David Gibson
  0 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-03-29  7:21 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: paulus, agraf, anton, qemu-devel

On Mon, Mar 28, 2011 at 08:13:04AM -0500, Anthony Liguori wrote:
> On 03/27/2011 08:19 PM, David Gibson wrote:
> >>We should pull in SLOF via a git submodule.  That ensures we ship
> >>the source code along with the binary.
> >Um, ok.  Do I need to do anything about this?
> 
> We should introduce SLOF as one patch that adds the git submodule
> and the binary.
> 
> The best way to do this is for me to pull as binary diffs on the
> list kind of suck.
> 
> But before we do the git submodule, I need to mirror SLOF on
> qemu.org such that everything can be fetched from one place.  Ping
> me later today when you get online and I'll explain how to do the
> git submodule part.

Sorry, I slept badly last night and wasn't up until after you'd gone.
Can you email the instructions instead.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 18:02             ` Anthony Liguori
  2011-03-28 18:24               ` Aurelien Jarno
@ 2011-03-29  9:07               ` Alexander Graf
  1 sibling, 0 replies; 55+ messages in thread
From: Alexander Graf @ 2011-03-29  9:07 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: qemu-devel, Blue Swirl, paulus, anton, Aurelien Jarno, David Gibson


On 28.03.2011, at 20:02, Anthony Liguori wrote:

> On 03/28/2011 12:42 PM, Blue Swirl wrote:
>> On Mon, Mar 28, 2011 at 4:16 PM, Anthony Liguori<anthony@codemonkey.ws>  wrote:
>>> On 03/28/2011 04:03 AM, Alexander Graf wrote:
>>>>> Um, ok.  Do I need to do anything about this?
>>>> I'm also not sure this is too important.
>>> It's GPL compliance so yes, it's very important.
>>> 
>>>>  Most of our firmware blobs come from svn repos which can't be submoduled.
>>> The only firmware blob we're not currently including as a git submodule is
>>> OpenBIOS.
>> No, there's also OpenHack'Ware (ppc_rom.bin) and s390-zipl.rom.
> 
> Alex, what's the source of zipl?

See the README file :P.

  git://repo.or.cz/s390-tools.git virtio-zipl


Alex

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options
  2011-03-28 19:52                   ` Aurelien Jarno
@ 2011-03-29  9:09                     ` Alexander Graf
  0 siblings, 0 replies; 55+ messages in thread
From: Alexander Graf @ 2011-03-29  9:09 UTC (permalink / raw)
  To: Aurelien Jarno
  Cc: QEMU-devel Developers, Blue Swirl, Andreas Färber,
	Paul Mackerras, anton, David Gibson


On 28.03.2011, at 21:52, Aurelien Jarno wrote:

> On Mon, Mar 28, 2011 at 01:50:40PM -0500, Anthony Liguori wrote:
>> On 03/28/2011 01:24 PM, Aurelien Jarno wrote:
>>> On Mon, Mar 28, 2011 at 01:02:45PM -0500, Anthony Liguori wrote:
>>>> On 03/28/2011 12:42 PM, Blue Swirl wrote:
>>>>> On Mon, Mar 28, 2011 at 4:16 PM, Anthony Liguori<anthony@codemonkey.ws>   wrote:
>>>>>> On 03/28/2011 04:03 AM, Alexander Graf wrote:
>>>>>>>> Um, ok.  Do I need to do anything about this?
>>>>>>> I'm also not sure this is too important.
>>>>>> It's GPL compliance so yes, it's very important.
>>>>>> 
>>>>>>> Most of our firmware blobs come from svn repos which can't be submoduled.
>>>>>> The only firmware blob we're not currently including as a git submodule is
>>>>>> OpenBIOS.
>>>>> No, there's also OpenHack'Ware (ppc_rom.bin) and s390-zipl.rom.
>>>> Alex, what's the source of zipl?
>>>> 
>>>>>> I believe the main reason is that different boards use different
>>>>>> commits so a single submodule is a bit challenge.  We probably ought to
>>>>>> figure something out here though for the next release.
>>>>>> 
>>>>>> Can anyone comment a bit more about OpenBIOS?
>>>>>> 
>>>>>> BTW, OpenBIOS is already actively mirrored on git.qemu.org so all that's
>>>>>> needed is a patch that does a git submodule add with the appropriate commit.
>>>>> That would be an improvement. Though building various OpenBIOS images
>>>>> depends on appropriate cross compilers. The situation is actually same
>>>>> as with SeaBIOS.
>>>> Can you do a git submodule add then?
>>>> 
>>>>>>> And as long as we don't have a consistent policy about it, we can just as
>>>>>>> well stick with the README file.
>>>>>> We do have a consistent policy :-)  We're just not enforcing it as tightly
>>>>>> as we should.
>>>>>> 
>>>>>> Any binary we ship in the release tgz's should also have corresponding
>>>>>> source in a submodule.
>>>>> What about OpenHack'Ware (and PReP machine), should it be deleted?
>>>> Yes.  I don't think the source for that is available, correct?  I
>>>> don't think we have any other choice.
>>>> 
>>> Debian still holds a copy of the code.
>> 
>> I had thought that the actual binary was from Jocelyn and contains
>> patches that noone else has.  In fact, the last commit is:
>> 
>> commit 55aa45ddde3283cdd781326d001f7456bf02f684
>> Author: j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
>> Date:   Mon Oct 1 06:44:33 2007 +0000
>> 
>>    Quickly hack PowerPC BIOS able to boot on CDROM again.
>> 
>>> People have worked recently to
>>> restore prep support that has been broken by various patches, it would
>>> be a pitty to remove it without before asking them.
>> 
>> I'd be very happy to just submodule whatever sources Debian is using.
>> 
> 
> I am not sure that it corresponds to the latest code, so it might have
> some issues, but at least it is something that is usable. The code is a
> vailable from:
> 
> http://ftp.debian.org/debian/pool/main/o/openhackware/
> 
> Note that the .diff.gz contains a few patches needed to fix build
> issues.

I really wouldn't want to see PREP getting removed, now that we have a maintainer for it again :). It might be a good idea to recompile the binary we ship from that source though?


Alex

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 01/27] Clean up PowerPC SLB handling code
  2011-03-25  3:21 ` [Qemu-devel] [PATCH 01/27] Clean up PowerPC SLB handling code David Gibson
@ 2011-05-19  5:35   ` Andreas Färber
  2011-05-19  5:39     ` David Gibson
  0 siblings, 1 reply; 55+ messages in thread
From: Andreas Färber @ 2011-05-19  5:35 UTC (permalink / raw)
  To: David Gibson, Alexander Graf
  Cc: Kenneth Salerno, The OpenBIOS Mailinglist, paulus,
	qemu-devel@nongnu.org Developers, anton

Am 25.03.2011 um 04:21 schrieb David Gibson:

> Currently the SLB information when emulating a PowerPC 970 is
> storeed in a structure with the unhelpfully named fields 'tmp'
> and 'tmp64'.  While the layout in these fields does match the
> description of the SLB in the architecture document, it is not
> convenient either for looking up the SLB, or for emulating the
> slbmte instruction.
>
> This patch, therefore, reorganizes the SLB entry structure to be
> divided in the the "ESID related" and "VSID related" fields as
> they are divided in instructions accessing the SLB.
>
> In addition to making the code smaller and more readable, this will
> make it easier to implement for the 1TB segments used in more
> recent PowerPC chips.
>
> Signed-off-by: David Gibson <dwg@au1.ibm.com>

According to my bisect, this patch broke ppc64 OpenBIOS.

David, would you please take a look?

Thanks,
Andreas


## bad: 3964f535c35c08470ac69bd553282af500bc8bb0 Merge remote-tracking  
branch 'mst/for_anthony' into staging
## bad: 8500e3a91292f253002783da267f3b08aead86c1 Clean up slb_lookup()  
function
# good: [a0843a68c4e79df801342ec37347dfebc591082e] vnc: fix build  
error from VNC_DIRTY_WORDS
git bisect good a0843a68c4e79df801342ec37347dfebc591082e
# good: [5256d8bfad9b0113dc2f9b57706eaad26b008987] pci: use devfn for  
pci_find_device() instead of (slot, fn) pair
git bisect good 5256d8bfad9b0113dc2f9b57706eaad26b008987
# good: [fcda98630b121a63c9de0705df02e59f4dc2fecc] lm32: rename raise  
opcode to scall
git bisect good fcda98630b121a63c9de0705df02e59f4dc2fecc
# bad: [d569956eaff4be808419f1f259a5c388d8789db4] Add a hook to allow  
hypercalls to be emulated on PowerPC
git bisect bad d569956eaff4be808419f1f259a5c388d8789db4
# good: [17d9b3af5b7f93e43d7fbdcb6f14cad54de9f1ae] target-ppc: ext32u  
instead of andi with constant
git bisect good 17d9b3af5b7f93e43d7fbdcb6f14cad54de9f1ae
# bad: [c48974903051ceb7cfbda23c22c159ea4b482d93] Allow  
qemu_devtree_setprop() to take arbitrary values
git bisect bad c48974903051ceb7cfbda23c22c159ea4b482d93
# bad: [81762d6dd0d430d87024f2c83e9c4dcc4329fb7d] Clean up PowerPC SLB  
handling code
git bisect bad 81762d6dd0d430d87024f2c83e9c4dcc4329fb7d


> ---
> target-ppc/cpu.h       |   29 +++++++-
> target-ppc/helper.c    |  178 +++++++++++++ 
> +----------------------------------
> target-ppc/helper.h    |    1 -
> target-ppc/op_helper.c |    9 +--
> 4 files changed, 80 insertions(+), 137 deletions(-)
>
> diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
> index deb8d7c..124bbbf 100644
> --- a/target-ppc/cpu.h
> +++ b/target-ppc/cpu.h
> @@ -43,6 +43,8 @@
> # define TARGET_VIRT_ADDR_SPACE_BITS 64
> #endif
>
> +#define TARGET_PAGE_BITS_16M 24
> +
> #else /* defined (TARGET_PPC64) */
> /* PowerPC 32 definitions */
> #define TARGET_LONG_BITS 32
> @@ -359,10 +361,31 @@ union ppc_tlb_t {
>
> typedef struct ppc_slb_t ppc_slb_t;
> struct ppc_slb_t {
> -    uint64_t tmp64;
> -    uint32_t tmp;
> +    uint64_t esid;
> +    uint64_t vsid;
> };
>
> +/* Bits in the SLB ESID word */
> +#define SLB_ESID_ESID           0xFFFFFFFFF0000000ULL
> +#define SLB_ESID_V              0x0000000008000000ULL /* valid */
> +
> +/* Bits in the SLB VSID word */
> +#define SLB_VSID_SHIFT          12
> +#define SLB_VSID_SSIZE_SHIFT    62
> +#define SLB_VSID_B              0xc000000000000000ULL
> +#define SLB_VSID_B_256M         0x0000000000000000ULL
> +#define SLB_VSID_VSID           0x3FFFFFFFFFFFF000ULL
> +#define SLB_VSID_KS             0x0000000000000800ULL
> +#define SLB_VSID_KP             0x0000000000000400ULL
> +#define SLB_VSID_N              0x0000000000000200ULL /* no-execute  
> */
> +#define SLB_VSID_L              0x0000000000000100ULL
> +#define SLB_VSID_C              0x0000000000000080ULL /* class */
> +#define SLB_VSID_LP             0x0000000000000030ULL
> +#define SLB_VSID_ATTR           0x0000000000000FFFULL
> +
> +#define SEGMENT_SHIFT_256M      28
> +#define SEGMENT_MASK_256M       (~((1ULL << SEGMENT_SHIFT_256M) - 1))
> +
> / 
> *****************************************************************************/
> /* Machine state register bits  
> definition                                    */
> #define MSR_SF   63 /* Sixty-four-bit  
> mode                            hflags */
> @@ -755,7 +778,7 @@ void ppc_store_sdr1 (CPUPPCState *env,  
> target_ulong value);
> void ppc_store_asr (CPUPPCState *env, target_ulong value);
> target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr);
> target_ulong ppc_load_sr (CPUPPCState *env, int sr_nr);
> -void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong  
> rs);
> +int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong  
> rs);
> #endif /* defined(TARGET_PPC64) */
> void ppc_store_sr (CPUPPCState *env, int srnum, target_ulong value);
> #endif /* !defined(CONFIG_USER_ONLY) */
> diff --git a/target-ppc/helper.c b/target-ppc/helper.c
> index 4b49101..2094ca3 100644
> --- a/target-ppc/helper.c
> +++ b/target-ppc/helper.c
> @@ -672,85 +672,36 @@ static inline int find_pte(CPUState *env,  
> mmu_ctx_t *ctx, int h, int rw,
> }
>
> #if defined(TARGET_PPC64)
> -static ppc_slb_t *slb_get_entry(CPUPPCState *env, int nr)
> -{
> -    ppc_slb_t *retval = &env->slb[nr];
> -
> -#if 0 // XXX implement bridge mode?
> -    if (env->spr[SPR_ASR] & 1) {
> -        target_phys_addr_t sr_base;
> -
> -        sr_base = env->spr[SPR_ASR] & 0xfffffffffffff000;
> -        sr_base += (12 * nr);
> -
> -        retval->tmp64 = ldq_phys(sr_base);
> -        retval->tmp = ldl_phys(sr_base + 8);
> -    }
> -#endif
> -
> -    return retval;
> -}
> -
> -static void slb_set_entry(CPUPPCState *env, int nr, ppc_slb_t *slb)
> -{
> -    ppc_slb_t *entry = &env->slb[nr];
> -
> -    if (slb == entry)
> -        return;
> -
> -    entry->tmp64 = slb->tmp64;
> -    entry->tmp = slb->tmp;
> -}
> -
> -static inline int slb_is_valid(ppc_slb_t *slb)
> -{
> -    return (int)(slb->tmp64 & 0x0000000008000000ULL);
> -}
> -
> -static inline void slb_invalidate(ppc_slb_t *slb)
> -{
> -    slb->tmp64 &= ~0x0000000008000000ULL;
> -}
> -
> static inline int slb_lookup(CPUPPCState *env, target_ulong eaddr,
>                              target_ulong *vsid, target_ulong  
> *page_mask,
>                              int *attr, int *target_page_bits)
> {
> -    target_ulong mask;
> -    int n, ret;
> +    uint64_t esid;
> +    int n;
>
> -    ret = -5;
>     LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr);
> -    mask = 0x0000000000000000ULL; /* Avoid gcc warning */
> +
> +    esid = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V;
> +
>     for (n = 0; n < env->slb_nr; n++) {
> -        ppc_slb_t *slb = slb_get_entry(env, n);
> -
> -        LOG_SLB("%s: seg %d %016" PRIx64 " %08"
> -                    PRIx32 "\n", __func__, n, slb->tmp64, slb->tmp);
> -        if (slb_is_valid(slb)) {
> -            /* SLB entry is valid */
> -            mask = 0xFFFFFFFFF0000000ULL;
> -            if (slb->tmp & 0x8) {
> -                /* 16 MB PTEs */
> -                if (target_page_bits)
> -                    *target_page_bits = 24;
> -            } else {
> -                /* 4 KB PTEs */
> -                if (target_page_bits)
> -                    *target_page_bits = TARGET_PAGE_BITS;
> -            }
> -            if ((eaddr & mask) == (slb->tmp64 & mask)) {
> -                /* SLB match */
> -                *vsid = ((slb->tmp64 << 24) | (slb->tmp >> 8)) &  
> 0x0003FFFFFFFFFFFFULL;
> -                *page_mask = ~mask;
> -                *attr = slb->tmp & 0xFF;
> -                ret = n;
> -                break;
> +        ppc_slb_t *slb = &env->slb[n];
> +
> +        LOG_SLB("%s: slot %d %016" PRIx64 " %016"
> +                    PRIx64 "\n", __func__, n, slb->esid, slb->vsid);
> +        if (slb->esid == esid) {
> +            *vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT;
> +            *page_mask = ~SEGMENT_MASK_256M;
> +            *attr = slb->vsid & SLB_VSID_ATTR;
> +            if (target_page_bits) {
> +                *target_page_bits = (slb->vsid & SLB_VSID_L)
> +                    ? TARGET_PAGE_BITS_16M
> +                    : TARGET_PAGE_BITS;
>             }
> +            return n;
>         }
>     }
>
> -    return ret;
> +    return -5;
> }
>
> void ppc_slb_invalidate_all (CPUPPCState *env)
> @@ -760,11 +711,10 @@ void ppc_slb_invalidate_all (CPUPPCState *env)
>     do_invalidate = 0;
>     /* XXX: Warning: slbia never invalidates the first segment */
>     for (n = 1; n < env->slb_nr; n++) {
> -        ppc_slb_t *slb = slb_get_entry(env, n);
> +        ppc_slb_t *slb = &env->slb[n];
>
> -        if (slb_is_valid(slb)) {
> -            slb_invalidate(slb);
> -            slb_set_entry(env, n, slb);
> +        if (slb->esid & SLB_ESID_V) {
> +            slb->esid &= ~SLB_ESID_V;
>             /* XXX: given the fact that segment size is 256 MB or 1TB,
>              *      and we still don't have a tlb_flush_mask(env, n,  
> mask)
>              *      in Qemu, we just invalidate all TLBs
> @@ -781,68 +731,44 @@ void ppc_slb_invalidate_one (CPUPPCState *env,  
> uint64_t T0)
>     target_ulong vsid, page_mask;
>     int attr;
>     int n;
> +    ppc_slb_t *slb;
>
>     n = slb_lookup(env, T0, &vsid, &page_mask, &attr, NULL);
> -    if (n >= 0) {
> -        ppc_slb_t *slb = slb_get_entry(env, n);
> -
> -        if (slb_is_valid(slb)) {
> -            slb_invalidate(slb);
> -            slb_set_entry(env, n, slb);
> -            /* XXX: given the fact that segment size is 256 MB or  
> 1TB,
> -             *      and we still don't have a tlb_flush_mask(env,  
> n, mask)
> -             *      in Qemu, we just invalidate all TLBs
> -             */
> -            tlb_flush(env, 1);
> -        }
> +    if (n < 0) {
> +        return;
>     }
> -}
>
> -target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr)
> -{
> -    target_ulong rt;
> -    ppc_slb_t *slb = slb_get_entry(env, slb_nr);
> +    slb = &env->slb[n];
>
> -    if (slb_is_valid(slb)) {
> -        /* SLB entry is valid */
> -        /* Copy SLB bits 62:88 to Rt 37:63 (VSID 23:49) */
> -        rt = slb->tmp >> 8;             /* 65:88 => 40:63 */
> -        rt |= (slb->tmp64 & 0x7) << 24; /* 62:64 => 37:39 */
> -        /* Copy SLB bits 89:92 to Rt 33:36 (KsKpNL) */
> -        rt |= ((slb->tmp >> 4) & 0xF) << 27;
> -    } else {
> -        rt = 0;
> -    }
> -    LOG_SLB("%s: %016" PRIx64 " %08" PRIx32 " => %d "
> -            TARGET_FMT_lx "\n", __func__, slb->tmp64, slb->tmp,  
> slb_nr, rt);
> +    if (slb->esid & SLB_ESID_V) {
> +        slb->esid &= ~SLB_ESID_V;
>
> -    return rt;
> +        /* XXX: given the fact that segment size is 256 MB or 1TB,
> +         *      and we still don't have a tlb_flush_mask(env, n,  
> mask)
> +         *      in Qemu, we just invalidate all TLBs
> +         */
> +        tlb_flush(env, 1);
> +    }
> }
>
> -void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong  
> rs)
> +int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong  
> rs)
> {
> -    ppc_slb_t *slb;
> -
> -    uint64_t vsid;
> -    uint64_t esid;
> -    int flags, valid, slb_nr;
> -
> -    vsid = rs >> 12;
> -    flags = ((rs >> 8) & 0xf);
> +    int slot = rb & 0xfff;
> +    uint64_t esid = rb & ~0xfff;
> +    ppc_slb_t *slb = &env->slb[slot];
>
> -    esid = rb >> 28;
> -    valid = (rb & (1 << 27));
> -    slb_nr = rb & 0xfff;
> +    if (slot >= env->slb_nr) {
> +        return -1;
> +    }
>
> -    slb = slb_get_entry(env, slb_nr);
> -    slb->tmp64 = (esid << 28) | valid | (vsid >> 24);
> -    slb->tmp = (vsid << 8) | (flags << 3);
> +    slb->esid = esid;
> +    slb->vsid = rs;
>
>     LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016"  
> PRIx64
> -            " %08" PRIx32 "\n", __func__, slb_nr, rb, rs, slb->tmp64,
> -            slb->tmp);
> +            " %016" PRIx64 "\n", __func__, slot, rb, rs,
> +            slb->esid, slb->vsid);
>
> -    slb_set_entry(env, slb_nr, slb);
> +    return 0;
> }
> #endif /* defined(TARGET_PPC64) */
>
> @@ -860,24 +786,22 @@ static inline int get_segment(CPUState *env,  
> mmu_ctx_t *ctx,
> {
>     target_phys_addr_t sdr, hash, mask, sdr_mask, htab_mask;
>     target_ulong sr, vsid, vsid_mask, pgidx, page_mask;
> -#if defined(TARGET_PPC64)
> -    int attr;
> -#endif
>     int ds, vsid_sh, sdr_sh, pr, target_page_bits;
>     int ret, ret2;
>
>     pr = msr_pr;
> #if defined(TARGET_PPC64)
>     if (env->mmu_model & POWERPC_MMU_64) {
> +        int attr;
> +
>         LOG_MMU("Check SLBs\n");
>         ret = slb_lookup(env, eaddr, &vsid, &page_mask, &attr,
>                          &target_page_bits);
>         if (ret < 0)
>             return ret;
> -        ctx->key = ((attr & 0x40) && (pr != 0)) ||
> -            ((attr & 0x80) && (pr == 0)) ? 1 : 0;
> +        ctx->key = !!(pr ? (attr & SLB_VSID_KP) : (attr &  
> SLB_VSID_KS));
>         ds = 0;
> -        ctx->nx = attr & 0x10 ? 1 : 0;
> +        ctx->nx = !!(attr & SLB_VSID_N);
>         ctx->eaddr = eaddr;
>         vsid_mask = 0x00003FFFFFFFFF80ULL;
>         vsid_sh = 7;
> diff --git a/target-ppc/helper.h b/target-ppc/helper.h
> index 2bf9283..d512cb0 100644
> --- a/target-ppc/helper.h
> +++ b/target-ppc/helper.h
> @@ -340,7 +340,6 @@ DEF_HELPER_1(74xx_tlbi, void, tl)
> DEF_HELPER_FLAGS_0(tlbia, TCG_CALL_CONST, void)
> DEF_HELPER_FLAGS_1(tlbie, TCG_CALL_CONST, void, tl)
> #if defined(TARGET_PPC64)
> -DEF_HELPER_FLAGS_1(load_slb, TCG_CALL_CONST, tl, tl)
> DEF_HELPER_FLAGS_2(store_slb, TCG_CALL_CONST, void, tl, tl)
> DEF_HELPER_FLAGS_0(slbia, TCG_CALL_CONST, void)
> DEF_HELPER_FLAGS_1(slbie, TCG_CALL_CONST, void, tl)
> diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
> index 17e070a..bf41627 100644
> --- a/target-ppc/op_helper.c
> +++ b/target-ppc/op_helper.c
> @@ -3746,14 +3746,11 @@ void helper_store_sr (target_ulong sr_num,  
> target_ulong val)
>
> /* SLB management */
> #if defined(TARGET_PPC64)
> -target_ulong helper_load_slb (target_ulong slb_nr)
> -{
> -    return ppc_load_slb(env, slb_nr);
> -}
> -
> void helper_store_slb (target_ulong rb, target_ulong rs)
> {
> -    ppc_store_slb(env, rb, rs);
> +    if (ppc_store_slb(env, rb, rs) < 0) {
> +        helper_raise_exception_err(POWERPC_EXCP_PROGRAM,  
> POWERPC_EXCP_INVAL);
> +    }
> }
>
> void helper_slbia (void)
> -- 
> 1.7.1
>
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 01/27] Clean up PowerPC SLB handling code
  2011-05-19  5:35   ` Andreas Färber
@ 2011-05-19  5:39     ` David Gibson
       [not found]       ` <67F15A3F-5EE2-4825-8766-2CA2D6B3356B@web.de>
  0 siblings, 1 reply; 55+ messages in thread
From: David Gibson @ 2011-05-19  5:39 UTC (permalink / raw)
  To: Andreas Färber
  Cc: Alexander Graf, qemu-devel@nongnu.org Developers,
	Kenneth Salerno, paulus, anton, The OpenBIOS Mailinglist

On Thu, May 19, 2011 at 07:35:30AM +0200, Andreas Färber wrote:
> Am 25.03.2011 um 04:21 schrieb David Gibson:
> 
> >Currently the SLB information when emulating a PowerPC 970 is
> >storeed in a structure with the unhelpfully named fields 'tmp'
> >and 'tmp64'.  While the layout in these fields does match the
> >description of the SLB in the architecture document, it is not
> >convenient either for looking up the SLB, or for emulating the
> >slbmte instruction.
> >
> >This patch, therefore, reorganizes the SLB entry structure to be
> >divided in the the "ESID related" and "VSID related" fields as
> >they are divided in instructions accessing the SLB.
> >
> >In addition to making the code smaller and more readable, this will
> >make it easier to implement for the 1TB segments used in more
> >recent PowerPC chips.
> >
> >Signed-off-by: David Gibson <dwg@au1.ibm.com>
> 
> According to my bisect, this patch broke ppc64 OpenBIOS.
> 
> David, would you please take a look?

Uh, sure.  can you describe the symptoms of the breakage, and the
exact qemu setup you've observed the problem with?

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [Qemu-devel] [PATCH 01/27] Clean up PowerPC SLB handling code
       [not found]       ` <67F15A3F-5EE2-4825-8766-2CA2D6B3356B@web.de>
@ 2011-05-19  6:45         ` David Gibson
  0 siblings, 0 replies; 55+ messages in thread
From: David Gibson @ 2011-05-19  6:45 UTC (permalink / raw)
  To: Andreas Färber
  Cc: Alexander Graf, qemu-devel@nongnu.org Developers,
	Kenneth Salerno, paulus, anton, The OpenBIOS Mailinglist

On Thu, May 19, 2011 at 08:00:53AM +0200, Andreas Färber wrote:
> Am 19.05.2011 um 07:39 schrieb David Gibson:
> 
> >On Thu, May 19, 2011 at 07:35:30AM +0200, Andreas Färber wrote:
> >>Am 25.03.2011 um 04:21 schrieb David Gibson:
> >>
> >>>Currently the SLB information when emulating a PowerPC 970 is
> >>>storeed in a structure with the unhelpfully named fields 'tmp'
> >>>and 'tmp64'.  While the layout in these fields does match the
> >>>description of the SLB in the architecture document, it is not
> >>>convenient either for looking up the SLB, or for emulating the
> >>>slbmte instruction.
> >>>
> >>>This patch, therefore, reorganizes the SLB entry structure to be
> >>>divided in the the "ESID related" and "VSID related" fields as
> >>>they are divided in instructions accessing the SLB.
> >>>
> >>>In addition to making the code smaller and more readable, this will
> >>>make it easier to implement for the 1TB segments used in more
> >>>recent PowerPC chips.
> >>>
> >>>Signed-off-by: David Gibson <dwg@au1.ibm.com>
> >>
> >>According to my bisect, this patch broke ppc64 OpenBIOS.
> >>
> >>David, would you please take a look?
> >
> >Uh, sure.  can you describe the symptoms of the breakage, and the
> >exact qemu setup you've observed the problem with?
> 
> $ uname -a
> Darwin PMG5-3.local 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15
> 16:57:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_PPC Power Macintosh
> # Mac OS X 10.5.8 on dual-core G5
> 
> $ gcc-4.2 --version
> powerpc-apple-darwin9-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5577)
> Copyright (C) 2007 Free Software Foundation, Inc.
> This is free software; see the source for copying conditions.  There
> is NO
> warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
> PURPOSE.
> 
> ../qemu/configure --prefix=/Users/andreas/QEMU/latest64 \
> --target-list=ppc-softmmu,ppc64-softmmu \
> --extra-cflags="-arch ppc64" --extra-ldflags="-arch ppc64" --cc=gcc-4.2
> --host-cc=gcc-4.2 \
> --enable-cocoa --disable-kvm --disable-strip --disable-docs
> --enable-io-thread $*
> # I/O thread doesn't matter
> 
> /Users/andreas/QEMU/qemu64/ppc64-softmmu/qemu-system-ppc64 -boot d -cdrom
> /Users/andreas/QEMU/AIX/dvd.1022A4_OBETA_710.iso \
> -bios /Users/andreas/QEMU/OpenBIOS/openbios/obj-ppc64/openbios-qemu.elf
> -m 1024 \
> -M mac99 -prom-env 'auto-boot?=false' -nographic
> 
> CD should be irrelevant, -boot c -hda /dev/null should work the same.

Um, yeah, I thought putting an AIX dvd into a Mac was a bit odd.

> Expected: OpenBIOS banner and > prompt
> Observed: DSI exception on mtmsrd, no serial output

Does this happen only with your openbios image, or does it also happen
with the one included in qemu?

My working theory is that openbios is using the old 32-64 bridge stuff
where segment register accesses are emulated to populate certain SLB
slots.  I have a vague recollection that I saw the code implementing
that when I was doing the cleanup and convinced myself it was not
necessary.  More information as I investigate further.

> (The regression on ppc32 AIX observed by Kenneth seems to be a
> different issue, introduced earlier.)

I don't know what that regression is; I don't read qemu-devel
exhaustively.  If you want my attention, best to CC me directly.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ permalink raw reply	[flat|nested] 55+ messages in thread

end of thread, other threads:[~2011-05-19  6:45 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-25  3:21 [Qemu-devel] [0/27] Implement emulation of pSeries logical partitions (v5) David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 01/27] Clean up PowerPC SLB handling code David Gibson
2011-05-19  5:35   ` Andreas Färber
2011-05-19  5:39     ` David Gibson
     [not found]       ` <67F15A3F-5EE2-4825-8766-2CA2D6B3356B@web.de>
2011-05-19  6:45         ` David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 02/27] Allow qemu_devtree_setprop() to take arbitrary values David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 03/27] Add a hook to allow hypercalls to be emulated on PowerPC David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 04/27] Implement PowerPC slbmfee and slbmfev instructions David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 05/27] Implement missing parts of the logic for the POWER PURR David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 06/27] Correct ppc popcntb logic, implement popcntw and popcntd David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 07/27] Clean up slb_lookup() function David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 08/27] Parse SDR1 on mtspr instead of at translate time David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 09/27] Use "hash" more consistently in ppc mmu code David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 10/27] Better factor the ppc hash translation path David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 11/27] Support 1T segments on ppc David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 12/27] Add POWER7 support for ppc David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 13/27] Start implementing pSeries logical partition machine David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 14/27] Implement the bus structure for PAPR virtual IO David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 15/27] Virtual hash page table handling on pSeries machine David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 16/27] Implement hcall based RTAS for pSeries machines David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 17/27] Implement assorted pSeries hcalls and RTAS methods David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 18/27] Implement the PAPR (pSeries) virtualized interrupt controller (xics) David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 19/27] Add PAPR H_VIO_SIGNAL hypercall and infrastructure for VIO interrupts David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 20/27] Add (virtual) interrupt to PAPR virtual tty device David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 21/27] Implement TCE translation for sPAPR VIO David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 22/27] Implement sPAPR Virtual LAN (ibmveth) David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 23/27] Implement PAPR CRQ hypercalls David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 24/27] Implement PAPR virtual SCSI interface (ibmvscsi) David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 25/27] Add a PAPR TCE-bypass mechanism for the pSeries machine David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 26/27] Implement PAPR VPA functions for pSeries shared processor partitions David Gibson
2011-03-25  3:21 ` [Qemu-devel] [PATCH 27/27] Add SLOF-based partition firmware for pSeries machine, allowing more boot options David Gibson
2011-03-25 18:29   ` Anthony Liguori
2011-03-28  1:19     ` David Gibson
2011-03-28  9:03       ` Alexander Graf
2011-03-28 12:49         ` Avi Kivity
2011-03-28 12:53           ` Alexander Graf
2011-03-28 13:02             ` Avi Kivity
2011-03-28 13:08               ` Alexander Graf
2011-03-28 13:20                 ` Anthony Liguori
2011-03-28 13:16         ` Anthony Liguori
2011-03-28 14:07           ` David Gibson
2011-03-28 17:42           ` Blue Swirl
2011-03-28 18:02             ` Anthony Liguori
2011-03-28 18:24               ` Aurelien Jarno
2011-03-28 18:50                 ` Anthony Liguori
2011-03-28 19:52                   ` Aurelien Jarno
2011-03-29  9:09                     ` Alexander Graf
2011-03-29  9:07               ` Alexander Graf
2011-03-28 13:13       ` Anthony Liguori
2011-03-29  7:21         ` David Gibson
2011-03-28 10:30   ` [Qemu-devel] " Alexander Graf
2011-03-28 10:51     ` Paolo Bonzini
2011-03-28 10:51     ` Paolo Bonzini
2011-03-28 11:19       ` Alexander Graf
2011-03-28 11:22 ` [Qemu-devel] Re: [0/27] Implement emulation of pSeries logical partitions (v5) Alexander Graf

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.