All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support
@ 2016-09-08 14:51 Paul Burton
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 1/8] hw/mips_cmgcr: allow GCR base to be moved Paul Burton
                   ` (9 more replies)
  0 siblings, 10 replies; 16+ messages in thread
From: Paul Burton @ 2016-09-08 14:51 UTC (permalink / raw)
  To: qemu-devel, Aurelien Jarno, Leon Alrae; +Cc: Paul Burton

This series introduces support for the MIPS Boston development board. It begins
by introducing support for moving MIPS Coherence Manager GCRs which Boston
software typically does to avoid conflicting with its flash memory region. An
API is then added to retrieve the emulated MIPS GIC timer frequency, which is
used to report system clock frequency to software via "platform registers"
which the Boston board provides. An issue with the MIPS GIC that current Boston
Linux kernels encounter is fixed, and an API introduced to allow the board to
determine whether the MIPS CPS hardware is supported.

The last 3 patches are more extensive, providing support for the FIT image
format used with Boston, the Xilinx PCIe controller which Boston boards include
3 of, and finally the Boston board support itself.

This can be tested with either U-Boot or Linux if desired. U-Boot support is
available in the following patchset:

  https://www.mail-archive.com/u-boot@lists.denx.de/msg221003.html

Linux kernel support can be found as part of the generic kernel patchset:

  https://www.linux-mips.org/archives/linux-mips/2016-08/msg00456.html

Hopefully this will be merged for v4.9, but it can also be found in a
downstream kernel from Imagination Technologies in the "eng" branch of:

  git://git.linux-mips.org/pub/scm/linux-mti.git

Linux may be built with:

  $ make 64r6el_defconfig
  $ make

The arch/mips/boot/vmlinux.gz.itb image may then be provided to QEMU's -kernel
argument, for example:

  $ qemu-system-mips64el -M boston -kernel vmlinux.gz.itb -serial stdio

Paul Burton (8):
  hw/mips_cmgcr: allow GCR base to be moved
  hw/mips_gictimer: provide API for retrieving frequency
  hw/mips_gic: Update pin state on mask changes
  target-mips: Provide function to test if a CPU supports an ISA
  dtc: Update requirement to v1.4.2
  loader: Support Flattened Image Trees (FIT images)
  hw: xilinx-pcie: Add support for Xilinx AXI PCIe Controller
  hw/mips: MIPS Boston board support

 configure                               |   8 +-
 default-configs/mips-softmmu-common.mak |   2 +
 dtc                                     |   2 +-
 hw/core/Makefile.objs                   |   1 +
 hw/core/loader-fit.c                    | 287 +++++++++++++++++
 hw/core/loader.c                        |   3 +-
 hw/intc/mips_gic.c                      |  56 ++--
 hw/mips/Makefile.objs                   |   1 +
 hw/mips/boston.c                        | 526 ++++++++++++++++++++++++++++++++
 hw/misc/mips_cmgcr.c                    |  17 ++
 hw/pci-host/Makefile.objs               |   1 +
 hw/pci-host/xilinx-pcie.c               | 310 +++++++++++++++++++
 hw/timer/mips_gictimer.c                |   5 +
 include/hw/loader-fit.h                 |  41 +++
 include/hw/loader.h                     |   2 +
 include/hw/misc/mips_cmgcr.h            |   3 +
 include/hw/pci-host/xilinx-pcie.h       | 102 +++++++
 include/hw/timer/mips_gictimer.h        |   1 +
 target-mips/cpu.h                       |   1 +
 target-mips/translate.c                 |  10 +
 20 files changed, 1347 insertions(+), 32 deletions(-)
 create mode 100644 hw/core/loader-fit.c
 create mode 100644 hw/mips/boston.c
 create mode 100644 hw/pci-host/xilinx-pcie.c
 create mode 100644 include/hw/loader-fit.h
 create mode 100644 include/hw/pci-host/xilinx-pcie.h

-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 1/8] hw/mips_cmgcr: allow GCR base to be moved
  2016-09-08 14:51 [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support Paul Burton
@ 2016-09-08 14:51 ` Paul Burton
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 2/8] hw/mips_gictimer: provide API for retrieving frequency Paul Burton
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Paul Burton @ 2016-09-08 14:51 UTC (permalink / raw)
  To: qemu-devel, Aurelien Jarno, Leon Alrae; +Cc: Paul Burton

Support moving the GCR base address & updating the CPU's CP0 CMGCRBase
register appropriately. This is required if a platform needs to move its
GCRs away from other memory, as the MIPS Boston development board does
to avoid its flash memory.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Reviewed-by: Leon Alrae <leon.alrae@imgtec.com>
---
Changes in v2:
- Remove extraneous semicolon from GCR_BASE_GCRBASE_MSK
---
 hw/misc/mips_cmgcr.c         | 17 +++++++++++++++++
 include/hw/misc/mips_cmgcr.h |  3 +++
 2 files changed, 20 insertions(+)

diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c
index b3ba166..a1edb53 100644
--- a/hw/misc/mips_cmgcr.c
+++ b/hw/misc/mips_cmgcr.c
@@ -29,6 +29,20 @@ static inline bool is_gic_connected(MIPSGCRState *s)
     return s->gic_mr != NULL;
 }
 
+static inline void update_gcr_base(MIPSGCRState *gcr, uint64_t val)
+{
+    CPUState *cpu;
+    MIPSCPU *mips_cpu;
+
+    gcr->gcr_base = val & GCR_BASE_GCRBASE_MSK;
+    memory_region_set_address(&gcr->iomem, gcr->gcr_base);
+
+    CPU_FOREACH(cpu) {
+        mips_cpu = MIPS_CPU(cpu);
+        mips_cpu->env.CP0_CMGCRBase = gcr->gcr_base >> 4;
+    }
+}
+
 static inline void update_cpc_base(MIPSGCRState *gcr, uint64_t val)
 {
     if (is_cpc_connected(gcr)) {
@@ -117,6 +131,9 @@ static void gcr_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
     MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other];
 
     switch (addr) {
+    case GCR_BASE_OFS:
+        update_gcr_base(gcr, data);
+        break;
     case GCR_GIC_BASE_OFS:
         update_gic_base(gcr, data);
         break;
diff --git a/include/hw/misc/mips_cmgcr.h b/include/hw/misc/mips_cmgcr.h
index a209d91..c9dfcb4 100644
--- a/include/hw/misc/mips_cmgcr.h
+++ b/include/hw/misc/mips_cmgcr.h
@@ -41,6 +41,9 @@
 #define GCR_L2_CONFIG_BYPASS_SHF    20
 #define GCR_L2_CONFIG_BYPASS_MSK    ((0x1ULL) << GCR_L2_CONFIG_BYPASS_SHF)
 
+/* GCR_BASE register fields */
+#define GCR_BASE_GCRBASE_MSK     0xffffffff8000ULL
+
 /* GCR_GIC_BASE register fields */
 #define GCR_GIC_BASE_GICEN_MSK   1
 #define GCR_GIC_BASE_GICBASE_MSK 0xFFFFFFFE0000ULL
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 2/8] hw/mips_gictimer: provide API for retrieving frequency
  2016-09-08 14:51 [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support Paul Burton
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 1/8] hw/mips_cmgcr: allow GCR base to be moved Paul Burton
@ 2016-09-08 14:51 ` Paul Burton
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 3/8] hw/mips_gic: Update pin state on mask changes Paul Burton
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Paul Burton @ 2016-09-08 14:51 UTC (permalink / raw)
  To: qemu-devel, Aurelien Jarno, Leon Alrae; +Cc: Paul Burton

Provide a new function mips_gictimer_get_freq() which returns the
frequency at which a GIC timer will count. This will be useful for
boards which perform setup based upon this frequency.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Reviewed-by: Leon Alrae <leon.alrae@imgtec.com>
---
 hw/timer/mips_gictimer.c         | 5 +++++
 include/hw/timer/mips_gictimer.h | 1 +
 2 files changed, 6 insertions(+)

diff --git a/hw/timer/mips_gictimer.c b/hw/timer/mips_gictimer.c
index 3698889..f5c5806 100644
--- a/hw/timer/mips_gictimer.c
+++ b/hw/timer/mips_gictimer.c
@@ -14,6 +14,11 @@
 
 #define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
 
+uint32_t mips_gictimer_get_freq(MIPSGICTimerState *gic)
+{
+    return NANOSECONDS_PER_SECOND / TIMER_PERIOD;
+}
+
 static void gic_vptimer_update(MIPSGICTimerState *gictimer,
                                    uint32_t vp_index, uint64_t now)
 {
diff --git a/include/hw/timer/mips_gictimer.h b/include/hw/timer/mips_gictimer.h
index c8bc5d2..c7ca6c8 100644
--- a/include/hw/timer/mips_gictimer.h
+++ b/include/hw/timer/mips_gictimer.h
@@ -31,6 +31,7 @@ struct MIPSGICTimerState {
     MIPSGICTimerCB *cb;
 };
 
+uint32_t mips_gictimer_get_freq(MIPSGICTimerState *gic);
 uint32_t mips_gictimer_get_sh_count(MIPSGICTimerState *gic);
 void mips_gictimer_store_sh_count(MIPSGICTimerState *gic, uint64_t count);
 uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer,
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 3/8] hw/mips_gic: Update pin state on mask changes
  2016-09-08 14:51 [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support Paul Burton
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 1/8] hw/mips_cmgcr: allow GCR base to be moved Paul Burton
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 2/8] hw/mips_gictimer: provide API for retrieving frequency Paul Burton
@ 2016-09-08 14:51 ` Paul Burton
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 4/8] target-mips: Provide function to test if a CPU supports an ISA Paul Burton
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Paul Burton @ 2016-09-08 14:51 UTC (permalink / raw)
  To: qemu-devel, Aurelien Jarno, Leon Alrae; +Cc: Paul Burton

If the GIC interrupt mask is changed by a write to the smask (set mask)
or rmask (reset mask) registers, we need to re-evaluate the state of the
pins/IRQs fed to the CPU. Without doing so we risk leaving a pin high
despite the interrupt that led to that state being masked, or losing
interrupts if an already pending interrupt is unmasked.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Reviewed-by: Leon Alrae <leon.alrae@imgtec.com>
---
 hw/intc/mips_gic.c | 56 ++++++++++++++++++++++++++++++------------------------
 1 file changed, 31 insertions(+), 25 deletions(-)

diff --git a/hw/intc/mips_gic.c b/hw/intc/mips_gic.c
index 6e25773..15e6e40 100644
--- a/hw/intc/mips_gic.c
+++ b/hw/intc/mips_gic.c
@@ -20,31 +20,29 @@
 #include "kvm_mips.h"
 #include "hw/intc/mips_gic.h"
 
