From 655073e4e179e601e35a444f585d8e2049df97f5 Mon Sep 17 00:00:00 2001 From: Xianglai Li Date: Wed, 5 Feb 2025 19:56:54 +0800 Subject: [PATCH 1/5] target/loongarch: fix vcpu reset command word issue When the KVM_REG_LOONGARCH_VCPU_RESET command word is sent to the kernel through the kvm_set_one_reg interface, the parameter source needs to be a legal address, otherwise the kernel will return an error and the command word will fail to be sent. Signed-off-by: Xianglai Li --- target/loongarch/cpu.c | 2 +- target/loongarch/kvm/kvm.c | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index ee764f0bc7..570ce8be3b 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -638,8 +638,8 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) loongarch_cpu_register_gdb_regs_for_features(cs); - cpu_reset(cs); qemu_init_vcpu(cs); + cpu_reset(cs); lacc->parent_realize(dev, errp); } diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 0acdd5c4c1..277210ca04 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -590,9 +590,16 @@ static int kvm_loongarch_get_lbt(CPUState *cs) void kvm_arch_reset_vcpu(CPUState *cs) { CPULoongArchState *env = cpu_env(cs); + int ret = 0; + uint64_t unused = 0; env->mp_state = KVM_MP_STATE_RUNNABLE; - kvm_set_one_reg(cs, KVM_REG_LOONGARCH_VCPU_RESET, 0); + ret = kvm_set_one_reg(cs, KVM_REG_LOONGARCH_VCPU_RESET, &unused); + if (ret) { + error_report("Failed to set KVM_REG_LOONGARCH_VCPU_RESET: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } } static int kvm_loongarch_get_mpstate(CPUState *cs) -- Gitee From 2a51f062a46c2e3fbd96a1d75f9d53cab449f4ac Mon Sep 17 00:00:00 2001 From: Xianglai Li Date: Fri, 21 Mar 2025 20:40:37 +0800 Subject: [PATCH 2/5] target/loongarch: Fix the cpu unplug resource leak When the cpu is created, qemu_add_vm_change_state_handler is called in the kvm_arch_init_vcpu function to create the VMChangeStateEntry resource. However, the resource is not released when the cpu is destroyed. This results in a qemu process segment error when the virtual machine restarts after the cpu is unplugged. This patch solves the problem by adding the corresponding resource release process to the kvm_arch_destroy_vcpu function. Signed-off-by: Xianglai Li --- target/loongarch/cpu.c | 2 +- target/loongarch/cpu.h | 1 + target/loongarch/kvm/kvm.c | 5 ++++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 570ce8be3b..561566f3a0 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -573,7 +573,7 @@ static void loongarch_cpu_reset_hold(Object *obj) env->CSR_ECFG = FIELD_DP64(env->CSR_ECFG, CSR_ECFG, VS, 0); env->CSR_ECFG = FIELD_DP64(env->CSR_ECFG, CSR_ECFG, LIE, 0); - env->CSR_ESTAT = env->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2)); + env->CSR_ESTAT = 0; env->CSR_RVACFG = FIELD_DP64(env->CSR_RVACFG, CSR_RVACFG, RBITS, 0); env->CSR_CPUID = cs->cpu_index; env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0); diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 9af622aba5..6cc717c5ea 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -427,6 +427,7 @@ struct ArchCPU { const char *dtb_compatible; /* used by KVM_REG_LOONGARCH_COUNTER ioctl to access guest time counters */ uint64_t kvm_state_counter; + VMChangeStateEntry *vmsentry; }; /** diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 277210ca04..f6e008a517 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -905,9 +905,10 @@ int kvm_arch_init_vcpu(CPUState *cs) uint64_t val; int ret; Error *local_err = NULL; + LoongArchCPU *cpu = LOONGARCH_CPU(cs); ret = 0; - qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs); + cpu->vmsentry = qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs); if (!kvm_get_one_reg(cs, KVM_REG_LOONGARCH_DEBUG_INST, &val)) { brk_insn = val; @@ -928,6 +929,8 @@ int kvm_arch_init_vcpu(CPUState *cs) int kvm_arch_destroy_vcpu(CPUState *cs) { + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + qemu_del_vm_change_state_handler(cpu->vmsentry); return 0; } -- Gitee From 16670675cbf7fc4db147a698ba7787d2e2fa675b Mon Sep 17 00:00:00 2001 From: Xianglai Li Date: Wed, 26 Mar 2025 17:02:37 +0800 Subject: [PATCH 3/5] hw/loongarch/boot: Adjust the loading position of the initrd When only the -kernel parameter is used to load the elf kernel, the initrd is loaded in the ram. If the initrd size is too large, the loading fails, resulting in a VM startup failure. This patch first loads initrd near the kernel. When the nearby memory space of the kernel is insufficient, it tries to load it to the starting position of high memory. If there is still not enough, qemu will report an error and ask the user to increase the memory space for the virtual machine to boot. Signed-off-by: Xianglai Li --- hw/loongarch/boot.c | 53 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 53dcefbb55..39c4a6d8c6 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -171,6 +171,48 @@ static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); } +static void find_initrd_loadoffset(struct loongarch_boot_info *info, + uint64_t kernel_high, ssize_t kernel_size) +{ + hwaddr base, size, gap, low_end; + ram_addr_t initrd_end, initrd_start; + + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + initrd_start = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB); + initrd_end = initrd_start + initrd_size; + + size = info->ram_size; + low_end = base + MIN(size, gap); + if (initrd_end <= low_end) { + initrd_offset = initrd_start; + return; + } + + if (size <= gap) { + error_report("The low memory too small for initial ram disk '%s'," + "You need to expand the memory space", + info->initrd_filename); + exit(1); + } + + /* + * Try to load initrd in the high memory + */ + size -= gap; + base = VIRT_HIGHMEM_BASE; + initrd_start = ROUND_UP(base, 64 * KiB); + if (initrd_size <= size) { + initrd_offset = initrd_start; + return; + } + + error_report("The high memory too small for initial ram disk '%s'," + "You need to expand the memory space", + info->initrd_filename); + exit(1); +} + static int64_t load_kernel_info(struct loongarch_boot_info *info) { uint64_t kernel_entry, kernel_low, kernel_high; @@ -192,16 +234,9 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) if (info->initrd_filename) { initrd_size = get_image_size(info->initrd_filename); if (initrd_size > 0) { - initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB); - - if (initrd_offset + initrd_size > info->ram_size) { - error_report("memory too small for initial ram disk '%s'", - info->initrd_filename); - exit(1); - } - + find_initrd_loadoffset(info, kernel_high, kernel_size); initrd_size = load_image_targphys(info->initrd_filename, initrd_offset, - info->ram_size - initrd_offset); + initrd_size); } if (initrd_size == (target_ulong)-1) { -- Gitee From 4044284b230182cbaeb401bdb1b65dcbd11c7550 Mon Sep 17 00:00:00 2001 From: Xianglai Li Date: Mon, 7 Apr 2025 18:59:42 +0800 Subject: [PATCH 4/5] hw/rtc: Fixed loongson rtc emulation errors The expire time is sent to the timer only when the expire Time is greater than 0 or greater than now. Otherwise, the timer will trigger interruption continuously. Timer interrupts are sent using pulse functions. Signed-off-by: Xianglai Li --- hw/loongarch/virt.c | 9 +++++++-- hw/rtc/ls7a_rtc.c | 22 +++++++++++++--------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 0c24e632bb..ce026a4c3c 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -51,6 +51,11 @@ #include "qemu/error-report.h" #include "qemu/guest-random.h" +#define FDT_IRQ_FLAGS_EDGE_LO_HI 1 +#define FDT_IRQ_FLAGS_EDGE_HI_LO 2 +#define FDT_IRQ_FLAGS_LEVEL_HI 4 +#define FDT_IRQ_FLAGS_LEVEL_LO 8 + static bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) { if (lvms->veiointc == ON_OFF_AUTO_OFF) { @@ -275,7 +280,7 @@ static void fdt_add_rtc_node(LoongArchVirtMachineState *lvms, "loongson,ls7a-rtc"); qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", - VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4); + VIRT_RTC_IRQ - VIRT_GSI_BASE , FDT_IRQ_FLAGS_EDGE_LO_HI); qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", *pch_pic_phandle); g_free(nodename); @@ -334,7 +339,7 @@ static void fdt_add_uart_node(LoongArchVirtMachineState *lvms, qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000); if (chosen) qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); - qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4); + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, FDT_IRQ_FLAGS_LEVEL_HI); qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", *pch_pic_phandle); g_free(nodename); diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c index 1f9e38a735..be9546c850 100644 --- a/hw/rtc/ls7a_rtc.c +++ b/hw/rtc/ls7a_rtc.c @@ -145,20 +145,22 @@ static void toymatch_write(LS7ARtcState *s, uint64_t val, int num) now = qemu_clock_get_ms(rtc_clock); toymatch_val_to_time(s, val, &tm); expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000; - timer_mod(s->toy_timer[num], expire_time); + if (expire_time > now) + timer_mod(s->toy_timer[num], expire_time); } } static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num) { - uint64_t expire_ns; + int64_t expire_ns; /* it do not support write when toy disabled */ if (rtc_enabled(s)) { s->rtcmatch[num] = val; /* calculate expire time */ expire_ns = ticks_to_ns(val) - ticks_to_ns(s->offset_rtc); - timer_mod_ns(s->rtc_timer[num], expire_ns); + if (expire_ns > 0) + timer_mod_ns(s->rtc_timer[num], expire_ns); } } @@ -185,7 +187,7 @@ static void ls7a_rtc_stop(LS7ARtcState *s) static void ls7a_toy_start(LS7ARtcState *s) { int i; - uint64_t expire_time, now; + int64_t expire_time, now; struct tm tm = {}; now = qemu_clock_get_ms(rtc_clock); @@ -194,19 +196,21 @@ static void ls7a_toy_start(LS7ARtcState *s) for (i = 0; i < TIMER_NUMS; i++) { toymatch_val_to_time(s, s->toymatch[i], &tm); expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000; - timer_mod(s->toy_timer[i], expire_time); + if (expire_time > now) + timer_mod(s->toy_timer[i], expire_time); } } static void ls7a_rtc_start(LS7ARtcState *s) { int i; - uint64_t expire_time; + int64_t expire_time; /* recalculate expire time and enable timer */ for (i = 0; i < TIMER_NUMS; i++) { expire_time = ticks_to_ns(s->rtcmatch[i]) - ticks_to_ns(s->offset_rtc); - timer_mod_ns(s->rtc_timer[i], expire_time); + if (expire_time > 0) + timer_mod_ns(s->rtc_timer[i], expire_time); } } @@ -370,7 +374,7 @@ static void toy_timer_cb(void *opaque) LS7ARtcState *s = opaque; if (toy_enabled(s)) { - qemu_irq_raise(s->irq); + qemu_irq_pulse(s->irq); } } @@ -379,7 +383,7 @@ static void rtc_timer_cb(void *opaque) LS7ARtcState *s = opaque; if (rtc_enabled(s)) { - qemu_irq_raise(s->irq); + qemu_irq_pulse(s->irq); } } -- Gitee From d6f75f9e532a4a4b6bb4610049f4fa7f26160733 Mon Sep 17 00:00:00 2001 From: Xianglai Li Date: Thu, 20 Feb 2025 19:24:18 +0800 Subject: [PATCH 5/5] hw/intc: Add extioi ability of 256 vcpu interrupt routing Add the feature field for the CPU-encoded interrupt route to extioi and the corresponding mechanism for backup recovery. Signed-off-by: Xianglai Li --- hw/intc/loongarch_extioi_kvm.c | 65 ++++++++++++++++++++++++++++-- hw/loongarch/virt.c | 2 + include/hw/intc/loongarch_extioi.h | 4 ++ linux-headers/asm-loongarch/kvm.h | 10 +++++ 4 files changed, 77 insertions(+), 4 deletions(-) diff --git a/hw/intc/loongarch_extioi_kvm.c b/hw/intc/loongarch_extioi_kvm.c index f5bbc33255..2e7c764b7c 100644 --- a/hw/intc/loongarch_extioi_kvm.c +++ b/hw/intc/loongarch_extioi_kvm.c @@ -18,8 +18,32 @@ static void kvm_extioi_access_regs(int fd, uint64_t addr, void *val, int is_write) { - kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS, - addr, val, is_write, &error_abort); + kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS, + addr, val, is_write, &error_abort); +} + +static void kvm_extioi_access_sw_status(int fd, uint64_t addr, + void *val, bool is_write) +{ + kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_SW_STATUS, + addr, val, is_write, &error_abort); +} + +static void kvm_extioi_save_load_sw_status(void *opaque, bool is_write) +{ + KVMLoongArchExtIOI *s = (KVMLoongArchExtIOI *)opaque; + KVMLoongArchExtIOIClass *class = KVM_LOONGARCH_EXTIOI_GET_CLASS(s); + int fd = class->dev_fd; + int addr; + + addr = KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_NUM_CPU; + kvm_extioi_access_sw_status(fd, addr, (void *)&s->num_cpu, is_write); + + addr = KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_FEATURE; + kvm_extioi_access_sw_status(fd, addr, (void *)&s->features, is_write); + + addr = KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_STATE; + kvm_extioi_access_sw_status(fd, addr, (void *)&s->status, is_write); } static int kvm_loongarch_extioi_pre_save(void *opaque) @@ -41,6 +65,8 @@ static int kvm_loongarch_extioi_pre_save(void *opaque) kvm_extioi_access_regs(fd, EXTIOI_COREISR_START, (void *)s->coreisr, false); + kvm_extioi_save_load_sw_status(opaque, false); + return 0; } @@ -61,12 +87,19 @@ static int kvm_loongarch_extioi_post_load(void *opaque, int version_id) (void *)s->sw_coremap, true); kvm_extioi_access_regs(fd, EXTIOI_COREISR_START, (void *)s->coreisr, true); + kvm_extioi_save_load_sw_status(opaque, true); + + kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, + KVM_DEV_LOONGARCH_EXTIOI_CTRL_LOAD_FINISHED, + NULL, true, &error_abort); + return 0; } static void kvm_loongarch_extioi_realize(DeviceState *dev, Error **errp) { KVMLoongArchExtIOIClass *extioi_class = KVM_LOONGARCH_EXTIOI_GET_CLASS(dev); + KVMLoongArchExtIOI *s = KVM_LOONGARCH_EXTIOI(dev); struct kvm_create_device cd = {0}; Error *err = NULL; int ret,i; @@ -77,6 +110,10 @@ static void kvm_loongarch_extioi_realize(DeviceState *dev, Error **errp) return; } + if (s->features & BIT(EXTIOI_HAS_VIRT_EXTENSION)) { + s->features |= EXTIOI_VIRT_HAS_FEATURES; + } + if (!extioi_class->is_created) { cd.type = KVM_DEV_TYPE_LA_EXTIOI; ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); @@ -87,6 +124,15 @@ static void kvm_loongarch_extioi_realize(DeviceState *dev, Error **errp) } extioi_class->is_created = true; extioi_class->dev_fd = cd.fd; + + kvm_device_access(cd.fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, + KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU, + &s->num_cpu, true, NULL); + + kvm_device_access(cd.fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, + KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_FEATURE, + &s->features, true, NULL); + fprintf(stdout, "Create LoongArch extioi irqchip in KVM done!\n"); } @@ -102,8 +148,8 @@ static void kvm_loongarch_extioi_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_kvm_extioi_core = { .name = "kvm-extioi-single", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .pre_save = kvm_loongarch_extioi_pre_save, .post_load = kvm_loongarch_extioi_post_load, .fields = (VMStateField[]) { @@ -119,10 +165,20 @@ static const VMStateDescription vmstate_kvm_extioi_core = { EXTIOI_IRQS_IPMAP_SIZE / 4), VMSTATE_UINT32_ARRAY(coremap, KVMLoongArchExtIOI, EXTIOI_IRQS / 4), VMSTATE_UINT8_ARRAY(sw_coremap, KVMLoongArchExtIOI, EXTIOI_IRQS), + VMSTATE_UINT32(num_cpu, KVMLoongArchExtIOI), + VMSTATE_UINT32(features, KVMLoongArchExtIOI), + VMSTATE_UINT32(status, KVMLoongArchExtIOI), VMSTATE_END_OF_LIST() } }; +static Property extioi_properties[] = { + DEFINE_PROP_UINT32("num-cpu", KVMLoongArchExtIOI, num_cpu, 1), + DEFINE_PROP_BIT("has-virtualization-extension", KVMLoongArchExtIOI, + features, EXTIOI_HAS_VIRT_EXTENSION, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static void kvm_loongarch_extioi_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -131,6 +187,7 @@ static void kvm_loongarch_extioi_class_init(ObjectClass *oc, void *data) extioi_class->parent_realize = dc->realize; dc->realize = kvm_loongarch_extioi_realize; extioi_class->is_created = false; + device_class_set_props(dc, extioi_properties); dc->vmsd = &vmstate_kvm_extioi_core; } diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index ce026a4c3c..233297d78f 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -874,6 +874,8 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) /* Create EXTIOI device */ if (kvm_enabled() && kvm_irqchip_in_kernel()) { extioi = qdev_new(TYPE_KVM_LOONGARCH_EXTIOI); + qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.max_cpus); + qdev_prop_set_bit(extioi, "has-virtualization-extension", true); sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); } else { extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index 9966cd98d3..92b38d5c38 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -94,6 +94,10 @@ struct LoongArchExtIOI { struct KVMLoongArchExtIOI { SysBusDevice parent_obj; + uint32_t num_cpu; + uint32_t features; + uint32_t status; + /* hardware state */ uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2]; uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT]; diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h index 13c1280662..34abd65939 100644 --- a/linux-headers/asm-loongarch/kvm.h +++ b/linux-headers/asm-loongarch/kvm.h @@ -141,6 +141,16 @@ struct kvm_iocsr_entry { #define KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS 0x40000003 +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_SW_STATUS 0x40000006 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_NUM_CPU 0x0 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_FEATURE 0x1 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_STATE 0x2 + +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL 0x40000007 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU 0x0 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_FEATURE 0x1 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_LOAD_FINISHED 0x3 + #define KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL 0x40000004 #define KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT 0 -- Gitee