diff mbox series

[RFC,v2,1/7] hw/misc: Add device to help managing aliased memory regions

Message ID 20210419094329.1402767-2-f4bug@amsat.org (mailing list archive)
State New, archived
Headers show
Series hw/misc: Add memory_region_add_subregion_aliased() helper [pflash part] | expand

Commit Message

Philippe Mathieu-Daudé April 19, 2021, 9:43 a.m. UTC
// TODO explain here how buses work? when some address lines are
// not bound we get memory aliasing, high addresses are masked.
// etc...

Add a helper to manage this use case easily.

For example a having @span_size = @region_size / 4 we get such mapping:

          ^-----------^
          |           |
          |           |
          | +-------+ |                 +---------+          <--+
          |           |                 +---------+             |
          |           |                 |         |             |
          |           |   +-----------> | alias#3 |             |
          |           |   |             |         |             |
          |           |   |             +---------+             |
          |           |   |             +---------+             |
          |           |   |             |         |             |
          |           |   |   +-------> | alias#2 |             |
          |           |   |   |         |         |             |region
          | container |   |   |         +---------+             | size
          |           |   |   |         +---------+             |
          |           |   |   |         |         |             |
          |           |   |   |  +----> | alias#1 |             |
          |           |   |   |  |      |         |             |
          |           |   |   |  |      +---------+  <--+       |
          |           | +-+---+--+--+   +---------+     |       |
          |           | |           |   |         |     |span   |
          |           | | subregion +-> | alias#0 |     |size   |
   offset |           | |           |   |         |     |       |
   +----> | +-------+ | +-----------+   +---------+  <--+    <--+
   |      |           |
   |      |           |
   |      |           |
   |      |           |
   |      |           |
   |      ^-----------^

Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
---
Not really RFC, simply that I'v to add the technical description,
but I'd like to know if there could be a possibility to not accept
this device (because I missed something) before keeping working on
it. So far it is only used in hobbyist boards.

Cc: Peter Xu <peterx@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
---
 include/hw/misc/aliased_region.h |  87 ++++++++++++++++++++
 hw/misc/aliased_region.c         | 132 +++++++++++++++++++++++++++++++
 MAINTAINERS                      |   6 ++
 hw/misc/Kconfig                  |   3 +
 hw/misc/meson.build              |   1 +
 5 files changed, 229 insertions(+)
 create mode 100644 include/hw/misc/aliased_region.h
 create mode 100644 hw/misc/aliased_region.c

Comments

Richard Henderson April 22, 2021, 1:33 a.m. UTC | #1
On 4/19/21 2:43 AM, Philippe Mathieu-Daudé wrote:
> Not really RFC, simply that I'v to add the technical description,
> but I'd like to know if there could be a possibility to not accept
> this device (because I missed something) before keeping working on
> it. So far it is only used in hobbyist boards.
> 
> Cc: Peter Xu<peterx@redhat.com>
> Cc: Paolo Bonzini<pbonzini@redhat.com>
> ---
>   include/hw/misc/aliased_region.h |  87 ++++++++++++++++++++
>   hw/misc/aliased_region.c         | 132 +++++++++++++++++++++++++++++++
>   MAINTAINERS                      |   6 ++
>   hw/misc/Kconfig                  |   3 +
>   hw/misc/meson.build              |   1 +
>   5 files changed, 229 insertions(+)
>   create mode 100644 include/hw/misc/aliased_region.h
>   create mode 100644 hw/misc/aliased_region.c

Looks reasonable to me.


> +    subregion_bits = 64 - clz64(s->span_size - 1);
> +    s->mem.count = s->region_size >> subregion_bits;
> +    assert(s->mem.count > 1);
> +    subregion_size = 1ULL << subregion_bits;

So... subregion_size = pow2floor(s->span_size)?

Why use a floor-ish computation here and pow2ceil later in aliased_mr_realize? 
  Why use either floor or ceil as opposed to asserting that the size is in fact 
a power of 2?  Why must the region be a power of 2, as opposed to e.g. a 
multiple of page_size?  Or just the most basic requirement that region_size be 
a multiple of span_size?