-static void mips_gic_set_vp_irq(MIPSGICState *gic, int vp, int pin, int level)
+static void mips_gic_set_vp_irq(MIPSGICState *gic, int vp, int pin)
 {
-    int ored_level = level;
+    int ored_level = 0;
     int i;
 
     /* ORing pending registers sharing same pin */
-    if (!ored_level) {
-        for (i = 0; i < gic->num_irq; i++) {
-            if ((gic->irq_state[i].map_pin & GIC_MAP_MSK) == pin &&
-                    gic->irq_state[i].map_vp == vp &&
-                    gic->irq_state[i].enabled) {
-                ored_level |= gic->irq_state[i].pending;
-            }
-            if (ored_level) {
-                /* no need to iterate all interrupts */
-                break;
-            }
+    for (i = 0; i < gic->num_irq; i++) {
+        if ((gic->irq_state[i].map_pin & GIC_MAP_MSK) == pin &&
+                gic->irq_state[i].map_vp == vp &&
+                gic->irq_state[i].enabled) {
+            ored_level |= gic->irq_state[i].pending;
         }
-        if (((gic->vps[vp].compare_map & GIC_MAP_MSK) == pin) &&
-                (gic->vps[vp].mask & GIC_VP_MASK_CMP_MSK)) {
-            /* ORing with local pending register (count/compare) */
-            ored_level |= (gic->vps[vp].pend & GIC_VP_MASK_CMP_MSK) >>
-                          GIC_VP_MASK_CMP_SHF;
+        if (ored_level) {
+            /* no need to iterate all interrupts */
+            break;
         }
     }
+    if (((gic->vps[vp].compare_map & GIC_MAP_MSK) == pin) &&
+            (gic->vps[vp].mask & GIC_VP_MASK_CMP_MSK)) {
+        /* ORing with local pending register (count/compare) */
+        ored_level |= (gic->vps[vp].pend & GIC_VP_MASK_CMP_MSK) >>
+                      GIC_VP_MASK_CMP_SHF;
+    }
     if (kvm_enabled())  {
         kvm_mips_set_ipi_interrupt(mips_env_get_cpu(gic->vps[vp].env),
                                    pin + GIC_CPU_PIN_OFFSET,
@@ -55,21 +53,27 @@ static void mips_gic_set_vp_irq(MIPSGICState *gic, int vp, int pin, int level)
     }
 }
 
-static void gic_set_irq(void *opaque, int n_IRQ, int level)
+static void gic_update_pin_for_irq(MIPSGICState *gic, int n_IRQ)
 {
-    MIPSGICState *gic = (MIPSGICState *) opaque;
     int vp = gic->irq_state[n_IRQ].map_vp;
     int pin = gic->irq_state[n_IRQ].map_pin & GIC_MAP_MSK;
 
+    if (vp < 0 || vp >= gic->num_vps) {
+        return;
+    }
+    mips_gic_set_vp_irq(gic, vp, pin);
+}
+
+static void gic_set_irq(void *opaque, int n_IRQ, int level)
+{
+    MIPSGICState *gic = (MIPSGICState *) opaque;
+
     gic->irq_state[n_IRQ].pending = (uint8_t) level;
     if (!gic->irq_state[n_IRQ].enabled) {
         /* GIC interrupt source disabled */
         return;
     }
-    if (vp < 0 || vp >= gic->num_vps) {
-        return;
-    }
-    mips_gic_set_vp_irq(gic, vp, pin, level);
+    gic_update_pin_for_irq(gic, n_IRQ);
 }
 
 #define OFFSET_CHECK(c)                         \
@@ -209,7 +213,7 @@ static void gic_timer_store_vp_compare(MIPSGICState *gic, uint32_t vp_index,
     gic->vps[vp_index].pend &= ~(1 << GIC_LOCAL_INT_COMPARE);
     if (gic->vps[vp_index].compare_map & GIC_MAP_TO_PIN_MSK) {
         uint32_t pin = (gic->vps[vp_index].compare_map & GIC_MAP_MSK);
-        mips_gic_set_vp_irq(gic, vp_index, pin, 0);
+        mips_gic_set_vp_irq(gic, vp_index, pin);
     }
     mips_gictimer_store_vp_compare(gic->gic_timer, vp_index, compare);
 }
@@ -286,6 +290,7 @@ static void gic_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
         OFFSET_CHECK((base + size * 8) <= gic->num_irq);
         for (i = 0; i < size * 8; i++) {
             gic->irq_state[base + i].enabled &= !((data >> i) & 1);
+            gic_update_pin_for_irq(gic, base + i);
         }
         break;
     case GIC_SH_WEDGE_OFS:
@@ -305,6 +310,7 @@ static void gic_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
         OFFSET_CHECK((base + size * 8) <= gic->num_irq);
         for (i = 0; i < size * 8; i++) {
             gic->irq_state[base + i].enabled |= (data >> i) & 1;
+            gic_update_pin_for_irq(gic, base + i);
         }
         break;
     case GIC_SH_MAP0_PIN_OFS ... GIC_SH_MAP255_PIN_OFS:
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 4/8] target-mips: Provide function to test if a CPU supports an ISA
  2016-09-08 14:51 [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support Paul Burton
                   ` (2 preceding siblings ...)
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 3/8] hw/mips_gic: Update pin state on mask changes Paul Burton
@ 2016-09-08 14:51 ` Paul Burton
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 5/8] dtc: Update requirement to v1.4.2 Paul Burton
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Paul Burton @ 2016-09-08 14:51 UTC (permalink / raw)
  To: qemu-devel, Aurelien Jarno, Leon Alrae; +Cc: Paul Burton

Provide a new cpu_supports_isa function which allows callers to
determine whether a CPU supports one of the ISA_ flags, by testing
whether the associated struct mips_def_t sets the ISA flags in its
insn_flags field.

An example use of this is to allow boards which generate bootloader code
to determine the properties of the CPU that will be used, for example
whether the CPU is 64 bit or which architecture revision it implements.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Reviewed-by: Leon Alrae <leon.alrae@imgtec.com>
---
 target-mips/cpu.h       |  1 +
 target-mips/translate.c | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 5182dc7..cbd17df 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -812,6 +812,7 @@ int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc);
 
 #define cpu_init(cpu_model) CPU(cpu_mips_init(cpu_model))
 bool cpu_supports_cps_smp(const char *cpu_model);
+bool cpu_supports_isa(const char *cpu_model, unsigned int isa);
 void cpu_set_exception_base(int vp_index, target_ulong address);
 
 /* TODO QOM'ify CPU reset and remove */
diff --git a/target-mips/translate.c b/target-mips/translate.c
index bab52cb..c212e4f 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -20192,6 +20192,16 @@ bool cpu_supports_cps_smp(const char *cpu_model)
     return (def->CP0_Config3 & (1 << CP0C3_CMGCR)) != 0;
 }
 
+bool cpu_supports_isa(const char *cpu_model, unsigned int isa)
+{
+    const mips_def_t *def = cpu_mips_find_by_name(cpu_model);
+    if (!def) {
+        return false;
+    }
+
+    return (def->insn_flags & isa) != 0;
+}
+
 void cpu_set_exception_base(int vp_index, target_ulong address)
 {
     MIPSCPU *vp = MIPS_CPU(qemu_get_cpu(vp_index));
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 5/8] dtc: Update requirement to v1.4.2
  2016-09-08 14:51 [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support Paul Burton
                   ` (3 preceding siblings ...)
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 4/8] target-mips: Provide function to test if a CPU supports an ISA Paul Burton
@ 2016-09-08 14:51 ` Paul Burton
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 6/8] loader: Support Flattened Image Trees (FIT images) Paul Burton
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Paul Burton @ 2016-09-08 14:51 UTC (permalink / raw)
  To: qemu-devel, Aurelien Jarno, Leon Alrae; +Cc: Paul Burton

In order to obtain fdt_first_subnode & fdt_next_subnode symbols from
libfdt for use by a later patch, bump the requirement for dtc to v1.4.2
& the submodule to that same version.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
This will rely upon the dtc v1.4.2 tag being pushed to the dtc
repository on git.qemu.org.

Changes in v2:
- New patch
---
 configure | 6 +++---
 dtc       | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/configure b/configure
index 5a9bda1..1c4e117 100755
--- a/configure
+++ b/configure
@@ -3326,11 +3326,11 @@ fi
 if test "$fdt" != "no" ; then
   fdt_libs="-lfdt"
   # explicitly check for libfdt_env.h as it is missing in some stable installs
-  # and test for required functions to make sure we are on a version >= 1.4.0
+  # and test for required functions to make sure we are on a version >= 1.4.2
   cat > $TMPC << EOF
 #include <libfdt.h>
 #include <libfdt_env.h>
-int main(void) { fdt_get_property_by_offset(0, 0, 0); return 0; }
+int main(void) { fdt_first_subnode(0, 0); return 0; }
 EOF
   if compile_prog "" "$fdt_libs" ; then
     # system DTC is good - use it
@@ -3348,7 +3348,7 @@ EOF
     fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs"
   elif test "$fdt" = "yes" ; then
     # have neither and want - prompt for system/submodule install
-    error_exit "DTC (libfdt) version >= 1.4.0 not present. Your options:" \
+    error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:" \
         "  (1) Preferred: Install the DTC (libfdt) devel package" \
         "  (2) Fetch the DTC submodule, using:" \
         "      git submodule update --init dtc"
diff --git a/dtc b/dtc
index 65cc4d2..ec02b34 160000
--- a/dtc
+++ b/dtc
@@ -1 +1 @@
-Subproject commit 65cc4d2748a2c2e6f27f1cf39e07a5dbabd80ebf
+Subproject commit ec02b34c05be04f249ffaaca4b666f5246877dea
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 6/8] loader: Support Flattened Image Trees (FIT images)
  2016-09-08 14:51 [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support Paul Burton
                   ` (4 preceding siblings ...)
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 5/8] dtc: Update requirement to v1.4.2 Paul Burton
@ 2016-09-08 14:51 ` Paul Burton
  2016-10-27 23:31   ` Yongbok Kim
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 7/8] hw: xilinx-pcie: Add support for Xilinx AXI PCIe Controller Paul Burton
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 16+ messages in thread
From: Paul Burton @ 2016-09-08 14:51 UTC (permalink / raw)
  To: qemu-devel, Aurelien Jarno, Leon Alrae; +Cc: Paul Burton

Introduce support for loading Flattened Image Trees, as used by modern
U-Boot. FIT images are essentially flattened device tree files which
contain binary images such as kernels, FDTs or ramdisks along with one
or more configuration nodes describing boot configurations.

The MIPS Boston board typically boots kernels in the form of FIT images,
and will make use of this code.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 hw/core/Makefile.objs   |   1 +
 hw/core/loader-fit.c    | 287 ++++++++++++++++++++++++++++++++++++++++++++++++
 hw/core/loader.c        |   3 +-
 include/hw/loader-fit.h |  41 +++++++
 include/hw/loader.h     |   2 +
 5 files changed, 332 insertions(+), 2 deletions(-)
 create mode 100644 hw/core/loader-fit.c
 create mode 100644 include/hw/loader-fit.h

diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index cfd4840..4adb860 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -14,6 +14,7 @@ common-obj-$(CONFIG_SOFTMMU) += sysbus.o
 common-obj-$(CONFIG_SOFTMMU) += machine.o
 common-obj-$(CONFIG_SOFTMMU) += null-machine.o
 common-obj-$(CONFIG_SOFTMMU) += loader.o
+common-obj-$(CONFIG_SOFTMMU) += loader-fit.o
 common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
 common-obj-$(CONFIG_SOFTMMU) += register.o
 common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o
diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c
new file mode 100644
index 0000000..479a7fb
--- /dev/null
+++ b/hw/core/loader-fit.c
@@ -0,0 +1,287 @@
+/*
+ * Flattened Image Tree loader.
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * 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 "qemu/osdep.h"
+#include "exec/address-spaces.h"
+#include "exec/memory.h"
+#include "hw/loader.h"
+#include "hw/loader-fit.h"
+#include "qemu/cutils.h"
+#include "qemu/error-report.h"
+#include "sysemu/device_tree.h"
+#include "sysemu/sysemu.h"
+
+#include <libfdt.h>
+#include <zlib.h>
+
+static const void *fit_load_image(const void *itb, const char *name,
+                                  int *poff, size_t *psz)
+{
+    const void *data;
+    const char *comp;
+    void *uncomp_data;
+    char path[128];
+    int off, sz;
+    ssize_t uncomp_len;
+
+    snprintf(path, sizeof(path), "/images/%s", name);
+
+    off = fdt_path_offset(itb, path);
+    if (off < 0) {
+        return NULL;
+    }
+    if (poff) {
+        *poff = off;
+    }
+
+    data = fdt_getprop(itb, off, "data", &sz);
+    if (!data) {
+        return NULL;
+    }
+
+    comp = fdt_getprop(itb, off, "compression", NULL);
+    if (!comp || !strcmp(comp, "none")) {
+        if (psz) {
+            *psz = sz;
+        }
+        return data;
+    }
+
+    if (!strcmp(comp, "gzip")) {
+        uncomp_len = 64 << 20;
+        uncomp_data = g_malloc(uncomp_len);
+
+        uncomp_len = gunzip(uncomp_data, uncomp_len, (void *)data, sz);
+        if (uncomp_len < 0) {
+            error_printf("unable to decompress %s image\n", name);
+            g_free(uncomp_data);
+            return NULL;
+        }
+
+        data = g_realloc(uncomp_data, uncomp_len);
+        if (psz) {
+            *psz = uncomp_len;
+        }
+        return data;
+    }
+
+    error_printf("unknown compression '%s'\n", comp);
+    return NULL;
+}
+
+static int fit_image_addr(const void *itb, int img, const char *name,
+                          hwaddr *addr)
+{
+    const void *prop;
+    int len;
+
+    prop = fdt_getprop(itb, img, name, &len);
+    if (!prop) {
+        return -ENOENT;
+    }
+
+    switch (len) {
+    case 4:
+        *addr = fdt32_to_cpu(*(fdt32_t *)prop);
+        return 0;
+    case 8:
+        *addr = fdt64_to_cpu(*(fdt64_t *)prop);
+        return 0;
+    default:
+        error_printf("invalid %s address length %d\n", name, len);
+        return -EINVAL;
+    }
+}
+
+static int fit_load_kernel(const struct fit_loader *ldr, const void *itb,
+                           int cfg, void *opaque, hwaddr *pend)
+{
+    const char *name;
+    const void *data;
+    hwaddr load_addr, entry_addr;
+    int img_off, err;
+    size_t sz;
+
+    name = fdt_getprop(itb, cfg, "kernel", NULL);
+    if (!name) {
+        error_printf("no kernel specified by FIT configuration\n");
+        return -EINVAL;
+    }
+
+    data = fit_load_image(itb, name, &img_off, &sz);
+    if (!data) {
+        error_printf("unable to load kernel image from FIT\n");
+        return -EINVAL;
+    }
+
+    err = fit_image_addr(itb, img_off, "load", &load_addr);
+    if (err) {
+        error_printf("unable to read kernel load address from FIT\n");
+        return err;
+    }
+
+    err = fit_image_addr(itb, img_off, "entry", &entry_addr);
+    if (err) {
+        return err;
+    }
+
+    if (ldr->kernel_filter) {
+        data = ldr->kernel_filter(opaque, data, &load_addr, &entry_addr);
+    }
+
+    if (pend) {
+        *pend = load_addr + sz;
+    }
+
+    load_addr = ldr->addr_to_phys(opaque, load_addr);
+    rom_add_blob_fixed(name, data, sz, load_addr);
+
+    return 0;
+}
+
+static int fit_load_fdt(const struct fit_loader *ldr, const void *itb,
+                        int cfg, void *opaque, const void *match_data,
+                        hwaddr kernel_end)
+{
+    const char *name;
+    const void *data;
+    hwaddr load_addr;
+    int img_off, err;
+    size_t sz;
+
+    name = fdt_getprop(itb, cfg, "fdt", NULL);
+    if (!name) {
+        return 0;
+    }
+
+    data = fit_load_image(itb, name, &img_off, &sz);
+    if (!data) {
+        error_printf("unable to load FDT image from FIT\n");
+        return -EINVAL;
+    }
+
+    err = fit_image_addr(itb, img_off, "load", &load_addr);
+    if (err == -ENOENT) {
+        load_addr = ROUND_UP(kernel_end, 64 * K_BYTE) + (10 * M_BYTE);
+    } else if (err) {
+        return err;
+    }
+
+    if (ldr->fdt_filter) {
+        data = ldr->fdt_filter(opaque, data, match_data, &load_addr);
+    }
+
+    load_addr = ldr->addr_to_phys(opaque, load_addr);
+    sz = fdt_totalsize(data);
+    rom_add_blob_fixed(name, data, sz, load_addr);
+
+    return 0;
+}
+
+static bool fit_cfg_compatible(const void *itb, int cfg, const char *compat)
+{
+    const void *fdt;
+    const char *fdt_name;
+
+    fdt_name = fdt_getprop(itb, cfg, "fdt", NULL);
+    if (!fdt_name) {
+        return false;
+    }
+
+    fdt = fit_load_image(itb, fdt_name, NULL, NULL);
+    if (!fdt) {
+        return false;
+    }
+
+    if (fdt_check_header(fdt)) {
+        return false;
+    }
+
+    if (fdt_node_check_compatible(fdt, 0, compat)) {
+        return false;
+    }
+
+    return true;
+}
+
+int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque)
+{
+    const struct fit_loader_match *match;
+    const void *itb, *match_data = NULL;
+    const char *def_cfg_name;
+    char path[128];
+    int itb_size, configs, cfg_off, off, err;
+    hwaddr kernel_end;
+
+    itb = load_device_tree(filename, &itb_size);
+    if (!itb) {
+        return -EINVAL;
+    }
+
+    configs = fdt_path_offset(itb, "/configurations");
+    if (configs < 0) {
+        return configs;
+    }
+
+    cfg_off = -FDT_ERR_NOTFOUND;
+
+    if (ldr->matches) {
+        for (match = ldr->matches; match->compatible; match++) {
+            off = fdt_first_subnode(itb, configs);
+            while (off >= 0) {
+                if (fit_cfg_compatible(itb, off, match->compatible)) {
+                    cfg_off = off;
+                    match_data = match->data;
+                    break;
+                }
+
+                off = fdt_next_subnode(itb, off);
+            }
+
+            if (cfg_off >= 0) {
+                break;
+            }
+        }
+    }
+
+    if (cfg_off < 0) {
+        def_cfg_name = fdt_getprop(itb, configs, "default", NULL);
+        if (def_cfg_name) {
+            snprintf(path, sizeof(path), "/configurations/%s", def_cfg_name);
+            cfg_off = fdt_path_offset(itb, path);
+        }
+    }
+
+    if (cfg_off < 0) {
+        /* couldn't find a configuration to use */
+        return cfg_off;
+    }
+
+    err = fit_load_kernel(ldr, itb, cfg_off, opaque, &kernel_end);
+    if (err) {
+        return err;
+    }
+
+    err = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end);
+    if (err) {
+        return err;
+    }
+
+    return 0;
+}
diff --git a/hw/core/loader.c b/hw/core/loader.c
index 53e0e41..54a5189 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -513,8 +513,7 @@ static void zfree(void *x, void *addr)
  * overflow on real hardware too. */
 #define UBOOT_MAX_GUNZIP_BYTES (64 << 20)
 
-static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src,
-                      size_t srclen)
+ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen)
 {
     z_stream s;
     ssize_t dstbytes;
diff --git a/include/hw/loader-fit.h b/include/hw/loader-fit.h
new file mode 100644
index 0000000..9e2a068
--- /dev/null
+++ b/include/hw/loader-fit.h
@@ -0,0 +1,41 @@
+/*
+ * Flattened Image Tree loader.
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * 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/>.
+ */
+
+#ifndef HW_LOADER_FIT_H
+#define HW_LOADER_FIT_H
+
+#include <exec/hwaddr.h>
+
+struct fit_loader_match {
+    const char *compatible;
+    const void *data;
+};
+
+struct fit_loader {
+    const struct fit_loader_match *matches;
+    hwaddr (*addr_to_phys)(void *opaque, uint64_t addr);
+    const void *(*fdt_filter)(void *opaque, const void *fdt,
+                              const void *match_data, hwaddr *load_addr);
+    const void *(*kernel_filter)(void *opaque, const void *kernel,
+                                 hwaddr *load_addr, hwaddr *entry_addr);
+};
+
+int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque);
+
+#endif /* HW_LOADER_FIT_H */
diff --git a/include/hw/loader.h b/include/hw/loader.h
index 4879b63..ed8d6e0 100644
--- a/include/hw/loader.h
+++ b/include/hw/loader.h
@@ -107,6 +107,8 @@ int load_uimage(const char *filename, hwaddr *ep,
  */
 int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz);
 
+ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen);
+
 ssize_t read_targphys(const char *name,
                       int fd, hwaddr dst_addr, size_t nbytes);
 void pstrcpy_targphys(const char *name,
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 7/8] hw: xilinx-pcie: Add support for Xilinx AXI PCIe Controller
  2016-09-08 14:51 [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support Paul Burton
                   ` (5 preceding siblings ...)
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 6/8] loader: Support Flattened Image Trees (FIT images) Paul Burton
@ 2016-09-08 14:51 ` Paul Burton
  2016-11-28 23:37   ` Alistair Francis
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 8/8] hw/mips: MIPS Boston board support Paul Burton
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 16+ messages in thread
From: Paul Burton @ 2016-09-08 14:51 UTC (permalink / raw)
  To: qemu-devel, Aurelien Jarno, Leon Alrae; +Cc: Paul Burton

Add support for emulating the Xilinx AXI Root Port Bridge for PCI
Express as described by Xilinx' PG055 document. This is a PCIe
controller that can be used with certain series of Xilinx FPGAs, and is
used on the MIPS Boston board which will make use of this code.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
 hw/pci-host/Makefile.objs         |   1 +
 hw/pci-host/xilinx-pcie.c         | 310 ++++++++++++++++++++++++++++++++++++++
 include/hw/pci-host/xilinx-pcie.h | 102 +++++++++++++
 3 files changed, 413 insertions(+)
 create mode 100644 hw/pci-host/xilinx-pcie.c
 create mode 100644 include/hw/pci-host/xilinx-pcie.h

diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
index 45f1f0e..9c7909c 100644
--- a/hw/pci-host/Makefile.objs
+++ b/hw/pci-host/Makefile.objs
@@ -16,3 +16,4 @@ common-obj-$(CONFIG_FULONG) += bonito.o
 common-obj-$(CONFIG_PCI_PIIX) += piix.o
 common-obj-$(CONFIG_PCI_Q35) += q35.o
 common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
