diff mbox series

[1/3] hw/intc/loongson_ipi_common: Add loongson ipi common class

Message ID 20240703021245.3752786-2-maobibo@loongson.cn
State New
Headers show
Series Replace loongson ipi with loongarch ipi | expand

Commit Message

maobibo July 3, 2024, 2:12 a.m. UTC
Loongson ipi common class and instance is created here, it comes
from file loongson_ipi mostly. For the new added loongson ipi
common class, there is four interfaces defined here:
 1. Interfaces pre_save/post_load are used for future kvm child class
 2. Interface get_iocsr_as can be used for different architectures,
now MIPS 3A4000 and LoongArch 3A5000 machine use this ip, can inherit
this common class.
 3. Interace cpu_by_arch_id is added, by default generic function
cpu_by_arch_id() is used to search vcpu from physical cpuid, it is
generic searching method. Different machine may define another search
method such binary searching method.

Signed-off-by: Bibo Mao <maobibo@loongson.cn>
---
 hw/intc/loongson_ipi_common.c         | 394 ++++++++++++++++++++++++++
 include/hw/intc/loongson_ipi_common.h |  71 +++++
 2 files changed, 465 insertions(+)
 create mode 100644 hw/intc/loongson_ipi_common.c
 create mode 100644 include/hw/intc/loongson_ipi_common.h

Comments

Jiaxun Yang July 3, 2024, 6:16 a.m. UTC | #1
在2024年7月3日七月 上午10:12,Bibo Mao写道:
> Loongson ipi common class and instance is created here, it comes
> from file loongson_ipi mostly. For the new added loongson ipi
> common class, there is four interfaces defined here:
>  1. Interfaces pre_save/post_load are used for future kvm child class
>  2. Interface get_iocsr_as can be used for different architectures,
> now MIPS 3A4000 and LoongArch 3A5000 machine use this ip, can inherit
> this common class.

Please consider MMIO implementation here as well. Can you demonstrate
how would you share implementation with MMIO based IPI? In current
implementation we share memory R/W callbacks but in your implementation
that's nolonger possible.

If current implementation is hindering your future plan can you elaborate so we
can work on a resolution.

I'm happy to help with devlopment and testing.

>  3. Interace cpu_by_arch_id is added, by default generic function
> cpu_by_arch_id() is used to search vcpu from physical cpuid, it is
> generic searching method. Different machine may define another search
> method such binary searching method.

If you are going to implement some faster searching algorithm why don't we
 make it generic for all architectures?

Thanks
- Jiaxun

