On Wed, Jun 17, 2026 at 5:04 AM Kuan-Wei Chiu <[email protected]> wrote:
>
> Add support for the Milk-V Duo development board, which is powered by
> the Sophgo CV1800B SoC.
>
> The implementation includes:
> - Board-level machine initialization with 64mb of default ram.
> - Integration of the CV1800B SoC.
> - Support for loading external FDT, kernel, and initrd images.
> - Proper setup of the reset vector to match the CV1800B's boot flow.
>
> Signed-off-by: Kuan-Wei Chiu <[email protected]>

Acked-by: Alistair Francis <[email protected]>

Alistair

> ---
>  MAINTAINERS                                 |  11 ++
>  configs/devices/riscv64-softmmu/default.mak |   1 +
>  hw/riscv/Kconfig                            |   6 +
>  hw/riscv/meson.build                        |   1 +
>  hw/riscv/milkv_duo.c                        | 124 ++++++++++++++++++++
>  5 files changed, 143 insertions(+)
>  create mode 100644 hw/riscv/milkv_duo.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 93df53d87f..e197dcb5b2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1797,6 +1797,17 @@ F: include/hw/riscv/k230.h
>  F: include/hw/watchdog/k230_wdt.h
>  F: tests/qtest/k230-wdt-test.c
>
> +Milk-V Duo
> +M: Kuan-Wei Chiu <[email protected]>
> +S: Maintained
> +F: hw/char/dw8250.c
> +F: hw/misc/cv1800b_clk.c
> +F: hw/riscv/cv1800b.c
> +F: hw/riscv/milkv_duo.c
> +F: include/hw/char/dw8250.h
> +F: include/hw/misc/cv1800b_clk.h
> +F: include/hw/riscv/cv1800b.h
> +
>  RX Machines
>  -----------
>  rx-gdbsim
> diff --git a/configs/devices/riscv64-softmmu/default.mak 
> b/configs/devices/riscv64-softmmu/default.mak
> index a8e4d0ab33..2ba91b14d4 100644
> --- a/configs/devices/riscv64-softmmu/default.mak
> +++ b/configs/devices/riscv64-softmmu/default.mak
> @@ -13,3 +13,4 @@
>  # CONFIG_SHAKTI_C=n
>  # CONFIG_XIANGSHAN_KUNMINGHU=n
>  # CONFIG_MIPS_BOSTON_AIA=n
> +# CONFIG_MILKV_DUO=n
> diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
> index 299baed4a8..42a0e78574 100644
> --- a/hw/riscv/Kconfig
> +++ b/hw/riscv/Kconfig
> @@ -157,3 +157,9 @@ config SOPHGO_CV1800B
>      select SIFIVE_PLIC
>      select SOPHGO_CV1800B_CLK
>      select DW8250
> +
> +config MILKV_DUO
> +    bool
> +    depends on RISCV64
> +    default y
> +    select SOPHGO_CV1800B
> diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
> index 6c242d77da..8c88f2bc25 100644
> --- a/hw/riscv/meson.build
> +++ b/hw/riscv/meson.build
> @@ -20,5 +20,6 @@ riscv_ss.add(when: 'CONFIG_MIPS_BOSTON_AIA', if_true: 
> files('boston-aia.c'))
>  riscv_ss.add(when: 'CONFIG_K230', if_true: files('k230.c'))
>
>  riscv_ss.add(when: 'CONFIG_SOPHGO_CV1800B', if_true: files('cv1800b.c'))
> +riscv_ss.add(when: 'CONFIG_MILKV_DUO', if_true: files('milkv_duo.c'))
>
>  hw_arch += {'riscv': riscv_ss}
> diff --git a/hw/riscv/milkv_duo.c b/hw/riscv/milkv_duo.c
> new file mode 100644
> index 0000000000..8e9a553a57
> --- /dev/null
> +++ b/hw/riscv/milkv_duo.c
> @@ -0,0 +1,124 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Milk-V Duo board
> + *
> + * Copyright (c) 2026 Kuan-Wei Chiu <[email protected]>
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/units.h"
> +#include "qapi/error.h"
> +#include "hw/core/boards.h"
> +#include "hw/riscv/cv1800b.h"
> +#include "hw/riscv/boot.h"
> +#include "target/riscv/cpu-qom.h"
> +#include "system/system.h"
> +#include "system/device_tree.h"
> +#include "qemu/error-report.h"
> +#include "hw/core/loader.h"
> +#include <libfdt.h>
> +#include "hw/riscv/machines-qom.h"
> +
> +struct MilkVDuoState {
> +    MachineState parent_obj;
> +    CV1800BSoCState soc;
> +};
> +
> +#define TYPE_MILK_V_DUO MACHINE_TYPE_NAME("milkv-duo")
> +OBJECT_DECLARE_SIMPLE_TYPE(MilkVDuoState, MILK_V_DUO)
> +
> +static void milkv_duo_init(MachineState *machine)
> +{
> +    MilkVDuoState *s = MILK_V_DUO(machine);
> +    MemoryRegion *system_memory = get_system_memory();
> +    RISCVBootInfo boot_info;
> +    hwaddr firmware_load_addr, firmware_end_addr;
> +    hwaddr fdt_load_addr = 0;
> +    int fdt_size = 0;
> +    uint64_t kernel_entry = 0;
> +
> +    object_initialize_child(OBJECT(machine), "soc", &s->soc, 
> TYPE_CV1800B_SOC);
> +    qdev_realize(DEVICE(&s->soc), NULL, &error_fatal);
> +
> +    memory_region_add_subregion(system_memory,
> +                                cv1800b_memmap[CV1800B_DEV_DRAM].base,
> +                                machine->ram);
> +
> +    riscv_boot_info_init(&boot_info, &s->soc.cpus);
> +
> +    firmware_load_addr = cv1800b_memmap[CV1800B_DEV_DRAM].base;
> +    firmware_end_addr = firmware_load_addr;
> +    if (machine->firmware) {
> +        firmware_end_addr = riscv_find_and_load_firmware(machine, 
> machine->firmware,
> +                                                         
> &firmware_load_addr, NULL);
> +    }
> +
> +    if (machine->dtb) {
> +        machine->fdt = load_device_tree(machine->dtb, &fdt_size);
> +        if (!machine->fdt) {
> +            error_report("Failed to load device tree");
> +            exit(1);
> +        }
> +
> +        if (machine->kernel_cmdline && *machine->kernel_cmdline) {
> +            if (fdt_path_offset(machine->fdt, "/chosen") < 0) {
> +                qemu_fdt_add_subnode(machine->fdt, "/chosen");
> +            }
> +            qemu_fdt_setprop_string(machine->fdt, "/chosen", "bootargs",
> +                                    machine->kernel_cmdline);
> +        }
> +    }
> +
> +    if (machine->kernel_filename) {
> +        hwaddr kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
> +                                                                
> firmware_end_addr);
> +        riscv_load_kernel(machine, &boot_info, kernel_start_addr, true, 
> NULL);
> +        kernel_entry = boot_info.image_low_addr;
> +    }
> +
> +    if (machine->dtb) {
> +        fdt_load_addr = 
> riscv_compute_fdt_addr(cv1800b_memmap[CV1800B_DEV_DRAM].base,
> +                                               machine->ram_size, machine, 
> &boot_info);
> +        rom_add_blob_fixed_as("fdt", machine->fdt, fdt_size, fdt_load_addr,
> +                              &address_space_memory);
> +    }
> +
> +    riscv_setup_rom_reset_vec(machine, &s->soc.cpus,
> +                              firmware_load_addr,
> +                              cv1800b_memmap[CV1800B_DEV_ROM].base,
> +                              cv1800b_memmap[CV1800B_DEV_ROM].size,
> +                              kernel_entry,
> +                              fdt_load_addr);
> +}
> +
> +static void milkv_duo_machine_class_init(ObjectClass *oc, const void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +    static const char *const valid_cpu_types[] = {
> +        TYPE_RISCV_CPU_THEAD_C906,
> +        NULL
> +    };
> +
> +    mc->desc = "Milk-V Duo Board (CV1800B)";
> +    mc->init = milkv_duo_init;
> +    mc->max_cpus = 2;
> +    mc->default_cpu_type = TYPE_RISCV_CPU_THEAD_C906;
> +    mc->valid_cpu_types = valid_cpu_types;
> +    mc->default_ram_size = 64 * MiB;
> +    mc->default_ram_id = "riscv.milkv_duo.ram";
> +}
> +
> +static const TypeInfo milkv_duo_machine_type_info = {
> +    .name = TYPE_MILK_V_DUO,
> +    .parent = TYPE_MACHINE,
> +    .instance_size = sizeof(MilkVDuoState),
> +    .class_init = milkv_duo_machine_class_init,
> +    .interfaces = riscv64_machine_interfaces,
> +};
> +
> +static void milkv_duo_machine_register_types(void)
> +{
> +    type_register_static(&milkv_duo_machine_type_info);
> +}
> +
> +type_init(milkv_duo_machine_register_types)
> --
> 2.54.0.1136.gdb2ca164c4-goog
>
>

Reply via email to