+common-obj-$(CONFIG_PCI_XILINX) += xilinx-pcie.o
diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c
new file mode 100644
index 0000000..2f3a712
--- /dev/null
+++ b/hw/pci-host/xilinx-pcie.c
@@ -0,0 +1,310 @@
+/*
+ * Xilinx PCIe host controller emulation.
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * 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 "qemu/osdep.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci-host/xilinx-pcie.h"
+
+enum root_cfg_reg {
+    ROOTCFG_INTDEC              = 0x138,
+
+    ROOTCFG_INTMASK             = 0x13c,
+#define ROOTCFG_INTMASK_INTX    (1 << 16)
+#define ROOTCFG_INTMASK_MSI     (1 << 17)
+
+    ROOTCFG_PSCR                = 0x144,
+#define ROOTCFG_PSCR_LINK       (1 << 11)
+
+    ROOTCFG_RPSCR               = 0x148,
+#define ROOTCFG_RPSCR_BRIDGEEN  (1 << 0)
+#define ROOTCFG_RPSCR_INTNEMPTY (1 << 18)
+#define ROOTCFG_RPSCR_INTOVF    (1 << 19)
+
+    ROOTCFG_RPIFR1              = 0x158,
+    ROOTCFG_RPIFR2              = 0x15c,
+};
+
+static void xilinx_pcie_update_intr(XilinxPCIEHost *s,
+                                    uint32_t set, uint32_t clear)
+{
+    int level;
+
+    s->intr |= set;
+    s->intr &= ~clear;
+
+    if (s->intr_fifo_r != s->intr_fifo_w) {
+        s->intr |= ROOTCFG_INTMASK_INTX;
+    }
+
+    level = !!(s->intr & s->intr_mask);
+    qemu_set_irq(s->irq, level);
+}
+
+static void xilinx_pcie_queue_intr(XilinxPCIEHost *s,
+                                   uint32_t fifo_reg1, uint32_t fifo_reg2)
+{
+    XilinxPCIEInt *intr;
+    unsigned int new_w;
+
+    new_w = (s->intr_fifo_w + 1) % ARRAY_SIZE(s->intr_fifo);
+    if (new_w == s->intr_fifo_r) {
+        s->rpscr |= ROOTCFG_RPSCR_INTOVF;
+        return;
+    }
+
+    intr = &s->intr_fifo[s->intr_fifo_w];
+    s->intr_fifo_w = new_w;
+
+    intr->fifo_reg1 = fifo_reg1;
+    intr->fifo_reg2 = fifo_reg2;
+
+    xilinx_pcie_update_intr(s, ROOTCFG_INTMASK_INTX, 0);
+}
+
+static void xilinx_pcie_set_irq(void *opaque, int irq_num, int level)
+{
+    XilinxPCIEHost *s = XILINX_PCIE_HOST(opaque);
+
+    if (!level) {
+        return;
+    }
+
+    xilinx_pcie_queue_intr(s, (irq_num << 27) | (level << 29) | (1 << 31), 0);
+}
+
+static void xilinx_pcie_host_realize(DeviceState *dev, Error **errp)
+{
+    PCIHostState *pci = PCI_HOST_BRIDGE(dev);
+    XilinxPCIEHost *s = XILINX_PCIE_HOST(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
+
+    snprintf(s->name, sizeof(s->name), "pcie%u", s->bus_nr);
+
+    /* PCI configuration space */
+    pcie_host_mmcfg_init(pex, s->cfg_size);
+
+    /* MMIO region */
+    memory_region_init(&s->mmio, OBJECT(s), "mmio", UINT64_MAX);
+    memory_region_set_enabled(&s->mmio, false);
+
+    /* dummy I/O region */
+    memory_region_init_ram(&s->io, OBJECT(s), "io", 16, NULL);
+    memory_region_set_enabled(&s->io, false);
+
+    sysbus_init_mmio(sbd, &pex->mmio);
+    sysbus_init_mmio(sbd, &s->mmio);
+
+    pci->bus = pci_register_bus(dev, s->name, xilinx_pcie_set_irq,
+                                pci_swizzle_map_irq_fn, s, &s->mmio,
+                                &s->io, 0, 4, TYPE_PCIE_BUS);
+
+    qdev_set_parent_bus(DEVICE(&s->root), BUS(pci->bus));
+    qdev_init_nofail(DEVICE(&s->root));
+}
+
+static const char *xilinx_pcie_host_root_bus_path(PCIHostState *host_bridge,
+                                                  PCIBus *rootbus)
+{
+    return "0000:00";
+}
+
+static void xilinx_pcie_host_init(Object *obj)
+{
+    XilinxPCIEHost *s = XILINX_PCIE_HOST(obj);
+    XilinxPCIERoot *root = &s->root;
+
+    object_initialize(root, sizeof(*root), TYPE_XILINX_PCIE_ROOT);
+    object_property_add_child(obj, "root", OBJECT(root), NULL);
+    qdev_prop_set_uint32(DEVICE(root), "addr", PCI_DEVFN(0, 0));
+    qdev_prop_set_bit(DEVICE(root), "multifunction", false);
+}
+
+static Property xilinx_pcie_host_props[] = {
+    DEFINE_PROP_UINT32("bus_nr", XilinxPCIEHost, bus_nr, 0),
+    DEFINE_PROP_SIZE("cfg_base", XilinxPCIEHost, cfg_base, 0),
+    DEFINE_PROP_SIZE("cfg_size", XilinxPCIEHost, cfg_size, 32 << 20),
+    DEFINE_PROP_SIZE("mmio_base", XilinxPCIEHost, mmio_base, 0),
+    DEFINE_PROP_SIZE("mmio_size", XilinxPCIEHost, mmio_size, 1 << 20),
+    DEFINE_PROP_PTR("irq", XilinxPCIEHost, irq_void),
+    DEFINE_PROP_BOOL("link_up", XilinxPCIEHost, link_up, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xilinx_pcie_host_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
+
+    hc->root_bus_path = xilinx_pcie_host_root_bus_path;
+    dc->realize = xilinx_pcie_host_realize;
+    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+    dc->fw_name = "pci";
+    dc->props = xilinx_pcie_host_props;
+}
+
+static const TypeInfo xilinx_pcie_host_info = {
+    .name       = TYPE_XILINX_PCIE_HOST,
+    .parent     = TYPE_PCIE_HOST_BRIDGE,
+    .instance_size = sizeof(XilinxPCIEHost),
+    .instance_init = xilinx_pcie_host_init,
+    .class_init = xilinx_pcie_host_class_init,
+};
+
+static uint32_t xilinx_pcie_root_config_read(PCIDevice *d,
+                                             uint32_t address, int len)
+{
+    XilinxPCIEHost *s = XILINX_PCIE_HOST(OBJECT(d)->parent);
+    uint32_t val;
+
+    switch (address) {
+    case ROOTCFG_INTDEC:
+        return s->intr;
+
+    case ROOTCFG_INTMASK:
+        return s->intr_mask;
+
+    case ROOTCFG_PSCR:
+        val = s->link_up ? ROOTCFG_PSCR_LINK : 0;
+        return val;
+
+    case ROOTCFG_RPSCR:
+        if (s->intr_fifo_r != s->intr_fifo_w) {
+            s->rpscr &= ~ROOTCFG_RPSCR_INTNEMPTY;
+        } else {
+            s->rpscr |= ROOTCFG_RPSCR_INTNEMPTY;
+        }
+        return s->rpscr;
+
+    case ROOTCFG_RPIFR1:
+        if (s->intr_fifo_w == s->intr_fifo_r) {
+            /* FIFO empty */
+            return 0;
+        }
+        return s->intr_fifo[s->intr_fifo_r].fifo_reg1;
+
+    case ROOTCFG_RPIFR2:
+        if (s->intr_fifo_w == s->intr_fifo_r) {
+            /* FIFO empty */
+            return 0;
+        }
+        return s->intr_fifo[s->intr_fifo_r].fifo_reg2;
+
+    default:
+        return pci_default_read_config(d, address, len);
+    }
+}
+
+static void xilinx_pcie_root_config_write(PCIDevice *d, uint32_t address,
+                                          uint32_t val, int len)
+{
+    XilinxPCIEHost *s = XILINX_PCIE_HOST(OBJECT(d)->parent);
+
+    switch (address) {
+    case ROOTCFG_INTDEC:
+        xilinx_pcie_update_intr(s, 0, val);
+        break;
+
+    case ROOTCFG_INTMASK:
+        s->intr_mask = val;
+        xilinx_pcie_update_intr(s, 0, 0);
+        break;
+
+    case ROOTCFG_RPSCR:
+        s->rpscr &= ~ROOTCFG_RPSCR_BRIDGEEN;
+        s->rpscr |= val & ROOTCFG_RPSCR_BRIDGEEN;
+        memory_region_set_enabled(&s->mmio, val & ROOTCFG_RPSCR_BRIDGEEN);
+
+        if (val & ROOTCFG_INTMASK_INTX) {
+            s->rpscr &= ~ROOTCFG_INTMASK_INTX;
+        }
+        break;
+
+    case ROOTCFG_RPIFR1:
+    case ROOTCFG_RPIFR2:
+        if (s->intr_fifo_w == s->intr_fifo_r) {
+            /* FIFO empty */
+            return;
+        }
+        s->intr_fifo_r = (s->intr_fifo_r + 1) % ARRAY_SIZE(s->intr_fifo);
+        break;
+
+    default:
+        pci_default_write_config(d, address, val, len);
+        break;
+    }
+}
+
+static int xilinx_pcie_root_init(PCIDevice *dev)
+{
+    BusState *bus = qdev_get_parent_bus(DEVICE(dev));
+    XilinxPCIEHost *s = XILINX_PCIE_HOST(bus->parent);
+
+    dev->config[PCI_COMMAND] = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+    stw_le_p(&dev->config[PCI_MEMORY_BASE], s->mmio_base >> 16);
+    stw_le_p(&dev->config[PCI_MEMORY_LIMIT],
+             ((s->mmio_base + s->mmio_size - 1) >> 16) & 0xfff0);
+
+    pci_bridge_initfn(dev, TYPE_PCI_BUS);
+
+    if (pcie_endpoint_cap_v1_init(dev, 0x80) < 0) {
+        hw_error("Failed to initialize PCIe capability");
+    }
+
+    return 0;
+}
+
+static void xilinx_pcie_root_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+    dc->desc = "Xilinx AXI-PCIe Host Bridge";
+    k->vendor_id = 0x10ee;
+    k->device_id = 0x7021;
+    k->revision = 0;
+    k->class_id = PCI_CLASS_BRIDGE_HOST;
+    k->is_express = true;
+    k->is_bridge = true;
+    k->init = xilinx_pcie_root_init;
+    k->exit = pci_bridge_exitfn;
+    dc->reset = pci_bridge_reset;
+    k->config_read = xilinx_pcie_root_config_read;
+    k->config_write = xilinx_pcie_root_config_write;
+    /*
+     * PCI-facing part of the host bridge, not usable without the
+     * host-facing part, which can't be device_add'ed, yet.
+     */
+    dc->cannot_instantiate_with_device_add_yet = true;
+}
+
+static const TypeInfo xilinx_pcie_root_info = {
+    .name = TYPE_XILINX_PCIE_ROOT,
+    .parent = TYPE_PCI_BRIDGE,
+    .instance_size = sizeof(XilinxPCIERoot),
+    .class_init = xilinx_pcie_root_class_init,
+};
+
+static void xilinx_pcie_register(void)
+{
+    type_register_static(&xilinx_pcie_root_info);
+    type_register_static(&xilinx_pcie_host_info);
+}
+type_init(xilinx_pcie_register)
diff --git a/include/hw/pci-host/xilinx-pcie.h b/include/hw/pci-host/xilinx-pcie.h
new file mode 100644
index 0000000..443c1a6
--- /dev/null
+++ b/include/hw/pci-host/xilinx-pcie.h
@@ -0,0 +1,102 @@
+/*
+ * Xilinx PCIe host controller emulation.
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * 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/>.
+ */
+
+#ifndef HW_XILINX_PCIE_H
+#define HW_XILINX_PCIE_H
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/pci/pcie_host.h"
+
+#define TYPE_XILINX_PCIE_HOST "xilinx-pcie-host"
+#define XILINX_PCIE_HOST(obj) \
+     OBJECT_CHECK(XilinxPCIEHost, (obj), TYPE_XILINX_PCIE_HOST)
+
+#define TYPE_XILINX_PCIE_ROOT "xilinx-pcie-root"
+#define XILINX_PCIE_ROOT(obj) \
+     OBJECT_CHECK(XilinxPCIERoot, (obj), TYPE_XILINX_PCIE_ROOT)
+
+typedef struct XilinxPCIERoot {
+    PCIBridge parent_obj;
+} XilinxPCIERoot;
+
+typedef struct XilinxPCIEInt {
+    uint32_t fifo_reg1;
+    uint32_t fifo_reg2;
+} XilinxPCIEInt;
+
+typedef struct XilinxPCIEHost {
+    PCIExpressHost parent_obj;
+
+    char name[16];
+
+    uint32_t bus_nr;
+    uint64_t cfg_base, cfg_size;
+    uint64_t mmio_base, mmio_size;
+    bool link_up;
+
+    union {
+        qemu_irq irq;
+        void *irq_void;
+    };
+
+    MemoryRegion mmio, io;
+
+    XilinxPCIERoot root;
+
+    uint32_t intr;
+    uint32_t intr_mask;
+    XilinxPCIEInt intr_fifo[16];
+    unsigned int intr_fifo_r, intr_fifo_w;
+    uint32_t rpscr;
+} XilinxPCIEHost;
+
+static inline XilinxPCIEHost *
+xilinx_pcie_init(MemoryRegion *sys_mem, uint32_t bus_nr,
+                 hwaddr cfg_base, uint64_t cfg_size,
+                 hwaddr mmio_base, uint64_t mmio_size,
+                 qemu_irq irq, bool link_up)
+{
+    DeviceState *dev;
+    MemoryRegion *cfg, *mmio;
+
+    dev = qdev_create(NULL, TYPE_XILINX_PCIE_HOST);
+
+    qdev_prop_set_uint32(dev, "bus_nr", bus_nr);
+    qdev_prop_set_uint64(dev, "cfg_base", cfg_base);
+    qdev_prop_set_uint64(dev, "cfg_size", cfg_size);
+    qdev_prop_set_uint64(dev, "mmio_base", mmio_base);
+    qdev_prop_set_uint64(dev, "mmio_size", mmio_size);
+    qdev_prop_set_ptr(dev, "irq", irq);
+    qdev_prop_set_bit(dev, "link_up", link_up);
+
+    qdev_init_nofail(dev);
+
+    cfg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
+    memory_region_add_subregion_overlap(sys_mem, cfg_base, cfg, 0);
+
+    mmio = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
+    memory_region_add_subregion_overlap(sys_mem, 0, mmio, 0);
+
+    return XILINX_PCIE_HOST(dev);
+}
+
+#endif /* HW_XILINX_PCIE_H */
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 8/8] hw/mips: MIPS Boston board support
  2016-09-08 14:51 [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support Paul Burton
                   ` (6 preceding siblings ...)
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 7/8] hw: xilinx-pcie: Add support for Xilinx AXI PCIe Controller Paul Burton
@ 2016-09-08 14:51 ` Paul Burton
  2017-01-24  0:17   ` Yongbok Kim
  2016-09-08 18:58 ` [Qemu-devel] [PATCH v2 0/8] " no-reply
  2016-10-27 18:04 ` Paul Burton
  9 siblings, 1 reply; 16+ messages in thread
From: Paul Burton @ 2016-09-08 14:51 UTC (permalink / raw)
  To: qemu-devel, Aurelien Jarno, Leon Alrae; +Cc: Paul Burton

Introduce support for emulating the MIPS Boston development board. The
Boston board is built around an FPGA & 3 PCIe controllers, one of which
is connected to an Intel EG20T Platform Controller Hub. It is used
during the development & debug of new CPUs and the software intended to
run on them, and is essentially the successor to the older MIPS Malta
board.

This patch does not implement the EG20T, instead connecting an already
supported ICH-9 AHCI controller. Whilst this isn't accurate it's enough
for typical stock Boston software (eg. Linux kernels) to work with hard
disks given that both the ICH-9 & EG20T implement the AHCI
specification.

Boston boards typically boot kernels in the FIT image format, and this
patch will treat kernels provided to QEMU as such. When loading a kernel
directly, the board code will generate minimal firmware much as the
Malta board code does. This firmware will set up the CM, CPC & GIC
register base addresses then set argument registers & jump to the kernel
entry point. Alternatively, bootloader code may be loaded using the bios
argument in which case no firmware will be generated & execution will
proceed from the start of the boot code at the default MIPS boot
exception vector (offset 0x1fc00000 into (c)kseg1).

Currently real Boston boards are always used with FPGA bitfiles that
include a Global Interrupt Controller (GIC), so the interrupt
configuration is only defined for such cases. Therefore the board will
only allow use of CPUs which implement the CPS components, including the
GIC, and will otherwise exit with a message.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
---
Changes in v2:
- Require libfdt for mips*-softmmu builds, so that the FIT loader can be used.
---
 configure                               |   2 +-
 default-configs/mips-softmmu-common.mak |   2 +
 hw/mips/Makefile.objs                   |   1 +
 hw/mips/boston.c                        | 526 ++++++++++++++++++++++++++++++++
 4 files changed, 530 insertions(+), 1 deletion(-)
 create mode 100644 hw/mips/boston.c

diff --git a/configure b/configure
index 1c4e117..242a113 100755
--- a/configure
+++ b/configure
@@ -3308,7 +3308,7 @@ fi
 fdt_required=no
 for target in $target_list; do
   case $target in
-    aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu)
+    aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips*-softmmu)
       fdt_required=yes
     ;;
   esac
diff --git a/default-configs/mips-softmmu-common.mak b/default-configs/mips-softmmu-common.mak
index 0394514..09b7334 100644
--- a/default-configs/mips-softmmu-common.mak
+++ b/default-configs/mips-softmmu-common.mak
@@ -32,3 +32,5 @@ CONFIG_ISA_TESTDEV=y
 CONFIG_EMPTY_SLOT=y
 CONFIG_MIPS_CPS=y
 CONFIG_MIPS_ITU=y
+CONFIG_MIPS_BOSTON=y
+CONFIG_PCI_XILINX=y
diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs
index 9352a1c..48cd2ef 100644
--- a/hw/mips/Makefile.objs
+++ b/hw/mips/Makefile.objs
@@ -4,3 +4,4 @@ obj-$(CONFIG_JAZZ) += mips_jazz.o
 obj-$(CONFIG_FULONG) += mips_fulong2e.o
 obj-y += gt64xxx_pci.o
 obj-$(CONFIG_MIPS_CPS) += cps.o
+obj-$(CONFIG_MIPS_BOSTON) += boston.o
diff --git a/hw/mips/boston.c b/hw/mips/boston.c
new file mode 100644
index 0000000..4e02907
--- /dev/null
+++ b/hw/mips/boston.c
@@ -0,0 +1,526 @@
+/*
+ * MIPS Boston development board emulation.
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * 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 "qemu/osdep.h"
+#include "qemu-common.h"
+
+#include "exec/address-spaces.h"
+#include "hw/boards.h"
+#include "hw/char/serial.h"
+#include "hw/hw.h"
+#include "hw/ide/pci.h"
+#include "hw/ide/ahci.h"
+#include "hw/loader.h"
+#include "hw/loader-fit.h"
+#include "hw/mips/cps.h"
+#include "hw/mips/cpudevs.h"
+#include "hw/pci-host/xilinx-pcie.h"
+#include "qapi/error.h"
+#include "qemu/cutils.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "sysemu/char.h"
+#include "sysemu/device_tree.h"
+#include "sysemu/sysemu.h"
+
+#include <libfdt.h>
+
+#define TYPE_MIPS_BOSTON "mips-boston"
+#define BOSTON(obj) OBJECT_CHECK(BostonState, (obj), TYPE_MIPS_BOSTON)
+
+typedef struct {
+    SysBusDevice parent_obj;
+
+    MachineState *mach;
+    MIPSCPSState *cps;
+    SerialState *uart;
+
+    CharDriverState *lcd_display;
+    char lcd_content[8];
+
+    hwaddr kernel_entry;
+    hwaddr fdt_base;
+} BostonState;
+
+enum boston_plat_reg {
+    PLAT_FPGA_BUILD     = 0x00,
+    PLAT_CORE_CL        = 0x04,
+    PLAT_WRAPPER_CL     = 0x08,
+    PLAT_SYSCLK_STATUS  = 0x0c,
+    PLAT_SOFTRST_CTL    = 0x10,
+#define PLAT_SOFTRST_CTL_SYSRESET       (1 << 4)
+    PLAT_DDR3_STATUS    = 0x14,
+#define PLAT_DDR3_STATUS_LOCKED         (1 << 0)
+#define PLAT_DDR3_STATUS_CALIBRATED     (1 << 2)
+    PLAT_PCIE_STATUS    = 0x18,
+#define PLAT_PCIE_STATUS_PCIE0_LOCKED   (1 << 0)
+#define PLAT_PCIE_STATUS_PCIE1_LOCKED   (1 << 8)
+#define PLAT_PCIE_STATUS_PCIE2_LOCKED   (1 << 16)
+    PLAT_FLASH_CTL      = 0x1c,
+    PLAT_SPARE0         = 0x20,
+    PLAT_SPARE1         = 0x24,
+    PLAT_SPARE2         = 0x28,
+    PLAT_SPARE3         = 0x2c,
+    PLAT_MMCM_DIV       = 0x30,
+#define PLAT_MMCM_DIV_CLK0DIV_SHIFT     0
+#define PLAT_MMCM_DIV_INPUT_SHIFT       8
+#define PLAT_MMCM_DIV_MUL_SHIFT         16
+#define PLAT_MMCM_DIV_CLK1DIV_SHIFT     24
+    PLAT_BUILD_CFG      = 0x34,
+#define PLAT_BUILD_CFG_IOCU_EN          (1 << 0)
+#define PLAT_BUILD_CFG_PCIE0_EN         (1 << 1)
+#define PLAT_BUILD_CFG_PCIE1_EN         (1 << 2)
+#define PLAT_BUILD_CFG_PCIE2_EN         (1 << 3)
+    PLAT_DDR_CFG        = 0x38,
+#define PLAT_DDR_CFG_SIZE               (0xf << 0)
+#define PLAT_DDR_CFG_MHZ                (0xfff << 4)
+    PLAT_NOC_PCIE0_ADDR = 0x3c,
+    PLAT_NOC_PCIE1_ADDR = 0x40,
+    PLAT_NOC_PCIE2_ADDR = 0x44,
+    PLAT_SYS_CTL        = 0x48,
+};
+
+static void boston_lcd_init(CharDriverState *chr)
+{
+    qemu_chr_fe_printf(chr, "        ");
+}
+
+static uint64_t boston_lcd_read(void *opaque, hwaddr addr,
+                                unsigned size)
+{
+    BostonState *s = opaque;
+    uint64_t val = 0;
+
+    switch (size) {
+    case 8:
+        val |= (uint64_t)s->lcd_content[(addr + 7) & 0x7] << 56;
+        val |= (uint64_t)s->lcd_content[(addr + 6) & 0x7] << 48;
+        val |= (uint64_t)s->lcd_content[(addr + 5) & 0x7] << 40;
+        val |= (uint64_t)s->lcd_content[(addr + 4) & 0x7] << 32;
+    case 4:
+        val |= (uint64_t)s->lcd_content[(addr + 3) & 0x7] << 24;
+        val |= (uint64_t)s->lcd_content[(addr + 2) & 0x7] << 16;
+    case 2:
+        val |= (uint64_t)s->lcd_content[(addr + 1) & 0x7] << 8;
+    case 1:
+        val |= (uint64_t)s->lcd_content[(addr + 0) & 0x7];
+        break;
+    }
+
+    return val;
+}
+
+static void boston_lcd_write(void *opaque, hwaddr addr,
+                             uint64_t val, unsigned size)
+{
+    BostonState *s = opaque;
+
+    switch (size) {
+    case 8:
+        s->lcd_content[(addr + 7) & 0x7] = val >> 56;
+        s->lcd_content[(addr + 6) & 0x7] = val >> 48;
+        s->lcd_content[(addr + 5) & 0x7] = val >> 40;
+        s->lcd_content[(addr + 4) & 0x7] = val >> 32;
+    case 4:
+        s->lcd_content[(addr + 3) & 0x7] = val >> 24;
+        s->lcd_content[(addr + 2) & 0x7] = val >> 16;
+    case 2:
+        s->lcd_content[(addr + 1) & 0x7] = val >> 8;
+    case 1:
+        s->lcd_content[(addr + 0) & 0x7] = val;
+        break;
+    }
+
+    qemu_chr_fe_printf(s->lcd_display,
+                       "\r%-8.8s", s->lcd_content);
+}
+
+static const MemoryRegionOps boston_lcd_ops = {
+    .read = boston_lcd_read,
+    .write = boston_lcd_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static uint64_t boston_platreg_read(void *opaque, hwaddr addr,
+                                    unsigned size)
+{
+    BostonState *s = opaque;
+    uint32_t gic_freq, val;
+
+    if (size != 4) {
+        qemu_log_mask(LOG_UNIMP, "%uB platform register read", size);
+        return 0;
+    }
+
+    switch (addr & 0xffff) {
+    case PLAT_FPGA_BUILD:
+    case PLAT_CORE_CL:
+    case PLAT_WRAPPER_CL:
+        return 0;
+    case PLAT_DDR3_STATUS:
+        return PLAT_DDR3_STATUS_LOCKED | PLAT_DDR3_STATUS_CALIBRATED;
+    case PLAT_MMCM_DIV:
+        gic_freq = mips_gictimer_get_freq(s->cps->gic.gic_timer) / 1000000;
+        val = gic_freq << PLAT_MMCM_DIV_INPUT_SHIFT;
+        val |= 1 << PLAT_MMCM_DIV_MUL_SHIFT;
+        val |= 1 << PLAT_MMCM_DIV_CLK0DIV_SHIFT;
+        val |= 1 << PLAT_MMCM_DIV_CLK1DIV_SHIFT;
+        return val;
+    case PLAT_BUILD_CFG:
+        val = PLAT_BUILD_CFG_PCIE0_EN;
+        val |= PLAT_BUILD_CFG_PCIE1_EN;
+        val |= PLAT_BUILD_CFG_PCIE2_EN;
+        return val;
+    case PLAT_DDR_CFG:
+        val = s->mach->ram_size / G_BYTE;
+        assert(!(val & ~PLAT_DDR_CFG_SIZE));
+        val |= PLAT_DDR_CFG_MHZ;
+        return val;
+    default:
+        qemu_log_mask(LOG_UNIMP, "Read platform register 0x%" HWADDR_PRIx,
+                      addr & 0xffff);
+        return 0;
+    }
+}
+
+static void boston_platreg_write(void *opaque, hwaddr addr,
+                                 uint64_t val, unsigned size)
+{
+    if (size != 4) {
+        qemu_log_mask(LOG_UNIMP, "%uB platform register write", size);
+        return;
+    }
+
+    switch (addr & 0xffff) {
+    case PLAT_FPGA_BUILD:
+    case PLAT_CORE_CL:
+    case PLAT_WRAPPER_CL:
+    case PLAT_DDR3_STATUS:
+    case PLAT_PCIE_STATUS:
+    case PLAT_MMCM_DIV:
+    case PLAT_BUILD_CFG:
+    case PLAT_DDR_CFG:
+        /* read only */
+        break;
+    case PLAT_SOFTRST_CTL:
+        if (val & PLAT_SOFTRST_CTL_SYSRESET) {
+            qemu_system_reset_request();
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "Write platform register 0x%" HWADDR_PRIx
+                      " = 0x%" PRIx64, addr & 0xffff, val);
+        break;
+    }
+}
+
+static const MemoryRegionOps boston_platreg_ops = {
+    .read = boston_platreg_read,
+    .write = boston_platreg_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void boston_flash_write(void *opaque, hwaddr addr,
+                               uint64_t val, unsigned size)
+{
+}
+
+static const MemoryRegionOps boston_flash_ops = {
+    .write = boston_flash_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const TypeInfo boston_device = {
+    .name          = TYPE_MIPS_BOSTON,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(BostonState),
+};
+
+static void boston_register_types(void)
+{
+    type_register_static(&boston_device);
+}
+type_init(boston_register_types)
+
+static void gen_firmware(uint32_t *p, hwaddr kernel_entry, hwaddr fdt_addr,
+                         bool is_64b)
+{
+    const uint32_t cm_base = 0x16100000;
+    const uint32_t gic_base = 0x16120000;
+    const uint32_t cpc_base = 0x16200000;
+
+    /* Move CM GCRs */
+    if (is_64b) {
+        stl_p(p++, 0x40287803);                 /* dmfc0 $8, CMGCRBase */
+        stl_p(p++, 0x00084138);                 /* dsll $8, $8, 4 */
+    } else {
+        stl_p(p++, 0x40087803);                 /* mfc0 $8, CMGCRBase */
+        stl_p(p++, 0x00084100);                 /* sll  $8, $8, 4 */
+    }
+    stl_p(p++, 0x3c09a000);                     /* lui  $9, 0xa000 */
+    stl_p(p++, 0x01094025);                     /* or   $8, $9 */
+    stl_p(p++, 0x3c0a0000 | (cm_base >> 16));   /* lui  $10, cm_base >> 16 */
+    if (is_64b) {
+        stl_p(p++, 0xfd0a0008);                 /* sd   $10, 0x8($8) */
+    } else {
+        stl_p(p++, 0xad0a0008);                 /* sw   $10, 0x8($8) */
+    }
+    stl_p(p++, 0x012a4025);                     /* or   $8, $10 */
+
+    /* Move & enable GIC GCRs */
+    stl_p(p++, 0x3c090000 | (gic_base >> 16));  /* lui  $9, gic_base >> 16 */
+    stl_p(p++, 0x35290001);                     /* ori  $9, 0x1 */
+    if (is_64b) {
+        stl_p(p++, 0xfd090080);                 /* sd   $9, 0x80($8) */
+    } else {
+        stl_p(p++, 0xad090080);                 /* sw   $9, 0x80($8) */
+    }
+
+    /* Move & enable CPC GCRs */
+    stl_p(p++, 0x3c090000 | (cpc_base >> 16));  /* lui  $9, cpc_base >> 16 */
+    stl_p(p++, 0x35290001);                     /* ori  $9, 0x1 */
+    if (is_64b) {
+        stl_p(p++, 0xfd090088);                 /* sd   $9, 0x88($8) */
+    } else {
+        stl_p(p++, 0xad090088);                 /* sw   $9, 0x88($8) */
+    }
+
+    /*
+     * Setup argument registers to follow the UHI boot protocol:
+     *
+     * a0/$4 = -2
+     * a1/$5 = virtual address of FDT
+     * a2/$6 = 0
+     * a3/$7 = 0
+     */
+    stl_p(p++, 0x2404fffe);                     /* li   $4, -2 */
+                                                /* lui  $5, hi(fdt_addr) */
+    stl_p(p++, 0x3c050000 | ((fdt_addr >> 16) & 0xffff));
+    if (fdt_addr & 0xffff) {                    /* ori  $5, lo(fdt_addr) */
+        stl_p(p++, 0x34a50000 | (fdt_addr & 0xffff));
+    }
+    stl_p(p++, 0x34060000);                     /* li   $6, 0 */
+    stl_p(p++, 0x34070000);                     /* li   $7, 0 */
+
+    /* Load kernel entry address & jump to it */
+                                                /* lui  $25, hi(kernel_entry) */
+    stl_p(p++, 0x3c190000 | ((kernel_entry >> 16) & 0xffff));
+                                                /* ori  $25, lo(kernel_entry) */
+    stl_p(p++, 0x37390000 | (kernel_entry & 0xffff));
+    stl_p(p++, 0x03200009);                     /* jr   $25 */
+}
+
+static const void *boston_fdt_filter(void *opaque, const void *fdt_orig,
+                                     const void *match_data, hwaddr *load_addr)
+{
+    BostonState *s = BOSTON(opaque);
+    MachineState *machine = s->mach;
+    const char *cmdline;
+    int err;
+    void *fdt;
+    size_t fdt_sz, ram_low_sz, ram_high_sz;
+
+    fdt_sz = fdt_totalsize(fdt_orig) * 2;
+    fdt = g_malloc0(fdt_sz);
+
+    err = fdt_open_into(fdt_orig, fdt, fdt_sz);
+    if (err) {
+        fprintf(stderr, "unable to open FDT\n");
+        return NULL;
+    }
+
+    cmdline = (machine->kernel_cmdline && machine->kernel_cmdline[0])
+            ? machine->kernel_cmdline : " ";
+    err = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+    if (err < 0) {
+        fprintf(stderr, "couldn't set /chosen/bootargs\n");
+        return NULL;
+    }
+
+    ram_low_sz = MIN(256 * M_BYTE, machine->ram_size);
+    ram_high_sz = machine->ram_size - ram_low_sz;
+    qemu_fdt_setprop_sized_cells(fdt, "/memory@0", "reg",
+                                 1, 0x00000000, 1, ram_low_sz,
+                                 1, 0x90000000, 1, ram_high_sz);
+
+    fdt = g_realloc(fdt, fdt_totalsize(fdt));
+    qemu_fdt_dumpdtb(fdt, fdt_sz);
+
+    s->fdt_base = *load_addr;
+
+    return fdt;
+}
+
+static const void *boston_kernel_filter(void *opaque, const void *kernel,
+                                        hwaddr *load_addr, hwaddr *entry_addr)
+{
+    BostonState *s = BOSTON(opaque);
+
+    s->kernel_entry = *entry_addr;
+
+    return kernel;
+}
+
+static const struct fit_loader_match boston_matches[] = {
+    { "img,boston" },
+    { NULL },
+};
+
+static const struct fit_loader boston_fit_loader = {
+    .matches = boston_matches,
+    .addr_to_phys = cpu_mips_kseg0_to_phys,
+    .fdt_filter = boston_fdt_filter,
+    .kernel_filter = boston_kernel_filter,
+};
+
+static void boston_mach_init(MachineState *machine)
+{
+    DeviceState *dev;
+    BostonState *s;
+    Error *err = NULL;
+    const char *cpu_model;
+    MemoryRegion *flash, *ddr, *ddr_low_alias, *lcd, *platreg;
+    MemoryRegion *sys_mem = get_system_memory();
+    XilinxPCIEHost *pcie2;
+    PCIDevice *ahci;
+    DriveInfo *hd[6];
+    int fw_size, fit_err;
+    bool is_64b;
+
+    if (machine->ram_size % G_BYTE) {
+        error_report("Memory size must be a multiple of 1GB");
+        exit(1);
+    }
+
+    cpu_model = machine->cpu_model ?: "I6400";
+
+    dev = qdev_create(NULL, TYPE_MIPS_BOSTON);
+    qdev_init_nofail(dev);
+
+    s = BOSTON(dev);
+    s->mach = machine;
+    s->cps = g_new0(MIPSCPSState, 1);
+
+    if (!cpu_supports_cps_smp(cpu_model)) {
+        error_report("Boston requires CPUs which support CPS");
+        exit(1);
+    }
+
+    is_64b = cpu_supports_isa(cpu_model, ISA_MIPS64);
+
+    object_initialize(s->cps, sizeof(MIPSCPSState), TYPE_MIPS_CPS);
+    qdev_set_parent_bus(DEVICE(s->cps), sysbus_get_default());
+
+    object_property_set_str(OBJECT(s->cps), cpu_model, "cpu-model", &err);
+    object_property_set_int(OBJECT(s->cps), smp_cpus, "num-vp", &err);
+    object_property_set_bool(OBJECT(s->cps), true, "realized", &err);
+
+    if (err != NULL) {
+        error_report("%s", error_get_pretty(err));
+        exit(1);
+    }
+
+    sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1);
+
+    flash =  g_new(MemoryRegion, 1);
+    memory_region_init_rom_device(flash, NULL, &boston_flash_ops, s,
+                                  "boston.flash", 128 * M_BYTE, &err);
+    memory_region_add_subregion_overlap(sys_mem, 0x18000000, flash, 0);
+
+    ddr = g_new(MemoryRegion, 1);
+    memory_region_allocate_system_memory(ddr, NULL, "boston.ddr",
+                                         machine->ram_size);
+    memory_region_add_subregion_overlap(sys_mem, 0x80000000, ddr, 0);
+
+    ddr_low_alias = g_new(MemoryRegion, 1);
+    memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr",
+                             ddr, 0, MIN(machine->ram_size, (256 * M_BYTE)));
+    memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0);
+
+    xilinx_pcie_init(sys_mem, 0,
+                     0x10000000, 32 * M_BYTE,
+                     0x40000000, 1 * G_BYTE,
+                     get_cps_irq(s->cps, 2), false);
+
+    xilinx_pcie_init(sys_mem, 1,
+                     0x12000000, 32 * M_BYTE,
+                     0x20000000, 512 * M_BYTE,
+                     get_cps_irq(s->cps, 1), false);
+
+    pcie2 = xilinx_pcie_init(sys_mem, 2,
+                             0x14000000, 32 * M_BYTE,
+                             0x16000000, 1 * M_BYTE,
+                             get_cps_irq(s->cps, 0), true);
+
+    platreg = g_new(MemoryRegion, 1);
+    memory_region_init_io(platreg, NULL, &boston_platreg_ops, s,
+                          "boston-platregs", 0x1000);
+    memory_region_add_subregion_overlap(sys_mem, 0x17ffd000, platreg, 0);
+
+    if (!serial_hds[0]) {
+            serial_hds[0] = qemu_chr_new("serial0", "null", NULL);
+    }
+
+    s->uart = serial_mm_init(sys_mem, 0x17ffe000, 2,
+                             get_cps_irq(s->cps, 3), 10000000,
+                             serial_hds[0], DEVICE_NATIVE_ENDIAN);
+
+    lcd = g_new(MemoryRegion, 1);
+    memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8);
+    memory_region_add_subregion_overlap(sys_mem, 0x17fff000, lcd, 0);
+    s->lcd_display = qemu_chr_new("lcd", "vc:320x240", boston_lcd_init);
+
+    ahci = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus,
+                                           PCI_DEVFN(0, 0),
+                                           true, TYPE_ICH9_AHCI);
+    g_assert(ARRAY_SIZE(hd) == ICH_AHCI(ahci)->ahci.ports);
+    ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports);
+    ahci_ide_create_devs(ahci, hd);
+
+    if (machine->firmware) {
+        fw_size = load_image_targphys(machine->firmware,
+                                      0x1fc00000, 4 * M_BYTE);
+        if (fw_size == -1) {
+            error_printf("unable to load firmware image '%s'\n",
+                          machine->firmware);
+            exit(1);
+        }
+    } else if (machine->kernel_filename) {
+        fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s);
+        if (fit_err) {
+            error_printf("unable to load FIT image\n");
+            exit(1);
+        }
+
+        gen_firmware(memory_region_get_ram_ptr(flash) + 0x7c00000,
+                     s->kernel_entry, s->fdt_base, is_64b);
+    } else {
+        error_printf("Please provide either a -kernel or -bios argument\n");
+        exit(1);
+    }
+}
+
+static void boston_mach_class_init(MachineClass *mc)
+{
+    mc->desc = "MIPS Boston";
+    mc->init = boston_mach_init;
+    mc->default_ram_size = 2 * G_BYTE;
+    mc->max_cpus = 16;
+}
+DEFINE_MACHINE("boston", boston_mach_class_init)
-- 
2.9.3

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

* Re: [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support
  2016-09-08 14:51 [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support Paul Burton
                   ` (7 preceding siblings ...)
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 8/8] hw/mips: MIPS Boston board support Paul Burton
@ 2016-09-08 18:58 ` no-reply
  2016-09-08 21:16   ` Paul Burton
  2016-10-27 18:04 ` Paul Burton
  9 siblings, 1 reply; 16+ messages in thread
From: no-reply @ 2016-09-08 18:58 UTC (permalink / raw)
  To: paul.burton; +Cc: famz, qemu-devel, aurelien, leon.alrae

Hi,

Your series failed automatic build test. Please find the testing commands and
their output below. If you have docker installed, you can probably reproduce it
locally.

Type: series
Message-id: 20160908145158.30720-1-paul.burton@imgtec.com
Subject: [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support

=== TEST SCRIPT BEGIN ===
#!/bin/bash
set -e
git submodule update --init dtc
make J=8 docker-test-quick@centos6
make J=8 docker-test-mingw@fedora
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
Switched to a new branch 'test'
282c6f6 hw/mips: MIPS Boston board support
cda70d4 hw: xilinx-pcie: Add support for Xilinx AXI PCIe Controller
855acb4 loader: Support Flattened Image Trees (FIT images)
9064092 dtc: Update requirement to v1.4.2
6a4fdbf target-mips: Provide function to test if a CPU supports an ISA
9663016 hw/mips_gic: Update pin state on mask changes
a08d9fd hw/mips_gictimer: provide API for retrieving frequency
9626d9b hw/mips_cmgcr: allow GCR base to be moved

=== OUTPUT BEGIN ===
Submodule 'dtc' (git://git.qemu-project.org/dtc.git) registered for path 'dtc'
Cloning into 'dtc'...
fatal: reference is not a tree: ec02b34c05be04f249ffaaca4b666f5246877dea
Unable to checkout 'ec02b34c05be04f249ffaaca4b666f5246877dea' in submodule path 'dtc'
=== OUTPUT END ===

Test command exited with code: 1


---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to patchew-devel@freelists.org

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

* Re: [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support
  2016-09-08 18:58 ` [Qemu-devel] [PATCH v2 0/8] " no-reply