>
> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
> ---
>  hw/intc/loongson_ipi_common.c         | 394 ++++++++++++++++++++++++++
>  include/hw/intc/loongson_ipi_common.h |  71 +++++
>  2 files changed, 465 insertions(+)
>  create mode 100644 hw/intc/loongson_ipi_common.c
>  create mode 100644 include/hw/intc/loongson_ipi_common.h
>
> diff --git a/hw/intc/loongson_ipi_common.c 
> b/hw/intc/loongson_ipi_common.c
> new file mode 100644
> index 0000000000..f462f24f32
> --- /dev/null
> +++ b/hw/intc/loongson_ipi_common.c
> @@ -0,0 +1,394 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Loongson ipi interrupt support
> + *
> + * Copyright (C) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/boards.h"
> +#include "hw/sysbus.h"
> +#include "hw/intc/loongson_ipi_common.h"
> +#include "hw/irq.h"
> +#include "hw/qdev-properties.h"
> +#include "qapi/error.h"
> +#include "qemu/log.h"
> +#include "exec/address-spaces.h"
> +#include "exec/memory.h"
> +#include "migration/vmstate.h"
> +#include "trace.h"
> +
> +static MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr,
> +                                           uint64_t *data,
> +                                           unsigned size, MemTxAttrs 
> attrs)
> +{
> +    IPICore *s = opaque;
> +    uint64_t ret = 0;
> +    int index = 0;
> +
> +    addr &= 0xff;
> +    switch (addr) {
> +    case CORE_STATUS_OFF:
> +        ret = s->status;
> +        break;
> +    case CORE_EN_OFF:
> +        ret = s->en;
> +        break;
> +    case CORE_SET_OFF:
> +        ret = 0;
> +        break;
> +    case CORE_CLEAR_OFF:
> +        ret = 0;
> +        break;
> +    case CORE_BUF_20 ... CORE_BUF_38 + 4:
> +        index = (addr - CORE_BUF_20) >> 2;
> +        ret = s->buf[index];
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
> +        break;
> +    }
> +
> +    trace_loongson_ipi_read(size, (uint64_t)addr, ret);
> +    *data = ret;
> +    return MEMTX_OK;
> +}
> +
> +static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr,
> +                                            uint64_t *data,
> +                                            unsigned size, MemTxAttrs 
> attrs)
> +{
> +    LoongsonIPICommonState *ipi = opaque;
> +    IPICore *s;
> +
> +    if (attrs.requester_id >= ipi->num_cpu) {
> +        return MEMTX_DECODE_ERROR;
> +    }
> +
> +    s = &ipi->cpu[attrs.requester_id];
> +    return loongson_ipi_core_readl(s, addr, data, size, attrs);
> +}
> +
> +static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState 
> *cpu,
> +                                 uint64_t val,
> +                                 hwaddr addr, MemTxAttrs attrs)
> +{
> +    int i, mask = 0, data = 0;
> +    AddressSpace *iocsr_as;
> +    LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
> +
> +    iocsr_as = NULL;
> +    if (licc->get_iocsr_as) {
> +        iocsr_as = licc->get_iocsr_as(cpu);
> +    }
> +
> +    if (!iocsr_as) {
> +        return MEMTX_DECODE_ERROR;
> +    }
> +
> +    /*
> +     * bit 27-30 is mask for byte writing,
> +     * if the mask is 0, we need not to do anything.
> +     */
> +    if ((val >> 27) & 0xf) {
> +        data = address_space_ldl_le(iocsr_as, addr, attrs, NULL);
> +        for (i = 0; i < 4; i++) {
> +            /* get mask for byte writing */
> +            if (val & (0x1 << (27 + i))) {
> +                mask |= 0xff << (i * 8);
> +            }
> +        }
> +    }
> +
> +    data &= mask;
> +    data |= (val >> 32) & ~mask;
> +    address_space_stl_le(iocsr_as, addr, data, attrs, NULL);
> +
> +    return MEMTX_OK;
> +}
> +
> +static CPUState *get_cpu_by_arch_id(LoongsonIPICommonState *ipi, 
> uint32_t cpuid)
> +{
> +    LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
> +
> +    if (licc->cpu_by_arch_id) {
> +        return licc->cpu_by_arch_id(cpuid);
> +    }
> +
> +    return cpu_by_arch_id(cpuid);
> +}
> +
> +static MemTxResult mail_send(LoongsonIPICommonState *ipi, uint64_t val,
> +                             MemTxAttrs attrs)
> +{
> +    uint32_t cpuid;
> +    hwaddr addr;
> +    CPUState *cs;
> +
> +    cpuid = extract32(val, 16, 10);
> +    cs = get_cpu_by_arch_id(ipi, cpuid);
> +    if (cs == NULL) {
> +        return MEMTX_DECODE_ERROR;
> +    }
> +
> +    /* override requester_id */
> +    addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
> +    attrs.requester_id = cs->cpu_index;
> +    return send_ipi_data(ipi, cs, val, addr, attrs);
> +}
> +
> +static MemTxResult any_send(LoongsonIPICommonState *ipi, uint64_t val,
> +                            MemTxAttrs attrs)
> +{
> +    uint32_t cpuid;
> +    hwaddr addr;
> +    CPUState *cs;
> +
> +    cpuid = extract32(val, 16, 10);
> +    cs = get_cpu_by_arch_id(ipi, cpuid);
> +    if (cs == NULL) {
> +        return MEMTX_DECODE_ERROR;
> +    }
> +
> +    /* override requester_id */
> +    addr = val & 0xffff;
> +    attrs.requester_id = cs->cpu_index;
> +    return send_ipi_data(ipi, cs, val, addr, attrs);
> +}
> +
> +static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr,
> +                                            uint64_t val, unsigned 
> size,
> +                                            MemTxAttrs attrs)
> +{
> +    IPICore *s = opaque;
> +    LoongsonIPICommonState *ipi = s->ipi;
> +    int index = 0;
> +    uint32_t cpuid;
> +    uint8_t vector;
> +    CPUState *cs;
> +
> +    addr &= 0xff;
> +    trace_loongson_ipi_write(size, (uint64_t)addr, val);
> +    switch (addr) {
> +    case CORE_STATUS_OFF:
> +        qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
> +        break;
> +    case CORE_EN_OFF:
> +        s->en = val;
> +        break;
> +    case CORE_SET_OFF:
> +        s->status |= val;
> +        if (s->status != 0 && (s->status & s->en) != 0) {
> +            qemu_irq_raise(s->irq);
> +        }
> +        break;
> +    case CORE_CLEAR_OFF:
> +        s->status &= ~val;
> +        if (s->status == 0 && s->en != 0) {
> +            qemu_irq_lower(s->irq);
> +        }
> +        break;
> +    case CORE_BUF_20 ... CORE_BUF_38 + 4:
> +        index = (addr - CORE_BUF_20) >> 2;
> +        s->buf[index] = val;
> +        break;
> +    case IOCSR_IPI_SEND:
> +        cpuid = extract32(val, 16, 10);
> +        /* IPI status vector */
> +        vector = extract8(val, 0, 5);
> +        cs = get_cpu_by_arch_id(ipi, cpuid);
> +        if (cs == NULL || cs->cpu_index >= ipi->num_cpu) {
> +            return MEMTX_DECODE_ERROR;
> +        }
> +        loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index], 
> CORE_SET_OFF,
> +                                 BIT(vector), 4, attrs);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
> +        break;
> +    }
> +
> +    return MEMTX_OK;
> +}
> +
> +static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr,
> +                                            uint64_t val, unsigned 
> size,
> +                                            MemTxAttrs attrs)
> +{
> +    LoongsonIPICommonState *ipi = opaque;
> +    IPICore *s;
> +
> +    if (attrs.requester_id >= ipi->num_cpu) {
> +        return MEMTX_DECODE_ERROR;
> +    }
> +
> +    s = &ipi->cpu[attrs.requester_id];
> +    return loongson_ipi_core_writel(s, addr, val, size, attrs);
> +}
> +
> +static const MemoryRegionOps loongson_ipi_iocsr_ops = {
> +    .read_with_attrs = loongson_ipi_iocsr_readl,
> +    .write_with_attrs = loongson_ipi_iocsr_writel,
> +    .impl.min_access_size = 4,
> +    .impl.max_access_size = 4,
> +    .valid.min_access_size = 4,
> +    .valid.max_access_size = 8,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +/* mail send and any send only support writeq */
> +static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, 
> uint64_t val,
> +                                        unsigned size, MemTxAttrs 
> attrs)
> +{
> +    MemTxResult ret = MEMTX_OK;
> +    LoongsonIPICommonState *ipi = opaque;
> +
> +    addr &= 0xfff;
> +    switch (addr) {
> +    case MAIL_SEND_OFFSET:
> +        ret = mail_send(ipi, val, attrs);
> +        break;
> +    case ANY_SEND_OFFSET:
> +        ret = any_send(ipi, val, attrs);
> +        break;
> +    default:
> +       break;
> +    }
> +
> +    return ret;
> +}
> +
> +static const MemoryRegionOps loongson_ipi64_ops = {
> +    .write_with_attrs = loongson_ipi_writeq,
> +    .impl.min_access_size = 8,
> +    .impl.max_access_size = 8,
> +    .valid.min_access_size = 8,
> +    .valid.max_access_size = 8,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +static void loongson_ipi_common_realize(DeviceState *dev, Error **errp)
> +{
> +    LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +    int i;
> +
> +    if (s->num_cpu == 0) {
> +        error_setg(errp, "num-cpu must be at least 1");
> +        return;
> +    }
> +
> +    memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev),
> +                          &loongson_ipi_iocsr_ops,
> +                          s, "loongson_ipi_iocsr", 0x48);
> +
> +    /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */
> +    s->ipi_iocsr_mem.disable_reentrancy_guard = true;
> +
> +    sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
> +
> +    memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev),
> +                          &loongson_ipi64_ops,
> +                          s, "loongson_ipi64_iocsr", 0x118);
> +    sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
> +
> +    s->cpu = g_new0(IPICore, s->num_cpu);
> +    if (s->cpu == NULL) {
> +        error_setg(errp, "Memory allocation for IPICore faile");
> +        return;
> +    }
> +
> +    for (i = 0; i < s->num_cpu; i++) {
> +        s->cpu[i].ipi = s;
> +        qdev_init_gpio_out(dev, &s->cpu[i].irq, 1);
> +    }
> +}
> +
> +static int loongson_ipi_pre_save(void *opaque)
> +{
> +    LoongsonIPICommonState *s = (LoongsonIPICommonState *)opaque;
> +    LoongsonIPICommonClass *c = LOONGSON_IPI_COMMON_GET_CLASS(s);
> +
> +    if (c->pre_save) {
> +        c->pre_save(s);
> +    }
> +
> +    return 0;
> +}
> +
> +static int loongson_ipi_post_load(void *opaque, int version_id)
> +{
> +    LoongsonIPICommonState *s = (LoongsonIPICommonState *)opaque;
> +    LoongsonIPICommonClass *c = LOONGSON_IPI_COMMON_GET_CLASS(s);
> +
> +    if (c->post_load) {
> +        c->post_load(s);
> +    }
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_ipi_core = {
> +    .name = "ipi-single",
> +    .version_id = 2,
> +    .minimum_version_id = 2,
> +    .fields = (const VMStateField[]) {
> +        VMSTATE_UINT32(status, IPICore),
> +        VMSTATE_UINT32(en, IPICore),
> +        VMSTATE_UINT32(set, IPICore),
> +        VMSTATE_UINT32(clear, IPICore),
> +        VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static const VMStateDescription vmstate_loongson_ipi = {
> +    /* Fixed name to keep compatible */
> +    .name = "loongson_ipi",
> +    .pre_save  = loongson_ipi_pre_save,
> +    .post_load = loongson_ipi_post_load,
> +    .version_id = 2,
> +    .minimum_version_id = 2,
> +    .fields = (const VMStateField[]) {
> +        VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, 
> LoongsonIPICommonState,
> +                                       num_cpu, vmstate_ipi_core, 
> IPICore),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static Property ipi_properties[] = {
> +    DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void loongson_ipi_common_class_init(ObjectClass *klass, void 
> *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = loongson_ipi_common_realize;
> +    device_class_set_props(dc, ipi_properties);
> +    dc->vmsd = &vmstate_loongson_ipi;
> +}
> +
> +static void loongson_ipi_common_finalize(Object *obj)
> +{
> +    LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(obj);
> +
> +    g_free(s->cpu);
> +}
> +
> +static const TypeInfo loongson_ipi_common_info = {
> +    .name          = TYPE_LOONGSON_IPI_COMMON,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(LoongsonIPICommonState),
> +    .class_size    = sizeof(LoongsonIPICommonClass),
> +    .class_init    = loongson_ipi_common_class_init,
> +    .instance_finalize = loongson_ipi_common_finalize,
> +    .abstract = true,
> +};
> +
> +static void loongson_ipi_common_register_types(void)
> +{
> +    type_register_static(&loongson_ipi_common_info);
> +}
> +
> +type_init(loongson_ipi_common_register_types)
> diff --git a/include/hw/intc/loongson_ipi_common.h 
> b/include/hw/intc/loongson_ipi_common.h
> new file mode 100644
> index 0000000000..1f074863e6
> --- /dev/null
> +++ b/include/hw/intc/loongson_ipi_common.h
> @@ -0,0 +1,71 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Loongson ipi interrupt header files
> + *
> + * Copyright (C) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#ifndef HW_LOONGSON_IPI_COMMON_H
> +#define HW_LOONGSON_IPI_COMMON_H
> +
> +#include "hw/sysbus.h"
> +#include "hw/core/cpu.h"
> +#include "qom/object.h"
> +
> +/* Mainy used by iocsr read and write */
> +#define SMP_IPI_MAILBOX      0x1000ULL
> +#define CORE_STATUS_OFF       0x0
> +#define CORE_EN_OFF           0x4
> +#define CORE_SET_OFF          0x8
> +#define CORE_CLEAR_OFF        0xc
> +#define CORE_BUF_20           0x20
> +#define CORE_BUF_28           0x28
> +#define CORE_BUF_30           0x30
> +#define CORE_BUF_38           0x38
> +#define IOCSR_IPI_SEND        0x40
> +#define IOCSR_MAIL_SEND       0x48
> +#define IOCSR_ANY_SEND        0x158
> +
> +#define MAIL_SEND_ADDR        (SMP_IPI_MAILBOX + IOCSR_MAIL_SEND)
> +#define MAIL_SEND_OFFSET      0
> +#define ANY_SEND_OFFSET       (IOCSR_ANY_SEND - IOCSR_MAIL_SEND)
> +
> +#define IPI_MBX_NUM           4
> +
> +#define TYPE_LOONGSON_IPI_COMMON "loongson_ipi_common"
> +typedef struct LoongsonIPICommonClass LoongsonIPICommonClass;
> +typedef struct LoongsonIPICommonState LoongsonIPICommonState;
> +DECLARE_OBJ_CHECKERS(LoongsonIPICommonState, LoongsonIPICommonClass,
> +                     LOONGSON_IPI_COMMON, TYPE_LOONGSON_IPI_COMMON)
> +
> +typedef struct IPICore {
> +    LoongsonIPICommonState *ipi;
> +    uint32_t status;
> +    uint32_t en;
> +    uint32_t set;
> +    uint32_t clear;
> +    /* 64bit buf divide into 2 32bit buf */
> +    uint32_t buf[IPI_MBX_NUM * 2];
> +    qemu_irq irq;
> +} IPICore;
> +
> +struct LoongsonIPICommonState {
> +    SysBusDevice parent_obj;
> +    MemoryRegion ipi_iocsr_mem;
> +    MemoryRegion ipi64_iocsr_mem;
> +    uint32_t num_cpu;
> +    IPICore *cpu;
> +};
> +
> +struct LoongsonIPICommonClass {
> +    /*< private >*/
> +    SysBusDeviceClass parent_class;
> +    /*< public >*/
> +
> +    void (*pre_save)(LoongsonIPICommonState *s);
> +    void (*post_load)(LoongsonIPICommonState *s);
> +    AddressSpace *(*get_iocsr_as)(CPUState *cpu);
> +    CPUState *(*cpu_by_arch_id)(int64_t id);
> +};
> +
> +#endif
> -- 
> 2.39.3
maobibo July 3, 2024, 6:40 a.m. UTC | #2
On 2024/7/3 下午2:16, Jiaxun Yang wrote:
> 
> 
> 在2024年7月3日七月 上午10:12,Bibo Mao写道:
>> Loongson ipi common class and instance is created here, it comes
>> from file loongson_ipi mostly. For the new added loongson ipi
>> common class, there is four interfaces defined here:
>>   1. Interfaces pre_save/post_load are used for future kvm child class
>>   2. Interface get_iocsr_as can be used for different architectures,
>> now MIPS 3A4000 and LoongArch 3A5000 machine use this ip, can inherit
>> this common class.
> 
> Please consider MMIO implementation here as well. Can you demonstrate
> how would you share implementation with MMIO based IPI? In current
> implementation we share memory R/W callbacks but in your implementation
> that's nolonger possible.
Jiaxun,

Thanks for your quick response.

MMIO is loongson ipi specific, it is not necessary to put into common 
function. Functions loongson_ipi_core_readl/loongson_ipi_core_writel can 
be exported in header file include/hw/intc/loongson_ipi_common.h, or get 
MemoryRegionOps of first memoryregion of loongson_ipi instance.

There is pseudo code:

static void loongson_ipi_realize(DeviceState *dev, Error **errp)
{
     LoongarchIPIState *s = LOONGARCH_IPI(dev);
     LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(s);
     Error *local_err = NULL;

     lic->parent_realize(dev, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
     }

     ....
     *do mmio specific implematation in loongson ipi itself*
}

static void loongson_ipi_class_init(ObjectClass *klass, void *data)
{
     DeviceClass *dc = DEVICE_CLASS(klass);
     LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass);
     LoongarchIPIClass *lic = LOONGARCH_IPI_CLASS(klass);

     device_class_set_parent_realize(dc, loongson_ipi_realize, 
&lic->parent_realize);
     licc->get_iocsr_as = get_iocsr_as;
}

> 
> If current implementation is hindering your future plan can you elaborate so we
> can work on a resolution.
> 
> I'm happy to help with devlopment and testing.
> 
>>   3. Interace cpu_by_arch_id is added, by default generic function
>> cpu_by_arch_id() is used to search vcpu from physical cpuid, it is
>> generic searching method. Different machine may define another search
>> method such binary searching method.
> 
> If you are going to implement some faster searching algorithm why don't we
>   make it generic for all architectures?
It depends on the detailed physical id layout, is physical id is growing 
up with logic cpu id or irrelative with logic cpu id? Different 
architecture has different logic definition about physical id.

Regards
Bibo Mao

> 
> Thanks
> - Jiaxun
> 
>>
>> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
>> ---
>>   hw/intc/loongson_ipi_common.c         | 394 ++++++++++++++++++++++++++
>>   include/hw/intc/loongson_ipi_common.h |  71 +++++
>>   2 files changed, 465 insertions(+)
>>   create mode 100644 hw/intc/loongson_ipi_common.c
>>   create mode 100644 include/hw/intc/loongson_ipi_common.h
>>
>> diff --git a/hw/intc/loongson_ipi_common.c
>> b/hw/intc/loongson_ipi_common.c
>> new file mode 100644
>> index 0000000000..f462f24f32
>> --- /dev/null
>> +++ b/hw/intc/loongson_ipi_common.c
>> @@ -0,0 +1,394 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Loongson ipi interrupt support
>> + *
>> + * Copyright (C) 2021 Loongson Technology Corporation Limited
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "hw/boards.h"
>> +#include "hw/sysbus.h"
>> +#include "hw/intc/loongson_ipi_common.h"
>> +#include "hw/irq.h"
>> +#include "hw/qdev-properties.h"
>> +#include "qapi/error.h"
>> +#include "qemu/log.h"
>> +#include "exec/address-spaces.h"
>> +#include "exec/memory.h"
>> +#include "migration/vmstate.h"
>> +#include "trace.h"
>> +
>> +static MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr,
>> +                                           uint64_t *data,
>> +                                           unsigned size, MemTxAttrs
>> attrs)
>> +{
>> +    IPICore *s = opaque;
>> +    uint64_t ret = 0;
>> +    int index = 0;
>> +
>> +    addr &= 0xff;
>> +    switch (addr) {
>> +    case CORE_STATUS_OFF:
>> +        ret = s->status;
>> +        break;
>> +    case CORE_EN_OFF:
>> +        ret = s->en;
>> +        break;
>> +    case CORE_SET_OFF:
>> +        ret = 0;
>> +        break;
>> +    case CORE_CLEAR_OFF:
>> +        ret = 0;
>> +        break;
>> +    case CORE_BUF_20 ... CORE_BUF_38 + 4:
>> +        index = (addr - CORE_BUF_20) >> 2;
>> +        ret = s->buf[index];
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
>> +        break;
>> +    }
>> +
>> +    trace_loongson_ipi_read(size, (uint64_t)addr, ret);
>> +    *data = ret;
>> +    return MEMTX_OK;
>> +}
>> +
>> +static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr,
>> +                                            uint64_t *data,
>> +                                            unsigned size, MemTxAttrs
>> attrs)
>> +{
>> +    LoongsonIPICommonState *ipi = opaque;
>> +    IPICore *s;
>> +
>> +    if (attrs.requester_id >= ipi->num_cpu) {
>> +        return MEMTX_DECODE_ERROR;
>> +    }
>> +
>> +    s = &ipi->cpu[attrs.requester_id];
>> +    return loongson_ipi_core_readl(s, addr, data, size, attrs);
>> +}
>> +
>> +static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState
>> *cpu,
>> +                                 uint64_t val,
>> +                                 hwaddr addr, MemTxAttrs attrs)
>> +{
>> +    int i, mask = 0, data = 0;
>> +    AddressSpace *iocsr_as;
>> +    LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
>> +
>> +    iocsr_as = NULL;
>> +    if (licc->get_iocsr_as) {
>> +        iocsr_as = licc->get_iocsr_as(cpu);
>> +    }
>> +
>> +    if (!iocsr_as) {
>> +        return MEMTX_DECODE_ERROR;
>> +    }
>> +
>> +    /*
>> +     * bit 27-30 is mask for byte writing,
>> +     * if the mask is 0, we need not to do anything.
>> +     */
>> +    if ((val >> 27) & 0xf) {
>> +        data = address_space_ldl_le(iocsr_as, addr, attrs, NULL);
>> +        for (i = 0; i < 4; i++) {
>> +            /* get mask for byte writing */
>> +            if (val & (0x1 << (27 + i))) {
>> +                mask |= 0xff << (i * 8);
>> +            }
>> +        }
>> +    }
>> +
>> +    data &= mask;
>> +    data |= (val >> 32) & ~mask;
>> +    address_space_stl_le(iocsr_as, addr, data, attrs, NULL);
>> +
>> +    return MEMTX_OK;
>> +}
>> +
>> +static CPUState *get_cpu_by_arch_id(LoongsonIPICommonState *ipi,
>> uint32_t cpuid)
>> +{
>> +    LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
>> +
>> +    if (licc->cpu_by_arch_id) {
>> +        return licc->cpu_by_arch_id(cpuid);
>> +    }
>> +
>> +    return cpu_by_arch_id(cpuid);
>> +}
>> +
>> +static MemTxResult mail_send(LoongsonIPICommonState *ipi, uint64_t val,
>> +                             MemTxAttrs attrs)
>> +{
>> +    uint32_t cpuid;
>> +    hwaddr addr;
>> +    CPUState *cs;
>> +
>> +    cpuid = extract32(val, 16, 10);
>> +    cs = get_cpu_by_arch_id(ipi, cpuid);
>> +    if (cs == NULL) {
>> +        return MEMTX_DECODE_ERROR;
>> +    }
>> +
>> +    /* override requester_id */
>> +    addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
>> +    attrs.requester_id = cs->cpu_index;
>> +    return send_ipi_data(ipi, cs, val, addr, attrs);
>> +}
>> +
>> +static MemTxResult any_send(LoongsonIPICommonState *ipi, uint64_t val,
>> +                            MemTxAttrs attrs)
>> +{
>> +    uint32_t cpuid;
>> +    hwaddr addr;
>> +    CPUState *cs;
>> +
>> +    cpuid = extract32(val, 16, 10);
>> +    cs = get_cpu_by_arch_id(ipi, cpuid);
>> +    if (cs == NULL) {
>> +        return MEMTX_DECODE_ERROR;
>> +    }
>> +
>> +    /* override requester_id */
>> +    addr = val & 0xffff;
>> +    attrs.requester_id = cs->cpu_index;
>> +    return send_ipi_data(ipi, cs, val, addr, attrs);
>> +}
>> +
>> +static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr,
>> +                                            uint64_t val, unsigned
>> size,
>> +                                            MemTxAttrs attrs)
>> +{
>> +    IPICore *s = opaque;
>> +    LoongsonIPICommonState *ipi = s->ipi;
>> +    int index = 0;
>> +    uint32_t cpuid;
>> +    uint8_t vector;
>> +    CPUState *cs;
>> +
>> +    addr &= 0xff;
>> +    trace_loongson_ipi_write(size, (uint64_t)addr, val);
>> +    switch (addr) {
>> +    case CORE_STATUS_OFF:
>> +        qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
>> +        break;
>> +    case CORE_EN_OFF:
>> +        s->en = val;
>> +        break;
>> +    case CORE_SET_OFF:
>> +        s->status |= val;
>> +        if (s->status != 0 && (s->status & s->en) != 0) {
>> +            qemu_irq_raise(s->irq);
>> +        }
>> +        break;
>> +    case CORE_CLEAR_OFF:
>> +        s->status &= ~val;
>> +        if (s->status == 0 && s->en != 0) {
>> +            qemu_irq_lower(s->irq);
>> +        }
>> +        break;
>> +    case CORE_BUF_20 ... CORE_BUF_38 + 4:
>> +        index = (addr - CORE_BUF_20) >> 2;
>> +        s->buf[index] = val;
>> +        break;
>> +    case IOCSR_IPI_SEND:
>> +        cpuid = extract32(val, 16, 10);
>> +        /* IPI status vector */
>> +        vector = extract8(val, 0, 5);
>> +        cs = get_cpu_by_arch_id(ipi, cpuid);
>> +        if (cs == NULL || cs->cpu_index >= ipi->num_cpu) {
>> +            return MEMTX_DECODE_ERROR;
>> +        }
>> +        loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index],
>> CORE_SET_OFF,
>> +                                 BIT(vector), 4, attrs);
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
>> +        break;
>> +    }
>> +
>> +    return MEMTX_OK;
>> +}
>> +
>> +static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr,
>> +                                            uint64_t val, unsigned
>> size,
>> +                                            MemTxAttrs attrs)
>> +{
>> +    LoongsonIPICommonState *ipi = opaque;
>> +    IPICore *s;
>> +
>> +    if (attrs.requester_id >= ipi->num_cpu) {
>> +        return MEMTX_DECODE_ERROR;
>> +    }
>> +
>> +    s = &ipi->cpu[attrs.requester_id];
>> +    return loongson_ipi_core_writel(s, addr, val, size, attrs);
>> +}
>> +
>> +static const MemoryRegionOps loongson_ipi_iocsr_ops = {
>> +    .read_with_attrs = loongson_ipi_iocsr_readl,
>> +    .write_with_attrs = loongson_ipi_iocsr_writel,
>> +    .impl.min_access_size = 4,
>> +    .impl.max_access_size = 4,
>> +    .valid.min_access_size = 4,
>> +    .valid.max_access_size = 8,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +};
>> +
>> +/* mail send and any send only support writeq */
>> +static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr,
>> uint64_t val,
>> +                                        unsigned size, MemTxAttrs
>> attrs)
>> +{
>> +    MemTxResult ret = MEMTX_OK;
>> +    LoongsonIPICommonState *ipi = opaque;
>> +
>> +    addr &= 0xfff;
>> +    switch (addr) {
>> +    case MAIL_SEND_OFFSET:
>> +        ret = mail_send(ipi, val, attrs);
>> +        break;
>> +    case ANY_SEND_OFFSET:
>> +        ret = any_send(ipi, val, attrs);
>> +        break;
>> +    default:
>> +       break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static const MemoryRegionOps loongson_ipi64_ops = {
>> +    .write_with_attrs = loongson_ipi_writeq,
>> +    .impl.min_access_size = 8,
>> +    .impl.max_access_size = 8,
>> +    .valid.min_access_size = 8,
>> +    .valid.max_access_size = 8,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +};
>> +
>> +static void loongson_ipi_common_realize(DeviceState *dev, Error **errp)
>> +{
>> +    LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>> +    int i;
>> +
>> +    if (s->num_cpu == 0) {
>> +        error_setg(errp, "num-cpu must be at least 1");
>> +        return;
>> +    }
>> +
>> +    memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev),
>> +                          &loongson_ipi_iocsr_ops,
>> +                          s, "loongson_ipi_iocsr", 0x48);
>> +
>> +    /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */
>> +    s->ipi_iocsr_mem.disable_reentrancy_guard = true;
>> +
>> +    sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
>> +
>> +    memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev),
>> +                          &loongson_ipi64_ops,
>> +                          s, "loongson_ipi64_iocsr", 0x118);
>> +    sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
>> +
>> +    s->cpu = g_new0(IPICore, s->num_cpu);
>> +    if (s->cpu == NULL) {
>> +        error_setg(errp, "Memory allocation for IPICore faile");
>> +        return;
>> +    }
>> +
>> +    for (i = 0; i < s->num_cpu; i++) {
>> +        s->cpu[i].ipi = s;
>> +        qdev_init_gpio_out(dev, &s->cpu[i].irq, 1);
>> +    }
>> +}
>> +
>> +static int loongson_ipi_pre_save(void *opaque)
>> +{
>> +    LoongsonIPICommonState *s = (LoongsonIPICommonState *)opaque;
>> +    LoongsonIPICommonClass *c = LOONGSON_IPI_COMMON_GET_CLASS(s);
>> +
>> +    if (c->pre_save) {
>> +        c->pre_save(s);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int loongson_ipi_post_load(void *opaque, int version_id)
>> +{
>> +    LoongsonIPICommonState *s = (LoongsonIPICommonState *)opaque;
>> +    LoongsonIPICommonClass *c = LOONGSON_IPI_COMMON_GET_CLASS(s);
>> +
>> +    if (c->post_load) {
>> +        c->post_load(s);
>> +    }
>> +    return 0;
>> +}
>> +
>> +static const VMStateDescription vmstate_ipi_core = {
>> +    .name = "ipi-single",
>> +    .version_id = 2,
>> +    .minimum_version_id = 2,
>> +    .fields = (const VMStateField[]) {
>> +        VMSTATE_UINT32(status, IPICore),
>> +        VMSTATE_UINT32(en, IPICore),
>> +        VMSTATE_UINT32(set, IPICore),
>> +        VMSTATE_UINT32(clear, IPICore),
>> +        VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static const VMStateDescription vmstate_loongson_ipi = {
>> +    /* Fixed name to keep compatible */
>> +    .name = "loongson_ipi",
>> +    .pre_save  = loongson_ipi_pre_save,
>> +    .post_load = loongson_ipi_post_load,
>> +    .version_id = 2,
>> +    .minimum_version_id = 2,
>> +    .fields = (const VMStateField[]) {
>> +        VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu,
>> LoongsonIPICommonState,
>> +                                       num_cpu, vmstate_ipi_core,
>> IPICore),
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static Property ipi_properties[] = {
>> +    DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void loongson_ipi_common_class_init(ObjectClass *klass, void
>> *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    dc->realize = loongson_ipi_common_realize;
>> +    device_class_set_props(dc, ipi_properties);
>> +    dc->vmsd = &vmstate_loongson_ipi;
>> +}
>> +
>> +static void loongson_ipi_common_finalize(Object *obj)
>> +{
>> +    LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(obj);
>> +
>> +    g_free(s->cpu);
>> +}
>> +
>> +static const TypeInfo loongson_ipi_common_info = {
>> +    .name          = TYPE_LOONGSON_IPI_COMMON,
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(LoongsonIPICommonState),
>> +    .class_size    = sizeof(LoongsonIPICommonClass),
>> +    .class_init    = loongson_ipi_common_class_init,
>> +    .instance_finalize = loongson_ipi_common_finalize,
>> +    .abstract = true,
>> +};
>> +
>> +static void loongson_ipi_common_register_types(void)
>> +{
>> +    type_register_static(&loongson_ipi_common_info);
>> +}
>> +
>> +type_init(loongson_ipi_common_register_types)
>> diff --git a/include/hw/intc/loongson_ipi_common.h
>> b/include/hw/intc/loongson_ipi_common.h
>> new file mode 100644
>> index 0000000000..1f074863e6
>> --- /dev/null
>> +++ b/include/hw/intc/loongson_ipi_common.h
>> @@ -0,0 +1,71 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Loongson ipi interrupt header files
>> + *
>> + * Copyright (C) 2021 Loongson Technology Corporation Limited
>> + */
>> +
>> +#ifndef HW_LOONGSON_IPI_COMMON_H
>> +#define HW_LOONGSON_IPI_COMMON_H
>> +
>> +#include "hw/sysbus.h"
>> +#include "hw/core/cpu.h"
>> +#include "qom/object.h"
>> +
>> +/* Mainy used by iocsr read and write */
>> +#define SMP_IPI_MAILBOX      0x1000ULL
>> +#define CORE_STATUS_OFF       0x0
>> +#define CORE_EN_OFF           0x4
>> +#define CORE_SET_OFF          0x8
>> +#define CORE_CLEAR_OFF        0xc
>> +#define CORE_BUF_20           0x20
>> +#define CORE_BUF_28           0x28
>> +#define CORE_BUF_30           0x30
>> +#define CORE_BUF_38           0x38
>> +#define IOCSR_IPI_SEND        0x40
>> +#define IOCSR_MAIL_SEND       0x48
>> +#define IOCSR_ANY_SEND        0x158
>> +
>> +#define MAIL_SEND_ADDR        (SMP_IPI_MAILBOX + IOCSR_MAIL_SEND)
>> +#define MAIL_SEND_OFFSET      0
>> +#define ANY_SEND_OFFSET       (IOCSR_ANY_SEND - IOCSR_MAIL_SEND)
>> +
>> +#define IPI_MBX_NUM           4
>> +
>> +#define TYPE_LOONGSON_IPI_COMMON "loongson_ipi_common"
>> +typedef struct LoongsonIPICommonClass LoongsonIPICommonClass;
>> +typedef struct LoongsonIPICommonState LoongsonIPICommonState;
>> +DECLARE_OBJ_CHECKERS(LoongsonIPICommonState, LoongsonIPICommonClass,
>> +                     LOONGSON_IPI_COMMON, TYPE_LOONGSON_IPI_COMMON)
>> +
>> +typedef struct IPICore {
>> +    LoongsonIPICommonState *ipi;
>> +    uint32_t status;
>> +    uint32_t en;
>> +    uint32_t set;
>> +    uint32_t clear;
>> +    /* 64bit buf divide into 2 32bit buf */
>> +    uint32_t buf[IPI_MBX_NUM * 2];
>> +    qemu_irq irq;
>> +} IPICore;
>> +
>> +struct LoongsonIPICommonState {
>> +    SysBusDevice parent_obj;
>> +    MemoryRegion ipi_iocsr_mem;
>> +    MemoryRegion ipi64_iocsr_mem;
>> +    uint32_t num_cpu;
>> +    IPICore *cpu;
>> +};
>> +
>> +struct LoongsonIPICommonClass {
>> +    /*< private >*/
>> +    SysBusDeviceClass parent_class;
>> +    /*< public >*/
>> +
>> +    void (*pre_save)(LoongsonIPICommonState *s);
>> +    void (*post_load)(LoongsonIPICommonState *s);
>> +    AddressSpace *(*get_iocsr_as)(CPUState *cpu);
>> +    CPUState *(*cpu_by_arch_id)(int64_t id);
>> +};
>> +
>> +#endif
>> -- 
>> 2.39.3
>
Jiaxun Yang July 3, 2024, 7:33 a.m. UTC | #3
在2024年7月3日七月 下午2:40,maobibo写道:
[...]
Hi Bobo,
>
> MMIO is loongson ipi specific, it is not necessary to put into common 
> function. Functions loongson_ipi_core_readl/loongson_ipi_core_writel can 
> be exported in header file include/hw/intc/loongson_ipi_common.h, or get 
> MemoryRegionOps of first memoryregion of loongson_ipi instance.
>
> There is pseudo code:

Thanks for your demonstration. I'm still not quite convinced it's worthy to split
but I'm not going to block you if we don't have other oppositions.

Do you mind to finish conversion of loongson_ipi as well? Since you are drafting the
design.

>
> static void loongson_ipi_realize(DeviceState *dev, Error **errp)
> {
>      LoongarchIPIState *s = LOONGARCH_IPI(dev);
>      LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(s);
>      Error *local_err = NULL;
>
>      lic->parent_realize(dev, &local_err);
>      if (local_err) {
>          error_propagate(errp, local_err);
>          return;
>      }
>
>      ....
>      *do mmio specific implematation in loongson ipi itself*
> }
>
> static void loongson_ipi_class_init(ObjectClass *klass, void *data)
> {
>      DeviceClass *dc = DEVICE_CLASS(klass);
>      LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass);
>      LoongarchIPIClass *lic = LOONGARCH_IPI_CLASS(klass);
>
>      device_class_set_parent_realize(dc, loongson_ipi_realize, 
> &lic->parent_realize);
>      licc->get_iocsr_as = get_iocsr_as;
> }
>
>> 
>> If current implementation is hindering your future plan can you elaborate so we
>> can work on a resolution.
>> 
>> I'm happy to help with devlopment and testing.
>> 
>>>   3. Interace cpu_by_arch_id is added, by default generic function
>>> cpu_by_arch_id() is used to search vcpu from physical cpuid, it is
>>> generic searching method. Different machine may define another search
>>> method such binary searching method.
>> 
>> If you are going to implement some faster searching algorithm why don't we
>>   make it generic for all architectures?
> It depends on the detailed physical id layout, is physical id is growing 
> up with logic cpu id or irrelative with logic cpu id? Different 
> architecture has different logic definition about physical id.

For x86' APIC id and RISC-V's hardid they are all somehow linear.
I'd suggest you to post a RFC patch regarding better algorithm.

Thanks
- Jiaxun
>
> Regards
> Bibo Mao
>
>> 
>> Thanks
>> - Jiaxun
>> 
>>>
>>> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
>>> ---
>>>   hw/intc/loongson_ipi_common.c         | 394 ++++++++++++++++++++++++++
>>>   include/hw/intc/loongson_ipi_common.h |  71 +++++
>>>   2 files changed, 465 insertions(+)
>>>   create mode 100644 hw/intc/loongson_ipi_common.c
>>>   create mode 100644 include/hw/intc/loongson_ipi_common.h
>>>
>>> diff --git a/hw/intc/loongson_ipi_common.c
>>> b/hw/intc/loongson_ipi_common.c
>>> new file mode 100644
>>> index 0000000000..f462f24f32
>>> --- /dev/null
>>> +++ b/hw/intc/loongson_ipi_common.c
>>> @@ -0,0 +1,394 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>> +/*
>>> + * Loongson ipi interrupt support
>>> + *
>>> + * Copyright (C) 2021 Loongson Technology Corporation Limited
>>> + */
>>> +
>>> +#include "qemu/osdep.h"
>>> +#include "hw/boards.h"
>>> +#include "hw/sysbus.h"
>>> +#include "hw/intc/loongson_ipi_common.h"
>>> +#include "hw/irq.h"
>>> +#include "hw/qdev-properties.h"
>>> +#include "qapi/error.h"
>>> +#include "qemu/log.h"
>>> +#include "exec/address-spaces.h"
>>> +#include "exec/memory.h"
>>> +#include "migration/vmstate.h"
>>> +#include "trace.h"
>>> +
>>> +static MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr,
>>> +                                           uint64_t *data,
>>> +                                           unsigned size, MemTxAttrs
>>> attrs)
>>> +{
>>> +    IPICore *s = opaque;
>>> +    uint64_t ret = 0;
>>> +    int index = 0;
>>> +
>>> +    addr &= 0xff;
>>> +    switch (addr) {
>>> +    case CORE_STATUS_OFF:
>>> +        ret = s->status;
>>> +        break;
>>> +    case CORE_EN_OFF:
>>> +        ret = s->en;
>>> +        break;
>>> +    case CORE_SET_OFF:
>>> +        ret = 0;
>>> +        break;
>>> +    case CORE_CLEAR_OFF:
>>> +        ret = 0;
>>> +        break;
>>> +    case CORE_BUF_20 ... CORE_BUF_38 + 4:
>>> +        index = (addr - CORE_BUF_20) >> 2;
>>> +        ret = s->buf[index];
>>> +        break;
>>> +    default:
>>> +        qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
>>> +        break;
>>> +    }
>>> +
>>> +    trace_loongson_ipi_read(size, (uint64_t)addr, ret);
>>> +    *data = ret;
>>> +    return MEMTX_OK;
>>> +}
>>> +
>>> +static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr,
>>> +                                            uint64_t *data,
>>> +                                            unsigned size, MemTxAttrs
>>> attrs)
>>> +{
>>> +    LoongsonIPICommonState *ipi = opaque;
>>> +    IPICore *s;
>>> +
>>> +    if (attrs.requester_id >= ipi->num_cpu) {
>>> +        return MEMTX_DECODE_ERROR;
>>> +    }
>>> +
>>> +    s = &ipi->cpu[attrs.requester_id];
>>> +    return loongson_ipi_core_readl(s, addr, data, size, attrs);
>>> +}
>>> +
>>> +static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState
>>> *cpu,
>>> +                                 uint64_t val,
>>> +                                 hwaddr addr, MemTxAttrs attrs)
>>> +{
>>> +    int i, mask = 0, data = 0;
>>> +    AddressSpace *iocsr_as;
>>> +    LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
>>> +
>>> +    iocsr_as = NULL;
>>> +    if (licc->get_iocsr_as) {
>>> +        iocsr_as = licc->get_iocsr_as(cpu);
>>> +    }
>>> +
>>> +    if (!iocsr_as) {
>>> +        return MEMTX_DECODE_ERROR;
>>> +    }
>>> +
>>> +    /*
>>> +     * bit 27-30 is mask for byte writing,
>>> +     * if the mask is 0, we need not to do anything.
>>> +     */
>>> +    if ((val >> 27) & 0xf) {
>>> +        data = address_space_ldl_le(iocsr_as, addr, attrs, NULL);
>>> +        for (i = 0; i < 4; i++) {
>>> +            /* get mask for byte writing */
>>> +            if (val & (0x1 << (27 + i))) {
>>> +                mask |= 0xff << (i * 8);
>>> +            }
>>> +        }
>>> +    }
>>> +
>>> +    data &= mask;
>>> +    data |= (val >> 32) & ~mask;
>>> +    address_space_stl_le(iocsr_as, addr, data, attrs, NULL);
>>> +
>>> +    return MEMTX_OK;
>>> +}
>>> +
>>> +static CPUState *get_cpu_by_arch_id(LoongsonIPICommonState *ipi,
>>> uint32_t cpuid)
>>> +{
>>> +    LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
>>> +
>>> +    if (licc->cpu_by_arch_id) {
>>> +        return licc->cpu_by_arch_id(cpuid);
>>> +    }
>>> +
>>> +    return cpu_by_arch_id(cpuid);
>>> +}
>>> +
>>> +static MemTxResult mail_send(LoongsonIPICommonState *ipi, uint64_t val,
>>> +                             MemTxAttrs attrs)
>>> +{
>>> +    uint32_t cpuid;
>>> +    hwaddr addr;
>>> +    CPUState *cs;
>>> +
>>> +    cpuid = extract32(val, 16, 10);
>>> +    cs = get_cpu_by_arch_id(ipi, cpuid);
>>> +    if (cs == NULL) {
>>> +        return MEMTX_DECODE_ERROR;
>>> +    }
>>> +
>>> +    /* override requester_id */
>>> +    addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
>>> +    attrs.requester_id = cs->cpu_index;
>>> +    return send_ipi_data(ipi, cs, val, addr, attrs);
>>> +}
>>> +
>>> +static MemTxResult any_send(LoongsonIPICommonState *ipi, uint64_t val,
>>> +                            MemTxAttrs attrs)
>>> +{
>>> +    uint32_t cpuid;
>>> +    hwaddr addr;
>>> +    CPUState *cs;
>>> +
>>> +    cpuid = extract32(val, 16, 10);
>>> +    cs = get_cpu_by_arch_id(ipi, cpuid);
>>> +    if (cs == NULL) {
>>> +        return MEMTX_DECODE_ERROR;
>>> +    }
>>> +
>>> +    /* override requester_id */
>>> +    addr = val & 0xffff;
>>> +    attrs.requester_id = cs->cpu_index;
>>> +    return send_ipi_data(ipi, cs, val, addr, attrs);
>>> +}
>>> +
>>> +static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr,
>>> +                                            uint64_t val, unsigned
>>> size,
>>> +                                            MemTxAttrs attrs)
>>> +{
>>> +    IPICore *s = opaque;
>>> +    LoongsonIPICommonState *ipi = s->ipi;
>>> +    int index = 0;
>>> +    uint32_t cpuid;
>>> +    uint8_t vector;
>>> +    CPUState *cs;
>>> +
>>> +    addr &= 0xff;
>>> +    trace_loongson_ipi_write(size, (uint64_t)addr, val);
>>> +    switch (addr) {
>>> +    case CORE_STATUS_OFF:
>>> +        qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
>>> +        break;
>>> +    case CORE_EN_OFF:
>>> +        s->en = val;
>>> +        break;
>>> +    case CORE_SET_OFF:
>>> +        s->status |= val;
>>> +        if (s->status != 0 && (s->status & s->en) != 0) {
>>> +            qemu_irq_raise(s->irq);
>>> +        }
>>> +        break;
>>> +    case CORE_CLEAR_OFF:
>>> +        s->status &= ~val;
>>> +        if (s->status == 0 && s->en != 0) {
>>> +            qemu_irq_lower(s->irq);
>>> +        }
>>> +        break;
>>> +    case CORE_BUF_20 ... CORE_BUF_38 + 4:
>>> +        index = (addr - CORE_BUF_20) >> 2;
>>> +        s->buf[index] = val;
>>> +        break;
>>> +    case IOCSR_IPI_SEND:
>>> +        cpuid = extract32(val, 16, 10);
>>> +        /* IPI status vector */
>>> +        vector = extract8(val, 0, 5);
>>> +        cs = get_cpu_by_arch_id(ipi, cpuid);
>>> +        if (cs == NULL || cs->cpu_index >= ipi->num_cpu) {
>>> +            return MEMTX_DECODE_ERROR;
>>> +        }
>>> +        loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index],
>>> CORE_SET_OFF,
>>> +                                 BIT(vector), 4, attrs);
>>> +        break;
>>> +    default:
>>> +        qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
>>> +        break;
>>> +    }
>>> +
>>> +    return MEMTX_OK;
>>> +}
>>> +
>>> +static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr,
>>> +                                            uint64_t val, unsigned
>>> size,
>>> +                                            MemTxAttrs attrs)
>>> +{
>>> +    LoongsonIPICommonState *ipi = opaque;
>>> +    IPICore *s;
>>> +
>>> +    if (attrs.requester_id >= ipi->num_cpu) {
>>> +        return MEMTX_DECODE_ERROR;
>>> +    }
>>> +
>>> +    s = &ipi->cpu[attrs.requester_id];
>>> +    return loongson_ipi_core_writel(s, addr, val, size, attrs);
>>> +}
>>> +
>>> +static const MemoryRegionOps loongson_ipi_iocsr_ops = {
>>> +    .read_with_attrs = loongson_ipi_iocsr_readl,
>>> +    .write_with_attrs = loongson_ipi_iocsr_writel,
>>> +    .impl.min_access_size = 4,
>>> +    .impl.max_access_size = 4,
>>> +    .valid.min_access_size = 4,
>>> +    .valid.max_access_size = 8,
>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>> +};
>>> +
>>> +/* mail send and any send only support writeq */
>>> +static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr,
>>> uint64_t val,
>>> +                                        unsigned size, MemTxAttrs
>>> attrs)
>>> +{
>>> +    MemTxResult ret = MEMTX_OK;
>>> +    LoongsonIPICommonState *ipi = opaque;
>>> +
>>> +    addr &= 0xfff;
>>> +    switch (addr) {
>>> +    case MAIL_SEND_OFFSET:
>>> +        ret = mail_send(ipi, val, attrs);
>>> +        break;
>>> +    case ANY_SEND_OFFSET:
>>> +        ret = any_send(ipi, val, attrs);
>>> +        break;
>>> +    default:
>>> +       break;
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static const MemoryRegionOps loongson_ipi64_ops = {
>>> +    .write_with_attrs = loongson_ipi_writeq,
>>> +    .impl.min_access_size = 8,
>>> +    .impl.max_access_size = 8,
>>> +    .valid.min_access_size = 8,
>>> +    .valid.max_access_size = 8,
>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>> +};
>>> +
>>> +static void loongson_ipi_common_realize(DeviceState *dev, Error **errp)
>>> +{
>>> +    LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
>>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>>> +    int i;
>>> +
>>> +    if (s->num_cpu == 0) {
>>> +        error_setg(errp, "num-cpu must be at least 1");
>>> +        return;
>>> +    }
>>> +
>>> +    memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev),
>>> +                          &loongson_ipi_iocsr_ops,
>>> +                          s, "loongson_ipi_iocsr", 0x48);
>>> +
>>> +    /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */
>>> +    s->ipi_iocsr_mem.disable_reentrancy_guard = true;
>>> +
>>> +    sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
>>> +
>>> +    memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev),
>>> +                          &loongson_ipi64_ops,
>>> +                          s, "loongson_ipi64_iocsr", 0x118);
>>> +    sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
>>> +
>>> +    s->cpu = g_new0(IPICore, s->num_cpu);
>>> +    if (s->cpu == NULL) {
>>> +        error_setg(errp, "Memory allocation for IPICore faile");
>>> +        return;
>>> +    }
>>> +
>>> +    for (i = 0; i < s->num_cpu; i++) {
>>> +        s->cpu[i].ipi = s;
>>> +        qdev_init_gpio_out(dev, &s->cpu[i].irq, 1);
>>> +    }
>>> +}
>>> +
>>> +static int loongson_ipi_pre_save(void *opaque)
>>> +{
>>> +    LoongsonIPICommonState *s = (LoongsonIPICommonState *)opaque;
>>> +    LoongsonIPICommonClass *c = LOONGSON_IPI_COMMON_GET_CLASS(s);
>>> +
>>> +    if (c->pre_save) {
>>> +        c->pre_save(s);
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int loongson_ipi_post_load(void *opaque, int version_id)
>>> +{
>>> +    LoongsonIPICommonState *s = (LoongsonIPICommonState *)opaque;
>>> +    LoongsonIPICommonClass *c = LOONGSON_IPI_COMMON_GET_CLASS(s);
>>> +
>>> +    if (c->post_load) {
>>> +        c->post_load(s);
>>> +    }
>>> +    return 0;
>>> +}
>>> +
>>> +static const VMStateDescription vmstate_ipi_core = {
>>> +    .name = "ipi-single",
>>> +    .version_id = 2,
>>> +    .minimum_version_id = 2,
>>> +    .fields = (const VMStateField[]) {
>>> +        VMSTATE_UINT32(status, IPICore),
>>> +        VMSTATE_UINT32(en, IPICore),
>>> +        VMSTATE_UINT32(set, IPICore),
>>> +        VMSTATE_UINT32(clear, IPICore),
>>> +        VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
>>> +        VMSTATE_END_OF_LIST()
>>> +    }
>>> +};
>>> +
>>> +static const VMStateDescription vmstate_loongson_ipi = {
>>> +    /* Fixed name to keep compatible */
>>> +    .name = "loongson_ipi",
>>> +    .pre_save  = loongson_ipi_pre_save,
>>> +    .post_load = loongson_ipi_post_load,
>>> +    .version_id = 2,
>>> +    .minimum_version_id = 2,
>>> +    .fields = (const VMStateField[]) {
>>> +        VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu,
>>> LoongsonIPICommonState,
>>> +                                       num_cpu, vmstate_ipi_core,
>>> IPICore),
>>> +        VMSTATE_END_OF_LIST()
>>> +    }
>>> +};
>>> +
>>> +static Property ipi_properties[] = {
>>> +    DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1),
>>> +    DEFINE_PROP_END_OF_LIST(),
>>> +};
>>> +
>>> +static void loongson_ipi_common_class_init(ObjectClass *klass, void
>>> *data)
>>> +{
>>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>>> +
>>> +    dc->realize = loongson_ipi_common_realize;
>>> +    device_class_set_props(dc, ipi_properties);
>>> +    dc->vmsd = &vmstate_loongson_ipi;
>>> +}
>>> +
>>> +static void loongson_ipi_common_finalize(Object *obj)
>>> +{
>>> +    LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(obj);
>>> +
>>> +    g_free(s->cpu);
>>> +}
>>> +
>>> +static const TypeInfo loongson_ipi_common_info = {
>>> +    .name          = TYPE_LOONGSON_IPI_COMMON,
>>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>>> +    .instance_size = sizeof(LoongsonIPICommonState),
>>> +    .class_size    = sizeof(LoongsonIPICommonClass),
>>> +    .class_init    = loongson_ipi_common_class_init,
>>> +    .instance_finalize = loongson_ipi_common_finalize,
>>> +    .abstract = true,
>>> +};
>>> +
>>> +static void loongson_ipi_common_register_types(void)
>>> +{
>>> +    type_register_static(&loongson_ipi_common_info);
>>> +}
>>> +
>>> +type_init(loongson_ipi_common_register_types)
>>> diff --git a/include/hw/intc/loongson_ipi_common.h
>>> b/include/hw/intc/loongson_ipi_common.h
>>> new file mode 100644
>>> index 0000000000..1f074863e6
>>> --- /dev/null
>>> +++ b/include/hw/intc/loongson_ipi_common.h
>>> @@ -0,0 +1,71 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>> +/*
>>> + * Loongson ipi interrupt header files
>>> + *
>>> + * Copyright (C) 2021 Loongson Technology Corporation Limited
>>> + */
>>> +
>>> +#ifndef HW_LOONGSON_IPI_COMMON_H
>>> +#define HW_LOONGSON_IPI_COMMON_H
>>> +
>>> +#include "hw/sysbus.h"
>>> +#include "hw/core/cpu.h"
>>> +#include "qom/object.h"
>>> +
>>> +/* Mainy used by iocsr read and write */
>>> +#define SMP_IPI_MAILBOX      0x1000ULL
>>> +#define CORE_STATUS_OFF       0x0
>>> +#define CORE_EN_OFF           0x4
>>> +#define CORE_SET_OFF          0x8
>>> +#define CORE_CLEAR_OFF        0xc
>>> +#define CORE_BUF_20           0x20
>>> +#define CORE_BUF_28           0x28
>>> +#define CORE_BUF_30           0x30
>>> +#define CORE_BUF_38           0x38
>>> +#define IOCSR_IPI_SEND        0x40
>>> +#define IOCSR_MAIL_SEND       0x48
>>> +#define IOCSR_ANY_SEND        0x158
>>> +
>>> +#define MAIL_SEND_ADDR        (SMP_IPI_MAILBOX + IOCSR_MAIL_SEND)
>>> +#define MAIL_SEND_OFFSET      0
>>> +#define ANY_SEND_OFFSET       (IOCSR_ANY_SEND - IOCSR_MAIL_SEND)
>>> +
>>> +#define IPI_MBX_NUM           4
>>> +
>>> +#define TYPE_LOONGSON_IPI_COMMON "loongson_ipi_common"
>>> +typedef struct LoongsonIPICommonClass LoongsonIPICommonClass;
>>> +typedef struct LoongsonIPICommonState LoongsonIPICommonState;
>>> +DECLARE_OBJ_CHECKERS(LoongsonIPICommonState, LoongsonIPICommonClass,
>>> +                     LOONGSON_IPI_COMMON, TYPE_LOONGSON_IPI_COMMON)
>>> +
>>> +typedef struct IPICore {
>>> +    LoongsonIPICommonState *ipi;
>>> +    uint32_t status;
>>> +    uint32_t en;
>>> +    uint32_t set;
>>> +    uint32_t clear;
>>> +    /* 64bit buf divide into 2 32bit buf */
>>> +    uint32_t buf[IPI_MBX_NUM * 2];
>>> +    qemu_irq irq;
>>> +} IPICore;
>>> +
>>> +struct LoongsonIPICommonState {
>>> +    SysBusDevice parent_obj;
>>> +    MemoryRegion ipi_iocsr_mem;
>>> +    MemoryRegion ipi64_iocsr_mem;
>>> +    uint32_t num_cpu;
>>> +    IPICore *cpu;
>>> +};
>>> +
>>> +struct LoongsonIPICommonClass {
>>> +    /*< private >*/
>>> +    SysBusDeviceClass parent_class;
>>> +    /*< public >*/
>>> +
>>> +    void (*pre_save)(LoongsonIPICommonState *s);
>>> +    void (*post_load)(LoongsonIPICommonState *s);
>>> +    AddressSpace *(*get_iocsr_as)(CPUState *cpu);
>>> +    CPUState *(*cpu_by_arch_id)(int64_t id);
>>> +};
>>> +
>>> +#endif
>>> -- 
>>> 2.39.3
>>
maobibo July 3, 2024, 7:45 a.m. UTC | #4
On 2024/7/3 下午3:33, Jiaxun Yang wrote:
> 
> 
> 在2024年7月3日七月 下午2:40,maobibo写道:
> [...]
> Hi Bobo,
>>
>> MMIO is loongson ipi specific, it is not necessary to put into common
>> function. Functions loongson_ipi_core_readl/loongson_ipi_core_writel can
>> be exported in header file include/hw/intc/loongson_ipi_common.h, or get
>> MemoryRegionOps of first memoryregion of loongson_ipi instance.
>>
>> There is pseudo code:
> 
> Thanks for your demonstration. I'm still not quite convinced it's worthy to split
> but I'm not going to block you if we don't have other oppositions.
> 
> Do you mind to finish conversion of loongson_ipi as well? Since you are drafting the
> design.
It depends on schedule, I hope Loongarch IPI can merge before qemu is 
frozen. However I will try to write loongson_ipi but it can just pass to 
compile, no test. If it takes too long time to merge both Loongarch IPI 
and loongson ipi, I will only provide support for Loongarch IPI :)

