qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines
@ 2020-08-25  0:16 Havard Skinnemoen via
  2020-08-25  0:16 ` [PATCH v8 01/14] hw/misc: Add NPCM7xx System Global Control Registers device model Havard Skinnemoen via
                   ` (15 more replies)
  0 siblings, 16 replies; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:16 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen

I also pushed this and the previous patchsets to my qemu fork on github.
The branches are named npcm7xx-v[1-8].

  https://github.com/hskinnemoen/qemu

This patch series models enough of the Nuvoton NPCM730 and NPCM750 SoCs to boot
an OpenBMC image built for quanta-gsj. This includes device models for:

  - Global Configuration Registers
  - Clock Control
  - Timers
  - Fuses
  - Memory Controller
  - Flash Controller

These modules, along with the existing Cortex A9 CPU cores and built-in
peripherals, are integrated into a NPCM730 or NPCM750 SoC, which in turn form
the foundation for the quanta-gsj and npcm750-evb machines, respectively. The
two SoCs are very similar; the only difference is that NPCM730 is missing some
peripherals that NPCM750 has, and which are not considered essential for
datacenter use (e.g. graphics controllers). For more information, see

https://www.nuvoton.com/products/cloud-computing/ibmc/

Both quanta-gsj and npcm750-evb correspond to real boards supported by OpenBMC.
At the end of the series, qemu can boot an OpenBMC image built for one of these
boards with some minor modifications.

The patches in this series were developed by Google and reviewed by Nuvoton. We
will be maintaining the machine and peripheral support together.

The data sheet for these SoCs is not generally available. Please let me know if
more comments are needed to understand the device behavior.

Changes since v7:

  - Move register enums to .c files throughout, leaving a single
    NPCM7XX_FOO_NR_REGS definition behind in the .h file. A QEMU_BUILD_BUG_ON
    should alert anyone accidentally expanding the register enum that they need
    to update the corresponding NR_REGS define, which in turn has a comment
    reminding them to update the vmstate version_id as well.
  - Skip loading the bootrom if a kernel filename is provided by the user.
  - New patch adding a board setup stub to tweak clocks before booting directly
    into the kernel.
  - Add stuff to meson files instead of Makefiles.
  - Try to disable the slowest drivers and services to speed up the flash boot
    acceptance test a bit. This is somewhat based on the following
    systemd-analyze blame report:
    https://gist.github.com/hskinnemoen/475cb0676530cd2cebaa1754cf16ca97

Changes since v6:

  - Use size_to_str to report DRAM sizes in npcm7xx_gcr.
  - Simplify the interrupt logic in npcm7xx_timer.
  - Update global bios_name instead of temporary.
  - Add npcm7xx_bootrom to MAINTAINERS and pc-bios/README.
  - Use a predefined name for the gsj boot image in the acceptance test.

Changes since v5:

  - Boot ROM included, as a git submodule and a binary blob, and loaded by
    default, so the -bios option is usually not necessary anymore.
  - Two acceptance tests added (openbmc image boot, and direct kernel boot).
  - npcm7xx_load_kernel() moved to SoC code.
  - NPCM7XX_TIMER_REF_HZ definition moved to CLK header.
  - Comments added clarifying available SPI flash chip selects.
  - Error handling adjustments:
      - Errors from CPU and GCR realization are propagated through the SoC
        since they may be triggered by user-configurable parameters.
      - Machine init uses error_fatal instead of error_abort for SoC
        realization flash init. This makes error messages more helpful.
      - Comments added to indicate whether peripherals may fail to realize.
      - Use ERRP_GUARD() instead of Error *err when possible.
  - Default CPU type is now set, and attempting to set it to anything else
    will fail.
  - Format string fixes (use HWADDR_PRIx, etc.)
  - Simplified memory size encoding and error checking in npcm7xx_gcr.
  - Encapsulate non-obvious pointer subtraction into helper functions in the
    FIU and TIMER modules.
  - Incorporate review feedback into the FIU module:
      - Add select/deselect trace events.
      - Use npcm7xx_fiu_{de,}select() consistently.
      - Use extract/deposit in more places for consistency.
      - Use -Wimplicit-fallthrough compatible fallthrough comments.
      - Use qdev_init_gpio_out_named instead of sysbus_init_irq for chip
        selects.
  - Incorporate review feedback into the TIMER module:
      - Assert that we never pause a timer that has already expired, instead of
        trying to handle it. This should be safe since QEMU_CLOCK_VIRTUAL is
        stopped while this code is running.
      - Simplify the switch blocks in the read and write handlers.

I made a change to error out if a flash drive was not specified, but reverted
it because it caused make check to fail (qom-test). When specifying a NULL
block device, the m25p flash device initializes its in-memory storage with 0xff
and doesn't attempt to write anything back. This seems correct to me.

Changes since v4:

  - OTP cleanups suggested by Philippe Mathieu-Daudé.
      - Added fuse array definitions based on public Nuvoton bootblock code.
      - Moved class structure to .c file since it's only used internally.
      - Readability improvements.
  - Split the first patch and folded parts of it into three other patches so
    that CONFIG_NPCM7XX is only enabled after the initial NPCM7xx machine
    support is added.
  - DRAM init moved to machine init code.
  - Consistently use lower-case hex literals.
  - Switched to fine-grained unimplemented devices, based on public bootblock
    source code. Added a tiny SRAM that got left out previously.
  - Simplified error handling in npcm7xx_realize() since the board code will
    abort anyway, and SoCs are not hot-pluggable.

Changes since v3:

  - License headers are now GPL v2-or-later throughout.
  - Added vmstate throughout (except the memory controller, which doesn't
    really have any state worth saving). Successfully booted a gsj image
    with two stop/savevm/quit/loadvm cycles along the way.
      - JFFS2 really doesn't like it if I let qemu keep running after savevm,
        and then jump back in time with loadvm. I assume this is expected.
  - Fixed an error API violation in npcm7xx_realize, removed pointless error
    check after object_property_set_link().
  - Switched the OTP device to use an embedded array instead of a g_malloc0'd
    one because I couldn't figure out how to set up vmstate for the latter.

Changes since v2:

  - Simplified the MAINTAINERS entry.
  - Added link to OpenPOWER jenkins for gsj BMC images.
  - Reverted the smpboot change, back to byte swapping.
  - Adapted to upstream API changes:
      - sysbus_init_child_obj -> object_initialize_child
      - object_property_set_bool -> qdev_realize / sysbus_realize
      - ssi_create_slave_no_init -> qdev_new
      - qdev_init_nofail -> qdev_realize_and_unref
      - ssi_auto_connect_slaves removed
  - Moved Boot ROM loading from soc to machine init.
  - Plumbed power-on-straps property from GCR to the machine init code so it
    can be properly initialized. Turns out npcm750 memory init doesn't work
    without this. npcm730 is fine either way, though I'm not sure why.
  - Reworked the flash init code so it looks more like aspeed (i.e. the flash
    device gets added even if there's no drive).

Changes since v1 (requested by reviewers):

  - Clarify the source of CLK reset values.
  - Made smpboot a constant byte array, eliinated byte swapping.
  - NPCM7xxState now stores an array of ARMCPUs, not pointers to ARMCPUs.
  - Clarify why EL3 is disabled.
  - Introduce NPCM7XX_NUM_IRQ constant.
  - Set the number of CPUs according to SoC variant, and disallow command line
    overrides (i.e. you can no longer override the number of CPUs with the -smp
    parameter). This is trying to follow the spirit of
    https://patchwork.kernel.org/patch/11595407/.
  - Switch register operations to DEVICE_LITTLE_ENDIAN throughout.
  - Machine documentation added (new patch).

Changes since v1 to support flash booting:

  - GCR reset value changes to get past memory initialization when booting
    from flash (patches 2 and 5):
      - INTCR2 now indicates that the DDR controller is initialized.
      - INTCR3 is initialized according to DDR memory size. A realize()
	method was implemented to achieve this.
  - Refactor the machine initialization a bit to make it easier to drop in
    machine-specific flash initialization (patch 6).
  - Extend the series with additional patches to enable booting from flash:
      - Boot ROM (through the -bios option).
      - OTP (fuse) controller.
      - Memory Controller stub (just enough to skip memory training).
      - Flash controller.
      - Board-specific flash initialization.

Thanks for reviewing,

Havard

Havard Skinnemoen (14):
  hw/misc: Add NPCM7xx System Global Control Registers device model
  hw/misc: Add NPCM7xx Clock Controller device model
  hw/timer: Add NPCM7xx Timer device model
  hw/arm: Add NPCM730 and NPCM750 SoC models
  hw/arm: Add two NPCM7xx-based machines
  roms: Add virtual Boot ROM for NPCM7xx SoCs
  hw/arm: Load -bios image as a boot ROM for npcm7xx
  hw/nvram: NPCM7xx OTP device model
  hw/mem: Stubbed out NPCM7xx Memory Controller model
  hw/ssi: NPCM7xx Flash Interface Unit device model
  hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  hw/arm/npcm7xx: add board setup stub for CPU and UART clocks
  docs/system: Add Nuvoton machine documentation
  tests/acceptance: console boot tests for quanta-gsj

 docs/system/arm/nuvoton.rst            |  90 ++++
 docs/system/target-arm.rst             |   1 +
 Makefile                               |   1 +
 default-configs/arm-softmmu.mak        |   1 +
 include/hw/arm/npcm7xx.h               | 112 +++++
 include/hw/mem/npcm7xx_mc.h            |  36 ++
 include/hw/misc/npcm7xx_clk.h          |  48 +++
 include/hw/misc/npcm7xx_gcr.h          |  43 ++
 include/hw/nvram/npcm7xx_otp.h         |  79 ++++
 include/hw/ssi/npcm7xx_fiu.h           |  73 ++++
 include/hw/timer/npcm7xx_timer.h       |  78 ++++
 hw/arm/npcm7xx.c                       | 532 +++++++++++++++++++++++
 hw/arm/npcm7xx_boards.c                | 197 +++++++++
 hw/mem/npcm7xx_mc.c                    |  84 ++++
 hw/misc/npcm7xx_clk.c                  | 266 ++++++++++++
 hw/misc/npcm7xx_gcr.c                  | 269 ++++++++++++
 hw/nvram/npcm7xx_otp.c                 | 439 +++++++++++++++++++
 hw/ssi/npcm7xx_fiu.c                   | 572 +++++++++++++++++++++++++
 hw/timer/npcm7xx_timer.c               | 509 ++++++++++++++++++++++
 .gitmodules                            |   3 +
 MAINTAINERS                            |  10 +
 hw/arm/Kconfig                         |   9 +
 hw/arm/meson.build                     |   1 +
 hw/mem/meson.build                     |   1 +
 hw/misc/meson.build                    |   4 +
 hw/misc/trace-events                   |   8 +
 hw/nvram/meson.build                   |   1 +
 hw/ssi/meson.build                     |   1 +
 hw/ssi/trace-events                    |  11 +
 hw/timer/meson.build                   |   1 +
 hw/timer/trace-events                  |   5 +
 pc-bios/README                         |   6 +
 pc-bios/npcm7xx_bootrom.bin            | Bin 0 -> 768 bytes
 roms/Makefile                          |   7 +
 roms/vbootrom                          |   1 +
 tests/acceptance/boot_linux_console.py |  83 ++++
 36 files changed, 3582 insertions(+)
 create mode 100644 docs/system/arm/nuvoton.rst
 create mode 100644 include/hw/arm/npcm7xx.h
 create mode 100644 include/hw/mem/npcm7xx_mc.h
 create mode 100644 include/hw/misc/npcm7xx_clk.h
 create mode 100644 include/hw/misc/npcm7xx_gcr.h
 create mode 100644 include/hw/nvram/npcm7xx_otp.h
 create mode 100644 include/hw/ssi/npcm7xx_fiu.h
 create mode 100644 include/hw/timer/npcm7xx_timer.h
 create mode 100644 hw/arm/npcm7xx.c
 create mode 100644 hw/arm/npcm7xx_boards.c
 create mode 100644 hw/mem/npcm7xx_mc.c
 create mode 100644 hw/misc/npcm7xx_clk.c
 create mode 100644 hw/misc/npcm7xx_gcr.c
 create mode 100644 hw/nvram/npcm7xx_otp.c
 create mode 100644 hw/ssi/npcm7xx_fiu.c
 create mode 100644 hw/timer/npcm7xx_timer.c
 create mode 100644 pc-bios/npcm7xx_bootrom.bin
 create mode 160000 roms/vbootrom

-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 01/14] hw/misc: Add NPCM7xx System Global Control Registers device model
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
@ 2020-08-25  0:16 ` Havard Skinnemoen via
  2020-08-25  0:16 ` [PATCH v8 02/14] hw/misc: Add NPCM7xx Clock Controller " Havard Skinnemoen via
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:16 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Joel Stanley, Cédric Le Goater

Implement a device model for the System Global Control Registers in the
NPCM730 and NPCM750 BMC SoCs.

This is primarily used to enable SMP boot (the boot ROM spins reading
the SCRPAD register) and DDR memory initialization; other registers are
best effort for now.

The reset values of the MDLR and PWRON registers are determined by the
SoC variant (730 vs 750) and board straps respectively.

Reviewed-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/misc/npcm7xx_gcr.h |  43 ++++++
 hw/misc/npcm7xx_gcr.c         | 269 ++++++++++++++++++++++++++++++++++
 MAINTAINERS                   |   8 +
 hw/arm/Kconfig                |   3 +
 hw/misc/meson.build           |   3 +
 hw/misc/trace-events          |   4 +
 6 files changed, 330 insertions(+)
 create mode 100644 include/hw/misc/npcm7xx_gcr.h
 create mode 100644 hw/misc/npcm7xx_gcr.c

diff --git a/include/hw/misc/npcm7xx_gcr.h b/include/hw/misc/npcm7xx_gcr.h
new file mode 100644
index 0000000000..13109d9d32
--- /dev/null
+++ b/include/hw/misc/npcm7xx_gcr.h
@@ -0,0 +1,43 @@
+/*
+ * Nuvoton NPCM7xx System Global Control Registers.
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+#ifndef NPCM7XX_GCR_H
+#define NPCM7XX_GCR_H
+
+#include "exec/memory.h"
+#include "hw/sysbus.h"
+
+/*
+ * Number of registers in our device state structure. Don't change this without
+ * incrementing the version_id in the vmstate.
+ */
+#define NPCM7XX_GCR_NR_REGS (0x148 / sizeof(uint32_t))
+
+typedef struct NPCM7xxGCRState {
+    SysBusDevice parent;
+
+    MemoryRegion iomem;
+
+    uint32_t regs[NPCM7XX_GCR_NR_REGS];
+
+    uint32_t reset_pwron;
+    uint32_t reset_mdlr;
+    uint32_t reset_intcr3;
+} NPCM7xxGCRState;
+
+#define TYPE_NPCM7XX_GCR "npcm7xx-gcr"
+#define NPCM7XX_GCR(obj) OBJECT_CHECK(NPCM7xxGCRState, (obj), TYPE_NPCM7XX_GCR)
+
+#endif /* NPCM7XX_GCR_H */
diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c
new file mode 100644
index 0000000000..745f690809
--- /dev/null
+++ b/hw/misc/npcm7xx_gcr.c
@@ -0,0 +1,269 @@
+/*
+ * Nuvoton NPCM7xx System Global Control Registers.
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/misc/npcm7xx_gcr.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qemu/cutils.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+
+#include "trace.h"
+
+#define NPCM7XX_GCR_MIN_DRAM_SIZE   (128 * MiB)
+#define NPCM7XX_GCR_MAX_DRAM_SIZE   (2 * GiB)
+
+enum NPCM7xxGCRRegisters {
+    NPCM7XX_GCR_PDID,
+    NPCM7XX_GCR_PWRON,
+    NPCM7XX_GCR_MFSEL1          = 0x0c / sizeof(uint32_t),
+    NPCM7XX_GCR_MFSEL2,
+    NPCM7XX_GCR_MISCPE,
+    NPCM7XX_GCR_SPSWC           = 0x038 / sizeof(uint32_t),
+    NPCM7XX_GCR_INTCR,
+    NPCM7XX_GCR_INTSR,
+    NPCM7XX_GCR_HIFCR           = 0x050 / sizeof(uint32_t),
+    NPCM7XX_GCR_INTCR2          = 0x060 / sizeof(uint32_t),
+    NPCM7XX_GCR_MFSEL3,
+    NPCM7XX_GCR_SRCNT,
+    NPCM7XX_GCR_RESSR,
+    NPCM7XX_GCR_RLOCKR1,
+    NPCM7XX_GCR_FLOCKR1,
+    NPCM7XX_GCR_DSCNT,
+    NPCM7XX_GCR_MDLR,
+    NPCM7XX_GCR_SCRPAD3,
+    NPCM7XX_GCR_SCRPAD2,
+    NPCM7XX_GCR_DAVCLVLR        = 0x098 / sizeof(uint32_t),
+    NPCM7XX_GCR_INTCR3,
+    NPCM7XX_GCR_VSINTR          = 0x0ac / sizeof(uint32_t),
+    NPCM7XX_GCR_MFSEL4,
+    NPCM7XX_GCR_CPBPNTR         = 0x0c4 / sizeof(uint32_t),
+    NPCM7XX_GCR_CPCTL           = 0x0d0 / sizeof(uint32_t),
+    NPCM7XX_GCR_CP2BST,
+    NPCM7XX_GCR_B2CPNT,
+    NPCM7XX_GCR_CPPCTL,
+    NPCM7XX_GCR_I2CSEGSEL,
+    NPCM7XX_GCR_I2CSEGCTL,
+    NPCM7XX_GCR_VSRCR,
+    NPCM7XX_GCR_MLOCKR,
+    NPCM7XX_GCR_SCRPAD          = 0x013c / sizeof(uint32_t),
+    NPCM7XX_GCR_USB1PHYCTL,
+    NPCM7XX_GCR_USB2PHYCTL,
+    NPCM7XX_GCR_REGS_END,
+};
+
+static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = {
+    [NPCM7XX_GCR_PDID]          = 0x04a92750,   /* Poleg A1 */
+    [NPCM7XX_GCR_MISCPE]        = 0x0000ffff,
+    [NPCM7XX_GCR_SPSWC]         = 0x00000003,
+    [NPCM7XX_GCR_INTCR]         = 0x0000035e,
+    [NPCM7XX_GCR_HIFCR]         = 0x0000004e,
+    [NPCM7XX_GCR_INTCR2]        = (1U << 19),   /* DDR initialized */
+    [NPCM7XX_GCR_RESSR]         = 0x80000000,
+    [NPCM7XX_GCR_DSCNT]         = 0x000000c0,
+    [NPCM7XX_GCR_DAVCLVLR]      = 0x5a00f3cf,
+    [NPCM7XX_GCR_SCRPAD]        = 0x00000008,
+    [NPCM7XX_GCR_USB1PHYCTL]    = 0x034730e4,
+    [NPCM7XX_GCR_USB2PHYCTL]    = 0x034730e4,
+};
+
+static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size)
+{
+    uint32_t reg = offset / sizeof(uint32_t);
+    NPCM7xxGCRState *s = opaque;
+
+    if (reg >= NPCM7XX_GCR_NR_REGS) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
+                      __func__, offset);
+        return 0;
+    }
+
+    trace_npcm7xx_gcr_read(offset, s->regs[reg]);
+
+    return s->regs[reg];
+}
+
+static void npcm7xx_gcr_write(void *opaque, hwaddr offset,
+                              uint64_t v, unsigned size)
+{
+    uint32_t reg = offset / sizeof(uint32_t);
+    NPCM7xxGCRState *s = opaque;
+    uint32_t value = v;
+
+    trace_npcm7xx_gcr_write(offset, value);
+
+    if (reg >= NPCM7XX_GCR_NR_REGS) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
+                      __func__, offset);
+        return;
+    }
+
+    switch (reg) {
+    case NPCM7XX_GCR_PDID:
+    case NPCM7XX_GCR_PWRON:
+    case NPCM7XX_GCR_INTSR:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
+                      __func__, offset);
+        return;
+
+    case NPCM7XX_GCR_RESSR:
+    case NPCM7XX_GCR_CP2BST:
+        /* Write 1 to clear */
+        value = s->regs[reg] & ~value;
+        break;
+
+    case NPCM7XX_GCR_RLOCKR1:
+    case NPCM7XX_GCR_MDLR:
+        /* Write 1 to set */
+        value |= s->regs[reg];
+        break;
+    };
+
+    s->regs[reg] = value;
+}
+
+static const struct MemoryRegionOps npcm7xx_gcr_ops = {
+    .read       = npcm7xx_gcr_read,
+    .write      = npcm7xx_gcr_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid      = {
+        .min_access_size        = 4,
+        .max_access_size        = 4,
+        .unaligned              = false,
+    },
+};
+
+static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type)
+{
+    NPCM7xxGCRState *s = NPCM7XX_GCR(obj);
+
+    QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
+
+    switch (type) {
+    case RESET_TYPE_COLD:
+        memcpy(s->regs, cold_reset_values, sizeof(s->regs));
+        s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron;
+        s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr;
+        s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3;
+        break;
+    }
+}
+
+static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp)
+{
+    ERRP_GUARD();
+    NPCM7xxGCRState *s = NPCM7XX_GCR(dev);
+    uint64_t dram_size;
+    Object *obj;
+
+    obj = object_property_get_link(OBJECT(dev), "dram-mr", errp);
+    if (!obj) {
+        error_prepend(errp, "%s: required dram-mr link not found: ", __func__);
+        return;
+    }
+    dram_size = memory_region_size(MEMORY_REGION(obj));
+    if (!is_power_of_2(dram_size) ||
+        dram_size < NPCM7XX_GCR_MIN_DRAM_SIZE ||
+        dram_size > NPCM7XX_GCR_MAX_DRAM_SIZE) {
+        g_autofree char *sz = size_to_str(dram_size);
+        g_autofree char *min_sz = size_to_str(NPCM7XX_GCR_MIN_DRAM_SIZE);
+        g_autofree char *max_sz = size_to_str(NPCM7XX_GCR_MAX_DRAM_SIZE);
+        error_setg(errp, "%s: unsupported DRAM size %s", __func__, sz);
+        error_append_hint(errp,
+                          "DRAM size must be a power of two between %s and %s,"
+                          " inclusive.\n", min_sz, max_sz);
+        return;
+    }
+
+    /* Power-on reset value */
+    s->reset_intcr3 = 0x00001002;
+
+    /*
+     * The GMMAP (Graphics Memory Map) field is used by u-boot to detect the
+     * DRAM size, and is normally initialized by the boot block as part of DRAM
+     * training. However, since we don't have a complete emulation of the
+     * memory controller and try to make it look like it has already been
+     * initialized, the boot block will skip this initialization, and we need
+     * to make sure this field is set correctly up front.
+     *
+     * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB of
+     * DRAM will be interpreted as 128 MiB.
+     *
+     * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244
+     */
+    s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8;
+}
+
+static void npcm7xx_gcr_init(Object *obj)
+{
+    NPCM7xxGCRState *s = NPCM7XX_GCR(obj);
+
+    memory_region_init_io(&s->iomem, obj, &npcm7xx_gcr_ops, s,
+                          TYPE_NPCM7XX_GCR, 4 * KiB);
+    sysbus_init_mmio(&s->parent, &s->iomem);
+}
+
+static const VMStateDescription vmstate_npcm7xx_gcr = {
+    .name = "npcm7xx-gcr",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, NPCM7xxGCRState, NPCM7XX_GCR_NR_REGS),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static Property npcm7xx_gcr_properties[] = {
+    DEFINE_PROP_UINT32("disabled-modules", NPCM7xxGCRState, reset_mdlr, 0),
+    DEFINE_PROP_UINT32("power-on-straps", NPCM7xxGCRState, reset_pwron, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    QEMU_BUILD_BUG_ON(NPCM7XX_GCR_REGS_END > NPCM7XX_GCR_NR_REGS);
+
+    dc->desc = "NPCM7xx System Global Control Registers";
+    dc->realize = npcm7xx_gcr_realize;
+    dc->vmsd = &vmstate_npcm7xx_gcr;
+    rc->phases.enter = npcm7xx_gcr_enter_reset;
+
+    device_class_set_props(dc, npcm7xx_gcr_properties);
+}
+
+static const TypeInfo npcm7xx_gcr_info = {
+    .name               = TYPE_NPCM7XX_GCR,
+    .parent             = TYPE_SYS_BUS_DEVICE,
+    .instance_size      = sizeof(NPCM7xxGCRState),
+    .instance_init      = npcm7xx_gcr_init,
+    .class_init         = npcm7xx_gcr_class_init,
+};
+
+static void npcm7xx_gcr_register_type(void)
+{
+    type_register_static(&npcm7xx_gcr_info);
+}
+type_init(npcm7xx_gcr_register_type);
diff --git a/MAINTAINERS b/MAINTAINERS
index 5a22c8be42..3f72250630 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -749,6 +749,14 @@ S: Odd Fixes
 F: hw/arm/musicpal.c
 F: docs/system/arm/musicpal.rst
 
+Nuvoton NPCM7xx
+M: Havard Skinnemoen <hskinnemoen@google.com>
+M: Tyrone Ting <kfting@nuvoton.com>
+L: qemu-arm@nongnu.org
+S: Supported
+F: hw/*/npcm7xx*
+F: include/hw/*/npcm7xx*
+
 nSeries
 M: Andrzej Zaborowski <balrogg@gmail.com>
 M: Peter Maydell <peter.maydell@linaro.org>
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index bc3a423940..4aec9c63f8 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -355,6 +355,9 @@ config XLNX_VERSAL
     select VIRTIO_MMIO
     select UNIMP
 
+config NPCM7XX
+    bool
+
 config FSL_IMX25
     bool
     select IMX
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 84fed0494d..99ffb9f9e8 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -50,6 +50,9 @@ softmmu_ss.add(when: 'CONFIG_IMX', if_true: files(
 ))
 softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-hpdmc.c', 'milkymist-pfpu.c'))
 softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
+  'npcm7xx_gcr.c',
+))
 softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files(
   'omap_clk.c',
   'omap_gpmc.c',
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 066752aa90..dbf90db8ed 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -111,6 +111,10 @@ mos6522_set_sr_int(void) "set sr_int"
 mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64
 mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x"
 
+# npcm7xx_gcr.c
+npcm7xx_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
+npcm7xx_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
+
 # stm32f4xx_syscfg
 stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interupt: GPIO: %d, Line: %d; Level: %d"
 stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d"
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 02/14] hw/misc: Add NPCM7xx Clock Controller device model
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
  2020-08-25  0:16 ` [PATCH v8 01/14] hw/misc: Add NPCM7xx System Global Control Registers device model Havard Skinnemoen via
@ 2020-08-25  0:16 ` Havard Skinnemoen via
  2020-09-04  9:32   ` Philippe Mathieu-Daudé
  2020-08-25  0:17 ` [PATCH v8 03/14] hw/timer: Add NPCM7xx Timer " Havard Skinnemoen via
                   ` (13 subsequent siblings)
  15 siblings, 1 reply; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:16 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Joel Stanley, Cédric Le Goater

Enough functionality to boot the Linux kernel has been implemented. This
includes:

  - Correct power-on reset values so the various clock rates can be
    accurately calculated.
  - Clock enables stick around when written.

In addition, a best effort attempt to implement SECCNT and CNTR25M was
made even though I don't think the kernel needs them.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/misc/npcm7xx_clk.h |  48 ++++++
 hw/misc/npcm7xx_clk.c         | 266 ++++++++++++++++++++++++++++++++++
 hw/misc/meson.build           |   1 +
 hw/misc/trace-events          |   4 +
 4 files changed, 319 insertions(+)
 create mode 100644 include/hw/misc/npcm7xx_clk.h
 create mode 100644 hw/misc/npcm7xx_clk.c

diff --git a/include/hw/misc/npcm7xx_clk.h b/include/hw/misc/npcm7xx_clk.h
new file mode 100644
index 0000000000..cdcc9e8534
--- /dev/null
+++ b/include/hw/misc/npcm7xx_clk.h
@@ -0,0 +1,48 @@
+/*
+ * Nuvoton NPCM7xx Clock Control Registers.
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+#ifndef NPCM7XX_CLK_H
+#define NPCM7XX_CLK_H
+
+#include "exec/memory.h"
+#include "hw/sysbus.h"
+
+/*
+ * The reference clock frequency for the timer modules, and the SECCNT and
+ * CNTR25M registers in this module, is always 25 MHz.
+ */
+#define NPCM7XX_TIMER_REF_HZ            (25000000)
+
+/*
+ * Number of registers in our device state structure. Don't change this without
+ * incrementing the version_id in the vmstate.
+ */
+#define NPCM7XX_CLK_NR_REGS             (0x70 / sizeof(uint32_t))
+
+typedef struct NPCM7xxCLKState {
+    SysBusDevice parent;
+
+    MemoryRegion iomem;
+
+    uint32_t regs[NPCM7XX_CLK_NR_REGS];
+
+    /* Time reference for SECCNT and CNTR25M, initialized by power on reset */
+    int64_t ref_ns;
+} NPCM7xxCLKState;
+
+#define TYPE_NPCM7XX_CLK "npcm7xx-clk"
+#define NPCM7XX_CLK(obj) OBJECT_CHECK(NPCM7xxCLKState, (obj), TYPE_NPCM7XX_CLK)
+
+#endif /* NPCM7XX_CLK_H */
diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
new file mode 100644
index 0000000000..21ab4200d1
--- /dev/null
+++ b/hw/misc/npcm7xx_clk.c
@@ -0,0 +1,266 @@
+/*
+ * Nuvoton NPCM7xx Clock Control Registers.
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/misc/npcm7xx_clk.h"
+#include "migration/vmstate.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "qemu/units.h"
+#include "trace.h"
+
+#define PLLCON_LOKI     BIT(31)
+#define PLLCON_LOKS     BIT(30)
+#define PLLCON_PWDEN    BIT(12)
+
+enum NPCM7xxCLKRegisters {
+    NPCM7XX_CLK_CLKEN1,
+    NPCM7XX_CLK_CLKSEL,
+    NPCM7XX_CLK_CLKDIV1,
+    NPCM7XX_CLK_PLLCON0,
+    NPCM7XX_CLK_PLLCON1,
+    NPCM7XX_CLK_SWRSTR,
+    NPCM7XX_CLK_IPSRST1         = 0x20 / sizeof(uint32_t),
+    NPCM7XX_CLK_IPSRST2,
+    NPCM7XX_CLK_CLKEN2,
+    NPCM7XX_CLK_CLKDIV2,
+    NPCM7XX_CLK_CLKEN3,
+    NPCM7XX_CLK_IPSRST3,
+    NPCM7XX_CLK_WD0RCR,
+    NPCM7XX_CLK_WD1RCR,
+    NPCM7XX_CLK_WD2RCR,
+    NPCM7XX_CLK_SWRSTC1,
+    NPCM7XX_CLK_SWRSTC2,
+    NPCM7XX_CLK_SWRSTC3,
+    NPCM7XX_CLK_SWRSTC4,
+    NPCM7XX_CLK_PLLCON2,
+    NPCM7XX_CLK_CLKDIV3,
+    NPCM7XX_CLK_CORSTC,
+    NPCM7XX_CLK_PLLCONG,
+    NPCM7XX_CLK_AHBCKFI,
+    NPCM7XX_CLK_SECCNT,
+    NPCM7XX_CLK_CNTR25M,
+    NPCM7XX_CLK_REGS_END,
+};
+
+/*
+ * These reset values were taken from version 0.91 of the NPCM750R data sheet.
+ *
+ * All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on
+ * core domain reset, but this reset type is not yet supported by QEMU.
+ */
+static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
+    [NPCM7XX_CLK_CLKEN1]        = 0xffffffff,
+    [NPCM7XX_CLK_CLKSEL]        = 0x004aaaaa,
+    [NPCM7XX_CLK_CLKDIV1]       = 0x5413f855,
+    [NPCM7XX_CLK_PLLCON0]       = 0x00222101 | PLLCON_LOKI,
+    [NPCM7XX_CLK_PLLCON1]       = 0x00202101 | PLLCON_LOKI,
+    [NPCM7XX_CLK_IPSRST1]       = 0x00001000,
+    [NPCM7XX_CLK_IPSRST2]       = 0x80000000,
+    [NPCM7XX_CLK_CLKEN2]        = 0xffffffff,
+    [NPCM7XX_CLK_CLKDIV2]       = 0xaa4f8f9f,
+    [NPCM7XX_CLK_CLKEN3]        = 0xffffffff,
+    [NPCM7XX_CLK_IPSRST3]       = 0x03000000,
+    [NPCM7XX_CLK_WD0RCR]        = 0xffffffff,
+    [NPCM7XX_CLK_WD1RCR]        = 0xffffffff,
+    [NPCM7XX_CLK_WD2RCR]        = 0xffffffff,
+    [NPCM7XX_CLK_SWRSTC1]       = 0x00000003,
+    [NPCM7XX_CLK_PLLCON2]       = 0x00c02105 | PLLCON_LOKI,
+    [NPCM7XX_CLK_CORSTC]        = 0x04000003,
+    [NPCM7XX_CLK_PLLCONG]       = 0x01228606 | PLLCON_LOKI,
+    [NPCM7XX_CLK_AHBCKFI]       = 0x000000c8,
+};
+
+static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size)
+{
+    uint32_t reg = offset / sizeof(uint32_t);
+    NPCM7xxCLKState *s = opaque;
+    int64_t now_ns;
+    uint32_t value = 0;
+
+    if (reg >= NPCM7XX_CLK_NR_REGS) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
+                      __func__, offset);
+        return 0;
+    }
+
+    switch (reg) {
+    case NPCM7XX_CLK_SWRSTR:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n",
+                      __func__, offset);
+        break;
+
+    case NPCM7XX_CLK_SECCNT:
+        now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        value = (now_ns - s->ref_ns) / NANOSECONDS_PER_SECOND;
+        break;
+
+    case NPCM7XX_CLK_CNTR25M:
+        now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        /*
+         * This register counts 25 MHz cycles, updating every 640 ns. It rolls
+         * over to zero every second.
+         *
+         * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000.
+         */
+        value = (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_TIMER_REF_HZ;
+        break;
+
+    default:
+        value = s->regs[reg];
+        break;
+    };
+
+    trace_npcm7xx_clk_read(offset, value);
+
+    return value;
+}
+
+static void npcm7xx_clk_write(void *opaque, hwaddr offset,
+                              uint64_t v, unsigned size)
+{
+    uint32_t reg = offset / sizeof(uint32_t);
+    NPCM7xxCLKState *s = opaque;
+    uint32_t value = v;
+
+    trace_npcm7xx_clk_write(offset, value);
+
+    if (reg >= NPCM7XX_CLK_NR_REGS) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
+                      __func__, offset);
+        return;
+    }
+
+    switch (reg) {
+    case NPCM7XX_CLK_SWRSTR:
+        qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n",
+                      __func__, value);
+        value = 0;
+        break;
+
+    case NPCM7XX_CLK_PLLCON0:
+    case NPCM7XX_CLK_PLLCON1:
+    case NPCM7XX_CLK_PLLCON2:
+    case NPCM7XX_CLK_PLLCONG:
+        if (value & PLLCON_PWDEN) {
+            /* Power down -- clear lock and indicate loss of lock */
+            value &= ~PLLCON_LOKI;
+            value |= PLLCON_LOKS;
+        } else {
+            /* Normal mode -- assume always locked */
+            value |= PLLCON_LOKI;
+            /* Keep LOKS unchanged unless cleared by writing 1 */
+            if (value & PLLCON_LOKS) {
+                value &= ~PLLCON_LOKS;
+            } else {
+                value |= (value & PLLCON_LOKS);
+            }
+        }
+        break;
+
+    case NPCM7XX_CLK_CNTR25M:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
+                      __func__, offset);
+        return;
+    }
+
+    s->regs[reg] = value;
+}
+
+static const struct MemoryRegionOps npcm7xx_clk_ops = {
+    .read       = npcm7xx_clk_read,
+    .write      = npcm7xx_clk_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid      = {
+        .min_access_size        = 4,
+        .max_access_size        = 4,
+        .unaligned              = false,
+    },
+};
+
+static void npcm7xx_clk_enter_reset(Object *obj, ResetType type)
+{
+    NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
+
+    QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
+
+    switch (type) {
+    case RESET_TYPE_COLD:
+        memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values));
+        s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        return;
+    }
+
+    /*
+     * A small number of registers need to be reset on a core domain reset,
+     * but no such reset type exists yet.
+     */
+    qemu_log_mask(LOG_UNIMP, "%s: reset type %d not implemented.",
+                  __func__, type);
+}
+
+static void npcm7xx_clk_init(Object *obj)
+{
+    NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
+
+    memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s,
+                          TYPE_NPCM7XX_CLK, 4 * KiB);
+    sysbus_init_mmio(&s->parent, &s->iomem);
+}
+
+static const VMStateDescription vmstate_npcm7xx_clk = {
+    .name = "npcm7xx-clk",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS),
+        VMSTATE_INT64(ref_ns, NPCM7xxCLKState),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM7XX_CLK_NR_REGS);
+
+    dc->desc = "NPCM7xx Clock Control Registers";
+    dc->vmsd = &vmstate_npcm7xx_clk;
+    rc->phases.enter = npcm7xx_clk_enter_reset;
+}
+
+static const TypeInfo npcm7xx_clk_info = {
+    .name               = TYPE_NPCM7XX_CLK,
+    .parent             = TYPE_SYS_BUS_DEVICE,
+    .instance_size      = sizeof(NPCM7xxCLKState),
+    .instance_init      = npcm7xx_clk_init,
+    .class_init         = npcm7xx_clk_class_init,
+};
+
+static void npcm7xx_clk_register_type(void)
+{
+    type_register_static(&npcm7xx_clk_info);
+}
+type_init(npcm7xx_clk_register_type);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 99ffb9f9e8..531cc31dcc 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -51,6 +51,7 @@ softmmu_ss.add(when: 'CONFIG_IMX', if_true: files(
 softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-hpdmc.c', 'milkymist-pfpu.c'))
 softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c'))
 softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
+  'npcm7xx_clk.c',
   'npcm7xx_gcr.c',
 ))
 softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files(
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index dbf90db8ed..a010699b4f 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -111,6 +111,10 @@ mos6522_set_sr_int(void) "set sr_int"
 mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64
 mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x"
 
+# npcm7xx_clk.c
+npcm7xx_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
+npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
+
 # npcm7xx_gcr.c
 npcm7xx_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
 npcm7xx_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 03/14] hw/timer: Add NPCM7xx Timer device model
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
  2020-08-25  0:16 ` [PATCH v8 01/14] hw/misc: Add NPCM7xx System Global Control Registers device model Havard Skinnemoen via
  2020-08-25  0:16 ` [PATCH v8 02/14] hw/misc: Add NPCM7xx Clock Controller " Havard Skinnemoen via
@ 2020-08-25  0:17 ` Havard Skinnemoen via
  2020-08-25  0:17 ` [PATCH v8 04/14] hw/arm: Add NPCM730 and NPCM750 SoC models Havard Skinnemoen via
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:17 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Joel Stanley

The NPCM730 and NPCM750 SoCs have three timer modules each holding five
timers and some shared registers (e.g. interrupt status).

Each timer runs at 25 MHz divided by a prescaler, and counts down from a
configurable initial value to zero. When zero is reached, the interrupt
flag for the timer is set, and the timer is disabled (one-shot mode) or
reloaded from its initial value (periodic mode).

This implementation is sufficient to boot a Linux kernel configured for
NPCM750. Note that the kernel does not seem to actually turn on the
interrupts.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/timer/npcm7xx_timer.h |  78 +++++
 hw/timer/npcm7xx_timer.c         | 509 +++++++++++++++++++++++++++++++
 hw/timer/meson.build             |   1 +
 hw/timer/trace-events            |   5 +
 4 files changed, 593 insertions(+)
 create mode 100644 include/hw/timer/npcm7xx_timer.h
 create mode 100644 hw/timer/npcm7xx_timer.c

diff --git a/include/hw/timer/npcm7xx_timer.h b/include/hw/timer/npcm7xx_timer.h
new file mode 100644
index 0000000000..878a365a79
--- /dev/null
+++ b/include/hw/timer/npcm7xx_timer.h
@@ -0,0 +1,78 @@
+/*
+ * Nuvoton NPCM7xx Timer Controller
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+#ifndef NPCM7XX_TIMER_H
+#define NPCM7XX_TIMER_H
+
+#include "exec/memory.h"
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+
+/* Each Timer Module (TIM) instance holds five 25 MHz timers. */
+#define NPCM7XX_TIMERS_PER_CTRL (5)
+
+/*
+ * Number of registers in our device state structure. Don't change this without
+ * incrementing the version_id in the vmstate.
+ */
+#define NPCM7XX_TIMER_NR_REGS (0x54 / sizeof(uint32_t))
+
+typedef struct NPCM7xxTimerCtrlState NPCM7xxTimerCtrlState;
+
+/**
+ * struct NPCM7xxTimer - Individual timer state.
+ * @irq: GIC interrupt line to fire on expiration (if enabled).
+ * @qtimer: QEMU timer that notifies us on expiration.
+ * @expires_ns: Absolute virtual expiration time.
+ * @remaining_ns: Remaining time until expiration if timer is paused.
+ * @tcsr: The Timer Control and Status Register.
+ * @ticr: The Timer Initial Count Register.
+ */
+typedef struct NPCM7xxTimer {
+    NPCM7xxTimerCtrlState *ctrl;
+
+    qemu_irq    irq;
+    QEMUTimer   qtimer;
+    int64_t     expires_ns;
+    int64_t     remaining_ns;
+
+    uint32_t    tcsr;
+    uint32_t    ticr;
+} NPCM7xxTimer;
+
+/**
+ * struct NPCM7xxTimerCtrlState - Timer Module device state.
+ * @parent: System bus device.
+ * @iomem: Memory region through which registers are accessed.
+ * @tisr: The Timer Interrupt Status Register.
+ * @wtcr: The Watchdog Timer Control Register.
+ * @timer: The five individual timers managed by this module.
+ */
+struct NPCM7xxTimerCtrlState {
+    SysBusDevice parent;
+
+    MemoryRegion iomem;
+
+    uint32_t    tisr;
+    uint32_t    wtcr;
+
+    NPCM7xxTimer timer[NPCM7XX_TIMERS_PER_CTRL];
+};
+
+#define TYPE_NPCM7XX_TIMER "npcm7xx-timer"
+#define NPCM7XX_TIMER(obj)                                              \
+    OBJECT_CHECK(NPCM7xxTimerCtrlState, (obj), TYPE_NPCM7XX_TIMER)
+
+#endif /* NPCM7XX_TIMER_H */
diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c
new file mode 100644
index 0000000000..ad5ebd9878
--- /dev/null
+++ b/hw/timer/npcm7xx_timer.c
@@ -0,0 +1,509 @@
+/*
+ * Nuvoton NPCM7xx Timer Controller
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/irq.h"
+#include "hw/misc/npcm7xx_clk.h"
+#include "hw/timer/npcm7xx_timer.h"
+#include "migration/vmstate.h"
+#include "qemu/bitops.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "qemu/units.h"
+#include "trace.h"
+
+/* 32-bit register indices. */
+enum NPCM7xxTimerRegisters {
+    NPCM7XX_TIMER_TCSR0,
+    NPCM7XX_TIMER_TCSR1,
+    NPCM7XX_TIMER_TICR0,
+    NPCM7XX_TIMER_TICR1,
+    NPCM7XX_TIMER_TDR0,
+    NPCM7XX_TIMER_TDR1,
+    NPCM7XX_TIMER_TISR,
+    NPCM7XX_TIMER_WTCR,
+    NPCM7XX_TIMER_TCSR2,
+    NPCM7XX_TIMER_TCSR3,
+    NPCM7XX_TIMER_TICR2,
+    NPCM7XX_TIMER_TICR3,
+    NPCM7XX_TIMER_TDR2,
+    NPCM7XX_TIMER_TDR3,
+    NPCM7XX_TIMER_TCSR4         = 0x0040 / sizeof(uint32_t),
+    NPCM7XX_TIMER_TICR4         = 0x0048 / sizeof(uint32_t),
+    NPCM7XX_TIMER_TDR4          = 0x0050 / sizeof(uint32_t),
+    NPCM7XX_TIMER_REGS_END,
+};
+
+/* Register field definitions. */
+#define NPCM7XX_TCSR_CEN                BIT(30)
+#define NPCM7XX_TCSR_IE                 BIT(29)
+#define NPCM7XX_TCSR_PERIODIC           BIT(27)
+#define NPCM7XX_TCSR_CRST               BIT(26)
+#define NPCM7XX_TCSR_CACT               BIT(25)
+#define NPCM7XX_TCSR_RSVD               0x21ffff00
+#define NPCM7XX_TCSR_PRESCALE_START     0
+#define NPCM7XX_TCSR_PRESCALE_LEN       8
+
+/*
+ * Returns the index of timer in the tc->timer array. This can be used to
+ * locate the registers that belong to this timer.
+ */
+static int npcm7xx_timer_index(NPCM7xxTimerCtrlState *tc, NPCM7xxTimer *timer)
+{
+    int index = timer - tc->timer;
+
+    g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL);
+
+    return index;
+}
+
+/* Return the value by which to divide the reference clock rate. */
+static uint32_t npcm7xx_timer_prescaler(const NPCM7xxTimer *t)
+{
+    return extract32(t->tcsr, NPCM7XX_TCSR_PRESCALE_START,
+                     NPCM7XX_TCSR_PRESCALE_LEN) + 1;
+}
+
+/* Convert a timer cycle count to a time interval in nanoseconds. */
+static int64_t npcm7xx_timer_count_to_ns(NPCM7xxTimer *t, uint32_t count)
+{
+    int64_t ns = count;
+
+    ns *= NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ;
+    ns *= npcm7xx_timer_prescaler(t);
+
+    return ns;
+}
+
+/* Convert a time interval in nanoseconds to a timer cycle count. */
+static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns)
+{
+    int64_t count;
+
+    count = ns / (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ);
+    count /= npcm7xx_timer_prescaler(t);
+
+    return count;
+}
+
+/*
+ * Raise the interrupt line if there's a pending interrupt and interrupts are
+ * enabled for this timer. If not, lower it.
+ */
+static void npcm7xx_timer_check_interrupt(NPCM7xxTimer *t)
+{
+    NPCM7xxTimerCtrlState *tc = t->ctrl;
+    int index = npcm7xx_timer_index(tc, t);
+    bool pending = (t->tcsr & NPCM7XX_TCSR_IE) && (tc->tisr & BIT(index));
+
+    qemu_set_irq(t->irq, pending);
+    trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, pending);
+}
+
+/* Start or resume the timer. */
+static void npcm7xx_timer_start(NPCM7xxTimer *t)
+{
+    int64_t now;
+
+    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    t->expires_ns = now + t->remaining_ns;
+    timer_mod(&t->qtimer, t->expires_ns);
+}
+
+/*
+ * Called when the counter reaches zero. Sets the interrupt flag, and either
+ * restarts or disables the timer.
+ */
+static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t)
+{
+    NPCM7xxTimerCtrlState *tc = t->ctrl;
+    int index = npcm7xx_timer_index(tc, t);
+
+    tc->tisr |= BIT(index);
+
+    if (t->tcsr & NPCM7XX_TCSR_PERIODIC) {
+        t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
+        if (t->tcsr & NPCM7XX_TCSR_CEN) {
+            npcm7xx_timer_start(t);
+        }
+    } else {
+        t->tcsr &= ~(NPCM7XX_TCSR_CEN | NPCM7XX_TCSR_CACT);
+    }
+
+    npcm7xx_timer_check_interrupt(t);
+}
+
+/* Stop counting. Record the time remaining so we can continue later. */
+static void npcm7xx_timer_pause(NPCM7xxTimer *t)
+{
+    int64_t now;
+
+    timer_del(&t->qtimer);
+    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    t->remaining_ns = t->expires_ns - now;
+    g_assert(t->remaining_ns > 0);
+}
+
+/*
+ * Restart the timer from its initial value. If the timer was enabled and stays
+ * enabled, adjust the QEMU timer according to the new count. If the timer is
+ * transitioning from disabled to enabled, the caller is expected to start the
+ * timer later.
+ */
+static void npcm7xx_timer_restart(NPCM7xxTimer *t, uint32_t old_tcsr)
+{
+    t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
+
+    if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) {
+        npcm7xx_timer_start(t);
+    }
+}
+
+/* Register read and write handlers */
+
+static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr)
+{
+    uint32_t old_tcsr = t->tcsr;
+
+    if (new_tcsr & NPCM7XX_TCSR_RSVD) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits in 0x%08x ignored\n",
+                      __func__, new_tcsr);
+        new_tcsr &= ~NPCM7XX_TCSR_RSVD;
+    }
+    if (new_tcsr & NPCM7XX_TCSR_CACT) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only bits in 0x%08x ignored\n",
+                      __func__, new_tcsr);
+        new_tcsr &= ~NPCM7XX_TCSR_CACT;
+    }
+
+    t->tcsr = (t->tcsr & NPCM7XX_TCSR_CACT) | new_tcsr;
+
+    if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_IE) {
+        npcm7xx_timer_check_interrupt(t);
+    }
+    if (new_tcsr & NPCM7XX_TCSR_CRST) {
+        npcm7xx_timer_restart(t, old_tcsr);
+        t->tcsr &= ~NPCM7XX_TCSR_CRST;
+    }
+    if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_CEN) {
+        if (new_tcsr & NPCM7XX_TCSR_CEN) {
+            npcm7xx_timer_start(t);
+        } else {
+            npcm7xx_timer_pause(t);
+        }
+    }
+}
+
+static void npcm7xx_timer_write_ticr(NPCM7xxTimer *t, uint32_t new_ticr)
+{
+    t->ticr = new_ticr;
+
+    npcm7xx_timer_restart(t, t->tcsr);
+}
+
+static uint32_t npcm7xx_timer_read_tdr(NPCM7xxTimer *t)
+{
+    if (t->tcsr & NPCM7XX_TCSR_CEN) {
+        int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+        return npcm7xx_timer_ns_to_count(t, t->expires_ns - now);
+    }
+
+    return npcm7xx_timer_ns_to_count(t, t->remaining_ns);
+}
+
+static hwaddr npcm7xx_tcsr_index(hwaddr reg)
+{
+    switch (reg) {
+    case NPCM7XX_TIMER_TCSR0:
+        return 0;
+    case NPCM7XX_TIMER_TCSR1:
+        return 1;
+    case NPCM7XX_TIMER_TCSR2:
+        return 2;
+    case NPCM7XX_TIMER_TCSR3:
+        return 3;
+    case NPCM7XX_TIMER_TCSR4:
+        return 4;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static hwaddr npcm7xx_ticr_index(hwaddr reg)
+{
+    switch (reg) {
+    case NPCM7XX_TIMER_TICR0:
+        return 0;
+    case NPCM7XX_TIMER_TICR1:
+        return 1;
+    case NPCM7XX_TIMER_TICR2:
+        return 2;
+    case NPCM7XX_TIMER_TICR3:
+        return 3;
+    case NPCM7XX_TIMER_TICR4:
+        return 4;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static hwaddr npcm7xx_tdr_index(hwaddr reg)
+{
+    switch (reg) {
+    case NPCM7XX_TIMER_TDR0:
+        return 0;
+    case NPCM7XX_TIMER_TDR1:
+        return 1;
+    case NPCM7XX_TIMER_TDR2:
+        return 2;
+    case NPCM7XX_TIMER_TDR3:
+        return 3;
+    case NPCM7XX_TIMER_TDR4:
+        return 4;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static uint64_t npcm7xx_timer_read(void *opaque, hwaddr offset, unsigned size)
+{
+    NPCM7xxTimerCtrlState *s = opaque;
+    uint64_t value = 0;
+    hwaddr reg;
+
+    reg = offset / sizeof(uint32_t);
+    switch (reg) {
+    case NPCM7XX_TIMER_TCSR0:
+    case NPCM7XX_TIMER_TCSR1:
+    case NPCM7XX_TIMER_TCSR2:
+    case NPCM7XX_TIMER_TCSR3:
+    case NPCM7XX_TIMER_TCSR4:
+        value = s->timer[npcm7xx_tcsr_index(reg)].tcsr;
+        break;
+
+    case NPCM7XX_TIMER_TICR0:
+    case NPCM7XX_TIMER_TICR1:
+    case NPCM7XX_TIMER_TICR2:
+    case NPCM7XX_TIMER_TICR3:
+    case NPCM7XX_TIMER_TICR4:
+        value = s->timer[npcm7xx_ticr_index(reg)].ticr;
+        break;
+
+    case NPCM7XX_TIMER_TDR0:
+    case NPCM7XX_TIMER_TDR1:
+    case NPCM7XX_TIMER_TDR2:
+    case NPCM7XX_TIMER_TDR3:
+    case NPCM7XX_TIMER_TDR4:
+        value = npcm7xx_timer_read_tdr(&s->timer[npcm7xx_tdr_index(reg)]);
+        break;
+
+    case NPCM7XX_TIMER_TISR:
+        value = s->tisr;
+        break;
+
+    case NPCM7XX_TIMER_WTCR:
+        value = s->wtcr;
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid offset 0x%04" HWADDR_PRIx "\n",
+                      __func__, offset);
+        break;
+    }
+
+    trace_npcm7xx_timer_read(DEVICE(s)->canonical_path, offset, value);
+
+    return value;
+}
+
+static void npcm7xx_timer_write(void *opaque, hwaddr offset,
+                                uint64_t v, unsigned size)
+{
+    uint32_t reg = offset / sizeof(uint32_t);
+    NPCM7xxTimerCtrlState *s = opaque;
+    uint32_t value = v;
+
+    trace_npcm7xx_timer_write(DEVICE(s)->canonical_path, offset, value);
+
+    switch (reg) {
+    case NPCM7XX_TIMER_TCSR0:
+    case NPCM7XX_TIMER_TCSR1:
+    case NPCM7XX_TIMER_TCSR2:
+    case NPCM7XX_TIMER_TCSR3:
+    case NPCM7XX_TIMER_TCSR4:
+        npcm7xx_timer_write_tcsr(&s->timer[npcm7xx_tcsr_index(reg)], value);
+        return;
+
+    case NPCM7XX_TIMER_TICR0:
+    case NPCM7XX_TIMER_TICR1:
+    case NPCM7XX_TIMER_TICR2:
+    case NPCM7XX_TIMER_TICR3:
+    case NPCM7XX_TIMER_TICR4:
+        npcm7xx_timer_write_ticr(&s->timer[npcm7xx_ticr_index(reg)], value);
+        return;
+
+    case NPCM7XX_TIMER_TDR0:
+    case NPCM7XX_TIMER_TDR1:
+    case NPCM7XX_TIMER_TDR2:
+    case NPCM7XX_TIMER_TDR3:
+    case NPCM7XX_TIMER_TDR4:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
+                      __func__, offset);
+        return;
+
+    case NPCM7XX_TIMER_TISR:
+        s->tisr &= ~value;
+        return;
+
+    case NPCM7XX_TIMER_WTCR:
+        qemu_log_mask(LOG_UNIMP, "%s: WTCR write not implemented: 0x%08x\n",
+                      __func__, value);
+        return;
+    }
+
+    qemu_log_mask(LOG_GUEST_ERROR,
+                  "%s: invalid offset 0x%04" HWADDR_PRIx "\n",
+                  __func__, offset);
+}
+
+static const struct MemoryRegionOps npcm7xx_timer_ops = {
+    .read       = npcm7xx_timer_read,
+    .write      = npcm7xx_timer_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid      = {
+        .min_access_size        = 4,
+        .max_access_size        = 4,
+        .unaligned              = false,
+    },
+};
+
+/* Called when the QEMU timer expires. */
+static void npcm7xx_timer_expired(void *opaque)
+{
+    NPCM7xxTimer *t = opaque;
+
+    if (t->tcsr & NPCM7XX_TCSR_CEN) {
+        npcm7xx_timer_reached_zero(t);
+    }
+}
+
+static void npcm7xx_timer_enter_reset(Object *obj, ResetType type)
+{
+    NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
+    int i;
+
+    for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
+        NPCM7xxTimer *t = &s->timer[i];
+
+        timer_del(&t->qtimer);
+        t->expires_ns = 0;
+        t->remaining_ns = 0;
+        t->tcsr = 0x00000005;
+        t->ticr = 0x00000000;
+    }
+
+    s->tisr = 0x00000000;
+    s->wtcr = 0x00000400;
+}
+
+static void npcm7xx_timer_hold_reset(Object *obj)
+{
+    NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
+    int i;
+
+    for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
+        qemu_irq_lower(s->timer[i].irq);
+    }
+}
+
+static void npcm7xx_timer_realize(DeviceState *dev, Error **errp)
+{
+    NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev);
+    SysBusDevice *sbd = &s->parent;
+    int i;
+
+    for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
+        NPCM7xxTimer *t = &s->timer[i];
+        t->ctrl = s;
+        timer_init_ns(&t->qtimer, QEMU_CLOCK_VIRTUAL, npcm7xx_timer_expired, t);
+        sysbus_init_irq(sbd, &t->irq);
+    }
+
+    memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_timer_ops, s,
+                          TYPE_NPCM7XX_TIMER, 4 * KiB);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription vmstate_npcm7xx_timer = {
+    .name = "npcm7xx-timer",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_TIMER(qtimer, NPCM7xxTimer),
+        VMSTATE_INT64(expires_ns, NPCM7xxTimer),
+        VMSTATE_INT64(remaining_ns, NPCM7xxTimer),
+        VMSTATE_UINT32(tcsr, NPCM7xxTimer),
+        VMSTATE_UINT32(ticr, NPCM7xxTimer),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static const VMStateDescription vmstate_npcm7xx_timer_ctrl = {
+    .name = "npcm7xx-timer-ctrl",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState),
+        VMSTATE_UINT32(wtcr, NPCM7xxTimerCtrlState),
+        VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState,
+                             NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_timer,
+                             NPCM7xxTimer),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static void npcm7xx_timer_class_init(ObjectClass *klass, void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    QEMU_BUILD_BUG_ON(NPCM7XX_TIMER_REGS_END > NPCM7XX_TIMER_NR_REGS);
+
+    dc->desc = "NPCM7xx Timer Controller";
+    dc->realize = npcm7xx_timer_realize;
+    dc->vmsd = &vmstate_npcm7xx_timer_ctrl;
+    rc->phases.enter = npcm7xx_timer_enter_reset;
+    rc->phases.hold = npcm7xx_timer_hold_reset;
+}
+
+static const TypeInfo npcm7xx_timer_info = {
+    .name               = TYPE_NPCM7XX_TIMER,
+    .parent             = TYPE_SYS_BUS_DEVICE,
+    .instance_size      = sizeof(NPCM7xxTimerCtrlState),
+    .class_init         = npcm7xx_timer_class_init,
+};
+
+static void npcm7xx_timer_register_type(void)
+{
+    type_register_static(&npcm7xx_timer_info);
+}
+type_init(npcm7xx_timer_register_type);
diff --git a/hw/timer/meson.build b/hw/timer/meson.build
index 9f0a267c83..be343f68fe 100644
--- a/hw/timer/meson.build
+++ b/hw/timer/meson.build
@@ -23,6 +23,7 @@ softmmu_ss.add(when: 'CONFIG_LM32', if_true: files('lm32_timer.c'))
 softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-sysctl.c'))
 softmmu_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gictimer.c'))
 softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-timer.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_timer.c'))
 softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_timer.c'))
 softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gptimer.c'))
 softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_synctimer.c'))
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index 447b7c405b..ee66d8c84f 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -66,6 +66,11 @@ cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK A
 cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
 cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset"
 
+# npcm7xx_timer.c
+npcm7xx_timer_read(const char *id, uint64_t offset, uint64_t value) " %s offset: 0x%04" PRIx64 " value 0x%08" PRIx64
+npcm7xx_timer_write(const char *id, uint64_t offset, uint64_t value) "%s offset: 0x%04" PRIx64 " value 0x%08" PRIx64
+npcm7xx_timer_irq(const char *id, int timer, int state) "%s timer %d state %d"
+
 # nrf51_timer.c
 nrf51_timer_read(uint8_t timer_id, uint64_t addr, uint32_t value, unsigned size) "timer %u read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
 nrf51_timer_write(uint8_t timer_id, uint64_t addr, uint32_t value, unsigned size) "timer %u write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 04/14] hw/arm: Add NPCM730 and NPCM750 SoC models
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (2 preceding siblings ...)
  2020-08-25  0:17 ` [PATCH v8 03/14] hw/timer: Add NPCM7xx Timer " Havard Skinnemoen via
@ 2020-08-25  0:17 ` Havard Skinnemoen via
  2020-08-25  0:17 ` [PATCH v8 05/14] hw/arm: Add two NPCM7xx-based machines Havard Skinnemoen via
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:17 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Joel Stanley

The Nuvoton NPCM7xx SoC family are used to implement Baseboard
Management Controllers in servers. While the family includes four SoCs,
this patch implements limited support for two of them: NPCM730 (targeted
for Data Center applications) and NPCM750 (targeted for Enterprise
applications).

This patch includes little more than the bare minimum needed to boot a
Linux kernel built with NPCM7xx support in direct-kernel mode:

  - Two Cortex-A9 CPU cores with built-in periperhals.
  - Global Configuration Registers.
  - Clock Management.
  - 3 Timer Modules with 5 timers each.
  - 4 serial ports.

The chips themselves have a lot more features, some of which will be
added to the model at a later stage.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/arm/npcm7xx.h |  85 ++++++++
 hw/arm/npcm7xx.c         | 407 +++++++++++++++++++++++++++++++++++++++
 hw/arm/Kconfig           |   5 +
 hw/arm/meson.build       |   1 +
 4 files changed, 498 insertions(+)
 create mode 100644 include/hw/arm/npcm7xx.h
 create mode 100644 hw/arm/npcm7xx.c

diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
new file mode 100644
index 0000000000..e68d9c79e6
--- /dev/null
+++ b/include/hw/arm/npcm7xx.h
@@ -0,0 +1,85 @@
+/*
+ * Nuvoton NPCM7xx SoC family.
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+#ifndef NPCM7XX_H
+#define NPCM7XX_H
+
+#include "hw/boards.h"
+#include "hw/cpu/a9mpcore.h"
+#include "hw/misc/npcm7xx_clk.h"
+#include "hw/misc/npcm7xx_gcr.h"
+#include "hw/timer/npcm7xx_timer.h"
+#include "target/arm/cpu.h"
+
+#define NPCM7XX_MAX_NUM_CPUS    (2)
+
+/* The first half of the address space is reserved for DDR4 DRAM. */
+#define NPCM7XX_DRAM_BA         (0x00000000)
+#define NPCM7XX_DRAM_SZ         (2 * GiB)
+
+/* Magic addresses for setting up direct kernel booting and SMP boot stubs. */
+#define NPCM7XX_LOADER_START            (0x00000000)  /* Start of SDRAM */
+#define NPCM7XX_SMP_LOADER_START        (0xffff0000)  /* Boot ROM */
+#define NPCM7XX_SMP_BOOTREG_ADDR        (0xf080013c)  /* GCR.SCRPAD */
+#define NPCM7XX_GIC_CPU_IF_ADDR         (0xf03fe100)  /* GIC within A9 */
+
+typedef struct NPCM7xxState {
+    DeviceState         parent;
+
+    ARMCPU              cpu[NPCM7XX_MAX_NUM_CPUS];
+    A9MPPrivState       a9mpcore;
+
+    MemoryRegion        sram;
+    MemoryRegion        irom;
+    MemoryRegion        ram3;
+    MemoryRegion        *dram;
+
+    NPCM7xxGCRState     gcr;
+    NPCM7xxCLKState     clk;
+    NPCM7xxTimerCtrlState tim[3];
+} NPCM7xxState;
+
+#define TYPE_NPCM7XX    "npcm7xx"
+#define NPCM7XX(obj)    OBJECT_CHECK(NPCM7xxState, (obj), TYPE_NPCM7XX)
+
+#define TYPE_NPCM730    "npcm730"
+#define TYPE_NPCM750    "npcm750"
+
+typedef struct NPCM7xxClass {
+    DeviceClass         parent;
+
+    /* Bitmask of modules that are permanently disabled on this chip. */
+    uint32_t            disabled_modules;
+    /* Number of CPU cores enabled in this SoC class (may be 1 or 2). */
+    uint32_t            num_cpus;
+} NPCM7xxClass;
+
+#define NPCM7XX_CLASS(klass)                                            \
+    OBJECT_CLASS_CHECK(NPCM7xxClass, (klass), TYPE_NPCM7XX)
+#define NPCM7XX_GET_CLASS(obj)                                          \
+    OBJECT_GET_CLASS(NPCM7xxClass, (obj), TYPE_NPCM7XX)
+
+/**
+ * npcm7xx_load_kernel - Loads memory with everything needed to boot
+ * @machine - The machine containing the SoC to be booted.
+ * @soc - The SoC containing the CPU to be booted.
+ *
+ * This will set up the ARM boot info structure for the specific NPCM7xx
+ * derivative and call arm_load_kernel() to set up loading of the kernel, etc.
+ * into memory, if requested by the user.
+ */
+void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc);
+
+#endif /* NPCM7XX_H */
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
new file mode 100644
index 0000000000..9669ac5fa0
--- /dev/null
+++ b/hw/arm/npcm7xx.c
@@ -0,0 +1,407 @@
+/*
+ * Nuvoton NPCM7xx SoC family.
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+
+#include "exec/address-spaces.h"
+#include "hw/arm/boot.h"
+#include "hw/arm/npcm7xx.h"
+#include "hw/char/serial.h"
+#include "hw/loader.h"
+#include "hw/misc/unimp.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/units.h"
+#include "sysemu/sysemu.h"
+
+/*
+ * This covers the whole MMIO space. We'll use this to catch any MMIO accesses
+ * that aren't handled by any device.
+ */
+#define NPCM7XX_MMIO_BA         (0x80000000)
+#define NPCM7XX_MMIO_SZ         (0x7ffd0000)
+
+/* Core system modules. */
+#define NPCM7XX_L2C_BA          (0xf03fc000)
+#define NPCM7XX_CPUP_BA         (0xf03fe000)
+#define NPCM7XX_GCR_BA          (0xf0800000)
+#define NPCM7XX_CLK_BA          (0xf0801000)
+
+/* Internal AHB SRAM */
+#define NPCM7XX_RAM3_BA         (0xc0008000)
+#define NPCM7XX_RAM3_SZ         (4 * KiB)
+
+/* Memory blocks at the end of the address space */
+#define NPCM7XX_RAM2_BA         (0xfffd0000)
+#define NPCM7XX_RAM2_SZ         (128 * KiB)
+#define NPCM7XX_ROM_BA          (0xffff0000)
+#define NPCM7XX_ROM_SZ          (64 * KiB)
+
+/*
+ * Interrupt lines going into the GIC. This does not include internal Cortex-A9
+ * interrupts.
+ */
+enum NPCM7xxInterrupt {
+    NPCM7XX_UART0_IRQ           = 2,
+    NPCM7XX_UART1_IRQ,
+    NPCM7XX_UART2_IRQ,
+    NPCM7XX_UART3_IRQ,
+    NPCM7XX_TIMER0_IRQ          = 32,   /* Timer Module 0 */
+    NPCM7XX_TIMER1_IRQ,
+    NPCM7XX_TIMER2_IRQ,
+    NPCM7XX_TIMER3_IRQ,
+    NPCM7XX_TIMER4_IRQ,
+    NPCM7XX_TIMER5_IRQ,                 /* Timer Module 1 */
+    NPCM7XX_TIMER6_IRQ,
+    NPCM7XX_TIMER7_IRQ,
+    NPCM7XX_TIMER8_IRQ,
+    NPCM7XX_TIMER9_IRQ,
+    NPCM7XX_TIMER10_IRQ,                /* Timer Module 2 */
+    NPCM7XX_TIMER11_IRQ,
+    NPCM7XX_TIMER12_IRQ,
+    NPCM7XX_TIMER13_IRQ,
+    NPCM7XX_TIMER14_IRQ,
+};
+
+/* Total number of GIC interrupts, including internal Cortex-A9 interrupts. */
+#define NPCM7XX_NUM_IRQ         (160)
+
+/* Register base address for each Timer Module */
+static const hwaddr npcm7xx_tim_addr[] = {
+    0xf0008000,
+    0xf0009000,
+    0xf000a000,
+};
+
+/* Register base address for each 16550 UART */
+static const hwaddr npcm7xx_uart_addr[] = {
+    0xf0001000,
+    0xf0002000,
+    0xf0003000,
+    0xf0004000,
+};
+
+static void npcm7xx_write_secondary_boot(ARMCPU *cpu,
+                                         const struct arm_boot_info *info)
+{
+    /*
+     * The default smpboot stub halts the secondary CPU with a 'wfi'
+     * instruction, but the arch/arm/mach-npcm/platsmp.c in the Linux kernel
+     * does not send an IPI to wake it up, so the second CPU fails to boot. So
+     * we need to provide our own smpboot stub that can not use 'wfi', it has
+     * to spin the secondary CPU until the first CPU writes to the SCRPAD reg.
+     */
+    uint32_t smpboot[] = {
+        0xe59f2018,     /* ldr r2, bootreg_addr */
+        0xe3a00000,     /* mov r0, #0 */
+        0xe5820000,     /* str r0, [r2] */
+        0xe320f002,     /* wfe */
+        0xe5921000,     /* ldr r1, [r2] */
+        0xe1110001,     /* tst r1, r1 */
+        0x0afffffb,     /* beq <wfe> */
+        0xe12fff11,     /* bx r1 */
+        NPCM7XX_SMP_BOOTREG_ADDR,
+    };
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(smpboot); i++) {
+        smpboot[i] = tswap32(smpboot[i]);
+    }
+
+    rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
+                       NPCM7XX_SMP_LOADER_START);
+}
+
+static struct arm_boot_info npcm7xx_binfo = {
+    .loader_start           = NPCM7XX_LOADER_START,
+    .smp_loader_start       = NPCM7XX_SMP_LOADER_START,
+    .smp_bootreg_addr       = NPCM7XX_SMP_BOOTREG_ADDR,
+    .gic_cpu_if_addr        = NPCM7XX_GIC_CPU_IF_ADDR,
+    .write_secondary_boot   = npcm7xx_write_secondary_boot,
+    .board_id               = -1,
+};
+
+void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
+{
+    NPCM7xxClass *sc = NPCM7XX_GET_CLASS(soc);
+
+    npcm7xx_binfo.ram_size = machine->ram_size;
+    npcm7xx_binfo.nb_cpus = sc->num_cpus;
+
+    arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo);
+}
+
+static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n)
+{
+    return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n);
+}
+
+static void npcm7xx_init(Object *obj)
+{
+    NPCM7xxState *s = NPCM7XX(obj);
+    int i;
+
+    for (i = 0; i < NPCM7XX_MAX_NUM_CPUS; i++) {
+        object_initialize_child(obj, "cpu[*]", &s->cpu[i],
+                                ARM_CPU_TYPE_NAME("cortex-a9"));
+    }
+
+    object_initialize_child(obj, "a9mpcore", &s->a9mpcore, TYPE_A9MPCORE_PRIV);
+    object_initialize_child(obj, "gcr", &s->gcr, TYPE_NPCM7XX_GCR);
+    object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
+                              "power-on-straps");
+    object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM7XX_CLK);
+
+    for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
+        object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
+    }
+}
+
+static void npcm7xx_realize(DeviceState *dev, Error **errp)
+{
+    NPCM7xxState *s = NPCM7XX(dev);
+    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
+    int i;
+
+    if (memory_region_size(s->dram) > NPCM7XX_DRAM_SZ) {
+        error_setg(errp, "%s: NPCM7xx cannot address more than %" PRIu64
+                   " MiB of DRAM", __func__, NPCM7XX_DRAM_SZ / MiB);
+        return;
+    }
+
+    /* CPUs */
+    for (i = 0; i < nc->num_cpus; i++) {
+        object_property_set_int(OBJECT(&s->cpu[i]), "mp-affinity",
+                                arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
+                                &error_abort);
+        object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar",
+                                NPCM7XX_GIC_CPU_IF_ADDR, &error_abort);
+        object_property_set_bool(OBJECT(&s->cpu[i]), "reset-hivecs", true,
+                                 &error_abort);
+
+        /* Disable security extensions. */
+        object_property_set_bool(OBJECT(&s->cpu[i]), "has_el3", false,
+                                 &error_abort);
+
+        if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
+            return;
+        }
+    }
+
+    /* A9MPCORE peripherals. Can only fail if we pass bad parameters here. */
+    object_property_set_int(OBJECT(&s->a9mpcore), "num-cpu", nc->num_cpus,
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->a9mpcore), "num-irq", NPCM7XX_NUM_IRQ,
+                            &error_abort);
+    sysbus_realize(SYS_BUS_DEVICE(&s->a9mpcore), &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, NPCM7XX_CPUP_BA);
+
+    for (i = 0; i < nc->num_cpus; i++) {
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i,
+                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + nc->num_cpus,
+                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ));
+    }
+
+    /* L2 cache controller */
+    sysbus_create_simple("l2x0", NPCM7XX_L2C_BA, NULL);
+
+    /* System Global Control Registers (GCR). Can fail due to user input. */
+    object_property_set_int(OBJECT(&s->gcr), "disabled-modules",
+                            nc->disabled_modules, &error_abort);
+    object_property_add_const_link(OBJECT(&s->gcr), "dram-mr", OBJECT(s->dram));
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->gcr), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gcr), 0, NPCM7XX_GCR_BA);
+
+    /* Clock Control Registers (CLK). Cannot fail. */
+    sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM7XX_CLK_BA);
+
+    /* Timer Modules (TIM). Cannot fail. */
+    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
+    for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
+        SysBusDevice *sbd = SYS_BUS_DEVICE(&s->tim[i]);
+        int first_irq;
+        int j;
+
+        sysbus_realize(sbd, &error_abort);
+        sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
+
+        first_irq = NPCM7XX_TIMER0_IRQ + i * NPCM7XX_TIMERS_PER_CTRL;
+        for (j = 0; j < NPCM7XX_TIMERS_PER_CTRL; j++) {
+            qemu_irq irq = npcm7xx_irq(s, first_irq + j);
+            sysbus_connect_irq(sbd, j, irq);
+        }
+    }
+
+    /* UART0..3 (16550 compatible) */
+    for (i = 0; i < ARRAY_SIZE(npcm7xx_uart_addr); i++) {
+        serial_mm_init(get_system_memory(), npcm7xx_uart_addr[i], 2,
+                       npcm7xx_irq(s, NPCM7XX_UART0_IRQ + i), 115200,
+                       serial_hd(i), DEVICE_LITTLE_ENDIAN);
+    }
+
+    /* RAM2 (SRAM) */
+    memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
+                           NPCM7XX_RAM2_SZ, &error_abort);
+    memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM2_BA, &s->sram);
+
+    /* RAM3 (SRAM) */
+    memory_region_init_ram(&s->ram3, OBJECT(dev), "ram3",
+                           NPCM7XX_RAM3_SZ, &error_abort);
+    memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM3_BA, &s->ram3);
+
+    /* Internal ROM */
+    memory_region_init_rom(&s->irom, OBJECT(dev), "irom", NPCM7XX_ROM_SZ,
+                           &error_abort);
+    memory_region_add_subregion(get_system_memory(), NPCM7XX_ROM_BA, &s->irom);
+
+    create_unimplemented_device("npcm7xx.shm",          0xc0001000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.vdmx",         0xe0800000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.pcierc",       0xe1000000,  64 * KiB);
+    create_unimplemented_device("npcm7xx.kcs",          0xf0007000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.rng",          0xf000b000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.adc",          0xf000c000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gfxi",         0xf000e000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[0]",      0xf0010000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[1]",      0xf0011000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[2]",      0xf0012000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[3]",      0xf0013000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[4]",      0xf0014000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[5]",      0xf0015000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[6]",      0xf0016000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[7]",      0xf0017000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[0]",     0xf0080000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[1]",     0xf0081000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[2]",     0xf0082000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[3]",     0xf0083000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[4]",     0xf0084000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[5]",     0xf0085000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[6]",     0xf0086000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[7]",     0xf0087000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[8]",     0xf0088000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[9]",     0xf0089000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[10]",    0xf008a000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[11]",    0xf008b000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[12]",    0xf008c000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[13]",    0xf008d000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[14]",    0xf008e000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[15]",    0xf008f000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.espi",         0xf009f000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.peci",         0xf0100000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.siox[1]",      0xf0101000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.siox[2]",      0xf0102000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.pwm[0]",       0xf0103000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.pwm[1]",       0xf0104000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[0]",       0xf0180000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[1]",       0xf0181000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[2]",       0xf0182000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[3]",       0xf0183000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[4]",       0xf0184000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[5]",       0xf0185000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[6]",       0xf0186000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[7]",       0xf0187000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.pspi1",        0xf0200000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.pspi2",        0xf0201000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.ahbpci",       0xf0400000,   1 * MiB);
+    create_unimplemented_device("npcm7xx.mcphy",        0xf05f0000,  64 * KiB);
+    create_unimplemented_device("npcm7xx.gmac1",        0xf0802000,   8 * KiB);
+    create_unimplemented_device("npcm7xx.gmac2",        0xf0804000,   8 * KiB);
+    create_unimplemented_device("npcm7xx.ehci",         0xf0806000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.ohci",         0xf0807000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.vcd",          0xf0810000,  64 * KiB);
+    create_unimplemented_device("npcm7xx.ece",          0xf0820000,   8 * KiB);
+    create_unimplemented_device("npcm7xx.vdma",         0xf0822000,   8 * KiB);
+    create_unimplemented_device("npcm7xx.emc1",         0xf0825000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.emc2",         0xf0826000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[0]",      0xf0830000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[1]",      0xf0831000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[2]",      0xf0832000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[3]",      0xf0833000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[4]",      0xf0834000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[5]",      0xf0835000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[6]",      0xf0836000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[7]",      0xf0837000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[8]",      0xf0838000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[9]",      0xf0839000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.sd",           0xf0840000,   8 * KiB);
+    create_unimplemented_device("npcm7xx.mmc",          0xf0842000,   8 * KiB);
+    create_unimplemented_device("npcm7xx.pcimbx",       0xf0848000, 512 * KiB);
+    create_unimplemented_device("npcm7xx.aes",          0xf0858000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.des",          0xf0859000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.sha",          0xf085a000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.secacc",       0xf085b000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.spixcs0",      0xf8000000,  16 * MiB);
+    create_unimplemented_device("npcm7xx.spixcs1",      0xf9000000,  16 * MiB);
+    create_unimplemented_device("npcm7xx.spix",         0xfb001000,   4 * KiB);
+}
+
+static Property npcm7xx_properties[] = {
+    DEFINE_PROP_LINK("dram-mr", NPCM7xxState, dram, TYPE_MEMORY_REGION,
+                     MemoryRegion *),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void npcm7xx_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = npcm7xx_realize;
+    dc->user_creatable = false;
+    device_class_set_props(dc, npcm7xx_properties);
+}
+
+static void npcm730_class_init(ObjectClass *oc, void *data)
+{
+    NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
+
+    /* NPCM730 is optimized for data center use, so no graphics, etc. */
+    nc->disabled_modules = 0x00300395;
+    nc->num_cpus = 2;
+}
+
+static void npcm750_class_init(ObjectClass *oc, void *data)
+{
+    NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
+
+    /* NPCM750 has 2 cores and a full set of peripherals */
+    nc->disabled_modules = 0x00000000;
+    nc->num_cpus = 2;
+}
+
+static const TypeInfo npcm7xx_soc_types[] = {
+    {
+        .name           = TYPE_NPCM7XX,
+        .parent         = TYPE_DEVICE,
+        .instance_size  = sizeof(NPCM7xxState),
+        .instance_init  = npcm7xx_init,
+        .class_size     = sizeof(NPCM7xxClass),
+        .class_init     = npcm7xx_class_init,
+        .abstract       = true,
+    }, {
+        .name           = TYPE_NPCM730,
+        .parent         = TYPE_NPCM7XX,
+        .class_init     = npcm730_class_init,
+    }, {
+        .name           = TYPE_NPCM750,
+        .parent         = TYPE_NPCM7XX,
+        .class_init     = npcm750_class_init,
+    },
+};
+
+DEFINE_TYPES(npcm7xx_soc_types);
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 4aec9c63f8..cd74642034 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -357,6 +357,11 @@ config XLNX_VERSAL
 
 config NPCM7XX
     bool
+    select A9MPCORE
+    select ARM_GIC
+    select PL310  # cache controller
+    select SERIAL
+    select UNIMP
 
 config FSL_IMX25
     bool
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 1ae5e17eeb..b777a6ae39 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -13,6 +13,7 @@ arm_ss.add(when: 'CONFIG_MICROBIT', if_true: files('microbit.c'))
 arm_ss.add(when: 'CONFIG_MUSICPAL', if_true: files('musicpal.c'))
 arm_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c'))
 arm_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c'))
+arm_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c'))
 arm_ss.add(when: 'CONFIG_NSERIES', if_true: files('nseries.c'))
 arm_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c'))
 arm_ss.add(when: 'CONFIG_CHEETAH', if_true: files('palm.c'))
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 05/14] hw/arm: Add two NPCM7xx-based machines
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (3 preceding siblings ...)
  2020-08-25  0:17 ` [PATCH v8 04/14] hw/arm: Add NPCM730 and NPCM750 SoC models Havard Skinnemoen via
@ 2020-08-25  0:17 ` Havard Skinnemoen via
  2020-08-25  0:17 ` [PATCH v8 06/14] roms: Add virtual Boot ROM for NPCM7xx SoCs Havard Skinnemoen via
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:17 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Joel Stanley, Cédric Le Goater

This adds two new machines, both supported by OpenBMC:

  - npcm750-evb: Nuvoton NPCM750 Evaluation Board.
  - quanta-gsj: A board with a NPCM730 chip.

They rely on the NPCM7xx SoC device to do the heavy lifting. They are
almost completely identical at the moment, apart from the SoC type,
which currently only changes the reset contents of one register
(GCR.MDLR), but they might grow apart a bit more as more functionality
is added.

Both machines can boot the Linux kernel into /bin/sh.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 default-configs/arm-softmmu.mak |   1 +
 include/hw/arm/npcm7xx.h        |  19 +++++
 hw/arm/npcm7xx_boards.c         | 144 ++++++++++++++++++++++++++++++++
 hw/arm/meson.build              |   2 +-
 4 files changed, 165 insertions(+), 1 deletion(-)
 create mode 100644 hw/arm/npcm7xx_boards.c

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 8fc09a4a51..9a94ebd0be 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -27,6 +27,7 @@ CONFIG_GUMSTIX=y
 CONFIG_SPITZ=y
 CONFIG_TOSA=y
 CONFIG_Z2=y
+CONFIG_NPCM7XX=y
 CONFIG_COLLIE=y
 CONFIG_ASPEED_SOC=y
 CONFIG_NETDUINO2=y
diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
index e68d9c79e6..ba7495869d 100644
--- a/include/hw/arm/npcm7xx.h
+++ b/include/hw/arm/npcm7xx.h
@@ -35,6 +35,25 @@
 #define NPCM7XX_SMP_BOOTREG_ADDR        (0xf080013c)  /* GCR.SCRPAD */
 #define NPCM7XX_GIC_CPU_IF_ADDR         (0xf03fe100)  /* GIC within A9 */
 
+typedef struct NPCM7xxMachine {
+    MachineState        parent;
+} NPCM7xxMachine;
+
+#define TYPE_NPCM7XX_MACHINE MACHINE_TYPE_NAME("npcm7xx")
+#define NPCM7XX_MACHINE(obj)                                            \
+    OBJECT_CHECK(NPCM7xxMachine, (obj), TYPE_NPCM7XX_MACHINE)
+
+typedef struct NPCM7xxMachineClass {
+    MachineClass        parent;
+
+    const char          *soc_type;
+} NPCM7xxMachineClass;
+
+#define NPCM7XX_MACHINE_CLASS(klass)                                    \
+    OBJECT_CLASS_CHECK(NPCM7xxMachineClass, (klass), TYPE_NPCM7XX_MACHINE)
+#define NPCM7XX_MACHINE_GET_CLASS(obj)                                  \
+    OBJECT_GET_CLASS(NPCM7xxMachineClass, (obj), TYPE_NPCM7XX_MACHINE)
+
 typedef struct NPCM7xxState {
     DeviceState         parent;
 
diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
new file mode 100644
index 0000000000..0b9dce2b35
--- /dev/null
+++ b/hw/arm/npcm7xx_boards.c
@@ -0,0 +1,144 @@
+/*
+ * Machine definitions for boards featuring an NPCM7xx SoC.
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/arm/npcm7xx.h"
+#include "hw/core/cpu.h"
+#include "qapi/error.h"
+#include "qemu/units.h"
+
+#define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7
+#define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff
+
+static void npcm7xx_connect_dram(NPCM7xxState *soc, MemoryRegion *dram)
+{
+    memory_region_add_subregion(get_system_memory(), NPCM7XX_DRAM_BA, dram);
+
+    object_property_set_link(OBJECT(soc), "dram-mr", OBJECT(dram),
+                             &error_abort);
+}
+
+static NPCM7xxState *npcm7xx_create_soc(MachineState *machine,
+                                        uint32_t hw_straps)
+{
+    NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_GET_CLASS(machine);
+    MachineClass *mc = &nmc->parent;
+    Object *obj;
+
+    if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
+        error_report("This board can only be used with %s",
+                     mc->default_cpu_type);
+        exit(1);
+    }
+
+    obj = object_new_with_props(nmc->soc_type, OBJECT(machine), "soc",
+                                &error_abort, NULL);
+    object_property_set_uint(obj, "power-on-straps", hw_straps, &error_abort);
+
+    return NPCM7XX(obj);
+}
+
+static void npcm750_evb_init(MachineState *machine)
+{
+    NPCM7xxState *soc;
+
+    soc = npcm7xx_create_soc(machine, NPCM750_EVB_POWER_ON_STRAPS);
+    npcm7xx_connect_dram(soc, machine->ram);
+    qdev_realize(DEVICE(soc), NULL, &error_fatal);
+
+    npcm7xx_load_kernel(machine, soc);
+}
+
+static void quanta_gsj_init(MachineState *machine)
+{
+    NPCM7xxState *soc;
+
+    soc = npcm7xx_create_soc(machine, QUANTA_GSJ_POWER_ON_STRAPS);
+    npcm7xx_connect_dram(soc, machine->ram);
+    qdev_realize(DEVICE(soc), NULL, &error_fatal);
+
+    npcm7xx_load_kernel(machine, soc);
+}
+
+static void npcm7xx_set_soc_type(NPCM7xxMachineClass *nmc, const char *type)
+{
+    NPCM7xxClass *sc = NPCM7XX_CLASS(object_class_by_name(type));
+    MachineClass *mc = MACHINE_CLASS(nmc);
+
+    nmc->soc_type = type;
+    mc->default_cpus = mc->min_cpus = mc->max_cpus = sc->num_cpus;
+}
+
+static void npcm7xx_machine_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->no_floppy = 1;
+    mc->no_cdrom = 1;
+    mc->no_parallel = 1;
+    mc->default_ram_id = "ram";
+    mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a9");
+}
+
+/*
+ * Schematics:
+ * https://github.com/Nuvoton-Israel/nuvoton-info/blob/master/npcm7xx-poleg/evaluation-board/board_deliverables/NPCM750x_EB_ver.A1.1_COMPLETE.pdf
+ */
+static void npcm750_evb_machine_class_init(ObjectClass *oc, void *data)
+{
+    NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc);
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    npcm7xx_set_soc_type(nmc, TYPE_NPCM750);
+
+    mc->desc = "Nuvoton NPCM750 Evaluation Board (Cortex A9)";
+    mc->init = npcm750_evb_init;
+    mc->default_ram_size = 512 * MiB;
+};
+
+static void gsj_machine_class_init(ObjectClass *oc, void *data)
+{
+    NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc);
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    npcm7xx_set_soc_type(nmc, TYPE_NPCM730);
+
+    mc->desc = "Quanta GSJ (Cortex A9)";
+    mc->init = quanta_gsj_init;
+    mc->default_ram_size = 512 * MiB;
+};
+
+static const TypeInfo npcm7xx_machine_types[] = {
+    {
+        .name           = TYPE_NPCM7XX_MACHINE,
+        .parent         = TYPE_MACHINE,
+        .instance_size  = sizeof(NPCM7xxMachine),
+        .class_size     = sizeof(NPCM7xxMachineClass),
+        .class_init     = npcm7xx_machine_class_init,
+        .abstract       = true,
+    }, {
+        .name           = MACHINE_TYPE_NAME("npcm750-evb"),
+        .parent         = TYPE_NPCM7XX_MACHINE,
+        .class_init     = npcm750_evb_machine_class_init,
+    }, {
+        .name           = MACHINE_TYPE_NAME("quanta-gsj"),
+        .parent         = TYPE_NPCM7XX_MACHINE,
+        .class_init     = gsj_machine_class_init,
+    },
+};
+
+DEFINE_TYPES(npcm7xx_machine_types)
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index b777a6ae39..aa045d924a 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -13,7 +13,7 @@ arm_ss.add(when: 'CONFIG_MICROBIT', if_true: files('microbit.c'))
 arm_ss.add(when: 'CONFIG_MUSICPAL', if_true: files('musicpal.c'))
 arm_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c'))
 arm_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c'))
-arm_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c'))
+arm_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c'))
 arm_ss.add(when: 'CONFIG_NSERIES', if_true: files('nseries.c'))
 arm_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c'))
 arm_ss.add(when: 'CONFIG_CHEETAH', if_true: files('palm.c'))
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 06/14] roms: Add virtual Boot ROM for NPCM7xx SoCs
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (4 preceding siblings ...)
  2020-08-25  0:17 ` [PATCH v8 05/14] hw/arm: Add two NPCM7xx-based machines Havard Skinnemoen via
@ 2020-08-25  0:17 ` Havard Skinnemoen via
  2020-09-04  9:24   ` Philippe Mathieu-Daudé
  2020-08-25  0:17 ` [PATCH v8 07/14] hw/arm: Load -bios image as a boot ROM for npcm7xx Havard Skinnemoen via
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:17 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen

This is a minimalistic boot ROM written specifically for use with QEMU.
It supports loading the second-stage loader from SPI flash into RAM, SMP
boot, and not much else.

Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 Makefile                    |   1 +
 .gitmodules                 |   3 +++
 MAINTAINERS                 |   2 ++
 pc-bios/README              |   6 ++++++
 pc-bios/npcm7xx_bootrom.bin | Bin 0 -> 768 bytes
 roms/Makefile               |   7 +++++++
 roms/vbootrom               |   1 +
 7 files changed, 20 insertions(+)
 create mode 100644 pc-bios/npcm7xx_bootrom.bin
 create mode 160000 roms/vbootrom

diff --git a/Makefile b/Makefile
index 81794d5c34..08e9af3793 100644
--- a/Makefile
+++ b/Makefile
@@ -246,6 +246,7 @@ s390-ccw.img s390-netboot.img \
 slof.bin skiboot.lid \
 palcode-clipper \
 u-boot.e500 u-boot-sam460-20100605.bin \
+npcm7xx_bootrom.bin \
 qemu_vga.ndrv \
 edk2-licenses.txt \
 hppa-firmware.img \
diff --git a/.gitmodules b/.gitmodules
index ce979398a8..9ffb9f3f4f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -61,3 +61,6 @@
 [submodule "meson"]
 	path = meson
 	url = https://github.com/mesonbuild/meson/
+[submodule "roms/vbootrom"]
+	path = roms/vbootrom
+	url = https://github.com/google/vbootrom.git
diff --git a/MAINTAINERS b/MAINTAINERS
index 3f72250630..d71afedc20 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -756,6 +756,8 @@ L: qemu-arm@nongnu.org
 S: Supported
 F: hw/*/npcm7xx*
 F: include/hw/*/npcm7xx*
+F: pc-bios/npcm7xx_bootrom.bin
+F: roms/vbootrom
 
 nSeries
 M: Andrzej Zaborowski <balrogg@gmail.com>
diff --git a/pc-bios/README b/pc-bios/README
index fa8b58b797..4d6297fca7 100644
--- a/pc-bios/README
+++ b/pc-bios/README
@@ -71,3 +71,9 @@
   ("Simplified BSD License" or "FreeBSD License", SPDX: BSD-2-Clause). OpenSBI
   source code also contains code reused from other projects desribed here:
   https://github.com/riscv/opensbi/blob/master/ThirdPartyNotices.md.
+
+- npcm7xx_bootrom.bin is a simplified, free (Apache 2.0) boot ROM for Nuvoton
+  NPCM7xx BMC devices. It currently implements the bare minimum to load, parse,
+  initialize and run boot images stored in SPI flash, but may grow more
+  features over time as needed. The source code is available at:
+  https://github.com/google/vbootrom
diff --git a/pc-bios/npcm7xx_bootrom.bin b/pc-bios/npcm7xx_bootrom.bin
new file mode 100644
index 0000000000000000000000000000000000000000..38f89d1b97b0c2e133af2a9fbed0521be132065b
GIT binary patch
literal 768
zcmd5)JxClu6n-<aczPbVhZYusb8wKx;7TklHfmuZdYT9pDRLwd1p_t-DFpWpyA+8(
zwKtZg3J4a0aCM3_X(ZL&4g;46VVk5e$K;z;L99|b@aE%v^S$rQ8)h(Vm@cB9IYc+2
z2SHd4^NwTIGE%w>9S05p1#kf90Sj5Z(jG8}+)IZIp~iXK=T&)dL`%d-q*8aR#mq{7
z9`=6;Dr(H0ACe72R5x?!)^86Qj-X%{+!K9iZNA@*wkBAV&iZ(l^I9?!Gz=S2I_*1d
zr+tTQDHjvyzKnw(hu00yX`u!Fv<!~XVcX?@kr#<B0(gGU?$W{gSsQa}CF^8Cfzp2X
z@P}yDV-bci(K9XL$FU!som2C`c)?Uc&294s^}Wzumap{hg1X^jN|V25M5tQZ=<9lN
z%(zKz#t-qCwHKb;HygOCpvCNL_4@1tXV1YGf^XUE_$zr{g8zWh-6gz-teI(eibtxo
z?0OZI4%rU0741PgUD`2xq@H|*4=+Rs?%N)Ox5G+q>C;DilBe_YlkeSUVHA-crNk+k
jtiF_MudA<CB(}8|fqYwCf3re&=&@_s761P#-ID$TwgmBa

literal 0
HcmV?d00001

diff --git a/roms/Makefile b/roms/Makefile
index 5d9f15b677..7405d67326 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -34,6 +34,7 @@ find-cross-gcc = $(firstword $(wildcard $(patsubst %ld,%gcc,$(call find-cross-ld
 # finally strip off path + toolname so we get the prefix
 find-cross-prefix = $(subst gcc,,$(notdir $(call find-cross-gcc,$(1))))
 
+arm_cross_prefix := $(call find-cross-prefix,arm)
 powerpc64_cross_prefix := $(call find-cross-prefix,powerpc64)
 powerpc_cross_prefix := $(call find-cross-prefix,powerpc)
 x86_64_cross_prefix := $(call find-cross-prefix,x86_64)
@@ -63,6 +64,7 @@ default help:
 	@echo "  skiboot            -- update skiboot.lid"
 	@echo "  u-boot.e500        -- update u-boot.e500"
 	@echo "  u-boot.sam460      -- update u-boot.sam460"
+	@echo "  npcm7xx_bootrom    -- update vbootrom for npcm7xx"
 	@echo "  efi                -- update UEFI (edk2) platform firmware"
 	@echo "  opensbi32-generic  -- update OpenSBI for 32-bit generic machine"
 	@echo "  opensbi64-generic  -- update OpenSBI for 64-bit generic machine"
@@ -186,6 +188,10 @@ bios-microvm:
 	$(MAKE) -C qboot
 	cp qboot/bios.bin ../pc-bios/bios-microvm.bin
 
+npcm7xx_bootrom:
+	$(MAKE) -C vbootrom CROSS_COMPILE=$(arm_cross_prefix)
+	cp vbootrom/npcm7xx_bootrom.bin ../pc-bios/npcm7xx_bootrom.bin
+
 clean:
 	rm -rf seabios/.config seabios/out seabios/builds
 	$(MAKE) -C sgabios clean
@@ -199,3 +205,4 @@ clean:
 	$(MAKE) -f Makefile.edk2 clean
 	$(MAKE) -C opensbi clean
 	$(MAKE) -C qboot clean
+	$(MAKE) -C vbootrom clean
diff --git a/roms/vbootrom b/roms/vbootrom
new file mode 160000
index 0000000000..0c37a43527
--- /dev/null
+++ b/roms/vbootrom
@@ -0,0 +1 @@
+Subproject commit 0c37a43527f0ee2b9584e7fb2fdc805e902635ac
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 07/14] hw/arm: Load -bios image as a boot ROM for npcm7xx
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (5 preceding siblings ...)
  2020-08-25  0:17 ` [PATCH v8 06/14] roms: Add virtual Boot ROM for NPCM7xx SoCs Havard Skinnemoen via
@ 2020-08-25  0:17 ` Havard Skinnemoen via
  2020-09-03 18:59   ` Philippe Mathieu-Daudé
  2020-08-25  0:17 ` [PATCH v8 08/14] hw/nvram: NPCM7xx OTP device model Havard Skinnemoen via
                   ` (8 subsequent siblings)
  15 siblings, 1 reply; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:17 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Cédric Le Goater

If a -bios option is specified on the command line, load the image into
the internal ROM memory region, which contains the first instructions
run by the CPU after reset.

If -bios is not specified, the vbootrom included with qemu is loaded by
default.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 hw/arm/npcm7xx_boards.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
index 0b9dce2b35..b67e45e913 100644
--- a/hw/arm/npcm7xx_boards.c
+++ b/hw/arm/npcm7xx_boards.c
@@ -18,12 +18,43 @@
 
 #include "hw/arm/npcm7xx.h"
 #include "hw/core/cpu.h"
+#include "hw/loader.h"
 #include "qapi/error.h"
+#include "qemu-common.h"
 #include "qemu/units.h"
+#include "sysemu/sysemu.h"
 
 #define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7
 #define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff
 
+static const char npcm7xx_default_bootrom[] = "npcm7xx_bootrom.bin";
+
+static void npcm7xx_load_bootrom(MachineState *machine, NPCM7xxState *soc)
+{
+    g_autofree char *filename = NULL;
+    int ret;
+
+    if (machine->kernel_filename) {
+        /* Don't load the bootrom if user requested direct kernel boot. */
+        return;
+    }
+
+    if (!bios_name) {
+        bios_name = npcm7xx_default_bootrom;
+    }
+
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+    if (!filename) {
+        error_report("Could not find ROM image '%s'", bios_name);
+        exit(1);
+    }
+    ret = load_image_mr(filename, &soc->irom);
+    if (ret < 0) {
+        error_report("Failed to load ROM image '%s'", filename);
+        exit(1);
+    }
+}
+
 static void npcm7xx_connect_dram(NPCM7xxState *soc, MemoryRegion *dram)
 {
     memory_region_add_subregion(get_system_memory(), NPCM7XX_DRAM_BA, dram);
@@ -60,6 +91,7 @@ static void npcm750_evb_init(MachineState *machine)
     npcm7xx_connect_dram(soc, machine->ram);
     qdev_realize(DEVICE(soc), NULL, &error_fatal);
 
+    npcm7xx_load_bootrom(machine, soc);
     npcm7xx_load_kernel(machine, soc);
 }
 
@@ -71,6 +103,7 @@ static void quanta_gsj_init(MachineState *machine)
     npcm7xx_connect_dram(soc, machine->ram);
     qdev_realize(DEVICE(soc), NULL, &error_fatal);
 
+    npcm7xx_load_bootrom(machine, soc);
     npcm7xx_load_kernel(machine, soc);
 }
 
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 08/14] hw/nvram: NPCM7xx OTP device model
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (6 preceding siblings ...)
  2020-08-25  0:17 ` [PATCH v8 07/14] hw/arm: Load -bios image as a boot ROM for npcm7xx Havard Skinnemoen via
@ 2020-08-25  0:17 ` Havard Skinnemoen via
  2020-09-07 19:47   ` Alexander Bulekov
  2020-08-25  0:17 ` [PATCH v8 09/14] hw/mem: Stubbed out NPCM7xx Memory Controller model Havard Skinnemoen via
                   ` (7 subsequent siblings)
  15 siblings, 1 reply; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:17 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Avi Fishman

This supports reading and writing OTP fuses and keys. Only fuse reading
has been tested. Protection is not implemented.

Reviewed-by: Avi Fishman <avi.fishman@nuvoton.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/arm/npcm7xx.h       |   3 +
 include/hw/nvram/npcm7xx_otp.h |  79 ++++++
 hw/arm/npcm7xx.c               |  29 +++
 hw/nvram/npcm7xx_otp.c         | 439 +++++++++++++++++++++++++++++++++
 hw/nvram/meson.build           |   1 +
 5 files changed, 551 insertions(+)
 create mode 100644 include/hw/nvram/npcm7xx_otp.h
 create mode 100644 hw/nvram/npcm7xx_otp.c

diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
index ba7495869d..5816a07a72 100644
--- a/include/hw/arm/npcm7xx.h
+++ b/include/hw/arm/npcm7xx.h
@@ -20,6 +20,7 @@
 #include "hw/cpu/a9mpcore.h"
 #include "hw/misc/npcm7xx_clk.h"
 #include "hw/misc/npcm7xx_gcr.h"
+#include "hw/nvram/npcm7xx_otp.h"
 #include "hw/timer/npcm7xx_timer.h"
 #include "target/arm/cpu.h"
 
@@ -68,6 +69,8 @@ typedef struct NPCM7xxState {
     NPCM7xxGCRState     gcr;
     NPCM7xxCLKState     clk;
     NPCM7xxTimerCtrlState tim[3];
+    NPCM7xxOTPState     key_storage;
+    NPCM7xxOTPState     fuse_array;
 } NPCM7xxState;
 
 #define TYPE_NPCM7XX    "npcm7xx"
diff --git a/include/hw/nvram/npcm7xx_otp.h b/include/hw/nvram/npcm7xx_otp.h
new file mode 100644
index 0000000000..156bbd151a
--- /dev/null
+++ b/include/hw/nvram/npcm7xx_otp.h
@@ -0,0 +1,79 @@
+/*
+ * Nuvoton NPCM7xx OTP (Fuse Array) Interface
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+#ifndef NPCM7XX_OTP_H
+#define NPCM7XX_OTP_H
+
+#include "exec/memory.h"
+#include "hw/sysbus.h"
+
+/* Each OTP module holds 8192 bits of one-time programmable storage */
+#define NPCM7XX_OTP_ARRAY_BITS (8192)
+#define NPCM7XX_OTP_ARRAY_BYTES (NPCM7XX_OTP_ARRAY_BITS / BITS_PER_BYTE)
+
+/* Fuse array offsets */
+#define NPCM7XX_FUSE_FUSTRAP (0)
+#define NPCM7XX_FUSE_CP_FUSTRAP (12)
+#define NPCM7XX_FUSE_DAC_CALIB (16)
+#define NPCM7XX_FUSE_ADC_CALIB (24)
+#define NPCM7XX_FUSE_DERIVATIVE (64)
+#define NPCM7XX_FUSE_TEST_SIG (72)
+#define NPCM7XX_FUSE_DIE_LOCATION (74)
+#define NPCM7XX_FUSE_GP1 (80)
+#define NPCM7XX_FUSE_GP2 (128)
+
+/*
+ * Number of registers in our device state structure. Don't change this without
+ * incrementing the version_id in the vmstate.
+ */
+#define NPCM7XX_OTP_NR_REGS (0x18 / sizeof(uint32_t))
+
+/**
+ * struct NPCM7xxOTPState - Device state for one OTP module.
+ * @parent: System bus device.
+ * @mmio: Memory region through which registers are accessed.
+ * @regs: Register contents.
+ * @array: OTP storage array.
+ */
+typedef struct NPCM7xxOTPState {
+    SysBusDevice parent;
+
+    MemoryRegion mmio;
+    uint32_t regs[NPCM7XX_OTP_NR_REGS];
+    uint8_t array[NPCM7XX_OTP_ARRAY_BYTES];
+} NPCM7xxOTPState;
+
+#define TYPE_NPCM7XX_OTP "npcm7xx-otp"
+#define NPCM7XX_OTP(obj) OBJECT_CHECK(NPCM7xxOTPState, (obj), TYPE_NPCM7XX_OTP)
+
+#define TYPE_NPCM7XX_KEY_STORAGE "npcm7xx-key-storage"
+#define TYPE_NPCM7XX_FUSE_ARRAY "npcm7xx-fuse-array"
+
+typedef struct NPCM7xxOTPClass NPCM7xxOTPClass;
+
+/**
+ * npcm7xx_otp_array_write - ECC encode and write data to OTP array.
+ * @s: OTP module.
+ * @data: Data to be encoded and written.
+ * @offset: Offset of first byte to be written in the OTP array.
+ * @len: Number of bytes before ECC encoding.
+ *
+ * Each nibble of data is encoded into a byte, so the number of bytes written
+ * to the array will be @len * 2.
+ */
+extern void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
+                                    unsigned int offset, unsigned int len);
+
+#endif /* NPCM7XX_OTP_H */
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 9669ac5fa0..9166002598 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -34,6 +34,10 @@
 #define NPCM7XX_MMIO_BA         (0x80000000)
 #define NPCM7XX_MMIO_SZ         (0x7ffd0000)
 
+/* OTP key storage and fuse strap array */
+#define NPCM7XX_OTP1_BA         (0xf0189000)
+#define NPCM7XX_OTP2_BA         (0xf018a000)
+
 /* Core system modules. */
 #define NPCM7XX_L2C_BA          (0xf03fc000)
 #define NPCM7XX_CPUP_BA         (0xf03fe000)
@@ -144,6 +148,20 @@ void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
     arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo);
 }
 
+static void npcm7xx_init_fuses(NPCM7xxState *s)
+{
+    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
+    uint32_t value;
+
+    /*
+     * The initial mask of disabled modules indicates the chip derivative (e.g.
+     * NPCM750 or NPCM730).
+     */
+    value = tswap32(nc->disabled_modules);
+    npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE,
+                            sizeof(value));
+}
+
 static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n)
 {
     return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n);
@@ -164,6 +182,10 @@ static void npcm7xx_init(Object *obj)
     object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
                               "power-on-straps");
     object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM7XX_CLK);
+    object_initialize_child(obj, "otp1", &s->key_storage,
+                            TYPE_NPCM7XX_KEY_STORAGE);
+    object_initialize_child(obj, "otp2", &s->fuse_array,
+                            TYPE_NPCM7XX_FUSE_ARRAY);
 
     for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
         object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
@@ -232,6 +254,13 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
     sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM7XX_CLK_BA);
 
+    /* OTP key storage and fuse strap array. Cannot fail. */
+    sysbus_realize(SYS_BUS_DEVICE(&s->key_storage), &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->key_storage), 0, NPCM7XX_OTP1_BA);
+    sysbus_realize(SYS_BUS_DEVICE(&s->fuse_array), &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM7XX_OTP2_BA);
+    npcm7xx_init_fuses(s);
+
     /* Timer Modules (TIM). Cannot fail. */
     QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
     for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
diff --git a/hw/nvram/npcm7xx_otp.c b/hw/nvram/npcm7xx_otp.c
new file mode 100644
index 0000000000..ba4188ada8
--- /dev/null
+++ b/hw/nvram/npcm7xx_otp.c
@@ -0,0 +1,439 @@
+/*
+ * Nuvoton NPCM7xx OTP (Fuse Array) Interface
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/nvram/npcm7xx_otp.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+
+/* Each module has 4 KiB of register space. Only a fraction of it is used. */
+#define NPCM7XX_OTP_REGS_SIZE (4 * KiB)
+
+/* 32-bit register indices. */
+typedef enum NPCM7xxOTPRegister {
+    NPCM7XX_OTP_FST,
+    NPCM7XX_OTP_FADDR,
+    NPCM7XX_OTP_FDATA,
+    NPCM7XX_OTP_FCFG,
+    /* Offset 0x10 is FKEYIND in OTP1, FUSTRAP in OTP2 */
+    NPCM7XX_OTP_FKEYIND = 0x0010 / sizeof(uint32_t),
+    NPCM7XX_OTP_FUSTRAP = 0x0010 / sizeof(uint32_t),
+    NPCM7XX_OTP_FCTL,
+    NPCM7XX_OTP_REGS_END,
+} NPCM7xxOTPRegister;
+
+/* Register field definitions. */
+#define FST_RIEN BIT(2)
+#define FST_RDST BIT(1)
+#define FST_RDY BIT(0)
+#define FST_RO_MASK (FST_RDST | FST_RDY)
+
+#define FADDR_BYTEADDR(rv) extract32((rv), 0, 10)
+#define FADDR_BITPOS(rv) extract32((rv), 10, 3)
+
+#define FDATA_CLEAR 0x00000001
+
+#define FCFG_FDIS BIT(31)
+#define FCFG_FCFGLK_MASK 0x00ff0000
+
+#define FCTL_PROG_CMD1 0x00000001
+#define FCTL_PROG_CMD2 0xbf79e5d0
+#define FCTL_READ_CMD 0x00000002
+
+/**
+ * struct NPCM7xxOTPClass - OTP module class.
+ * @parent: System bus device class.
+ * @mmio_ops: MMIO register operations for this type of module.
+ *
+ * The two OTP modules (key-storage and fuse-array) have slightly different
+ * behavior, so we give them different MMIO register operations.
+ */
+struct NPCM7xxOTPClass {
+    SysBusDeviceClass parent;
+
+    const MemoryRegionOps *mmio_ops;
+};
+
+#define NPCM7XX_OTP_CLASS(klass) \
+    OBJECT_CLASS_CHECK(NPCM7xxOTPClass, (klass), TYPE_NPCM7XX_OTP)
+#define NPCM7XX_OTP_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(NPCM7xxOTPClass, (obj), TYPE_NPCM7XX_OTP)
+
+static uint8_t ecc_encode_nibble(uint8_t n)
+{
+    uint8_t result = n;
+
+    result |= (((n >> 0) & 1) ^ ((n >> 1) & 1)) << 4;
+    result |= (((n >> 2) & 1) ^ ((n >> 3) & 1)) << 5;
+    result |= (((n >> 0) & 1) ^ ((n >> 2) & 1)) << 6;
+    result |= (((n >> 1) & 1) ^ ((n >> 3) & 1)) << 7;
+
+    return result;
+}
+
+void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
+                             unsigned int offset, unsigned int len)
+{
+    const uint8_t *src = data;
+    uint8_t *dst = &s->array[offset];
+
+    while (len-- > 0) {
+        uint8_t c = *src++;
+
+        *dst++ = ecc_encode_nibble(extract8(c, 0, 4));
+        *dst++ = ecc_encode_nibble(extract8(c, 4, 4));
+    }
+}
+
+/* Common register read handler for both OTP classes. */
+static uint64_t npcm7xx_otp_read(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg)
+{
+    uint32_t value = 0;
+
+    switch (reg) {
+    case NPCM7XX_OTP_FST:
+    case NPCM7XX_OTP_FADDR:
+    case NPCM7XX_OTP_FDATA:
+    case NPCM7XX_OTP_FCFG:
+        value = s->regs[reg];
+        break;
+
+    case NPCM7XX_OTP_FCTL:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: read from write-only FCTL register\n",
+                      DEVICE(s)->canonical_path);
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: read from invalid offset 0x%zx\n",
+                      DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
+        break;
+    }
+
+    return value;
+}
+
+/* Read a byte from the OTP array into the data register. */
+static void npcm7xx_otp_read_array(NPCM7xxOTPState *s)
+{
+    uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
+
+    s->regs[NPCM7XX_OTP_FDATA] = s->array[FADDR_BYTEADDR(faddr)];
+    s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
+}
+
+/* Program a byte from the data register into the OTP array. */
+static void npcm7xx_otp_program_array(NPCM7xxOTPState *s)
+{
+    uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
+
+    /* Bits can only go 0->1, never 1->0. */
+    s->array[FADDR_BYTEADDR(faddr)] |= (1U << FADDR_BITPOS(faddr));
+    s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
+}
+
+/* Compute the next value of the FCFG register. */
+static uint32_t npcm7xx_otp_compute_fcfg(uint32_t cur_value, uint32_t new_value)
+{
+    uint32_t lock_mask;
+    uint32_t value;
+
+    /*
+     * FCFGLK holds sticky bits 16..23, indicating which bits in FPRGLK (8..15)
+     * and FRDLK (0..7) that are read-only.
+     */
+    lock_mask = (cur_value & FCFG_FCFGLK_MASK) >> 8;
+    lock_mask |= lock_mask >> 8;
+    /* FDIS and FCFGLK bits are sticky (write 1 to set; can't clear). */
+    value = cur_value & (FCFG_FDIS | FCFG_FCFGLK_MASK);
+    /* Preserve read-only bits in FPRGLK and FRDLK */
+    value |= cur_value & lock_mask;
+    /* Set all bits that aren't read-only. */
+    value |= new_value & ~lock_mask;
+
+    return value;
+}
+
+/* Common register write handler for both OTP classes. */
+static void npcm7xx_otp_write(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg,
+                              uint32_t value)
+{
+    switch (reg) {
+    case NPCM7XX_OTP_FST:
+        /* RDST is cleared by writing 1 to it. */
+        if (value & FST_RDST) {
+            s->regs[NPCM7XX_OTP_FST] &= ~FST_RDST;
+        }
+        /* Preserve read-only and write-one-to-clear bits */
+        value &= ~FST_RO_MASK;
+        value |= s->regs[NPCM7XX_OTP_FST] & FST_RO_MASK;
+        break;
+
+    case NPCM7XX_OTP_FADDR:
+        break;
+
+    case NPCM7XX_OTP_FDATA:
+        /*
+         * This register is cleared by writing a magic value to it; no other
+         * values can be written.
+         */
+        if (value == FDATA_CLEAR) {
+            value = 0;
+        } else {
+            value = s->regs[NPCM7XX_OTP_FDATA];
+        }
+        break;
+
+    case NPCM7XX_OTP_FCFG:
+        value = npcm7xx_otp_compute_fcfg(s->regs[NPCM7XX_OTP_FCFG], value);
+        break;
+
+    case NPCM7XX_OTP_FCTL:
+        switch (value) {
+        case FCTL_READ_CMD:
+            npcm7xx_otp_read_array(s);
+            break;
+
+        case FCTL_PROG_CMD1:
+            /*
+             * Programming requires writing two separate magic values to this
+             * register; this is the first one. Just store it so it can be
+             * verified later when the second magic value is received.
+             */
+            break;
+
+        case FCTL_PROG_CMD2:
+            /*
+             * Only initiate programming if we received the first half of the
+             * command immediately before this one.
+             */
+            if (s->regs[NPCM7XX_OTP_FCTL] == FCTL_PROG_CMD1) {
+                npcm7xx_otp_program_array(s);
+            }
+            break;
+
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: unrecognized FCNTL value 0x%" PRIx32 "\n",
+                          DEVICE(s)->canonical_path, value);
+            break;
+        }
+        if (value != FCTL_PROG_CMD1) {
+            value = 0;
+        }
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: write to invalid offset 0x%zx\n",
+                      DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
+        return;
+    }
+
+    s->regs[reg] = value;
+}
+
+/* Register read handler specific to the fuse array OTP module. */
+static uint64_t npcm7xx_fuse_array_read(void *opaque, hwaddr addr,
+                                        unsigned int size)
+{
+    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
+    NPCM7xxOTPState *s = opaque;
+    uint32_t value;
+
+    /*
+     * Only the Fuse Strap register needs special handling; all other registers
+     * work the same way for both kinds of OTP modules.
+     */
+    if (reg != NPCM7XX_OTP_FUSTRAP) {
+        value = npcm7xx_otp_read(s, reg);
+    } else {
+        /* FUSTRAP is stored as three copies in the OTP array. */
+        uint32_t fustrap[3];
+
+        memcpy(fustrap, &s->array[0], sizeof(fustrap));
+
+        /* Determine value by a majority vote on each bit. */
+        value = (fustrap[0] & fustrap[1]) | (fustrap[0] & fustrap[2]) |
+                (fustrap[1] & fustrap[2]);
+    }
+
+    return value;
+}
+
+/* Register write handler specific to the fuse array OTP module. */
+static void npcm7xx_fuse_array_write(void *opaque, hwaddr addr, uint64_t v,
+                                     unsigned int size)
+{
+    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
+    NPCM7xxOTPState *s = opaque;
+
+    /*
+     * The Fuse Strap register is read-only. Other registers are handled by
+     * common code.
+     */
+    if (reg != NPCM7XX_OTP_FUSTRAP) {
+        npcm7xx_otp_write(s, reg, v);
+    }
+}
+
+static const MemoryRegionOps npcm7xx_fuse_array_ops = {
+    .read       = npcm7xx_fuse_array_read,
+    .write      = npcm7xx_fuse_array_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid      = {
+        .min_access_size        = 4,
+        .max_access_size        = 4,
+        .unaligned              = false,
+    },
+};
+
+/* Register read handler specific to the key storage OTP module. */
+static uint64_t npcm7xx_key_storage_read(void *opaque, hwaddr addr,
+                                         unsigned int size)
+{
+    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
+    NPCM7xxOTPState *s = opaque;
+
+    /*
+     * Only the Fuse Key Index register needs special handling; all other
+     * registers work the same way for both kinds of OTP modules.
+     */
+    if (reg != NPCM7XX_OTP_FKEYIND) {
+        return npcm7xx_otp_read(s, reg);
+    }
+
+    qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
+
+    return s->regs[NPCM7XX_OTP_FKEYIND];
+}
+
+/* Register write handler specific to the key storage OTP module. */
+static void npcm7xx_key_storage_write(void *opaque, hwaddr addr, uint64_t v,
+                                      unsigned int size)
+{
+    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
+    NPCM7xxOTPState *s = opaque;
+
+    /*
+     * Only the Fuse Key Index register needs special handling; all other
+     * registers work the same way for both kinds of OTP modules.
+     */
+    if (reg != NPCM7XX_OTP_FKEYIND) {
+        npcm7xx_otp_write(s, reg, v);
+        return;
+    }
+
+    qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
+
+    s->regs[NPCM7XX_OTP_FKEYIND] = v;
+}
+
+static const MemoryRegionOps npcm7xx_key_storage_ops = {
+    .read       = npcm7xx_key_storage_read,
+    .write      = npcm7xx_key_storage_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid      = {
+        .min_access_size        = 4,
+        .max_access_size        = 4,
+        .unaligned              = false,
+    },
+};
+
+static void npcm7xx_otp_enter_reset(Object *obj, ResetType type)
+{
+    NPCM7xxOTPState *s = NPCM7XX_OTP(obj);
+
+    memset(s->regs, 0, sizeof(s->regs));
+
+    s->regs[NPCM7XX_OTP_FST] = 0x00000001;
+    s->regs[NPCM7XX_OTP_FCFG] = 0x20000000;
+}
+
+static void npcm7xx_otp_realize(DeviceState *dev, Error **errp)
+{
+    NPCM7xxOTPClass *oc = NPCM7XX_OTP_GET_CLASS(dev);
+    NPCM7xxOTPState *s = NPCM7XX_OTP(dev);
+    SysBusDevice *sbd = &s->parent;
+
+    memset(s->array, 0, sizeof(s->array));
+
+    memory_region_init_io(&s->mmio, OBJECT(s), oc->mmio_ops, s, "regs",
+                          NPCM7XX_OTP_REGS_SIZE);
+    sysbus_init_mmio(sbd, &s->mmio);
+}
+
+static const VMStateDescription vmstate_npcm7xx_otp = {
+    .name = "npcm7xx-otp",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, NPCM7xxOTPState, NPCM7XX_OTP_NR_REGS),
+        VMSTATE_UINT8_ARRAY(array, NPCM7xxOTPState, NPCM7XX_OTP_ARRAY_BYTES),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static void npcm7xx_otp_class_init(ObjectClass *klass, void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    QEMU_BUILD_BUG_ON(NPCM7XX_OTP_REGS_END > NPCM7XX_OTP_NR_REGS);
+
+    dc->realize = npcm7xx_otp_realize;
+    dc->vmsd = &vmstate_npcm7xx_otp;
+    rc->phases.enter = npcm7xx_otp_enter_reset;
+}
+
+static void npcm7xx_key_storage_class_init(ObjectClass *klass, void *data)
+{
+    NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
+
+    oc->mmio_ops = &npcm7xx_key_storage_ops;
+}
+
+static void npcm7xx_fuse_array_class_init(ObjectClass *klass, void *data)
+{
+    NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
+
+    oc->mmio_ops = &npcm7xx_fuse_array_ops;
+}
+
+static const TypeInfo npcm7xx_otp_types[] = {
+    {
+        .name = TYPE_NPCM7XX_OTP,
+        .parent = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(NPCM7xxOTPState),
+        .class_init = npcm7xx_otp_class_init,
+        .abstract = true,
+    },
+    {
+        .name = TYPE_NPCM7XX_KEY_STORAGE,
+        .parent = TYPE_NPCM7XX_OTP,
+        .class_init = npcm7xx_key_storage_class_init,
+    },
+    {
+        .name = TYPE_NPCM7XX_FUSE_ARRAY,
+        .parent = TYPE_NPCM7XX_OTP,
+        .class_init = npcm7xx_fuse_array_class_init,
+    },
+};
+DEFINE_TYPES(npcm7xx_otp_types);
diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build
index ba214558ac..1f2ed013b2 100644
--- a/hw/nvram/meson.build
+++ b/hw/nvram/meson.build
@@ -4,6 +4,7 @@ softmmu_ss.add(when: 'CONFIG_DS1225Y', if_true: files('ds1225y.c'))
 softmmu_ss.add(when: 'CONFIG_NMC93XX_EEPROM', if_true: files('eeprom93xx.c'))
 softmmu_ss.add(when: 'CONFIG_AT24C', if_true: files('eeprom_at24c.c'))
 softmmu_ss.add(when: 'CONFIG_MAC_NVRAM', if_true: files('mac_nvram.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_otp.c'))
 softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_nvm.c'))
 
 specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_nvram.c'))
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 09/14] hw/mem: Stubbed out NPCM7xx Memory Controller model
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (7 preceding siblings ...)
  2020-08-25  0:17 ` [PATCH v8 08/14] hw/nvram: NPCM7xx OTP device model Havard Skinnemoen via
@ 2020-08-25  0:17 ` Havard Skinnemoen via
  2020-08-25  0:17 ` [PATCH v8 10/14] hw/ssi: NPCM7xx Flash Interface Unit device model Havard Skinnemoen via
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:17 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Cédric Le Goater

This just implements the bare minimum to cause the boot block to skip
memory initialization.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/arm/npcm7xx.h    |  2 +
 include/hw/mem/npcm7xx_mc.h | 36 ++++++++++++++++
 hw/arm/npcm7xx.c            |  6 +++
 hw/mem/npcm7xx_mc.c         | 84 +++++++++++++++++++++++++++++++++++++
 hw/mem/meson.build          |  1 +
 5 files changed, 129 insertions(+)
 create mode 100644 include/hw/mem/npcm7xx_mc.h
 create mode 100644 hw/mem/npcm7xx_mc.c

diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
index 5816a07a72..9fa84a0702 100644
--- a/include/hw/arm/npcm7xx.h
+++ b/include/hw/arm/npcm7xx.h
@@ -18,6 +18,7 @@
 
 #include "hw/boards.h"
 #include "hw/cpu/a9mpcore.h"
+#include "hw/mem/npcm7xx_mc.h"
 #include "hw/misc/npcm7xx_clk.h"
 #include "hw/misc/npcm7xx_gcr.h"
 #include "hw/nvram/npcm7xx_otp.h"
@@ -71,6 +72,7 @@ typedef struct NPCM7xxState {
     NPCM7xxTimerCtrlState tim[3];
     NPCM7xxOTPState     key_storage;
     NPCM7xxOTPState     fuse_array;
+    NPCM7xxMCState      mc;
 } NPCM7xxState;
 
 #define TYPE_NPCM7XX    "npcm7xx"
diff --git a/include/hw/mem/npcm7xx_mc.h b/include/hw/mem/npcm7xx_mc.h
new file mode 100644
index 0000000000..7ed38be243
--- /dev/null
+++ b/include/hw/mem/npcm7xx_mc.h
@@ -0,0 +1,36 @@
+/*
+ * Nuvoton NPCM7xx Memory Controller stub
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+#ifndef NPCM7XX_MC_H
+#define NPCM7XX_MC_H
+
+#include "exec/memory.h"
+#include "hw/sysbus.h"
+
+/**
+ * struct NPCM7xxMCState - Device state for the memory controller.
+ * @parent: System bus device.
+ * @mmio: Memory region through which registers are accessed.
+ */
+typedef struct NPCM7xxMCState {
+    SysBusDevice parent;
+
+    MemoryRegion mmio;
+} NPCM7xxMCState;
+
+#define TYPE_NPCM7XX_MC "npcm7xx-mc"
+#define NPCM7XX_MC(obj) OBJECT_CHECK(NPCM7xxMCState, (obj), TYPE_NPCM7XX_MC)
+
+#endif /* NPCM7XX_MC_H */
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 9166002598..6bb1693833 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -43,6 +43,7 @@
 #define NPCM7XX_CPUP_BA         (0xf03fe000)
 #define NPCM7XX_GCR_BA          (0xf0800000)
 #define NPCM7XX_CLK_BA          (0xf0801000)
+#define NPCM7XX_MC_BA           (0xf0824000)
 
 /* Internal AHB SRAM */
 #define NPCM7XX_RAM3_BA         (0xc0008000)
@@ -186,6 +187,7 @@ static void npcm7xx_init(Object *obj)
                             TYPE_NPCM7XX_KEY_STORAGE);
     object_initialize_child(obj, "otp2", &s->fuse_array,
                             TYPE_NPCM7XX_FUSE_ARRAY);
+    object_initialize_child(obj, "mc", &s->mc, TYPE_NPCM7XX_MC);
 
     for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
         object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
@@ -261,6 +263,10 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM7XX_OTP2_BA);
     npcm7xx_init_fuses(s);
 
+    /* Fake Memory Controller (MC). Cannot fail. */
+    sysbus_realize(SYS_BUS_DEVICE(&s->mc), &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->mc), 0, NPCM7XX_MC_BA);
+
     /* Timer Modules (TIM). Cannot fail. */
     QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
     for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
diff --git a/hw/mem/npcm7xx_mc.c b/hw/mem/npcm7xx_mc.c
new file mode 100644
index 0000000000..0435d06ab4
--- /dev/null
+++ b/hw/mem/npcm7xx_mc.c
@@ -0,0 +1,84 @@
+/*
+ * Nuvoton NPCM7xx Memory Controller stub
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/mem/npcm7xx_mc.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+
+#define NPCM7XX_MC_REGS_SIZE (4 * KiB)
+
+static uint64_t npcm7xx_mc_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    /*
+     * If bits 8..11 @ offset 0 are not zero, the boot block thinks the memory
+     * controller has already been initialized and will skip DDR training.
+     */
+    if (addr == 0) {
+        return 0x100;
+    }
+
+    qemu_log_mask(LOG_UNIMP, "%s: mostly unimplemented\n", __func__);
+
+    return 0;
+}
+
+static void npcm7xx_mc_write(void *opaque, hwaddr addr, uint64_t v,
+                             unsigned int size)
+{
+    qemu_log_mask(LOG_UNIMP, "%s: mostly unimplemented\n", __func__);
+}
+
+static const MemoryRegionOps npcm7xx_mc_ops = {
+    .read = npcm7xx_mc_read,
+    .write = npcm7xx_mc_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false,
+    },
+};
+
+static void npcm7xx_mc_realize(DeviceState *dev, Error **errp)
+{
+    NPCM7xxMCState *s = NPCM7XX_MC(dev);
+
+    memory_region_init_io(&s->mmio, OBJECT(s), &npcm7xx_mc_ops, s, "regs",
+                          NPCM7XX_MC_REGS_SIZE);
+    sysbus_init_mmio(&s->parent, &s->mmio);
+}
+
+static void npcm7xx_mc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc = "NPCM7xx Memory Controller stub";
+    dc->realize = npcm7xx_mc_realize;
+}
+
+static const TypeInfo npcm7xx_mc_types[] = {
+    {
+        .name = TYPE_NPCM7XX_MC,
+        .parent = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(NPCM7xxMCState),
+        .class_init = npcm7xx_mc_class_init,
+    },
+};
+DEFINE_TYPES(npcm7xx_mc_types);
diff --git a/hw/mem/meson.build b/hw/mem/meson.build
index ba424622bb..0d22f2b572 100644
--- a/hw/mem/meson.build
+++ b/hw/mem/meson.build
@@ -1,6 +1,7 @@
 mem_ss = ss.source_set()
 mem_ss.add(files('memory-device.c'))
 mem_ss.add(when: 'CONFIG_DIMM', if_true: files('pc-dimm.c'))
+mem_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_mc.c'))
 mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c'))
 
 softmmu_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss)
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 10/14] hw/ssi: NPCM7xx Flash Interface Unit device model
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (8 preceding siblings ...)
  2020-08-25  0:17 ` [PATCH v8 09/14] hw/mem: Stubbed out NPCM7xx Memory Controller model Havard Skinnemoen via
@ 2020-08-25  0:17 ` Havard Skinnemoen via
  2020-08-25  0:17 ` [PATCH v8 11/14] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj Havard Skinnemoen via
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:17 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Cédric Le Goater

This implements a device model for the NPCM7xx SPI flash controller.

Direct reads and writes, and user-mode transactions have been tested in
various modes. Protection features are not implemented yet.

All the FIU instances are available in the SoC's address space,
regardless of whether or not they're connected to actual flash chips.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/arm/npcm7xx.h     |   2 +
 include/hw/ssi/npcm7xx_fiu.h |  73 +++++
 hw/arm/npcm7xx.c             |  58 ++++
 hw/ssi/npcm7xx_fiu.c         | 572 +++++++++++++++++++++++++++++++++++
 hw/arm/Kconfig               |   1 +
 hw/ssi/meson.build           |   1 +
 hw/ssi/trace-events          |  11 +
 7 files changed, 718 insertions(+)
 create mode 100644 include/hw/ssi/npcm7xx_fiu.h
 create mode 100644 hw/ssi/npcm7xx_fiu.c

diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
index 9fa84a0702..78d0d78c52 100644
--- a/include/hw/arm/npcm7xx.h
+++ b/include/hw/arm/npcm7xx.h
@@ -23,6 +23,7 @@
 #include "hw/misc/npcm7xx_gcr.h"
 #include "hw/nvram/npcm7xx_otp.h"
 #include "hw/timer/npcm7xx_timer.h"
+#include "hw/ssi/npcm7xx_fiu.h"
 #include "target/arm/cpu.h"
 
 #define NPCM7XX_MAX_NUM_CPUS    (2)
@@ -73,6 +74,7 @@ typedef struct NPCM7xxState {
     NPCM7xxOTPState     key_storage;
     NPCM7xxOTPState     fuse_array;
     NPCM7xxMCState      mc;
+    NPCM7xxFIUState     fiu[2];
 } NPCM7xxState;
 
 #define TYPE_NPCM7XX    "npcm7xx"
diff --git a/include/hw/ssi/npcm7xx_fiu.h b/include/hw/ssi/npcm7xx_fiu.h
new file mode 100644
index 0000000000..a3a1704289
--- /dev/null
+++ b/include/hw/ssi/npcm7xx_fiu.h
@@ -0,0 +1,73 @@
+/*
+ * Nuvoton NPCM7xx Flash Interface Unit (FIU)
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+#ifndef NPCM7XX_FIU_H
+#define NPCM7XX_FIU_H
+
+#include "hw/ssi/ssi.h"
+#include "hw/sysbus.h"
+
+/*
+ * Number of registers in our device state structure. Don't change this without
+ * incrementing the version_id in the vmstate.
+ */
+#define NPCM7XX_FIU_NR_REGS (0x7c / sizeof(uint32_t))
+
+typedef struct NPCM7xxFIUState NPCM7xxFIUState;
+
+/**
+ * struct NPCM7xxFIUFlash - Per-chipselect flash controller state.
+ * @direct_access: Memory region for direct flash access.
+ * @fiu: Pointer to flash controller shared state.
+ */
+typedef struct NPCM7xxFIUFlash {
+    MemoryRegion direct_access;
+    NPCM7xxFIUState *fiu;
+} NPCM7xxFIUFlash;
+
+/**
+ * NPCM7xxFIUState - Device state for one Flash Interface Unit.
+ * @parent: System bus device.
+ * @mmio: Memory region for register access.
+ * @cs_count: Number of flash chips that may be connected to this module.
+ * @active_cs: Currently active chip select, or -1 if no chip is selected.
+ * @cs_lines: GPIO lines that may be wired to flash chips.
+ * @flash: Array of @cs_count per-flash-chip state objects.
+ * @spi: The SPI bus mastered by this controller.
+ * @regs: Register contents.
+ *
+ * Each FIU has a shared bank of registers, and controls up to four chip
+ * selects. Each chip select has a dedicated memory region which may be used to
+ * read and write the flash connected to that chip select as if it were memory.
+ */
+struct NPCM7xxFIUState {
+    SysBusDevice parent;
+
+    MemoryRegion mmio;
+
+    int32_t cs_count;
+    int32_t active_cs;
+    qemu_irq *cs_lines;
+    NPCM7xxFIUFlash *flash;
+
+    SSIBus *spi;
+
+    uint32_t regs[NPCM7XX_FIU_NR_REGS];
+};
+
+#define TYPE_NPCM7XX_FIU "npcm7xx-fiu"
+#define NPCM7XX_FIU(obj) OBJECT_CHECK(NPCM7xxFIUState, (obj), TYPE_NPCM7XX_FIU)
+
+#endif /* NPCM7XX_FIU_H */
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 6bb1693833..7884b2b03d 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -99,6 +99,39 @@ static const hwaddr npcm7xx_uart_addr[] = {
     0xf0004000,
 };
 
+/* Direct memory-mapped access to SPI0 CS0-1. */
+static const hwaddr npcm7xx_fiu0_flash_addr[] = {
+    0x80000000, /* CS0 */
+    0x88000000, /* CS1 */
+};
+
+/* Direct memory-mapped access to SPI3 CS0-3. */
+static const hwaddr npcm7xx_fiu3_flash_addr[] = {
+    0xa0000000, /* CS0 */
+    0xa8000000, /* CS1 */
+    0xb0000000, /* CS2 */
+    0xb8000000, /* CS3 */
+};
+
+static const struct {
+    const char *name;
+    hwaddr regs_addr;
+    int cs_count;
+    const hwaddr *flash_addr;
+} npcm7xx_fiu[] = {
+    {
+        .name = "fiu0",
+        .regs_addr = 0xfb000000,
+        .cs_count = ARRAY_SIZE(npcm7xx_fiu0_flash_addr),
+        .flash_addr = npcm7xx_fiu0_flash_addr,
+    }, {
+        .name = "fiu3",
+        .regs_addr = 0xc0000000,
+        .cs_count = ARRAY_SIZE(npcm7xx_fiu3_flash_addr),
+        .flash_addr = npcm7xx_fiu3_flash_addr,
+    },
+};
+
 static void npcm7xx_write_secondary_boot(ARMCPU *cpu,
                                          const struct arm_boot_info *info)
 {
@@ -192,6 +225,12 @@ static void npcm7xx_init(Object *obj)
     for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
         object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
     }
+
+    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu));
+    for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
+        object_initialize_child(obj, npcm7xx_fiu[i].name, &s->fiu[i],
+                                TYPE_NPCM7XX_FIU);
+    }
 }
 
 static void npcm7xx_realize(DeviceState *dev, Error **errp)
@@ -291,6 +330,25 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
                        serial_hd(i), DEVICE_LITTLE_ENDIAN);
     }
 
+    /*
+     * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects
+     * specified, but this is a programming error.
+     */
+    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu));
+    for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
+        SysBusDevice *sbd = SYS_BUS_DEVICE(&s->fiu[i]);
+        int j;
+
+        object_property_set_int(OBJECT(sbd), "cs-count",
+                                npcm7xx_fiu[i].cs_count, &error_abort);
+        sysbus_realize(sbd, &error_abort);
+
+        sysbus_mmio_map(sbd, 0, npcm7xx_fiu[i].regs_addr);
+        for (j = 0; j < npcm7xx_fiu[i].cs_count; j++) {
+            sysbus_mmio_map(sbd, j + 1, npcm7xx_fiu[i].flash_addr[j]);
+        }
+    }
+
     /* RAM2 (SRAM) */
     memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
                            NPCM7XX_RAM2_SZ, &error_abort);
diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c
new file mode 100644
index 0000000000..104e8f2b96
--- /dev/null
+++ b/hw/ssi/npcm7xx_fiu.c
@@ -0,0 +1,572 @@
+/*
+ * Nuvoton NPCM7xx Flash Interface Unit (FIU)
+ *
+ * Copyright 2020 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/ssi/npcm7xx_fiu.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+
+#include "trace.h"
+
+/* Up to 128 MiB of flash may be accessed directly as memory. */
+#define NPCM7XX_FIU_FLASH_WINDOW_SIZE (128 * MiB)
+
+/* Each module has 4 KiB of register space. Only a fraction of it is used. */
+#define NPCM7XX_FIU_CTRL_REGS_SIZE (4 * KiB)
+
+/* 32-bit FIU register indices. */
+enum NPCM7xxFIURegister {
+    NPCM7XX_FIU_DRD_CFG,
+    NPCM7XX_FIU_DWR_CFG,
+    NPCM7XX_FIU_UMA_CFG,
+    NPCM7XX_FIU_UMA_CTS,
+    NPCM7XX_FIU_UMA_CMD,
+    NPCM7XX_FIU_UMA_ADDR,
+    NPCM7XX_FIU_PRT_CFG,
+    NPCM7XX_FIU_UMA_DW0 = 0x0020 / sizeof(uint32_t),
+    NPCM7XX_FIU_UMA_DW1,
+    NPCM7XX_FIU_UMA_DW2,
+    NPCM7XX_FIU_UMA_DW3,
+    NPCM7XX_FIU_UMA_DR0,
+    NPCM7XX_FIU_UMA_DR1,
+    NPCM7XX_FIU_UMA_DR2,
+    NPCM7XX_FIU_UMA_DR3,
+    NPCM7XX_FIU_PRT_CMD0,
+    NPCM7XX_FIU_PRT_CMD1,
+    NPCM7XX_FIU_PRT_CMD2,
+    NPCM7XX_FIU_PRT_CMD3,
+    NPCM7XX_FIU_PRT_CMD4,
+    NPCM7XX_FIU_PRT_CMD5,
+    NPCM7XX_FIU_PRT_CMD6,
+    NPCM7XX_FIU_PRT_CMD7,
+    NPCM7XX_FIU_PRT_CMD8,
+    NPCM7XX_FIU_PRT_CMD9,
+    NPCM7XX_FIU_CFG = 0x78 / sizeof(uint32_t),
+    NPCM7XX_FIU_REGS_END,
+};
+
+/* FIU_{DRD,DWR,UMA,PTR}_CFG cannot be written when this bit is set. */
+#define NPCM7XX_FIU_CFG_LCK BIT(31)
+
+/* Direct Read configuration register fields. */
+#define FIU_DRD_CFG_ADDSIZ(rv) extract32(rv, 16, 2)
+#define FIU_ADDSIZ_3BYTES 0
+#define FIU_ADDSIZ_4BYTES 1
+#define FIU_DRD_CFG_DBW(rv) extract32(rv, 12, 2)
+#define FIU_DRD_CFG_ACCTYPE(rv) extract32(rv, 8, 2)
+#define FIU_DRD_CFG_RDCMD(rv) extract32(rv, 0, 8)
+
+/* Direct Write configuration register fields. */
+#define FIU_DWR_CFG_ADDSIZ(rv) extract32(rv, 16, 2)
+#define FIU_DWR_CFG_WRCMD(rv) extract32(rv, 0, 8)
+
+/* User-Mode Access register fields. */
+
+/* Command Mode Lock and the bits protected by it. */
+#define FIU_UMA_CFG_CMMLCK BIT(30)
+#define FIU_UMA_CFG_CMMLCK_MASK 0x00000403
+
+#define FIU_UMA_CFG_RDATSIZ(rv) extract32(rv, 24, 5)
+#define FIU_UMA_CFG_DBSIZ(rv) extract32(rv, 21, 3)
+#define FIU_UMA_CFG_WDATSIZ(rv) extract32(rv, 16, 5)
+#define FIU_UMA_CFG_ADDSIZ(rv) extract32(rv, 11, 3)
+#define FIU_UMA_CFG_CMDSIZ(rv) extract32(rv, 10, 1)
+#define FIU_UMA_CFG_DBPCK(rv) extract32(rv, 6, 2)
+
+#define FIU_UMA_CTS_RDYIE BIT(25)
+#define FIU_UMA_CTS_RDYST BIT(24)
+#define FIU_UMA_CTS_SW_CS BIT(16)
+#define FIU_UMA_CTS_DEV_NUM(rv) extract32(rv, 8, 2)
+#define FIU_UMA_CTS_EXEC_DONE BIT(0)
+
+/*
+ * Returns the index of flash in the fiu->flash array. This corresponds to the
+ * chip select ID of the flash.
+ */
+static int npcm7xx_fiu_cs_index(NPCM7xxFIUState *fiu, NPCM7xxFIUFlash *flash)
+{
+    int index = flash - fiu->flash;
+
+    g_assert(index >= 0 && index < fiu->cs_count);
+
+    return index;
+}
+
+/* Assert the chip select specified in the UMA Control/Status Register. */
+static void npcm7xx_fiu_select(NPCM7xxFIUState *s, int cs_id)
+{
+    trace_npcm7xx_fiu_select(DEVICE(s)->canonical_path, cs_id);
+
+    if (cs_id < s->cs_count) {
+        qemu_irq_lower(s->cs_lines[cs_id]);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: UMA to CS%d; this module has only %d chip selects",
+                      DEVICE(s)->canonical_path, cs_id, s->cs_count);
+        cs_id = -1;
+    }
+
+    s->active_cs = cs_id;
+}
+
+/* Deassert the currently active chip select. */
+static void npcm7xx_fiu_deselect(NPCM7xxFIUState *s)
+{
+    if (s->active_cs < 0) {
+        return;
+    }
+
+    trace_npcm7xx_fiu_deselect(DEVICE(s)->canonical_path, s->active_cs);
+
+    qemu_irq_raise(s->cs_lines[s->active_cs]);
+    s->active_cs = -1;
+}
+
+/* Direct flash memory read handler. */
+static uint64_t npcm7xx_fiu_flash_read(void *opaque, hwaddr addr,
+                                       unsigned int size)
+{
+    NPCM7xxFIUFlash *f = opaque;
+    NPCM7xxFIUState *fiu = f->fiu;
+    uint64_t value = 0;
+    uint32_t drd_cfg;
+    int dummy_cycles;
+    int i;
+
+    if (fiu->active_cs != -1) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: direct flash read with CS%d already active",
+                      DEVICE(fiu)->canonical_path, fiu->active_cs);
+    }
+
+    npcm7xx_fiu_select(fiu, npcm7xx_fiu_cs_index(fiu, f));
+
+    drd_cfg = fiu->regs[NPCM7XX_FIU_DRD_CFG];
+    ssi_transfer(fiu->spi, FIU_DRD_CFG_RDCMD(drd_cfg));
+
+    switch (FIU_DRD_CFG_ADDSIZ(drd_cfg)) {
+    case FIU_ADDSIZ_4BYTES:
+        ssi_transfer(fiu->spi, extract32(addr, 24, 8));
+        /* fall through */
+    case FIU_ADDSIZ_3BYTES:
+        ssi_transfer(fiu->spi, extract32(addr, 16, 8));
+        ssi_transfer(fiu->spi, extract32(addr, 8, 8));
+        ssi_transfer(fiu->spi, extract32(addr, 0, 8));
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad address size %d\n",
+                      DEVICE(fiu)->canonical_path, FIU_DRD_CFG_ADDSIZ(drd_cfg));
+        break;
+    }
+
+    /* Flash chip model expects one transfer per dummy bit, not byte */
+    dummy_cycles =
+        (FIU_DRD_CFG_DBW(drd_cfg) * 8) >> FIU_DRD_CFG_ACCTYPE(drd_cfg);
+    for (i = 0; i < dummy_cycles; i++) {
+        ssi_transfer(fiu->spi, 0);
+    }
+
+    for (i = 0; i < size; i++) {
+        value = deposit64(value, 8 * i, 8, ssi_transfer(fiu->spi, 0));
+    }
+
+    trace_npcm7xx_fiu_flash_read(DEVICE(fiu)->canonical_path, fiu->active_cs,
+                                 addr, size, value);
+
+    npcm7xx_fiu_deselect(fiu);
+
+    return value;
+}
+
+/* Direct flash memory write handler. */
+static void npcm7xx_fiu_flash_write(void *opaque, hwaddr addr, uint64_t v,
+                                    unsigned int size)
+{
+    NPCM7xxFIUFlash *f = opaque;
+    NPCM7xxFIUState *fiu = f->fiu;
+    uint32_t dwr_cfg;
+    int cs_id;
+    int i;
+
+    if (fiu->active_cs != -1) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: direct flash write with CS%d already active",
+                      DEVICE(fiu)->canonical_path, fiu->active_cs);
+    }
+
+    cs_id = npcm7xx_fiu_cs_index(fiu, f);
+    trace_npcm7xx_fiu_flash_write(DEVICE(fiu)->canonical_path, cs_id, addr,
+                                  size, v);
+    npcm7xx_fiu_select(fiu, cs_id);
+
+    dwr_cfg = fiu->regs[NPCM7XX_FIU_DWR_CFG];
+    ssi_transfer(fiu->spi, FIU_DWR_CFG_WRCMD(dwr_cfg));
+
+    switch (FIU_DWR_CFG_ADDSIZ(dwr_cfg)) {
+    case FIU_ADDSIZ_4BYTES:
+        ssi_transfer(fiu->spi, extract32(addr, 24, 8));
+        /* fall through */
+    case FIU_ADDSIZ_3BYTES:
+        ssi_transfer(fiu->spi, extract32(addr, 16, 8));
+        ssi_transfer(fiu->spi, extract32(addr, 8, 8));
+        ssi_transfer(fiu->spi, extract32(addr, 0, 8));
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad address size %d\n",
+                      DEVICE(fiu)->canonical_path, FIU_DWR_CFG_ADDSIZ(dwr_cfg));
+        break;
+    }
+
+    for (i = 0; i < size; i++) {
+        ssi_transfer(fiu->spi, extract64(v, i * 8, 8));
+    }
+
+    npcm7xx_fiu_deselect(fiu);
+}
+
+static const MemoryRegionOps npcm7xx_fiu_flash_ops = {
+    .read = npcm7xx_fiu_flash_read,
+    .write = npcm7xx_fiu_flash_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+        .unaligned = true,
+    },
+};
+
+/* Control register read handler. */
+static uint64_t npcm7xx_fiu_ctrl_read(void *opaque, hwaddr addr,
+                                      unsigned int size)
+{
+    hwaddr reg = addr / sizeof(uint32_t);
+    NPCM7xxFIUState *s = opaque;
+    uint32_t value;
+
+    if (reg < NPCM7XX_FIU_NR_REGS) {
+        value = s->regs[reg];
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: read from invalid offset 0x%" PRIx64 "\n",
+                      DEVICE(s)->canonical_path, addr);
+        value = 0;
+    }
+
+    trace_npcm7xx_fiu_ctrl_read(DEVICE(s)->canonical_path, addr, value);
+
+    return value;
+}
+
+/* Send the specified number of address bytes from the UMA address register. */
+static void send_address(SSIBus *spi, unsigned int addsiz, uint32_t addr)
+{
+    switch (addsiz) {
+    case 4:
+        ssi_transfer(spi, extract32(addr, 24, 8));
+        /* fall through */
+    case 3:
+        ssi_transfer(spi, extract32(addr, 16, 8));
+        /* fall through */
+    case 2:
+        ssi_transfer(spi, extract32(addr, 8, 8));
+        /* fall through */
+    case 1:
+        ssi_transfer(spi, extract32(addr, 0, 8));
+        /* fall through */
+    case 0:
+        break;
+    }
+}
+
+/* Send the number of dummy bits specified in the UMA config register. */
+static void send_dummy_bits(SSIBus *spi, uint32_t uma_cfg, uint32_t uma_cmd)
+{
+    unsigned int bits_per_clock = 1U << FIU_UMA_CFG_DBPCK(uma_cfg);
+    unsigned int i;
+
+    for (i = 0; i < FIU_UMA_CFG_DBSIZ(uma_cfg); i++) {
+        /* Use bytes 0 and 1 first, then keep repeating byte 2 */
+        unsigned int field = (i < 2) ? ((i + 1) * 8) : 24;
+        unsigned int j;
+
+        for (j = 0; j < 8; j += bits_per_clock) {
+            ssi_transfer(spi, extract32(uma_cmd, field + j, bits_per_clock));
+        }
+    }
+}
+
+/* Perform a User-Mode Access transaction. */
+static void npcm7xx_fiu_uma_transaction(NPCM7xxFIUState *s)
+{
+    uint32_t uma_cts = s->regs[NPCM7XX_FIU_UMA_CTS];
+    uint32_t uma_cfg;
+    unsigned int i;
+
+    /* SW_CS means the CS is already forced low, so don't touch it. */
+    if (uma_cts & FIU_UMA_CTS_SW_CS) {
+        int cs_id = FIU_UMA_CTS_DEV_NUM(s->regs[NPCM7XX_FIU_UMA_CTS]);
+        npcm7xx_fiu_select(s, cs_id);
+    }
+
+    /* Send command, if present. */
+    uma_cfg = s->regs[NPCM7XX_FIU_UMA_CFG];
+    if (FIU_UMA_CFG_CMDSIZ(uma_cfg) > 0) {
+        ssi_transfer(s->spi, extract32(s->regs[NPCM7XX_FIU_UMA_CMD], 0, 8));
+    }
+
+    /* Send address, if present. */
+    send_address(s->spi, FIU_UMA_CFG_ADDSIZ(uma_cfg),
+                 s->regs[NPCM7XX_FIU_UMA_ADDR]);
+
+    /* Write data, if present. */
+    for (i = 0; i < FIU_UMA_CFG_WDATSIZ(uma_cfg); i++) {
+        unsigned int reg =
+            (i < 16) ? (NPCM7XX_FIU_UMA_DW0 + i / 4) : NPCM7XX_FIU_UMA_DW3;
+        unsigned int field = (i % 4) * 8;
+
+        ssi_transfer(s->spi, extract32(s->regs[reg], field, 8));
+    }
+
+    /* Send dummy bits, if present. */
+    send_dummy_bits(s->spi, uma_cfg, s->regs[NPCM7XX_FIU_UMA_CMD]);
+
+    /* Read data, if present. */
+    for (i = 0; i < FIU_UMA_CFG_RDATSIZ(uma_cfg); i++) {
+        unsigned int reg = NPCM7XX_FIU_UMA_DR0 + i / 4;
+        unsigned int field = (i % 4) * 8;
+        uint8_t c;
+
+        c = ssi_transfer(s->spi, 0);
+        if (reg <= NPCM7XX_FIU_UMA_DR3) {
+            s->regs[reg] = deposit32(s->regs[reg], field, 8, c);
+        }
+    }
+
+    /* Again, don't touch CS if the user is forcing it low. */
+    if (uma_cts & FIU_UMA_CTS_SW_CS) {
+        npcm7xx_fiu_deselect(s);
+    }
+
+    /* RDYST means a command has completed since it was cleared. */
+    s->regs[NPCM7XX_FIU_UMA_CTS] |= FIU_UMA_CTS_RDYST;
+    /* EXEC_DONE means Execute Command / Not Done, so clear it here. */
+    s->regs[NPCM7XX_FIU_UMA_CTS] &= ~FIU_UMA_CTS_EXEC_DONE;
+}
+
+/* Control register write handler. */
+static void npcm7xx_fiu_ctrl_write(void *opaque, hwaddr addr, uint64_t v,
+                                   unsigned int size)
+{
+    hwaddr reg = addr / sizeof(uint32_t);
+    NPCM7xxFIUState *s = opaque;
+    uint32_t value = v;
+
+    trace_npcm7xx_fiu_ctrl_write(DEVICE(s)->canonical_path, addr, value);
+
+    switch (reg) {
+    case NPCM7XX_FIU_UMA_CFG:
+        if (s->regs[reg] & FIU_UMA_CFG_CMMLCK) {
+            value &= ~FIU_UMA_CFG_CMMLCK_MASK;
+            value |= (s->regs[reg] & FIU_UMA_CFG_CMMLCK_MASK);
+        }
+        /* fall through */
+    case NPCM7XX_FIU_DRD_CFG:
+    case NPCM7XX_FIU_DWR_CFG:
+        if (s->regs[reg] & NPCM7XX_FIU_CFG_LCK) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: write to locked register @ 0x%" PRIx64 "\n",
+                          DEVICE(s)->canonical_path, addr);
+            return;
+        }
+        s->regs[reg] = value;
+        break;
+
+    case NPCM7XX_FIU_UMA_CTS:
+        if (value & FIU_UMA_CTS_RDYST) {
+            value &= ~FIU_UMA_CTS_RDYST;
+        } else {
+            value |= s->regs[reg] & FIU_UMA_CTS_RDYST;
+        }
+        if ((s->regs[reg] ^ value) & FIU_UMA_CTS_SW_CS) {
+            if (value & FIU_UMA_CTS_SW_CS) {
+                /*
+                 * Don't drop CS if there's a transfer in progress, or we're
+                 * about to start one.
+                 */
+                if (!((value | s->regs[reg]) & FIU_UMA_CTS_EXEC_DONE)) {
+                    npcm7xx_fiu_deselect(s);
+                }
+            } else {
+                int cs_id = FIU_UMA_CTS_DEV_NUM(s->regs[NPCM7XX_FIU_UMA_CTS]);
+                npcm7xx_fiu_select(s, cs_id);
+            }
+        }
+        s->regs[reg] = value | (s->regs[reg] & FIU_UMA_CTS_EXEC_DONE);
+        if (value & FIU_UMA_CTS_EXEC_DONE) {
+            npcm7xx_fiu_uma_transaction(s);
+        }
+        break;
+
+    case NPCM7XX_FIU_UMA_DR0 ... NPCM7XX_FIU_UMA_DR3:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: write to read-only register @ 0x%" PRIx64 "\n",
+                      DEVICE(s)->canonical_path, addr);
+        return;
+
+    case NPCM7XX_FIU_PRT_CFG:
+    case NPCM7XX_FIU_PRT_CMD0 ... NPCM7XX_FIU_PRT_CMD9:
+        qemu_log_mask(LOG_UNIMP, "%s: PRT is not implemented\n", __func__);
+        break;
+
+    case NPCM7XX_FIU_UMA_CMD:
+    case NPCM7XX_FIU_UMA_ADDR:
+    case NPCM7XX_FIU_UMA_DW0 ... NPCM7XX_FIU_UMA_DW3:
+    case NPCM7XX_FIU_CFG:
+        s->regs[reg] = value;
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: write to invalid offset 0x%" PRIx64 "\n",
+                      DEVICE(s)->canonical_path, addr);
+        return;
+    }
+}
+
+static const MemoryRegionOps npcm7xx_fiu_ctrl_ops = {
+    .read = npcm7xx_fiu_ctrl_read,
+    .write = npcm7xx_fiu_ctrl_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false,
+    },
+};
+
+static void npcm7xx_fiu_enter_reset(Object *obj, ResetType type)
+{
+    NPCM7xxFIUState *s = NPCM7XX_FIU(obj);
+
+    trace_npcm7xx_fiu_enter_reset(DEVICE(obj)->canonical_path, type);
+
+    memset(s->regs, 0, sizeof(s->regs));
+
+    s->regs[NPCM7XX_FIU_DRD_CFG] = 0x0300100b;
+    s->regs[NPCM7XX_FIU_DWR_CFG] = 0x03000002;
+    s->regs[NPCM7XX_FIU_UMA_CFG] = 0x00000400;
+    s->regs[NPCM7XX_FIU_UMA_CTS] = 0x00010000;
+    s->regs[NPCM7XX_FIU_UMA_CMD] = 0x0000000b;
+    s->regs[NPCM7XX_FIU_PRT_CFG] = 0x00000400;
+    s->regs[NPCM7XX_FIU_CFG] = 0x0000000b;
+}
+
+static void npcm7xx_fiu_hold_reset(Object *obj)
+{
+    NPCM7xxFIUState *s = NPCM7XX_FIU(obj);
+    int i;
+
+    trace_npcm7xx_fiu_hold_reset(DEVICE(obj)->canonical_path);
+
+    for (i = 0; i < s->cs_count; i++) {
+        qemu_irq_raise(s->cs_lines[i]);
+    }
+}
+
+static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp)
+{
+    NPCM7xxFIUState *s = NPCM7XX_FIU(dev);
+    SysBusDevice *sbd = &s->parent;
+    int i;
+
+    if (s->cs_count <= 0) {
+        error_setg(errp, "%s: %d chip selects specified, need at least one",
+                   dev->canonical_path, s->cs_count);
+        return;
+    }
+
+    s->spi = ssi_create_bus(dev, "spi");
+    s->cs_lines = g_new0(qemu_irq, s->cs_count);
+    qdev_init_gpio_out_named(DEVICE(s), s->cs_lines, "cs", s->cs_count);
+    s->flash = g_new0(NPCM7xxFIUFlash, s->cs_count);
+
+    /*
+     * Register the control registers region first. It may be followed by one
+     * or more direct flash access regions.
+     */
+    memory_region_init_io(&s->mmio, OBJECT(s), &npcm7xx_fiu_ctrl_ops, s, "ctrl",
+                          NPCM7XX_FIU_CTRL_REGS_SIZE);
+    sysbus_init_mmio(sbd, &s->mmio);
+
+    for (i = 0; i < s->cs_count; i++) {
+        NPCM7xxFIUFlash *flash = &s->flash[i];
+        flash->fiu = s;
+        memory_region_init_io(&flash->direct_access, OBJECT(s),
+                              &npcm7xx_fiu_flash_ops, &s->flash[i], "flash",
+                              NPCM7XX_FIU_FLASH_WINDOW_SIZE);
+        sysbus_init_mmio(sbd, &flash->direct_access);
+    }
+}
+
+static const VMStateDescription vmstate_npcm7xx_fiu = {
+    .name = "npcm7xx-fiu",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT32(active_cs, NPCM7xxFIUState),
+        VMSTATE_UINT32_ARRAY(regs, NPCM7xxFIUState, NPCM7XX_FIU_NR_REGS),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static Property npcm7xx_fiu_properties[] = {
+    DEFINE_PROP_INT32("cs-count", NPCM7xxFIUState, cs_count, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void npcm7xx_fiu_class_init(ObjectClass *klass, void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    QEMU_BUILD_BUG_ON(NPCM7XX_FIU_REGS_END > NPCM7XX_FIU_NR_REGS);
+
+    dc->desc = "NPCM7xx Flash Interface Unit";
+    dc->realize = npcm7xx_fiu_realize;
+    dc->vmsd = &vmstate_npcm7xx_fiu;
+    rc->phases.enter = npcm7xx_fiu_enter_reset;
+    rc->phases.hold = npcm7xx_fiu_hold_reset;
+    device_class_set_props(dc, npcm7xx_fiu_properties);
+}
+
+static const TypeInfo npcm7xx_fiu_types[] = {
+    {
+        .name = TYPE_NPCM7XX_FIU,
+        .parent = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(NPCM7xxFIUState),
+        .class_init = npcm7xx_fiu_class_init,
+    },
+};
+DEFINE_TYPES(npcm7xx_fiu_types);
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index cd74642034..f303c6bead 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -361,6 +361,7 @@ config NPCM7XX
     select ARM_GIC
     select PL310  # cache controller
     select SERIAL
+    select SSI
     select UNIMP
 
 config FSL_IMX25
diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build
index f1f5c287d0..dee00c0da6 100644
--- a/hw/ssi/meson.build
+++ b/hw/ssi/meson.build
@@ -1,5 +1,6 @@
 softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c'))
 softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c'))
 softmmu_ss.add(when: 'CONFIG_PL022', if_true: files('pl022.c'))
 softmmu_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c'))
diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
index 0ea498de91..2f83ef833f 100644
--- a/hw/ssi/trace-events
+++ b/hw/ssi/trace-events
@@ -9,3 +9,14 @@ aspeed_smc_dma_checksum(uint32_t addr, uint32_t data) "0x%08x: 0x%08x"
 aspeed_smc_dma_rw(const char *dir, uint32_t flash_addr, uint32_t dram_addr, uint32_t size) "%s flash:@0x%08x dram:@0x%08x size:0x%08x"
 aspeed_smc_write(uint64_t addr,  uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64
 aspeed_smc_flash_select(int cs, const char *prefix) "CS%d %sselect"
+
+# npcm7xx_fiu.c
+
+npcm7xx_fiu_enter_reset(const char *id, int reset_type) "%s reset type: %d"
+npcm7xx_fiu_hold_reset(const char *id) "%s"
+npcm7xx_fiu_select(const char *id, int cs) "%s select CS%d"
+npcm7xx_fiu_deselect(const char *id, int cs) "%s deselect CS%d"
+npcm7xx_fiu_ctrl_read(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
+npcm7xx_fiu_ctrl_write(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
+npcm7xx_fiu_flash_read(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
+npcm7xx_fiu_flash_write(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 11/14] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (9 preceding siblings ...)
  2020-08-25  0:17 ` [PATCH v8 10/14] hw/ssi: NPCM7xx Flash Interface Unit device model Havard Skinnemoen via
@ 2020-08-25  0:17 ` Havard Skinnemoen via
  2020-08-25  0:17 ` [PATCH v8 12/14] hw/arm/npcm7xx: add board setup stub for CPU and UART clocks Havard Skinnemoen via
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:17 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Cédric Le Goater

This allows these NPCM7xx-based boards to boot from a flash image, e.g.
one built with OpenBMC. For example like this:

IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
qemu-system-arm -machine quanta-gsj -nographic \
	-drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Tested-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 hw/arm/npcm7xx_boards.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
index b67e45e913..70e5c34216 100644
--- a/hw/arm/npcm7xx_boards.c
+++ b/hw/arm/npcm7xx_boards.c
@@ -19,6 +19,7 @@
 #include "hw/arm/npcm7xx.h"
 #include "hw/core/cpu.h"
 #include "hw/loader.h"
+#include "hw/qdev-properties.h"
 #include "qapi/error.h"
 #include "qemu-common.h"
 #include "qemu/units.h"
@@ -55,6 +56,22 @@ static void npcm7xx_load_bootrom(MachineState *machine, NPCM7xxState *soc)
     }
 }
 
+static void npcm7xx_connect_flash(NPCM7xxFIUState *fiu, int cs_no,
+                                  const char *flash_type, DriveInfo *dinfo)
+{
+    DeviceState *flash;
+    qemu_irq flash_cs;
+
+    flash = qdev_new(flash_type);
+    if (dinfo) {
+        qdev_prop_set_drive(flash, "drive", blk_by_legacy_dinfo(dinfo));
+    }
+    qdev_realize_and_unref(flash, BUS(fiu->spi), &error_fatal);
+
+    flash_cs = qdev_get_gpio_in_named(flash, SSI_GPIO_CS, 0);
+    qdev_connect_gpio_out_named(DEVICE(fiu), "cs", cs_no, flash_cs);
+}
+
 static void npcm7xx_connect_dram(NPCM7xxState *soc, MemoryRegion *dram)
 {
     memory_region_add_subregion(get_system_memory(), NPCM7XX_DRAM_BA, dram);
@@ -92,6 +109,7 @@ static void npcm750_evb_init(MachineState *machine)
     qdev_realize(DEVICE(soc), NULL, &error_fatal);
 
     npcm7xx_load_bootrom(machine, soc);
+    npcm7xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0));
     npcm7xx_load_kernel(machine, soc);
 }
 
@@ -104,6 +122,8 @@ static void quanta_gsj_init(MachineState *machine)
     qdev_realize(DEVICE(soc), NULL, &error_fatal);
 
     npcm7xx_load_bootrom(machine, soc);
+    npcm7xx_connect_flash(&soc->fiu[0], 0, "mx25l25635e",
+                          drive_get(IF_MTD, 0, 0));
     npcm7xx_load_kernel(machine, soc);
 }
 
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 12/14] hw/arm/npcm7xx: add board setup stub for CPU and UART clocks
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (10 preceding siblings ...)
  2020-08-25  0:17 ` [PATCH v8 11/14] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj Havard Skinnemoen via
@ 2020-08-25  0:17 ` Havard Skinnemoen via
  2020-09-04  9:34   ` Philippe Mathieu-Daudé
  2020-08-25  0:17 ` [PATCH v8 13/14] docs/system: Add Nuvoton machine documentation Havard Skinnemoen via
                   ` (3 subsequent siblings)
  15 siblings, 1 reply; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:17 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen

When booting directly into a kernel, bypassing the boot loader, the CPU and
UART clocks are not set up correctly. This makes the system appear very
slow, and causes the initrd boot test to fail when optimization is off.

The UART clock must run at 24 MHz. The default 25 MHz reference clock
cannot achieve this, so switch to PLL2/2 @ 480 MHz, which works
perfectly with the default /20 divider.

The CPU clock should run at 800 MHz, so switch it to PLL1/2. PLL1 runs
at 800 MHz by default, so we need to double the feedback divider as well
to make it run at 1600 MHz (so PLL1/2 runs at 800 MHz).

We don't bother checking for PLL lock because we know our emulated PLLs
lock instantly.

Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/arm/npcm7xx.h |  1 +
 hw/arm/npcm7xx.c         | 32 ++++++++++++++++++++++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
index 78d0d78c52..13106af215 100644
--- a/include/hw/arm/npcm7xx.h
+++ b/include/hw/arm/npcm7xx.h
@@ -37,6 +37,7 @@
 #define NPCM7XX_SMP_LOADER_START        (0xffff0000)  /* Boot ROM */
 #define NPCM7XX_SMP_BOOTREG_ADDR        (0xf080013c)  /* GCR.SCRPAD */
 #define NPCM7XX_GIC_CPU_IF_ADDR         (0xf03fe100)  /* GIC within A9 */
+#define NPCM7XX_BOARD_SETUP_ADDR        (0xffff1000)  /* Boot ROM */
 
 typedef struct NPCM7xxMachine {
     MachineState        parent;
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 7884b2b03d..037f3a26f2 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -55,6 +55,13 @@
 #define NPCM7XX_ROM_BA          (0xffff0000)
 #define NPCM7XX_ROM_SZ          (64 * KiB)
 
+/* Clock configuration values to be fixed up when bypassing bootloader */
+
+/* Run PLL1 at 1600 MHz */
+#define NPCM7XX_PLLCON1_FIXUP_VAL   (0x00402101)
+/* Run the CPU from PLL1 and UART from PLL2 */
+#define NPCM7XX_CLKSEL_FIXUP_VAL    (0x004aaba9)
+
 /*
  * Interrupt lines going into the GIC. This does not include internal Cortex-A9
  * interrupts.
@@ -132,6 +139,29 @@ static const struct {
     },
 };
 
+static void npcm7xx_write_board_setup(ARMCPU *cpu,
+                                      const struct arm_boot_info *info)
+{
+    uint32_t board_setup[] = {
+        0xe59f0010,     /* ldr r0, clk_base_addr */
+        0xe59f1010,     /* ldr r1, pllcon1_value */
+        0xe5801010,     /* str r1, [r0, #16] */
+        0xe59f100c,     /* ldr r1, clksel_value */
+        0xe5801004,     /* str r1, [r0, #4] */
+        0xe12fff1e,     /* bx lr */
+        NPCM7XX_CLK_BA,
+        NPCM7XX_PLLCON1_FIXUP_VAL,
+        NPCM7XX_CLKSEL_FIXUP_VAL,
+    };
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(board_setup); i++) {
+        board_setup[i] = tswap32(board_setup[i]);
+    }
+    rom_add_blob_fixed("board-setup", board_setup, sizeof(board_setup),
+                       info->board_setup_addr);
+}
+
 static void npcm7xx_write_secondary_boot(ARMCPU *cpu,
                                          const struct arm_boot_info *info)
 {
@@ -170,6 +200,8 @@ static struct arm_boot_info npcm7xx_binfo = {
     .gic_cpu_if_addr        = NPCM7XX_GIC_CPU_IF_ADDR,
     .write_secondary_boot   = npcm7xx_write_secondary_boot,
     .board_id               = -1,
+    .board_setup_addr       = NPCM7XX_BOARD_SETUP_ADDR,
+    .write_board_setup      = npcm7xx_write_board_setup,
 };
 
 void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 13/14] docs/system: Add Nuvoton machine documentation
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (11 preceding siblings ...)
  2020-08-25  0:17 ` [PATCH v8 12/14] hw/arm/npcm7xx: add board setup stub for CPU and UART clocks Havard Skinnemoen via
@ 2020-08-25  0:17 ` Havard Skinnemoen via
  2020-09-03 19:11   ` Philippe Mathieu-Daudé
  2020-08-25  0:17 ` [PATCH v8 14/14] tests/acceptance: console boot tests for quanta-gsj Havard Skinnemoen via
                   ` (2 subsequent siblings)
  15 siblings, 1 reply; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:17 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Cédric Le Goater

Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 docs/system/arm/nuvoton.rst | 90 +++++++++++++++++++++++++++++++++++++
 docs/system/target-arm.rst  |  1 +
 2 files changed, 91 insertions(+)
 create mode 100644 docs/system/arm/nuvoton.rst

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
new file mode 100644
index 0000000000..36bf901122
--- /dev/null
+++ b/docs/system/arm/nuvoton.rst
@@ -0,0 +1,90 @@
+Nuvoton iBMC boards (``npcm750-evb``, ``quanta-gsj``)
+=====================================================
+
+The `Nuvoton iBMC`_ chips (NPCM7xx) are a family of ARM-based SoCs that are
+designed to be used as Baseboard Management Controllers (BMCs) in various
+servers. They all feature one or two ARM Cortex A9 CPU cores, as well as an
+assortment of peripherals targeted for either Enterprise or Data Center /
+Hyperscale applications. The former is a superset of the latter, so NPCM750 has
+all the peripherals of NPCM730 and more.
+
+.. _Nuvoton iBMC: https://www.nuvoton.com/products/cloud-computing/ibmc/
+
+The NPCM750 SoC has two Cortex A9 cores and is targeted for the Enterprise
+segment. The following machines are based on this chip :
+
+- ``npcm750-evb``       Nuvoton NPCM750 Evaluation board
+
+The NPCM730 SoC has two Cortex A9 cores and is targeted for Data Center and
+Hyperscale applications. The following machines are based on this chip :
+
+- ``quanta-gsj``        Quanta GSJ server BMC
+
+There are also two more SoCs, NPCM710 and NPCM705, which are single-core
+variants of NPCM750 and NPCM730, respectively. These are currently not
+supported by QEMU.
+
+Supported devices
+-----------------
+
+ * SMP (Dual Core Cortex-A9)
+ * Cortex-A9MPCore built-in peripherals: SCU, GIC, Global Timer, Private Timer
+   and Watchdog.
+ * SRAM, ROM and DRAM mappings
+ * System Global Control Registers (GCR)
+ * Clock and reset controller (CLK)
+ * Timer controller (TIM)
+ * Serial ports (16550-based)
+ * DDR4 memory controller (dummy interface indicating memory training is done)
+ * OTP controllers (no protection features)
+ * Flash Interface Unit (FIU; no protection features)
+
+Missing devices
+---------------
+
+ * GPIO controller
+ * LPC/eSPI host-to-BMC interface, including
+
+   * Keyboard and mouse controller interface (KBCI)
+   * Keyboard Controller Style (KCS) channels
+   * BIOS POST code FIFO
+   * System Wake-up Control (SWC)
+   * Shared memory (SHM)
+   * eSPI slave interface
+
+ * Ethernet controllers (GMAC and EMC)
+ * USB host (USBH)
+ * USB device (USBD)
+ * SMBus controller (SMBF)
+ * Peripheral SPI controller (PSPI)
+ * Analog to Digital Converter (ADC)
+ * SD/MMC host
+ * Random Number Generator (RNG)
+ * PECI interface
+ * Pulse Width Modulation (PWM)
+ * Tachometer
+ * PCI and PCIe root complex and bridges
+ * VDM and MCTP support
+ * Serial I/O expansion
+ * LPC/eSPI host
+ * Coprocessor
+ * Graphics
+ * Video capture
+ * Encoding compression engine
+ * Security features
+
+Boot options
+------------
+
+The Nuvoton machines can boot from an OpenBMC firmware image, or directly into
+a kernel using the ``-kernel`` option. OpenBMC images for `quanta-gsj` and
+possibly others can be downloaded from the OpenPOWER jenkins :
+
+   https://openpower.xyz/
+
+The firmware image should be attached as an MTD drive. Example :
+
+.. code-block:: bash
+
+  $ qemu-system-arm -machine quanta-gsj -nographic \
+      -drive file=image-bmc,if=mtd,bus=0,unit=0,format=raw
diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst
index afdb37e738..fdcf25c237 100644
--- a/docs/system/target-arm.rst
+++ b/docs/system/target-arm.rst
@@ -86,6 +86,7 @@ undocumented; you can get a complete list by running
    arm/musicpal
    arm/gumstix
    arm/nseries
+   arm/nuvoton
    arm/orangepi
    arm/palm
    arm/xscale
-- 
2.28.0.297.g1956fa8f8d-goog



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

* [PATCH v8 14/14] tests/acceptance: console boot tests for quanta-gsj
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (12 preceding siblings ...)
  2020-08-25  0:17 ` [PATCH v8 13/14] docs/system: Add Nuvoton machine documentation Havard Skinnemoen via
@ 2020-08-25  0:17 ` Havard Skinnemoen via
  2020-09-03 18:20 ` [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Philippe Mathieu-Daudé
  2020-09-08 15:02 ` Alexander Bulekov
  15 siblings, 0 replies; 35+ messages in thread
From: Havard Skinnemoen via @ 2020-08-25  0:17 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen

This adds two acceptance tests for the quanta-gsj machine.

One test downloads a lightly patched openbmc flash image from github and
verifies that it boots all the way to the login prompt.

The other test downloads a kernel, initrd and dtb built from the same
openbmc source and verifies that the kernel detects all CPUs and boots
to the point where it can't find the root filesystem (because we have no
flash image in this case).

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 tests/acceptance/boot_linux_console.py | 83 ++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py
index aaa781a581..4a366ce93e 100644
--- a/tests/acceptance/boot_linux_console.py
+++ b/tests/acceptance/boot_linux_console.py
@@ -568,6 +568,89 @@ class BootLinuxConsole(LinuxKernelTest):
                                                 'sda')
         # cubieboard's reboot is not functioning; omit reboot test.
 
+    def test_arm_quanta_gsj(self):
+        """
+        :avocado: tags=arch:arm
+        :avocado: tags=machine:quanta-gsj
+        """
+        # 25 MiB compressed, 32 MiB uncompressed.
+        image_url = (
+                'https://github.com/hskinnemoen/openbmc/releases/download/'
+                '20200711-gsj-qemu-0/obmc-phosphor-image-gsj.static.mtd.gz')
+        image_hash = '14895e634923345cb5c8776037ff7876df96f6b1'
+        image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash)
+        image_name = 'obmc.mtd'
+        image_path = os.path.join(self.workdir, image_name)
+        archive.gzip_uncompress(image_path_gz, image_path)
+
+        self.vm.set_console()
+        drive_args = 'file=' + image_path + ',if=mtd,bus=0,unit=0'
+        self.vm.add_args('-drive', drive_args)
+        self.vm.launch()
+
+        # Disable drivers and services that stall for a long time during boot,
+        # to avoid running past the 90-second timeout. These may be removed
+        # as the corresponding device support is added.
+        kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + (
+                'console=${console} '
+                'mem=${mem} '
+                'initcall_blacklist=npcm_i2c_bus_driver_init '
+                'systemd.mask=systemd-random-seed.service '
+                'systemd.mask=dropbearkey.service '
+        )
+
+        self.wait_for_console_pattern('> BootBlock by Nuvoton')
+        self.wait_for_console_pattern('>Device: Poleg BMC NPCM730')
+        self.wait_for_console_pattern('>Skip DDR init.')
+        self.wait_for_console_pattern('U-Boot ')
+        interrupt_interactive_console_until_pattern(
+                self, 'Hit any key to stop autoboot:', 'U-Boot>')
+        exec_command_and_wait_for_pattern(
+                self, "setenv bootargs ${bootargs} " + kernel_command_line,
+                'U-Boot>')
+        exec_command_and_wait_for_pattern(
+                self, 'run romboot', 'Booting Kernel from flash')
+        self.wait_for_console_pattern('Booting Linux on physical CPU 0x0')
+        self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0')
+        self.wait_for_console_pattern('OpenBMC Project Reference Distro')
+        self.wait_for_console_pattern('gsj login:')
+
+    def test_arm_quanta_gsj_initrd(self):
+        """
+        :avocado: tags=arch:arm
+        :avocado: tags=machine:quanta-gsj
+        """
+        initrd_url = (
+                'https://github.com/hskinnemoen/openbmc/releases/download/'
+                '20200711-gsj-qemu-0/obmc-phosphor-initramfs-gsj.cpio.xz')
+        initrd_hash = '98fefe5d7e56727b1eb17d5c00311b1b5c945300'
+        initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+        kernel_url = (
+                'https://github.com/hskinnemoen/openbmc/releases/download/'
+                '20200711-gsj-qemu-0/uImage-gsj.bin')
+        kernel_hash = 'fa67b2f141d56d39b3c54305c0e8a899c99eb2c7'
+        kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+        dtb_url = (
+                'https://github.com/hskinnemoen/openbmc/releases/download/'
+                '20200711-gsj-qemu-0/nuvoton-npcm730-gsj.dtb')
+        dtb_hash = '18315f7006d7b688d8312d5c727eecd819aa36a4'
+        dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash)
+
+        self.vm.set_console()
+        kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+                               'console=ttyS0,115200n8 '
+                               'earlycon=uart8250,mmio32,0xf0001000')
+        self.vm.add_args('-kernel', kernel_path,
+                         '-initrd', initrd_path,
+                         '-dtb', dtb_path,
+                         '-append', kernel_command_line)
+        self.vm.launch()
+
+        self.wait_for_console_pattern('Booting Linux on physical CPU 0x0')
+        self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0')
+        self.wait_for_console_pattern(
+                'Give root password for system maintenance')
+
     def test_arm_orangepi(self):
         """
         :avocado: tags=arch:arm
-- 
2.28.0.297.g1956fa8f8d-goog



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

* Re: [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (13 preceding siblings ...)
  2020-08-25  0:17 ` [PATCH v8 14/14] tests/acceptance: console boot tests for quanta-gsj Havard Skinnemoen via
@ 2020-09-03 18:20 ` Philippe Mathieu-Daudé
  2020-09-08 15:02 ` Alexander Bulekov
  15 siblings, 0 replies; 35+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-03 18:20 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: kfting, qemu-arm, qemu-devel, Avi.Fishman

On 8/25/20 2:16 AM, Havard Skinnemoen via wrote:
> I also pushed this and the previous patchsets to my qemu fork on github.
> The branches are named npcm7xx-v[1-8].
> 
>   https://github.com/hskinnemoen/qemu
> 
> This patch series models enough of the Nuvoton NPCM730 and NPCM750 SoCs to boot
> an OpenBMC image built for quanta-gsj. This includes device models for:
> 
>   - Global Configuration Registers
>   - Clock Control
>   - Timers
>   - Fuses
>   - Memory Controller
>   - Flash Controller
> 
> These modules, along with the existing Cortex A9 CPU cores and built-in
> peripherals, are integrated into a NPCM730 or NPCM750 SoC, which in turn form
> the foundation for the quanta-gsj and npcm750-evb machines, respectively. The
> two SoCs are very similar; the only difference is that NPCM730 is missing some
> peripherals that NPCM750 has, and which are not considered essential for
> datacenter use (e.g. graphics controllers). For more information, see
> 
> https://www.nuvoton.com/products/cloud-computing/ibmc/
> 
> Both quanta-gsj and npcm750-evb correspond to real boards supported by OpenBMC.
> At the end of the series, qemu can boot an OpenBMC image built for one of these
> boards with some minor modifications.
> 
> The patches in this series were developed by Google and reviewed by Nuvoton. We
> will be maintaining the machine and peripheral support together.
> 
> The data sheet for these SoCs is not generally available. Please let me know if
> more comments are needed to understand the device behavior.

Series:
Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>


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

* Re: [PATCH v8 07/14] hw/arm: Load -bios image as a boot ROM for npcm7xx
  2020-08-25  0:17 ` [PATCH v8 07/14] hw/arm: Load -bios image as a boot ROM for npcm7xx Havard Skinnemoen via
@ 2020-09-03 18:59   ` Philippe Mathieu-Daudé
  2020-09-10 23:52     ` Havard Skinnemoen
  0 siblings, 1 reply; 35+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-03 18:59 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: kfting, Cédric Le Goater, qemu-arm, qemu-devel, Avi.Fishman

On 8/25/20 2:17 AM, Havard Skinnemoen via wrote:
> If a -bios option is specified on the command line, load the image into
> the internal ROM memory region, which contains the first instructions
> run by the CPU after reset.
> 
> If -bios is not specified, the vbootrom included with qemu is loaded by
> default.
> 
> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  hw/arm/npcm7xx_boards.c | 33 +++++++++++++++++++++++++++++++++
>  1 file changed, 33 insertions(+)
> 
> diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
> index 0b9dce2b35..b67e45e913 100644
> --- a/hw/arm/npcm7xx_boards.c
> +++ b/hw/arm/npcm7xx_boards.c
> @@ -18,12 +18,43 @@
>  
>  #include "hw/arm/npcm7xx.h"
>  #include "hw/core/cpu.h"
> +#include "hw/loader.h"
>  #include "qapi/error.h"
> +#include "qemu-common.h"
>  #include "qemu/units.h"
> +#include "sysemu/sysemu.h"
>  
>  #define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7
>  #define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff
>  
> +static const char npcm7xx_default_bootrom[] = "npcm7xx_bootrom.bin";
> +
> +static void npcm7xx_load_bootrom(MachineState *machine, NPCM7xxState *soc)
> +{
> +    g_autofree char *filename = NULL;
> +    int ret;
> +
> +    if (machine->kernel_filename) {
> +        /* Don't load the bootrom if user requested direct kernel boot. */
> +        return;

Wondering why not load it always if available, and what would
happen if booting from a kernel (without bootrom) and running
"reboot" (via SysRq) in the guest I got:

# echo b > /proc/sysrq-trigger
sysrq: Resetting
CPU0: stopping
CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.4.32-7dc9442-dirty-8978043 #1
Hardware name: NPCM7XX Chip family
Backtrace:
[<b0107c4c>] (dump_backtrace) from [<b010823c>] (show_stack+0x20/0x24)
 r7:00000000 r6:600f0193 r5:00000000 r4:b0b5b1b8
[<b010821c>] (show_stack) from [<b077b230>] (dump_stack+0x94/0xa8)
[<b077b19c>] (dump_stack) from [<b010a20c>] (handle_IPI+0x1f0/0x38c)
 r7:00000000 r6:00000004 r5:b0b5eb64 r4:b0b729e0
[<b010a01c>] (handle_IPI) from [<b0102264>] (gic_handle_irq+0x9c/0xa0)
 r10:10c5387d r9:b0b01ef8 r8:d0803100 r7:d0802100 r6:d080210c r5:b0b37db4
 r4:b0b04504
[<b01021c8>] (gic_handle_irq) from [<b0101a8c>] (__irq_svc+0x6c/0x90)
Exception stack(0xb0b01ef8 to 0xb0b01f40)
1ee0:                                                       00000000
00012f74
1f00: cfbc1774 b01112e0 b0b00000 00000000 b0b03e28 b0b03e6c b0b5e72c
b08e5ea8
1f20: 10c5387d b0b01f54 b0b01f58 b0b01f48 b0104114 b0104118 600f0013
ffffffff
 r9:b0b00000 r8:b0b5e72c r7:b0b01f2c r6:ffffffff r5:600f0013 r4:b0104118
[<b01040d4>] (arch_cpu_idle) from [<b014b240>] (do_idle+0xec/0x140)
[<b014b154>] (do_idle) from [<b014b56c>] (cpu_startup_entry+0x28/0x2c)
 r9:410fc090 r8:0810c000 r7:00000000 r6:b0a53a40 r5:00000001 r4:000000ce
[<b014b544>] (cpu_startup_entry) from [<b0794f6c>] (rest_init+0xb4/0xbc)
[<b0794eb8>] (rest_init) from [<b0a00c50>] (arch_call_rest_init+0x18/0x1c)
 r5:00000001 r4:b0b72754
[<b0a00c38>] (arch_call_rest_init) from [<b0a010f4>]
(start_kernel+0x428/0x458)
[<b0a00ccc>] (start_kernel) from [<00000000>] (0x0)
Reboot failed -- System halted

So not a problem so far...
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

But still I'd keep loading the bootrom regardless -kernel.

> +    }
> +
> +    if (!bios_name) {
> +        bios_name = npcm7xx_default_bootrom;
> +    }
> +
> +    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
> +    if (!filename) {
> +        error_report("Could not find ROM image '%s'", bios_name);
> +        exit(1);
> +    }
> +    ret = load_image_mr(filename, &soc->irom);
> +    if (ret < 0) {
> +        error_report("Failed to load ROM image '%s'", filename);
> +        exit(1);
> +    }
> +}
> +
>  static void npcm7xx_connect_dram(NPCM7xxState *soc, MemoryRegion *dram)
>  {
>      memory_region_add_subregion(get_system_memory(), NPCM7XX_DRAM_BA, dram);
> @@ -60,6 +91,7 @@ static void npcm750_evb_init(MachineState *machine)
>      npcm7xx_connect_dram(soc, machine->ram);
>      qdev_realize(DEVICE(soc), NULL, &error_fatal);
>  
> +    npcm7xx_load_bootrom(machine, soc);
>      npcm7xx_load_kernel(machine, soc);
>  }
>  
> @@ -71,6 +103,7 @@ static void quanta_gsj_init(MachineState *machine)
>      npcm7xx_connect_dram(soc, machine->ram);
>      qdev_realize(DEVICE(soc), NULL, &error_fatal);
>  
> +    npcm7xx_load_bootrom(machine, soc);
>      npcm7xx_load_kernel(machine, soc);
>  }
>  
> 


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

* Re: [PATCH v8 13/14] docs/system: Add Nuvoton machine documentation
  2020-08-25  0:17 ` [PATCH v8 13/14] docs/system: Add Nuvoton machine documentation Havard Skinnemoen via
@ 2020-09-03 19:11   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 35+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-03 19:11 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: kfting, Cédric Le Goater, qemu-arm, qemu-devel, Avi.Fishman

On 8/25/20 2:17 AM, Havard Skinnemoen via wrote:
> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  docs/system/arm/nuvoton.rst | 90 +++++++++++++++++++++++++++++++++++++
>  docs/system/target-arm.rst  |  1 +
>  2 files changed, 91 insertions(+)
>  create mode 100644 docs/system/arm/nuvoton.rst
> 
> diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
> new file mode 100644
> index 0000000000..36bf901122
> --- /dev/null
> +++ b/docs/system/arm/nuvoton.rst
> @@ -0,0 +1,90 @@
> +Nuvoton iBMC boards (``npcm750-evb``, ``quanta-gsj``)
> +=====================================================
> +
> +The `Nuvoton iBMC`_ chips (NPCM7xx) are a family of ARM-based SoCs that are
> +designed to be used as Baseboard Management Controllers (BMCs) in various
> +servers. They all feature one or two ARM Cortex A9 CPU cores, as well as an
> +assortment of peripherals targeted for either Enterprise or Data Center /
> +Hyperscale applications. The former is a superset of the latter, so NPCM750 has
> +all the peripherals of NPCM730 and more.
> +
> +.. _Nuvoton iBMC: https://www.nuvoton.com/products/cloud-computing/ibmc/
> +
> +The NPCM750 SoC has two Cortex A9 cores and is targeted for the Enterprise
> +segment. The following machines are based on this chip :
> +
> +- ``npcm750-evb``       Nuvoton NPCM750 Evaluation board
> +
> +The NPCM730 SoC has two Cortex A9 cores and is targeted for Data Center and
> +Hyperscale applications. The following machines are based on this chip :
> +
> +- ``quanta-gsj``        Quanta GSJ server BMC
> +
> +There are also two more SoCs, NPCM710 and NPCM705, which are single-core
> +variants of NPCM750 and NPCM730, respectively. These are currently not
> +supported by QEMU.
> +
> +Supported devices
> +-----------------
> +
> + * SMP (Dual Core Cortex-A9)
> + * Cortex-A9MPCore built-in peripherals: SCU, GIC, Global Timer, Private Timer
> +   and Watchdog.
> + * SRAM, ROM and DRAM mappings
> + * System Global Control Registers (GCR)
> + * Clock and reset controller (CLK)
> + * Timer controller (TIM)
> + * Serial ports (16550-based)
> + * DDR4 memory controller (dummy interface indicating memory training is done)
> + * OTP controllers (no protection features)
> + * Flash Interface Unit (FIU; no protection features)
> +
> +Missing devices
> +---------------
> +
> + * GPIO controller
> + * LPC/eSPI host-to-BMC interface, including
> +
> +   * Keyboard and mouse controller interface (KBCI)
> +   * Keyboard Controller Style (KCS) channels
> +   * BIOS POST code FIFO
> +   * System Wake-up Control (SWC)
> +   * Shared memory (SHM)
> +   * eSPI slave interface
> +
> + * Ethernet controllers (GMAC and EMC)
> + * USB host (USBH)
> + * USB device (USBD)
> + * SMBus controller (SMBF)
> + * Peripheral SPI controller (PSPI)
> + * Analog to Digital Converter (ADC)
> + * SD/MMC host
> + * Random Number Generator (RNG)
> + * PECI interface
> + * Pulse Width Modulation (PWM)
> + * Tachometer
> + * PCI and PCIe root complex and bridges
> + * VDM and MCTP support
> + * Serial I/O expansion
> + * LPC/eSPI host
> + * Coprocessor
> + * Graphics
> + * Video capture
> + * Encoding compression engine
> + * Security features
> +
> +Boot options
> +------------
> +
> +The Nuvoton machines can boot from an OpenBMC firmware image, or directly into
> +a kernel using the ``-kernel`` option. OpenBMC images for `quanta-gsj` and
> +possibly others can be downloaded from the OpenPOWER jenkins :
> +
> +   https://openpower.xyz/
> +

Maybe mention the usual root password is '0penBmc'?

Otherwise:
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +The firmware image should be attached as an MTD drive. Example :
> +
> +.. code-block:: bash
> +
> +  $ qemu-system-arm -machine quanta-gsj -nographic \
> +      -drive file=image-bmc,if=mtd,bus=0,unit=0,format=raw
> diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst
> index afdb37e738..fdcf25c237 100644
> --- a/docs/system/target-arm.rst
> +++ b/docs/system/target-arm.rst
> @@ -86,6 +86,7 @@ undocumented; you can get a complete list by running
>     arm/musicpal
>     arm/gumstix
>     arm/nseries
> +   arm/nuvoton
>     arm/orangepi
>     arm/palm
>     arm/xscale
> 


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

* Re: [PATCH v8 06/14] roms: Add virtual Boot ROM for NPCM7xx SoCs
  2020-08-25  0:17 ` [PATCH v8 06/14] roms: Add virtual Boot ROM for NPCM7xx SoCs Havard Skinnemoen via
@ 2020-09-04  9:24   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 35+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-04  9:24 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: kfting, qemu-arm, qemu-devel, Avi.Fishman

On 8/25/20 2:17 AM, Havard Skinnemoen via wrote:
> This is a minimalistic boot ROM written specifically for use with QEMU.
> It supports loading the second-stage loader from SPI flash into RAM, SMP
> boot, and not much else.
> 
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  Makefile                    |   1 +
>  .gitmodules                 |   3 +++
>  MAINTAINERS                 |   2 ++
>  pc-bios/README              |   6 ++++++
>  pc-bios/npcm7xx_bootrom.bin | Bin 0 -> 768 bytes
>  roms/Makefile               |   7 +++++++
>  roms/vbootrom               |   1 +
>  7 files changed, 20 insertions(+)
>  create mode 100644 pc-bios/npcm7xx_bootrom.bin
>  create mode 160000 roms/vbootrom

This seems to need yet another rebase...

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>


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

* Re: [PATCH v8 02/14] hw/misc: Add NPCM7xx Clock Controller device model
  2020-08-25  0:16 ` [PATCH v8 02/14] hw/misc: Add NPCM7xx Clock Controller " Havard Skinnemoen via
@ 2020-09-04  9:32   ` Philippe Mathieu-Daudé
  2020-09-04 22:02     ` Havard Skinnemoen
  0 siblings, 1 reply; 35+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-04  9:32 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: Avi.Fishman, qemu-devel, kfting, qemu-arm, Cédric Le Goater,
	Joel Stanley

On 8/25/20 2:16 AM, Havard Skinnemoen via wrote:
> Enough functionality to boot the Linux kernel has been implemented. This
> includes:
> 
>   - Correct power-on reset values so the various clock rates can be
>     accurately calculated.
>   - Clock enables stick around when written.
> 
> In addition, a best effort attempt to implement SECCNT and CNTR25M was
> made even though I don't think the kernel needs them.
> 
> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> Reviewed-by: Joel Stanley <joel@jms.id.au>
> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  include/hw/misc/npcm7xx_clk.h |  48 ++++++
>  hw/misc/npcm7xx_clk.c         | 266 ++++++++++++++++++++++++++++++++++
>  hw/misc/meson.build           |   1 +
>  hw/misc/trace-events          |   4 +
>  4 files changed, 319 insertions(+)
>  create mode 100644 include/hw/misc/npcm7xx_clk.h
>  create mode 100644 hw/misc/npcm7xx_clk.c
> 
> diff --git a/include/hw/misc/npcm7xx_clk.h b/include/hw/misc/npcm7xx_clk.h
> new file mode 100644
> index 0000000000..cdcc9e8534
> --- /dev/null
> +++ b/include/hw/misc/npcm7xx_clk.h
> @@ -0,0 +1,48 @@
> +/*
> + * Nuvoton NPCM7xx Clock Control Registers.
> + *
> + * Copyright 2020 Google LLC
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + * for more details.
> + */
> +#ifndef NPCM7XX_CLK_H
> +#define NPCM7XX_CLK_H
> +
> +#include "exec/memory.h"
> +#include "hw/sysbus.h"
> +
> +/*
> + * The reference clock frequency for the timer modules, and the SECCNT and
> + * CNTR25M registers in this module, is always 25 MHz.
> + */
> +#define NPCM7XX_TIMER_REF_HZ            (25000000)
> +
> +/*
> + * Number of registers in our device state structure. Don't change this without
> + * incrementing the version_id in the vmstate.
> + */
> +#define NPCM7XX_CLK_NR_REGS             (0x70 / sizeof(uint32_t))
> +
> +typedef struct NPCM7xxCLKState {
> +    SysBusDevice parent;
> +
> +    MemoryRegion iomem;
> +
> +    uint32_t regs[NPCM7XX_CLK_NR_REGS];
> +
> +    /* Time reference for SECCNT and CNTR25M, initialized by power on reset */
> +    int64_t ref_ns;
> +} NPCM7xxCLKState;
> +
> +#define TYPE_NPCM7XX_CLK "npcm7xx-clk"
> +#define NPCM7XX_CLK(obj) OBJECT_CHECK(NPCM7xxCLKState, (obj), TYPE_NPCM7XX_CLK)
> +
> +#endif /* NPCM7XX_CLK_H */
> diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
> new file mode 100644
> index 0000000000..21ab4200d1
> --- /dev/null
> +++ b/hw/misc/npcm7xx_clk.c
> @@ -0,0 +1,266 @@
> +/*
> + * Nuvoton NPCM7xx Clock Control Registers.
> + *
> + * Copyright 2020 Google LLC
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + * for more details.
> + */
> +
> +#include "qemu/osdep.h"
> +
> +#include "hw/misc/npcm7xx_clk.h"
> +#include "migration/vmstate.h"
> +#include "qemu/error-report.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qemu/timer.h"
> +#include "qemu/units.h"
> +#include "trace.h"
> +
> +#define PLLCON_LOKI     BIT(31)
> +#define PLLCON_LOKS     BIT(30)
> +#define PLLCON_PWDEN    BIT(12)
> +
> +enum NPCM7xxCLKRegisters {
> +    NPCM7XX_CLK_CLKEN1,
> +    NPCM7XX_CLK_CLKSEL,
> +    NPCM7XX_CLK_CLKDIV1,
> +    NPCM7XX_CLK_PLLCON0,
> +    NPCM7XX_CLK_PLLCON1,
> +    NPCM7XX_CLK_SWRSTR,
> +    NPCM7XX_CLK_IPSRST1         = 0x20 / sizeof(uint32_t),
> +    NPCM7XX_CLK_IPSRST2,
> +    NPCM7XX_CLK_CLKEN2,
> +    NPCM7XX_CLK_CLKDIV2,
> +    NPCM7XX_CLK_CLKEN3,
> +    NPCM7XX_CLK_IPSRST3,
> +    NPCM7XX_CLK_WD0RCR,
> +    NPCM7XX_CLK_WD1RCR,
> +    NPCM7XX_CLK_WD2RCR,
> +    NPCM7XX_CLK_SWRSTC1,
> +    NPCM7XX_CLK_SWRSTC2,
> +    NPCM7XX_CLK_SWRSTC3,
> +    NPCM7XX_CLK_SWRSTC4,
> +    NPCM7XX_CLK_PLLCON2,
> +    NPCM7XX_CLK_CLKDIV3,
> +    NPCM7XX_CLK_CORSTC,
> +    NPCM7XX_CLK_PLLCONG,
> +    NPCM7XX_CLK_AHBCKFI,
> +    NPCM7XX_CLK_SECCNT,
> +    NPCM7XX_CLK_CNTR25M,
> +    NPCM7XX_CLK_REGS_END,
> +};
> +
> +/*
> + * These reset values were taken from version 0.91 of the NPCM750R data sheet.
> + *
> + * All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on
> + * core domain reset, but this reset type is not yet supported by QEMU.
> + */
> +static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
> +    [NPCM7XX_CLK_CLKEN1]        = 0xffffffff,
> +    [NPCM7XX_CLK_CLKSEL]        = 0x004aaaaa,
> +    [NPCM7XX_CLK_CLKDIV1]       = 0x5413f855,
> +    [NPCM7XX_CLK_PLLCON0]       = 0x00222101 | PLLCON_LOKI,
> +    [NPCM7XX_CLK_PLLCON1]       = 0x00202101 | PLLCON_LOKI,
> +    [NPCM7XX_CLK_IPSRST1]       = 0x00001000,
> +    [NPCM7XX_CLK_IPSRST2]       = 0x80000000,
> +    [NPCM7XX_CLK_CLKEN2]        = 0xffffffff,
> +    [NPCM7XX_CLK_CLKDIV2]       = 0xaa4f8f9f,
> +    [NPCM7XX_CLK_CLKEN3]        = 0xffffffff,
> +    [NPCM7XX_CLK_IPSRST3]       = 0x03000000,
> +    [NPCM7XX_CLK_WD0RCR]        = 0xffffffff,
> +    [NPCM7XX_CLK_WD1RCR]        = 0xffffffff,
> +    [NPCM7XX_CLK_WD2RCR]        = 0xffffffff,
> +    [NPCM7XX_CLK_SWRSTC1]       = 0x00000003,
> +    [NPCM7XX_CLK_PLLCON2]       = 0x00c02105 | PLLCON_LOKI,
> +    [NPCM7XX_CLK_CORSTC]        = 0x04000003,
> +    [NPCM7XX_CLK_PLLCONG]       = 0x01228606 | PLLCON_LOKI,
> +    [NPCM7XX_CLK_AHBCKFI]       = 0x000000c8,
> +};
> +
> +static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size)
> +{
> +    uint32_t reg = offset / sizeof(uint32_t);
> +    NPCM7xxCLKState *s = opaque;
> +    int64_t now_ns;
> +    uint32_t value = 0;
> +
> +    if (reg >= NPCM7XX_CLK_NR_REGS) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",

I find "out of range" not clear, but this might be the correct
terminology.

> +                      __func__, offset);
> +        return 0;
> +    }
> +
> +    switch (reg) {
> +    case NPCM7XX_CLK_SWRSTR:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n",
> +                      __func__, offset);
> +        break;
> +
> +    case NPCM7XX_CLK_SECCNT:
> +        now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +        value = (now_ns - s->ref_ns) / NANOSECONDS_PER_SECOND;
> +        break;
> +
> +    case NPCM7XX_CLK_CNTR25M:
> +        now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +        /*
> +         * This register counts 25 MHz cycles, updating every 640 ns. It rolls
> +         * over to zero every second.
> +         *
> +         * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000.
> +         */
> +        value = (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_TIMER_REF_HZ;
> +        break;
> +
> +    default:
> +        value = s->regs[reg];
> +        break;
> +    };
> +
> +    trace_npcm7xx_clk_read(offset, value);
> +
> +    return value;
> +}
> +
> +static void npcm7xx_clk_write(void *opaque, hwaddr offset,
> +                              uint64_t v, unsigned size)
> +{
> +    uint32_t reg = offset / sizeof(uint32_t);
> +    NPCM7xxCLKState *s = opaque;
> +    uint32_t value = v;
> +
> +    trace_npcm7xx_clk_write(offset, value);
> +
> +    if (reg >= NPCM7XX_CLK_NR_REGS) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
> +                      __func__, offset);
> +        return;
> +    }
> +
> +    switch (reg) {
> +    case NPCM7XX_CLK_SWRSTR:
> +        qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n",
> +                      __func__, value);

Isn't this sufficient?

           qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);

> +        value = 0;
> +        break;
> +
> +    case NPCM7XX_CLK_PLLCON0:
> +    case NPCM7XX_CLK_PLLCON1:
> +    case NPCM7XX_CLK_PLLCON2:
> +    case NPCM7XX_CLK_PLLCONG:
> +        if (value & PLLCON_PWDEN) {
> +            /* Power down -- clear lock and indicate loss of lock */
> +            value &= ~PLLCON_LOKI;
> +            value |= PLLCON_LOKS;
> +        } else {
> +            /* Normal mode -- assume always locked */
> +            value |= PLLCON_LOKI;
> +            /* Keep LOKS unchanged unless cleared by writing 1 */
> +            if (value & PLLCON_LOKS) {
> +                value &= ~PLLCON_LOKS;
> +            } else {
> +                value |= (value & PLLCON_LOKS);
> +            }
> +        }
> +        break;
> +
> +    case NPCM7XX_CLK_CNTR25M:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
> +                      __func__, offset);
> +        return;
> +    }
> +
> +    s->regs[reg] = value;
> +}
> +
> +static const struct MemoryRegionOps npcm7xx_clk_ops = {
> +    .read       = npcm7xx_clk_read,
> +    .write      = npcm7xx_clk_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid      = {
> +        .min_access_size        = 4,
> +        .max_access_size        = 4,
> +        .unaligned              = false,
> +    },
> +};
> +
> +static void npcm7xx_clk_enter_reset(Object *obj, ResetType type)
> +{
> +    NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
> +
> +    QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
> +
> +    switch (type) {
> +    case RESET_TYPE_COLD:
> +        memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values));
> +        s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +        return;
> +    }
> +
> +    /*
> +     * A small number of registers need to be reset on a core domain reset,
> +     * but no such reset type exists yet.
> +     */
> +    qemu_log_mask(LOG_UNIMP, "%s: reset type %d not implemented.",
> +                  __func__, type);

Clean :)

> +}
> +
> +static void npcm7xx_clk_init(Object *obj)
> +{
> +    NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
> +
> +    memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s,
> +                          TYPE_NPCM7XX_CLK, 4 * KiB);
> +    sysbus_init_mmio(&s->parent, &s->iomem);
> +}
> +
> +static const VMStateDescription vmstate_npcm7xx_clk = {
> +    .name = "npcm7xx-clk",
> +    .version_id = 0,
> +    .minimum_version_id = 0,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS),

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +        VMSTATE_INT64(ref_ns, NPCM7xxCLKState),
> +        VMSTATE_END_OF_LIST(),
> +    },
> +};
> +
> +static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
> +{
> +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM7XX_CLK_NR_REGS);
> +
> +    dc->desc = "NPCM7xx Clock Control Registers";
> +    dc->vmsd = &vmstate_npcm7xx_clk;
> +    rc->phases.enter = npcm7xx_clk_enter_reset;
> +}
> +
> +static const TypeInfo npcm7xx_clk_info = {
> +    .name               = TYPE_NPCM7XX_CLK,
> +    .parent             = TYPE_SYS_BUS_DEVICE,
> +    .instance_size      = sizeof(NPCM7xxCLKState),
> +    .instance_init      = npcm7xx_clk_init,
> +    .class_init         = npcm7xx_clk_class_init,
> +};
> +
> +static void npcm7xx_clk_register_type(void)
> +{
> +    type_register_static(&npcm7xx_clk_info);
> +}
> +type_init(npcm7xx_clk_register_type);
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 99ffb9f9e8..531cc31dcc 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -51,6 +51,7 @@ softmmu_ss.add(when: 'CONFIG_IMX', if_true: files(
>  softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-hpdmc.c', 'milkymist-pfpu.c'))
>  softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c'))
>  softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
> +  'npcm7xx_clk.c',
>    'npcm7xx_gcr.c',
>  ))
>  softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files(
> diff --git a/hw/misc/trace-events b/hw/misc/trace-events
> index dbf90db8ed..a010699b4f 100644
> --- a/hw/misc/trace-events
> +++ b/hw/misc/trace-events
> @@ -111,6 +111,10 @@ mos6522_set_sr_int(void) "set sr_int"
>  mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64
>  mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x"
>  
> +# npcm7xx_clk.c
> +npcm7xx_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
> +npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
> +
>  # npcm7xx_gcr.c
>  npcm7xx_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
>  npcm7xx_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
> 


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

* Re: [PATCH v8 12/14] hw/arm/npcm7xx: add board setup stub for CPU and UART clocks
  2020-08-25  0:17 ` [PATCH v8 12/14] hw/arm/npcm7xx: add board setup stub for CPU and UART clocks Havard Skinnemoen via
@ 2020-09-04  9:34   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 35+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-04  9:34 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: kfting, qemu-arm, qemu-devel, Avi.Fishman

On 8/25/20 2:17 AM, Havard Skinnemoen via wrote:
> When booting directly into a kernel, bypassing the boot loader, the CPU and
> UART clocks are not set up correctly. This makes the system appear very
> slow, and causes the initrd boot test to fail when optimization is off.
> 
> The UART clock must run at 24 MHz. The default 25 MHz reference clock
> cannot achieve this, so switch to PLL2/2 @ 480 MHz, which works
> perfectly with the default /20 divider.
> 
> The CPU clock should run at 800 MHz, so switch it to PLL1/2. PLL1 runs
> at 800 MHz by default, so we need to double the feedback divider as well
> to make it run at 1600 MHz (so PLL1/2 runs at 800 MHz).
> 
> We don't bother checking for PLL lock because we know our emulated PLLs
> lock instantly.
> 
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  include/hw/arm/npcm7xx.h |  1 +
>  hw/arm/npcm7xx.c         | 32 ++++++++++++++++++++++++++++++++
>  2 files changed, 33 insertions(+)
> 
> diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
> index 78d0d78c52..13106af215 100644
> --- a/include/hw/arm/npcm7xx.h
> +++ b/include/hw/arm/npcm7xx.h
> @@ -37,6 +37,7 @@
>  #define NPCM7XX_SMP_LOADER_START        (0xffff0000)  /* Boot ROM */
>  #define NPCM7XX_SMP_BOOTREG_ADDR        (0xf080013c)  /* GCR.SCRPAD */
>  #define NPCM7XX_GIC_CPU_IF_ADDR         (0xf03fe100)  /* GIC within A9 */
> +#define NPCM7XX_BOARD_SETUP_ADDR        (0xffff1000)  /* Boot ROM */
>  
>  typedef struct NPCM7xxMachine {
>      MachineState        parent;
> diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> index 7884b2b03d..037f3a26f2 100644
> --- a/hw/arm/npcm7xx.c
> +++ b/hw/arm/npcm7xx.c
> @@ -55,6 +55,13 @@
>  #define NPCM7XX_ROM_BA          (0xffff0000)
>  #define NPCM7XX_ROM_SZ          (64 * KiB)
>  
> +/* Clock configuration values to be fixed up when bypassing bootloader */
> +
> +/* Run PLL1 at 1600 MHz */
> +#define NPCM7XX_PLLCON1_FIXUP_VAL   (0x00402101)
> +/* Run the CPU from PLL1 and UART from PLL2 */
> +#define NPCM7XX_CLKSEL_FIXUP_VAL    (0x004aaba9)
> +
>  /*
>   * Interrupt lines going into the GIC. This does not include internal Cortex-A9
>   * interrupts.
> @@ -132,6 +139,29 @@ static const struct {
>      },
>  };
>  
> +static void npcm7xx_write_board_setup(ARMCPU *cpu,
> +                                      const struct arm_boot_info *info)
> +{
> +    uint32_t board_setup[] = {
> +        0xe59f0010,     /* ldr r0, clk_base_addr */
> +        0xe59f1010,     /* ldr r1, pllcon1_value */
> +        0xe5801010,     /* str r1, [r0, #16] */
> +        0xe59f100c,     /* ldr r1, clksel_value */
> +        0xe5801004,     /* str r1, [r0, #4] */
> +        0xe12fff1e,     /* bx lr */
> +        NPCM7XX_CLK_BA,
> +        NPCM7XX_PLLCON1_FIXUP_VAL,
> +        NPCM7XX_CLKSEL_FIXUP_VAL,
> +    };
> +    int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(board_setup); i++) {
> +        board_setup[i] = tswap32(board_setup[i]);
> +    }
> +    rom_add_blob_fixed("board-setup", board_setup, sizeof(board_setup),
> +                       info->board_setup_addr);
> +}
> +
>  static void npcm7xx_write_secondary_boot(ARMCPU *cpu,
>                                           const struct arm_boot_info *info)
>  {
> @@ -170,6 +200,8 @@ static struct arm_boot_info npcm7xx_binfo = {
>      .gic_cpu_if_addr        = NPCM7XX_GIC_CPU_IF_ADDR,
>      .write_secondary_boot   = npcm7xx_write_secondary_boot,
>      .board_id               = -1,
> +    .board_setup_addr       = NPCM7XX_BOARD_SETUP_ADDR,
> +    .write_board_setup      = npcm7xx_write_board_setup,
>  };
>  
>  void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
> 

Very very clean :) :)

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>


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

* Re: [PATCH v8 02/14] hw/misc: Add NPCM7xx Clock Controller device model
  2020-09-04  9:32   ` Philippe Mathieu-Daudé
@ 2020-09-04 22:02     ` Havard Skinnemoen
  2020-09-04 22:38       ` Havard Skinnemoen
  0 siblings, 1 reply; 35+ messages in thread
From: Havard Skinnemoen @ 2020-09-04 22:02 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, QEMU Developers, IS20 Avi Fishman,
	CS20 KFTing, Joel Stanley, Cédric Le Goater

On Fri, Sep 4, 2020 at 2:32 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 8/25/20 2:16 AM, Havard Skinnemoen via wrote:
> > Enough functionality to boot the Linux kernel has been implemented. This
> > includes:
> >
> >   - Correct power-on reset values so the various clock rates can be
> >     accurately calculated.
> >   - Clock enables stick around when written.
> >
> > In addition, a best effort attempt to implement SECCNT and CNTR25M was
> > made even though I don't think the kernel needs them.
> >
> > Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> > Reviewed-by: Joel Stanley <joel@jms.id.au>
> > Reviewed-by: Cédric Le Goater <clg@kaod.org>
> > Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> > ---
> >  include/hw/misc/npcm7xx_clk.h |  48 ++++++
> >  hw/misc/npcm7xx_clk.c         | 266 ++++++++++++++++++++++++++++++++++
> >  hw/misc/meson.build           |   1 +
> >  hw/misc/trace-events          |   4 +
> >  4 files changed, 319 insertions(+)
> >  create mode 100644 include/hw/misc/npcm7xx_clk.h
> >  create mode 100644 hw/misc/npcm7xx_clk.c
> >
> > diff --git a/include/hw/misc/npcm7xx_clk.h b/include/hw/misc/npcm7xx_clk.h
> > new file mode 100644
> > index 0000000000..cdcc9e8534
> > --- /dev/null
> > +++ b/include/hw/misc/npcm7xx_clk.h
> > @@ -0,0 +1,48 @@
> > +/*
> > + * Nuvoton NPCM7xx Clock Control Registers.
> > + *
> > + * Copyright 2020 Google LLC
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License as published by the
> > + * Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> > + * for more details.
> > + */
> > +#ifndef NPCM7XX_CLK_H
> > +#define NPCM7XX_CLK_H
> > +
> > +#include "exec/memory.h"
> > +#include "hw/sysbus.h"
> > +
> > +/*
> > + * The reference clock frequency for the timer modules, and the SECCNT and
> > + * CNTR25M registers in this module, is always 25 MHz.
> > + */
> > +#define NPCM7XX_TIMER_REF_HZ            (25000000)
> > +
> > +/*
> > + * Number of registers in our device state structure. Don't change this without
> > + * incrementing the version_id in the vmstate.
> > + */
> > +#define NPCM7XX_CLK_NR_REGS             (0x70 / sizeof(uint32_t))
> > +
> > +typedef struct NPCM7xxCLKState {
> > +    SysBusDevice parent;
> > +
> > +    MemoryRegion iomem;
> > +
> > +    uint32_t regs[NPCM7XX_CLK_NR_REGS];
> > +
> > +    /* Time reference for SECCNT and CNTR25M, initialized by power on reset */
> > +    int64_t ref_ns;
> > +} NPCM7xxCLKState;
> > +
> > +#define TYPE_NPCM7XX_CLK "npcm7xx-clk"
> > +#define NPCM7XX_CLK(obj) OBJECT_CHECK(NPCM7xxCLKState, (obj), TYPE_NPCM7XX_CLK)
> > +
> > +#endif /* NPCM7XX_CLK_H */
> > diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
> > new file mode 100644
> > index 0000000000..21ab4200d1
> > --- /dev/null
> > +++ b/hw/misc/npcm7xx_clk.c
> > @@ -0,0 +1,266 @@
> > +/*
> > + * Nuvoton NPCM7xx Clock Control Registers.
> > + *
> > + * Copyright 2020 Google LLC
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License as published by the
> > + * Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> > + * for more details.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +
> > +#include "hw/misc/npcm7xx_clk.h"
> > +#include "migration/vmstate.h"
> > +#include "qemu/error-report.h"
> > +#include "qemu/log.h"
> > +#include "qemu/module.h"
> > +#include "qemu/timer.h"
> > +#include "qemu/units.h"
> > +#include "trace.h"
> > +
> > +#define PLLCON_LOKI     BIT(31)
> > +#define PLLCON_LOKS     BIT(30)
> > +#define PLLCON_PWDEN    BIT(12)
> > +
> > +enum NPCM7xxCLKRegisters {
> > +    NPCM7XX_CLK_CLKEN1,
> > +    NPCM7XX_CLK_CLKSEL,
> > +    NPCM7XX_CLK_CLKDIV1,
> > +    NPCM7XX_CLK_PLLCON0,
> > +    NPCM7XX_CLK_PLLCON1,
> > +    NPCM7XX_CLK_SWRSTR,
> > +    NPCM7XX_CLK_IPSRST1         = 0x20 / sizeof(uint32_t),
> > +    NPCM7XX_CLK_IPSRST2,
> > +    NPCM7XX_CLK_CLKEN2,
> > +    NPCM7XX_CLK_CLKDIV2,
> > +    NPCM7XX_CLK_CLKEN3,
> > +    NPCM7XX_CLK_IPSRST3,
> > +    NPCM7XX_CLK_WD0RCR,
> > +    NPCM7XX_CLK_WD1RCR,
> > +    NPCM7XX_CLK_WD2RCR,
> > +    NPCM7XX_CLK_SWRSTC1,
> > +    NPCM7XX_CLK_SWRSTC2,
> > +    NPCM7XX_CLK_SWRSTC3,
> > +    NPCM7XX_CLK_SWRSTC4,
> > +    NPCM7XX_CLK_PLLCON2,
> > +    NPCM7XX_CLK_CLKDIV3,
> > +    NPCM7XX_CLK_CORSTC,
> > +    NPCM7XX_CLK_PLLCONG,
> > +    NPCM7XX_CLK_AHBCKFI,
> > +    NPCM7XX_CLK_SECCNT,
> > +    NPCM7XX_CLK_CNTR25M,
> > +    NPCM7XX_CLK_REGS_END,
> > +};
> > +
> > +/*
> > + * These reset values were taken from version 0.91 of the NPCM750R data sheet.
> > + *
> > + * All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on
> > + * core domain reset, but this reset type is not yet supported by QEMU.
> > + */
> > +static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
> > +    [NPCM7XX_CLK_CLKEN1]        = 0xffffffff,
> > +    [NPCM7XX_CLK_CLKSEL]        = 0x004aaaaa,
> > +    [NPCM7XX_CLK_CLKDIV1]       = 0x5413f855,
> > +    [NPCM7XX_CLK_PLLCON0]       = 0x00222101 | PLLCON_LOKI,
> > +    [NPCM7XX_CLK_PLLCON1]       = 0x00202101 | PLLCON_LOKI,
> > +    [NPCM7XX_CLK_IPSRST1]       = 0x00001000,
> > +    [NPCM7XX_CLK_IPSRST2]       = 0x80000000,
> > +    [NPCM7XX_CLK_CLKEN2]        = 0xffffffff,
> > +    [NPCM7XX_CLK_CLKDIV2]       = 0xaa4f8f9f,
> > +    [NPCM7XX_CLK_CLKEN3]        = 0xffffffff,
> > +    [NPCM7XX_CLK_IPSRST3]       = 0x03000000,
> > +    [NPCM7XX_CLK_WD0RCR]        = 0xffffffff,
> > +    [NPCM7XX_CLK_WD1RCR]        = 0xffffffff,
> > +    [NPCM7XX_CLK_WD2RCR]        = 0xffffffff,
> > +    [NPCM7XX_CLK_SWRSTC1]       = 0x00000003,
> > +    [NPCM7XX_CLK_PLLCON2]       = 0x00c02105 | PLLCON_LOKI,
> > +    [NPCM7XX_CLK_CORSTC]        = 0x04000003,
> > +    [NPCM7XX_CLK_PLLCONG]       = 0x01228606 | PLLCON_LOKI,
> > +    [NPCM7XX_CLK_AHBCKFI]       = 0x000000c8,
> > +};
> > +
> > +static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size)
> > +{
> > +    uint32_t reg = offset / sizeof(uint32_t);
> > +    NPCM7xxCLKState *s = opaque;
> > +    int64_t now_ns;
> > +    uint32_t value = 0;
> > +
> > +    if (reg >= NPCM7XX_CLK_NR_REGS) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
>
> I find "out of range" not clear, but this might be the correct
> terminology.
>
> > +                      __func__, offset);
> > +        return 0;
> > +    }
> > +
> > +    switch (reg) {
> > +    case NPCM7XX_CLK_SWRSTR:
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n",
> > +                      __func__, offset);
> > +        break;
> > +
> > +    case NPCM7XX_CLK_SECCNT:
> > +        now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > +        value = (now_ns - s->ref_ns) / NANOSECONDS_PER_SECOND;
> > +        break;
> > +
> > +    case NPCM7XX_CLK_CNTR25M:
> > +        now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > +        /*
> > +         * This register counts 25 MHz cycles, updating every 640 ns. It rolls
> > +         * over to zero every second.
> > +         *
> > +         * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000.
> > +         */
> > +        value = (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_TIMER_REF_HZ;
> > +        break;
> > +
> > +    default:
> > +        value = s->regs[reg];
> > +        break;
> > +    };
> > +
> > +    trace_npcm7xx_clk_read(offset, value);
> > +
> > +    return value;
> > +}
> > +
> > +static void npcm7xx_clk_write(void *opaque, hwaddr offset,
> > +                              uint64_t v, unsigned size)
> > +{
> > +    uint32_t reg = offset / sizeof(uint32_t);
> > +    NPCM7xxCLKState *s = opaque;
> > +    uint32_t value = v;
> > +
> > +    trace_npcm7xx_clk_write(offset, value);
> > +
> > +    if (reg >= NPCM7XX_CLK_NR_REGS) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
> > +                      __func__, offset);
> > +        return;
> > +    }
> > +
> > +    switch (reg) {
> > +    case NPCM7XX_CLK_SWRSTR:
> > +        qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n",
> > +                      __func__, value);
>
> Isn't this sufficient?
>
>            qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);

It's not quite that easy; this register holds 4 bits, each of which
maps to a separate register which defines the modules to reset. It's
not that hard, but a little more than I'd like to add to the series at
this point. I'll send a followup patch once the initial series is in.

>
> > +        value = 0;
> > +        break;
> > +
> > +    case NPCM7XX_CLK_PLLCON0:
> > +    case NPCM7XX_CLK_PLLCON1:
> > +    case NPCM7XX_CLK_PLLCON2:
> > +    case NPCM7XX_CLK_PLLCONG:
> > +        if (value & PLLCON_PWDEN) {
> > +            /* Power down -- clear lock and indicate loss of lock */
> > +            value &= ~PLLCON_LOKI;
> > +            value |= PLLCON_LOKS;
> > +        } else {
> > +            /* Normal mode -- assume always locked */
> > +            value |= PLLCON_LOKI;
> > +            /* Keep LOKS unchanged unless cleared by writing 1 */
> > +            if (value & PLLCON_LOKS) {
> > +                value &= ~PLLCON_LOKS;
> > +            } else {
> > +                value |= (value & PLLCON_LOKS);
> > +            }
> > +        }
> > +        break;
> > +
> > +    case NPCM7XX_CLK_CNTR25M:
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
> > +                      __func__, offset);
> > +        return;
> > +    }
> > +
> > +    s->regs[reg] = value;
> > +}
> > +
> > +static const struct MemoryRegionOps npcm7xx_clk_ops = {
> > +    .read       = npcm7xx_clk_read,
> > +    .write      = npcm7xx_clk_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid      = {
> > +        .min_access_size        = 4,
> > +        .max_access_size        = 4,
> > +        .unaligned              = false,
> > +    },
> > +};
> > +
> > +static void npcm7xx_clk_enter_reset(Object *obj, ResetType type)
> > +{
> > +    NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
> > +
> > +    QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
> > +
> > +    switch (type) {
> > +    case RESET_TYPE_COLD:
> > +        memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values));
> > +        s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > +        return;
> > +    }
> > +
> > +    /*
> > +     * A small number of registers need to be reset on a core domain reset,
> > +     * but no such reset type exists yet.
> > +     */
> > +    qemu_log_mask(LOG_UNIMP, "%s: reset type %d not implemented.",
> > +                  __func__, type);
>
> Clean :)
>
> > +}
> > +
> > +static void npcm7xx_clk_init(Object *obj)
> > +{
> > +    NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
> > +
> > +    memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s,
> > +                          TYPE_NPCM7XX_CLK, 4 * KiB);
> > +    sysbus_init_mmio(&s->parent, &s->iomem);
> > +}
> > +
> > +static const VMStateDescription vmstate_npcm7xx_clk = {
> > +    .name = "npcm7xx-clk",
> > +    .version_id = 0,
> > +    .minimum_version_id = 0,
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS),
>
> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

Thank you!

> > +        VMSTATE_INT64(ref_ns, NPCM7xxCLKState),
> > +        VMSTATE_END_OF_LIST(),
> > +    },
> > +};
> > +
> > +static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
> > +{
> > +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > +    QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM7XX_CLK_NR_REGS);
> > +
> > +    dc->desc = "NPCM7xx Clock Control Registers";
> > +    dc->vmsd = &vmstate_npcm7xx_clk;
> > +    rc->phases.enter = npcm7xx_clk_enter_reset;
> > +}
> > +
> > +static const TypeInfo npcm7xx_clk_info = {
> > +    .name               = TYPE_NPCM7XX_CLK,
> > +    .parent             = TYPE_SYS_BUS_DEVICE,
> > +    .instance_size      = sizeof(NPCM7xxCLKState),
> > +    .instance_init      = npcm7xx_clk_init,
> > +    .class_init         = npcm7xx_clk_class_init,
> > +};
> > +
> > +static void npcm7xx_clk_register_type(void)
> > +{
> > +    type_register_static(&npcm7xx_clk_info);
> > +}
> > +type_init(npcm7xx_clk_register_type);
> > diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> > index 99ffb9f9e8..531cc31dcc 100644
> > --- a/hw/misc/meson.build
> > +++ b/hw/misc/meson.build
> > @@ -51,6 +51,7 @@ softmmu_ss.add(when: 'CONFIG_IMX', if_true: files(
> >  softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-hpdmc.c', 'milkymist-pfpu.c'))
> >  softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c'))
> >  softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
> > +  'npcm7xx_clk.c',
> >    'npcm7xx_gcr.c',
> >  ))
> >  softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files(
> > diff --git a/hw/misc/trace-events b/hw/misc/trace-events
> > index dbf90db8ed..a010699b4f 100644
> > --- a/hw/misc/trace-events
> > +++ b/hw/misc/trace-events
> > @@ -111,6 +111,10 @@ mos6522_set_sr_int(void) "set sr_int"
> >  mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64
> >  mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x"
> >
> > +# npcm7xx_clk.c
> > +npcm7xx_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
> > +npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
> > +
> >  # npcm7xx_gcr.c
> >  npcm7xx_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
> >  npcm7xx_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
> >


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

* Re: [PATCH v8 02/14] hw/misc: Add NPCM7xx Clock Controller device model
  2020-09-04 22:02     ` Havard Skinnemoen
@ 2020-09-04 22:38       ` Havard Skinnemoen
  2020-09-07 13:40         ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 35+ messages in thread
From: Havard Skinnemoen @ 2020-09-04 22:38 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, QEMU Developers, IS20 Avi Fishman,
	CS20 KFTing, Joel Stanley, Cédric Le Goater

On Fri, Sep 4, 2020 at 3:02 PM Havard Skinnemoen <hskinnemoen@google.com> wrote:
>
> On Fri, Sep 4, 2020 at 2:32 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >
> > On 8/25/20 2:16 AM, Havard Skinnemoen via wrote:
> > > Enough functionality to boot the Linux kernel has been implemented. This
> > > includes:
> > >
> > >   - Correct power-on reset values so the various clock rates can be
> > >     accurately calculated.
> > >   - Clock enables stick around when written.
> > >
> > > In addition, a best effort attempt to implement SECCNT and CNTR25M was
> > > made even though I don't think the kernel needs them.
> > >
> > > Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> > > Reviewed-by: Joel Stanley <joel@jms.id.au>
> > > Reviewed-by: Cédric Le Goater <clg@kaod.org>
> > > Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> > > ---
> > >  include/hw/misc/npcm7xx_clk.h |  48 ++++++
> > >  hw/misc/npcm7xx_clk.c         | 266 ++++++++++++++++++++++++++++++++++
> > >  hw/misc/meson.build           |   1 +
> > >  hw/misc/trace-events          |   4 +
> > >  4 files changed, 319 insertions(+)
> > >  create mode 100644 include/hw/misc/npcm7xx_clk.h
> > >  create mode 100644 hw/misc/npcm7xx_clk.c
> > >
> > > diff --git a/include/hw/misc/npcm7xx_clk.h b/include/hw/misc/npcm7xx_clk.h
> > > new file mode 100644
> > > index 0000000000..cdcc9e8534
> > > --- /dev/null
> > > +++ b/include/hw/misc/npcm7xx_clk.h
> > > @@ -0,0 +1,48 @@
> > > +/*
> > > + * Nuvoton NPCM7xx Clock Control Registers.
> > > + *
> > > + * Copyright 2020 Google LLC
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify it
> > > + * under the terms of the GNU General Public License as published by the
> > > + * Free Software Foundation; either version 2 of the License, or
> > > + * (at your option) any later version.
> > > + *
> > > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> > > + * for more details.
> > > + */
> > > +#ifndef NPCM7XX_CLK_H
> > > +#define NPCM7XX_CLK_H
> > > +
> > > +#include "exec/memory.h"
> > > +#include "hw/sysbus.h"
> > > +
> > > +/*
> > > + * The reference clock frequency for the timer modules, and the SECCNT and
> > > + * CNTR25M registers in this module, is always 25 MHz.
> > > + */
> > > +#define NPCM7XX_TIMER_REF_HZ            (25000000)
> > > +
> > > +/*
> > > + * Number of registers in our device state structure. Don't change this without
> > > + * incrementing the version_id in the vmstate.
> > > + */
> > > +#define NPCM7XX_CLK_NR_REGS             (0x70 / sizeof(uint32_t))
> > > +
> > > +typedef struct NPCM7xxCLKState {
> > > +    SysBusDevice parent;
> > > +
> > > +    MemoryRegion iomem;
> > > +
> > > +    uint32_t regs[NPCM7XX_CLK_NR_REGS];
> > > +
> > > +    /* Time reference for SECCNT and CNTR25M, initialized by power on reset */
> > > +    int64_t ref_ns;
> > > +} NPCM7xxCLKState;
> > > +
> > > +#define TYPE_NPCM7XX_CLK "npcm7xx-clk"
> > > +#define NPCM7XX_CLK(obj) OBJECT_CHECK(NPCM7xxCLKState, (obj), TYPE_NPCM7XX_CLK)
> > > +
> > > +#endif /* NPCM7XX_CLK_H */
> > > diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
> > > new file mode 100644
> > > index 0000000000..21ab4200d1
> > > --- /dev/null
> > > +++ b/hw/misc/npcm7xx_clk.c
> > > @@ -0,0 +1,266 @@
> > > +/*
> > > + * Nuvoton NPCM7xx Clock Control Registers.
> > > + *
> > > + * Copyright 2020 Google LLC
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify it
> > > + * under the terms of the GNU General Public License as published by the
> > > + * Free Software Foundation; either version 2 of the License, or
> > > + * (at your option) any later version.
> > > + *
> > > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> > > + * for more details.
> > > + */
> > > +
> > > +#include "qemu/osdep.h"
> > > +
> > > +#include "hw/misc/npcm7xx_clk.h"
> > > +#include "migration/vmstate.h"
> > > +#include "qemu/error-report.h"
> > > +#include "qemu/log.h"
> > > +#include "qemu/module.h"
> > > +#include "qemu/timer.h"
> > > +#include "qemu/units.h"
> > > +#include "trace.h"
> > > +
> > > +#define PLLCON_LOKI     BIT(31)
> > > +#define PLLCON_LOKS     BIT(30)
> > > +#define PLLCON_PWDEN    BIT(12)
> > > +
> > > +enum NPCM7xxCLKRegisters {
> > > +    NPCM7XX_CLK_CLKEN1,
> > > +    NPCM7XX_CLK_CLKSEL,
> > > +    NPCM7XX_CLK_CLKDIV1,
> > > +    NPCM7XX_CLK_PLLCON0,
> > > +    NPCM7XX_CLK_PLLCON1,
> > > +    NPCM7XX_CLK_SWRSTR,
> > > +    NPCM7XX_CLK_IPSRST1         = 0x20 / sizeof(uint32_t),
> > > +    NPCM7XX_CLK_IPSRST2,
> > > +    NPCM7XX_CLK_CLKEN2,
> > > +    NPCM7XX_CLK_CLKDIV2,
> > > +    NPCM7XX_CLK_CLKEN3,
> > > +    NPCM7XX_CLK_IPSRST3,
> > > +    NPCM7XX_CLK_WD0RCR,
> > > +    NPCM7XX_CLK_WD1RCR,
> > > +    NPCM7XX_CLK_WD2RCR,
> > > +    NPCM7XX_CLK_SWRSTC1,
> > > +    NPCM7XX_CLK_SWRSTC2,
> > > +    NPCM7XX_CLK_SWRSTC3,
> > > +    NPCM7XX_CLK_SWRSTC4,
> > > +    NPCM7XX_CLK_PLLCON2,
> > > +    NPCM7XX_CLK_CLKDIV3,
> > > +    NPCM7XX_CLK_CORSTC,
> > > +    NPCM7XX_CLK_PLLCONG,
> > > +    NPCM7XX_CLK_AHBCKFI,
> > > +    NPCM7XX_CLK_SECCNT,
> > > +    NPCM7XX_CLK_CNTR25M,
> > > +    NPCM7XX_CLK_REGS_END,
> > > +};
> > > +
> > > +/*
> > > + * These reset values were taken from version 0.91 of the NPCM750R data sheet.
> > > + *
> > > + * All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on
> > > + * core domain reset, but this reset type is not yet supported by QEMU.
> > > + */
> > > +static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
> > > +    [NPCM7XX_CLK_CLKEN1]        = 0xffffffff,
> > > +    [NPCM7XX_CLK_CLKSEL]        = 0x004aaaaa,
> > > +    [NPCM7XX_CLK_CLKDIV1]       = 0x5413f855,
> > > +    [NPCM7XX_CLK_PLLCON0]       = 0x00222101 | PLLCON_LOKI,
> > > +    [NPCM7XX_CLK_PLLCON1]       = 0x00202101 | PLLCON_LOKI,
> > > +    [NPCM7XX_CLK_IPSRST1]       = 0x00001000,
> > > +    [NPCM7XX_CLK_IPSRST2]       = 0x80000000,
> > > +    [NPCM7XX_CLK_CLKEN2]        = 0xffffffff,
> > > +    [NPCM7XX_CLK_CLKDIV2]       = 0xaa4f8f9f,
> > > +    [NPCM7XX_CLK_CLKEN3]        = 0xffffffff,
> > > +    [NPCM7XX_CLK_IPSRST3]       = 0x03000000,
> > > +    [NPCM7XX_CLK_WD0RCR]        = 0xffffffff,
> > > +    [NPCM7XX_CLK_WD1RCR]        = 0xffffffff,
> > > +    [NPCM7XX_CLK_WD2RCR]        = 0xffffffff,
> > > +    [NPCM7XX_CLK_SWRSTC1]       = 0x00000003,
> > > +    [NPCM7XX_CLK_PLLCON2]       = 0x00c02105 | PLLCON_LOKI,
> > > +    [NPCM7XX_CLK_CORSTC]        = 0x04000003,
> > > +    [NPCM7XX_CLK_PLLCONG]       = 0x01228606 | PLLCON_LOKI,
> > > +    [NPCM7XX_CLK_AHBCKFI]       = 0x000000c8,
> > > +};
> > > +
> > > +static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size)
> > > +{
> > > +    uint32_t reg = offset / sizeof(uint32_t);
> > > +    NPCM7xxCLKState *s = opaque;
> > > +    int64_t now_ns;
> > > +    uint32_t value = 0;
> > > +
> > > +    if (reg >= NPCM7XX_CLK_NR_REGS) {
> > > +        qemu_log_mask(LOG_GUEST_ERROR,
> > > +                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
> >
> > I find "out of range" not clear, but this might be the correct
> > terminology.
> >
> > > +                      __func__, offset);
> > > +        return 0;
> > > +    }
> > > +
> > > +    switch (reg) {
> > > +    case NPCM7XX_CLK_SWRSTR:
> > > +        qemu_log_mask(LOG_GUEST_ERROR,
> > > +                      "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n",
> > > +                      __func__, offset);
> > > +        break;
> > > +
> > > +    case NPCM7XX_CLK_SECCNT:
> > > +        now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > > +        value = (now_ns - s->ref_ns) / NANOSECONDS_PER_SECOND;
> > > +        break;
> > > +
> > > +    case NPCM7XX_CLK_CNTR25M:
> > > +        now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > > +        /*
> > > +         * This register counts 25 MHz cycles, updating every 640 ns. It rolls
> > > +         * over to zero every second.
> > > +         *
> > > +         * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000.
> > > +         */
> > > +        value = (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_TIMER_REF_HZ;
> > > +        break;
> > > +
> > > +    default:
> > > +        value = s->regs[reg];
> > > +        break;
> > > +    };
> > > +
> > > +    trace_npcm7xx_clk_read(offset, value);
> > > +
> > > +    return value;
> > > +}
> > > +
> > > +static void npcm7xx_clk_write(void *opaque, hwaddr offset,
> > > +                              uint64_t v, unsigned size)
> > > +{
> > > +    uint32_t reg = offset / sizeof(uint32_t);
> > > +    NPCM7xxCLKState *s = opaque;
> > > +    uint32_t value = v;
> > > +
> > > +    trace_npcm7xx_clk_write(offset, value);
> > > +
> > > +    if (reg >= NPCM7XX_CLK_NR_REGS) {
> > > +        qemu_log_mask(LOG_GUEST_ERROR,
> > > +                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
> > > +                      __func__, offset);
> > > +        return;
> > > +    }
> > > +
> > > +    switch (reg) {
> > > +    case NPCM7XX_CLK_SWRSTR:
> > > +        qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n",
> > > +                      __func__, value);
> >
> > Isn't this sufficient?
> >
> >            qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
>
> It's not quite that easy; this register holds 4 bits, each of which
> maps to a separate register which defines the modules to reset. It's
> not that hard, but a little more than I'd like to add to the series at
> this point. I'll send a followup patch once the initial series is in.

Actually, I'm not sure if this would have any effect on being able to
reboot. Running with -d unimp shows:

reboot: Restarting system
npcm7xx_timer_write: WTCR write not implemented: 0x00000083
Reboot failed -- System halted

So we need to implement watchdog support, which is something we were
planning to do fairly soon.

> >
> > > +        value = 0;
> > > +        break;
> > > +
> > > +    case NPCM7XX_CLK_PLLCON0:
> > > +    case NPCM7XX_CLK_PLLCON1:
> > > +    case NPCM7XX_CLK_PLLCON2:
> > > +    case NPCM7XX_CLK_PLLCONG:
> > > +        if (value & PLLCON_PWDEN) {
> > > +            /* Power down -- clear lock and indicate loss of lock */
> > > +            value &= ~PLLCON_LOKI;
> > > +            value |= PLLCON_LOKS;
> > > +        } else {
> > > +            /* Normal mode -- assume always locked */
> > > +            value |= PLLCON_LOKI;
> > > +            /* Keep LOKS unchanged unless cleared by writing 1 */
> > > +            if (value & PLLCON_LOKS) {
> > > +                value &= ~PLLCON_LOKS;
> > > +            } else {
> > > +                value |= (value & PLLCON_LOKS);
> > > +            }
> > > +        }
> > > +        break;
> > > +
> > > +    case NPCM7XX_CLK_CNTR25M:
> > > +        qemu_log_mask(LOG_GUEST_ERROR,
> > > +                      "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
> > > +                      __func__, offset);
> > > +        return;
> > > +    }
> > > +
> > > +    s->regs[reg] = value;
> > > +}
> > > +
> > > +static const struct MemoryRegionOps npcm7xx_clk_ops = {
> > > +    .read       = npcm7xx_clk_read,
> > > +    .write      = npcm7xx_clk_write,
> > > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > > +    .valid      = {
> > > +        .min_access_size        = 4,
> > > +        .max_access_size        = 4,
> > > +        .unaligned              = false,
> > > +    },
> > > +};
> > > +
> > > +static void npcm7xx_clk_enter_reset(Object *obj, ResetType type)
> > > +{
> > > +    NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
> > > +
> > > +    QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
> > > +
> > > +    switch (type) {
> > > +    case RESET_TYPE_COLD:
> > > +        memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values));
> > > +        s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > > +        return;
> > > +    }
> > > +
> > > +    /*
> > > +     * A small number of registers need to be reset on a core domain reset,
> > > +     * but no such reset type exists yet.
> > > +     */
> > > +    qemu_log_mask(LOG_UNIMP, "%s: reset type %d not implemented.",
> > > +                  __func__, type);
> >
> > Clean :)
> >
> > > +}
> > > +
> > > +static void npcm7xx_clk_init(Object *obj)
> > > +{
> > > +    NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
> > > +
> > > +    memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s,
> > > +                          TYPE_NPCM7XX_CLK, 4 * KiB);
> > > +    sysbus_init_mmio(&s->parent, &s->iomem);
> > > +}
> > > +
> > > +static const VMStateDescription vmstate_npcm7xx_clk = {
> > > +    .name = "npcm7xx-clk",
> > > +    .version_id = 0,
> > > +    .minimum_version_id = 0,
> > > +    .fields = (VMStateField[]) {
> > > +        VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS),
> >
> > Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> > Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
>
> Thank you!
>
> > > +        VMSTATE_INT64(ref_ns, NPCM7xxCLKState),
> > > +        VMSTATE_END_OF_LIST(),
> > > +    },
> > > +};
> > > +
> > > +static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
> > > +{
> > > +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> > > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > > +
> > > +    QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM7XX_CLK_NR_REGS);
> > > +
> > > +    dc->desc = "NPCM7xx Clock Control Registers";
> > > +    dc->vmsd = &vmstate_npcm7xx_clk;
> > > +    rc->phases.enter = npcm7xx_clk_enter_reset;
> > > +}
> > > +
> > > +static const TypeInfo npcm7xx_clk_info = {
> > > +    .name               = TYPE_NPCM7XX_CLK,
> > > +    .parent             = TYPE_SYS_BUS_DEVICE,
> > > +    .instance_size      = sizeof(NPCM7xxCLKState),
> > > +    .instance_init      = npcm7xx_clk_init,
> > > +    .class_init         = npcm7xx_clk_class_init,
> > > +};
> > > +
> > > +static void npcm7xx_clk_register_type(void)
> > > +{
> > > +    type_register_static(&npcm7xx_clk_info);
> > > +}
> > > +type_init(npcm7xx_clk_register_type);
> > > diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> > > index 99ffb9f9e8..531cc31dcc 100644
> > > --- a/hw/misc/meson.build
> > > +++ b/hw/misc/meson.build
> > > @@ -51,6 +51,7 @@ softmmu_ss.add(when: 'CONFIG_IMX', if_true: files(
> > >  softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-hpdmc.c', 'milkymist-pfpu.c'))
> > >  softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c'))
> > >  softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
> > > +  'npcm7xx_clk.c',
> > >    'npcm7xx_gcr.c',
> > >  ))
> > >  softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files(
> > > diff --git a/hw/misc/trace-events b/hw/misc/trace-events
> > > index dbf90db8ed..a010699b4f 100644
> > > --- a/hw/misc/trace-events
> > > +++ b/hw/misc/trace-events
> > > @@ -111,6 +111,10 @@ mos6522_set_sr_int(void) "set sr_int"
> > >  mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64
> > >  mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x"
> > >
> > > +# npcm7xx_clk.c
> > > +npcm7xx_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
> > > +npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
> > > +
> > >  # npcm7xx_gcr.c
> > >  npcm7xx_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
> > >  npcm7xx_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
> > >


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

* Re: [PATCH v8 02/14] hw/misc: Add NPCM7xx Clock Controller device model
  2020-09-04 22:38       ` Havard Skinnemoen
@ 2020-09-07 13:40         ` Philippe Mathieu-Daudé
  2020-09-07 17:58           ` Havard Skinnemoen
  0 siblings, 1 reply; 35+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-07 13:40 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: Peter Maydell, QEMU Developers, CS20 KFTing,
	Cédric Le Goater, qemu-arm, Joel Stanley, IS20 Avi Fishman

On 9/5/20 12:38 AM, Havard Skinnemoen wrote:
> On Fri, Sep 4, 2020 at 3:02 PM Havard Skinnemoen <hskinnemoen@google.com> wrote:
>>
>> On Fri, Sep 4, 2020 at 2:32 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>>
>>> On 8/25/20 2:16 AM, Havard Skinnemoen via wrote:
>>>> Enough functionality to boot the Linux kernel has been implemented. This
>>>> includes:
>>>>
>>>>   - Correct power-on reset values so the various clock rates can be
>>>>     accurately calculated.
>>>>   - Clock enables stick around when written.
>>>>
>>>> In addition, a best effort attempt to implement SECCNT and CNTR25M was
>>>> made even though I don't think the kernel needs them.
>>>>
>>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
>>>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>>> ---
[...]
>>>> +static void npcm7xx_clk_write(void *opaque, hwaddr offset,
>>>> +                              uint64_t v, unsigned size)
>>>> +{
>>>> +    uint32_t reg = offset / sizeof(uint32_t);
>>>> +    NPCM7xxCLKState *s = opaque;
>>>> +    uint32_t value = v;
>>>> +
>>>> +    trace_npcm7xx_clk_write(offset, value);
>>>> +
>>>> +    if (reg >= NPCM7XX_CLK_NR_REGS) {
>>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>>> +                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
>>>> +                      __func__, offset);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    switch (reg) {
>>>> +    case NPCM7XX_CLK_SWRSTR:
>>>> +        qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n",
>>>> +                      __func__, value);
>>>
>>> Isn't this sufficient?
>>>
>>>            qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
>>
>> It's not quite that easy; this register holds 4 bits, each of which
>> maps to a separate register which defines the modules to reset. It's
>> not that hard, but a little more than I'd like to add to the series at
>> this point. I'll send a followup patch once the initial series is in.
> 
> Actually, I'm not sure if this would have any effect on being able to
> reboot. Running with -d unimp shows:
> 
> reboot: Restarting system
> npcm7xx_timer_write: WTCR write not implemented: 0x00000083
> Reboot failed -- System halted
> 
> So we need to implement watchdog support, which is something we were
> planning to do fairly soon.

Well this seems a guest implementation decision to prefer
wait the watchdog to kick (hard reset?) rather than doing
a soft reset.

Two different issues IMO. Anyway this is certainly not
blocking your series to get merged.

Regards,

Phil.


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

* Re: [PATCH v8 02/14] hw/misc: Add NPCM7xx Clock Controller device model
  2020-09-07 13:40         ` Philippe Mathieu-Daudé
@ 2020-09-07 17:58           ` Havard Skinnemoen
  0 siblings, 0 replies; 35+ messages in thread
From: Havard Skinnemoen @ 2020-09-07 17:58 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, QEMU Developers, IS20 Avi Fishman,
	CS20 KFTing, Joel Stanley, Cédric Le Goater

On Mon, Sep 7, 2020 at 6:40 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 9/5/20 12:38 AM, Havard Skinnemoen wrote:
> > On Fri, Sep 4, 2020 at 3:02 PM Havard Skinnemoen <hskinnemoen@google.com> wrote:
> >>
> >> On Fri, Sep 4, 2020 at 2:32 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >>>
> >>> On 8/25/20 2:16 AM, Havard Skinnemoen via wrote:
> >>>> Enough functionality to boot the Linux kernel has been implemented. This
> >>>> includes:
> >>>>
> >>>>   - Correct power-on reset values so the various clock rates can be
> >>>>     accurately calculated.
> >>>>   - Clock enables stick around when written.
> >>>>
> >>>> In addition, a best effort attempt to implement SECCNT and CNTR25M was
> >>>> made even though I don't think the kernel needs them.
> >>>>
> >>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> >>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
> >>>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> >>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> >>>> ---
> [...]
> >>>> +static void npcm7xx_clk_write(void *opaque, hwaddr offset,
> >>>> +                              uint64_t v, unsigned size)
> >>>> +{
> >>>> +    uint32_t reg = offset / sizeof(uint32_t);
> >>>> +    NPCM7xxCLKState *s = opaque;
> >>>> +    uint32_t value = v;
> >>>> +
> >>>> +    trace_npcm7xx_clk_write(offset, value);
> >>>> +
> >>>> +    if (reg >= NPCM7XX_CLK_NR_REGS) {
> >>>> +        qemu_log_mask(LOG_GUEST_ERROR,
> >>>> +                      "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
> >>>> +                      __func__, offset);
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    switch (reg) {
> >>>> +    case NPCM7XX_CLK_SWRSTR:
> >>>> +        qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n",
> >>>> +                      __func__, value);
> >>>
> >>> Isn't this sufficient?
> >>>
> >>>            qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
> >>
> >> It's not quite that easy; this register holds 4 bits, each of which
> >> maps to a separate register which defines the modules to reset. It's
> >> not that hard, but a little more than I'd like to add to the series at
> >> this point. I'll send a followup patch once the initial series is in.
> >
> > Actually, I'm not sure if this would have any effect on being able to
> > reboot. Running with -d unimp shows:
> >
> > reboot: Restarting system
> > npcm7xx_timer_write: WTCR write not implemented: 0x00000083
> > Reboot failed -- System halted
> >
> > So we need to implement watchdog support, which is something we were
> > planning to do fairly soon.
>
> Well this seems a guest implementation decision to prefer
> wait the watchdog to kick (hard reset?) rather than doing
> a soft reset.
>
> Two different issues IMO. Anyway this is certainly not
> blocking your series to get merged.

I agree, we need to implement both. I just wanted to note that
implementing SWRST alone may not be enough to make reboots work with
the current image. The SW reset registers are actually very similar to
the WD reset registers, so implementing WD reset should make it almost
trivial to add SW reset as well.

It looks like the kernel has a driver that can use SW reset to reboot,
but the DT is missing a property needed to set up the restart handler.

https://elixir.bootlin.com/linux/v5.8.7/source/drivers/reset/reset-npcm.c#L269

Havard


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

* Re: [PATCH v8 08/14] hw/nvram: NPCM7xx OTP device model
  2020-08-25  0:17 ` [PATCH v8 08/14] hw/nvram: NPCM7xx OTP device model Havard Skinnemoen via
@ 2020-09-07 19:47   ` Alexander Bulekov
  2020-09-07 19:57     ` Alexander Bulekov
  0 siblings, 1 reply; 35+ messages in thread
From: Alexander Bulekov @ 2020-09-07 19:47 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: peter.maydell, f4bug, qemu-devel, kfting, qemu-arm, Avi.Fishman

On 200824 1717, Havard Skinnemoen via wrote:
> This supports reading and writing OTP fuses and keys. Only fuse reading
> has been tested. Protection is not implemented.
> 
> Reviewed-by: Avi Fishman <avi.fishman@nuvoton.com>
> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  include/hw/arm/npcm7xx.h       |   3 +
>  include/hw/nvram/npcm7xx_otp.h |  79 ++++++
>  hw/arm/npcm7xx.c               |  29 +++
>  hw/nvram/npcm7xx_otp.c         | 439 +++++++++++++++++++++++++++++++++
>  hw/nvram/meson.build           |   1 +
>  5 files changed, 551 insertions(+)
>  create mode 100644 include/hw/nvram/npcm7xx_otp.h
>  create mode 100644 hw/nvram/npcm7xx_otp.c
> 
> diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
> index ba7495869d..5816a07a72 100644
> --- a/include/hw/arm/npcm7xx.h
> +++ b/include/hw/arm/npcm7xx.h
> @@ -20,6 +20,7 @@
>  #include "hw/cpu/a9mpcore.h"
>  #include "hw/misc/npcm7xx_clk.h"
>  #include "hw/misc/npcm7xx_gcr.h"
> +#include "hw/nvram/npcm7xx_otp.h"
>  #include "hw/timer/npcm7xx_timer.h"
>  #include "target/arm/cpu.h"
>  
> @@ -68,6 +69,8 @@ typedef struct NPCM7xxState {
>      NPCM7xxGCRState     gcr;
>      NPCM7xxCLKState     clk;
>      NPCM7xxTimerCtrlState tim[3];
> +    NPCM7xxOTPState     key_storage;
> +    NPCM7xxOTPState     fuse_array;
>  } NPCM7xxState;
>  
>  #define TYPE_NPCM7XX    "npcm7xx"
> diff --git a/include/hw/nvram/npcm7xx_otp.h b/include/hw/nvram/npcm7xx_otp.h
> new file mode 100644
> index 0000000000..156bbd151a
> --- /dev/null
> +++ b/include/hw/nvram/npcm7xx_otp.h
> @@ -0,0 +1,79 @@
> +/*
> + * Nuvoton NPCM7xx OTP (Fuse Array) Interface
> + *
> + * Copyright 2020 Google LLC
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + * for more details.
> + */
> +#ifndef NPCM7XX_OTP_H
> +#define NPCM7XX_OTP_H
> +
> +#include "exec/memory.h"
> +#include "hw/sysbus.h"
> +
> +/* Each OTP module holds 8192 bits of one-time programmable storage */
> +#define NPCM7XX_OTP_ARRAY_BITS (8192)
> +#define NPCM7XX_OTP_ARRAY_BYTES (NPCM7XX_OTP_ARRAY_BITS / BITS_PER_BYTE)
> +
> +/* Fuse array offsets */
> +#define NPCM7XX_FUSE_FUSTRAP (0)
> +#define NPCM7XX_FUSE_CP_FUSTRAP (12)
> +#define NPCM7XX_FUSE_DAC_CALIB (16)
> +#define NPCM7XX_FUSE_ADC_CALIB (24)
> +#define NPCM7XX_FUSE_DERIVATIVE (64)
> +#define NPCM7XX_FUSE_TEST_SIG (72)
> +#define NPCM7XX_FUSE_DIE_LOCATION (74)
> +#define NPCM7XX_FUSE_GP1 (80)
> +#define NPCM7XX_FUSE_GP2 (128)
> +
> +/*
> + * Number of registers in our device state structure. Don't change this without
> + * incrementing the version_id in the vmstate.
> + */
> +#define NPCM7XX_OTP_NR_REGS (0x18 / sizeof(uint32_t))
> +
> +/**
> + * struct NPCM7xxOTPState - Device state for one OTP module.
> + * @parent: System bus device.
> + * @mmio: Memory region through which registers are accessed.
> + * @regs: Register contents.
> + * @array: OTP storage array.
> + */
> +typedef struct NPCM7xxOTPState {
> +    SysBusDevice parent;
> +
> +    MemoryRegion mmio;
> +    uint32_t regs[NPCM7XX_OTP_NR_REGS];
> +    uint8_t array[NPCM7XX_OTP_ARRAY_BYTES];
> +} NPCM7xxOTPState;
> +
> +#define TYPE_NPCM7XX_OTP "npcm7xx-otp"
> +#define NPCM7XX_OTP(obj) OBJECT_CHECK(NPCM7xxOTPState, (obj), TYPE_NPCM7XX_OTP)
> +
> +#define TYPE_NPCM7XX_KEY_STORAGE "npcm7xx-key-storage"
> +#define TYPE_NPCM7XX_FUSE_ARRAY "npcm7xx-fuse-array"
> +
> +typedef struct NPCM7xxOTPClass NPCM7xxOTPClass;
> +
> +/**
> + * npcm7xx_otp_array_write - ECC encode and write data to OTP array.
> + * @s: OTP module.
> + * @data: Data to be encoded and written.
> + * @offset: Offset of first byte to be written in the OTP array.
> + * @len: Number of bytes before ECC encoding.
> + *
> + * Each nibble of data is encoded into a byte, so the number of bytes written
> + * to the array will be @len * 2.
> + */
> +extern void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
> +                                    unsigned int offset, unsigned int len);
> +
> +#endif /* NPCM7XX_OTP_H */
> diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> index 9669ac5fa0..9166002598 100644
> --- a/hw/arm/npcm7xx.c
> +++ b/hw/arm/npcm7xx.c
> @@ -34,6 +34,10 @@
>  #define NPCM7XX_MMIO_BA         (0x80000000)
>  #define NPCM7XX_MMIO_SZ         (0x7ffd0000)
>  
> +/* OTP key storage and fuse strap array */
> +#define NPCM7XX_OTP1_BA         (0xf0189000)
> +#define NPCM7XX_OTP2_BA         (0xf018a000)
> +
>  /* Core system modules. */
>  #define NPCM7XX_L2C_BA          (0xf03fc000)
>  #define NPCM7XX_CPUP_BA         (0xf03fe000)
> @@ -144,6 +148,20 @@ void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
>      arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo);
>  }
>  
> +static void npcm7xx_init_fuses(NPCM7xxState *s)
> +{
> +    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
> +    uint32_t value;
> +
> +    /*
> +     * The initial mask of disabled modules indicates the chip derivative (e.g.
> +     * NPCM750 or NPCM730).
> +     */
> +    value = tswap32(nc->disabled_modules);
> +    npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE,
> +                            sizeof(value));
> +}
> +
>  static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n)
>  {
>      return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n);
> @@ -164,6 +182,10 @@ static void npcm7xx_init(Object *obj)
>      object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
>                                "power-on-straps");
>      object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM7XX_CLK);
> +    object_initialize_child(obj, "otp1", &s->key_storage,
> +                            TYPE_NPCM7XX_KEY_STORAGE);
> +    object_initialize_child(obj, "otp2", &s->fuse_array,
> +                            TYPE_NPCM7XX_FUSE_ARRAY);
>  
>      for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
>          object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
> @@ -232,6 +254,13 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
>      sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
>      sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM7XX_CLK_BA);
>  
> +    /* OTP key storage and fuse strap array. Cannot fail. */
> +    sysbus_realize(SYS_BUS_DEVICE(&s->key_storage), &error_abort);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->key_storage), 0, NPCM7XX_OTP1_BA);
> +    sysbus_realize(SYS_BUS_DEVICE(&s->fuse_array), &error_abort);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM7XX_OTP2_BA);
> +    npcm7xx_init_fuses(s);
> +
>      /* Timer Modules (TIM). Cannot fail. */
>      QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
>      for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> diff --git a/hw/nvram/npcm7xx_otp.c b/hw/nvram/npcm7xx_otp.c
> new file mode 100644
> index 0000000000..ba4188ada8
> --- /dev/null
> +++ b/hw/nvram/npcm7xx_otp.c
> @@ -0,0 +1,439 @@
> +/*
> + * Nuvoton NPCM7xx OTP (Fuse Array) Interface
> + *
> + * Copyright 2020 Google LLC
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + * for more details.
> + */
> +
> +#include "qemu/osdep.h"
> +
> +#include "hw/nvram/npcm7xx_otp.h"
> +#include "migration/vmstate.h"
> +#include "qapi/error.h"
> +#include "qemu/bitops.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qemu/units.h"
> +
> +/* Each module has 4 KiB of register space. Only a fraction of it is used. */
> +#define NPCM7XX_OTP_REGS_SIZE (4 * KiB)
> +
> +/* 32-bit register indices. */
> +typedef enum NPCM7xxOTPRegister {
> +    NPCM7XX_OTP_FST,
> +    NPCM7XX_OTP_FADDR,
> +    NPCM7XX_OTP_FDATA,
> +    NPCM7XX_OTP_FCFG,
> +    /* Offset 0x10 is FKEYIND in OTP1, FUSTRAP in OTP2 */
> +    NPCM7XX_OTP_FKEYIND = 0x0010 / sizeof(uint32_t),
> +    NPCM7XX_OTP_FUSTRAP = 0x0010 / sizeof(uint32_t),
> +    NPCM7XX_OTP_FCTL,
> +    NPCM7XX_OTP_REGS_END,
> +} NPCM7xxOTPRegister;
> +
> +/* Register field definitions. */
> +#define FST_RIEN BIT(2)
> +#define FST_RDST BIT(1)
> +#define FST_RDY BIT(0)
> +#define FST_RO_MASK (FST_RDST | FST_RDY)
> +
> +#define FADDR_BYTEADDR(rv) extract32((rv), 0, 10)
> +#define FADDR_BITPOS(rv) extract32((rv), 10, 3)
> +
> +#define FDATA_CLEAR 0x00000001
> +
> +#define FCFG_FDIS BIT(31)
> +#define FCFG_FCFGLK_MASK 0x00ff0000
> +
> +#define FCTL_PROG_CMD1 0x00000001
> +#define FCTL_PROG_CMD2 0xbf79e5d0
> +#define FCTL_READ_CMD 0x00000002
> +
> +/**
> + * struct NPCM7xxOTPClass - OTP module class.
> + * @parent: System bus device class.
> + * @mmio_ops: MMIO register operations for this type of module.
> + *
> + * The two OTP modules (key-storage and fuse-array) have slightly different
> + * behavior, so we give them different MMIO register operations.
> + */
> +struct NPCM7xxOTPClass {
> +    SysBusDeviceClass parent;
> +
> +    const MemoryRegionOps *mmio_ops;
> +};
> +
> +#define NPCM7XX_OTP_CLASS(klass) \
> +    OBJECT_CLASS_CHECK(NPCM7xxOTPClass, (klass), TYPE_NPCM7XX_OTP)
> +#define NPCM7XX_OTP_GET_CLASS(obj) \
> +    OBJECT_GET_CLASS(NPCM7xxOTPClass, (obj), TYPE_NPCM7XX_OTP)
> +
> +static uint8_t ecc_encode_nibble(uint8_t n)
> +{
> +    uint8_t result = n;
> +
> +    result |= (((n >> 0) & 1) ^ ((n >> 1) & 1)) << 4;
> +    result |= (((n >> 2) & 1) ^ ((n >> 3) & 1)) << 5;
> +    result |= (((n >> 0) & 1) ^ ((n >> 2) & 1)) << 6;
> +    result |= (((n >> 1) & 1) ^ ((n >> 3) & 1)) << 7;
> +
> +    return result;
> +}
> +
> +void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
> +                             unsigned int offset, unsigned int len)
> +{
> +    const uint8_t *src = data;
> +    uint8_t *dst = &s->array[offset];
> +
> +    while (len-- > 0) {
> +        uint8_t c = *src++;
> +
> +        *dst++ = ecc_encode_nibble(extract8(c, 0, 4));
> +        *dst++ = ecc_encode_nibble(extract8(c, 4, 4));
> +    }
> +}
> +
> +/* Common register read handler for both OTP classes. */
> +static uint64_t npcm7xx_otp_read(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg)
> +{
> +    uint32_t value = 0;
> +
> +    switch (reg) {
> +    case NPCM7XX_OTP_FST:
> +    case NPCM7XX_OTP_FADDR:
> +    case NPCM7XX_OTP_FDATA:
> +    case NPCM7XX_OTP_FCFG:
> +        value = s->regs[reg];
> +        break;
> +
> +    case NPCM7XX_OTP_FCTL:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: read from write-only FCTL register\n",
> +                      DEVICE(s)->canonical_path);
> +        break;
> +
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: read from invalid offset 0x%zx\n",
> +                      DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
> +        break;
> +    }
> +
> +    return value;
> +}
> +
> +/* Read a byte from the OTP array into the data register. */
> +static void npcm7xx_otp_read_array(NPCM7xxOTPState *s)
> +{
> +    uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
> +
> +    s->regs[NPCM7XX_OTP_FDATA] = s->array[FADDR_BYTEADDR(faddr)];
> +    s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
> +}
> +
> +/* Program a byte from the data register into the OTP array. */
> +static void npcm7xx_otp_program_array(NPCM7xxOTPState *s)
> +{
> +    uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
> +
> +    /* Bits can only go 0->1, never 1->0. */
> +    s->array[FADDR_BYTEADDR(faddr)] |= (1U << FADDR_BITPOS(faddr));
> +    s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
> +}
> +
> +/* Compute the next value of the FCFG register. */
> +static uint32_t npcm7xx_otp_compute_fcfg(uint32_t cur_value, uint32_t new_value)
> +{
> +    uint32_t lock_mask;
> +    uint32_t value;
> +
> +    /*
> +     * FCFGLK holds sticky bits 16..23, indicating which bits in FPRGLK (8..15)
> +     * and FRDLK (0..7) that are read-only.
> +     */
> +    lock_mask = (cur_value & FCFG_FCFGLK_MASK) >> 8;
> +    lock_mask |= lock_mask >> 8;
> +    /* FDIS and FCFGLK bits are sticky (write 1 to set; can't clear). */
> +    value = cur_value & (FCFG_FDIS | FCFG_FCFGLK_MASK);
> +    /* Preserve read-only bits in FPRGLK and FRDLK */
> +    value |= cur_value & lock_mask;
> +    /* Set all bits that aren't read-only. */
> +    value |= new_value & ~lock_mask;
> +
> +    return value;
> +}
> +
> +/* Common register write handler for both OTP classes. */
> +static void npcm7xx_otp_write(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg,
> +                              uint32_t value)
> +{
> +    switch (reg) {
> +    case NPCM7XX_OTP_FST:
> +        /* RDST is cleared by writing 1 to it. */
> +        if (value & FST_RDST) {
> +            s->regs[NPCM7XX_OTP_FST] &= ~FST_RDST;
> +        }
> +        /* Preserve read-only and write-one-to-clear bits */
> +        value &= ~FST_RO_MASK;
> +        value |= s->regs[NPCM7XX_OTP_FST] & FST_RO_MASK;
> +        break;
> +
> +    case NPCM7XX_OTP_FADDR:
> +        break;
> +
> +    case NPCM7XX_OTP_FDATA:
> +        /*
> +         * This register is cleared by writing a magic value to it; no other
> +         * values can be written.
> +         */
> +        if (value == FDATA_CLEAR) {
> +            value = 0;
> +        } else {
> +            value = s->regs[NPCM7XX_OTP_FDATA];
> +        }
> +        break;
> +
> +    case NPCM7XX_OTP_FCFG:
> +        value = npcm7xx_otp_compute_fcfg(s->regs[NPCM7XX_OTP_FCFG], value);
> +        break;
> +
> +    case NPCM7XX_OTP_FCTL:
> +        switch (value) {
> +        case FCTL_READ_CMD:
> +            npcm7xx_otp_read_array(s);
> +            break;
> +
> +        case FCTL_PROG_CMD1:
> +            /*
> +             * Programming requires writing two separate magic values to this
> +             * register; this is the first one. Just store it so it can be
> +             * verified later when the second magic value is received.
> +             */
> +            break;
> +
> +        case FCTL_PROG_CMD2:
> +            /*
> +             * Only initiate programming if we received the first half of the
> +             * command immediately before this one.
> +             */
> +            if (s->regs[NPCM7XX_OTP_FCTL] == FCTL_PROG_CMD1) {
> +                npcm7xx_otp_program_array(s);
> +            }
> +            break;
> +
> +        default:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "%s: unrecognized FCNTL value 0x%" PRIx32 "\n",
> +                          DEVICE(s)->canonical_path, value);
> +            break;
> +        }
> +        if (value != FCTL_PROG_CMD1) {
> +            value = 0;
> +        }
> +        break;
> +
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: write to invalid offset 0x%zx\n",
> +                      DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
> +        return;
> +    }
> +
> +    s->regs[reg] = value;
> +}
> +
> +/* Register read handler specific to the fuse array OTP module. */
> +static uint64_t npcm7xx_fuse_array_read(void *opaque, hwaddr addr,
> +                                        unsigned int size)
> +{
> +    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
> +    NPCM7xxOTPState *s = opaque;
> +    uint32_t value;
> +
> +    /*
> +     * Only the Fuse Strap register needs special handling; all other registers
> +     * work the same way for both kinds of OTP modules.
> +     */
> +    if (reg != NPCM7XX_OTP_FUSTRAP) {
> +        value = npcm7xx_otp_read(s, reg);
> +    } else {
> +        /* FUSTRAP is stored as three copies in the OTP array. */
> +        uint32_t fustrap[3];
> +
> +        memcpy(fustrap, &s->array[0], sizeof(fustrap));
> +
> +        /* Determine value by a majority vote on each bit. */
> +        value = (fustrap[0] & fustrap[1]) | (fustrap[0] & fustrap[2]) |
> +                (fustrap[1] & fustrap[2]);
> +    }
> +
> +    return value;
> +}
> +
> +/* Register write handler specific to the fuse array OTP module. */
> +static void npcm7xx_fuse_array_write(void *opaque, hwaddr addr, uint64_t v,
> +                                     unsigned int size)
> +{
> +    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
> +    NPCM7xxOTPState *s = opaque;
> +
> +    /*
> +     * The Fuse Strap register is read-only. Other registers are handled by
> +     * common code.
> +     */
> +    if (reg != NPCM7XX_OTP_FUSTRAP) {
> +        npcm7xx_otp_write(s, reg, v);
> +    }
> +}
> +
> +static const MemoryRegionOps npcm7xx_fuse_array_ops = {
> +    .read       = npcm7xx_fuse_array_read,
> +    .write      = npcm7xx_fuse_array_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid      = {
> +        .min_access_size        = 4,
> +        .max_access_size        = 4,
> +        .unaligned              = false,
> +    },
> +};
> +
> +/* Register read handler specific to the key storage OTP module. */
> +static uint64_t npcm7xx_key_storage_read(void *opaque, hwaddr addr,
> +                                         unsigned int size)
> +{
> +    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
> +    NPCM7xxOTPState *s = opaque;
> +
> +    /*
> +     * Only the Fuse Key Index register needs special handling; all other
> +     * registers work the same way for both kinds of OTP modules.
> +     */
> +    if (reg != NPCM7XX_OTP_FKEYIND) {
> +        return npcm7xx_otp_read(s, reg);
> +    }
> +
> +    qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
> +
> +    return s->regs[NPCM7XX_OTP_FKEYIND];
> +}
> +
> +/* Register write handler specific to the key storage OTP module. */
> +static void npcm7xx_key_storage_write(void *opaque, hwaddr addr, uint64_t v,
> +                                      unsigned int size)
> +{
> +    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
> +    NPCM7xxOTPState *s = opaque;
> +
> +    /*
> +     * Only the Fuse Key Index register needs special handling; all other
> +     * registers work the same way for both kinds of OTP modules.
> +     */
> +    if (reg != NPCM7XX_OTP_FKEYIND) {
> +        npcm7xx_otp_write(s, reg, v);
> +        return;
> +    }
> +
> +    qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
> +
> +    s->regs[NPCM7XX_OTP_FKEYIND] = v;
> +}
> +
> +static const MemoryRegionOps npcm7xx_key_storage_ops = {
> +    .read       = npcm7xx_key_storage_read,
> +    .write      = npcm7xx_key_storage_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid      = {
> +        .min_access_size        = 4,
> +        .max_access_size        = 4,
> +        .unaligned              = false,
> +    },
> +};
> +
> +static void npcm7xx_otp_enter_reset(Object *obj, ResetType type)
> +{
> +    NPCM7xxOTPState *s = NPCM7XX_OTP(obj);
> +
> +    memset(s->regs, 0, sizeof(s->regs));
> +
> +    s->regs[NPCM7XX_OTP_FST] = 0x00000001;
> +    s->regs[NPCM7XX_OTP_FCFG] = 0x20000000;
> +}
> +
> +static void npcm7xx_otp_realize(DeviceState *dev, Error **errp)
> +{
> +    NPCM7xxOTPClass *oc = NPCM7XX_OTP_GET_CLASS(dev);
> +    NPCM7xxOTPState *s = NPCM7XX_OTP(dev);
> +    SysBusDevice *sbd = &s->parent;
> +
> +    memset(s->array, 0, sizeof(s->array));
> +
> +    memory_region_init_io(&s->mmio, OBJECT(s), oc->mmio_ops, s, "regs",
> +                          NPCM7XX_OTP_REGS_SIZE);
> +    sysbus_init_mmio(sbd, &s->mmio);
> +}
> +
> +static const VMStateDescription vmstate_npcm7xx_otp = {
> +    .name = "npcm7xx-otp",
> +    .version_id = 0,
> +    .minimum_version_id = 0,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, NPCM7xxOTPState, NPCM7XX_OTP_NR_REGS),
> +        VMSTATE_UINT8_ARRAY(array, NPCM7xxOTPState, NPCM7XX_OTP_ARRAY_BYTES),
> +        VMSTATE_END_OF_LIST(),
> +    },
> +};
> +
> +static void npcm7xx_otp_class_init(ObjectClass *klass, void *data)
> +{
> +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    QEMU_BUILD_BUG_ON(NPCM7XX_OTP_REGS_END > NPCM7XX_OTP_NR_REGS);
> +
> +    dc->realize = npcm7xx_otp_realize;
> +    dc->vmsd = &vmstate_npcm7xx_otp;
> +    rc->phases.enter = npcm7xx_otp_enter_reset;
> +}
> +
> +static void npcm7xx_key_storage_class_init(ObjectClass *klass, void *data)
> +{
> +    NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
> +
> +    oc->mmio_ops = &npcm7xx_key_storage_ops;
> +}

Hello,
With this series applied, when I build with --enable-sanitizers and run
./qemu-system-arm:

==373753==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61000000b400 at pc 0x557496abbefc bp 0x7ffdd5851210 sp 0x7ffdd5851208
WRITE of size 8 at 0x61000000b400 thread T0
    #0 0x557496abbefb in npcm7xx_key_storage_class_init /../hw/nvram/npcm7xx_otp.c:410:18
    #1 0x5574998a8780 in type_initialize /../qom/object.c:362:9
    #2 0x5574998a9bef in object_class_foreach_tramp /../qom/object.c:1030:5
    #3 0x7fc26b427b2f in g_hash_table_foreach (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x3eb2f)
    #4 0x5574998a9a41 in object_class_foreach /../qom/object.c:1052:5
    #5 0x5574998ab28a in object_class_get_list /../qom/object.c:1109:5
    #6 0x557498e6f8e1 in select_machine /../softmmu/vl.c:2438:24
    #7 0x557498e5a921 in qemu_init /../softmmu/vl.c:3842:21
    #8 0x557495b181d7 in main /../softmmu/main.c:49:5
    #9 0x7fc269e7dcc9 in __libc_start_main csu/../csu/libc-start.c:308:16
    #10 0x557495a6d9b9 in _start (/qemu-system-arm+0x35959b9)

0x61000000b400 is located 0 bytes to the right of 192-byte region [0x61000000b340,0x61000000b400)
allocated by thread T0 here:
    #0 0x557495ae6272 in calloc (/qemu-system-arm+0x360e272)
    #1 0x7fc26b43f210 in g_malloc0 (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x56210)
    #2 0x5574998a9bef in object_class_foreach_tramp /../qom/object.c:1030:5
    #3 0x7fc26b427b2f in g_hash_table_foreach (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x3eb2f)

SUMMARY: AddressSanitizer: heap-buffer-overflow /../hw/nvram/npcm7xx_otp.c:410:18 in npcm7xx_key_storage_class_init

-Alex

> +
> +static void npcm7xx_fuse_array_class_init(ObjectClass *klass, void *data)
> +{
> +    NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
> +
> +    oc->mmio_ops = &npcm7xx_fuse_array_ops;
> +}
> +
> +static const TypeInfo npcm7xx_otp_types[] = {
> +    {
> +        .name = TYPE_NPCM7XX_OTP,
> +        .parent = TYPE_SYS_BUS_DEVICE,
> +        .instance_size = sizeof(NPCM7xxOTPState),
> +        .class_init = npcm7xx_otp_class_init,
> +        .abstract = true,
> +    },
> +    {
> +        .name = TYPE_NPCM7XX_KEY_STORAGE,
> +        .parent = TYPE_NPCM7XX_OTP,
> +        .class_init = npcm7xx_key_storage_class_init,
> +    },
> +    {
> +        .name = TYPE_NPCM7XX_FUSE_ARRAY,
> +        .parent = TYPE_NPCM7XX_OTP,
> +        .class_init = npcm7xx_fuse_array_class_init,
> +    },
> +};
> +DEFINE_TYPES(npcm7xx_otp_types);
> diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build
> index ba214558ac..1f2ed013b2 100644
> --- a/hw/nvram/meson.build
> +++ b/hw/nvram/meson.build
> @@ -4,6 +4,7 @@ softmmu_ss.add(when: 'CONFIG_DS1225Y', if_true: files('ds1225y.c'))
>  softmmu_ss.add(when: 'CONFIG_NMC93XX_EEPROM', if_true: files('eeprom93xx.c'))
>  softmmu_ss.add(when: 'CONFIG_AT24C', if_true: files('eeprom_at24c.c'))
>  softmmu_ss.add(when: 'CONFIG_MAC_NVRAM', if_true: files('mac_nvram.c'))
> +softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_otp.c'))
>  softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_nvm.c'))
>  
>  specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_nvram.c'))
> -- 
> 2.28.0.297.g1956fa8f8d-goog
> 
> 


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

* Re: [PATCH v8 08/14] hw/nvram: NPCM7xx OTP device model
  2020-09-07 19:47   ` Alexander Bulekov
@ 2020-09-07 19:57     ` Alexander Bulekov
  2020-09-07 21:52       ` Havard Skinnemoen
  0 siblings, 1 reply; 35+ messages in thread
From: Alexander Bulekov @ 2020-09-07 19:57 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: peter.maydell, f4bug, qemu-devel, kfting, qemu-arm, Avi.Fishman

On 200907 1547, Alexander Bulekov wrote:
> On 200824 1717, Havard Skinnemoen via wrote:
> > This supports reading and writing OTP fuses and keys. Only fuse reading
> > has been tested. Protection is not implemented.
> > 
> > Reviewed-by: Avi Fishman <avi.fishman@nuvoton.com>
> > Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> > Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> > ---
> >  include/hw/arm/npcm7xx.h       |   3 +
> >  include/hw/nvram/npcm7xx_otp.h |  79 ++++++
> >  hw/arm/npcm7xx.c               |  29 +++
> >  hw/nvram/npcm7xx_otp.c         | 439 +++++++++++++++++++++++++++++++++
> >  hw/nvram/meson.build           |   1 +
> >  5 files changed, 551 insertions(+)
> >  create mode 100644 include/hw/nvram/npcm7xx_otp.h
> >  create mode 100644 hw/nvram/npcm7xx_otp.c
> > 
> > diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
> > index ba7495869d..5816a07a72 100644
> > --- a/include/hw/arm/npcm7xx.h
> > +++ b/include/hw/arm/npcm7xx.h
> > @@ -20,6 +20,7 @@
> >  #include "hw/cpu/a9mpcore.h"
> >  #include "hw/misc/npcm7xx_clk.h"
> >  #include "hw/misc/npcm7xx_gcr.h"
> > +#include "hw/nvram/npcm7xx_otp.h"
> >  #include "hw/timer/npcm7xx_timer.h"
> >  #include "target/arm/cpu.h"
> >  
> > @@ -68,6 +69,8 @@ typedef struct NPCM7xxState {
> >      NPCM7xxGCRState     gcr;
> >      NPCM7xxCLKState     clk;
> >      NPCM7xxTimerCtrlState tim[3];
> > +    NPCM7xxOTPState     key_storage;
> > +    NPCM7xxOTPState     fuse_array;
> >  } NPCM7xxState;
> >  
> >  #define TYPE_NPCM7XX    "npcm7xx"
> > diff --git a/include/hw/nvram/npcm7xx_otp.h b/include/hw/nvram/npcm7xx_otp.h
> > new file mode 100644
> > index 0000000000..156bbd151a
> > --- /dev/null
> > +++ b/include/hw/nvram/npcm7xx_otp.h
> > @@ -0,0 +1,79 @@
> > +/*
> > + * Nuvoton NPCM7xx OTP (Fuse Array) Interface
> > + *
> > + * Copyright 2020 Google LLC
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License as published by the
> > + * Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> > + * for more details.
> > + */
> > +#ifndef NPCM7XX_OTP_H
> > +#define NPCM7XX_OTP_H
> > +
> > +#include "exec/memory.h"
> > +#include "hw/sysbus.h"
> > +
> > +/* Each OTP module holds 8192 bits of one-time programmable storage */
> > +#define NPCM7XX_OTP_ARRAY_BITS (8192)
> > +#define NPCM7XX_OTP_ARRAY_BYTES (NPCM7XX_OTP_ARRAY_BITS / BITS_PER_BYTE)
> > +
> > +/* Fuse array offsets */
> > +#define NPCM7XX_FUSE_FUSTRAP (0)
> > +#define NPCM7XX_FUSE_CP_FUSTRAP (12)
> > +#define NPCM7XX_FUSE_DAC_CALIB (16)
> > +#define NPCM7XX_FUSE_ADC_CALIB (24)
> > +#define NPCM7XX_FUSE_DERIVATIVE (64)
> > +#define NPCM7XX_FUSE_TEST_SIG (72)
> > +#define NPCM7XX_FUSE_DIE_LOCATION (74)
> > +#define NPCM7XX_FUSE_GP1 (80)
> > +#define NPCM7XX_FUSE_GP2 (128)
> > +
> > +/*
> > + * Number of registers in our device state structure. Don't change this without
> > + * incrementing the version_id in the vmstate.
> > + */
> > +#define NPCM7XX_OTP_NR_REGS (0x18 / sizeof(uint32_t))
> > +
> > +/**
> > + * struct NPCM7xxOTPState - Device state for one OTP module.
> > + * @parent: System bus device.
> > + * @mmio: Memory region through which registers are accessed.
> > + * @regs: Register contents.
> > + * @array: OTP storage array.
> > + */
> > +typedef struct NPCM7xxOTPState {
> > +    SysBusDevice parent;
> > +
> > +    MemoryRegion mmio;
> > +    uint32_t regs[NPCM7XX_OTP_NR_REGS];
> > +    uint8_t array[NPCM7XX_OTP_ARRAY_BYTES];
> > +} NPCM7xxOTPState;
> > +
> > +#define TYPE_NPCM7XX_OTP "npcm7xx-otp"
> > +#define NPCM7XX_OTP(obj) OBJECT_CHECK(NPCM7xxOTPState, (obj), TYPE_NPCM7XX_OTP)
> > +
> > +#define TYPE_NPCM7XX_KEY_STORAGE "npcm7xx-key-storage"
> > +#define TYPE_NPCM7XX_FUSE_ARRAY "npcm7xx-fuse-array"
> > +
> > +typedef struct NPCM7xxOTPClass NPCM7xxOTPClass;
> > +
> > +/**
> > + * npcm7xx_otp_array_write - ECC encode and write data to OTP array.
> > + * @s: OTP module.
> > + * @data: Data to be encoded and written.
> > + * @offset: Offset of first byte to be written in the OTP array.
> > + * @len: Number of bytes before ECC encoding.
> > + *
> > + * Each nibble of data is encoded into a byte, so the number of bytes written
> > + * to the array will be @len * 2.
> > + */
> > +extern void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
> > +                                    unsigned int offset, unsigned int len);
> > +
> > +#endif /* NPCM7XX_OTP_H */
> > diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> > index 9669ac5fa0..9166002598 100644
> > --- a/hw/arm/npcm7xx.c
> > +++ b/hw/arm/npcm7xx.c
> > @@ -34,6 +34,10 @@
> >  #define NPCM7XX_MMIO_BA         (0x80000000)
> >  #define NPCM7XX_MMIO_SZ         (0x7ffd0000)
> >  
> > +/* OTP key storage and fuse strap array */
> > +#define NPCM7XX_OTP1_BA         (0xf0189000)
> > +#define NPCM7XX_OTP2_BA         (0xf018a000)
> > +
> >  /* Core system modules. */
> >  #define NPCM7XX_L2C_BA          (0xf03fc000)
> >  #define NPCM7XX_CPUP_BA         (0xf03fe000)
> > @@ -144,6 +148,20 @@ void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
> >      arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo);
> >  }
> >  
> > +static void npcm7xx_init_fuses(NPCM7xxState *s)
> > +{
> > +    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
> > +    uint32_t value;
> > +
> > +    /*
> > +     * The initial mask of disabled modules indicates the chip derivative (e.g.
> > +     * NPCM750 or NPCM730).
> > +     */
> > +    value = tswap32(nc->disabled_modules);
> > +    npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE,
> > +                            sizeof(value));
> > +}
> > +
> >  static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n)
> >  {
> >      return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n);
> > @@ -164,6 +182,10 @@ static void npcm7xx_init(Object *obj)
> >      object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
> >                                "power-on-straps");
> >      object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM7XX_CLK);
> > +    object_initialize_child(obj, "otp1", &s->key_storage,
> > +                            TYPE_NPCM7XX_KEY_STORAGE);
> > +    object_initialize_child(obj, "otp2", &s->fuse_array,
> > +                            TYPE_NPCM7XX_FUSE_ARRAY);
> >  
> >      for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> >          object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
> > @@ -232,6 +254,13 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
> >      sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
> >      sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM7XX_CLK_BA);
> >  
> > +    /* OTP key storage and fuse strap array. Cannot fail. */
> > +    sysbus_realize(SYS_BUS_DEVICE(&s->key_storage), &error_abort);
> > +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->key_storage), 0, NPCM7XX_OTP1_BA);
> > +    sysbus_realize(SYS_BUS_DEVICE(&s->fuse_array), &error_abort);
> > +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM7XX_OTP2_BA);
> > +    npcm7xx_init_fuses(s);
> > +
> >      /* Timer Modules (TIM). Cannot fail. */
> >      QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
> >      for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> > diff --git a/hw/nvram/npcm7xx_otp.c b/hw/nvram/npcm7xx_otp.c
> > new file mode 100644
> > index 0000000000..ba4188ada8
> > --- /dev/null
> > +++ b/hw/nvram/npcm7xx_otp.c
> > @@ -0,0 +1,439 @@
> > +/*
> > + * Nuvoton NPCM7xx OTP (Fuse Array) Interface
> > + *
> > + * Copyright 2020 Google LLC
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License as published by the
> > + * Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> > + * for more details.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +
> > +#include "hw/nvram/npcm7xx_otp.h"
> > +#include "migration/vmstate.h"
> > +#include "qapi/error.h"
> > +#include "qemu/bitops.h"
> > +#include "qemu/log.h"
> > +#include "qemu/module.h"
> > +#include "qemu/units.h"
> > +
> > +/* Each module has 4 KiB of register space. Only a fraction of it is used. */
> > +#define NPCM7XX_OTP_REGS_SIZE (4 * KiB)
> > +
> > +/* 32-bit register indices. */
> > +typedef enum NPCM7xxOTPRegister {
> > +    NPCM7XX_OTP_FST,
> > +    NPCM7XX_OTP_FADDR,
> > +    NPCM7XX_OTP_FDATA,
> > +    NPCM7XX_OTP_FCFG,
> > +    /* Offset 0x10 is FKEYIND in OTP1, FUSTRAP in OTP2 */
> > +    NPCM7XX_OTP_FKEYIND = 0x0010 / sizeof(uint32_t),
> > +    NPCM7XX_OTP_FUSTRAP = 0x0010 / sizeof(uint32_t),
> > +    NPCM7XX_OTP_FCTL,
> > +    NPCM7XX_OTP_REGS_END,
> > +} NPCM7xxOTPRegister;
> > +
> > +/* Register field definitions. */
> > +#define FST_RIEN BIT(2)
> > +#define FST_RDST BIT(1)
> > +#define FST_RDY BIT(0)
> > +#define FST_RO_MASK (FST_RDST | FST_RDY)
> > +
> > +#define FADDR_BYTEADDR(rv) extract32((rv), 0, 10)
> > +#define FADDR_BITPOS(rv) extract32((rv), 10, 3)
> > +
> > +#define FDATA_CLEAR 0x00000001
> > +
> > +#define FCFG_FDIS BIT(31)
> > +#define FCFG_FCFGLK_MASK 0x00ff0000
> > +
> > +#define FCTL_PROG_CMD1 0x00000001
> > +#define FCTL_PROG_CMD2 0xbf79e5d0
> > +#define FCTL_READ_CMD 0x00000002
> > +
> > +/**
> > + * struct NPCM7xxOTPClass - OTP module class.
> > + * @parent: System bus device class.
> > + * @mmio_ops: MMIO register operations for this type of module.
> > + *
> > + * The two OTP modules (key-storage and fuse-array) have slightly different
> > + * behavior, so we give them different MMIO register operations.
> > + */
> > +struct NPCM7xxOTPClass {
> > +    SysBusDeviceClass parent;
> > +
> > +    const MemoryRegionOps *mmio_ops;
> > +};
> > +
> > +#define NPCM7XX_OTP_CLASS(klass) \
> > +    OBJECT_CLASS_CHECK(NPCM7xxOTPClass, (klass), TYPE_NPCM7XX_OTP)
> > +#define NPCM7XX_OTP_GET_CLASS(obj) \
> > +    OBJECT_GET_CLASS(NPCM7xxOTPClass, (obj), TYPE_NPCM7XX_OTP)
> > +
> > +static uint8_t ecc_encode_nibble(uint8_t n)
> > +{
> > +    uint8_t result = n;
> > +
> > +    result |= (((n >> 0) & 1) ^ ((n >> 1) & 1)) << 4;
> > +    result |= (((n >> 2) & 1) ^ ((n >> 3) & 1)) << 5;
> > +    result |= (((n >> 0) & 1) ^ ((n >> 2) & 1)) << 6;
> > +    result |= (((n >> 1) & 1) ^ ((n >> 3) & 1)) << 7;
> > +
> > +    return result;
> > +}
> > +
> > +void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
> > +                             unsigned int offset, unsigned int len)
> > +{
> > +    const uint8_t *src = data;
> > +    uint8_t *dst = &s->array[offset];
> > +
> > +    while (len-- > 0) {
> > +        uint8_t c = *src++;
> > +
> > +        *dst++ = ecc_encode_nibble(extract8(c, 0, 4));
> > +        *dst++ = ecc_encode_nibble(extract8(c, 4, 4));
> > +    }
> > +}
> > +
> > +/* Common register read handler for both OTP classes. */
> > +static uint64_t npcm7xx_otp_read(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg)
> > +{
> > +    uint32_t value = 0;
> > +
> > +    switch (reg) {
> > +    case NPCM7XX_OTP_FST:
> > +    case NPCM7XX_OTP_FADDR:
> > +    case NPCM7XX_OTP_FDATA:
> > +    case NPCM7XX_OTP_FCFG:
> > +        value = s->regs[reg];
> > +        break;
> > +
> > +    case NPCM7XX_OTP_FCTL:
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: read from write-only FCTL register\n",
> > +                      DEVICE(s)->canonical_path);
> > +        break;
> > +
> > +    default:
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: read from invalid offset 0x%zx\n",
> > +                      DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
> > +        break;
> > +    }
> > +
> > +    return value;
> > +}
> > +
> > +/* Read a byte from the OTP array into the data register. */
> > +static void npcm7xx_otp_read_array(NPCM7xxOTPState *s)
> > +{
> > +    uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
> > +
> > +    s->regs[NPCM7XX_OTP_FDATA] = s->array[FADDR_BYTEADDR(faddr)];
> > +    s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
> > +}
> > +
> > +/* Program a byte from the data register into the OTP array. */
> > +static void npcm7xx_otp_program_array(NPCM7xxOTPState *s)
> > +{
> > +    uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
> > +
> > +    /* Bits can only go 0->1, never 1->0. */
> > +    s->array[FADDR_BYTEADDR(faddr)] |= (1U << FADDR_BITPOS(faddr));
> > +    s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
> > +}
> > +
> > +/* Compute the next value of the FCFG register. */
> > +static uint32_t npcm7xx_otp_compute_fcfg(uint32_t cur_value, uint32_t new_value)
> > +{
> > +    uint32_t lock_mask;
> > +    uint32_t value;
> > +
> > +    /*
> > +     * FCFGLK holds sticky bits 16..23, indicating which bits in FPRGLK (8..15)
> > +     * and FRDLK (0..7) that are read-only.
> > +     */
> > +    lock_mask = (cur_value & FCFG_FCFGLK_MASK) >> 8;
> > +    lock_mask |= lock_mask >> 8;
> > +    /* FDIS and FCFGLK bits are sticky (write 1 to set; can't clear). */
> > +    value = cur_value & (FCFG_FDIS | FCFG_FCFGLK_MASK);
> > +    /* Preserve read-only bits in FPRGLK and FRDLK */
> > +    value |= cur_value & lock_mask;
> > +    /* Set all bits that aren't read-only. */
> > +    value |= new_value & ~lock_mask;
> > +
> > +    return value;
> > +}
> > +
> > +/* Common register write handler for both OTP classes. */
> > +static void npcm7xx_otp_write(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg,
> > +                              uint32_t value)
> > +{
> > +    switch (reg) {
> > +    case NPCM7XX_OTP_FST:
> > +        /* RDST is cleared by writing 1 to it. */
> > +        if (value & FST_RDST) {
> > +            s->regs[NPCM7XX_OTP_FST] &= ~FST_RDST;
> > +        }
> > +        /* Preserve read-only and write-one-to-clear bits */
> > +        value &= ~FST_RO_MASK;
> > +        value |= s->regs[NPCM7XX_OTP_FST] & FST_RO_MASK;
> > +        break;
> > +
> > +    case NPCM7XX_OTP_FADDR:
> > +        break;
> > +
> > +    case NPCM7XX_OTP_FDATA:
> > +        /*
> > +         * This register is cleared by writing a magic value to it; no other
> > +         * values can be written.
> > +         */
> > +        if (value == FDATA_CLEAR) {
> > +            value = 0;
> > +        } else {
> > +            value = s->regs[NPCM7XX_OTP_FDATA];
> > +        }
> > +        break;
> > +
> > +    case NPCM7XX_OTP_FCFG:
> > +        value = npcm7xx_otp_compute_fcfg(s->regs[NPCM7XX_OTP_FCFG], value);
> > +        break;
> > +
> > +    case NPCM7XX_OTP_FCTL:
> > +        switch (value) {
> > +        case FCTL_READ_CMD:
> > +            npcm7xx_otp_read_array(s);
> > +            break;
> > +
> > +        case FCTL_PROG_CMD1:
> > +            /*
> > +             * Programming requires writing two separate magic values to this
> > +             * register; this is the first one. Just store it so it can be
> > +             * verified later when the second magic value is received.
> > +             */
> > +            break;
> > +
> > +        case FCTL_PROG_CMD2:
> > +            /*
> > +             * Only initiate programming if we received the first half of the
> > +             * command immediately before this one.
> > +             */
> > +            if (s->regs[NPCM7XX_OTP_FCTL] == FCTL_PROG_CMD1) {
> > +                npcm7xx_otp_program_array(s);
> > +            }
> > +            break;
> > +
> > +        default:
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                          "%s: unrecognized FCNTL value 0x%" PRIx32 "\n",
> > +                          DEVICE(s)->canonical_path, value);
> > +            break;
> > +        }
> > +        if (value != FCTL_PROG_CMD1) {
> > +            value = 0;
> > +        }
> > +        break;
> > +
> > +    default:
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: write to invalid offset 0x%zx\n",
> > +                      DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
> > +        return;
> > +    }
> > +
> > +    s->regs[reg] = value;
> > +}
> > +
> > +/* Register read handler specific to the fuse array OTP module. */
> > +static uint64_t npcm7xx_fuse_array_read(void *opaque, hwaddr addr,
> > +                                        unsigned int size)
> > +{
> > +    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
> > +    NPCM7xxOTPState *s = opaque;
> > +    uint32_t value;
> > +
> > +    /*
> > +     * Only the Fuse Strap register needs special handling; all other registers
> > +     * work the same way for both kinds of OTP modules.
> > +     */
> > +    if (reg != NPCM7XX_OTP_FUSTRAP) {
> > +        value = npcm7xx_otp_read(s, reg);
> > +    } else {
> > +        /* FUSTRAP is stored as three copies in the OTP array. */
> > +        uint32_t fustrap[3];
> > +
> > +        memcpy(fustrap, &s->array[0], sizeof(fustrap));
> > +
> > +        /* Determine value by a majority vote on each bit. */
> > +        value = (fustrap[0] & fustrap[1]) | (fustrap[0] & fustrap[2]) |
> > +                (fustrap[1] & fustrap[2]);
> > +    }
> > +
> > +    return value;
> > +}
> > +
> > +/* Register write handler specific to the fuse array OTP module. */
> > +static void npcm7xx_fuse_array_write(void *opaque, hwaddr addr, uint64_t v,
> > +                                     unsigned int size)
> > +{
> > +    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
> > +    NPCM7xxOTPState *s = opaque;
> > +
> > +    /*
> > +     * The Fuse Strap register is read-only. Other registers are handled by
> > +     * common code.
> > +     */
> > +    if (reg != NPCM7XX_OTP_FUSTRAP) {
> > +        npcm7xx_otp_write(s, reg, v);
> > +    }
> > +}
> > +
> > +static const MemoryRegionOps npcm7xx_fuse_array_ops = {
> > +    .read       = npcm7xx_fuse_array_read,
> > +    .write      = npcm7xx_fuse_array_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid      = {
> > +        .min_access_size        = 4,
> > +        .max_access_size        = 4,
> > +        .unaligned              = false,
> > +    },
> > +};
> > +
> > +/* Register read handler specific to the key storage OTP module. */
> > +static uint64_t npcm7xx_key_storage_read(void *opaque, hwaddr addr,
> > +                                         unsigned int size)
> > +{
> > +    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
> > +    NPCM7xxOTPState *s = opaque;
> > +
> > +    /*
> > +     * Only the Fuse Key Index register needs special handling; all other
> > +     * registers work the same way for both kinds of OTP modules.
> > +     */
> > +    if (reg != NPCM7XX_OTP_FKEYIND) {
> > +        return npcm7xx_otp_read(s, reg);
> > +    }
> > +
> > +    qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
> > +
> > +    return s->regs[NPCM7XX_OTP_FKEYIND];
> > +}
> > +
> > +/* Register write handler specific to the key storage OTP module. */
> > +static void npcm7xx_key_storage_write(void *opaque, hwaddr addr, uint64_t v,
> > +                                      unsigned int size)
> > +{
> > +    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
> > +    NPCM7xxOTPState *s = opaque;
> > +
> > +    /*
> > +     * Only the Fuse Key Index register needs special handling; all other
> > +     * registers work the same way for both kinds of OTP modules.
> > +     */
> > +    if (reg != NPCM7XX_OTP_FKEYIND) {
> > +        npcm7xx_otp_write(s, reg, v);
> > +        return;
> > +    }
> > +
> > +    qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
> > +
> > +    s->regs[NPCM7XX_OTP_FKEYIND] = v;
> > +}
> > +
> > +static const MemoryRegionOps npcm7xx_key_storage_ops = {
> > +    .read       = npcm7xx_key_storage_read,
> > +    .write      = npcm7xx_key_storage_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid      = {
> > +        .min_access_size        = 4,
> > +        .max_access_size        = 4,
> > +        .unaligned              = false,
> > +    },
> > +};
> > +
> > +static void npcm7xx_otp_enter_reset(Object *obj, ResetType type)
> > +{
> > +    NPCM7xxOTPState *s = NPCM7XX_OTP(obj);
> > +
> > +    memset(s->regs, 0, sizeof(s->regs));
> > +
> > +    s->regs[NPCM7XX_OTP_FST] = 0x00000001;
> > +    s->regs[NPCM7XX_OTP_FCFG] = 0x20000000;
> > +}
> > +
> > +static void npcm7xx_otp_realize(DeviceState *dev, Error **errp)
> > +{
> > +    NPCM7xxOTPClass *oc = NPCM7XX_OTP_GET_CLASS(dev);
> > +    NPCM7xxOTPState *s = NPCM7XX_OTP(dev);
> > +    SysBusDevice *sbd = &s->parent;
> > +
> > +    memset(s->array, 0, sizeof(s->array));
> > +
> > +    memory_region_init_io(&s->mmio, OBJECT(s), oc->mmio_ops, s, "regs",
> > +                          NPCM7XX_OTP_REGS_SIZE);
> > +    sysbus_init_mmio(sbd, &s->mmio);
> > +}
> > +
> > +static const VMStateDescription vmstate_npcm7xx_otp = {
> > +    .name = "npcm7xx-otp",
> > +    .version_id = 0,
> > +    .minimum_version_id = 0,
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_UINT32_ARRAY(regs, NPCM7xxOTPState, NPCM7XX_OTP_NR_REGS),
> > +        VMSTATE_UINT8_ARRAY(array, NPCM7xxOTPState, NPCM7XX_OTP_ARRAY_BYTES),
> > +        VMSTATE_END_OF_LIST(),
> > +    },
> > +};
> > +
> > +static void npcm7xx_otp_class_init(ObjectClass *klass, void *data)
> > +{
> > +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > +    QEMU_BUILD_BUG_ON(NPCM7XX_OTP_REGS_END > NPCM7XX_OTP_NR_REGS);
> > +
> > +    dc->realize = npcm7xx_otp_realize;
> > +    dc->vmsd = &vmstate_npcm7xx_otp;
> > +    rc->phases.enter = npcm7xx_otp_enter_reset;
> > +}
> > +
> > +static void npcm7xx_key_storage_class_init(ObjectClass *klass, void *data)
> > +{
> > +    NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
> > +
> > +    oc->mmio_ops = &npcm7xx_key_storage_ops;
> > +}
> 
> Hello,
> With this series applied, when I build with --enable-sanitizers and run
> ./qemu-system-arm:
> 
> ==373753==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61000000b400 at pc 0x557496abbefc bp 0x7ffdd5851210 sp 0x7ffdd5851208
> WRITE of size 8 at 0x61000000b400 thread T0
>     #0 0x557496abbefb in npcm7xx_key_storage_class_init /../hw/nvram/npcm7xx_otp.c:410:18
>     #1 0x5574998a8780 in type_initialize /../qom/object.c:362:9
>     #2 0x5574998a9bef in object_class_foreach_tramp /../qom/object.c:1030:5
>     #3 0x7fc26b427b2f in g_hash_table_foreach (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x3eb2f)
>     #4 0x5574998a9a41 in object_class_foreach /../qom/object.c:1052:5
>     #5 0x5574998ab28a in object_class_get_list /../qom/object.c:1109:5
>     #6 0x557498e6f8e1 in select_machine /../softmmu/vl.c:2438:24
>     #7 0x557498e5a921 in qemu_init /../softmmu/vl.c:3842:21
>     #8 0x557495b181d7 in main /../softmmu/main.c:49:5
>     #9 0x7fc269e7dcc9 in __libc_start_main csu/../csu/libc-start.c:308:16
>     #10 0x557495a6d9b9 in _start (/qemu-system-arm+0x35959b9)
> 
> 0x61000000b400 is located 0 bytes to the right of 192-byte region [0x61000000b340,0x61000000b400)
> allocated by thread T0 here:
>     #0 0x557495ae6272 in calloc (/qemu-system-arm+0x360e272)
>     #1 0x7fc26b43f210 in g_malloc0 (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x56210)
>     #2 0x5574998a9bef in object_class_foreach_tramp /../qom/object.c:1030:5
>     #3 0x7fc26b427b2f in g_hash_table_foreach (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x3eb2f)
> 
> SUMMARY: AddressSanitizer: heap-buffer-overflow /../hw/nvram/npcm7xx_otp.c:410:18 in npcm7xx_key_storage_class_init
> 
> -Alex
> 
> > +
> > +static void npcm7xx_fuse_array_class_init(ObjectClass *klass, void *data)
> > +{
> > +    NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
> > +
> > +    oc->mmio_ops = &npcm7xx_fuse_array_ops;
> > +}
> > +
> > +static const TypeInfo npcm7xx_otp_types[] = {
> > +    {
> > +        .name = TYPE_NPCM7XX_OTP,
> > +        .parent = TYPE_SYS_BUS_DEVICE,
> > +        .instance_size = sizeof(NPCM7xxOTPState),
Adding this fixes it for me:
             .class_size = sizeof(NPCM7xxOTPClass),

-Alex
> > +        .class_init = npcm7xx_otp_class_init,
> > +        .abstract = true,
> > +    },
> > +    {
> > +        .name = TYPE_NPCM7XX_KEY_STORAGE,
> > +        .parent = TYPE_NPCM7XX_OTP,
> > +        .class_init = npcm7xx_key_storage_class_init,
> > +    },
> > +    {
> > +        .name = TYPE_NPCM7XX_FUSE_ARRAY,
> > +        .parent = TYPE_NPCM7XX_OTP,
> > +        .class_init = npcm7xx_fuse_array_class_init,
> > +    },
> > +};
> > +DEFINE_TYPES(npcm7xx_otp_types);
> > diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build
> > index ba214558ac..1f2ed013b2 100644
> > --- a/hw/nvram/meson.build
> > +++ b/hw/nvram/meson.build
> > @@ -4,6 +4,7 @@ softmmu_ss.add(when: 'CONFIG_DS1225Y', if_true: files('ds1225y.c'))
> >  softmmu_ss.add(when: 'CONFIG_NMC93XX_EEPROM', if_true: files('eeprom93xx.c'))
> >  softmmu_ss.add(when: 'CONFIG_AT24C', if_true: files('eeprom_at24c.c'))
> >  softmmu_ss.add(when: 'CONFIG_MAC_NVRAM', if_true: files('mac_nvram.c'))
> > +softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_otp.c'))
> >  softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_nvm.c'))
> >  
> >  specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_nvram.c'))
> > -- 
> > 2.28.0.297.g1956fa8f8d-goog
> > 
> > 


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

* Re: [PATCH v8 08/14] hw/nvram: NPCM7xx OTP device model
  2020-09-07 19:57     ` Alexander Bulekov
@ 2020-09-07 21:52       ` Havard Skinnemoen
  0 siblings, 0 replies; 35+ messages in thread
From: Havard Skinnemoen @ 2020-09-07 21:52 UTC (permalink / raw)
  To: Alexander Bulekov
  Cc: Peter Maydell, Philippe Mathieu-Daudé,
	qemu-arm, QEMU Developers, IS20 Avi Fishman, CS20 KFTing

On Mon, Sep 7, 2020 at 12:58 PM Alexander Bulekov <alxndr@bu.edu> wrote:
>
> On 200907 1547, Alexander Bulekov wrote:
> > On 200824 1717, Havard Skinnemoen via wrote:
> > > This supports reading and writing OTP fuses and keys. Only fuse reading
> > > has been tested. Protection is not implemented.
> > >
> > > Reviewed-by: Avi Fishman <avi.fishman@nuvoton.com>
> > > Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> > > Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> > > ---
> > >  include/hw/arm/npcm7xx.h       |   3 +
> > >  include/hw/nvram/npcm7xx_otp.h |  79 ++++++
> > >  hw/arm/npcm7xx.c               |  29 +++
> > >  hw/nvram/npcm7xx_otp.c         | 439 +++++++++++++++++++++++++++++++++
> > >  hw/nvram/meson.build           |   1 +
> > >  5 files changed, 551 insertions(+)
> > >  create mode 100644 include/hw/nvram/npcm7xx_otp.h
> > >  create mode 100644 hw/nvram/npcm7xx_otp.c
> > >
> > > diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
> > > index ba7495869d..5816a07a72 100644
> > > --- a/include/hw/arm/npcm7xx.h
> > > +++ b/include/hw/arm/npcm7xx.h
> > > @@ -20,6 +20,7 @@
> > >  #include "hw/cpu/a9mpcore.h"
> > >  #include "hw/misc/npcm7xx_clk.h"
> > >  #include "hw/misc/npcm7xx_gcr.h"
> > > +#include "hw/nvram/npcm7xx_otp.h"
> > >  #include "hw/timer/npcm7xx_timer.h"
> > >  #include "target/arm/cpu.h"
> > >
> > > @@ -68,6 +69,8 @@ typedef struct NPCM7xxState {
> > >      NPCM7xxGCRState     gcr;
> > >      NPCM7xxCLKState     clk;
> > >      NPCM7xxTimerCtrlState tim[3];
> > > +    NPCM7xxOTPState     key_storage;
> > > +    NPCM7xxOTPState     fuse_array;
> > >  } NPCM7xxState;
> > >
> > >  #define TYPE_NPCM7XX    "npcm7xx"
> > > diff --git a/include/hw/nvram/npcm7xx_otp.h b/include/hw/nvram/npcm7xx_otp.h
> > > new file mode 100644
> > > index 0000000000..156bbd151a
> > > --- /dev/null
> > > +++ b/include/hw/nvram/npcm7xx_otp.h
> > > @@ -0,0 +1,79 @@
> > > +/*
> > > + * Nuvoton NPCM7xx OTP (Fuse Array) Interface
> > > + *
> > > + * Copyright 2020 Google LLC
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify it
> > > + * under the terms of the GNU General Public License as published by the
> > > + * Free Software Foundation; either version 2 of the License, or
> > > + * (at your option) any later version.
> > > + *
> > > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> > > + * for more details.
> > > + */
> > > +#ifndef NPCM7XX_OTP_H
> > > +#define NPCM7XX_OTP_H
> > > +
> > > +#include "exec/memory.h"
> > > +#include "hw/sysbus.h"
> > > +
> > > +/* Each OTP module holds 8192 bits of one-time programmable storage */
> > > +#define NPCM7XX_OTP_ARRAY_BITS (8192)
> > > +#define NPCM7XX_OTP_ARRAY_BYTES (NPCM7XX_OTP_ARRAY_BITS / BITS_PER_BYTE)
> > > +
> > > +/* Fuse array offsets */
> > > +#define NPCM7XX_FUSE_FUSTRAP (0)
> > > +#define NPCM7XX_FUSE_CP_FUSTRAP (12)
> > > +#define NPCM7XX_FUSE_DAC_CALIB (16)
> > > +#define NPCM7XX_FUSE_ADC_CALIB (24)
> > > +#define NPCM7XX_FUSE_DERIVATIVE (64)
> > > +#define NPCM7XX_FUSE_TEST_SIG (72)
> > > +#define NPCM7XX_FUSE_DIE_LOCATION (74)
> > > +#define NPCM7XX_FUSE_GP1 (80)
> > > +#define NPCM7XX_FUSE_GP2 (128)
> > > +
> > > +/*
> > > + * Number of registers in our device state structure. Don't change this without
> > > + * incrementing the version_id in the vmstate.
> > > + */
> > > +#define NPCM7XX_OTP_NR_REGS (0x18 / sizeof(uint32_t))
> > > +
> > > +/**
> > > + * struct NPCM7xxOTPState - Device state for one OTP module.
> > > + * @parent: System bus device.
> > > + * @mmio: Memory region through which registers are accessed.
> > > + * @regs: Register contents.
> > > + * @array: OTP storage array.
> > > + */
> > > +typedef struct NPCM7xxOTPState {
> > > +    SysBusDevice parent;
> > > +
> > > +    MemoryRegion mmio;
> > > +    uint32_t regs[NPCM7XX_OTP_NR_REGS];
> > > +    uint8_t array[NPCM7XX_OTP_ARRAY_BYTES];
> > > +} NPCM7xxOTPState;
> > > +
> > > +#define TYPE_NPCM7XX_OTP "npcm7xx-otp"
> > > +#define NPCM7XX_OTP(obj) OBJECT_CHECK(NPCM7xxOTPState, (obj), TYPE_NPCM7XX_OTP)
> > > +
> > > +#define TYPE_NPCM7XX_KEY_STORAGE "npcm7xx-key-storage"
> > > +#define TYPE_NPCM7XX_FUSE_ARRAY "npcm7xx-fuse-array"
> > > +
> > > +typedef struct NPCM7xxOTPClass NPCM7xxOTPClass;
> > > +
> > > +/**
> > > + * npcm7xx_otp_array_write - ECC encode and write data to OTP array.
> > > + * @s: OTP module.
> > > + * @data: Data to be encoded and written.
> > > + * @offset: Offset of first byte to be written in the OTP array.
> > > + * @len: Number of bytes before ECC encoding.
> > > + *
> > > + * Each nibble of data is encoded into a byte, so the number of bytes written
> > > + * to the array will be @len * 2.
> > > + */
> > > +extern void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
> > > +                                    unsigned int offset, unsigned int len);
> > > +
> > > +#endif /* NPCM7XX_OTP_H */
> > > diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> > > index 9669ac5fa0..9166002598 100644
> > > --- a/hw/arm/npcm7xx.c
> > > +++ b/hw/arm/npcm7xx.c
> > > @@ -34,6 +34,10 @@
> > >  #define NPCM7XX_MMIO_BA         (0x80000000)
> > >  #define NPCM7XX_MMIO_SZ         (0x7ffd0000)
> > >
> > > +/* OTP key storage and fuse strap array */
> > > +#define NPCM7XX_OTP1_BA         (0xf0189000)
> > > +#define NPCM7XX_OTP2_BA         (0xf018a000)
> > > +
> > >  /* Core system modules. */
> > >  #define NPCM7XX_L2C_BA          (0xf03fc000)
> > >  #define NPCM7XX_CPUP_BA         (0xf03fe000)
> > > @@ -144,6 +148,20 @@ void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
> > >      arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo);
> > >  }
> > >
> > > +static void npcm7xx_init_fuses(NPCM7xxState *s)
> > > +{
> > > +    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
> > > +    uint32_t value;
> > > +
> > > +    /*
> > > +     * The initial mask of disabled modules indicates the chip derivative (e.g.
> > > +     * NPCM750 or NPCM730).
> > > +     */
> > > +    value = tswap32(nc->disabled_modules);
> > > +    npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE,
> > > +                            sizeof(value));
> > > +}
> > > +
> > >  static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n)
> > >  {
> > >      return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n);
> > > @@ -164,6 +182,10 @@ static void npcm7xx_init(Object *obj)
> > >      object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
> > >                                "power-on-straps");
> > >      object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM7XX_CLK);
> > > +    object_initialize_child(obj, "otp1", &s->key_storage,
> > > +                            TYPE_NPCM7XX_KEY_STORAGE);
> > > +    object_initialize_child(obj, "otp2", &s->fuse_array,
> > > +                            TYPE_NPCM7XX_FUSE_ARRAY);
> > >
> > >      for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> > >          object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
> > > @@ -232,6 +254,13 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
> > >      sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
> > >      sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM7XX_CLK_BA);
> > >
> > > +    /* OTP key storage and fuse strap array. Cannot fail. */
> > > +    sysbus_realize(SYS_BUS_DEVICE(&s->key_storage), &error_abort);
> > > +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->key_storage), 0, NPCM7XX_OTP1_BA);
> > > +    sysbus_realize(SYS_BUS_DEVICE(&s->fuse_array), &error_abort);
> > > +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM7XX_OTP2_BA);
> > > +    npcm7xx_init_fuses(s);
> > > +
> > >      /* Timer Modules (TIM). Cannot fail. */
> > >      QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
> > >      for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> > > diff --git a/hw/nvram/npcm7xx_otp.c b/hw/nvram/npcm7xx_otp.c
> > > new file mode 100644
> > > index 0000000000..ba4188ada8
> > > --- /dev/null
> > > +++ b/hw/nvram/npcm7xx_otp.c
> > > @@ -0,0 +1,439 @@
> > > +/*
> > > + * Nuvoton NPCM7xx OTP (Fuse Array) Interface
> > > + *
> > > + * Copyright 2020 Google LLC
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify it
> > > + * under the terms of the GNU General Public License as published by the
> > > + * Free Software Foundation; either version 2 of the License, or
> > > + * (at your option) any later version.
> > > + *
> > > + * This program is distributed in the hope that it will be useful, but WITHOUT
> > > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> > > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> > > + * for more details.
> > > + */
> > > +
> > > +#include "qemu/osdep.h"
> > > +
> > > +#include "hw/nvram/npcm7xx_otp.h"
> > > +#include "migration/vmstate.h"
> > > +#include "qapi/error.h"
> > > +#include "qemu/bitops.h"
> > > +#include "qemu/log.h"
> > > +#include "qemu/module.h"
> > > +#include "qemu/units.h"
> > > +
> > > +/* Each module has 4 KiB of register space. Only a fraction of it is used. */
> > > +#define NPCM7XX_OTP_REGS_SIZE (4 * KiB)
> > > +
> > > +/* 32-bit register indices. */
> > > +typedef enum NPCM7xxOTPRegister {
> > > +    NPCM7XX_OTP_FST,
> > > +    NPCM7XX_OTP_FADDR,
> > > +    NPCM7XX_OTP_FDATA,
> > > +    NPCM7XX_OTP_FCFG,
> > > +    /* Offset 0x10 is FKEYIND in OTP1, FUSTRAP in OTP2 */
> > > +    NPCM7XX_OTP_FKEYIND = 0x0010 / sizeof(uint32_t),
> > > +    NPCM7XX_OTP_FUSTRAP = 0x0010 / sizeof(uint32_t),
> > > +    NPCM7XX_OTP_FCTL,
> > > +    NPCM7XX_OTP_REGS_END,
> > > +} NPCM7xxOTPRegister;
> > > +
> > > +/* Register field definitions. */
> > > +#define FST_RIEN BIT(2)
> > > +#define FST_RDST BIT(1)
> > > +#define FST_RDY BIT(0)
> > > +#define FST_RO_MASK (FST_RDST | FST_RDY)
> > > +
> > > +#define FADDR_BYTEADDR(rv) extract32((rv), 0, 10)
> > > +#define FADDR_BITPOS(rv) extract32((rv), 10, 3)
> > > +
> > > +#define FDATA_CLEAR 0x00000001
> > > +
> > > +#define FCFG_FDIS BIT(31)
> > > +#define FCFG_FCFGLK_MASK 0x00ff0000
> > > +
> > > +#define FCTL_PROG_CMD1 0x00000001
> > > +#define FCTL_PROG_CMD2 0xbf79e5d0
> > > +#define FCTL_READ_CMD 0x00000002
> > > +
> > > +/**
> > > + * struct NPCM7xxOTPClass - OTP module class.
> > > + * @parent: System bus device class.
> > > + * @mmio_ops: MMIO register operations for this type of module.
> > > + *
> > > + * The two OTP modules (key-storage and fuse-array) have slightly different
> > > + * behavior, so we give them different MMIO register operations.
> > > + */
> > > +struct NPCM7xxOTPClass {
> > > +    SysBusDeviceClass parent;
> > > +
> > > +    const MemoryRegionOps *mmio_ops;
> > > +};
> > > +
> > > +#define NPCM7XX_OTP_CLASS(klass) \
> > > +    OBJECT_CLASS_CHECK(NPCM7xxOTPClass, (klass), TYPE_NPCM7XX_OTP)
> > > +#define NPCM7XX_OTP_GET_CLASS(obj) \
> > > +    OBJECT_GET_CLASS(NPCM7xxOTPClass, (obj), TYPE_NPCM7XX_OTP)
> > > +
> > > +static uint8_t ecc_encode_nibble(uint8_t n)
> > > +{
> > > +    uint8_t result = n;
> > > +
> > > +    result |= (((n >> 0) & 1) ^ ((n >> 1) & 1)) << 4;
> > > +    result |= (((n >> 2) & 1) ^ ((n >> 3) & 1)) << 5;
> > > +    result |= (((n >> 0) & 1) ^ ((n >> 2) & 1)) << 6;
> > > +    result |= (((n >> 1) & 1) ^ ((n >> 3) & 1)) << 7;
> > > +
> > > +    return result;
> > > +}
> > > +
> > > +void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
> > > +                             unsigned int offset, unsigned int len)
> > > +{
> > > +    const uint8_t *src = data;
> > > +    uint8_t *dst = &s->array[offset];
> > > +
> > > +    while (len-- > 0) {
> > > +        uint8_t c = *src++;
> > > +
> > > +        *dst++ = ecc_encode_nibble(extract8(c, 0, 4));
> > > +        *dst++ = ecc_encode_nibble(extract8(c, 4, 4));
> > > +    }
> > > +}
> > > +
> > > +/* Common register read handler for both OTP classes. */
> > > +static uint64_t npcm7xx_otp_read(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg)
> > > +{
> > > +    uint32_t value = 0;
> > > +
> > > +    switch (reg) {
> > > +    case NPCM7XX_OTP_FST:
> > > +    case NPCM7XX_OTP_FADDR:
> > > +    case NPCM7XX_OTP_FDATA:
> > > +    case NPCM7XX_OTP_FCFG:
> > > +        value = s->regs[reg];
> > > +        break;
> > > +
> > > +    case NPCM7XX_OTP_FCTL:
> > > +        qemu_log_mask(LOG_GUEST_ERROR,
> > > +                      "%s: read from write-only FCTL register\n",
> > > +                      DEVICE(s)->canonical_path);
> > > +        break;
> > > +
> > > +    default:
> > > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: read from invalid offset 0x%zx\n",
> > > +                      DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
> > > +        break;
> > > +    }
> > > +
> > > +    return value;
> > > +}
> > > +
> > > +/* Read a byte from the OTP array into the data register. */
> > > +static void npcm7xx_otp_read_array(NPCM7xxOTPState *s)
> > > +{
> > > +    uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
> > > +
> > > +    s->regs[NPCM7XX_OTP_FDATA] = s->array[FADDR_BYTEADDR(faddr)];
> > > +    s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
> > > +}
> > > +
> > > +/* Program a byte from the data register into the OTP array. */
> > > +static void npcm7xx_otp_program_array(NPCM7xxOTPState *s)
> > > +{
> > > +    uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
> > > +
> > > +    /* Bits can only go 0->1, never 1->0. */
> > > +    s->array[FADDR_BYTEADDR(faddr)] |= (1U << FADDR_BITPOS(faddr));
> > > +    s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
> > > +}
> > > +
> > > +/* Compute the next value of the FCFG register. */
> > > +static uint32_t npcm7xx_otp_compute_fcfg(uint32_t cur_value, uint32_t new_value)
> > > +{
> > > +    uint32_t lock_mask;
> > > +    uint32_t value;
> > > +
> > > +    /*
> > > +     * FCFGLK holds sticky bits 16..23, indicating which bits in FPRGLK (8..15)
> > > +     * and FRDLK (0..7) that are read-only.
> > > +     */
> > > +    lock_mask = (cur_value & FCFG_FCFGLK_MASK) >> 8;
> > > +    lock_mask |= lock_mask >> 8;
> > > +    /* FDIS and FCFGLK bits are sticky (write 1 to set; can't clear). */
> > > +    value = cur_value & (FCFG_FDIS | FCFG_FCFGLK_MASK);
> > > +    /* Preserve read-only bits in FPRGLK and FRDLK */
> > > +    value |= cur_value & lock_mask;
> > > +    /* Set all bits that aren't read-only. */
> > > +    value |= new_value & ~lock_mask;
> > > +
> > > +    return value;
> > > +}
> > > +
> > > +/* Common register write handler for both OTP classes. */
> > > +static void npcm7xx_otp_write(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg,
> > > +                              uint32_t value)
> > > +{
> > > +    switch (reg) {
> > > +    case NPCM7XX_OTP_FST:
> > > +        /* RDST is cleared by writing 1 to it. */
> > > +        if (value & FST_RDST) {
> > > +            s->regs[NPCM7XX_OTP_FST] &= ~FST_RDST;
> > > +        }
> > > +        /* Preserve read-only and write-one-to-clear bits */
> > > +        value &= ~FST_RO_MASK;
> > > +        value |= s->regs[NPCM7XX_OTP_FST] & FST_RO_MASK;
> > > +        break;
> > > +
> > > +    case NPCM7XX_OTP_FADDR:
> > > +        break;
> > > +
> > > +    case NPCM7XX_OTP_FDATA:
> > > +        /*
> > > +         * This register is cleared by writing a magic value to it; no other
> > > +         * values can be written.
> > > +         */
> > > +        if (value == FDATA_CLEAR) {
> > > +            value = 0;
> > > +        } else {
> > > +            value = s->regs[NPCM7XX_OTP_FDATA];
> > > +        }
> > > +        break;
> > > +
> > > +    case NPCM7XX_OTP_FCFG:
> > > +        value = npcm7xx_otp_compute_fcfg(s->regs[NPCM7XX_OTP_FCFG], value);
> > > +        break;
> > > +
> > > +    case NPCM7XX_OTP_FCTL:
> > > +        switch (value) {
> > > +        case FCTL_READ_CMD:
> > > +            npcm7xx_otp_read_array(s);
> > > +            break;
> > > +
> > > +        case FCTL_PROG_CMD1:
> > > +            /*
> > > +             * Programming requires writing two separate magic values to this
> > > +             * register; this is the first one. Just store it so it can be
> > > +             * verified later when the second magic value is received.
> > > +             */
> > > +            break;
> > > +
> > > +        case FCTL_PROG_CMD2:
> > > +            /*
> > > +             * Only initiate programming if we received the first half of the
> > > +             * command immediately before this one.
> > > +             */
> > > +            if (s->regs[NPCM7XX_OTP_FCTL] == FCTL_PROG_CMD1) {
> > > +                npcm7xx_otp_program_array(s);
> > > +            }
> > > +            break;
> > > +
> > > +        default:
> > > +            qemu_log_mask(LOG_GUEST_ERROR,
> > > +                          "%s: unrecognized FCNTL value 0x%" PRIx32 "\n",
> > > +                          DEVICE(s)->canonical_path, value);
> > > +            break;
> > > +        }
> > > +        if (value != FCTL_PROG_CMD1) {
> > > +            value = 0;
> > > +        }
> > > +        break;
> > > +
> > > +    default:
> > > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: write to invalid offset 0x%zx\n",
> > > +                      DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
> > > +        return;
> > > +    }
> > > +
> > > +    s->regs[reg] = value;
> > > +}
> > > +
> > > +/* Register read handler specific to the fuse array OTP module. */
> > > +static uint64_t npcm7xx_fuse_array_read(void *opaque, hwaddr addr,
> > > +                                        unsigned int size)
> > > +{
> > > +    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
> > > +    NPCM7xxOTPState *s = opaque;
> > > +    uint32_t value;
> > > +
> > > +    /*
> > > +     * Only the Fuse Strap register needs special handling; all other registers
> > > +     * work the same way for both kinds of OTP modules.
> > > +     */
> > > +    if (reg != NPCM7XX_OTP_FUSTRAP) {
> > > +        value = npcm7xx_otp_read(s, reg);
> > > +    } else {
> > > +        /* FUSTRAP is stored as three copies in the OTP array. */
> > > +        uint32_t fustrap[3];
> > > +
> > > +        memcpy(fustrap, &s->array[0], sizeof(fustrap));
> > > +
> > > +        /* Determine value by a majority vote on each bit. */
> > > +        value = (fustrap[0] & fustrap[1]) | (fustrap[0] & fustrap[2]) |
> > > +                (fustrap[1] & fustrap[2]);
> > > +    }
> > > +
> > > +    return value;
> > > +}
> > > +
> > > +/* Register write handler specific to the fuse array OTP module. */
> > > +static void npcm7xx_fuse_array_write(void *opaque, hwaddr addr, uint64_t v,
> > > +                                     unsigned int size)
> > > +{
> > > +    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
> > > +    NPCM7xxOTPState *s = opaque;
> > > +
> > > +    /*
> > > +     * The Fuse Strap register is read-only. Other registers are handled by
> > > +     * common code.
> > > +     */
> > > +    if (reg != NPCM7XX_OTP_FUSTRAP) {
> > > +        npcm7xx_otp_write(s, reg, v);
> > > +    }
> > > +}
> > > +
> > > +static const MemoryRegionOps npcm7xx_fuse_array_ops = {
> > > +    .read       = npcm7xx_fuse_array_read,
> > > +    .write      = npcm7xx_fuse_array_write,
> > > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > > +    .valid      = {
> > > +        .min_access_size        = 4,
> > > +        .max_access_size        = 4,
> > > +        .unaligned              = false,
> > > +    },
> > > +};
> > > +
> > > +/* Register read handler specific to the key storage OTP module. */
> > > +static uint64_t npcm7xx_key_storage_read(void *opaque, hwaddr addr,
> > > +                                         unsigned int size)
> > > +{
> > > +    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
> > > +    NPCM7xxOTPState *s = opaque;
> > > +
> > > +    /*
> > > +     * Only the Fuse Key Index register needs special handling; all other
> > > +     * registers work the same way for both kinds of OTP modules.
> > > +     */
> > > +    if (reg != NPCM7XX_OTP_FKEYIND) {
> > > +        return npcm7xx_otp_read(s, reg);
> > > +    }
> > > +
> > > +    qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
> > > +
> > > +    return s->regs[NPCM7XX_OTP_FKEYIND];
> > > +}
> > > +
> > > +/* Register write handler specific to the key storage OTP module. */
> > > +static void npcm7xx_key_storage_write(void *opaque, hwaddr addr, uint64_t v,
> > > +                                      unsigned int size)
> > > +{
> > > +    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
> > > +    NPCM7xxOTPState *s = opaque;
> > > +
> > > +    /*
> > > +     * Only the Fuse Key Index register needs special handling; all other
> > > +     * registers work the same way for both kinds of OTP modules.
> > > +     */
> > > +    if (reg != NPCM7XX_OTP_FKEYIND) {
> > > +        npcm7xx_otp_write(s, reg, v);
> > > +        return;
> > > +    }
> > > +
> > > +    qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
> > > +
> > > +    s->regs[NPCM7XX_OTP_FKEYIND] = v;
> > > +}
> > > +
> > > +static const MemoryRegionOps npcm7xx_key_storage_ops = {
> > > +    .read       = npcm7xx_key_storage_read,
> > > +    .write      = npcm7xx_key_storage_write,
> > > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > > +    .valid      = {
> > > +        .min_access_size        = 4,
> > > +        .max_access_size        = 4,
> > > +        .unaligned              = false,
> > > +    },
> > > +};
> > > +
> > > +static void npcm7xx_otp_enter_reset(Object *obj, ResetType type)
> > > +{
> > > +    NPCM7xxOTPState *s = NPCM7XX_OTP(obj);
> > > +
> > > +    memset(s->regs, 0, sizeof(s->regs));
> > > +
> > > +    s->regs[NPCM7XX_OTP_FST] = 0x00000001;
> > > +    s->regs[NPCM7XX_OTP_FCFG] = 0x20000000;
> > > +}
> > > +
> > > +static void npcm7xx_otp_realize(DeviceState *dev, Error **errp)
> > > +{
> > > +    NPCM7xxOTPClass *oc = NPCM7XX_OTP_GET_CLASS(dev);
> > > +    NPCM7xxOTPState *s = NPCM7XX_OTP(dev);
> > > +    SysBusDevice *sbd = &s->parent;
> > > +
> > > +    memset(s->array, 0, sizeof(s->array));
> > > +
> > > +    memory_region_init_io(&s->mmio, OBJECT(s), oc->mmio_ops, s, "regs",
> > > +                          NPCM7XX_OTP_REGS_SIZE);
> > > +    sysbus_init_mmio(sbd, &s->mmio);
> > > +}
> > > +
> > > +static const VMStateDescription vmstate_npcm7xx_otp = {
> > > +    .name = "npcm7xx-otp",
> > > +    .version_id = 0,
> > > +    .minimum_version_id = 0,
> > > +    .fields = (VMStateField[]) {
> > > +        VMSTATE_UINT32_ARRAY(regs, NPCM7xxOTPState, NPCM7XX_OTP_NR_REGS),
> > > +        VMSTATE_UINT8_ARRAY(array, NPCM7xxOTPState, NPCM7XX_OTP_ARRAY_BYTES),
> > > +        VMSTATE_END_OF_LIST(),
> > > +    },
> > > +};
> > > +
> > > +static void npcm7xx_otp_class_init(ObjectClass *klass, void *data)
> > > +{
> > > +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> > > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > > +
> > > +    QEMU_BUILD_BUG_ON(NPCM7XX_OTP_REGS_END > NPCM7XX_OTP_NR_REGS);
> > > +
> > > +    dc->realize = npcm7xx_otp_realize;
> > > +    dc->vmsd = &vmstate_npcm7xx_otp;
> > > +    rc->phases.enter = npcm7xx_otp_enter_reset;
> > > +}
> > > +
> > > +static void npcm7xx_key_storage_class_init(ObjectClass *klass, void *data)
> > > +{
> > > +    NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
> > > +
> > > +    oc->mmio_ops = &npcm7xx_key_storage_ops;
> > > +}
> >
> > Hello,
> > With this series applied, when I build with --enable-sanitizers and run
> > ./qemu-system-arm:
> >
> > ==373753==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61000000b400 at pc 0x557496abbefc bp 0x7ffdd5851210 sp 0x7ffdd5851208
> > WRITE of size 8 at 0x61000000b400 thread T0
> >     #0 0x557496abbefb in npcm7xx_key_storage_class_init /../hw/nvram/npcm7xx_otp.c:410:18
> >     #1 0x5574998a8780 in type_initialize /../qom/object.c:362:9
> >     #2 0x5574998a9bef in object_class_foreach_tramp /../qom/object.c:1030:5
> >     #3 0x7fc26b427b2f in g_hash_table_foreach (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x3eb2f)
> >     #4 0x5574998a9a41 in object_class_foreach /../qom/object.c:1052:5
> >     #5 0x5574998ab28a in object_class_get_list /../qom/object.c:1109:5
> >     #6 0x557498e6f8e1 in select_machine /../softmmu/vl.c:2438:24
> >     #7 0x557498e5a921 in qemu_init /../softmmu/vl.c:3842:21
> >     #8 0x557495b181d7 in main /../softmmu/main.c:49:5
> >     #9 0x7fc269e7dcc9 in __libc_start_main csu/../csu/libc-start.c:308:16
> >     #10 0x557495a6d9b9 in _start (/qemu-system-arm+0x35959b9)
> >
> > 0x61000000b400 is located 0 bytes to the right of 192-byte region [0x61000000b340,0x61000000b400)
> > allocated by thread T0 here:
> >     #0 0x557495ae6272 in calloc (/qemu-system-arm+0x360e272)
> >     #1 0x7fc26b43f210 in g_malloc0 (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x56210)
> >     #2 0x5574998a9bef in object_class_foreach_tramp /../qom/object.c:1030:5
> >     #3 0x7fc26b427b2f in g_hash_table_foreach (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x3eb2f)
> >
> > SUMMARY: AddressSanitizer: heap-buffer-overflow /../hw/nvram/npcm7xx_otp.c:410:18 in npcm7xx_key_storage_class_init
> >
> > -Alex
> >
> > > +
> > > +static void npcm7xx_fuse_array_class_init(ObjectClass *klass, void *data)
> > > +{
> > > +    NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
> > > +
> > > +    oc->mmio_ops = &npcm7xx_fuse_array_ops;
> > > +}
> > > +
> > > +static const TypeInfo npcm7xx_otp_types[] = {
> > > +    {
> > > +        .name = TYPE_NPCM7XX_OTP,
> > > +        .parent = TYPE_SYS_BUS_DEVICE,
> > > +        .instance_size = sizeof(NPCM7xxOTPState),
> Adding this fixes it for me:
>              .class_size = sizeof(NPCM7xxOTPClass),

You're absolutely right. I'll send out another series with this fix.
Thanks for catching this.

Havard


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

* Re: [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines
  2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
                   ` (14 preceding siblings ...)
  2020-09-03 18:20 ` [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Philippe Mathieu-Daudé
@ 2020-09-08 15:02 ` Alexander Bulekov
  2020-09-08 15:52   ` Philippe Mathieu-Daudé
  15 siblings, 1 reply; 35+ messages in thread
From: Alexander Bulekov @ 2020-09-08 15:02 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: peter.maydell, f4bug, qemu-devel, kfting, qemu-arm, Avi.Fishman

Hi Havard,
I fuzzed the npcm750-evb machine until I hit over 85% coverage over all
the new npcm.*\.c files. The only thing I found specific to the new
code, so far:

cat << EOF | ./qemu-system-arm -machine npcm750-evb -m 128M -qtest stdio 
write 0xf0009040 0x4 0xc4c4c4c4
write 0xf0009040 0x4 0x4
EOF

ERROR:../hw/timer/npcm7xx_timer.c:160:npcm7xx_timer_pause: assertion failed: (t->remaining_ns > 0)
Bail out! ERROR:../hw/timer/npcm7xx_timer.c:160:npcm7xx_timer_pause: assertion failed: (t->remaining_ns > 0)
Aborted

I'm doing the same for the quanta-gsj machine, but I'm not sure whether
it will cover more code, so I'm happy to leave a:

Tested-by: Alexander Bulekov <alxndr@bu.edu>

for the patches that add new virtual-device code (1-5, 7-12 ?)
-Alex


On 200824 1716, Havard Skinnemoen via wrote:
> I also pushed this and the previous patchsets to my qemu fork on github.
> The branches are named npcm7xx-v[1-8].
> 
>   https://github.com/hskinnemoen/qemu
> 
> This patch series models enough of the Nuvoton NPCM730 and NPCM750 SoCs to boot
> an OpenBMC image built for quanta-gsj. This includes device models for:
> 
>   - Global Configuration Registers
>   - Clock Control
>   - Timers
>   - Fuses
>   - Memory Controller
>   - Flash Controller
> 
> These modules, along with the existing Cortex A9 CPU cores and built-in
> peripherals, are integrated into a NPCM730 or NPCM750 SoC, which in turn form
> the foundation for the quanta-gsj and npcm750-evb machines, respectively. The
> two SoCs are very similar; the only difference is that NPCM730 is missing some
> peripherals that NPCM750 has, and which are not considered essential for
> datacenter use (e.g. graphics controllers). For more information, see
> 
> https://www.nuvoton.com/products/cloud-computing/ibmc/
> 
> Both quanta-gsj and npcm750-evb correspond to real boards supported by OpenBMC.
> At the end of the series, qemu can boot an OpenBMC image built for one of these
> boards with some minor modifications.
> 
> The patches in this series were developed by Google and reviewed by Nuvoton. We
> will be maintaining the machine and peripheral support together.
> 
> The data sheet for these SoCs is not generally available. Please let me know if
> more comments are needed to understand the device behavior.
> 
> Changes since v7:
> 
>   - Move register enums to .c files throughout, leaving a single
>     NPCM7XX_FOO_NR_REGS definition behind in the .h file. A QEMU_BUILD_BUG_ON
>     should alert anyone accidentally expanding the register enum that they need
>     to update the corresponding NR_REGS define, which in turn has a comment
>     reminding them to update the vmstate version_id as well.
>   - Skip loading the bootrom if a kernel filename is provided by the user.
>   - New patch adding a board setup stub to tweak clocks before booting directly
>     into the kernel.
>   - Add stuff to meson files instead of Makefiles.
>   - Try to disable the slowest drivers and services to speed up the flash boot
>     acceptance test a bit. This is somewhat based on the following
>     systemd-analyze blame report:
>     https://gist.github.com/hskinnemoen/475cb0676530cd2cebaa1754cf16ca97
> 
> Changes since v6:
> 
>   - Use size_to_str to report DRAM sizes in npcm7xx_gcr.
>   - Simplify the interrupt logic in npcm7xx_timer.
>   - Update global bios_name instead of temporary.
>   - Add npcm7xx_bootrom to MAINTAINERS and pc-bios/README.
>   - Use a predefined name for the gsj boot image in the acceptance test.
> 
> Changes since v5:
> 
>   - Boot ROM included, as a git submodule and a binary blob, and loaded by
>     default, so the -bios option is usually not necessary anymore.
>   - Two acceptance tests added (openbmc image boot, and direct kernel boot).
>   - npcm7xx_load_kernel() moved to SoC code.
>   - NPCM7XX_TIMER_REF_HZ definition moved to CLK header.
>   - Comments added clarifying available SPI flash chip selects.
>   - Error handling adjustments:
>       - Errors from CPU and GCR realization are propagated through the SoC
>         since they may be triggered by user-configurable parameters.
>       - Machine init uses error_fatal instead of error_abort for SoC
>         realization flash init. This makes error messages more helpful.
>       - Comments added to indicate whether peripherals may fail to realize.
>       - Use ERRP_GUARD() instead of Error *err when possible.
>   - Default CPU type is now set, and attempting to set it to anything else
>     will fail.
>   - Format string fixes (use HWADDR_PRIx, etc.)
>   - Simplified memory size encoding and error checking in npcm7xx_gcr.
>   - Encapsulate non-obvious pointer subtraction into helper functions in the
>     FIU and TIMER modules.
>   - Incorporate review feedback into the FIU module:
>       - Add select/deselect trace events.
>       - Use npcm7xx_fiu_{de,}select() consistently.
>       - Use extract/deposit in more places for consistency.
>       - Use -Wimplicit-fallthrough compatible fallthrough comments.
>       - Use qdev_init_gpio_out_named instead of sysbus_init_irq for chip
>         selects.
>   - Incorporate review feedback into the TIMER module:
>       - Assert that we never pause a timer that has already expired, instead of
>         trying to handle it. This should be safe since QEMU_CLOCK_VIRTUAL is
>         stopped while this code is running.
>       - Simplify the switch blocks in the read and write handlers.
> 
> I made a change to error out if a flash drive was not specified, but reverted
> it because it caused make check to fail (qom-test). When specifying a NULL
> block device, the m25p flash device initializes its in-memory storage with 0xff
> and doesn't attempt to write anything back. This seems correct to me.
> 
> Changes since v4:
> 
>   - OTP cleanups suggested by Philippe Mathieu-Daudé.
>       - Added fuse array definitions based on public Nuvoton bootblock code.
>       - Moved class structure to .c file since it's only used internally.
>       - Readability improvements.
>   - Split the first patch and folded parts of it into three other patches so
>     that CONFIG_NPCM7XX is only enabled after the initial NPCM7xx machine
>     support is added.
>   - DRAM init moved to machine init code.
>   - Consistently use lower-case hex literals.
>   - Switched to fine-grained unimplemented devices, based on public bootblock
>     source code. Added a tiny SRAM that got left out previously.
>   - Simplified error handling in npcm7xx_realize() since the board code will
>     abort anyway, and SoCs are not hot-pluggable.
> 
> Changes since v3:
> 
>   - License headers are now GPL v2-or-later throughout.
>   - Added vmstate throughout (except the memory controller, which doesn't
>     really have any state worth saving). Successfully booted a gsj image
>     with two stop/savevm/quit/loadvm cycles along the way.
>       - JFFS2 really doesn't like it if I let qemu keep running after savevm,
>         and then jump back in time with loadvm. I assume this is expected.
>   - Fixed an error API violation in npcm7xx_realize, removed pointless error
>     check after object_property_set_link().
>   - Switched the OTP device to use an embedded array instead of a g_malloc0'd
>     one because I couldn't figure out how to set up vmstate for the latter.
> 
> Changes since v2:
> 
>   - Simplified the MAINTAINERS entry.
>   - Added link to OpenPOWER jenkins for gsj BMC images.
>   - Reverted the smpboot change, back to byte swapping.
>   - Adapted to upstream API changes:
>       - sysbus_init_child_obj -> object_initialize_child
>       - object_property_set_bool -> qdev_realize / sysbus_realize
>       - ssi_create_slave_no_init -> qdev_new
>       - qdev_init_nofail -> qdev_realize_and_unref
>       - ssi_auto_connect_slaves removed
>   - Moved Boot ROM loading from soc to machine init.
>   - Plumbed power-on-straps property from GCR to the machine init code so it
>     can be properly initialized. Turns out npcm750 memory init doesn't work
>     without this. npcm730 is fine either way, though I'm not sure why.
>   - Reworked the flash init code so it looks more like aspeed (i.e. the flash
>     device gets added even if there's no drive).
> 
> Changes since v1 (requested by reviewers):
> 
>   - Clarify the source of CLK reset values.
>   - Made smpboot a constant byte array, eliinated byte swapping.
>   - NPCM7xxState now stores an array of ARMCPUs, not pointers to ARMCPUs.
>   - Clarify why EL3 is disabled.
>   - Introduce NPCM7XX_NUM_IRQ constant.
>   - Set the number of CPUs according to SoC variant, and disallow command line
>     overrides (i.e. you can no longer override the number of CPUs with the -smp
>     parameter). This is trying to follow the spirit of
>     https://patchwork.kernel.org/patch/11595407/.
>   - Switch register operations to DEVICE_LITTLE_ENDIAN throughout.
>   - Machine documentation added (new patch).
> 
> Changes since v1 to support flash booting:
> 
>   - GCR reset value changes to get past memory initialization when booting
>     from flash (patches 2 and 5):
>       - INTCR2 now indicates that the DDR controller is initialized.
>       - INTCR3 is initialized according to DDR memory size. A realize()
> 	method was implemented to achieve this.
>   - Refactor the machine initialization a bit to make it easier to drop in
>     machine-specific flash initialization (patch 6).
>   - Extend the series with additional patches to enable booting from flash:
>       - Boot ROM (through the -bios option).
>       - OTP (fuse) controller.
>       - Memory Controller stub (just enough to skip memory training).
>       - Flash controller.
>       - Board-specific flash initialization.
> 
> Thanks for reviewing,
> 
> Havard
> 
> Havard Skinnemoen (14):
>   hw/misc: Add NPCM7xx System Global Control Registers device model
>   hw/misc: Add NPCM7xx Clock Controller device model
>   hw/timer: Add NPCM7xx Timer device model
>   hw/arm: Add NPCM730 and NPCM750 SoC models
>   hw/arm: Add two NPCM7xx-based machines
>   roms: Add virtual Boot ROM for NPCM7xx SoCs
>   hw/arm: Load -bios image as a boot ROM for npcm7xx
>   hw/nvram: NPCM7xx OTP device model
>   hw/mem: Stubbed out NPCM7xx Memory Controller model
>   hw/ssi: NPCM7xx Flash Interface Unit device model
>   hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
>   hw/arm/npcm7xx: add board setup stub for CPU and UART clocks
>   docs/system: Add Nuvoton machine documentation
>   tests/acceptance: console boot tests for quanta-gsj
> 
>  docs/system/arm/nuvoton.rst            |  90 ++++
>  docs/system/target-arm.rst             |   1 +
>  Makefile                               |   1 +
>  default-configs/arm-softmmu.mak        |   1 +
>  include/hw/arm/npcm7xx.h               | 112 +++++
>  include/hw/mem/npcm7xx_mc.h            |  36 ++
>  include/hw/misc/npcm7xx_clk.h          |  48 +++
>  include/hw/misc/npcm7xx_gcr.h          |  43 ++
>  include/hw/nvram/npcm7xx_otp.h         |  79 ++++
>  include/hw/ssi/npcm7xx_fiu.h           |  73 ++++
>  include/hw/timer/npcm7xx_timer.h       |  78 ++++
>  hw/arm/npcm7xx.c                       | 532 +++++++++++++++++++++++
>  hw/arm/npcm7xx_boards.c                | 197 +++++++++
>  hw/mem/npcm7xx_mc.c                    |  84 ++++
>  hw/misc/npcm7xx_clk.c                  | 266 ++++++++++++
>  hw/misc/npcm7xx_gcr.c                  | 269 ++++++++++++
>  hw/nvram/npcm7xx_otp.c                 | 439 +++++++++++++++++++
>  hw/ssi/npcm7xx_fiu.c                   | 572 +++++++++++++++++++++++++
>  hw/timer/npcm7xx_timer.c               | 509 ++++++++++++++++++++++
>  .gitmodules                            |   3 +
>  MAINTAINERS                            |  10 +
>  hw/arm/Kconfig                         |   9 +
>  hw/arm/meson.build                     |   1 +
>  hw/mem/meson.build                     |   1 +
>  hw/misc/meson.build                    |   4 +
>  hw/misc/trace-events                   |   8 +
>  hw/nvram/meson.build                   |   1 +
>  hw/ssi/meson.build                     |   1 +
>  hw/ssi/trace-events                    |  11 +
>  hw/timer/meson.build                   |   1 +
>  hw/timer/trace-events                  |   5 +
>  pc-bios/README                         |   6 +
>  pc-bios/npcm7xx_bootrom.bin            | Bin 0 -> 768 bytes
>  roms/Makefile                          |   7 +
>  roms/vbootrom                          |   1 +
>  tests/acceptance/boot_linux_console.py |  83 ++++
>  36 files changed, 3582 insertions(+)
>  create mode 100644 docs/system/arm/nuvoton.rst
>  create mode 100644 include/hw/arm/npcm7xx.h
>  create mode 100644 include/hw/mem/npcm7xx_mc.h
>  create mode 100644 include/hw/misc/npcm7xx_clk.h
>  create mode 100644 include/hw/misc/npcm7xx_gcr.h
>  create mode 100644 include/hw/nvram/npcm7xx_otp.h
>  create mode 100644 include/hw/ssi/npcm7xx_fiu.h
>  create mode 100644 include/hw/timer/npcm7xx_timer.h
>  create mode 100644 hw/arm/npcm7xx.c
>  create mode 100644 hw/arm/npcm7xx_boards.c
>  create mode 100644 hw/mem/npcm7xx_mc.c
>  create mode 100644 hw/misc/npcm7xx_clk.c
>  create mode 100644 hw/misc/npcm7xx_gcr.c
>  create mode 100644 hw/nvram/npcm7xx_otp.c
>  create mode 100644 hw/ssi/npcm7xx_fiu.c
>  create mode 100644 hw/timer/npcm7xx_timer.c
>  create mode 100644 pc-bios/npcm7xx_bootrom.bin
>  create mode 160000 roms/vbootrom
> 
> -- 
> 2.28.0.297.g1956fa8f8d-goog
> 
> 


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

* Re: [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines
  2020-09-08 15:02 ` Alexander Bulekov
@ 2020-09-08 15:52   ` Philippe Mathieu-Daudé
  2020-09-08 16:58     ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 35+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-08 15:52 UTC (permalink / raw)
  To: Alexander Bulekov, Havard Skinnemoen
  Cc: peter.maydell, qemu-arm, qemu-devel, Avi.Fishman, kfting

On 9/8/20 5:02 PM, Alexander Bulekov wrote:
> Hi Havard,
> I fuzzed the npcm750-evb machine until I hit over 85% coverage over all
> the new npcm.*\.c files. The only thing I found specific to the new
> code, so far:
> 
> cat << EOF | ./qemu-system-arm -machine npcm750-evb -m 128M -qtest stdio 
> write 0xf0009040 0x4 0xc4c4c4c4
> write 0xf0009040 0x4 0x4
> EOF

This is an odd test because with -qtest the timer is not running,
so this can not really happen on real hw.

The fix is:

-    g_assert(t->remaining_ns > 0);
+    g_assert(qtest_enabled() || t->remaining_ns > 0);

> 
> ERROR:../hw/timer/npcm7xx_timer.c:160:npcm7xx_timer_pause: assertion failed: (t->remaining_ns > 0)
> Bail out! ERROR:../hw/timer/npcm7xx_timer.c:160:npcm7xx_timer_pause: assertion failed: (t->remaining_ns > 0)
> Aborted
> 
> I'm doing the same for the quanta-gsj machine, but I'm not sure whether
> it will cover more code, so I'm happy to leave a:
> 
> Tested-by: Alexander Bulekov <alxndr@bu.edu>
> 
> for the patches that add new virtual-device code (1-5, 7-12 ?)
> -Alex

Very nice from you for testing running the fuzzer!

Regards,

Phil.

> 
> 
> On 200824 1716, Havard Skinnemoen via wrote:
>> I also pushed this and the previous patchsets to my qemu fork on github.
>> The branches are named npcm7xx-v[1-8].
>>
>>   https://github.com/hskinnemoen/qemu
>>
>> This patch series models enough of the Nuvoton NPCM730 and NPCM750 SoCs to boot
>> an OpenBMC image built for quanta-gsj. This includes device models for:
>>
>>   - Global Configuration Registers
>>   - Clock Control
>>   - Timers
>>   - Fuses
>>   - Memory Controller
>>   - Flash Controller
>>
>> These modules, along with the existing Cortex A9 CPU cores and built-in
>> peripherals, are integrated into a NPCM730 or NPCM750 SoC, which in turn form
>> the foundation for the quanta-gsj and npcm750-evb machines, respectively. The
>> two SoCs are very similar; the only difference is that NPCM730 is missing some
>> peripherals that NPCM750 has, and which are not considered essential for
>> datacenter use (e.g. graphics controllers). For more information, see
>>
>> https://www.nuvoton.com/products/cloud-computing/ibmc/
>>
>> Both quanta-gsj and npcm750-evb correspond to real boards supported by OpenBMC.
>> At the end of the series, qemu can boot an OpenBMC image built for one of these
>> boards with some minor modifications.
>>
>> The patches in this series were developed by Google and reviewed by Nuvoton. We
>> will be maintaining the machine and peripheral support together.
>>
>> The data sheet for these SoCs is not generally available. Please let me know if
>> more comments are needed to understand the device behavior.
>>
>> Changes since v7:
>>
>>   - Move register enums to .c files throughout, leaving a single
>>     NPCM7XX_FOO_NR_REGS definition behind in the .h file. A QEMU_BUILD_BUG_ON
>>     should alert anyone accidentally expanding the register enum that they need
>>     to update the corresponding NR_REGS define, which in turn has a comment
>>     reminding them to update the vmstate version_id as well.
>>   - Skip loading the bootrom if a kernel filename is provided by the user.
>>   - New patch adding a board setup stub to tweak clocks before booting directly
>>     into the kernel.
>>   - Add stuff to meson files instead of Makefiles.
>>   - Try to disable the slowest drivers and services to speed up the flash boot
>>     acceptance test a bit. This is somewhat based on the following
>>     systemd-analyze blame report:
>>     https://gist.github.com/hskinnemoen/475cb0676530cd2cebaa1754cf16ca97
>>
>> Changes since v6:
>>
>>   - Use size_to_str to report DRAM sizes in npcm7xx_gcr.
>>   - Simplify the interrupt logic in npcm7xx_timer.
>>   - Update global bios_name instead of temporary.
>>   - Add npcm7xx_bootrom to MAINTAINERS and pc-bios/README.
>>   - Use a predefined name for the gsj boot image in the acceptance test.
>>
>> Changes since v5:
>>
>>   - Boot ROM included, as a git submodule and a binary blob, and loaded by
>>     default, so the -bios option is usually not necessary anymore.
>>   - Two acceptance tests added (openbmc image boot, and direct kernel boot).
>>   - npcm7xx_load_kernel() moved to SoC code.
>>   - NPCM7XX_TIMER_REF_HZ definition moved to CLK header.
>>   - Comments added clarifying available SPI flash chip selects.
>>   - Error handling adjustments:
>>       - Errors from CPU and GCR realization are propagated through the SoC
>>         since they may be triggered by user-configurable parameters.
>>       - Machine init uses error_fatal instead of error_abort for SoC
>>         realization flash init. This makes error messages more helpful.
>>       - Comments added to indicate whether peripherals may fail to realize.
>>       - Use ERRP_GUARD() instead of Error *err when possible.
>>   - Default CPU type is now set, and attempting to set it to anything else
>>     will fail.
>>   - Format string fixes (use HWADDR_PRIx, etc.)
>>   - Simplified memory size encoding and error checking in npcm7xx_gcr.
>>   - Encapsulate non-obvious pointer subtraction into helper functions in the
>>     FIU and TIMER modules.
>>   - Incorporate review feedback into the FIU module:
>>       - Add select/deselect trace events.
>>       - Use npcm7xx_fiu_{de,}select() consistently.
>>       - Use extract/deposit in more places for consistency.
>>       - Use -Wimplicit-fallthrough compatible fallthrough comments.
>>       - Use qdev_init_gpio_out_named instead of sysbus_init_irq for chip
>>         selects.
>>   - Incorporate review feedback into the TIMER module:
>>       - Assert that we never pause a timer that has already expired, instead of
>>         trying to handle it. This should be safe since QEMU_CLOCK_VIRTUAL is
>>         stopped while this code is running.
>>       - Simplify the switch blocks in the read and write handlers.
>>
>> I made a change to error out if a flash drive was not specified, but reverted
>> it because it caused make check to fail (qom-test). When specifying a NULL
>> block device, the m25p flash device initializes its in-memory storage with 0xff
>> and doesn't attempt to write anything back. This seems correct to me.
>>
>> Changes since v4:
>>
>>   - OTP cleanups suggested by Philippe Mathieu-Daudé.
>>       - Added fuse array definitions based on public Nuvoton bootblock code.
>>       - Moved class structure to .c file since it's only used internally.
>>       - Readability improvements.
>>   - Split the first patch and folded parts of it into three other patches so
>>     that CONFIG_NPCM7XX is only enabled after the initial NPCM7xx machine
>>     support is added.
>>   - DRAM init moved to machine init code.
>>   - Consistently use lower-case hex literals.
>>   - Switched to fine-grained unimplemented devices, based on public bootblock
>>     source code. Added a tiny SRAM that got left out previously.
>>   - Simplified error handling in npcm7xx_realize() since the board code will
>>     abort anyway, and SoCs are not hot-pluggable.
>>
>> Changes since v3:
>>
>>   - License headers are now GPL v2-or-later throughout.
>>   - Added vmstate throughout (except the memory controller, which doesn't
>>     really have any state worth saving). Successfully booted a gsj image
>>     with two stop/savevm/quit/loadvm cycles along the way.
>>       - JFFS2 really doesn't like it if I let qemu keep running after savevm,
>>         and then jump back in time with loadvm. I assume this is expected.
>>   - Fixed an error API violation in npcm7xx_realize, removed pointless error
>>     check after object_property_set_link().
>>   - Switched the OTP device to use an embedded array instead of a g_malloc0'd
>>     one because I couldn't figure out how to set up vmstate for the latter.
>>
>> Changes since v2:
>>
>>   - Simplified the MAINTAINERS entry.
>>   - Added link to OpenPOWER jenkins for gsj BMC images.
>>   - Reverted the smpboot change, back to byte swapping.
>>   - Adapted to upstream API changes:
>>       - sysbus_init_child_obj -> object_initialize_child
>>       - object_property_set_bool -> qdev_realize / sysbus_realize
>>       - ssi_create_slave_no_init -> qdev_new
>>       - qdev_init_nofail -> qdev_realize_and_unref
>>       - ssi_auto_connect_slaves removed
>>   - Moved Boot ROM loading from soc to machine init.
>>   - Plumbed power-on-straps property from GCR to the machine init code so it
>>     can be properly initialized. Turns out npcm750 memory init doesn't work
>>     without this. npcm730 is fine either way, though I'm not sure why.
>>   - Reworked the flash init code so it looks more like aspeed (i.e. the flash
>>     device gets added even if there's no drive).
>>
>> Changes since v1 (requested by reviewers):
>>
>>   - Clarify the source of CLK reset values.
>>   - Made smpboot a constant byte array, eliinated byte swapping.
>>   - NPCM7xxState now stores an array of ARMCPUs, not pointers to ARMCPUs.
>>   - Clarify why EL3 is disabled.
>>   - Introduce NPCM7XX_NUM_IRQ constant.
>>   - Set the number of CPUs according to SoC variant, and disallow command line
>>     overrides (i.e. you can no longer override the number of CPUs with the -smp
>>     parameter). This is trying to follow the spirit of
>>     https://patchwork.kernel.org/patch/11595407/.
>>   - Switch register operations to DEVICE_LITTLE_ENDIAN throughout.
>>   - Machine documentation added (new patch).
>>
>> Changes since v1 to support flash booting:
>>
>>   - GCR reset value changes to get past memory initialization when booting
>>     from flash (patches 2 and 5):
>>       - INTCR2 now indicates that the DDR controller is initialized.
>>       - INTCR3 is initialized according to DDR memory size. A realize()
>> 	method was implemented to achieve this.
>>   - Refactor the machine initialization a bit to make it easier to drop in
>>     machine-specific flash initialization (patch 6).
>>   - Extend the series with additional patches to enable booting from flash:
>>       - Boot ROM (through the -bios option).
>>       - OTP (fuse) controller.
>>       - Memory Controller stub (just enough to skip memory training).
>>       - Flash controller.
>>       - Board-specific flash initialization.
>>
>> Thanks for reviewing,
>>
>> Havard
>>
>> Havard Skinnemoen (14):
>>   hw/misc: Add NPCM7xx System Global Control Registers device model
>>   hw/misc: Add NPCM7xx Clock Controller device model
>>   hw/timer: Add NPCM7xx Timer device model
>>   hw/arm: Add NPCM730 and NPCM750 SoC models
>>   hw/arm: Add two NPCM7xx-based machines
>>   roms: Add virtual Boot ROM for NPCM7xx SoCs
>>   hw/arm: Load -bios image as a boot ROM for npcm7xx
>>   hw/nvram: NPCM7xx OTP device model
>>   hw/mem: Stubbed out NPCM7xx Memory Controller model
>>   hw/ssi: NPCM7xx Flash Interface Unit device model
>>   hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
>>   hw/arm/npcm7xx: add board setup stub for CPU and UART clocks
>>   docs/system: Add Nuvoton machine documentation
>>   tests/acceptance: console boot tests for quanta-gsj
>>
>>  docs/system/arm/nuvoton.rst            |  90 ++++
>>  docs/system/target-arm.rst             |   1 +
>>  Makefile                               |   1 +
>>  default-configs/arm-softmmu.mak        |   1 +
>>  include/hw/arm/npcm7xx.h               | 112 +++++
>>  include/hw/mem/npcm7xx_mc.h            |  36 ++
>>  include/hw/misc/npcm7xx_clk.h          |  48 +++
>>  include/hw/misc/npcm7xx_gcr.h          |  43 ++
>>  include/hw/nvram/npcm7xx_otp.h         |  79 ++++
>>  include/hw/ssi/npcm7xx_fiu.h           |  73 ++++
>>  include/hw/timer/npcm7xx_timer.h       |  78 ++++
>>  hw/arm/npcm7xx.c                       | 532 +++++++++++++++++++++++
>>  hw/arm/npcm7xx_boards.c                | 197 +++++++++
>>  hw/mem/npcm7xx_mc.c                    |  84 ++++
>>  hw/misc/npcm7xx_clk.c                  | 266 ++++++++++++
>>  hw/misc/npcm7xx_gcr.c                  | 269 ++++++++++++
>>  hw/nvram/npcm7xx_otp.c                 | 439 +++++++++++++++++++
>>  hw/ssi/npcm7xx_fiu.c                   | 572 +++++++++++++++++++++++++
>>  hw/timer/npcm7xx_timer.c               | 509 ++++++++++++++++++++++
>>  .gitmodules                            |   3 +
>>  MAINTAINERS                            |  10 +
>>  hw/arm/Kconfig                         |   9 +
>>  hw/arm/meson.build                     |   1 +
>>  hw/mem/meson.build                     |   1 +
>>  hw/misc/meson.build                    |   4 +
>>  hw/misc/trace-events                   |   8 +
>>  hw/nvram/meson.build                   |   1 +
>>  hw/ssi/meson.build                     |   1 +
>>  hw/ssi/trace-events                    |  11 +
>>  hw/timer/meson.build                   |   1 +
>>  hw/timer/trace-events                  |   5 +
>>  pc-bios/README                         |   6 +
>>  pc-bios/npcm7xx_bootrom.bin            | Bin 0 -> 768 bytes
>>  roms/Makefile                          |   7 +
>>  roms/vbootrom                          |   1 +
>>  tests/acceptance/boot_linux_console.py |  83 ++++
>>  36 files changed, 3582 insertions(+)
>>  create mode 100644 docs/system/arm/nuvoton.rst
>>  create mode 100644 include/hw/arm/npcm7xx.h
>>  create mode 100644 include/hw/mem/npcm7xx_mc.h
>>  create mode 100644 include/hw/misc/npcm7xx_clk.h
>>  create mode 100644 include/hw/misc/npcm7xx_gcr.h
>>  create mode 100644 include/hw/nvram/npcm7xx_otp.h
>>  create mode 100644 include/hw/ssi/npcm7xx_fiu.h
>>  create mode 100644 include/hw/timer/npcm7xx_timer.h
>>  create mode 100644 hw/arm/npcm7xx.c
>>  create mode 100644 hw/arm/npcm7xx_boards.c
>>  create mode 100644 hw/mem/npcm7xx_mc.c
>>  create mode 100644 hw/misc/npcm7xx_clk.c
>>  create mode 100644 hw/misc/npcm7xx_gcr.c
>>  create mode 100644 hw/nvram/npcm7xx_otp.c
>>  create mode 100644 hw/ssi/npcm7xx_fiu.c
>>  create mode 100644 hw/timer/npcm7xx_timer.c
>>  create mode 100644 pc-bios/npcm7xx_bootrom.bin
>>  create mode 160000 roms/vbootrom
>>
>> -- 
>> 2.28.0.297.g1956fa8f8d-goog
>>
>>
> 


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

* Re: [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines
  2020-09-08 15:52   ` Philippe Mathieu-Daudé
@ 2020-09-08 16:58     ` Philippe Mathieu-Daudé
  2020-09-08 19:52       ` Havard Skinnemoen
  0 siblings, 1 reply; 35+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-08 16:58 UTC (permalink / raw)
  To: Alexander Bulekov, Havard Skinnemoen
  Cc: peter.maydell, qemu-arm, qemu-devel, kfting, Avi.Fishman

On 9/8/20 5:52 PM, Philippe Mathieu-Daudé wrote:
> On 9/8/20 5:02 PM, Alexander Bulekov wrote:
>> Hi Havard,
>> I fuzzed the npcm750-evb machine until I hit over 85% coverage over all
>> the new npcm.*\.c files. The only thing I found specific to the new
>> code, so far:
>>
>> cat << EOF | ./qemu-system-arm -machine npcm750-evb -m 128M -qtest stdio 
>> write 0xf0009040 0x4 0xc4c4c4c4
>> write 0xf0009040 0x4 0x4
>> EOF
> 
> This is an odd test because with -qtest the timer is not running,
> so this can not really happen on real hw.
> 
> The fix is:
> 
> -    g_assert(t->remaining_ns > 0);
> +    g_assert(qtest_enabled() || t->remaining_ns > 0);

Alex corrected me on IRC, qtest is irrelevant here.
The problem is he disables the timer twice.

So maybe something like:

 static void npcm7xx_timer_pause(NPCM7xxTimer *t)
 {
     int64_t now;

+    if (!timer_pending(&t->qtimer)) {
+        return;
+    }
     timer_del(&t->qtimer);
     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     t->remaining_ns = t->expires_ns - now;
     g_assert(t->remaining_ns > 0);
 }

> 
>>
>> ERROR:../hw/timer/npcm7xx_timer.c:160:npcm7xx_timer_pause: assertion failed: (t->remaining_ns > 0)
>> Bail out! ERROR:../hw/timer/npcm7xx_timer.c:160:npcm7xx_timer_pause: assertion failed: (t->remaining_ns > 0)
>> Aborted
>>
>> I'm doing the same for the quanta-gsj machine, but I'm not sure whether
>> it will cover more code, so I'm happy to leave a:
>>
>> Tested-by: Alexander Bulekov <alxndr@bu.edu>
>>
>> for the patches that add new virtual-device code (1-5, 7-12 ?)
>> -Alex
> 
> Very nice from you for testing running the fuzzer!
> 
> Regards,
> 
> Phil.
> 
>>
>>
>> On 200824 1716, Havard Skinnemoen via wrote:
>>> I also pushed this and the previous patchsets to my qemu fork on github.
>>> The branches are named npcm7xx-v[1-8].
>>>
>>>   https://github.com/hskinnemoen/qemu
>>>
>>> This patch series models enough of the Nuvoton NPCM730 and NPCM750 SoCs to boot
>>> an OpenBMC image built for quanta-gsj. This includes device models for:
>>>
>>>   - Global Configuration Registers
>>>   - Clock Control
>>>   - Timers
>>>   - Fuses
>>>   - Memory Controller
>>>   - Flash Controller
>>>
>>> These modules, along with the existing Cortex A9 CPU cores and built-in
>>> peripherals, are integrated into a NPCM730 or NPCM750 SoC, which in turn form
>>> the foundation for the quanta-gsj and npcm750-evb machines, respectively. The
>>> two SoCs are very similar; the only difference is that NPCM730 is missing some
>>> peripherals that NPCM750 has, and which are not considered essential for
>>> datacenter use (e.g. graphics controllers). For more information, see
>>>
>>> https://www.nuvoton.com/products/cloud-computing/ibmc/
>>>
>>> Both quanta-gsj and npcm750-evb correspond to real boards supported by OpenBMC.
>>> At the end of the series, qemu can boot an OpenBMC image built for one of these
>>> boards with some minor modifications.
>>>
>>> The patches in this series were developed by Google and reviewed by Nuvoton. We
>>> will be maintaining the machine and peripheral support together.
>>>
>>> The data sheet for these SoCs is not generally available. Please let me know if
>>> more comments are needed to understand the device behavior.
>>>
>>> Changes since v7:
>>>
>>>   - Move register enums to .c files throughout, leaving a single
>>>     NPCM7XX_FOO_NR_REGS definition behind in the .h file. A QEMU_BUILD_BUG_ON
>>>     should alert anyone accidentally expanding the register enum that they need
>>>     to update the corresponding NR_REGS define, which in turn has a comment
>>>     reminding them to update the vmstate version_id as well.
>>>   - Skip loading the bootrom if a kernel filename is provided by the user.
>>>   - New patch adding a board setup stub to tweak clocks before booting directly
>>>     into the kernel.
>>>   - Add stuff to meson files instead of Makefiles.
>>>   - Try to disable the slowest drivers and services to speed up the flash boot
>>>     acceptance test a bit. This is somewhat based on the following
>>>     systemd-analyze blame report:
>>>     https://gist.github.com/hskinnemoen/475cb0676530cd2cebaa1754cf16ca97
>>>
>>> Changes since v6:
>>>
>>>   - Use size_to_str to report DRAM sizes in npcm7xx_gcr.
>>>   - Simplify the interrupt logic in npcm7xx_timer.
>>>   - Update global bios_name instead of temporary.
>>>   - Add npcm7xx_bootrom to MAINTAINERS and pc-bios/README.
>>>   - Use a predefined name for the gsj boot image in the acceptance test.
>>>
>>> Changes since v5:
>>>
>>>   - Boot ROM included, as a git submodule and a binary blob, and loaded by
>>>     default, so the -bios option is usually not necessary anymore.
>>>   - Two acceptance tests added (openbmc image boot, and direct kernel boot).
>>>   - npcm7xx_load_kernel() moved to SoC code.
>>>   - NPCM7XX_TIMER_REF_HZ definition moved to CLK header.
>>>   - Comments added clarifying available SPI flash chip selects.
>>>   - Error handling adjustments:
>>>       - Errors from CPU and GCR realization are propagated through the SoC
>>>         since they may be triggered by user-configurable parameters.
>>>       - Machine init uses error_fatal instead of error_abort for SoC
>>>         realization flash init. This makes error messages more helpful.
>>>       - Comments added to indicate whether peripherals may fail to realize.
>>>       - Use ERRP_GUARD() instead of Error *err when possible.
>>>   - Default CPU type is now set, and attempting to set it to anything else
>>>     will fail.
>>>   - Format string fixes (use HWADDR_PRIx, etc.)
>>>   - Simplified memory size encoding and error checking in npcm7xx_gcr.
>>>   - Encapsulate non-obvious pointer subtraction into helper functions in the
>>>     FIU and TIMER modules.
>>>   - Incorporate review feedback into the FIU module:
>>>       - Add select/deselect trace events.
>>>       - Use npcm7xx_fiu_{de,}select() consistently.
>>>       - Use extract/deposit in more places for consistency.
>>>       - Use -Wimplicit-fallthrough compatible fallthrough comments.
>>>       - Use qdev_init_gpio_out_named instead of sysbus_init_irq for chip
>>>         selects.
>>>   - Incorporate review feedback into the TIMER module:
>>>       - Assert that we never pause a timer that has already expired, instead of
>>>         trying to handle it. This should be safe since QEMU_CLOCK_VIRTUAL is
>>>         stopped while this code is running.
>>>       - Simplify the switch blocks in the read and write handlers.
>>>
>>> I made a change to error out if a flash drive was not specified, but reverted
>>> it because it caused make check to fail (qom-test). When specifying a NULL
>>> block device, the m25p flash device initializes its in-memory storage with 0xff
>>> and doesn't attempt to write anything back. This seems correct to me.
>>>
>>> Changes since v4:
>>>
>>>   - OTP cleanups suggested by Philippe Mathieu-Daudé.
>>>       - Added fuse array definitions based on public Nuvoton bootblock code.
>>>       - Moved class structure to .c file since it's only used internally.
>>>       - Readability improvements.
>>>   - Split the first patch and folded parts of it into three other patches so
>>>     that CONFIG_NPCM7XX is only enabled after the initial NPCM7xx machine
>>>     support is added.
>>>   - DRAM init moved to machine init code.
>>>   - Consistently use lower-case hex literals.
>>>   - Switched to fine-grained unimplemented devices, based on public bootblock
>>>     source code. Added a tiny SRAM that got left out previously.
>>>   - Simplified error handling in npcm7xx_realize() since the board code will
>>>     abort anyway, and SoCs are not hot-pluggable.
>>>
>>> Changes since v3:
>>>
>>>   - License headers are now GPL v2-or-later throughout.
>>>   - Added vmstate throughout (except the memory controller, which doesn't
>>>     really have any state worth saving). Successfully booted a gsj image
>>>     with two stop/savevm/quit/loadvm cycles along the way.
>>>       - JFFS2 really doesn't like it if I let qemu keep running after savevm,
>>>         and then jump back in time with loadvm. I assume this is expected.
>>>   - Fixed an error API violation in npcm7xx_realize, removed pointless error
>>>     check after object_property_set_link().
>>>   - Switched the OTP device to use an embedded array instead of a g_malloc0'd
>>>     one because I couldn't figure out how to set up vmstate for the latter.
>>>
>>> Changes since v2:
>>>
>>>   - Simplified the MAINTAINERS entry.
>>>   - Added link to OpenPOWER jenkins for gsj BMC images.
>>>   - Reverted the smpboot change, back to byte swapping.
>>>   - Adapted to upstream API changes:
>>>       - sysbus_init_child_obj -> object_initialize_child
>>>       - object_property_set_bool -> qdev_realize / sysbus_realize
>>>       - ssi_create_slave_no_init -> qdev_new
>>>       - qdev_init_nofail -> qdev_realize_and_unref
>>>       - ssi_auto_connect_slaves removed
>>>   - Moved Boot ROM loading from soc to machine init.
>>>   - Plumbed power-on-straps property from GCR to the machine init code so it
>>>     can be properly initialized. Turns out npcm750 memory init doesn't work
>>>     without this. npcm730 is fine either way, though I'm not sure why.
>>>   - Reworked the flash init code so it looks more like aspeed (i.e. the flash
>>>     device gets added even if there's no drive).
>>>
>>> Changes since v1 (requested by reviewers):
>>>
>>>   - Clarify the source of CLK reset values.
>>>   - Made smpboot a constant byte array, eliinated byte swapping.
>>>   - NPCM7xxState now stores an array of ARMCPUs, not pointers to ARMCPUs.
>>>   - Clarify why EL3 is disabled.
>>>   - Introduce NPCM7XX_NUM_IRQ constant.
>>>   - Set the number of CPUs according to SoC variant, and disallow command line
>>>     overrides (i.e. you can no longer override the number of CPUs with the -smp
>>>     parameter). This is trying to follow the spirit of
>>>     https://patchwork.kernel.org/patch/11595407/.
>>>   - Switch register operations to DEVICE_LITTLE_ENDIAN throughout.
>>>   - Machine documentation added (new patch).
>>>
>>> Changes since v1 to support flash booting:
>>>
>>>   - GCR reset value changes to get past memory initialization when booting
>>>     from flash (patches 2 and 5):
>>>       - INTCR2 now indicates that the DDR controller is initialized.
>>>       - INTCR3 is initialized according to DDR memory size. A realize()
>>> 	method was implemented to achieve this.
>>>   - Refactor the machine initialization a bit to make it easier to drop in
>>>     machine-specific flash initialization (patch 6).
>>>   - Extend the series with additional patches to enable booting from flash:
>>>       - Boot ROM (through the -bios option).
>>>       - OTP (fuse) controller.
>>>       - Memory Controller stub (just enough to skip memory training).
>>>       - Flash controller.
>>>       - Board-specific flash initialization.
>>>
>>> Thanks for reviewing,
>>>
>>> Havard
>>>
>>> Havard Skinnemoen (14):
>>>   hw/misc: Add NPCM7xx System Global Control Registers device model
>>>   hw/misc: Add NPCM7xx Clock Controller device model
>>>   hw/timer: Add NPCM7xx Timer device model
>>>   hw/arm: Add NPCM730 and NPCM750 SoC models
>>>   hw/arm: Add two NPCM7xx-based machines
>>>   roms: Add virtual Boot ROM for NPCM7xx SoCs
>>>   hw/arm: Load -bios image as a boot ROM for npcm7xx
>>>   hw/nvram: NPCM7xx OTP device model
>>>   hw/mem: Stubbed out NPCM7xx Memory Controller model
>>>   hw/ssi: NPCM7xx Flash Interface Unit device model
>>>   hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
>>>   hw/arm/npcm7xx: add board setup stub for CPU and UART clocks
>>>   docs/system: Add Nuvoton machine documentation
>>>   tests/acceptance: console boot tests for quanta-gsj
>>>
>>>  docs/system/arm/nuvoton.rst            |  90 ++++
>>>  docs/system/target-arm.rst             |   1 +
>>>  Makefile                               |   1 +
>>>  default-configs/arm-softmmu.mak        |   1 +
>>>  include/hw/arm/npcm7xx.h               | 112 +++++
>>>  include/hw/mem/npcm7xx_mc.h            |  36 ++
>>>  include/hw/misc/npcm7xx_clk.h          |  48 +++
>>>  include/hw/misc/npcm7xx_gcr.h          |  43 ++
>>>  include/hw/nvram/npcm7xx_otp.h         |  79 ++++
>>>  include/hw/ssi/npcm7xx_fiu.h           |  73 ++++
>>>  include/hw/timer/npcm7xx_timer.h       |  78 ++++
>>>  hw/arm/npcm7xx.c                       | 532 +++++++++++++++++++++++
>>>  hw/arm/npcm7xx_boards.c                | 197 +++++++++
>>>  hw/mem/npcm7xx_mc.c                    |  84 ++++
>>>  hw/misc/npcm7xx_clk.c                  | 266 ++++++++++++
>>>  hw/misc/npcm7xx_gcr.c                  | 269 ++++++++++++
>>>  hw/nvram/npcm7xx_otp.c                 | 439 +++++++++++++++++++
>>>  hw/ssi/npcm7xx_fiu.c                   | 572 +++++++++++++++++++++++++
>>>  hw/timer/npcm7xx_timer.c               | 509 ++++++++++++++++++++++
>>>  .gitmodules                            |   3 +
>>>  MAINTAINERS                            |  10 +
>>>  hw/arm/Kconfig                         |   9 +
>>>  hw/arm/meson.build                     |   1 +
>>>  hw/mem/meson.build                     |   1 +
>>>  hw/misc/meson.build                    |   4 +
>>>  hw/misc/trace-events                   |   8 +
>>>  hw/nvram/meson.build                   |   1 +
>>>  hw/ssi/meson.build                     |   1 +
>>>  hw/ssi/trace-events                    |  11 +
>>>  hw/timer/meson.build                   |   1 +
>>>  hw/timer/trace-events                  |   5 +
>>>  pc-bios/README                         |   6 +
>>>  pc-bios/npcm7xx_bootrom.bin            | Bin 0 -> 768 bytes
>>>  roms/Makefile                          |   7 +
>>>  roms/vbootrom                          |   1 +
>>>  tests/acceptance/boot_linux_console.py |  83 ++++
>>>  36 files changed, 3582 insertions(+)
>>>  create mode 100644 docs/system/arm/nuvoton.rst
>>>  create mode 100644 include/hw/arm/npcm7xx.h
>>>  create mode 100644 include/hw/mem/npcm7xx_mc.h
>>>  create mode 100644 include/hw/misc/npcm7xx_clk.h
>>>  create mode 100644 include/hw/misc/npcm7xx_gcr.h
>>>  create mode 100644 include/hw/nvram/npcm7xx_otp.h
>>>  create mode 100644 include/hw/ssi/npcm7xx_fiu.h
>>>  create mode 100644 include/hw/timer/npcm7xx_timer.h
>>>  create mode 100644 hw/arm/npcm7xx.c
>>>  create mode 100644 hw/arm/npcm7xx_boards.c
>>>  create mode 100644 hw/mem/npcm7xx_mc.c
>>>  create mode 100644 hw/misc/npcm7xx_clk.c
>>>  create mode 100644 hw/misc/npcm7xx_gcr.c
>>>  create mode 100644 hw/nvram/npcm7xx_otp.c
>>>  create mode 100644 hw/ssi/npcm7xx_fiu.c
>>>  create mode 100644 hw/timer/npcm7xx_timer.c
>>>  create mode 100644 pc-bios/npcm7xx_bootrom.bin
>>>  create mode 160000 roms/vbootrom
>>>
>>> -- 
>>> 2.28.0.297.g1956fa8f8d-goog
>>>
>>>
>>
> 


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

* Re: [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines
  2020-09-08 16:58     ` Philippe Mathieu-Daudé
@ 2020-09-08 19:52       ` Havard Skinnemoen
  2020-09-09  1:32         ` Havard Skinnemoen
  0 siblings, 1 reply; 35+ messages in thread
From: Havard Skinnemoen @ 2020-09-08 19:52 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Alexander Bulekov, Peter Maydell, qemu-arm, QEMU Developers,
	IS20 Avi Fishman, CS20 KFTing

On Tue, Sep 8, 2020 at 9:58 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 9/8/20 5:52 PM, Philippe Mathieu-Daudé wrote:
> > On 9/8/20 5:02 PM, Alexander Bulekov wrote:
> >> Hi Havard,
> >> I fuzzed the npcm750-evb machine until I hit over 85% coverage over all
> >> the new npcm.*\.c files. The only thing I found specific to the new
> >> code, so far:
> >>
> >> cat << EOF | ./qemu-system-arm -machine npcm750-evb -m 128M -qtest stdio
> >> write 0xf0009040 0x4 0xc4c4c4c4
> >> write 0xf0009040 0x4 0x4
> >> EOF
> >
> > This is an odd test because with -qtest the timer is not running,
> > so this can not really happen on real hw.
> >
> > The fix is:
> >
> > -    g_assert(t->remaining_ns > 0);
> > +    g_assert(qtest_enabled() || t->remaining_ns > 0);
>
> Alex corrected me on IRC, qtest is irrelevant here.
> The problem is he disables the timer twice.
>
> So maybe something like:
>
>  static void npcm7xx_timer_pause(NPCM7xxTimer *t)
>  {
>      int64_t now;
>
> +    if (!timer_pending(&t->qtimer)) {
> +        return;
> +    }
>      timer_del(&t->qtimer);
>      now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>      t->remaining_ns = t->expires_ns - now;
>      g_assert(t->remaining_ns > 0);
>  }

Thanks, that makes sense. I was worried that making the assert
conditional on qtest_enabled() might hide real issues.

This fuzz testing is great, it would have been hard to find this bug
without it. Thanks a lot Alex for running it.

Havard

> >
> >>
> >> ERROR:../hw/timer/npcm7xx_timer.c:160:npcm7xx_timer_pause: assertion failed: (t->remaining_ns > 0)
> >> Bail out! ERROR:../hw/timer/npcm7xx_timer.c:160:npcm7xx_timer_pause: assertion failed: (t->remaining_ns > 0)
> >> Aborted
> >>
> >> I'm doing the same for the quanta-gsj machine, but I'm not sure whether
> >> it will cover more code, so I'm happy to leave a:
> >>
> >> Tested-by: Alexander Bulekov <alxndr@bu.edu>
> >>
> >> for the patches that add new virtual-device code (1-5, 7-12 ?)
> >> -Alex
> >
> > Very nice from you for testing running the fuzzer!
> >
> > Regards,
> >
> > Phil.
> >
> >>
> >>
> >> On 200824 1716, Havard Skinnemoen via wrote:
> >>> I also pushed this and the previous patchsets to my qemu fork on github.
> >>> The branches are named npcm7xx-v[1-8].
> >>>
> >>>   https://github.com/hskinnemoen/qemu
> >>>
> >>> This patch series models enough of the Nuvoton NPCM730 and NPCM750 SoCs to boot
> >>> an OpenBMC image built for quanta-gsj. This includes device models for:
> >>>
> >>>   - Global Configuration Registers
> >>>   - Clock Control
> >>>   - Timers
> >>>   - Fuses
> >>>   - Memory Controller
> >>>   - Flash Controller
> >>>
> >>> These modules, along with the existing Cortex A9 CPU cores and built-in
> >>> peripherals, are integrated into a NPCM730 or NPCM750 SoC, which in turn form
> >>> the foundation for the quanta-gsj and npcm750-evb machines, respectively. The
> >>> two SoCs are very similar; the only difference is that NPCM730 is missing some
> >>> peripherals that NPCM750 has, and which are not considered essential for
> >>> datacenter use (e.g. graphics controllers). For more information, see
> >>>
> >>> https://www.nuvoton.com/products/cloud-computing/ibmc/
> >>>
> >>> Both quanta-gsj and npcm750-evb correspond to real boards supported by OpenBMC.
> >>> At the end of the series, qemu can boot an OpenBMC image built for one of these
> >>> boards with some minor modifications.
> >>>
> >>> The patches in this series were developed by Google and reviewed by Nuvoton. We
> >>> will be maintaining the machine and peripheral support together.
> >>>
> >>> The data sheet for these SoCs is not generally available. Please let me know if
> >>> more comments are needed to understand the device behavior.
> >>>
> >>> Changes since v7:
> >>>
> >>>   - Move register enums to .c files throughout, leaving a single
> >>>     NPCM7XX_FOO_NR_REGS definition behind in the .h file. A QEMU_BUILD_BUG_ON
> >>>     should alert anyone accidentally expanding the register enum that they need
> >>>     to update the corresponding NR_REGS define, which in turn has a comment
> >>>     reminding them to update the vmstate version_id as well.
> >>>   - Skip loading the bootrom if a kernel filename is provided by the user.
> >>>   - New patch adding a board setup stub to tweak clocks before booting directly
> >>>     into the kernel.
> >>>   - Add stuff to meson files instead of Makefiles.
> >>>   - Try to disable the slowest drivers and services to speed up the flash boot
> >>>     acceptance test a bit. This is somewhat based on the following
> >>>     systemd-analyze blame report:
> >>>     https://gist.github.com/hskinnemoen/475cb0676530cd2cebaa1754cf16ca97
> >>>
> >>> Changes since v6:
> >>>
> >>>   - Use size_to_str to report DRAM sizes in npcm7xx_gcr.
> >>>   - Simplify the interrupt logic in npcm7xx_timer.
> >>>   - Update global bios_name instead of temporary.
> >>>   - Add npcm7xx_bootrom to MAINTAINERS and pc-bios/README.
> >>>   - Use a predefined name for the gsj boot image in the acceptance test.
> >>>
> >>> Changes since v5:
> >>>
> >>>   - Boot ROM included, as a git submodule and a binary blob, and loaded by
> >>>     default, so the -bios option is usually not necessary anymore.
> >>>   - Two acceptance tests added (openbmc image boot, and direct kernel boot).
> >>>   - npcm7xx_load_kernel() moved to SoC code.
> >>>   - NPCM7XX_TIMER_REF_HZ definition moved to CLK header.
> >>>   - Comments added clarifying available SPI flash chip selects.
> >>>   - Error handling adjustments:
> >>>       - Errors from CPU and GCR realization are propagated through the SoC
> >>>         since they may be triggered by user-configurable parameters.
> >>>       - Machine init uses error_fatal instead of error_abort for SoC
> >>>         realization flash init. This makes error messages more helpful.
> >>>       - Comments added to indicate whether peripherals may fail to realize.
> >>>       - Use ERRP_GUARD() instead of Error *err when possible.
> >>>   - Default CPU type is now set, and attempting to set it to anything else
> >>>     will fail.
> >>>   - Format string fixes (use HWADDR_PRIx, etc.)
> >>>   - Simplified memory size encoding and error checking in npcm7xx_gcr.
> >>>   - Encapsulate non-obvious pointer subtraction into helper functions in the
> >>>     FIU and TIMER modules.
> >>>   - Incorporate review feedback into the FIU module:
> >>>       - Add select/deselect trace events.
> >>>       - Use npcm7xx_fiu_{de,}select() consistently.
> >>>       - Use extract/deposit in more places for consistency.
> >>>       - Use -Wimplicit-fallthrough compatible fallthrough comments.
> >>>       - Use qdev_init_gpio_out_named instead of sysbus_init_irq for chip
> >>>         selects.
> >>>   - Incorporate review feedback into the TIMER module:
> >>>       - Assert that we never pause a timer that has already expired, instead of
> >>>         trying to handle it. This should be safe since QEMU_CLOCK_VIRTUAL is
> >>>         stopped while this code is running.
> >>>       - Simplify the switch blocks in the read and write handlers.
> >>>
> >>> I made a change to error out if a flash drive was not specified, but reverted
> >>> it because it caused make check to fail (qom-test). When specifying a NULL
> >>> block device, the m25p flash device initializes its in-memory storage with 0xff
> >>> and doesn't attempt to write anything back. This seems correct to me.
> >>>
> >>> Changes since v4:
> >>>
> >>>   - OTP cleanups suggested by Philippe Mathieu-Daudé.
> >>>       - Added fuse array definitions based on public Nuvoton bootblock code.
> >>>       - Moved class structure to .c file since it's only used internally.
> >>>       - Readability improvements.
> >>>   - Split the first patch and folded parts of it into three other patches so
> >>>     that CONFIG_NPCM7XX is only enabled after the initial NPCM7xx machine
> >>>     support is added.
> >>>   - DRAM init moved to machine init code.
> >>>   - Consistently use lower-case hex literals.
> >>>   - Switched to fine-grained unimplemented devices, based on public bootblock
> >>>     source code. Added a tiny SRAM that got left out previously.
> >>>   - Simplified error handling in npcm7xx_realize() since the board code will
> >>>     abort anyway, and SoCs are not hot-pluggable.
> >>>
> >>> Changes since v3:
> >>>
> >>>   - License headers are now GPL v2-or-later throughout.
> >>>   - Added vmstate throughout (except the memory controller, which doesn't
> >>>     really have any state worth saving). Successfully booted a gsj image
> >>>     with two stop/savevm/quit/loadvm cycles along the way.
> >>>       - JFFS2 really doesn't like it if I let qemu keep running after savevm,
> >>>         and then jump back in time with loadvm. I assume this is expected.
> >>>   - Fixed an error API violation in npcm7xx_realize, removed pointless error
> >>>     check after object_property_set_link().
> >>>   - Switched the OTP device to use an embedded array instead of a g_malloc0'd
> >>>     one because I couldn't figure out how to set up vmstate for the latter.
> >>>
> >>> Changes since v2:
> >>>
> >>>   - Simplified the MAINTAINERS entry.
> >>>   - Added link to OpenPOWER jenkins for gsj BMC images.
> >>>   - Reverted the smpboot change, back to byte swapping.
> >>>   - Adapted to upstream API changes:
> >>>       - sysbus_init_child_obj -> object_initialize_child
> >>>       - object_property_set_bool -> qdev_realize / sysbus_realize
> >>>       - ssi_create_slave_no_init -> qdev_new
> >>>       - qdev_init_nofail -> qdev_realize_and_unref
> >>>       - ssi_auto_connect_slaves removed
> >>>   - Moved Boot ROM loading from soc to machine init.
> >>>   - Plumbed power-on-straps property from GCR to the machine init code so it
> >>>     can be properly initialized. Turns out npcm750 memory init doesn't work
> >>>     without this. npcm730 is fine either way, though I'm not sure why.
> >>>   - Reworked the flash init code so it looks more like aspeed (i.e. the flash
> >>>     device gets added even if there's no drive).
> >>>
> >>> Changes since v1 (requested by reviewers):
> >>>
> >>>   - Clarify the source of CLK reset values.
> >>>   - Made smpboot a constant byte array, eliinated byte swapping.
> >>>   - NPCM7xxState now stores an array of ARMCPUs, not pointers to ARMCPUs.
> >>>   - Clarify why EL3 is disabled.
> >>>   - Introduce NPCM7XX_NUM_IRQ constant.
> >>>   - Set the number of CPUs according to SoC variant, and disallow command line
> >>>     overrides (i.e. you can no longer override the number of CPUs with the -smp
> >>>     parameter). This is trying to follow the spirit of
> >>>     https://patchwork.kernel.org/patch/11595407/.
> >>>   - Switch register operations to DEVICE_LITTLE_ENDIAN throughout.
> >>>   - Machine documentation added (new patch).
> >>>
> >>> Changes since v1 to support flash booting:
> >>>
> >>>   - GCR reset value changes to get past memory initialization when booting
> >>>     from flash (patches 2 and 5):
> >>>       - INTCR2 now indicates that the DDR controller is initialized.
> >>>       - INTCR3 is initialized according to DDR memory size. A realize()
> >>>     method was implemented to achieve this.
> >>>   - Refactor the machine initialization a bit to make it easier to drop in
> >>>     machine-specific flash initialization (patch 6).
> >>>   - Extend the series with additional patches to enable booting from flash:
> >>>       - Boot ROM (through the -bios option).
> >>>       - OTP (fuse) controller.
> >>>       - Memory Controller stub (just enough to skip memory training).
> >>>       - Flash controller.
> >>>       - Board-specific flash initialization.
> >>>
> >>> Thanks for reviewing,
> >>>
> >>> Havard
> >>>
> >>> Havard Skinnemoen (14):
> >>>   hw/misc: Add NPCM7xx System Global Control Registers device model
> >>>   hw/misc: Add NPCM7xx Clock Controller device model
> >>>   hw/timer: Add NPCM7xx Timer device model
> >>>   hw/arm: Add NPCM730 and NPCM750 SoC models
> >>>   hw/arm: Add two NPCM7xx-based machines
> >>>   roms: Add virtual Boot ROM for NPCM7xx SoCs
> >>>   hw/arm: Load -bios image as a boot ROM for npcm7xx
> >>>   hw/nvram: NPCM7xx OTP device model
> >>>   hw/mem: Stubbed out NPCM7xx Memory Controller model
> >>>   hw/ssi: NPCM7xx Flash Interface Unit device model
> >>>   hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
> >>>   hw/arm/npcm7xx: add board setup stub for CPU and UART clocks
> >>>   docs/system: Add Nuvoton machine documentation
> >>>   tests/acceptance: console boot tests for quanta-gsj
> >>>
> >>>  docs/system/arm/nuvoton.rst            |  90 ++++
> >>>  docs/system/target-arm.rst             |   1 +
> >>>  Makefile                               |   1 +
> >>>  default-configs/arm-softmmu.mak        |   1 +
> >>>  include/hw/arm/npcm7xx.h               | 112 +++++
> >>>  include/hw/mem/npcm7xx_mc.h            |  36 ++
> >>>  include/hw/misc/npcm7xx_clk.h          |  48 +++
> >>>  include/hw/misc/npcm7xx_gcr.h          |  43 ++
> >>>  include/hw/nvram/npcm7xx_otp.h         |  79 ++++
> >>>  include/hw/ssi/npcm7xx_fiu.h           |  73 ++++
> >>>  include/hw/timer/npcm7xx_timer.h       |  78 ++++
> >>>  hw/arm/npcm7xx.c                       | 532 +++++++++++++++++++++++
> >>>  hw/arm/npcm7xx_boards.c                | 197 +++++++++
> >>>  hw/mem/npcm7xx_mc.c                    |  84 ++++
> >>>  hw/misc/npcm7xx_clk.c                  | 266 ++++++++++++
> >>>  hw/misc/npcm7xx_gcr.c                  | 269 ++++++++++++
> >>>  hw/nvram/npcm7xx_otp.c                 | 439 +++++++++++++++++++
> >>>  hw/ssi/npcm7xx_fiu.c                   | 572 +++++++++++++++++++++++++
> >>>  hw/timer/npcm7xx_timer.c               | 509 ++++++++++++++++++++++
> >>>  .gitmodules                            |   3 +
> >>>  MAINTAINERS                            |  10 +
> >>>  hw/arm/Kconfig                         |   9 +
> >>>  hw/arm/meson.build                     |   1 +
> >>>  hw/mem/meson.build                     |   1 +
> >>>  hw/misc/meson.build                    |   4 +
> >>>  hw/misc/trace-events                   |   8 +
> >>>  hw/nvram/meson.build                   |   1 +
> >>>  hw/ssi/meson.build                     |   1 +
> >>>  hw/ssi/trace-events                    |  11 +
> >>>  hw/timer/meson.build                   |   1 +
> >>>  hw/timer/trace-events                  |   5 +
> >>>  pc-bios/README                         |   6 +
> >>>  pc-bios/npcm7xx_bootrom.bin            | Bin 0 -> 768 bytes
> >>>  roms/Makefile                          |   7 +
> >>>  roms/vbootrom                          |   1 +
> >>>  tests/acceptance/boot_linux_console.py |  83 ++++
> >>>  36 files changed, 3582 insertions(+)
> >>>  create mode 100644 docs/system/arm/nuvoton.rst
> >>>  create mode 100644 include/hw/arm/npcm7xx.h
> >>>  create mode 100644 include/hw/mem/npcm7xx_mc.h
> >>>  create mode 100644 include/hw/misc/npcm7xx_clk.h
> >>>  create mode 100644 include/hw/misc/npcm7xx_gcr.h
> >>>  create mode 100644 include/hw/nvram/npcm7xx_otp.h
> >>>  create mode 100644 include/hw/ssi/npcm7xx_fiu.h
> >>>  create mode 100644 include/hw/timer/npcm7xx_timer.h
> >>>  create mode 100644 hw/arm/npcm7xx.c
> >>>  create mode 100644 hw/arm/npcm7xx_boards.c
> >>>  create mode 100644 hw/mem/npcm7xx_mc.c
> >>>  create mode 100644 hw/misc/npcm7xx_clk.c
> >>>  create mode 100644 hw/misc/npcm7xx_gcr.c
> >>>  create mode 100644 hw/nvram/npcm7xx_otp.c
> >>>  create mode 100644 hw/ssi/npcm7xx_fiu.c
> >>>  create mode 100644 hw/timer/npcm7xx_timer.c
> >>>  create mode 100644 pc-bios/npcm7xx_bootrom.bin
> >>>  create mode 160000 roms/vbootrom
> >>>
> >>> --
> >>> 2.28.0.297.g1956fa8f8d-goog
> >>>
> >>>
> >>
> >


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

* Re: [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines
  2020-09-08 19:52       ` Havard Skinnemoen
@ 2020-09-09  1:32         ` Havard Skinnemoen
  2020-09-11  0:03           ` Havard Skinnemoen
  0 siblings, 1 reply; 35+ messages in thread
From: Havard Skinnemoen @ 2020-09-09  1:32 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Alexander Bulekov, Peter Maydell, qemu-arm, QEMU Developers,
	IS20 Avi Fishman, CS20 KFTing

On Tue, Sep 8, 2020 at 12:52 PM Havard Skinnemoen
<hskinnemoen@google.com> wrote:
>
> On Tue, Sep 8, 2020 at 9:58 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >
> > On 9/8/20 5:52 PM, Philippe Mathieu-Daudé wrote:
> > > On 9/8/20 5:02 PM, Alexander Bulekov wrote:
> > >> Hi Havard,
> > >> I fuzzed the npcm750-evb machine until I hit over 85% coverage over all
> > >> the new npcm.*\.c files. The only thing I found specific to the new
> > >> code, so far:
> > >>
> > >> cat << EOF | ./qemu-system-arm -machine npcm750-evb -m 128M -qtest stdio
> > >> write 0xf0009040 0x4 0xc4c4c4c4
> > >> write 0xf0009040 0x4 0x4
> > >> EOF
> > >
> > > This is an odd test because with -qtest the timer is not running,
> > > so this can not really happen on real hw.
> > >
> > > The fix is:
> > >
> > > -    g_assert(t->remaining_ns > 0);
> > > +    g_assert(qtest_enabled() || t->remaining_ns > 0);
> >
> > Alex corrected me on IRC, qtest is irrelevant here.
> > The problem is he disables the timer twice.
> >
> > So maybe something like:
> >
> >  static void npcm7xx_timer_pause(NPCM7xxTimer *t)
> >  {
> >      int64_t now;
> >
> > +    if (!timer_pending(&t->qtimer)) {
> > +        return;
> > +    }
> >      timer_del(&t->qtimer);
> >      now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> >      t->remaining_ns = t->expires_ns - now;
> >      g_assert(t->remaining_ns > 0);
> >  }
>
> Thanks, that makes sense. I was worried that making the assert
> conditional on qtest_enabled() might hide real issues.

Hmm, that didn't help, though it might make sense to keep it there anyway.

What the test case does is:

  1. Enable the timer (with zero expiration time) and reset it at the same time.
  2. Disable the timer zero cycles after it was enabled.

It also touches a bunch of other bits (including reserved bits), but
they should be irrelevant.

I think there are two issues here.

When the Reset bit is set, the Enable bit should be forced to zero.
This is easy to fix.

If the timer is enabled with zero expiration time, and immediately
disabled without advancing the virtual time, npcm7xx_timer_pause() is
called while the timer is active, but t->expires_ns ==
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL). So t->remaining_ns becomes zero
and triggers the assertion.

If I revert a change that Philippe asked me to do earlier:

    timer_del(&t->qtimer);
     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     t->remaining_ns = t->expires_ns - now;
-    g_assert(t->remaining_ns > 0);
+    if (t->remaining_ns <= 0) {
+        npcm7xx_timer_reached_zero(t);
+    }
 }

it doesn't crash:

$ cat << EOF | ./qemu-system-arm -machine npcm750-evb -m 128M -qtest
stdio --trace npcm7xx_timer*
write 0xf0009040 0x4 0xc4c4c4c4
write 0xf0009040 0x4 0x4
EOF
[I 1599613445.620379] OPENED
[R +0.180771] write 0xf0009040 0x4 0xc4c4c4c4
1361079@1599613445.801182:npcm7xx_timer_write /machine/soc/tim[1]
offset: 0x0040 value 0xc4c4c4c4
OK
[S +0.180816] OK
[R +0.180833] write 0xf0009040 0x4 0x4
1361079@1599613445.801220:npcm7xx_timer_write /machine/soc/tim[1]
offset: 0x0040 value 0x00000000
1361079@1599613445.801295:npcm7xx_timer_irq /machine/soc/tim[1] timer 4 state 0
OK
[S +0.180927] OK
[I +0.181319] CLOSED
[I +4.003267] CLOSED

Note that the npcm7xx_timer_irq trace event is a sign of the first
bug, but fixing that might mask the second bug. If we write the same
pattern, only without the Reset bit, this would be the correct
behavior (and it still causes the v8 code to crash).

I think this device deserves a qtest. I wonder if we'd trigger the
assertion if we set a nonzero expiration time, but happen to clear the
Enable bit on the exact cycle it's supposed to expire. That would be a
more realistic scenario, as it wouldn't require multiple register
writes in the same virtual clock cycle.

I probably won't add the qtest to the same series, as I'd like someone
from Nuvoton to get a chance to review it first.

Havard

>
> This fuzz testing is great, it would have been hard to find this bug
> without it. Thanks a lot Alex for running it.
>
> Havard
>
> > >
> > >>
> > >> ERROR:../hw/timer/npcm7xx_timer.c:160:npcm7xx_timer_pause: assertion failed: (t->remaining_ns > 0)
> > >> Bail out! ERROR:../hw/timer/npcm7xx_timer.c:160:npcm7xx_timer_pause: assertion failed: (t->remaining_ns > 0)
> > >> Aborted
> > >>
> > >> I'm doing the same for the quanta-gsj machine, but I'm not sure whether
> > >> it will cover more code, so I'm happy to leave a:
> > >>
> > >> Tested-by: Alexander Bulekov <alxndr@bu.edu>
> > >>
> > >> for the patches that add new virtual-device code (1-5, 7-12 ?)
> > >> -Alex
> > >
> > > Very nice from you for testing running the fuzzer!
> > >
> > > Regards,
> > >
> > > Phil.
> > >
> > >>
> > >>
> > >> On 200824 1716, Havard Skinnemoen via wrote:
> > >>> I also pushed this and the previous patchsets to my qemu fork on github.
> > >>> The branches are named npcm7xx-v[1-8].
> > >>>
> > >>>   https://github.com/hskinnemoen/qemu
> > >>>
> > >>> This patch series models enough of the Nuvoton NPCM730 and NPCM750 SoCs to boot
> > >>> an OpenBMC image built for quanta-gsj. This includes device models for:
> > >>>
> > >>>   - Global Configuration Registers
> > >>>   - Clock Control
> > >>>   - Timers
> > >>>   - Fuses
> > >>>   - Memory Controller
> > >>>   - Flash Controller
> > >>>
> > >>> These modules, along with the existing Cortex A9 CPU cores and built-in
> > >>> peripherals, are integrated into a NPCM730 or NPCM750 SoC, which in turn form
> > >>> the foundation for the quanta-gsj and npcm750-evb machines, respectively. The
> > >>> two SoCs are very similar; the only difference is that NPCM730 is missing some
> > >>> peripherals that NPCM750 has, and which are not considered essential for
> > >>> datacenter use (e.g. graphics controllers). For more information, see
> > >>>
> > >>> https://www.nuvoton.com/products/cloud-computing/ibmc/
> > >>>
> > >>> Both quanta-gsj and npcm750-evb correspond to real boards supported by OpenBMC.
> > >>> At the end of the series, qemu can boot an OpenBMC image built for one of these
> > >>> boards with some minor modifications.
> > >>>
> > >>> The patches in this series were developed by Google and reviewed by Nuvoton. We
> > >>> will be maintaining the machine and peripheral support together.
> > >>>
> > >>> The data sheet for these SoCs is not generally available. Please let me know if
> > >>> more comments are needed to understand the device behavior.
> > >>>
> > >>> Changes since v7:
> > >>>
> > >>>   - Move register enums to .c files throughout, leaving a single
> > >>>     NPCM7XX_FOO_NR_REGS definition behind in the .h file. A QEMU_BUILD_BUG_ON
> > >>>     should alert anyone accidentally expanding the register enum that they need
> > >>>     to update the corresponding NR_REGS define, which in turn has a comment
> > >>>     reminding them to update the vmstate version_id as well.
> > >>>   - Skip loading the bootrom if a kernel filename is provided by the user.
> > >>>   - New patch adding a board setup stub to tweak clocks before booting directly
> > >>>     into the kernel.
> > >>>   - Add stuff to meson files instead of Makefiles.
> > >>>   - Try to disable the slowest drivers and services to speed up the flash boot
> > >>>     acceptance test a bit. This is somewhat based on the following
> > >>>     systemd-analyze blame report:
> > >>>     https://gist.github.com/hskinnemoen/475cb0676530cd2cebaa1754cf16ca97
> > >>>
> > >>> Changes since v6:
> > >>>
> > >>>   - Use size_to_str to report DRAM sizes in npcm7xx_gcr.
> > >>>   - Simplify the interrupt logic in npcm7xx_timer.
> > >>>   - Update global bios_name instead of temporary.
> > >>>   - Add npcm7xx_bootrom to MAINTAINERS and pc-bios/README.
> > >>>   - Use a predefined name for the gsj boot image in the acceptance test.
> > >>>
> > >>> Changes since v5:
> > >>>
> > >>>   - Boot ROM included, as a git submodule and a binary blob, and loaded by
> > >>>     default, so the -bios option is usually not necessary anymore.
> > >>>   - Two acceptance tests added (openbmc image boot, and direct kernel boot).
> > >>>   - npcm7xx_load_kernel() moved to SoC code.
> > >>>   - NPCM7XX_TIMER_REF_HZ definition moved to CLK header.
> > >>>   - Comments added clarifying available SPI flash chip selects.
> > >>>   - Error handling adjustments:
> > >>>       - Errors from CPU and GCR realization are propagated through the SoC
> > >>>         since they may be triggered by user-configurable parameters.
> > >>>       - Machine init uses error_fatal instead of error_abort for SoC
> > >>>         realization flash init. This makes error messages more helpful.
> > >>>       - Comments added to indicate whether peripherals may fail to realize.
> > >>>       - Use ERRP_GUARD() instead of Error *err when possible.
> > >>>   - Default CPU type is now set, and attempting to set it to anything else
> > >>>     will fail.
> > >>>   - Format string fixes (use HWADDR_PRIx, etc.)
> > >>>   - Simplified memory size encoding and error checking in npcm7xx_gcr.
> > >>>   - Encapsulate non-obvious pointer subtraction into helper functions in the
> > >>>     FIU and TIMER modules.
> > >>>   - Incorporate review feedback into the FIU module:
> > >>>       - Add select/deselect trace events.
> > >>>       - Use npcm7xx_fiu_{de,}select() consistently.
> > >>>       - Use extract/deposit in more places for consistency.
> > >>>       - Use -Wimplicit-fallthrough compatible fallthrough comments.
> > >>>       - Use qdev_init_gpio_out_named instead of sysbus_init_irq for chip
> > >>>         selects.
> > >>>   - Incorporate review feedback into the TIMER module:
> > >>>       - Assert that we never pause a timer that has already expired, instead of
> > >>>         trying to handle it. This should be safe since QEMU_CLOCK_VIRTUAL is
> > >>>         stopped while this code is running.
> > >>>       - Simplify the switch blocks in the read and write handlers.
> > >>>
> > >>> I made a change to error out if a flash drive was not specified, but reverted
> > >>> it because it caused make check to fail (qom-test). When specifying a NULL
> > >>> block device, the m25p flash device initializes its in-memory storage with 0xff
> > >>> and doesn't attempt to write anything back. This seems correct to me.
> > >>>
> > >>> Changes since v4:
> > >>>
> > >>>   - OTP cleanups suggested by Philippe Mathieu-Daudé.
> > >>>       - Added fuse array definitions based on public Nuvoton bootblock code.
> > >>>       - Moved class structure to .c file since it's only used internally.
> > >>>       - Readability improvements.
> > >>>   - Split the first patch and folded parts of it into three other patches so
> > >>>     that CONFIG_NPCM7XX is only enabled after the initial NPCM7xx machine
> > >>>     support is added.
> > >>>   - DRAM init moved to machine init code.
> > >>>   - Consistently use lower-case hex literals.
> > >>>   - Switched to fine-grained unimplemented devices, based on public bootblock
> > >>>     source code. Added a tiny SRAM that got left out previously.
> > >>>   - Simplified error handling in npcm7xx_realize() since the board code will
> > >>>     abort anyway, and SoCs are not hot-pluggable.
> > >>>
> > >>> Changes since v3:
> > >>>
> > >>>   - License headers are now GPL v2-or-later throughout.
> > >>>   - Added vmstate throughout (except the memory controller, which doesn't
> > >>>     really have any state worth saving). Successfully booted a gsj image
> > >>>     with two stop/savevm/quit/loadvm cycles along the way.
> > >>>       - JFFS2 really doesn't like it if I let qemu keep running after savevm,
> > >>>         and then jump back in time with loadvm. I assume this is expected.
> > >>>   - Fixed an error API violation in npcm7xx_realize, removed pointless error
> > >>>     check after object_property_set_link().
> > >>>   - Switched the OTP device to use an embedded array instead of a g_malloc0'd
> > >>>     one because I couldn't figure out how to set up vmstate for the latter.
> > >>>
> > >>> Changes since v2:
> > >>>
> > >>>   - Simplified the MAINTAINERS entry.
> > >>>   - Added link to OpenPOWER jenkins for gsj BMC images.
> > >>>   - Reverted the smpboot change, back to byte swapping.
> > >>>   - Adapted to upstream API changes:
> > >>>       - sysbus_init_child_obj -> object_initialize_child
> > >>>       - object_property_set_bool -> qdev_realize / sysbus_realize
> > >>>       - ssi_create_slave_no_init -> qdev_new
> > >>>       - qdev_init_nofail -> qdev_realize_and_unref
> > >>>       - ssi_auto_connect_slaves removed
> > >>>   - Moved Boot ROM loading from soc to machine init.
> > >>>   - Plumbed power-on-straps property from GCR to the machine init code so it
> > >>>     can be properly initialized. Turns out npcm750 memory init doesn't work
> > >>>     without this. npcm730 is fine either way, though I'm not sure why.
> > >>>   - Reworked the flash init code so it looks more like aspeed (i.e. the flash
> > >>>     device gets added even if there's no drive).
> > >>>
> > >>> Changes since v1 (requested by reviewers):
> > >>>
> > >>>   - Clarify the source of CLK reset values.
> > >>>   - Made smpboot a constant byte array, eliinated byte swapping.
> > >>>   - NPCM7xxState now stores an array of ARMCPUs, not pointers to ARMCPUs.
> > >>>   - Clarify why EL3 is disabled.
> > >>>   - Introduce NPCM7XX_NUM_IRQ constant.
> > >>>   - Set the number of CPUs according to SoC variant, and disallow command line
> > >>>     overrides (i.e. you can no longer override the number of CPUs with the -smp
> > >>>     parameter). This is trying to follow the spirit of
> > >>>     https://patchwork.kernel.org/patch/11595407/.
> > >>>   - Switch register operations to DEVICE_LITTLE_ENDIAN throughout.
> > >>>   - Machine documentation added (new patch).
> > >>>
> > >>> Changes since v1 to support flash booting:
> > >>>
> > >>>   - GCR reset value changes to get past memory initialization when booting
> > >>>     from flash (patches 2 and 5):
> > >>>       - INTCR2 now indicates that the DDR controller is initialized.
> > >>>       - INTCR3 is initialized according to DDR memory size. A realize()
> > >>>     method was implemented to achieve this.
> > >>>   - Refactor the machine initialization a bit to make it easier to drop in
> > >>>     machine-specific flash initialization (patch 6).
> > >>>   - Extend the series with additional patches to enable booting from flash:
> > >>>       - Boot ROM (through the -bios option).
> > >>>       - OTP (fuse) controller.
> > >>>       - Memory Controller stub (just enough to skip memory training).
> > >>>       - Flash controller.
> > >>>       - Board-specific flash initialization.
> > >>>
> > >>> Thanks for reviewing,
> > >>>
> > >>> Havard
> > >>>
> > >>> Havard Skinnemoen (14):
> > >>>   hw/misc: Add NPCM7xx System Global Control Registers device model
> > >>>   hw/misc: Add NPCM7xx Clock Controller device model
> > >>>   hw/timer: Add NPCM7xx Timer device model
> > >>>   hw/arm: Add NPCM730 and NPCM750 SoC models
> > >>>   hw/arm: Add two NPCM7xx-based machines
> > >>>   roms: Add virtual Boot ROM for NPCM7xx SoCs
> > >>>   hw/arm: Load -bios image as a boot ROM for npcm7xx
> > >>>   hw/nvram: NPCM7xx OTP device model
> > >>>   hw/mem: Stubbed out NPCM7xx Memory Controller model
> > >>>   hw/ssi: NPCM7xx Flash Interface Unit device model
> > >>>   hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
> > >>>   hw/arm/npcm7xx: add board setup stub for CPU and UART clocks
> > >>>   docs/system: Add Nuvoton machine documentation
> > >>>   tests/acceptance: console boot tests for quanta-gsj
> > >>>
> > >>>  docs/system/arm/nuvoton.rst            |  90 ++++
> > >>>  docs/system/target-arm.rst             |   1 +
> > >>>  Makefile                               |   1 +
> > >>>  default-configs/arm-softmmu.mak        |   1 +
> > >>>  include/hw/arm/npcm7xx.h               | 112 +++++
> > >>>  include/hw/mem/npcm7xx_mc.h            |  36 ++
> > >>>  include/hw/misc/npcm7xx_clk.h          |  48 +++
> > >>>  include/hw/misc/npcm7xx_gcr.h          |  43 ++
> > >>>  include/hw/nvram/npcm7xx_otp.h         |  79 ++++
> > >>>  include/hw/ssi/npcm7xx_fiu.h           |  73 ++++
> > >>>  include/hw/timer/npcm7xx_timer.h       |  78 ++++
> > >>>  hw/arm/npcm7xx.c                       | 532 +++++++++++++++++++++++
> > >>>  hw/arm/npcm7xx_boards.c                | 197 +++++++++
> > >>>  hw/mem/npcm7xx_mc.c                    |  84 ++++
> > >>>  hw/misc/npcm7xx_clk.c                  | 266 ++++++++++++
> > >>>  hw/misc/npcm7xx_gcr.c                  | 269 ++++++++++++
> > >>>  hw/nvram/npcm7xx_otp.c                 | 439 +++++++++++++++++++
> > >>>  hw/ssi/npcm7xx_fiu.c                   | 572 +++++++++++++++++++++++++
> > >>>  hw/timer/npcm7xx_timer.c               | 509 ++++++++++++++++++++++
> > >>>  .gitmodules                            |   3 +
> > >>>  MAINTAINERS                            |  10 +
> > >>>  hw/arm/Kconfig                         |   9 +
> > >>>  hw/arm/meson.build                     |   1 +
> > >>>  hw/mem/meson.build                     |   1 +
> > >>>  hw/misc/meson.build                    |   4 +
> > >>>  hw/misc/trace-events                   |   8 +
> > >>>  hw/nvram/meson.build                   |   1 +
> > >>>  hw/ssi/meson.build                     |   1 +
> > >>>  hw/ssi/trace-events                    |  11 +
> > >>>  hw/timer/meson.build                   |   1 +
> > >>>  hw/timer/trace-events                  |   5 +
> > >>>  pc-bios/README                         |   6 +
> > >>>  pc-bios/npcm7xx_bootrom.bin            | Bin 0 -> 768 bytes
> > >>>  roms/Makefile                          |   7 +
> > >>>  roms/vbootrom                          |   1 +
> > >>>  tests/acceptance/boot_linux_console.py |  83 ++++
> > >>>  36 files changed, 3582 insertions(+)
> > >>>  create mode 100644 docs/system/arm/nuvoton.rst
> > >>>  create mode 100644 include/hw/arm/npcm7xx.h
> > >>>  create mode 100644 include/hw/mem/npcm7xx_mc.h
> > >>>  create mode 100644 include/hw/misc/npcm7xx_clk.h
> > >>>  create mode 100644 include/hw/misc/npcm7xx_gcr.h
> > >>>  create mode 100644 include/hw/nvram/npcm7xx_otp.h
> > >>>  create mode 100644 include/hw/ssi/npcm7xx_fiu.h
> > >>>  create mode 100644 include/hw/timer/npcm7xx_timer.h
> > >>>  create mode 100644 hw/arm/npcm7xx.c
> > >>>  create mode 100644 hw/arm/npcm7xx_boards.c
> > >>>  create mode 100644 hw/mem/npcm7xx_mc.c
> > >>>  create mode 100644 hw/misc/npcm7xx_clk.c
> > >>>  create mode 100644 hw/misc/npcm7xx_gcr.c
> > >>>  create mode 100644 hw/nvram/npcm7xx_otp.c
> > >>>  create mode 100644 hw/ssi/npcm7xx_fiu.c
> > >>>  create mode 100644 hw/timer/npcm7xx_timer.c
> > >>>  create mode 100644 pc-bios/npcm7xx_bootrom.bin
> > >>>  create mode 160000 roms/vbootrom
> > >>>
> > >>> --
> > >>> 2.28.0.297.g1956fa8f8d-goog
> > >>>
> > >>>
> > >>
> > >


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

* Re: [PATCH v8 07/14] hw/arm: Load -bios image as a boot ROM for npcm7xx
  2020-09-03 18:59   ` Philippe Mathieu-Daudé
@ 2020-09-10 23:52     ` Havard Skinnemoen
  0 siblings, 0 replies; 35+ messages in thread
From: Havard Skinnemoen @ 2020-09-10 23:52 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, QEMU Developers, IS20 Avi Fishman,
	CS20 KFTing, Cédric Le Goater

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

On Thu, Sep 3, 2020 at 11:59 AM Philippe Mathieu-Daudé <f4bug@amsat.org>
wrote:

> On 8/25/20 2:17 AM, Havard Skinnemoen via wrote:
> > If a -bios option is specified on the command line, load the image into
> > the internal ROM memory region, which contains the first instructions
> > run by the CPU after reset.
> >
> > If -bios is not specified, the vbootrom included with qemu is loaded by
> > default.
> >
> > Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> > Reviewed-by: Cédric Le Goater <clg@kaod.org>
> > Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> > ---
> >  hw/arm/npcm7xx_boards.c | 33 +++++++++++++++++++++++++++++++++
> >  1 file changed, 33 insertions(+)
> >
> > diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
> > index 0b9dce2b35..b67e45e913 100644
> > --- a/hw/arm/npcm7xx_boards.c
> > +++ b/hw/arm/npcm7xx_boards.c
> > @@ -18,12 +18,43 @@
> >
> >  #include "hw/arm/npcm7xx.h"
> >  #include "hw/core/cpu.h"
> > +#include "hw/loader.h"
> >  #include "qapi/error.h"
> > +#include "qemu-common.h"
> >  #include "qemu/units.h"
> > +#include "sysemu/sysemu.h"
> >
> >  #define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7
> >  #define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff
> >
> > +static const char npcm7xx_default_bootrom[] = "npcm7xx_bootrom.bin";
> > +
> > +static void npcm7xx_load_bootrom(MachineState *machine, NPCM7xxState
> *soc)
> > +{
> > +    g_autofree char *filename = NULL;
> > +    int ret;
> > +
> > +    if (machine->kernel_filename) {
> > +        /* Don't load the bootrom if user requested direct kernel boot.
> */
> > +        return;
>
> Wondering why not load it always if available, and what would
> happen if booting from a kernel (without bootrom) and running
> "reboot" (via SysRq) in the guest I got:
>
> # echo b > /proc/sysrq-trigger
> sysrq: Resetting
> CPU0: stopping
> CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.4.32-7dc9442-dirty-8978043 #1
> Hardware name: NPCM7XX Chip family
> Backtrace:
> [<b0107c4c>] (dump_backtrace) from [<b010823c>] (show_stack+0x20/0x24)
>  r7:00000000 r6:600f0193 r5:00000000 r4:b0b5b1b8
> [<b010821c>] (show_stack) from [<b077b230>] (dump_stack+0x94/0xa8)
> [<b077b19c>] (dump_stack) from [<b010a20c>] (handle_IPI+0x1f0/0x38c)
>  r7:00000000 r6:00000004 r5:b0b5eb64 r4:b0b729e0
> [<b010a01c>] (handle_IPI) from [<b0102264>] (gic_handle_irq+0x9c/0xa0)
>  r10:10c5387d r9:b0b01ef8 r8:d0803100 r7:d0802100 r6:d080210c r5:b0b37db4
>  r4:b0b04504
> [<b01021c8>] (gic_handle_irq) from [<b0101a8c>] (__irq_svc+0x6c/0x90)
> Exception stack(0xb0b01ef8 to 0xb0b01f40)
> 1ee0:                                                       00000000
> 00012f74
> 1f00: cfbc1774 b01112e0 b0b00000 00000000 b0b03e28 b0b03e6c b0b5e72c
> b08e5ea8
> 1f20: 10c5387d b0b01f54 b0b01f58 b0b01f48 b0104114 b0104118 600f0013
> ffffffff
>  r9:b0b00000 r8:b0b5e72c r7:b0b01f2c r6:ffffffff r5:600f0013 r4:b0104118
> [<b01040d4>] (arch_cpu_idle) from [<b014b240>] (do_idle+0xec/0x140)
> [<b014b154>] (do_idle) from [<b014b56c>] (cpu_startup_entry+0x28/0x2c)
>  r9:410fc090 r8:0810c000 r7:00000000 r6:b0a53a40 r5:00000001 r4:000000ce
> [<b014b544>] (cpu_startup_entry) from [<b0794f6c>] (rest_init+0xb4/0xbc)
> [<b0794eb8>] (rest_init) from [<b0a00c50>] (arch_call_rest_init+0x18/0x1c)
>  r5:00000001 r4:b0b72754
> [<b0a00c38>] (arch_call_rest_init) from [<b0a010f4>]
> (start_kernel+0x428/0x458)
> [<b0a00ccc>] (start_kernel) from [<00000000>] (0x0)
> Reboot failed -- System halted
>
> So not a problem so far...
> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
>
> But still I'd keep loading the bootrom regardless -kernel.
>

So you mean something like this?

@@ -35,11 +35,6 @@ static void npcm7xx_load_bootrom(MachineState *machine,
NPCM7xxState *soc)
     g_autofree char *filename = NULL;
     int ret;

-    if (machine->kernel_filename) {
-        /* Don't load the bootrom if user requested direct kernel boot. */
-        return;
-    }
-
     if (!bios_name) {
         bios_name = npcm7xx_default_bootrom;
     }
@@ -47,7 +42,11 @@ static void npcm7xx_load_bootrom(MachineState *machine,
NPCM7xxState *soc)
     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
     if (!filename) {
         error_report("Could not find ROM image '%s'", bios_name);
-        exit(1);
+        if (!machine->kernel_filename) {
+            /* We can't boot without a bootrom or a kernel image. */
+            exit(1);
+        }
+        return;
     }
     ret = load_image_mr(filename, &soc->irom);
     if (ret < 0) {

I'm not sure if it will make much of a difference, as (IIUC)
arm_load_kernel installs a reset hook to jump directly into the kernel,
bypassing the bootrom entirely. I'm assuming a watchdog or SW reset would
trigger the same reset hook and jump right back into the kernel, though I
could be wrong.

Anyway, I can make the above change in any case.

Havard

[-- Attachment #2: Type: text/html, Size: 6432 bytes --]

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

* Re: [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines
  2020-09-09  1:32         ` Havard Skinnemoen
@ 2020-09-11  0:03           ` Havard Skinnemoen
  0 siblings, 0 replies; 35+ messages in thread
From: Havard Skinnemoen @ 2020-09-11  0:03 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Alexander Bulekov, Peter Maydell, qemu-arm, QEMU Developers,
	IS20 Avi Fishman, CS20 KFTing

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

On Tue, Sep 8, 2020 at 6:32 PM Havard Skinnemoen <hskinnemoen@google.com>
wrote:

> On Tue, Sep 8, 2020 at 12:52 PM Havard Skinnemoen
> <hskinnemoen@google.com> wrote:
> >
> > On Tue, Sep 8, 2020 at 9:58 AM Philippe Mathieu-Daudé <f4bug@amsat.org>
> wrote:
> > >
> > > On 9/8/20 5:52 PM, Philippe Mathieu-Daudé wrote:
> > > > On 9/8/20 5:02 PM, Alexander Bulekov wrote:
> > > >> Hi Havard,
> > > >> I fuzzed the npcm750-evb machine until I hit over 85% coverage over
> all
> > > >> the new npcm.*\.c files. The only thing I found specific to the new
> > > >> code, so far:
> > > >>
> > > >> cat << EOF | ./qemu-system-arm -machine npcm750-evb -m 128M -qtest
> stdio
> > > >> write 0xf0009040 0x4 0xc4c4c4c4
> > > >> write 0xf0009040 0x4 0x4
> > > >> EOF
> > > >
> > > > This is an odd test because with -qtest the timer is not running,
> > > > so this can not really happen on real hw.
> > > >
> > > > The fix is:
> > > >
> > > > -    g_assert(t->remaining_ns > 0);
> > > > +    g_assert(qtest_enabled() || t->remaining_ns > 0);
> > >
> > > Alex corrected me on IRC, qtest is irrelevant here.
> > > The problem is he disables the timer twice.
> > >
> > > So maybe something like:
> > >
> > >  static void npcm7xx_timer_pause(NPCM7xxTimer *t)
> > >  {
> > >      int64_t now;
> > >
> > > +    if (!timer_pending(&t->qtimer)) {
> > > +        return;
> > > +    }
> > >      timer_del(&t->qtimer);
> > >      now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > >      t->remaining_ns = t->expires_ns - now;
> > >      g_assert(t->remaining_ns > 0);
> > >  }
> >
> > Thanks, that makes sense. I was worried that making the assert
> > conditional on qtest_enabled() might hide real issues.
>
> Hmm, that didn't help, though it might make sense to keep it there anyway.
>
> What the test case does is:
>
>   1. Enable the timer (with zero expiration time) and reset it at the same
> time.
>   2. Disable the timer zero cycles after it was enabled.
>
> It also touches a bunch of other bits (including reserved bits), but
> they should be irrelevant.
>
> I think there are two issues here.
>
> When the Reset bit is set, the Enable bit should be forced to zero.
> This is easy to fix.
>
> If the timer is enabled with zero expiration time, and immediately
> disabled without advancing the virtual time, npcm7xx_timer_pause() is
> called while the timer is active, but t->expires_ns ==
> qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL). So t->remaining_ns becomes zero
> and triggers the assertion.
>
> If I revert a change that Philippe asked me to do earlier:
>
>     timer_del(&t->qtimer);
>      now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>      t->remaining_ns = t->expires_ns - now;
> -    g_assert(t->remaining_ns > 0);
> +    if (t->remaining_ns <= 0) {
> +        npcm7xx_timer_reached_zero(t);
> +    }
>  }
>
> it doesn't crash:
>
> $ cat << EOF | ./qemu-system-arm -machine npcm750-evb -m 128M -qtest
> stdio --trace npcm7xx_timer*
> write 0xf0009040 0x4 0xc4c4c4c4
> write 0xf0009040 0x4 0x4
> EOF
> [I 1599613445.620379] OPENED
> [R +0.180771] write 0xf0009040 0x4 0xc4c4c4c4
> 1361079@1599613445.801182:npcm7xx_timer_write /machine/soc/tim[1]
> offset: 0x0040 value 0xc4c4c4c4
> OK
> [S +0.180816] OK
> [R +0.180833] write 0xf0009040 0x4 0x4
> 1361079@1599613445.801220:npcm7xx_timer_write /machine/soc/tim[1]
> offset: 0x0040 value 0x00000000
> 1361079@1599613445.801295:npcm7xx_timer_irq /machine/soc/tim[1] timer 4
> state 0
> OK
> [S +0.180927] OK
> [I +0.181319] CLOSED
> [I +4.003267] CLOSED
>
> Note that the npcm7xx_timer_irq trace event is a sign of the first
> bug, but fixing that might mask the second bug. If we write the same
> pattern, only without the Reset bit, this would be the correct
> behavior (and it still causes the v8 code to crash).
>
> I think this device deserves a qtest. I wonder if we'd trigger the
> assertion if we set a nonzero expiration time, but happen to clear the
> Enable bit on the exact cycle it's supposed to expire. That would be a
> more realistic scenario, as it wouldn't require multiple register
> writes in the same virtual clock cycle.
>

I wrote some qtests, and found several more bugs, but I wasn't able to
trigger this particular failure mode. I was able to reproduce both of the
bugs found by the fuzzer though.

I'll refresh the patch series tonight or tomorrow, and also send the qtest
to Nuvoton (and probably send it to the list within the next few weeks or
so).


> I probably won't add the qtest to the same series, as I'd like someone
> from Nuvoton to get a chance to review it first.
>
> Havard
>
> >
> > This fuzz testing is great, it would have been hard to find this bug
> > without it. Thanks a lot Alex for running it.
> >
> > Havard
> >
> > > >
> > > >>
> > > >> ERROR:../hw/timer/npcm7xx_timer.c:160:npcm7xx_timer_pause:
> assertion failed: (t->remaining_ns > 0)
> > > >> Bail out!
> ERROR:../hw/timer/npcm7xx_timer.c:160:npcm7xx_timer_pause: assertion
> failed: (t->remaining_ns > 0)
> > > >> Aborted
> > > >>
> > > >> I'm doing the same for the quanta-gsj machine, but I'm not sure
> whether
> > > >> it will cover more code, so I'm happy to leave a:
> > > >>
> > > >> Tested-by: Alexander Bulekov <alxndr@bu.edu>
> > > >>
> > > >> for the patches that add new virtual-device code (1-5, 7-12 ?)
> > > >> -Alex
> > > >
> > > > Very nice from you for testing running the fuzzer!
> > > >
> > > > Regards,
> > > >
> > > > Phil.
> > > >
> > > >>
> > > >>
> > > >> On 200824 1716, Havard Skinnemoen via wrote:
> > > >>> I also pushed this and the previous patchsets to my qemu fork on
> github.
> > > >>> The branches are named npcm7xx-v[1-8].
> > > >>>
> > > >>>   https://github.com/hskinnemoen/qemu
> > > >>>
> > > >>> This patch series models enough of the Nuvoton NPCM730 and NPCM750
> SoCs to boot
> > > >>> an OpenBMC image built for quanta-gsj. This includes device models
> for:
> > > >>>
> > > >>>   - Global Configuration Registers
> > > >>>   - Clock Control
> > > >>>   - Timers
> > > >>>   - Fuses
> > > >>>   - Memory Controller
> > > >>>   - Flash Controller
> > > >>>
> > > >>> These modules, along with the existing Cortex A9 CPU cores and
> built-in
> > > >>> peripherals, are integrated into a NPCM730 or NPCM750 SoC, which
> in turn form
> > > >>> the foundation for the quanta-gsj and npcm750-evb machines,
> respectively. The
> > > >>> two SoCs are very similar; the only difference is that NPCM730 is
> missing some
> > > >>> peripherals that NPCM750 has, and which are not considered
> essential for
> > > >>> datacenter use (e.g. graphics controllers). For more information,
> see
> > > >>>
> > > >>> https://www.nuvoton.com/products/cloud-computing/ibmc/
> > > >>>
> > > >>> Both quanta-gsj and npcm750-evb correspond to real boards
> supported by OpenBMC.
> > > >>> At the end of the series, qemu can boot an OpenBMC image built for
> one of these
> > > >>> boards with some minor modifications.
> > > >>>
> > > >>> The patches in this series were developed by Google and reviewed
> by Nuvoton. We
> > > >>> will be maintaining the machine and peripheral support together.
> > > >>>
> > > >>> The data sheet for these SoCs is not generally available. Please
> let me know if
> > > >>> more comments are needed to understand the device behavior.
> > > >>>
> > > >>> Changes since v7:
> > > >>>
> > > >>>   - Move register enums to .c files throughout, leaving a single
> > > >>>     NPCM7XX_FOO_NR_REGS definition behind in the .h file. A
> QEMU_BUILD_BUG_ON
> > > >>>     should alert anyone accidentally expanding the register enum
> that they need
> > > >>>     to update the corresponding NR_REGS define, which in turn has
> a comment
> > > >>>     reminding them to update the vmstate version_id as well.
> > > >>>   - Skip loading the bootrom if a kernel filename is provided by
> the user.
> > > >>>   - New patch adding a board setup stub to tweak clocks before
> booting directly
> > > >>>     into the kernel.
> > > >>>   - Add stuff to meson files instead of Makefiles.
> > > >>>   - Try to disable the slowest drivers and services to speed up
> the flash boot
> > > >>>     acceptance test a bit. This is somewhat based on the following
> > > >>>     systemd-analyze blame report:
> > > >>>
> https://gist.github.com/hskinnemoen/475cb0676530cd2cebaa1754cf16ca97
> > > >>>
> > > >>> Changes since v6:
> > > >>>
> > > >>>   - Use size_to_str to report DRAM sizes in npcm7xx_gcr.
> > > >>>   - Simplify the interrupt logic in npcm7xx_timer.
> > > >>>   - Update global bios_name instead of temporary.
> > > >>>   - Add npcm7xx_bootrom to MAINTAINERS and pc-bios/README.
> > > >>>   - Use a predefined name for the gsj boot image in the acceptance
> test.
> > > >>>
> > > >>> Changes since v5:
> > > >>>
> > > >>>   - Boot ROM included, as a git submodule and a binary blob, and
> loaded by
> > > >>>     default, so the -bios option is usually not necessary anymore.
> > > >>>   - Two acceptance tests added (openbmc image boot, and direct
> kernel boot).
> > > >>>   - npcm7xx_load_kernel() moved to SoC code.
> > > >>>   - NPCM7XX_TIMER_REF_HZ definition moved to CLK header.
> > > >>>   - Comments added clarifying available SPI flash chip selects.
> > > >>>   - Error handling adjustments:
> > > >>>       - Errors from CPU and GCR realization are propagated through
> the SoC
> > > >>>         since they may be triggered by user-configurable
> parameters.
> > > >>>       - Machine init uses error_fatal instead of error_abort for
> SoC
> > > >>>         realization flash init. This makes error messages more
> helpful.
> > > >>>       - Comments added to indicate whether peripherals may fail to
> realize.
> > > >>>       - Use ERRP_GUARD() instead of Error *err when possible.
> > > >>>   - Default CPU type is now set, and attempting to set it to
> anything else
> > > >>>     will fail.
> > > >>>   - Format string fixes (use HWADDR_PRIx, etc.)
> > > >>>   - Simplified memory size encoding and error checking in
> npcm7xx_gcr.
> > > >>>   - Encapsulate non-obvious pointer subtraction into helper
> functions in the
> > > >>>     FIU and TIMER modules.
> > > >>>   - Incorporate review feedback into the FIU module:
> > > >>>       - Add select/deselect trace events.
> > > >>>       - Use npcm7xx_fiu_{de,}select() consistently.
> > > >>>       - Use extract/deposit in more places for consistency.
> > > >>>       - Use -Wimplicit-fallthrough compatible fallthrough comments.
> > > >>>       - Use qdev_init_gpio_out_named instead of sysbus_init_irq
> for chip
> > > >>>         selects.
> > > >>>   - Incorporate review feedback into the TIMER module:
> > > >>>       - Assert that we never pause a timer that has already
> expired, instead of
> > > >>>         trying to handle it. This should be safe since
> QEMU_CLOCK_VIRTUAL is
> > > >>>         stopped while this code is running.
> > > >>>       - Simplify the switch blocks in the read and write handlers.
> > > >>>
> > > >>> I made a change to error out if a flash drive was not specified,
> but reverted
> > > >>> it because it caused make check to fail (qom-test). When
> specifying a NULL
> > > >>> block device, the m25p flash device initializes its in-memory
> storage with 0xff
> > > >>> and doesn't attempt to write anything back. This seems correct to
> me.
> > > >>>
> > > >>> Changes since v4:
> > > >>>
> > > >>>   - OTP cleanups suggested by Philippe Mathieu-Daudé.
> > > >>>       - Added fuse array definitions based on public Nuvoton
> bootblock code.
> > > >>>       - Moved class structure to .c file since it's only used
> internally.
> > > >>>       - Readability improvements.
> > > >>>   - Split the first patch and folded parts of it into three other
> patches so
> > > >>>     that CONFIG_NPCM7XX is only enabled after the initial NPCM7xx
> machine
> > > >>>     support is added.
> > > >>>   - DRAM init moved to machine init code.
> > > >>>   - Consistently use lower-case hex literals.
> > > >>>   - Switched to fine-grained unimplemented devices, based on
> public bootblock
> > > >>>     source code. Added a tiny SRAM that got left out previously.
> > > >>>   - Simplified error handling in npcm7xx_realize() since the board
> code will
> > > >>>     abort anyway, and SoCs are not hot-pluggable.
> > > >>>
> > > >>> Changes since v3:
> > > >>>
> > > >>>   - License headers are now GPL v2-or-later throughout.
> > > >>>   - Added vmstate throughout (except the memory controller, which
> doesn't
> > > >>>     really have any state worth saving). Successfully booted a gsj
> image
> > > >>>     with two stop/savevm/quit/loadvm cycles along the way.
> > > >>>       - JFFS2 really doesn't like it if I let qemu keep running
> after savevm,
> > > >>>         and then jump back in time with loadvm. I assume this is
> expected.
> > > >>>   - Fixed an error API violation in npcm7xx_realize, removed
> pointless error
> > > >>>     check after object_property_set_link().
> > > >>>   - Switched the OTP device to use an embedded array instead of a
> g_malloc0'd
> > > >>>     one because I couldn't figure out how to set up vmstate for
> the latter.
> > > >>>
> > > >>> Changes since v2:
> > > >>>
> > > >>>   - Simplified the MAINTAINERS entry.
> > > >>>   - Added link to OpenPOWER jenkins for gsj BMC images.
> > > >>>   - Reverted the smpboot change, back to byte swapping.
> > > >>>   - Adapted to upstream API changes:
> > > >>>       - sysbus_init_child_obj -> object_initialize_child
> > > >>>       - object_property_set_bool -> qdev_realize / sysbus_realize
> > > >>>       - ssi_create_slave_no_init -> qdev_new
> > > >>>       - qdev_init_nofail -> qdev_realize_and_unref
> > > >>>       - ssi_auto_connect_slaves removed
> > > >>>   - Moved Boot ROM loading from soc to machine init.
> > > >>>   - Plumbed power-on-straps property from GCR to the machine init
> code so it
> > > >>>     can be properly initialized. Turns out npcm750 memory init
> doesn't work
> > > >>>     without this. npcm730 is fine either way, though I'm not sure
> why.
> > > >>>   - Reworked the flash init code so it looks more like aspeed
> (i.e. the flash
> > > >>>     device gets added even if there's no drive).
> > > >>>
> > > >>> Changes since v1 (requested by reviewers):
> > > >>>
> > > >>>   - Clarify the source of CLK reset values.
> > > >>>   - Made smpboot a constant byte array, eliinated byte swapping.
> > > >>>   - NPCM7xxState now stores an array of ARMCPUs, not pointers to
> ARMCPUs.
> > > >>>   - Clarify why EL3 is disabled.
> > > >>>   - Introduce NPCM7XX_NUM_IRQ constant.
> > > >>>   - Set the number of CPUs according to SoC variant, and disallow
> command line
> > > >>>     overrides (i.e. you can no longer override the number of CPUs
> with the -smp
> > > >>>     parameter). This is trying to follow the spirit of
> > > >>>     https://patchwork.kernel.org/patch/11595407/.
> > > >>>   - Switch register operations to DEVICE_LITTLE_ENDIAN throughout.
> > > >>>   - Machine documentation added (new patch).
> > > >>>
> > > >>> Changes since v1 to support flash booting:
> > > >>>
> > > >>>   - GCR reset value changes to get past memory initialization when
> booting
> > > >>>     from flash (patches 2 and 5):
> > > >>>       - INTCR2 now indicates that the DDR controller is
> initialized.
> > > >>>       - INTCR3 is initialized according to DDR memory size. A
> realize()
> > > >>>     method was implemented to achieve this.
> > > >>>   - Refactor the machine initialization a bit to make it easier to
> drop in
> > > >>>     machine-specific flash initialization (patch 6).
> > > >>>   - Extend the series with additional patches to enable booting
> from flash:
> > > >>>       - Boot ROM (through the -bios option).
> > > >>>       - OTP (fuse) controller.
> > > >>>       - Memory Controller stub (just enough to skip memory
> training).
> > > >>>       - Flash controller.
> > > >>>       - Board-specific flash initialization.
> > > >>>
> > > >>> Thanks for reviewing,
> > > >>>
> > > >>> Havard
> > > >>>
> > > >>> Havard Skinnemoen (14):
> > > >>>   hw/misc: Add NPCM7xx System Global Control Registers device model
> > > >>>   hw/misc: Add NPCM7xx Clock Controller device model
> > > >>>   hw/timer: Add NPCM7xx Timer device model
> > > >>>   hw/arm: Add NPCM730 and NPCM750 SoC models
> > > >>>   hw/arm: Add two NPCM7xx-based machines
> > > >>>   roms: Add virtual Boot ROM for NPCM7xx SoCs
> > > >>>   hw/arm: Load -bios image as a boot ROM for npcm7xx
> > > >>>   hw/nvram: NPCM7xx OTP device model
> > > >>>   hw/mem: Stubbed out NPCM7xx Memory Controller model
> > > >>>   hw/ssi: NPCM7xx Flash Interface Unit device model
> > > >>>   hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
> > > >>>   hw/arm/npcm7xx: add board setup stub for CPU and UART clocks
> > > >>>   docs/system: Add Nuvoton machine documentation
> > > >>>   tests/acceptance: console boot tests for quanta-gsj
> > > >>>
> > > >>>  docs/system/arm/nuvoton.rst            |  90 ++++
> > > >>>  docs/system/target-arm.rst             |   1 +
> > > >>>  Makefile                               |   1 +
> > > >>>  default-configs/arm-softmmu.mak        |   1 +
> > > >>>  include/hw/arm/npcm7xx.h               | 112 +++++
> > > >>>  include/hw/mem/npcm7xx_mc.h            |  36 ++
> > > >>>  include/hw/misc/npcm7xx_clk.h          |  48 +++
> > > >>>  include/hw/misc/npcm7xx_gcr.h          |  43 ++
> > > >>>  include/hw/nvram/npcm7xx_otp.h         |  79 ++++
> > > >>>  include/hw/ssi/npcm7xx_fiu.h           |  73 ++++
> > > >>>  include/hw/timer/npcm7xx_timer.h       |  78 ++++
> > > >>>  hw/arm/npcm7xx.c                       | 532
> +++++++++++++++++++++++
> > > >>>  hw/arm/npcm7xx_boards.c                | 197 +++++++++
> > > >>>  hw/mem/npcm7xx_mc.c                    |  84 ++++
> > > >>>  hw/misc/npcm7xx_clk.c                  | 266 ++++++++++++
> > > >>>  hw/misc/npcm7xx_gcr.c                  | 269 ++++++++++++
> > > >>>  hw/nvram/npcm7xx_otp.c                 | 439 +++++++++++++++++++
> > > >>>  hw/ssi/npcm7xx_fiu.c                   | 572
> +++++++++++++++++++++++++
> > > >>>  hw/timer/npcm7xx_timer.c               | 509
> ++++++++++++++++++++++
> > > >>>  .gitmodules                            |   3 +
> > > >>>  MAINTAINERS                            |  10 +
> > > >>>  hw/arm/Kconfig                         |   9 +
> > > >>>  hw/arm/meson.build                     |   1 +
> > > >>>  hw/mem/meson.build                     |   1 +
> > > >>>  hw/misc/meson.build                    |   4 +
> > > >>>  hw/misc/trace-events                   |   8 +
> > > >>>  hw/nvram/meson.build                   |   1 +
> > > >>>  hw/ssi/meson.build                     |   1 +
> > > >>>  hw/ssi/trace-events                    |  11 +
> > > >>>  hw/timer/meson.build                   |   1 +
> > > >>>  hw/timer/trace-events                  |   5 +
> > > >>>  pc-bios/README                         |   6 +
> > > >>>  pc-bios/npcm7xx_bootrom.bin            | Bin 0 -> 768 bytes
> > > >>>  roms/Makefile                          |   7 +
> > > >>>  roms/vbootrom                          |   1 +
> > > >>>  tests/acceptance/boot_linux_console.py |  83 ++++
> > > >>>  36 files changed, 3582 insertions(+)
> > > >>>  create mode 100644 docs/system/arm/nuvoton.rst
> > > >>>  create mode 100644 include/hw/arm/npcm7xx.h
> > > >>>  create mode 100644 include/hw/mem/npcm7xx_mc.h
> > > >>>  create mode 100644 include/hw/misc/npcm7xx_clk.h
> > > >>>  create mode 100644 include/hw/misc/npcm7xx_gcr.h
> > > >>>  create mode 100644 include/hw/nvram/npcm7xx_otp.h
> > > >>>  create mode 100644 include/hw/ssi/npcm7xx_fiu.h
> > > >>>  create mode 100644 include/hw/timer/npcm7xx_timer.h
> > > >>>  create mode 100644 hw/arm/npcm7xx.c
> > > >>>  create mode 100644 hw/arm/npcm7xx_boards.c
> > > >>>  create mode 100644 hw/mem/npcm7xx_mc.c
> > > >>>  create mode 100644 hw/misc/npcm7xx_clk.c
> > > >>>  create mode 100644 hw/misc/npcm7xx_gcr.c
> > > >>>  create mode 100644 hw/nvram/npcm7xx_otp.c
> > > >>>  create mode 100644 hw/ssi/npcm7xx_fiu.c
> > > >>>  create mode 100644 hw/timer/npcm7xx_timer.c
> > > >>>  create mode 100644 pc-bios/npcm7xx_bootrom.bin
> > > >>>  create mode 160000 roms/vbootrom
> > > >>>
> > > >>> --
> > > >>> 2.28.0.297.g1956fa8f8d-goog
> > > >>>
> > > >>>
> > > >>
> > > >
>

[-- Attachment #2: Type: text/html, Size: 27887 bytes --]

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

end of thread, other threads:[~2020-09-11  0:04 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-25  0:16 [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen via
2020-08-25  0:16 ` [PATCH v8 01/14] hw/misc: Add NPCM7xx System Global Control Registers device model Havard Skinnemoen via
2020-08-25  0:16 ` [PATCH v8 02/14] hw/misc: Add NPCM7xx Clock Controller " Havard Skinnemoen via
2020-09-04  9:32   ` Philippe Mathieu-Daudé
2020-09-04 22:02     ` Havard Skinnemoen
2020-09-04 22:38       ` Havard Skinnemoen
2020-09-07 13:40         ` Philippe Mathieu-Daudé
2020-09-07 17:58           ` Havard Skinnemoen
2020-08-25  0:17 ` [PATCH v8 03/14] hw/timer: Add NPCM7xx Timer " Havard Skinnemoen via
2020-08-25  0:17 ` [PATCH v8 04/14] hw/arm: Add NPCM730 and NPCM750 SoC models Havard Skinnemoen via
2020-08-25  0:17 ` [PATCH v8 05/14] hw/arm: Add two NPCM7xx-based machines Havard Skinnemoen via
2020-08-25  0:17 ` [PATCH v8 06/14] roms: Add virtual Boot ROM for NPCM7xx SoCs Havard Skinnemoen via
2020-09-04  9:24   ` Philippe Mathieu-Daudé
2020-08-25  0:17 ` [PATCH v8 07/14] hw/arm: Load -bios image as a boot ROM for npcm7xx Havard Skinnemoen via
2020-09-03 18:59   ` Philippe Mathieu-Daudé
2020-09-10 23:52     ` Havard Skinnemoen
2020-08-25  0:17 ` [PATCH v8 08/14] hw/nvram: NPCM7xx OTP device model Havard Skinnemoen via
2020-09-07 19:47   ` Alexander Bulekov
2020-09-07 19:57     ` Alexander Bulekov
2020-09-07 21:52       ` Havard Skinnemoen
2020-08-25  0:17 ` [PATCH v8 09/14] hw/mem: Stubbed out NPCM7xx Memory Controller model Havard Skinnemoen via
2020-08-25  0:17 ` [PATCH v8 10/14] hw/ssi: NPCM7xx Flash Interface Unit device model Havard Skinnemoen via
2020-08-25  0:17 ` [PATCH v8 11/14] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj Havard Skinnemoen via
2020-08-25  0:17 ` [PATCH v8 12/14] hw/arm/npcm7xx: add board setup stub for CPU and UART clocks Havard Skinnemoen via
2020-09-04  9:34   ` Philippe Mathieu-Daudé
2020-08-25  0:17 ` [PATCH v8 13/14] docs/system: Add Nuvoton machine documentation Havard Skinnemoen via
2020-09-03 19:11   ` Philippe Mathieu-Daudé
2020-08-25  0:17 ` [PATCH v8 14/14] tests/acceptance: console boot tests for quanta-gsj Havard Skinnemoen via
2020-09-03 18:20 ` [PATCH v8 00/14] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Philippe Mathieu-Daudé
2020-09-08 15:02 ` Alexander Bulekov
2020-09-08 15:52   ` Philippe Mathieu-Daudé
2020-09-08 16:58     ` Philippe Mathieu-Daudé
2020-09-08 19:52       ` Havard Skinnemoen
2020-09-09  1:32         ` Havard Skinnemoen
2020-09-11  0:03           ` Havard Skinnemoen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).