@ 2016-09-08 21:16   ` Paul Burton
  0 siblings, 0 replies; 16+ messages in thread
From: Paul Burton @ 2016-09-08 21:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: famz, aurelien, leon.alrae

On 08/09/16 19:58, no-reply@patchew.org wrote:
> Hi,
> 
> Your series failed automatic build test. Please find the testing commands and
> their output below. If you have docker installed, you can probably reproduce it
> locally.
> 
> Type: series
> Message-id: 20160908145158.30720-1-paul.burton@imgtec.com
> Subject: [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support
> 
> === TEST SCRIPT BEGIN ===
> #!/bin/bash
> set -e
> git submodule update --init dtc
> make J=8 docker-test-quick@centos6
> make J=8 docker-test-mingw@fedora
> === TEST SCRIPT END ===
> 
> Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
> Switched to a new branch 'test'
> 282c6f6 hw/mips: MIPS Boston board support
> cda70d4 hw: xilinx-pcie: Add support for Xilinx AXI PCIe Controller
> 855acb4 loader: Support Flattened Image Trees (FIT images)
> 9064092 dtc: Update requirement to v1.4.2
> 6a4fdbf target-mips: Provide function to test if a CPU supports an ISA
> 9663016 hw/mips_gic: Update pin state on mask changes
> a08d9fd hw/mips_gictimer: provide API for retrieving frequency
> 9626d9b hw/mips_cmgcr: allow GCR base to be moved
> 
> === OUTPUT BEGIN ===
> Submodule 'dtc' (git://git.qemu-project.org/dtc.git) registered for path 'dtc'
> Cloning into 'dtc'...
> fatal: reference is not a tree: ec02b34c05be04f249ffaaca4b666f5246877dea
> Unable to checkout 'ec02b34c05be04f249ffaaca4b666f5246877dea' in submodule path 'dtc'

Hello all,

Note that this failure is because the dtc v1.4.2 tag (which tags commit
ec02b34c05be) would need pushing to the dtc repository on git.qemu.org,
as mentioned in the email for patch 5.

Thanks,
    Paul

> === OUTPUT END ===
> 
> Test command exited with code: 1
> 
> 
> ---
> Email generated automatically by Patchew [http://patchew.org/].
> Please send your feedback to patchew-devel@freelists.org
> 

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

* Re: [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support
  2016-09-08 14:51 [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support Paul Burton
                   ` (8 preceding siblings ...)
  2016-09-08 18:58 ` [Qemu-devel] [PATCH v2 0/8] " no-reply
@ 2016-10-27 18:04 ` Paul Burton
  2016-10-27 18:17   ` Peter Maydell
  9 siblings, 1 reply; 16+ messages in thread
From: Paul Burton @ 2016-10-27 18:04 UTC (permalink / raw)
  To: qemu-devel, Aurelien Jarno, Yongbok Kim

[-- Attachment #1: Type: text/plain, Size: 3709 bytes --]

On Thursday, 8 September 2016 15:51:50 BST Paul Burton wrote:
> This series introduces support for the MIPS Boston development board. It
> begins by introducing support for moving MIPS Coherence Manager GCRs which
> Boston software typically does to avoid conflicting with its flash memory
> region. An API is then added to retrieve the emulated MIPS GIC timer
> frequency, which is used to report system clock frequency to software via
> "platform registers" which the Boston board provides. An issue with the
> MIPS GIC that current Boston Linux kernels encounter is fixed, and an API
> introduced to allow the board to determine whether the MIPS CPS hardware is
> supported.
> 
> The last 3 patches are more extensive, providing support for the FIT image
> format used with Boston, the Xilinx PCIe controller which Boston boards
> include 3 of, and finally the Boston board support itself.
> 
> This can be tested with either U-Boot or Linux if desired. U-Boot support is
> available in the following patchset:
> 
>   https://www.mail-archive.com/u-boot@lists.denx.de/msg221003.html
> 
> Linux kernel support can be found as part of the generic kernel patchset:
> 
>   https://www.linux-mips.org/archives/linux-mips/2016-08/msg00456.html
> 
> Hopefully this will be merged for v4.9, but it can also be found in a
> downstream kernel from Imagination Technologies in the "eng" branch of:
> 
>   git://git.linux-mips.org/pub/scm/linux-mti.git
> 
> Linux may be built with:
> 
>   $ make 64r6el_defconfig
>   $ make
> 
> The arch/mips/boot/vmlinux.gz.itb image may then be provided to QEMU's
> -kernel argument, for example:
> 
>   $ qemu-system-mips64el -M boston -kernel vmlinux.gz.itb -serial stdio
> 
> Paul Burton (8):
>   hw/mips_cmgcr: allow GCR base to be moved
>   hw/mips_gictimer: provide API for retrieving frequency
>   hw/mips_gic: Update pin state on mask changes
>   target-mips: Provide function to test if a CPU supports an ISA
>   dtc: Update requirement to v1.4.2
>   loader: Support Flattened Image Trees (FIT images)
>   hw: xilinx-pcie: Add support for Xilinx AXI PCIe Controller
>   hw/mips: MIPS Boston board support
> 
>  configure                               |   8 +-
>  default-configs/mips-softmmu-common.mak |   2 +
>  dtc                                     |   2 +-
>  hw/core/Makefile.objs                   |   1 +
>  hw/core/loader-fit.c                    | 287 +++++++++++++++++
>  hw/core/loader.c                        |   3 +-
>  hw/intc/mips_gic.c                      |  56 ++--
>  hw/mips/Makefile.objs                   |   1 +
>  hw/mips/boston.c                        | 526
> ++++++++++++++++++++++++++++++++ hw/misc/mips_cmgcr.c                    | 
> 17 ++
>  hw/pci-host/Makefile.objs               |   1 +
>  hw/pci-host/xilinx-pcie.c               | 310 +++++++++++++++++++
>  hw/timer/mips_gictimer.c                |   5 +
>  include/hw/loader-fit.h                 |  41 +++
>  include/hw/loader.h                     |   2 +
>  include/hw/misc/mips_cmgcr.h            |   3 +
>  include/hw/pci-host/xilinx-pcie.h       | 102 +++++++
>  include/hw/timer/mips_gictimer.h        |   1 +
>  target-mips/cpu.h                       |   1 +
>  target-mips/translate.c                 |  10 +
>  20 files changed, 1347 insertions(+), 32 deletions(-)
>  create mode 100644 hw/core/loader-fit.c
>  create mode 100644 hw/mips/boston.c
>  create mode 100644 hw/pci-host/xilinx-pcie.c
>  create mode 100644 include/hw/loader-fit.h
>  create mode 100644 include/hw/pci-host/xilinx-pcie.h

Hello,

Is there anything else I can do to move this along? These patches have been 
sat waiting for review or merging for what's approaching 2 months now.

Thanks,
    Paul

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

* Re: [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support
  2016-10-27 18:04 ` Paul Burton