> 
>>
>> static void loongson_ipi_realize(DeviceState *dev, Error **errp)
>> {
>>       LoongarchIPIState *s = LOONGARCH_IPI(dev);
>>       LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(s);
>>       Error *local_err = NULL;
>>
>>       lic->parent_realize(dev, &local_err);
>>       if (local_err) {
>>           error_propagate(errp, local_err);
>>           return;
>>       }
>>
>>       ....
>>       *do mmio specific implematation in loongson ipi itself*
>> }
>>
>> static void loongson_ipi_class_init(ObjectClass *klass, void *data)
>> {
>>       DeviceClass *dc = DEVICE_CLASS(klass);
>>       LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass);
>>       LoongarchIPIClass *lic = LOONGARCH_IPI_CLASS(klass);
>>
>>       device_class_set_parent_realize(dc, loongson_ipi_realize,
>> &lic->parent_realize);
>>       licc->get_iocsr_as = get_iocsr_as;
>> }
>>
>>>
>>> If current implementation is hindering your future plan can you elaborate so we
>>> can work on a resolution.
>>>
>>> I'm happy to help with devlopment and testing.
>>>
>>>>    3. Interace cpu_by_arch_id is added, by default generic function
>>>> cpu_by_arch_id() is used to search vcpu from physical cpuid, it is
>>>> generic searching method. Different machine may define another search
>>>> method such binary searching method.
>>>
>>> If you are going to implement some faster searching algorithm why don't we
>>>    make it generic for all architectures?
>> It depends on the detailed physical id layout, is physical id is growing
>> up with logic cpu id or irrelative with logic cpu id? Different
>> architecture has different logic definition about physical id.
> 
> For x86' APIC id and RISC-V's hardid they are all somehow linear.
> I'd suggest you to post a RFC patch regarding better algorithm.
Thanks for your suggestion, currently there is no such plan, it is above 
my ability :(

Regards
Bibo Mao
> 
> Thanks
> - Jiaxun
>>
>> Regards
>> Bibo Mao
>>
>>>
>>> Thanks
>>> - Jiaxun
>>>
>>>>
>>>> Signed-off-by: Bibo Mao <maobibo@loongson.cn>
>>>> ---
>>>>    hw/intc/loongson_ipi_common.c         | 394 ++++++++++++++++++++++++++
>>>>    include/hw/intc/loongson_ipi_common.h |  71 +++++
>>>>    2 files changed, 465 insertions(+)
>>>>    create mode 100644 hw/intc/loongson_ipi_common.c
>>>>    create mode 100644 include/hw/intc/loongson_ipi_common.h
>>>>
>>>> diff --git a/hw/intc/loongson_ipi_common.c
>>>> b/hw/intc/loongson_ipi_common.c
>>>> new file mode 100644
>>>> index 0000000000..f462f24f32
>>>> --- /dev/null
>>>> +++ b/hw/intc/loongson_ipi_common.c
>>>> @@ -0,0 +1,394 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>>> +/*
>>>> + * Loongson ipi interrupt support
>>>> + *
>>>> + * Copyright (C) 2021 Loongson Technology Corporation Limited
>>>> + */
>>>> +
>>>> +#include "qemu/osdep.h"
>>>> +#include "hw/boards.h"
>>>> +#include "hw/sysbus.h"
>>>> +#include "hw/intc/loongson_ipi_common.h"
>>>> +#include "hw/irq.h"
>>>> +#include "hw/qdev-properties.h"
>>>> +#include "qapi/error.h"
>>>> +#include "qemu/log.h"
>>>> +#include "exec/address-spaces.h"
>>>> +#include "exec/memory.h"
>>>> +#include "migration/vmstate.h"
>>>> +#include "trace.h"
>>>> +
>>>> +static MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr,
>>>> +                                           uint64_t *data,
>>>> +                                           unsigned size, MemTxAttrs
>>>> attrs)
>>>> +{
>>>> +    IPICore *s = opaque;
>>>> +    uint64_t ret = 0;
>>>> +    int index = 0;
>>>> +
>>>> +    addr &= 0xff;
>>>> +    switch (addr) {
>>>> +    case CORE_STATUS_OFF:
>>>> +        ret = s->status;
>>>> +        break;
>>>> +    case CORE_EN_OFF:
>>>> +        ret = s->en;
>>>> +        break;
>>>> +    case CORE_SET_OFF:
>>>> +        ret = 0;
>>>> +        break;
>>>> +    case CORE_CLEAR_OFF:
>>>> +        ret = 0;
>>>> +        break;
>>>> +    case CORE_BUF_20 ... CORE_BUF_38 + 4:
>>>> +        index = (addr - CORE_BUF_20) >> 2;
>>>> +        ret = s->buf[index];
>>>> +        break;
>>>> +    default:
>>>> +        qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    trace_loongson_ipi_read(size, (uint64_t)addr, ret);
>>>> +    *data = ret;
>>>> +    return MEMTX_OK;
>>>> +}
>>>> +
>>>> +static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr,
>>>> +                                            uint64_t *data,
>>>> +                                            unsigned size, MemTxAttrs
>>>> attrs)
>>>> +{
>>>> +    LoongsonIPICommonState *ipi = opaque;
>>>> +    IPICore *s;
>>>> +
>>>> +    if (attrs.requester_id >= ipi->num_cpu) {
>>>> +        return MEMTX_DECODE_ERROR;
>>>> +    }
>>>> +
>>>> +    s = &ipi->cpu[attrs.requester_id];
>>>> +    return loongson_ipi_core_readl(s, addr, data, size, attrs);
>>>> +}
>>>> +
>>>> +static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState
>>>> *cpu,
>>>> +                                 uint64_t val,
>>>> +                                 hwaddr addr, MemTxAttrs attrs)
>>>> +{
>>>> +    int i, mask = 0, data = 0;
>>>> +    AddressSpace *iocsr_as;
>>>> +    LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
>>>> +
>>>> +    iocsr_as = NULL;
>>>> +    if (licc->get_iocsr_as) {
>>>> +        iocsr_as = licc->get_iocsr_as(cpu);
>>>> +    }
>>>> +
>>>> +    if (!iocsr_as) {
>>>> +        return MEMTX_DECODE_ERROR;
>>>> +    }
>>>> +
>>>> +    /*
>>>> +     * bit 27-30 is mask for byte writing,
>>>> +     * if the mask is 0, we need not to do anything.
>>>> +     */
>>>> +    if ((val >> 27) & 0xf) {
>>>> +        data = address_space_ldl_le(iocsr_as, addr, attrs, NULL);
>>>> +        for (i = 0; i < 4; i++) {
>>>> +            /* get mask for byte writing */
>>>> +            if (val & (0x1 << (27 + i))) {
>>>> +                mask |= 0xff << (i * 8);
>>>> +            }
>>>> +        }
>>>> +    }
>>>> +
>>>> +    data &= mask;
>>>> +    data |= (val >> 32) & ~mask;
>>>> +    address_space_stl_le(iocsr_as, addr, data, attrs, NULL);
>>>> +
>>>> +    return MEMTX_OK;
>>>> +}
>>>> +
>>>> +static CPUState *get_cpu_by_arch_id(LoongsonIPICommonState *ipi,
>>>> uint32_t cpuid)
>>>> +{
>>>> +    LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
>>>> +
>>>> +    if (licc->cpu_by_arch_id) {
>>>> +        return licc->cpu_by_arch_id(cpuid);
>>>> +    }
>>>> +
>>>> +    return cpu_by_arch_id(cpuid);
>>>> +}
>>>> +
>>>> +static MemTxResult mail_send(LoongsonIPICommonState *ipi, uint64_t val,
>>>> +                             MemTxAttrs attrs)
>>>> +{
>>>> +    uint32_t cpuid;
>>>> +    hwaddr addr;
>>>> +    CPUState *cs;
>>>> +
>>>> +    cpuid = extract32(val, 16, 10);
>>>> +    cs = get_cpu_by_arch_id(ipi, cpuid);
>>>> +    if (cs == NULL) {
>>>> +        return MEMTX_DECODE_ERROR;
>>>> +    }
>>>> +
>>>> +    /* override requester_id */
>>>> +    addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
>>>> +    attrs.requester_id = cs->cpu_index;
>>>> +    return send_ipi_data(ipi, cs, val, addr, attrs);
>>>> +}
>>>> +
>>>> +static MemTxResult any_send(LoongsonIPICommonState *ipi, uint64_t val,
>>>> +                            MemTxAttrs attrs)
>>>> +{
>>>> +    uint32_t cpuid;
>>>> +    hwaddr addr;
>>>> +    CPUState *cs;
>>>> +
>>>> +    cpuid = extract32(val, 16, 10);
>>>> +    cs = get_cpu_by_arch_id(ipi, cpuid);
>>>> +    if (cs == NULL) {
>>>> +        return MEMTX_DECODE_ERROR;
>>>> +    }
>>>> +
>>>> +    /* override requester_id */
>>>> +    addr = val & 0xffff;
>>>> +    attrs.requester_id = cs->cpu_index;
>>>> +    return send_ipi_data(ipi, cs, val, addr, attrs);
>>>> +}
>>>> +
>>>> +static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr,
>>>> +                                            uint64_t val, unsigned
>>>> size,
>>>> +                                            MemTxAttrs attrs)
>>>> +{
>>>> +    IPICore *s = opaque;
>>>> +    LoongsonIPICommonState *ipi = s->ipi;
>>>> +    int index = 0;
>>>> +    uint32_t cpuid;
>>>> +    uint8_t vector;
>>>> +    CPUState *cs;
>>>> +
>>>> +    addr &= 0xff;
>>>> +    trace_loongson_ipi_write(size, (uint64_t)addr, val);
>>>> +    switch (addr) {
>>>> +    case CORE_STATUS_OFF:
>>>> +        qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
>>>> +        break;
>>>> +    case CORE_EN_OFF:
>>>> +        s->en = val;
>>>> +        break;
>>>> +    case CORE_SET_OFF:
>>>> +        s->status |= val;
>>>> +        if (s->status != 0 && (s->status & s->en) != 0) {
>>>> +            qemu_irq_raise(s->irq);
>>>> +        }
>>>> +        break;
>>>> +    case CORE_CLEAR_OFF:
>>>> +        s->status &= ~val;
>>>> +        if (s->status == 0 && s->en != 0) {
>>>> +            qemu_irq_lower(s->irq);
>>>> +        }
>>>> +        break;
>>>> +    case CORE_BUF_20 ... CORE_BUF_38 + 4:
>>>> +        index = (addr - CORE_BUF_20) >> 2;
>>>> +        s->buf[index] = val;
>>>> +        break;
>>>> +    case IOCSR_IPI_SEND:
>>>> +        cpuid = extract32(val, 16, 10);
>>>> +        /* IPI status vector */
>>>> +        vector = extract8(val, 0, 5);
>>>> +        cs = get_cpu_by_arch_id(ipi, cpuid);
>>>> +        if (cs == NULL || cs->cpu_index >= ipi->num_cpu) {
>>>> +            return MEMTX_DECODE_ERROR;
>>>> +        }
>>>> +        loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index],
>>>> CORE_SET_OFF,
>>>> +                                 BIT(vector), 4, attrs);
>>>> +        break;
>>>> +    default:
>>>> +        qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    return MEMTX_OK;
>>>> +}
>>>> +
>>>> +static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr,
>>>> +                                            uint64_t val, unsigned
>>>> size,
>>>> +                                            MemTxAttrs attrs)
>>>> +{
>>>> +    LoongsonIPICommonState *ipi = opaque;
>>>> +    IPICore *s;
>>>> +
>>>> +    if (attrs.requester_id >= ipi->num_cpu) {
>>>> +        return MEMTX_DECODE_ERROR;
>>>> +    }
>>>> +
>>>> +    s = &ipi->cpu[attrs.requester_id];
>>>> +    return loongson_ipi_core_writel(s, addr, val, size, attrs);
>>>> +}
>>>> +
>>>> +static const MemoryRegionOps loongson_ipi_iocsr_ops = {
>>>> +    .read_with_attrs = loongson_ipi_iocsr_readl,
>>>> +    .write_with_attrs = loongson_ipi_iocsr_writel,
>>>> +    .impl.min_access_size = 4,
>>>> +    .impl.max_access_size = 4,
>>>> +    .valid.min_access_size = 4,
>>>> +    .valid.max_access_size = 8,
>>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>>> +};
>>>> +
>>>> +/* mail send and any send only support writeq */
>>>> +static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr,
>>>> uint64_t val,
>>>> +                                        unsigned size, MemTxAttrs
>>>> attrs)
>>>> +{
>>>> +    MemTxResult ret = MEMTX_OK;
>>>> +    LoongsonIPICommonState *ipi = opaque;
>>>> +
>>>> +    addr &= 0xfff;
>>>> +    switch (addr) {
>>>> +    case MAIL_SEND_OFFSET:
>>>> +        ret = mail_send(ipi, val, attrs);
>>>> +        break;
>>>> +    case ANY_SEND_OFFSET:
>>>> +        ret = any_send(ipi, val, attrs);
>>>> +        break;
>>>> +    default:
>>>> +       break;
>>>> +    }
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static const MemoryRegionOps loongson_ipi64_ops = {
>>>> +    .write_with_attrs = loongson_ipi_writeq,
>>>> +    .impl.min_access_size = 8,
>>>> +    .impl.max_access_size = 8,
>>>> +    .valid.min_access_size = 8,
>>>> +    .valid.max_access_size = 8,
>>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>>> +};
>>>> +
>>>> +static void loongson_ipi_common_realize(DeviceState *dev, Error **errp)
>>>> +{
>>>> +    LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
>>>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>>>> +    int i;
>>>> +
>>>> +    if (s->num_cpu == 0) {
>>>> +        error_setg(errp, "num-cpu must be at least 1");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev),
>>>> +                          &loongson_ipi_iocsr_ops,
>>>> +                          s, "loongson_ipi_iocsr", 0x48);
>>>> +
>>>> +    /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */
>>>> +    s->ipi_iocsr_mem.disable_reentrancy_guard = true;
>>>> +
>>>> +    sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
>>>> +
>>>> +    memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev),
>>>> +                          &loongson_ipi64_ops,
>>>> +                          s, "loongson_ipi64_iocsr", 0x118);
>>>> +    sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
>>>> +
>>>> +    s->cpu = g_new0(IPICore, s->num_cpu);
>>>> +    if (s->cpu == NULL) {
>>>> +        error_setg(errp, "Memory allocation for IPICore faile");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    for (i = 0; i < s->num_cpu; i++) {
>>>> +        s->cpu[i].ipi = s;
>>>> +        qdev_init_gpio_out(dev, &s->cpu[i].irq, 1);
>>>> +    }
>>>> +}
>>>> +
>>>> +static int loongson_ipi_pre_save(void *opaque)
>>>> +{
>>>> +    LoongsonIPICommonState *s = (LoongsonIPICommonState *)opaque;
>>>> +    LoongsonIPICommonClass *c = LOONGSON_IPI_COMMON_GET_CLASS(s);
>>>> +
>>>> +    if (c->pre_save) {
>>>> +        c->pre_save(s);
>>>> +    }
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int loongson_ipi_post_load(void *opaque, int version_id)
>>>> +{
>>>> +    LoongsonIPICommonState *s = (LoongsonIPICommonState *)opaque;
>>>> +    LoongsonIPICommonClass *c = LOONGSON_IPI_COMMON_GET_CLASS(s);
>>>> +
>>>> +    if (c->post_load) {
>>>> +        c->post_load(s);
>>>> +    }
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static const VMStateDescription vmstate_ipi_core = {
>>>> +    .name = "ipi-single",
>>>> +    .version_id = 2,
>>>> +    .minimum_version_id = 2,
>>>> +    .fields = (const VMStateField[]) {
>>>> +        VMSTATE_UINT32(status, IPICore),
>>>> +        VMSTATE_UINT32(en, IPICore),
>>>> +        VMSTATE_UINT32(set, IPICore),
>>>> +        VMSTATE_UINT32(clear, IPICore),
>>>> +        VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
>>>> +        VMSTATE_END_OF_LIST()
>>>> +    }
>>>> +};
>>>> +
>>>> +static const VMStateDescription vmstate_loongson_ipi = {
>>>> +    /* Fixed name to keep compatible */
>>>> +    .name = "loongson_ipi",
>>>> +    .pre_save  = loongson_ipi_pre_save,
>>>> +    .post_load = loongson_ipi_post_load,
>>>> +    .version_id = 2,
>>>> +    .minimum_version_id = 2,
>>>> +    .fields = (const VMStateField[]) {
>>>> +        VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu,
>>>> LoongsonIPICommonState,
>>>> +                                       num_cpu, vmstate_ipi_core,
>>>> IPICore),
>>>> +        VMSTATE_END_OF_LIST()
>>>> +    }
>>>> +};
>>>> +
>>>> +static Property ipi_properties[] = {
>>>> +    DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1),
>>>> +    DEFINE_PROP_END_OF_LIST(),
>>>> +};
>>>> +
>>>> +static void loongson_ipi_common_class_init(ObjectClass *klass, void
>>>> *data)
>>>> +{
>>>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>>>> +
>>>> +    dc->realize = loongson_ipi_common_realize;
>>>> +    device_class_set_props(dc, ipi_properties);
>>>> +    dc->vmsd = &vmstate_loongson_ipi;
>>>> +}
>>>> +
>>>> +static void loongson_ipi_common_finalize(Object *obj)
>>>> +{
>>>> +    LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(obj);
>>>> +
>>>> +    g_free(s->cpu);
>>>> +}
>>>> +
>>>> +static const TypeInfo loongson_ipi_common_info = {
>>>> +    .name          = TYPE_LOONGSON_IPI_COMMON,
>>>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>>>> +    .instance_size = sizeof(LoongsonIPICommonState),
>>>> +    .class_size    = sizeof(LoongsonIPICommonClass),
>>>> +    .class_init    = loongson_ipi_common_class_init,
>>>> +    .instance_finalize = loongson_ipi_common_finalize,
>>>> +    .abstract = true,
>>>> +};
>>>> +
>>>> +static void loongson_ipi_common_register_types(void)
>>>> +{
>>>> +    type_register_static(&loongson_ipi_common_info);
>>>> +}
>>>> +
>>>> +type_init(loongson_ipi_common_register_types)
>>>> diff --git a/include/hw/intc/loongson_ipi_common.h
>>>> b/include/hw/intc/loongson_ipi_common.h
>>>> new file mode 100644
>>>> index 0000000000..1f074863e6
>>>> --- /dev/null
>>>> +++ b/include/hw/intc/loongson_ipi_common.h
>>>> @@ -0,0 +1,71 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>>> +/*
>>>> + * Loongson ipi interrupt header files
>>>> + *
>>>> + * Copyright (C) 2021 Loongson Technology Corporation Limited
>>>> + */
>>>> +
>>>> +#ifndef HW_LOONGSON_IPI_COMMON_H
>>>> +#define HW_LOONGSON_IPI_COMMON_H
>>>> +
>>>> +#include "hw/sysbus.h"
>>>> +#include "hw/core/cpu.h"
>>>> +#include "qom/object.h"
>>>> +
>>>> +/* Mainy used by iocsr read and write */
>>>> +#define SMP_IPI_MAILBOX      0x1000ULL
>>>> +#define CORE_STATUS_OFF       0x0
>>>> +#define CORE_EN_OFF           0x4
>>>> +#define CORE_SET_OFF          0x8
>>>> +#define CORE_CLEAR_OFF        0xc
>>>> +#define CORE_BUF_20           0x20
>>>> +#define CORE_BUF_28           0x28
>>>> +#define CORE_BUF_30           0x30
>>>> +#define CORE_BUF_38           0x38
>>>> +#define IOCSR_IPI_SEND        0x40
>>>> +#define IOCSR_MAIL_SEND       0x48
>>>> +#define IOCSR_ANY_SEND        0x158
>>>> +
>>>> +#define MAIL_SEND_ADDR        (SMP_IPI_MAILBOX + IOCSR_MAIL_SEND)
>>>> +#define MAIL_SEND_OFFSET      0
>>>> +#define ANY_SEND_OFFSET       (IOCSR_ANY_SEND - IOCSR_MAIL_SEND)
>>>> +
>>>> +#define IPI_MBX_NUM           4
>>>> +
>>>> +#define TYPE_LOONGSON_IPI_COMMON "loongson_ipi_common"
>>>> +typedef struct LoongsonIPICommonClass LoongsonIPICommonClass;
>>>> +typedef struct LoongsonIPICommonState LoongsonIPICommonState;
>>>> +DECLARE_OBJ_CHECKERS(LoongsonIPICommonState, LoongsonIPICommonClass,
>>>> +                     LOONGSON_IPI_COMMON, TYPE_LOONGSON_IPI_COMMON)
>>>> +
>>>> +typedef struct IPICore {
>>>> +    LoongsonIPICommonState *ipi;
>>>> +    uint32_t status;
>>>> +    uint32_t en;
>>>> +    uint32_t set;
>>>> +    uint32_t clear;
>>>> +    /* 64bit buf divide into 2 32bit buf */
>>>> +    uint32_t buf[IPI_MBX_NUM * 2];
>>>> +    qemu_irq irq;
>>>> +} IPICore;
>>>> +
>>>> +struct LoongsonIPICommonState {
>>>> +    SysBusDevice parent_obj;
>>>> +    MemoryRegion ipi_iocsr_mem;
>>>> +    MemoryRegion ipi64_iocsr_mem;
>>>> +    uint32_t num_cpu;
>>>> +    IPICore *cpu;
>>>> +};
>>>> +
>>>> +struct LoongsonIPICommonClass {
>>>> +    /*< private >*/
>>>> +    SysBusDeviceClass parent_class;
>>>> +    /*< public >*/
>>>> +
>>>> +    void (*pre_save)(LoongsonIPICommonState *s);
>>>> +    void (*post_load)(LoongsonIPICommonState *s);
>>>> +    AddressSpace *(*get_iocsr_as)(CPUState *cpu);
>>>> +    CPUState *(*cpu_by_arch_id)(int64_t id);
>>>> +};
>>>> +
>>>> +#endif
>>>> -- 
>>>> 2.39.3
>>>
>
diff mbox series