r~
Philippe Mathieu-Daudé July 6, 2021, 9:24 p.m. UTC | #2
On 4/22/21 3:33 AM, Richard Henderson wrote:
> On 4/19/21 2:43 AM, Philippe Mathieu-Daudé wrote:
>> Not really RFC, simply that I'v to add the technical description,
>> but I'd like to know if there could be a possibility to not accept
>> this device (because I missed something) before keeping working on
>> it. So far it is only used in hobbyist boards.
>>
>> Cc: Peter Xu<peterx@redhat.com>
>> Cc: Paolo Bonzini<pbonzini@redhat.com>
>> ---
>>   include/hw/misc/aliased_region.h |  87 ++++++++++++++++++++
>>   hw/misc/aliased_region.c         | 132 +++++++++++++++++++++++++++++++
>>   MAINTAINERS                      |   6 ++
>>   hw/misc/Kconfig                  |   3 +
>>   hw/misc/meson.build              |   1 +
>>   5 files changed, 229 insertions(+)
>>   create mode 100644 include/hw/misc/aliased_region.h
>>   create mode 100644 hw/misc/aliased_region.c
> 
> Looks reasonable to me.
> 
> 
>> +    subregion_bits = 64 - clz64(s->span_size - 1);
>> +    s->mem.count = s->region_size >> subregion_bits;
>> +    assert(s->mem.count > 1);
>> +    subregion_size = 1ULL << subregion_bits;
> 
> So... subregion_size = pow2floor(s->span_size)?

No... should be subregion_size = pow2ceil(s->span_size).

> Why use a floor-ish computation here and pow2ceil later in
> aliased_mr_realize?

I missed it :)

> Why use either floor or ceil as opposed to
> asserting that the size is in fact a power of 2?