@ 2016-10-27 18:17   ` Peter Maydell
  0 siblings, 0 replies; 16+ messages in thread
From: Peter Maydell @ 2016-10-27 18:17 UTC (permalink / raw)
  To: Paul Burton; +Cc: QEMU Developers, Aurelien Jarno, Yongbok Kim

On 27 October 2016 at 19:04, Paul Burton <paul.burton@imgtec.com> wrote:
> On Thursday, 8 September 2016 15:51:50 BST Paul Burton wrote:
>> This series introduces support for the MIPS Boston development board. It
>> begins by introducing support for moving MIPS Coherence Manager GCRs which
>> Boston software typically does to avoid conflicting with its flash memory
>> region. An API is then added to retrieve the emulated MIPS GIC timer
>> frequency, which is used to report system clock frequency to software via
>> "platform registers" which the Boston board provides. An issue with the
>> MIPS GIC that current Boston Linux kernels encounter is fixed, and an API
>> introduced to allow the board to determine whether the MIPS CPS hardware is
>> supported.

> Is there anything else I can do to move this along? These patches have been
> sat waiting for review or merging for what's approaching 2 months now.

As a general rule, the way our dev process works is that until a
maintainer actively picks up a patchset it's still "owned" by the
submitter, and so you have to check that it hasn't been forgotten
and send 'ping' emails as necessary (usually after a couple of weeks
of nothing-happening) to ensure it doesn't get lost.

In this case I suspect the patchset may have accidentally got lost
in the transition of MIPS maintainers from Leon to Yongbok.

thanks
-- PMM

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

* Re: [Qemu-devel] [PATCH v2 6/8] loader: Support Flattened Image Trees (FIT images)
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 6/8] loader: Support Flattened Image Trees (FIT images) Paul Burton
@ 2016-10-27 23:31   ` Yongbok Kim
  0 siblings, 0 replies; 16+ messages in thread
From: Yongbok Kim @ 2016-10-27 23:31 UTC (permalink / raw)
  To: Paul Burton, qemu-devel, Aurelien Jarno