Patch

diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c
new file mode 100644
index 0000000000..f462f24f32
--- /dev/null
+++ b/hw/intc/loongson_ipi_common.c
@@ -0,0 +1,394 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Loongson ipi interrupt support
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "hw/boards.h"
+#include "hw/sysbus.h"
+#include "hw/intc/loongson_ipi_common.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "exec/address-spaces.h"
+#include "exec/memory.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+
+static MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr,
+                                           uint64_t *data,
+                                           unsigned size, MemTxAttrs attrs)
+{
+    IPICore *s = opaque;
+    uint64_t ret = 0;
+    int index = 0;
+
+    addr &= 0xff;
+    switch (addr) {
+    case CORE_STATUS_OFF:
+        ret = s->status;
+        break;
+    case CORE_EN_OFF:
+        ret = s->en;
+        break;
+    case CORE_SET_OFF:
+        ret = 0;
+        break;
+    case CORE_CLEAR_OFF:
+        ret = 0;
+        break;
+    case CORE_BUF_20 ... CORE_BUF_38 + 4:
+        index = (addr - CORE_BUF_20) >> 2;
+        ret = s->buf[index];
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
+        break;
+    }
+
+    trace_loongson_ipi_read(size, (uint64_t)addr, ret);
+    *data = ret;
+    return MEMTX_OK;
+}
+
+static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr,
+                                            uint64_t *data,
+                                            unsigned size, MemTxAttrs attrs)
+{
+    LoongsonIPICommonState *ipi = opaque;
+    IPICore *s;
+
+    if (attrs.requester_id >= ipi->num_cpu) {
+        return MEMTX_DECODE_ERROR;
+    }
+
+    s = &ipi->cpu[attrs.requester_id];
+    return loongson_ipi_core_readl(s, addr, data, size, attrs);
+}
+
+static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu,
+                                 uint64_t val,
+                                 hwaddr addr, MemTxAttrs attrs)
+{
+    int i, mask = 0, data = 0;
+    AddressSpace *iocsr_as;
+    LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
+
+    iocsr_as = NULL;
+    if (licc->get_iocsr_as) {
+        iocsr_as = licc->get_iocsr_as(cpu);
+    }
+
+    if (!iocsr_as) {
+        return MEMTX_DECODE_ERROR;
+    }
+
+    /*
+     * bit 27-30 is mask for byte writing,
+     * if the mask is 0, we need not to do anything.
+     */
+    if ((val >> 27) & 0xf) {
+        data = address_space_ldl_le(iocsr_as, addr, attrs, NULL);
+        for (i = 0; i < 4; i++) {
+            /* get mask for byte writing */
+            if (val & (0x1 << (27 + i))) {
+                mask |= 0xff << (i * 8);
+            }
+        }
+    }
+
+    data &= mask;
+    data |= (val >> 32) & ~mask;
+    address_space_stl_le(iocsr_as, addr, data, attrs, NULL);
+
+    return MEMTX_OK;
+}
+
+static CPUState *get_cpu_by_arch_id(LoongsonIPICommonState *ipi, uint32_t cpuid)
+{
+    LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
+
+    if (licc->cpu_by_arch_id) {
+        return licc->cpu_by_arch_id(cpuid);
+    }
+
+    return cpu_by_arch_id(cpuid);
+}
+
+static MemTxResult mail_send(LoongsonIPICommonState *ipi, uint64_t val,
+                             MemTxAttrs attrs)
+{
+    uint32_t cpuid;
+    hwaddr addr;
+    CPUState *cs;
+
+    cpuid = extract32(val, 16, 10);
+    cs = get_cpu_by_arch_id(ipi, cpuid);
+    if (cs == NULL) {
+        return MEMTX_DECODE_ERROR;
+    }
+
+    /* override requester_id */
+    addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
+    attrs.requester_id = cs->cpu_index;
+    return send_ipi_data(ipi, cs, val, addr, attrs);
+}
+
+static MemTxResult any_send(LoongsonIPICommonState *ipi, uint64_t val,
+                            MemTxAttrs attrs)
+{
+    uint32_t cpuid;
+    hwaddr addr;
+    CPUState *cs;
+
+    cpuid = extract32(val, 16, 10);
+    cs = get_cpu_by_arch_id(ipi, cpuid);
+    if (cs == NULL) {
+        return MEMTX_DECODE_ERROR;
+    }
+
+    /* override requester_id */
+    addr = val & 0xffff;
+    attrs.requester_id = cs->cpu_index;
+    return send_ipi_data(ipi, cs, val, addr, attrs);
+}
+
+static MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr,
+                                            uint64_t val, unsigned size,
+                                            MemTxAttrs attrs)
+{
+    IPICore *s = opaque;
+    LoongsonIPICommonState *ipi = s->ipi;
+    int index = 0;
+    uint32_t cpuid;
+    uint8_t vector;
+    CPUState *cs;
+
+    addr &= 0xff;
+    trace_loongson_ipi_write(size, (uint64_t)addr, val);
+    switch (addr) {
+    case CORE_STATUS_OFF:
+        qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
+        break;
+    case CORE_EN_OFF:
+        s->en = val;
+        break;
+    case CORE_SET_OFF:
+        s->status |= val;
+        if (s->status != 0 && (s->status & s->en) != 0) {
+            qemu_irq_raise(s->irq);
+        }
+        break;
+    case CORE_CLEAR_OFF:
+        s->status &= ~val;
+        if (s->status == 0 && s->en != 0) {
+            qemu_irq_lower(s->irq);
+        }
+        break;
+    case CORE_BUF_20 ... CORE_BUF_38 + 4:
+        index = (addr - CORE_BUF_20) >> 2;
+        s->buf[index] = val;
+        break;
+    case IOCSR_IPI_SEND:
+        cpuid = extract32(val, 16, 10);
+        /* IPI status vector */
+        vector = extract8(val, 0, 5);
+        cs = get_cpu_by_arch_id(ipi, cpuid);
+        if (cs == NULL || cs->cpu_index >= ipi->num_cpu) {
+            return MEMTX_DECODE_ERROR;
+        }
+        loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index], CORE_SET_OFF,
+                                 BIT(vector), 4, attrs);
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
+        break;
+    }
+
+    return MEMTX_OK;
+}
+
+static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr,
+                                            uint64_t val, unsigned size,
+                                            MemTxAttrs attrs)
+{
+    LoongsonIPICommonState *ipi = opaque;
+    IPICore *s;
+
+    if (attrs.requester_id >= ipi->num_cpu) {
+        return MEMTX_DECODE_ERROR;
+    }
+
+    s = &ipi->cpu[attrs.requester_id];
+    return loongson_ipi_core_writel(s, addr, val, size, attrs);
+}
+
+static const MemoryRegionOps loongson_ipi_iocsr_ops = {
+    .read_with_attrs = loongson_ipi_iocsr_readl,
+    .write_with_attrs = loongson_ipi_iocsr_writel,
+    .impl.min_access_size = 4,
+    .impl.max_access_size = 4,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 8,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+/* mail send and any send only support writeq */
+static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val,
+                                        unsigned size, MemTxAttrs attrs)
+{
+    MemTxResult ret = MEMTX_OK;
+    LoongsonIPICommonState *ipi = opaque;
+
+    addr &= 0xfff;
+    switch (addr) {
+    case MAIL_SEND_OFFSET:
+        ret = mail_send(ipi, val, attrs);
+        break;
+    case ANY_SEND_OFFSET:
+        ret = any_send(ipi, val, attrs);
+        break;
+    default:
+       break;
+    }
+
+    return ret;
+}
+
+static const MemoryRegionOps loongson_ipi64_ops = {
+    .write_with_attrs = loongson_ipi_writeq,
+    .impl.min_access_size = 8,
+    .impl.max_access_size = 8,
+    .valid.min_access_size = 8,
+    .valid.max_access_size = 8,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void loongson_ipi_common_realize(DeviceState *dev, Error **errp)
+{
+    LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    int i;
+
+    if (s->num_cpu == 0) {
+        error_setg(errp, "num-cpu must be at least 1");
+        return;
+    }
+
+    memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev),
+                          &loongson_ipi_iocsr_ops,
+                          s, "loongson_ipi_iocsr", 0x48);
+
+    /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */
+    s->ipi_iocsr_mem.disable_reentrancy_guard = true;
+
+    sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
+
+    memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev),
+                          &loongson_ipi64_ops,
+                          s, "loongson_ipi64_iocsr", 0x118);
+    sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
+
+    s->cpu = g_new0(IPICore, s->num_cpu);
+    if (s->cpu == NULL) {
+        error_setg(errp, "Memory allocation for IPICore faile");
+        return;
+    }
+
+    for (i = 0; i < s->num_cpu; i++) {
+        s->cpu[i].ipi = s;
+        qdev_init_gpio_out(dev, &s->cpu[i].irq, 1);
+    }
+}
+
+static int loongson_ipi_pre_save(void *opaque)
+{
+    LoongsonIPICommonState *s = (LoongsonIPICommonState *)opaque;
+    LoongsonIPICommonClass *c = LOONGSON_IPI_COMMON_GET_CLASS(s);
+
+    if (c->pre_save) {
+        c->pre_save(s);
+    }
+
+    return 0;
+}
+
+static int loongson_ipi_post_load(void *opaque, int version_id)
+{
+    LoongsonIPICommonState *s = (LoongsonIPICommonState *)opaque;
+    LoongsonIPICommonClass *c = LOONGSON_IPI_COMMON_GET_CLASS(s);
+
+    if (c->post_load) {
+        c->post_load(s);
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_ipi_core = {
+    .name = "ipi-single",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32(status, IPICore),
+        VMSTATE_UINT32(en, IPICore),
+        VMSTATE_UINT32(set, IPICore),
+        VMSTATE_UINT32(clear, IPICore),
+        VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_loongson_ipi = {
+    /* Fixed name to keep compatible */
+    .name = "loongson_ipi",
+    .pre_save  = loongson_ipi_pre_save,
+    .post_load = loongson_ipi_post_load,
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .fields = (const VMStateField[]) {
+        VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPICommonState,
+                                       num_cpu, vmstate_ipi_core, IPICore),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ipi_properties[] = {
+    DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void loongson_ipi_common_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = loongson_ipi_common_realize;
+    device_class_set_props(dc, ipi_properties);
+    dc->vmsd = &vmstate_loongson_ipi;
+}
+
+static void loongson_ipi_common_finalize(Object *obj)
+{
+    LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(obj);
+
+    g_free(s->cpu);
+}
+
+static const TypeInfo loongson_ipi_common_info = {
+    .name          = TYPE_LOONGSON_IPI_COMMON,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(LoongsonIPICommonState),
+    .class_size    = sizeof(LoongsonIPICommonClass),
+    .class_init    = loongson_ipi_common_class_init,
+    .instance_finalize = loongson_ipi_common_finalize,
+    .abstract = true,
+};
+
+static void loongson_ipi_common_register_types(void)
+{
+    type_register_static(&loongson_ipi_common_info);
+}
+
+type_init(loongson_ipi_common_register_types)
diff --git a/include/hw/intc/loongson_ipi_common.h b/include/hw/intc/loongson_ipi_common.h
new file mode 100644
index 0000000000..1f074863e6
--- /dev/null
+++ b/include/hw/intc/loongson_ipi_common.h
@@ -0,0 +1,71 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Loongson ipi interrupt header files
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef HW_LOONGSON_IPI_COMMON_H
+#define HW_LOONGSON_IPI_COMMON_H
+
+#include "hw/sysbus.h"
+#include "hw/core/cpu.h"
+#include "qom/object.h"
+
+/* Mainy used by iocsr read and write */
+#define SMP_IPI_MAILBOX      0x1000ULL
+#define CORE_STATUS_OFF       0x0
+#define CORE_EN_OFF           0x4
+#define CORE_SET_OFF          0x8
+#define CORE_CLEAR_OFF        0xc
+#define CORE_BUF_20           0x20
+#define CORE_BUF_28           0x28
+#define CORE_BUF_30           0x30
+#define CORE_BUF_38           0x38
+#define IOCSR_IPI_SEND        0x40
+#define IOCSR_MAIL_SEND       0x48
+#define IOCSR_ANY_SEND        0x158
+
+#define MAIL_SEND_ADDR        (SMP_IPI_MAILBOX + IOCSR_MAIL_SEND)
+#define MAIL_SEND_OFFSET      0
+#define ANY_SEND_OFFSET       (IOCSR_ANY_SEND - IOCSR_MAIL_SEND)
+
+#define IPI_MBX_NUM           4
+
+#define TYPE_LOONGSON_IPI_COMMON "loongson_ipi_common"
+typedef struct LoongsonIPICommonClass LoongsonIPICommonClass;
+typedef struct LoongsonIPICommonState LoongsonIPICommonState;
+DECLARE_OBJ_CHECKERS(LoongsonIPICommonState, LoongsonIPICommonClass,
+                     LOONGSON_IPI_COMMON, TYPE_LOONGSON_IPI_COMMON)
+
+typedef struct IPICore {
+    LoongsonIPICommonState *ipi;
+    uint32_t status;
+    uint32_t en;
+    uint32_t set;
+    uint32_t clear;
+    /* 64bit buf divide into 2 32bit buf */
+    uint32_t buf[IPI_MBX_NUM * 2];
+    qemu_irq irq;
+} IPICore;
+
+struct LoongsonIPICommonState {
+    SysBusDevice parent_obj;
+    MemoryRegion ipi_iocsr_mem;
+    MemoryRegion ipi64_iocsr_mem;
+    uint32_t num_cpu;
+    IPICore *cpu;
+};
+
+struct LoongsonIPICommonClass {
+    /*< private >*/
+    SysBusDeviceClass parent_class;
+    /*< public >*/
+
+    void (*pre_save)(LoongsonIPICommonState *s);
+    void (*post_load)(LoongsonIPICommonState *s);
+    AddressSpace *(*get_iocsr_as)(CPUState *cpu);
+    CPUState *(*cpu_by_arch_id)(int64_t id);
+};
+
+#endif