Unfortunately not all memory regions are power of 2 :(

See for example the armv7m_systick device (size 0xe0).

> Why must the region be
> a power of 2, as opposed to e.g. a multiple of page_size?

I/O regions don't have to be multiple of page_size... See
AVR devices for example:

(qemu) info mtree
address-space: memory
  0000000000000000-ffffffffffffffff (prio 0, i/o): system
    0000000000000000-0000000000007fff (prio 0, rom): flash
    0000000000800000-00000000008000ff (prio -1234, i/o): I/O
    0000000000800023-0000000000800025 (prio -1000, i/o): atmega-gpio-b
    0000000000800026-0000000000800028 (prio -1000, i/o): atmega-gpio-c
    0000000000800029-000000000080002b (prio -1000, i/o): atmega-gpio-d
    0000000000800035-0000000000800035 (prio -1000, i/o): avr-timer8-intflag
    0000000000800036-0000000000800036 (prio 0, i/o): avr-timer16-intflag
    0000000000800037-0000000000800037 (prio -1000, i/o): avr-timer8-intflag
    000000000080003f-0000000000800041 (prio -1000, i/o): avr-eeprom
    0000000000800044-0000000000800048 (prio -1000, i/o): avr-timer8
    000000000080004c-000000000080004e (prio -1000, i/o): avr-spi
    0000000000800060-0000000000800060 (prio -1000, i/o): avr-watchdog
    0000000000800064-0000000000800064 (prio 0, i/o): avr-power
    000000000080006e-000000000080006e (prio -1000, i/o): avr-timer8-intmask
    000000000080006f-000000000080006f (prio 0, i/o): avr-timer16-intmask
    0000000000800070-0000000000800070 (prio -1000, i/o): avr-timer8-intmask
    0000000000800074-0000000000800075 (prio -1000, i/o): avr-ext-mem-ctrl
    0000000000800078-000000000080007f (prio -1000, i/o): avr-adc
    0000000000800080-000000000080008d (prio 0, i/o): avr-timer16
    00000000008000b0-00000000008000b4 (prio -1000, i/o): avr-timer8
    00000000008000b8-00000000008000bd (prio -1000, i/o): avr-twi
    00000000008000c0-00000000008000c6 (prio 0, i/o): avr-usart
    0000000000800100-00000000008008ff (prio 0, ram): sram

> Or just the
> most basic requirement that region_size be a multiple of span_size?

OK.

Thanks for the review! Now I need to fill the documentation part :/

Phil.
diff mbox series

Patch

diff --git a/include/hw/misc/aliased_region.h b/include/hw/misc/aliased_region.h
new file mode 100644
index 00000000000..0ce0d5d1cef
--- /dev/null
+++ b/include/hw/misc/aliased_region.h
@@ -0,0 +1,87 @@ 
+/*
+ * Aliased memory regions
+ *
+ * Copyright (c) 2018  Philippe Mathieu-Daudé <f4bug@amsat.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MISC_ALIASED_REGION_H
+#define HW_MISC_ALIASED_REGION_H
+
+#include "exec/memory.h"
+#include "hw/sysbus.h"
+
+#define TYPE_ALIASED_REGION "aliased-memory-region"
+OBJECT_DECLARE_SIMPLE_TYPE(AliasedRegionState, ALIASED_REGION)
+
+struct AliasedRegionState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion container;
+    uint64_t region_size;
+    uint64_t span_size;
+    MemoryRegion *mr;
+
+    struct {
+        size_t count;
+        MemoryRegion *alias;
+    } mem;
+};
+
+/**
+ * memory_region_add_subregion_aliased:
+ * @container: the #MemoryRegion to contain the aliased subregions.
+ * @offset: the offset relative to @container where the aliased subregion
+ *          are added.
+ * @region_size: size of the region containing the aliased subregions.
+ * @subregion: the subregion to be aliased.
+ * @span_size: size between each aliased subregion
+ *
+ * This utility function creates and maps an instance of aliased-memory-region,
+ * which is a dummy device of a single region which simply contains multiple
+ * aliases of the provided @subregion, spanned over the @region_size every
+ * @span_size. The device is mapped at @offset within @container.
+ *
+ * For example a having @span_size = @region_size / 4 we get such mapping:
+ *
+ *               +-----------+
+ *               |           |
+ *               |           |
+ *               | +-------+ |                 +---------+          <--+
+ *               |           |                 +---------+             |
+ *               |           |                 |         |             |
+ *               |           |   +-----------> | alias#3 |             |
+ *               |           |   |             |         |             |
+ *               |           |   |             +---------+             |
+ *               |           |   |             +---------+             |
+ *               |           |   |             |         |             |
+ *               |           |   |   +-------> | alias#2 |             |
+ *               |           |   |   |         |         |             |region
+ *               | container |   |   |         +---------+             | size
+ *               |           |   |   |         +---------+             |
+ *               |           |   |   |         |         |             |
+ *               |           |   |   |  +----> | alias#1 |             |
+ *               |           |   |   |  |      |         |             |
+ *               |           |   |   |  |      +---------+  <--+       |
+ *               |           | +-+---+--+--+   +---------+     |       |
+ *               |           | |           |   |         |     |span   |
+ *               |           | | subregion +-> | alias#0 |     |size   |
+ *        offset |           | |           |   |         |     |       |
+ *        +----> | +-------+ | +-----------+   +---------+  <--+    <--+
+ *        |      |           |
+ *        |      |           |
+ *        |      |           |
+ *        |      |           |
+ *        |      |           |
+ *        +      +-----------+
+ */
+void memory_region_add_subregion_aliased(MemoryRegion *container,
+                                         hwaddr offset,
+                                         uint64_t region_size,
+                                         MemoryRegion *subregion,
+                                         uint64_t span_size);
+
+#endif
diff --git a/hw/misc/aliased_region.c b/hw/misc/aliased_region.c
new file mode 100644
index 00000000000..3132276af29
--- /dev/null
+++ b/hw/misc/aliased_region.c
@@ -0,0 +1,132 @@ 
+/*
+ * Aliased memory regions
+ *
+ * Copyright (c) 2018  Philippe Mathieu-Daudé <f4bug@amsat.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "hw/misc/aliased_region.h"
+#include "hw/qdev-properties.h"
+
+static void aliased_mem_realize(AliasedRegionState *s, const char *mr_name)
+{
+    uint64_t subregion_size;
+    int subregion_bits;
+
+    memory_region_init(&s->container, OBJECT(s), mr_name, s->region_size);
+
+    subregion_bits = 64 - clz64(s->span_size - 1);
+    s->mem.count = s->region_size >> subregion_bits;
+    assert(s->mem.count > 1);
+    subregion_size = 1ULL << subregion_bits;
+
+    s->mem.alias = g_new(MemoryRegion, s->mem.count);
+    for (size_t i = 0; i < s->mem.count; i++) {
+        g_autofree char *name = g_strdup_printf("%s [#%zu/%zu]",
+                                                memory_region_name(s->mr),
+                                                i, s->mem.count);
+        memory_region_init_alias(&s->mem.alias[i], OBJECT(s), name,
+                                 s->mr, 0, s->span_size);
+        memory_region_add_subregion(&s->container, i * subregion_size,
+                                    &s->mem.alias[i]);
+    }
+}
+
+static void aliased_mr_realize(DeviceState *dev, Error **errp)
+{
+    AliasedRegionState *s = ALIASED_REGION(dev);
+    g_autofree char *name = NULL, *span = NULL;
+
+    if (s->region_size == 0) {
+        error_setg(errp, "property 'region-size' not specified or zero");
+        return;
+    }
+
+    if (s->mr == NULL) {
+        error_setg(errp, "property 'iomem' not specified");
+        return;
+    }
+
+    if (!s->span_size) {
+        s->span_size = pow2ceil(memory_region_size(s->mr));
+    } else if (!is_power_of_2(s->span_size)) {
+        error_setg(errp, "property 'span-size' must be a power of 2");
+        return;
+    }
+
+    span = size_to_str(s->span_size);
+    name = g_strdup_printf("masked %s [span of %s]",
+                           memory_region_name(s->mr), span);
+    aliased_mem_realize(s, name);
+    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container);
+}
+
+static void aliased_mr_unrealize(DeviceState *dev)
+{
+    AliasedRegionState *s = ALIASED_REGION(dev);
+
+    g_free(s->mem.alias);
+}
+
+static Property aliased_mr_properties[] = {
+    DEFINE_PROP_UINT64("region-size", AliasedRegionState, region_size, 0),
+    DEFINE_PROP_UINT64("span-size", AliasedRegionState, span_size, 0),
+    DEFINE_PROP_LINK("iomem", AliasedRegionState, mr,
+                     TYPE_MEMORY_REGION, MemoryRegion *),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void aliased_mr_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = aliased_mr_realize;
+    dc->unrealize = aliased_mr_unrealize;
+    /* Reason: needs to be wired up to work */
+    dc->user_creatable = false;
+    device_class_set_props(dc, aliased_mr_properties);
+}
+
+static const TypeInfo aliased_mr_info = {
+    .name = TYPE_ALIASED_REGION,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(AliasedRegionState),
+    .class_init = aliased_mr_class_init,
+};
+
+static void aliased_mr_register_types(void)
+{
+    type_register_static(&aliased_mr_info);
+}
+
+type_init(aliased_mr_register_types)
+
+void memory_region_add_subregion_aliased(MemoryRegion *container,
+                                         hwaddr offset,
+                                         uint64_t region_size,
+                                         MemoryRegion *subregion,
+                                         uint64_t span_size)
+{
+    DeviceState *dev;
+
+    if (!region_size) {
+        region_size = pow2ceil(memory_region_size(container));
+    } else {
+        assert(region_size <= memory_region_size(container));
+    }
+
+    dev = qdev_new(TYPE_ALIASED_REGION);
+    qdev_prop_set_uint64(dev, "region-size", region_size);
+    qdev_prop_set_uint64(dev, "span-size", span_size);
+    object_property_set_link(OBJECT(dev), "iomem", OBJECT(subregion),
+                             &error_abort);
+    sysbus_realize(SYS_BUS_DEVICE(dev), &error_abort);
+
+    memory_region_add_subregion(container, offset,
+                                sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0));
+}
diff --git a/MAINTAINERS b/MAINTAINERS
index 36055f14c59..151c342e338 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2095,6 +2095,12 @@  S: Maintained
 F: include/hw/misc/empty_slot.h
 F: hw/misc/empty_slot.c
 
+Aliased memory region
+M: Philippe Mathieu-Daudé <f4bug@amsat.org>
+S: Maintained
+F: include/hw/misc/aliased_region.h
+F: hw/misc/aliased_region.c
+
 Standard VGA
 M: Gerd Hoffmann <kraxel@redhat.com>
 S: Maintained
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index c71ed258204..ca51b99989e 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -151,6 +151,9 @@  config AUX
 config UNIMP
     bool
 
+config ALIASED_REGION
+    bool
+
 config LED
     bool
 
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 21034dc60a8..e65541b835f 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -12,6 +12,7 @@ 
 softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c'))
 softmmu_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 softmmu_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
+softmmu_ss.add(when: 'CONFIG_ALIASED_REGION', if_true: files('aliased_region.c'))
 softmmu_ss.add(when: 'CONFIG_LED', if_true: files('led.c'))
 softmmu_ss.add(when: 'CONFIG_PVPANIC_COMMON', if_true: files('pvpanic.c'))