On 08/09/2016 15:51, Paul Burton wrote:
> Introduce support for loading Flattened Image Trees, as used by modern
> U-Boot. FIT images are essentially flattened device tree files which
> contain binary images such as kernels, FDTs or ramdisks along with one
> or more configuration nodes describing boot configurations.
> 
> The MIPS Boston board typically boots kernels in the form of FIT images,
> and will make use of this code.
> 
> Signed-off-by: Paul Burton <paul.burton@imgtec.com>
> ---
>  hw/core/Makefile.objs   |   1 +
>  hw/core/loader-fit.c    | 287 ++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/core/loader.c        |   3 +-
>  include/hw/loader-fit.h |  41 +++++++
>  include/hw/loader.h     |   2 +
>  5 files changed, 332 insertions(+), 2 deletions(-)
>  create mode 100644 hw/core/loader-fit.c
>  create mode 100644 include/hw/loader-fit.h
> 
> diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
> index cfd4840..4adb860 100644
> --- a/hw/core/Makefile.objs
> +++ b/hw/core/Makefile.objs
> @@ -14,6 +14,7 @@ common-obj-$(CONFIG_SOFTMMU) += sysbus.o
>  common-obj-$(CONFIG_SOFTMMU) += machine.o
>  common-obj-$(CONFIG_SOFTMMU) += null-machine.o
>  common-obj-$(CONFIG_SOFTMMU) += loader.o
> +common-obj-$(CONFIG_SOFTMMU) += loader-fit.o
>  common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
>  common-obj-$(CONFIG_SOFTMMU) += register.o
>  common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o
> diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c
> new file mode 100644
> index 0000000..479a7fb
> --- /dev/null
> +++ b/hw/core/loader-fit.c
> @@ -0,0 +1,287 @@
> +/*
> + * Flattened Image Tree loader.
> + *
> + * Copyright (c) 2016 Imagination Technologies
> + *
> + * 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 "qemu/osdep.h"
> +#include "exec/address-spaces.h"
> +#include "exec/memory.h"
> +#include "hw/loader.h"
> +#include "hw/loader-fit.h"
> +#include "qemu/cutils.h"
> +#include "qemu/error-report.h"
> +#include "sysemu/device_tree.h"
> +#include "sysemu/sysemu.h"
> +
> +#include <libfdt.h>
> +#include <zlib.h>
> +
> +static const void *fit_load_image(const void *itb, const char *name,
> +                                  int *poff, size_t *psz)
> +{
> +    const void *data;
> +    const char *comp;
> +    void *uncomp_data;
> +    char path[128];
> +    int off, sz;
> +    ssize_t uncomp_len;
> +
> +    snprintf(path, sizeof(path), "/images/%s", name);
> +
> +    off = fdt_path_offset(itb, path);
> +    if (off < 0) {
> +        return NULL;
> +    }
> +    if (poff) {
> +        *poff = off;
> +    }
> +
> +    data = fdt_getprop(itb, off, "data", &sz);
> +    if (!data) {
> +        return NULL;
> +    }
> +
> +    comp = fdt_getprop(itb, off, "compression", NULL);
> +    if (!comp || !strcmp(comp, "none")) {
> +        if (psz) {
> +            *psz = sz;
> +        }
> +        return data;
> +    }
> +
> +    if (!strcmp(comp, "gzip")) {
> +        uncomp_len = 64 << 20;

Magic number. Perhaps UBOOT_MAX_GUNZIP_BYTES which is defined in the
loader.c? It would be better to move the definition to the loader.h and
reuse it here.

> +        uncomp_data = g_malloc(uncomp_len);
> +
> +        uncomp_len = gunzip(uncomp_data, uncomp_len, (void *)data, sz);
> +        if (uncomp_len < 0) {
> +            error_printf("unable to decompress %s image\n", name);
> +            g_free(uncomp_data);
> +            return NULL;
> +        }
> +
> +        data = g_realloc(uncomp_data, uncomp_len);
> +        if (psz) {
> +            *psz = uncomp_len;
> +        }
> +        return data;
> +    }
> +
> +    error_printf("unknown compression '%s'\n", comp);
> +    return NULL;
> +}
> +
> +static int fit_image_addr(const void *itb, int img, const char *name,
> +                          hwaddr *addr)
> +{
> +    const void *prop;
> +    int len;
> +
> +    prop = fdt_getprop(itb, img, name, &len);
> +    if (!prop) {
> +        return -ENOENT;
> +    }
> +
> +    switch (len) {
> +    case 4:
> +        *addr = fdt32_to_cpu(*(fdt32_t *)prop);
> +        return 0;
> +    case 8:
> +        *addr = fdt64_to_cpu(*(fdt64_t *)prop);
> +        return 0;
> +    default:
> +        error_printf("invalid %s address length %d\n", name, len);
> +        return -EINVAL;
> +    }
> +}
> +
> +static int fit_load_kernel(const struct fit_loader *ldr, const void *itb,
> +                           int cfg, void *opaque, hwaddr *pend)
> +{
> +    const char *name;
> +    const void *data;
> +    hwaddr load_addr, entry_addr;
> +    int img_off, err;
> +    size_t sz;
> +
> +    name = fdt_getprop(itb, cfg, "kernel", NULL);
> +    if (!name) {
> +        error_printf("no kernel specified by FIT configuration\n");
> +        return -EINVAL;
> +    }
> +
> +    data = fit_load_image(itb, name, &img_off, &sz);
> +    if (!data) {
> +        error_printf("unable to load kernel image from FIT\n");
> +        return -EINVAL;
> +    }
> +
> +    err = fit_image_addr(itb, img_off, "load", &load_addr);
> +    if (err) {
> +        error_printf("unable to read kernel load address from FIT\n");
> +        return err;
> +    }
> +
> +    err = fit_image_addr(itb, img_off, "entry", &entry_addr);
> +    if (err) {

Error messages for the consistency.

> +        return err;
> +    }
> +
> +    if (ldr->kernel_filter) {
> +        data = ldr->kernel_filter(opaque, data, &load_addr, &entry_addr);
> +    }
> +
> +    if (pend) {
> +        *pend = load_addr + sz;
> +    }
> +
> +    load_addr = ldr->addr_to_phys(opaque, load_addr);
> +    rom_add_blob_fixed(name, data, sz, load_addr);
> +
> +    return 0;
> +}
> +
> +static int fit_load_fdt(const struct fit_loader *ldr, const void *itb,
> +                        int cfg, void *opaque, const void *match_data,
> +                        hwaddr kernel_end)
> +{
> +    const char *name;
> +    const void *data;
> +    hwaddr load_addr;
> +    int img_off, err;
> +    size_t sz;
> +
> +    name = fdt_getprop(itb, cfg, "fdt", NULL);
> +    if (!name) {
> +        return 0;
> +    }
> +
> +    data = fit_load_image(itb, name, &img_off, &sz);
> +    if (!data) {
> +        error_printf("unable to load FDT image from FIT\n");
> +        return -EINVAL;
> +    }
> +
> +    err = fit_image_addr(itb, img_off, "load", &load_addr);
> +    if (err == -ENOENT) {
> +        load_addr = ROUND_UP(kernel_end, 64 * K_BYTE) + (10 * M_BYTE);
> +    } else if (err) {
> +        return err;
> +    }
> +
> +    if (ldr->fdt_filter) {
> +        data = ldr->fdt_filter(opaque, data, match_data, &load_addr);
> +    }
> +
> +    load_addr = ldr->addr_to_phys(opaque, load_addr);
> +    sz = fdt_totalsize(data);
> +    rom_add_blob_fixed(name, data, sz, load_addr);
> +
> +    return 0;
> +}
> +
> +static bool fit_cfg_compatible(const void *itb, int cfg, const char *compat)
> +{
> +    const void *fdt;
> +    const char *fdt_name;
> +
> +    fdt_name = fdt_getprop(itb, cfg, "fdt", NULL);
> +    if (!fdt_name) {
> +        return false;
> +    }
> +
> +    fdt = fit_load_image(itb, fdt_name, NULL, NULL);

I guess fdt is always not compressed. Otherwise freeing uncompressed data
is required.

> +    if (!fdt) {
> +        return false;
> +    }
> +
> +    if (fdt_check_header(fdt)) {
> +        return false;
> +    }
> +
> +    if (fdt_node_check_compatible(fdt, 0, compat)) {
> +        return false;
> +    }
> +
> +    return true;
> +}
> +
> +int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque)
> +{
> +    const struct fit_loader_match *match;
> +    const void *itb, *match_data = NULL;
> +    const char *def_cfg_name;
> +    char path[128];
> +    int itb_size, configs, cfg_off, off, err;
> +    hwaddr kernel_end;
> +
> +    itb = load_device_tree(filename, &itb_size);
> +    if (!itb) {
> +        return -EINVAL;
> +    }
> +
> +    configs = fdt_path_offset(itb, "/configurations");
> +    if (configs < 0) {
> +        return configs;
> +    }
> +
> +    cfg_off = -FDT_ERR_NOTFOUND;
> +
> +    if (ldr->matches) {
> +        for (match = ldr->matches; match->compatible; match++) {
> +            off = fdt_first_subnode(itb, configs);
> +            while (off >= 0) {
> +                if (fit_cfg_compatible(itb, off, match->compatible)) {
> +                    cfg_off = off;
> +                    match_data = match->data;
> +                    break;
> +                }
> +
> +                off = fdt_next_subnode(itb, off);
> +            }
> +
> +            if (cfg_off >= 0) {
> +                break;
> +            }
> +        }
> +    }
> +
> +    if (cfg_off < 0) {
> +        def_cfg_name = fdt_getprop(itb, configs, "default", NULL);
> +        if (def_cfg_name) {
> +            snprintf(path, sizeof(path), "/configurations/%s", def_cfg_name);
> +            cfg_off = fdt_path_offset(itb, path);
> +        }
> +    }
> +
> +    if (cfg_off < 0) {
> +        /* couldn't find a configuration to use */
> +        return cfg_off;
> +    }
> +
> +    err = fit_load_kernel(ldr, itb, cfg_off, opaque, &kernel_end);
> +    if (err) {
> +        return err;
> +    }
> +
> +    err = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end);
> +    if (err) {
> +        return err;
> +    }
> +
> +    return 0;
> +}
> diff --git a/hw/core/loader.c b/hw/core/loader.c
> index 53e0e41..54a5189 100644
> --- a/hw/core/loader.c
> +++ b/hw/core/loader.c
> @@ -513,8 +513,7 @@ static void zfree(void *x, void *addr)
>   * overflow on real hardware too. */
>  #define UBOOT_MAX_GUNZIP_BYTES (64 << 20)
>  
> -static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src,
> -                      size_t srclen)
> +ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen)
>  {
>      z_stream s;
>      ssize_t dstbytes;
> diff --git a/include/hw/loader-fit.h b/include/hw/loader-fit.h
> new file mode 100644
> index 0000000..9e2a068
> --- /dev/null
> +++ b/include/hw/loader-fit.h
> @@ -0,0 +1,41 @@
> +/*
> + * Flattened Image Tree loader.
> + *
> + * Copyright (c) 2016 Imagination Technologies
> + *
> + * 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/>.
> + */
> +
> +#ifndef HW_LOADER_FIT_H
> +#define HW_LOADER_FIT_H
> +
> +#include <exec/hwaddr.h>
> +
> +struct fit_loader_match {
> +    const char *compatible;
> +    const void *data;
> +};
> +
> +struct fit_loader {
> +    const struct fit_loader_match *matches;
> +    hwaddr (*addr_to_phys)(void *opaque, uint64_t addr);
> +    const void *(*fdt_filter)(void *opaque, const void *fdt,
> +                              const void *match_data, hwaddr *load_addr);
> +    const void *(*kernel_filter)(void *opaque, const void *kernel,
> +                                 hwaddr *load_addr, hwaddr *entry_addr);
> +};
> +
> +int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque);
> +
> +#endif /* HW_LOADER_FIT_H */
> diff --git a/include/hw/loader.h b/include/hw/loader.h
> index 4879b63..ed8d6e0 100644
> --- a/include/hw/loader.h
> +++ b/include/hw/loader.h
> @@ -107,6 +107,8 @@ int load_uimage(const char *filename, hwaddr *ep,
>   */
>  int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz);
>  
> +ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen);
> +
>  ssize_t read_targphys(const char *name,
>                        int fd, hwaddr dst_addr, size_t nbytes);
>  void pstrcpy_targphys(const char *name,
> 


Regards,
Yongbok

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

* Re: [Qemu-devel] [PATCH v2 7/8] hw: xilinx-pcie: Add support for Xilinx AXI PCIe Controller
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 7/8] hw: xilinx-pcie: Add support for Xilinx AXI PCIe Controller Paul Burton
@ 2016-11-28 23:37   ` Alistair Francis
  0 siblings, 0 replies; 16+ messages in thread
From: Alistair Francis @ 2016-11-28 23:37 UTC (permalink / raw)
  To: Paul Burton; +Cc: qemu-devel@nongnu.org Developers, Aurelien Jarno, Leon Alrae

On Thu, Sep 8, 2016 at 7:51 AM, Paul Burton <paul.burton@imgtec.com> wrote:
> Add support for emulating the Xilinx AXI Root Port Bridge for PCI
> Express as described by Xilinx' PG055 document. This is a PCIe
> controller that can be used with certain series of Xilinx FPGAs, and is
> used on the MIPS Boston board which will make use of this code.
>
> Signed-off-by: Paul Burton <paul.burton@imgtec.com>

Hey Paul,

Thanks for upstreaming Xilinx related devices. I had a go at reveiwing
your patch. I don't know a huge amount about PCIe and how it is all
connnected in QEMU, so if that is how it is usually done just let me
know.

> ---
>  hw/pci-host/Makefile.objs         |   1 +
>  hw/pci-host/xilinx-pcie.c         | 310 ++++++++++++++++++++++++++++++++++++++
>  include/hw/pci-host/xilinx-pcie.h | 102 +++++++++++++
>  3 files changed, 413 insertions(+)
>  create mode 100644 hw/pci-host/xilinx-pcie.c
>  create mode 100644 include/hw/pci-host/xilinx-pcie.h
>
> diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
> index 45f1f0e..9c7909c 100644
> --- a/hw/pci-host/Makefile.objs
> +++ b/hw/pci-host/Makefile.objs
> @@ -16,3 +16,4 @@ common-obj-$(CONFIG_FULONG) += bonito.o
>  common-obj-$(CONFIG_PCI_PIIX) += piix.o
>  common-obj-$(CONFIG_PCI_Q35) += q35.o
>  common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
> +common-obj-$(CONFIG_PCI_XILINX) += xilinx-pcie.o

Where does this config option get added?

I think it should be added in this patch.

> diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c
> new file mode 100644
> index 0000000..2f3a712
> --- /dev/null
> +++ b/hw/pci-host/xilinx-pcie.c
> @@ -0,0 +1,310 @@
> +/*
> + * Xilinx PCIe host controller emulation.
> + *
> + * Copyright (c) 2016 Imagination Technologies
> + *
> + * 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 "qemu/osdep.h"
> +#include "hw/pci/pci_bridge.h"
> +#include "hw/pci-host/xilinx-pcie.h"
> +
> +enum root_cfg_reg {
> +    ROOTCFG_INTDEC              = 0x138,
> +
> +    ROOTCFG_INTMASK             = 0x13c,
> +#define ROOTCFG_INTMASK_INTX    (1 << 16)
> +#define ROOTCFG_INTMASK_MSI     (1 << 17)
> +
> +    ROOTCFG_PSCR                = 0x144,
> +#define ROOTCFG_PSCR_LINK       (1 << 11)
> +
> +    ROOTCFG_RPSCR               = 0x148,
> +#define ROOTCFG_RPSCR_BRIDGEEN  (1 << 0)
> +#define ROOTCFG_RPSCR_INTNEMPTY (1 << 18)
> +#define ROOTCFG_RPSCR_INTOVF    (1 << 19)
> +
> +    ROOTCFG_RPIFR1              = 0x158,
> +    ROOTCFG_RPIFR2              = 0x15c,
> +};

Do you not need any debug prints in here?

> +
> +static void xilinx_pcie_update_intr(XilinxPCIEHost *s,
> +                                    uint32_t set, uint32_t clear)
> +{
> +    int level;
> +
> +    s->intr |= set;
> +    s->intr &= ~clear;
> +
> +    if (s->intr_fifo_r != s->intr_fifo_w) {
> +        s->intr |= ROOTCFG_INTMASK_INTX;
> +    }
> +
> +    level = !!(s->intr & s->intr_mask);
> +    qemu_set_irq(s->irq, level);
> +}
> +
> +static void xilinx_pcie_queue_intr(XilinxPCIEHost *s,
> +                                   uint32_t fifo_reg1, uint32_t fifo_reg2)
> +{
> +    XilinxPCIEInt *intr;
> +    unsigned int new_w;
> +
> +    new_w = (s->intr_fifo_w + 1) % ARRAY_SIZE(s->intr_fifo);
> +    if (new_w == s->intr_fifo_r) {
> +        s->rpscr |= ROOTCFG_RPSCR_INTOVF;
> +        return;
> +    }
> +
> +    intr = &s->intr_fifo[s->intr_fifo_w];
> +    s->intr_fifo_w = new_w;
> +
> +    intr->fifo_reg1 = fifo_reg1;
> +    intr->fifo_reg2 = fifo_reg2;
> +
> +    xilinx_pcie_update_intr(s, ROOTCFG_INTMASK_INTX, 0);
> +}
> +
> +static void xilinx_pcie_set_irq(void *opaque, int irq_num, int level)
> +{
> +    XilinxPCIEHost *s = XILINX_PCIE_HOST(opaque);
> +
> +    if (!level) {
> +        return;
> +    }

How is the interrupt ever cleared?

> +
> +    xilinx_pcie_queue_intr(s, (irq_num << 27) | (level << 29) | (1 << 31), 0);
> +}
> +
> +static void xilinx_pcie_host_realize(DeviceState *dev, Error **errp)
> +{
> +    PCIHostState *pci = PCI_HOST_BRIDGE(dev);
> +    XilinxPCIEHost *s = XILINX_PCIE_HOST(dev);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +    PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
> +
> +    snprintf(s->name, sizeof(s->name), "pcie%u", s->bus_nr);
> +
> +    /* PCI configuration space */
> +    pcie_host_mmcfg_init(pex, s->cfg_size);
> +
> +    /* MMIO region */
> +    memory_region_init(&s->mmio, OBJECT(s), "mmio", UINT64_MAX);
> +    memory_region_set_enabled(&s->mmio, false);
> +
> +    /* dummy I/O region */
> +    memory_region_init_ram(&s->io, OBJECT(s), "io", 16, NULL);
> +    memory_region_set_enabled(&s->io, false);
> +
> +    sysbus_init_mmio(sbd, &pex->mmio);
> +    sysbus_init_mmio(sbd, &s->mmio);
> +
> +    pci->bus = pci_register_bus(dev, s->name, xilinx_pcie_set_irq,
> +                                pci_swizzle_map_irq_fn, s, &s->mmio,
> +                                &s->io, 0, 4, TYPE_PCIE_BUS);
> +
> +    qdev_set_parent_bus(DEVICE(&s->root), BUS(pci->bus));
> +    qdev_init_nofail(DEVICE(&s->root));
> +}
> +
> +static const char *xilinx_pcie_host_root_bus_path(PCIHostState *host_bridge,
> +                                                  PCIBus *rootbus)
> +{
> +    return "0000:00";
> +}
> +
> +static void xilinx_pcie_host_init(Object *obj)
> +{
> +    XilinxPCIEHost *s = XILINX_PCIE_HOST(obj);
> +    XilinxPCIERoot *root = &s->root;
> +
> +    object_initialize(root, sizeof(*root), TYPE_XILINX_PCIE_ROOT);
> +    object_property_add_child(obj, "root", OBJECT(root), NULL);
> +    qdev_prop_set_uint32(DEVICE(root), "addr", PCI_DEVFN(0, 0));
> +    qdev_prop_set_bit(DEVICE(root), "multifunction", false);
> +}
> +
> +static Property xilinx_pcie_host_props[] = {
> +    DEFINE_PROP_UINT32("bus_nr", XilinxPCIEHost, bus_nr, 0),
> +    DEFINE_PROP_SIZE("cfg_base", XilinxPCIEHost, cfg_base, 0),
> +    DEFINE_PROP_SIZE("cfg_size", XilinxPCIEHost, cfg_size, 32 << 20),
> +    DEFINE_PROP_SIZE("mmio_base", XilinxPCIEHost, mmio_base, 0),
> +    DEFINE_PROP_SIZE("mmio_size", XilinxPCIEHost, mmio_size, 1 << 20),
> +    DEFINE_PROP_PTR("irq", XilinxPCIEHost, irq_void),

This seems like a really strange way to connec the IRQ from how we
usually do it for other Xilinx related things. Is this what PCI
devices normally do? Is there any reason not to use the GPIO logic?

> +    DEFINE_PROP_BOOL("link_up", XilinxPCIEHost, link_up, true),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void xilinx_pcie_host_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
> +
> +    hc->root_bus_path = xilinx_pcie_host_root_bus_path;
> +    dc->realize = xilinx_pcie_host_realize;
> +    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> +    dc->fw_name = "pci";
> +    dc->props = xilinx_pcie_host_props;
> +}
> +
> +static const TypeInfo xilinx_pcie_host_info = {
> +    .name       = TYPE_XILINX_PCIE_HOST,
> +    .parent     = TYPE_PCIE_HOST_BRIDGE,
> +    .instance_size = sizeof(XilinxPCIEHost),
> +    .instance_init = xilinx_pcie_host_init,
> +    .class_init = xilinx_pcie_host_class_init,
> +};
> +
> +static uint32_t xilinx_pcie_root_config_read(PCIDevice *d,
> +                                             uint32_t address, int len)
> +{
> +    XilinxPCIEHost *s = XILINX_PCIE_HOST(OBJECT(d)->parent);
> +    uint32_t val;
> +
> +    switch (address) {
> +    case ROOTCFG_INTDEC:
> +        return s->intr;
> +
> +    case ROOTCFG_INTMASK:
> +        return s->intr_mask;
> +
> +    case ROOTCFG_PSCR:
> +        val = s->link_up ? ROOTCFG_PSCR_LINK : 0;
> +        return val;
> +
> +    case ROOTCFG_RPSCR:
> +        if (s->intr_fifo_r != s->intr_fifo_w) {
> +            s->rpscr &= ~ROOTCFG_RPSCR_INTNEMPTY;
> +        } else {
> +            s->rpscr |= ROOTCFG_RPSCR_INTNEMPTY;
> +        }
> +        return s->rpscr;
> +
> +    case ROOTCFG_RPIFR1:
> +        if (s->intr_fifo_w == s->intr_fifo_r) {
> +            /* FIFO empty */
> +            return 0;
> +        }
> +        return s->intr_fifo[s->intr_fifo_r].fifo_reg1;
> +
> +    case ROOTCFG_RPIFR2:
> +        if (s->intr_fifo_w == s->intr_fifo_r) {
> +            /* FIFO empty */
> +            return 0;
> +        }
> +        return s->intr_fifo[s->intr_fifo_r].fifo_reg2;
> +
> +    default:
> +        return pci_default_read_config(d, address, len);
> +    }
> +}
> +
> +static void xilinx_pcie_root_config_write(PCIDevice *d, uint32_t address,
> +                                          uint32_t val, int len)
> +{
> +    XilinxPCIEHost *s = XILINX_PCIE_HOST(OBJECT(d)->parent);
> +
> +    switch (address) {
> +    case ROOTCFG_INTDEC:
> +        xilinx_pcie_update_intr(s, 0, val);
> +        break;
> +
> +    case ROOTCFG_INTMASK:
> +        s->intr_mask = val;
> +        xilinx_pcie_update_intr(s, 0, 0);
> +        break;
> +
> +    case ROOTCFG_RPSCR:
> +        s->rpscr &= ~ROOTCFG_RPSCR_BRIDGEEN;
> +        s->rpscr |= val & ROOTCFG_RPSCR_BRIDGEEN;
> +        memory_region_set_enabled(&s->mmio, val & ROOTCFG_RPSCR_BRIDGEEN);
> +
> +        if (val & ROOTCFG_INTMASK_INTX) {
> +            s->rpscr &= ~ROOTCFG_INTMASK_INTX;
> +        }
> +        break;
> +
> +    case ROOTCFG_RPIFR1:
> +    case ROOTCFG_RPIFR2:
> +        if (s->intr_fifo_w == s->intr_fifo_r) {
> +            /* FIFO empty */
> +            return;
> +        }
> +        s->intr_fifo_r = (s->intr_fifo_r + 1) % ARRAY_SIZE(s->intr_fifo);
> +        break;
> +
> +    default:
> +        pci_default_write_config(d, address, val, len);
> +        break;
> +    }
> +}
> +
> +static int xilinx_pcie_root_init(PCIDevice *dev)
> +{
> +    BusState *bus = qdev_get_parent_bus(DEVICE(dev));
> +    XilinxPCIEHost *s = XILINX_PCIE_HOST(bus->parent);
> +
> +    dev->config[PCI_COMMAND] = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
> +    stw_le_p(&dev->config[PCI_MEMORY_BASE], s->mmio_base >> 16);
> +    stw_le_p(&dev->config[PCI_MEMORY_LIMIT],
> +             ((s->mmio_base + s->mmio_size - 1) >> 16) & 0xfff0);
> +
> +    pci_bridge_initfn(dev, TYPE_PCI_BUS);
> +
> +    if (pcie_endpoint_cap_v1_init(dev, 0x80) < 0) {
> +        hw_error("Failed to initialize PCIe capability");
> +    }
> +
> +    return 0;
> +}
> +
> +static void xilinx_pcie_root_class_init(ObjectClass *klass, void *data)
> +{
> +    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> +    dc->desc = "Xilinx AXI-PCIe Host Bridge";
> +    k->vendor_id = 0x10ee;
> +    k->device_id = 0x7021;
> +    k->revision = 0;
> +    k->class_id = PCI_CLASS_BRIDGE_HOST;
> +    k->is_express = true;
> +    k->is_bridge = true;
> +    k->init = xilinx_pcie_root_init;
> +    k->exit = pci_bridge_exitfn;
> +    dc->reset = pci_bridge_reset;
> +    k->config_read = xilinx_pcie_root_config_read;
> +    k->config_write = xilinx_pcie_root_config_write;
> +    /*
> +     * PCI-facing part of the host bridge, not usable without the
> +     * host-facing part, which can't be device_add'ed, yet.
> +     */
> +    dc->cannot_instantiate_with_device_add_yet = true;
> +}
> +
> +static const TypeInfo xilinx_pcie_root_info = {
> +    .name = TYPE_XILINX_PCIE_ROOT,
> +    .parent = TYPE_PCI_BRIDGE,
> +    .instance_size = sizeof(XilinxPCIERoot),
> +    .class_init = xilinx_pcie_root_class_init,
> +};
> +
> +static void xilinx_pcie_register(void)
> +{
> +    type_register_static(&xilinx_pcie_root_info);
> +    type_register_static(&xilinx_pcie_host_info);
> +}

Newline.

> +type_init(xilinx_pcie_register)
> diff --git a/include/hw/pci-host/xilinx-pcie.h b/include/hw/pci-host/xilinx-pcie.h
> new file mode 100644
> index 0000000..443c1a6
> --- /dev/null
> +++ b/include/hw/pci-host/xilinx-pcie.h
> @@ -0,0 +1,102 @@
> +/*
> + * Xilinx PCIe host controller emulation.
> + *
> + * Copyright (c) 2016 Imagination Technologies
> + *
> + * 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/>.
> + */
> +
> +#ifndef HW_XILINX_PCIE_H
> +#define HW_XILINX_PCIE_H
> +
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +#include "hw/pci/pci.h"
> +#include "hw/pci/pci_bus.h"
> +#include "hw/pci/pcie_host.h"
> +
> +#define TYPE_XILINX_PCIE_HOST "xilinx-pcie-host"
> +#define XILINX_PCIE_HOST(obj) \
> +     OBJECT_CHECK(XilinxPCIEHost, (obj), TYPE_XILINX_PCIE_HOST)
> +
> +#define TYPE_XILINX_PCIE_ROOT "xilinx-pcie-root"
> +#define XILINX_PCIE_ROOT(obj) \
> +     OBJECT_CHECK(XilinxPCIERoot, (obj), TYPE_XILINX_PCIE_ROOT)
> +
> +typedef struct XilinxPCIERoot {
> +    PCIBridge parent_obj;
> +} XilinxPCIERoot;
> +
> +typedef struct XilinxPCIEInt {
> +    uint32_t fifo_reg1;
> +    uint32_t fifo_reg2;
> +} XilinxPCIEInt;
> +
> +typedef struct XilinxPCIEHost {
> +    PCIExpressHost parent_obj;
> +
> +    char name[16];
> +
> +    uint32_t bus_nr;
> +    uint64_t cfg_base, cfg_size;
> +    uint64_t mmio_base, mmio_size;
> +    bool link_up;
> +
> +    union {
> +        qemu_irq irq;
> +        void *irq_void;
> +    };
> +
> +    MemoryRegion mmio, io;
> +
> +    XilinxPCIERoot root;
> +
> +    uint32_t intr;
> +    uint32_t intr_mask;
> +    XilinxPCIEInt intr_fifo[16];
> +    unsigned int intr_fifo_r, intr_fifo_w;
> +    uint32_t rpscr;
> +} XilinxPCIEHost;
> +
> +static inline XilinxPCIEHost *
> +xilinx_pcie_init(MemoryRegion *sys_mem, uint32_t bus_nr,
> +                 hwaddr cfg_base, uint64_t cfg_size,
> +                 hwaddr mmio_base, uint64_t mmio_size,
> +                 qemu_irq irq, bool link_up)
> +{
> +    DeviceState *dev;
> +    MemoryRegion *cfg, *mmio;
> +
> +    dev = qdev_create(NULL, TYPE_XILINX_PCIE_HOST);
> +
> +    qdev_prop_set_uint32(dev, "bus_nr", bus_nr);
> +    qdev_prop_set_uint64(dev, "cfg_base", cfg_base);
> +    qdev_prop_set_uint64(dev, "cfg_size", cfg_size);
> +    qdev_prop_set_uint64(dev, "mmio_base", mmio_base);
> +    qdev_prop_set_uint64(dev, "mmio_size", mmio_size);
> +    qdev_prop_set_ptr(dev, "irq", irq);
> +    qdev_prop_set_bit(dev, "link_up", link_up);
> +
> +    qdev_init_nofail(dev);
> +
> +    cfg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
> +    memory_region_add_subregion_overlap(sys_mem, cfg_base, cfg, 0);
> +
> +    mmio = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
> +    memory_region_add_subregion_overlap(sys_mem, 0, mmio, 0);
> +
> +    return XILINX_PCIE_HOST(dev);
> +}

I don't like this function being used to create the device. I think it
should all be done in the machine.

I feel like it breaks the whole QOMification effort by creating
specific functions to init devices.

In saying that I don't know enough about what other PCI devices do for
this, so if this is usual then that's fine.

Thanks,

Alistair

> +
> +#endif /* HW_XILINX_PCIE_H */
> --
> 2.9.3
>
>

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

* Re: [Qemu-devel] [PATCH v2 8/8] hw/mips: MIPS Boston board support
  2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 8/8] hw/mips: MIPS Boston board support Paul Burton
@ 2017-01-24  0:17   ` Yongbok Kim
  0 siblings, 0 replies; 16+ messages in thread
From: Yongbok Kim @ 2017-01-24  0:17 UTC (permalink / raw)
  To: Paul Burton, qemu-devel, Aurelien Jarno, Leon Alrae



On 08/09/2016 15:51, Paul Burton wrote:
> Introduce support for emulating the MIPS Boston development board. The
> Boston board is built around an FPGA & 3 PCIe controllers, one of which
> is connected to an Intel EG20T Platform Controller Hub. It is used
> during the development & debug of new CPUs and the software intended to
> run on them, and is essentially the successor to the older MIPS Malta
> board.
> 
> This patch does not implement the EG20T, instead connecting an already
> supported ICH-9 AHCI controller. Whilst this isn't accurate it's enough
> for typical stock Boston software (eg. Linux kernels) to work with hard
> disks given that both the ICH-9 & EG20T implement the AHCI
> specification.
> 
> Boston boards typically boot kernels in the FIT image format, and this
> patch will treat kernels provided to QEMU as such. When loading a kernel
> directly, the board code will generate minimal firmware much as the
> Malta board code does. This firmware will set up the CM, CPC & GIC
> register base addresses then set argument registers & jump to the kernel
> entry point. Alternatively, bootloader code may be loaded using the bios
> argument in which case no firmware will be generated & execution will
> proceed from the start of the boot code at the default MIPS boot
> exception vector (offset 0x1fc00000 into (c)kseg1).
> 
> Currently real Boston boards are always used with FPGA bitfiles that
> include a Global Interrupt Controller (GIC), so the interrupt
> configuration is only defined for such cases. Therefore the board will
> only allow use of CPUs which implement the CPS components, including the
> GIC, and will otherwise exit with a message.
> 
> Signed-off-by: Paul Burton <paul.burton@imgtec.com>
> ---
> Changes in v2:
> - Require libfdt for mips*-softmmu builds, so that the FIT loader can be used.
> ---
>  configure                               |   2 +-
>  default-configs/mips-softmmu-common.mak |   2 +
>  hw/mips/Makefile.objs                   |   1 +
>  hw/mips/boston.c                        | 526 ++++++++++++++++++++++++++++++++
>  4 files changed, 530 insertions(+), 1 deletion(-)
>  create mode 100644 hw/mips/boston.c
> 
> diff --git a/configure b/configure
> index 1c4e117..242a113 100755
> --- a/configure
> +++ b/configure
> @@ -3308,7 +3308,7 @@ fi
>  fdt_required=no
>  for target in $target_list; do
>    case $target in
> -    aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu)
> +    aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips*-softmmu)

As I mentioned earlier email, bigendian target doesn't work correctly. it
has be little endian only for now.

>        fdt_required=yes
>      ;;
>    esac
> diff --git a/default-configs/mips-softmmu-common.mak b/default-configs/mips-softmmu-common.mak
> index 0394514..09b7334 100644
> --- a/default-configs/mips-softmmu-common.mak
> +++ b/default-configs/mips-softmmu-common.mak
> @@ -32,3 +32,5 @@ CONFIG_ISA_TESTDEV=y
>  CONFIG_EMPTY_SLOT=y
>  CONFIG_MIPS_CPS=y
>  CONFIG_MIPS_ITU=y
> +CONFIG_MIPS_BOSTON=y
> +CONFIG_PCI_XILINX=y
> diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs
> index 9352a1c..48cd2ef 100644
> --- a/hw/mips/Makefile.objs
> +++ b/hw/mips/Makefile.objs
> @@ -4,3 +4,4 @@ obj-$(CONFIG_JAZZ) += mips_jazz.o
>  obj-$(CONFIG_FULONG) += mips_fulong2e.o
>  obj-y += gt64xxx_pci.o
>  obj-$(CONFIG_MIPS_CPS) += cps.o
> +obj-$(CONFIG_MIPS_BOSTON) += boston.o
> diff --git a/hw/mips/boston.c b/hw/mips/boston.c
> new file mode 100644
> index 0000000..4e02907
> --- /dev/null
> +++ b/hw/mips/boston.c
> @@ -0,0 +1,526 @@
> +/*
> + * MIPS Boston development board emulation.
> + *
> + * Copyright (c) 2016 Imagination Technologies
> + *
> + * 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 "qemu/osdep.h"
> +#include "qemu-common.h"
> +
> +#include "exec/address-spaces.h"
> +#include "hw/boards.h"
> +#include "hw/char/serial.h"
> +#include "hw/hw.h"
> +#include "hw/ide/pci.h"
> +#include "hw/ide/ahci.h"
> +#include "hw/loader.h"
> +#include "hw/loader-fit.h"
> +#include "hw/mips/cps.h"
> +#include "hw/mips/cpudevs.h"
> +#include "hw/pci-host/xilinx-pcie.h"
> +#include "qapi/error.h"
> +#include "qemu/cutils.h"
> +#include "qemu/error-report.h"
> +#include "qemu/log.h"
> +#include "sysemu/char.h"
> +#include "sysemu/device_tree.h"
> +#include "sysemu/sysemu.h"
> +
> +#include <libfdt.h>
> +
> +#define TYPE_MIPS_BOSTON "mips-boston"
> +#define BOSTON(obj) OBJECT_CHECK(BostonState, (obj), TYPE_MIPS_BOSTON)
> +
> +typedef struct {
> +    SysBusDevice parent_obj;
> +
> +    MachineState *mach;
> +    MIPSCPSState *cps;
> +    SerialState *uart;
> +
> +    CharDriverState *lcd_display;
> +    char lcd_content[8];
> +
> +    hwaddr kernel_entry;
> +    hwaddr fdt_base;
> +} BostonState;
> +
> +enum boston_plat_reg {
> +    PLAT_FPGA_BUILD     = 0x00,
> +    PLAT_CORE_CL        = 0x04,
> +    PLAT_WRAPPER_CL     = 0x08,
> +    PLAT_SYSCLK_STATUS  = 0x0c,
> +    PLAT_SOFTRST_CTL    = 0x10,
> +#define PLAT_SOFTRST_CTL_SYSRESET       (1 << 4)
> +    PLAT_DDR3_STATUS    = 0x14,
> +#define PLAT_DDR3_STATUS_LOCKED         (1 << 0)
> +#define PLAT_DDR3_STATUS_CALIBRATED     (1 << 2)
> +    PLAT_PCIE_STATUS    = 0x18,
> +#define PLAT_PCIE_STATUS_PCIE0_LOCKED   (1 << 0)
> +#define PLAT_PCIE_STATUS_PCIE1_LOCKED   (1 << 8)
> +#define PLAT_PCIE_STATUS_PCIE2_LOCKED   (1 << 16)
> +    PLAT_FLASH_CTL      = 0x1c,
> +    PLAT_SPARE0         = 0x20,
> +    PLAT_SPARE1         = 0x24,
> +    PLAT_SPARE2         = 0x28,
> +    PLAT_SPARE3         = 0x2c,
> +    PLAT_MMCM_DIV       = 0x30,
> +#define PLAT_MMCM_DIV_CLK0DIV_SHIFT     0
> +#define PLAT_MMCM_DIV_INPUT_SHIFT       8
> +#define PLAT_MMCM_DIV_MUL_SHIFT         16
> +#define PLAT_MMCM_DIV_CLK1DIV_SHIFT     24
> +    PLAT_BUILD_CFG      = 0x34,
> +#define PLAT_BUILD_CFG_IOCU_EN          (1 << 0)
> +#define PLAT_BUILD_CFG_PCIE0_EN         (1 << 1)
> +#define PLAT_BUILD_CFG_PCIE1_EN         (1 << 2)
> +#define PLAT_BUILD_CFG_PCIE2_EN         (1 << 3)
> +    PLAT_DDR_CFG        = 0x38,
> +#define PLAT_DDR_CFG_SIZE               (0xf << 0)
> +#define PLAT_DDR_CFG_MHZ                (0xfff << 4)
> +    PLAT_NOC_PCIE0_ADDR = 0x3c,
> +    PLAT_NOC_PCIE1_ADDR = 0x40,
> +    PLAT_NOC_PCIE2_ADDR = 0x44,
> +    PLAT_SYS_CTL        = 0x48,
> +};
> +
> +static void boston_lcd_init(CharDriverState *chr)
> +{
> +    qemu_chr_fe_printf(chr, "        ");
> +}
> +
> +static uint64_t boston_lcd_read(void *opaque, hwaddr addr,
> +                                unsigned size)
> +{
> +    BostonState *s = opaque;
> +    uint64_t val = 0;
> +
> +    switch (size) {
> +    case 8:
> +        val |= (uint64_t)s->lcd_content[(addr + 7) & 0x7] << 56;
> +        val |= (uint64_t)s->lcd_content[(addr + 6) & 0x7] << 48;
> +        val |= (uint64_t)s->lcd_content[(addr + 5) & 0x7] << 40;
> +        val |= (uint64_t)s->lcd_content[(addr + 4) & 0x7] << 32;

/* fall through */ for each cases

> +    case 4:
> +        val |= (uint64_t)s->lcd_content[(addr + 3) & 0x7] << 24;
> +        val |= (uint64_t)s->lcd_content[(addr + 2) & 0x7] << 16;
> +    case 2:
> +        val |= (uint64_t)s->lcd_content[(addr + 1) & 0x7] << 8;
> +    case 1:
> +        val |= (uint64_t)s->lcd_content[(addr + 0) & 0x7];
> +        break;
> +    }
> +
> +    return val;
> +}
> +
> +static void boston_lcd_write(void *opaque, hwaddr addr,
> +                             uint64_t val, unsigned size)
> +{
> +    BostonState *s = opaque;
> +
> +    switch (size) {
> +    case 8:
> +        s->lcd_content[(addr + 7) & 0x7] = val >> 56;
> +        s->lcd_content[(addr + 6) & 0x7] = val >> 48;
> +        s->lcd_content[(addr + 5) & 0x7] = val >> 40;
> +        s->lcd_content[(addr + 4) & 0x7] = val >> 32;
> +    case 4:
> +        s->lcd_content[(addr + 3) & 0x7] = val >> 24;
> +        s->lcd_content[(addr + 2) & 0x7] = val >> 16;
> +    case 2:
> +        s->lcd_content[(addr + 1) & 0x7] = val >> 8;
> +    case 1:
> +        s->lcd_content[(addr + 0) & 0x7] = val;
> +        break;
> +    }
> +
> +    qemu_chr_fe_printf(s->lcd_display,
> +                       "\r%-8.8s", s->lcd_content);
> +}
> +
> +static const MemoryRegionOps boston_lcd_ops = {
> +    .read = boston_lcd_read,
> +    .write = boston_lcd_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static uint64_t boston_platreg_read(void *opaque, hwaddr addr,
> +                                    unsigned size)
> +{
> +    BostonState *s = opaque;
> +    uint32_t gic_freq, val;
> +
> +    if (size != 4) {
> +        qemu_log_mask(LOG_UNIMP, "%uB platform register read", size);
> +        return 0;
> +    }
> +
> +    switch (addr & 0xffff) {
> +    case PLAT_FPGA_BUILD:
> +    case PLAT_CORE_CL:
> +    case PLAT_WRAPPER_CL:
> +        return 0;
> +    case PLAT_DDR3_STATUS:
> +        return PLAT_DDR3_STATUS_LOCKED | PLAT_DDR3_STATUS_CALIBRATED;
> +    case PLAT_MMCM_DIV:
> +        gic_freq = mips_gictimer_get_freq(s->cps->gic.gic_timer) / 1000000;

magic number

> +        val = gic_freq << PLAT_MMCM_DIV_INPUT_SHIFT;
> +        val |= 1 << PLAT_MMCM_DIV_MUL_SHIFT;
> +        val |= 1 << PLAT_MMCM_DIV_CLK0DIV_SHIFT;
> +        val |= 1 << PLAT_MMCM_DIV_CLK1DIV_SHIFT;
> +        return val;
> +    case PLAT_BUILD_CFG:
> +        val = PLAT_BUILD_CFG_PCIE0_EN;
> +        val |= PLAT_BUILD_CFG_PCIE1_EN;
> +        val |= PLAT_BUILD_CFG_PCIE2_EN;
> +        return val;
> +    case PLAT_DDR_CFG:
> +        val = s->mach->ram_size / G_BYTE;
> +        assert(!(val & ~PLAT_DDR_CFG_SIZE));
> +        val |= PLAT_DDR_CFG_MHZ;
> +        return val;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "Read platform register 0x%" HWADDR_PRIx,
> +                      addr & 0xffff);
> +        return 0;
> +    }
> +}
> +
> +static void boston_platreg_write(void *opaque, hwaddr addr,
> +                                 uint64_t val, unsigned size)
> +{
> +    if (size != 4) {
> +        qemu_log_mask(LOG_UNIMP, "%uB platform register write", size);
> +        return;
> +    }
> +
> +    switch (addr & 0xffff) {
> +    case PLAT_FPGA_BUILD:
> +    case PLAT_CORE_CL:
> +    case PLAT_WRAPPER_CL:
> +    case PLAT_DDR3_STATUS:
> +    case PLAT_PCIE_STATUS:
> +    case PLAT_MMCM_DIV:
> +    case PLAT_BUILD_CFG:
> +    case PLAT_DDR_CFG:
> +        /* read only */
> +        break;
> +    case PLAT_SOFTRST_CTL:
> +        if (val & PLAT_SOFTRST_CTL_SYSRESET) {
> +            qemu_system_reset_request();
> +        }
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "Write platform register 0x%" HWADDR_PRIx
> +                      " = 0x%" PRIx64, addr & 0xffff, val);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps boston_platreg_ops = {
> +    .read = boston_platreg_read,
> +    .write = boston_platreg_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static void boston_flash_write(void *opaque, hwaddr addr,
> +                               uint64_t val, unsigned size)
> +{
> +}
> +
> +static const MemoryRegionOps boston_flash_ops = {
> +    .write = boston_flash_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static const TypeInfo boston_device = {
> +    .name          = TYPE_MIPS_BOSTON,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(BostonState),
> +};
> +
> +static void boston_register_types(void)
> +{
> +    type_register_static(&boston_device);
> +}
> +type_init(boston_register_types)
> +
> +static void gen_firmware(uint32_t *p, hwaddr kernel_entry, hwaddr fdt_addr,
> +                         bool is_64b)
> +{
> +    const uint32_t cm_base = 0x16100000;
> +    const uint32_t gic_base = 0x16120000;
> +    const uint32_t cpc_base = 0x16200000;
> +
> +    /* Move CM GCRs */
> +    if (is_64b) {
> +        stl_p(p++, 0x40287803);                 /* dmfc0 $8, CMGCRBase */
> +        stl_p(p++, 0x00084138);                 /* dsll $8, $8, 4 */
> +    } else {
> +        stl_p(p++, 0x40087803);                 /* mfc0 $8, CMGCRBase */
> +        stl_p(p++, 0x00084100);                 /* sll  $8, $8, 4 */
> +    }
> +    stl_p(p++, 0x3c09a000);                     /* lui  $9, 0xa000 */
> +    stl_p(p++, 0x01094025);                     /* or   $8, $9 */
> +    stl_p(p++, 0x3c0a0000 | (cm_base >> 16));   /* lui  $10, cm_base >> 16 */
> +    if (is_64b) {
> +        stl_p(p++, 0xfd0a0008);                 /* sd   $10, 0x8($8) */
> +    } else {
> +        stl_p(p++, 0xad0a0008);                 /* sw   $10, 0x8($8) */
> +    }
> +    stl_p(p++, 0x012a4025);                     /* or   $8, $10 */
> +
> +    /* Move & enable GIC GCRs */
> +    stl_p(p++, 0x3c090000 | (gic_base >> 16));  /* lui  $9, gic_base >> 16 */
> +    stl_p(p++, 0x35290001);                     /* ori  $9, 0x1 */
> +    if (is_64b) {
> +        stl_p(p++, 0xfd090080);                 /* sd   $9, 0x80($8) */
> +    } else {
> +        stl_p(p++, 0xad090080);                 /* sw   $9, 0x80($8) */
> +    }
> +
> +    /* Move & enable CPC GCRs */
> +    stl_p(p++, 0x3c090000 | (cpc_base >> 16));  /* lui  $9, cpc_base >> 16 */
> +    stl_p(p++, 0x35290001);                     /* ori  $9, 0x1 */
> +    if (is_64b) {
> +        stl_p(p++, 0xfd090088);                 /* sd   $9, 0x88($8) */
> +    } else {
> +        stl_p(p++, 0xad090088);                 /* sw   $9, 0x88($8) */
> +    }
> +
> +    /*
> +     * Setup argument registers to follow the UHI boot protocol:
> +     *
> +     * a0/$4 = -2
> +     * a1/$5 = virtual address of FDT
> +     * a2/$6 = 0
> +     * a3/$7 = 0
> +     */
> +    stl_p(p++, 0x2404fffe);                     /* li   $4, -2 */
> +                                                /* lui  $5, hi(fdt_addr) */
> +    stl_p(p++, 0x3c050000 | ((fdt_addr >> 16) & 0xffff));
> +    if (fdt_addr & 0xffff) {                    /* ori  $5, lo(fdt_addr) */
> +        stl_p(p++, 0x34a50000 | (fdt_addr & 0xffff));
> +    }
> +    stl_p(p++, 0x34060000);                     /* li   $6, 0 */
> +    stl_p(p++, 0x34070000);                     /* li   $7, 0 */
> +
> +    /* Load kernel entry address & jump to it */
> +                                                /* lui  $25, hi(kernel_entry) */
> +    stl_p(p++, 0x3c190000 | ((kernel_entry >> 16) & 0xffff));
> +                                                /* ori  $25, lo(kernel_entry) */
> +    stl_p(p++, 0x37390000 | (kernel_entry & 0xffff));
> +    stl_p(p++, 0x03200009);                     /* jr   $25 */
> +}
> +
> +static const void *boston_fdt_filter(void *opaque, const void *fdt_orig,
> +                                     const void *match_data, hwaddr *load_addr)
> +{
> +    BostonState *s = BOSTON(opaque);
> +    MachineState *machine = s->mach;
> +    const char *cmdline;
> +    int err;
> +    void *fdt;
> +    size_t fdt_sz, ram_low_sz, ram_high_sz;
> +
> +    fdt_sz = fdt_totalsize(fdt_orig) * 2;
> +    fdt = g_malloc0(fdt_sz);
> +
> +    err = fdt_open_into(fdt_orig, fdt, fdt_sz);
> +    if (err) {
> +        fprintf(stderr, "unable to open FDT\n");
> +        return NULL;
> +    }
> +
> +    cmdline = (machine->kernel_cmdline && machine->kernel_cmdline[0])
> +            ? machine->kernel_cmdline : " ";
> +    err = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
> +    if (err < 0) {
> +        fprintf(stderr, "couldn't set /chosen/bootargs\n");
> +        return NULL;
> +    }
> +
> +    ram_low_sz = MIN(256 * M_BYTE, machine->ram_size);
> +    ram_high_sz = machine->ram_size - ram_low_sz;
> +    qemu_fdt_setprop_sized_cells(fdt, "/memory@0", "reg",
> +                                 1, 0x00000000, 1, ram_low_sz,
> +                                 1, 0x90000000, 1, ram_high_sz);
> +
> +    fdt = g_realloc(fdt, fdt_totalsize(fdt));
> +    qemu_fdt_dumpdtb(fdt, fdt_sz);
> +
> +    s->fdt_base = *load_addr;
> +
> +    return fdt;
> +}
> +
> +static const void *boston_kernel_filter(void *opaque, const void *kernel,
> +                                        hwaddr *load_addr, hwaddr *entry_addr)
> +{
> +    BostonState *s = BOSTON(opaque);
> +
> +    s->kernel_entry = *entry_addr;
> +
> +    return kernel;
> +}
> +
> +static const struct fit_loader_match boston_matches[] = {
> +    { "img,boston" },
> +    { NULL },
> +};
> +
> +static const struct fit_loader boston_fit_loader = {
> +    .matches = boston_matches,
> +    .addr_to_phys = cpu_mips_kseg0_to_phys,
> +    .fdt_filter = boston_fdt_filter,
> +    .kernel_filter = boston_kernel_filter,
> +};
> +
> +static void boston_mach_init(MachineState *machine)
> +{
> +    DeviceState *dev;
> +    BostonState *s;
> +    Error *err = NULL;
> +    const char *cpu_model;
> +    MemoryRegion *flash, *ddr, *ddr_low_alias, *lcd, *platreg;
> +    MemoryRegion *sys_mem = get_system_memory();
> +    XilinxPCIEHost *pcie2;
> +    PCIDevice *ahci;
> +    DriveInfo *hd[6];
> +    int fw_size, fit_err;
> +    bool is_64b;
> +
> +    if (machine->ram_size % G_BYTE) {

I've tested with differnt ram size options but only 1GB and 2GB worked
fine. Perhaps it is better to restrict it up to 2GB for now?

> +        error_report("Memory size must be a multiple of 1GB");
> +        exit(1);
> +    }
> +
> +    cpu_model = machine->cpu_model ?: "I6400";
> +
> +    dev = qdev_create(NULL, TYPE_MIPS_BOSTON);
> +    qdev_init_nofail(dev);
> +
> +    s = BOSTON(dev);
> +    s->mach = machine;
> +    s->cps = g_new0(MIPSCPSState, 1);
> +
> +    if (!cpu_supports_cps_smp(cpu_model)) {
> +        error_report("Boston requires CPUs which support CPS");
> +        exit(1);
> +    }
> +
> +    is_64b = cpu_supports_isa(cpu_model, ISA_MIPS64);
> +
> +    object_initialize(s->cps, sizeof(MIPSCPSState), TYPE_MIPS_CPS);
> +    qdev_set_parent_bus(DEVICE(s->cps), sysbus_get_default());
> +
> +    object_property_set_str(OBJECT(s->cps), cpu_model, "cpu-model", &err);
> +    object_property_set_int(OBJECT(s->cps), smp_cpus, "num-vp", &err);
> +    object_property_set_bool(OBJECT(s->cps), true, "realized", &err);
> +
> +    if (err != NULL) {
> +        error_report("%s", error_get_pretty(err));
> +        exit(1);
> +    }
> +
> +    sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1);
> +
> +    flash =  g_new(MemoryRegion, 1);
> +    memory_region_init_rom_device(flash, NULL, &boston_flash_ops, s,
> +                                  "boston.flash", 128 * M_BYTE, &err);
> +    memory_region_add_subregion_overlap(sys_mem, 0x18000000, flash, 0);
> +
> +    ddr = g_new(MemoryRegion, 1);
> +    memory_region_allocate_system_memory(ddr, NULL, "boston.ddr",
> +                                         machine->ram_size);
> +    memory_region_add_subregion_overlap(sys_mem, 0x80000000, ddr, 0);
> +
> +    ddr_low_alias = g_new(MemoryRegion, 1);
> +    memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr",
> +                             ddr, 0, MIN(machine->ram_size, (256 * M_BYTE)));
> +    memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0);
> +
> +    xilinx_pcie_init(sys_mem, 0,
> +                     0x10000000, 32 * M_BYTE,
> +                     0x40000000, 1 * G_BYTE,
> +                     get_cps_irq(s->cps, 2), false);
> +
> +    xilinx_pcie_init(sys_mem, 1,
> +                     0x12000000, 32 * M_BYTE,
> +                     0x20000000, 512 * M_BYTE,
> +                     get_cps_irq(s->cps, 1), false);
> +
> +    pcie2 = xilinx_pcie_init(sys_mem, 2,
> +                             0x14000000, 32 * M_BYTE,
> +                             0x16000000, 1 * M_BYTE,
> +                             get_cps_irq(s->cps, 0), true);
> +
> +    platreg = g_new(MemoryRegion, 1);
> +    memory_region_init_io(platreg, NULL, &boston_platreg_ops, s,
> +                          "boston-platregs", 0x1000);
> +    memory_region_add_subregion_overlap(sys_mem, 0x17ffd000, platreg, 0);
> +
> +    if (!serial_hds[0]) {
> +            serial_hds[0] = qemu_chr_new("serial0", "null", NULL);
> +    }
> +
> +    s->uart = serial_mm_init(sys_mem, 0x17ffe000, 2,
> +                             get_cps_irq(s->cps, 3), 10000000,
> +                             serial_hds[0], DEVICE_NATIVE_ENDIAN);
> +
> +    lcd = g_new(MemoryRegion, 1);
> +    memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8);
> +    memory_region_add_subregion_overlap(sys_mem, 0x17fff000, lcd, 0);
> +    s->lcd_display = qemu_chr_new("lcd", "vc:320x240", boston_lcd_init);
> +
> +    ahci = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus,
> +                                           PCI_DEVFN(0, 0),
> +                                           true, TYPE_ICH9_AHCI);
> +    g_assert(ARRAY_SIZE(hd) == ICH_AHCI(ahci)->ahci.ports);
> +    ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports);
> +    ahci_ide_create_devs(ahci, hd);
> +
> +    if (machine->firmware) {
> +        fw_size = load_image_targphys(machine->firmware,
> +                                      0x1fc00000, 4 * M_BYTE);
> +        if (fw_size == -1) {
> +            error_printf("unable to load firmware image '%s'\n",
> +                          machine->firmware);
> +            exit(1);
> +        }
> +    } else if (machine->kernel_filename) {
> +        fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s);
> +        if (fit_err) {
> +            error_printf("unable to load FIT image\n");
> +            exit(1);
> +        }
> +
> +        gen_firmware(memory_region_get_ram_ptr(flash) + 0x7c00000,
> +                     s->kernel_entry, s->fdt_base, is_64b);
> +    } else {
> +        error_printf("Please provide either a -kernel or -bios argument\n");
> +        exit(1);
> +    }
> +}
> +
> +static void boston_mach_class_init(MachineClass *mc)
> +{
> +    mc->desc = "MIPS Boston";
> +    mc->init = boston_mach_init;
> +    mc->default_ram_size = 2 * G_BYTE;
> +    mc->max_cpus = 16;
> +}
> +DEFINE_MACHINE("boston", boston_mach_class_init)
> 

Sorry for delaying but in general looking good other than the bigendian
target and the ram size issues.

Reviewed-by: Yongbok Kim <yongbok.kim@imgtec.com>

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

end of thread, other threads:[~2017-01-24  0:17 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-08 14:51 [Qemu-devel] [PATCH v2 0/8] MIPS Boston board support Paul Burton
2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 1/8] hw/mips_cmgcr: allow GCR base to be moved Paul Burton
2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 2/8] hw/mips_gictimer: provide API for retrieving frequency Paul Burton
2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 3/8] hw/mips_gic: Update pin state on mask changes Paul Burton
2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 4/8] target-mips: Provide function to test if a CPU supports an ISA Paul Burton
2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 5/8] dtc: Update requirement to v1.4.2 Paul Burton
2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 6/8] loader: Support Flattened Image Trees (FIT images) Paul Burton
2016-10-27 23:31   ` Yongbok Kim
2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 7/8] hw: xilinx-pcie: Add support for Xilinx AXI PCIe Controller Paul Burton
2016-11-28 23:37   ` Alistair Francis
2016-09-08 14:51 ` [Qemu-devel] [PATCH v2 8/8] hw/mips: MIPS Boston board support Paul Burton
2017-01-24  0:17   ` Yongbok Kim
2016-09-08 18:58 ` [Qemu-devel] [PATCH v2 0/8] " no-reply
2016-09-08 21:16   ` Paul Burton
2016-10-27 18:04 ` Paul Burton
2016-10-27 18:17   ` Peter Maydell

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.