qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v2 00/30] Add Loongarch softmmu support.
@ 2021-11-11  1:34 Xiaojuan Yang
  2021-11-11  1:34 ` [RFC PATCH v2 01/30] target/loongarch: Update README Xiaojuan Yang
                   ` (30 more replies)
  0 siblings, 31 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:34 UTC (permalink / raw)
  To: qemu-devel

Sorry only part of the v2 patch succeed. I consulted GNU sysadmin,
He said our mail server was getting temporarily banned by fail2ban. Now the ban
was removed. I resend the v2 series patch. For uefi is preparing to submit to
the community only uefi binary can be provided now. All of the series patch
add RFC title.

This series patch add softmmu support for LoongArch.
Base on the linux-user emulation support V9 patch.
  * https://patchew.org/QEMU/1630586467-22463-1-git-send-email-gaosong@loongson.cn/diff/1636340895-5255-1-git-send-email-gaosong@loongson.cn/

The latest kernel:
  * https://github.com/loongson/linux/tree/loongarch-next
The manual:
  * https://github.com/loongson/LoongArch-Documentation/releases/tag/2021.10.11

Changes for v2:
1.Combine patch 2 and 3 into one.
2.Adjust the order of the patch.
3.Put all the binaries on the github.
4.Modify some emulate errors when use the kernel from the github.
5.Adjust some format problem and the Naming problem 
6.Others mainly follow Richard's code review comments.

Please help review!

Thanks

Xiaojuan Yang (30):
  target/loongarch: Update README
  target/loongarch: Add CSR registers definition
  target/loongarch: Add basic vmstate description of CPU.
  target/loongarch: Define exceptions for LoongArch.
  target/loongarch: Implement qmp_query_cpu_definitions()
  target/loongarch: Add stabletimer support
  target/loongarch: Add MMU support for LoongArch CPU.
  target/loongarch: Add LoongArch CSR/IOCSR instruction
  target/loongarch: Add TLB instruction support
  target/loongarch: Add other core instructions support
  target/loongarch: Add LoongArch interrupt and exception handle
  target/loongarch: Add timer related instructions support.
  target/loongarch: Add gdb support.
  target/loongarch: Implement privilege instructions disassembly
  hw/pci-host: Add ls7a1000 PCIe Host bridge support for Loongson
    Platform
  hw/loongarch: Add a virt LoongArch 3A5000 board support
  hw/loongarch: Add LoongArch cpu interrupt support(CPUINTC)
  hw/loongarch: Add LoongArch ipi interrupt support(IPI)
  hw/intc: Add LoongArch ls7a interrupt controller support(PCH-PIC)
  hw/intc: Add LoongArch ls7a msi interrupt controller support(PCH-MSI)
  hw/intc: Add LoongArch extioi interrupt controller(EIOINTC)
  hw/loongarch: Add irq hierarchy for the system
  hw/loongarch: Add some devices support for 3A5000.
  hw/loongarch: Add LoongArch ls7a rtc device support
  hw/loongarch: Add default bios startup support.
  hw/loongarch: Add -kernel and -initrd options support
  hw/loongarch: Add LoongArch smbios support
  hw/loongarch: Add LoongArch acpi support
  hw/loongarch: Add machine->possible_cpus
  hw/loongarch: Add Numa support.

 .../devices/loongarch64-softmmu/default.mak   |   3 +
 configs/targets/loongarch64-softmmu.mak       |   4 +
 gdb-xml/loongarch-base64.xml                  |  43 +
 gdb-xml/loongarch-fpu64.xml                   |  57 ++
 hw/Kconfig                                    |   1 +
 hw/acpi/Kconfig                               |   4 +
 hw/acpi/ls7a.c                                | 349 +++++++
 hw/acpi/meson.build                           |   1 +
 hw/intc/Kconfig                               |  12 +
 hw/intc/loongarch_extioi.c                    | 588 ++++++++++++
 hw/intc/loongarch_pch_msi.c                   |  73 ++
 hw/intc/loongarch_pch_pic.c                   | 283 ++++++
 hw/intc/meson.build                           |   3 +
 hw/loongarch/Kconfig                          |  22 +
 hw/loongarch/acpi-build.c                     | 653 +++++++++++++
 hw/loongarch/fw_cfg.c                         |  33 +
 hw/loongarch/fw_cfg.h                         |  15 +
 hw/loongarch/ipi.c                            | 146 +++
 hw/loongarch/loongarch_int.c                  |  59 ++
 hw/loongarch/ls3a5000_virt.c                  | 647 +++++++++++++
 hw/loongarch/meson.build                      |   7 +
 hw/meson.build                                |   1 +
 hw/pci-host/Kconfig                           |   4 +
 hw/pci-host/ls7a.c                            | 223 +++++
 hw/pci-host/meson.build                       |   1 +
 hw/rtc/Kconfig                                |   3 +
 hw/rtc/ls7a_rtc.c                             | 323 +++++++
 hw/rtc/meson.build                            |   1 +
 include/exec/poison.h                         |   2 +
 include/hw/acpi/ls7a.h                        |  53 ++
 include/hw/intc/loongarch_extioi.h            | 101 ++
 include/hw/intc/loongarch_pch_msi.h           |  16 +
 include/hw/intc/loongarch_pch_pic.h           |  49 +
 include/hw/loongarch/gipi.h                   |  37 +
 include/hw/loongarch/loongarch.h              |  78 ++
 include/hw/pci-host/ls7a.h                    |  66 ++
 include/sysemu/arch_init.h                    |   1 +
 qapi/machine-target.json                      |   6 +-
 qapi/machine.json                             |   2 +-
 softmmu/qdev-monitor.c                        |   3 +-
 target/Kconfig                                |   1 +
 target/loongarch/Kconfig                      |   2 +
 target/loongarch/README                       |  20 +
 target/loongarch/cpu-csr.h                    | 334 +++++++
 target/loongarch/cpu-param.h                  |   3 +
 target/loongarch/cpu.c                        | 390 ++++++++
 target/loongarch/cpu.h                        | 220 ++++-
 target/loongarch/csr_helper.c                 | 331 +++++++
 target/loongarch/disas.c                      |  86 ++
 target/loongarch/gdbstub.c                    |  97 ++
 target/loongarch/helper.h                     |  24 +
 target/loongarch/insn_trans/trans_core.c.inc  | 570 +++++++++++
 target/loongarch/insn_trans/trans_extra.c.inc |  32 +
 target/loongarch/insns.decode                 |  51 +
 target/loongarch/internals.h                  |  26 +
 target/loongarch/machine.c                    | 210 ++++
 target/loongarch/meson.build                  |  10 +
 target/loongarch/op_helper.c                  |  58 ++
 target/loongarch/stabletimer.c                |  70 ++
 target/loongarch/tlb_helper.c                 | 901 ++++++++++++++++++
 target/loongarch/translate.c                  |   7 +
 61 files changed, 7410 insertions(+), 6 deletions(-)
 create mode 100644 configs/devices/loongarch64-softmmu/default.mak
 create mode 100644 configs/targets/loongarch64-softmmu.mak
 create mode 100644 gdb-xml/loongarch-base64.xml
 create mode 100644 gdb-xml/loongarch-fpu64.xml
 create mode 100644 hw/acpi/ls7a.c
 create mode 100644 hw/intc/loongarch_extioi.c
 create mode 100644 hw/intc/loongarch_pch_msi.c
 create mode 100644 hw/intc/loongarch_pch_pic.c
 create mode 100644 hw/loongarch/Kconfig
 create mode 100644 hw/loongarch/acpi-build.c
 create mode 100644 hw/loongarch/fw_cfg.c
 create mode 100644 hw/loongarch/fw_cfg.h
 create mode 100644 hw/loongarch/ipi.c
 create mode 100644 hw/loongarch/loongarch_int.c
 create mode 100644 hw/loongarch/ls3a5000_virt.c
 create mode 100644 hw/loongarch/meson.build
 create mode 100644 hw/pci-host/ls7a.c
 create mode 100644 hw/rtc/ls7a_rtc.c
 create mode 100644 include/hw/acpi/ls7a.h
 create mode 100644 include/hw/intc/loongarch_extioi.h
 create mode 100644 include/hw/intc/loongarch_pch_msi.h
 create mode 100644 include/hw/intc/loongarch_pch_pic.h
 create mode 100644 include/hw/loongarch/gipi.h
 create mode 100644 include/hw/loongarch/loongarch.h
 create mode 100644 include/hw/pci-host/ls7a.h
 create mode 100644 target/loongarch/Kconfig
 create mode 100644 target/loongarch/cpu-csr.h
 create mode 100644 target/loongarch/csr_helper.c
 create mode 100644 target/loongarch/gdbstub.c
 create mode 100644 target/loongarch/insn_trans/trans_core.c.inc
 create mode 100644 target/loongarch/machine.c
 create mode 100644 target/loongarch/stabletimer.c
 create mode 100644 target/loongarch/tlb_helper.c

-- 
2.27.0



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

* [RFC PATCH v2 01/30] target/loongarch: Update README
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
@ 2021-11-11  1:34 ` Xiaojuan Yang
  2021-11-11 11:50   ` chen huacai
  2021-11-11  1:35 ` [RFC PATCH v2 02/30] target/loongarch: Add CSR registers definition Xiaojuan Yang
                   ` (29 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

Mainly introduce how to run the softmmu

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/README | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/target/loongarch/README b/target/loongarch/README
index 09f809cf80..6f64bde22f 100644
--- a/target/loongarch/README
+++ b/target/loongarch/README
@@ -71,6 +71,26 @@
       ./qemu-loongarch64  /opt/clfs/usr/bin/pwd
       ...
 
+- Softmmu emulation
+
+  Add support softmmu emulation support in the following series patches.
+  Mainly emulate a virt 3A5000 board that is not exactly the same as the host.
+  Kernel code is on the github and the uefi code will be opened in the near future.
+  All required binaries can get from github for test.
+
+  1.Download kernel and the cross-tools.(vmlinux)
+
+      wget https://github.com/loongson/linux
+      wget https://github.com/loongson/build-tools/releases/latest/download/loongarch64-clfs-20210831-cross-tools.tar.xz
+
+  2.Download the clfs-system and made a ramdisk with busybox.(ramdisk)
+
+  3.Run with command,eg:
+
+   ./build/qemu-system-loongarch64 -m 4G -smp 16 --cpu Loongson-3A5000 --machine loongson7a -kernel ./vmlinux -initrd ./ramdisk  -append "root=/dev/ram console=ttyS0,115200 rdinit=/sbin/init loglevel=8" -monitor tcp::4000,server,nowait -nographic
+
+The vmlinux, ramdisk and uefi binary loongarch_bios.bin can get from :
+    git clone https://github.com/yangxiaojuan-loongson/qemu-binary
 
 - Note.
   We can get the latest LoongArch documents or LoongArch tools at https://github.com/loongson/
-- 
2.27.0



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

* [RFC PATCH v2 02/30] target/loongarch: Add CSR registers definition
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
  2021-11-11  1:34 ` [RFC PATCH v2 01/30] target/loongarch: Update README Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 13:29   ` Richard Henderson
  2021-11-11 13:33   ` Richard Henderson
  2021-11-11  1:35 ` [RFC PATCH v2 03/30] target/loongarch: Add basic vmstate description of CPU Xiaojuan Yang
                   ` (28 subsequent siblings)
  30 siblings, 2 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

1.Define All the CSR registers and its field.
2.Set some default csr values.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/cpu-csr.h | 334 +++++++++++++++++++++++++++++++++++++
 target/loongarch/cpu.c     |  12 ++
 target/loongarch/cpu.h     | 127 ++++++++++++++
 3 files changed, 473 insertions(+)
 create mode 100644 target/loongarch/cpu-csr.h

diff --git a/target/loongarch/cpu-csr.h b/target/loongarch/cpu-csr.h
new file mode 100644
index 0000000000..ef7511bf51
--- /dev/null
+++ b/target/loongarch/cpu-csr.h
@@ -0,0 +1,334 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU LoongArch CPU CSR registers
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef LOONGARCH_CPU_CSR_H
+#define LOONGARCH_CPU_CSR_H
+
+/* Base on: kernal: arch/loongarch/include/asm/loongarch.h */
+
+/* Basic CSR register */
+#define LOONGARCH_CSR_CRMD           0x0 /* Current mode info */
+FIELD(CSR_CRMD, PLV, 0, 2)
+FIELD(CSR_CRMD, IE, 2, 1)
+FIELD(CSR_CRMD, DA, 3, 1)
+FIELD(CSR_CRMD, PG, 4, 1)
+FIELD(CSR_CRMD, DATF, 5, 2)
+FIELD(CSR_CRMD, DATM, 7, 2)
+FIELD(CSR_CRMD, WE, 9, 1)
+
+#define LOONGARCH_CSR_PRMD           0x1 /* Prev-exception mode info */
+FIELD(CSR_PRMD, PPLV, 0, 2)
+FIELD(CSR_PRMD, PIE, 2, 1)
+FIELD(CSR_PRMD, PWE, 3, 1)
+
+#define LOONGARCH_CSR_EUEN           0x2 /* Extended unit enable */
+FIELD(CSR_EUEN, FPE, 0, 1)
+FIELD(CSR_EUEN, SXE, 1, 1)
+FIELD(CSR_EUEN, ASXE, 2, 1)
+FIELD(CSR_EUEN, BTE, 3, 1)
+
+#define LOONGARCH_CSR_MISC           0x3 /* Misc config */
+
+#define LOONGARCH_CSR_ECFG           0x4 /* Exception config */
+FIELD(CSR_ECFG, LIE, 0, 13)
+FIELD(CSR_ECFG, VS, 16, 3)
+
+#define LOONGARCH_CSR_ESTAT          0x5 /* Exception status */
+FIELD(CSR_ESTAT, IS, 0, 13)
+FIELD(CSR_ESTAT, ECODE, 16, 6)
+FIELD(CSR_ESTAT, ESUBCODE, 22, 9)
+
+#define  EXCODE_IP                   64
+#define  EXCCODE_INT                 0
+#define  EXCCODE_PIL                 1
+#define  EXCCODE_PIS                 2
+#define  EXCCODE_PIF                 3
+#define  EXCCODE_PME                 4
+#define  EXCCODE_PNR                 5
+#define  EXCCODE_PNX                 6
+#define  EXCCODE_PPI                 7
+#define  EXCCODE_ADE                 8
+#define  EXCCODE_ALE                 9
+#define  EXCCODE_BCE                 10
+#define  EXCCODE_SYS                 11
+#define  EXCCODE_BRK                 12
+#define  EXCCODE_INE                 13
+#define  EXCCODE_IPE                 14
+#define  EXCCODE_FPD                 15
+#define  EXCCODE_SXD                 16
+#define  EXCCODE_ASXD                17
+#define  EXCCODE_FPE                 18 /* Have different expsubcode */
+#define  EXCCODE_VFPE                18
+#define  EXCCODE_WPEF                19 /* Have different expsubcode */
+#define  EXCCODE_WPEM                19
+#define  EXCCODE_BTD                 20
+#define  EXCCODE_BTE                 21
+
+#define LOONGARCH_CSR_ERA            0x6 /* ERA */
+
+#define LOONGARCH_CSR_BADV           0x7 /* Bad virtual address */
+
+#define LOONGARCH_CSR_BADI           0x8 /* Bad instruction */
+
+#define LOONGARCH_CSR_EENTRY         0xc /* Exception enter base address */
+
+/* TLB related CSR register */
+#define LOONGARCH_CSR_TLBIDX         0x10 /* TLB Index, EHINV, PageSize, NP */
+FIELD(CSR_TLBIDX, INDEX, 0, 12)
+FIELD(CSR_TLBIDX, PS, 24, 6)
+FIELD(CSR_TLBIDX, NE, 31, 1)
+
+#define LOONGARCH_CSR_TLBEHI         0x11 /* TLB EntryHi without ASID */
+FIELD(CSR_TLBEHI, VPPN, 13, 35)
+
+#define LOONGARCH_CSR_TLBELO0        0x12 /* TLB EntryLo0 */
+FIELD(CSR_TLBELO0, V, 0, 1)
+FIELD(CSR_TLBELO0, D, 1, 1)
+FIELD(CSR_TLBELO0, PLV, 2, 2)
+FIELD(CSR_TLBELO0, MAT, 4, 2)
+FIELD(CSR_TLBELO0, G, 6, 1)
+FIELD(CSR_TLBELO0, PPN, 12, 36)
+FIELD(CSR_TLBELO0, NR, 61, 1)
+FIELD(CSR_TLBELO0, NX, 62, 1)
+FIELD(CSR_TLBELO0, RPLV, 63, 1)
+
+#define LOONGARCH_CSR_TLBELO1        0x13 /* 64 TLB EntryLo1 */
+FIELD(CSR_TLBELO1, V, 0, 1)
+FIELD(CSR_TLBELO1, D, 1, 1)
+FIELD(CSR_TLBELO1, PLV, 2, 2)
+FIELD(CSR_TLBELO1, MAT, 4, 2)
+FIELD(CSR_TLBELO1, G, 6, 1)
+FIELD(CSR_TLBELO1, PPN, 12, 36)
+FIELD(CSR_TLBELO1, NR, 61, 1)
+FIELD(CSR_TLBELO1, NX, 62, 1)
+FIELD(CSR_TLBELO1, RPLV, 63, 1)
+
+#define LOONGARCH_CSR_ASID           0x18 /* ASID */
+FIELD(CSR_ASID, ASID, 0, 10)
+FIELD(CSR_ASID, ASIDBITS, 16, 8)
+
+/* Page table base address when badv[47] = 0 */
+#define LOONGARCH_CSR_PGDL           0x19
+/* Page table base address when badv[47] = 1 */
+#define LOONGARCH_CSR_PGDH           0x1a
+
+#define LOONGARCH_CSR_PGD            0x1b /* Page table base */
+
+#define LOONGARCH_CSR_PWCL           0x1c /* PWCl */
+FIELD(CSR_PWCL, PTBASE, 0, 5)
+FIELD(CSR_PWCL, PTWIDTH, 5, 5)
+FIELD(CSR_PWCL, DIR1_BASE, 10, 5)
+FIELD(CSR_PWCL, DIR1_WIDTH, 15, 5)
+FIELD(CSR_PWCL, DIR2_BASE, 20, 5)
+FIELD(CSR_PWCL, DIR2_WIDTH, 25, 5)
+FIELD(CSR_PWCL, PTEWIDTH, 30, 2)
+
+#define LOONGARCH_CSR_PWCH           0x1d /* PWCh */
+FIELD(CSR_PWCH, DIR3_BASE, 0, 6)
+FIELD(CSR_PWCH, DIR3_WIDTH, 6, 6)
+FIELD(CSR_PWCH, DIR4_BASE, 12, 6)
+FIELD(CSR_PWCH, DIR4_WIDTH, 18, 6)
+
+#define LOONGARCH_CSR_STLBPS     0x1e /* 64 */
+FIELD(CSR_STLBPS, PS, 0, 5)
+
+#define LOONGARCH_CSR_RVACFG         0x1f
+
+/* Config CSR registers */
+#define LOONGARCH_CSR_CPUID          0x20 /* CPU core id */
+
+#define LOONGARCH_CSR_PRCFG1         0x21 /* Config1 */
+FIELD(CSR_PRCFG1, SAVE_NUM, 0, 4)
+FIELD(CSR_PRCFG1, TIMER_BITS, 4, 8)
+FIELD(CSR_PRCFG1, VSMAX, 12, 3)
+
+#define LOONGARCH_CSR_PRCFG2         0x22 /* Config2 */
+
+#define LOONGARCH_CSR_PRCFG3         0x23 /* Config3 */
+FIELD(CSR_PRCFG3, TLB_TYPE, 0, 4)
+FIELD(CSR_PRCFG3, MTLB_ENTRY, 4, 8)
+FIELD(CSR_PRCFG3, STLB_WAYS, 12, 8)
+FIELD(CSR_PRCFG3, STLB_SETS, 20, 8)
+
+/* Save registers */
+#define LOONGARCH_CSR_SAVE0            0x30
+#define LOONGARCH_CSR_SAVE1            0x31
+#define LOONGARCH_CSR_SAVE2            0x32
+#define LOONGARCH_CSR_SAVE3            0x33
+#define LOONGARCH_CSR_SAVE4            0x34
+#define LOONGARCH_CSR_SAVE5            0x35
+#define LOONGARCH_CSR_SAVE6            0x36
+#define LOONGARCH_CSR_SAVE7            0x37
+
+/* Timer registers */
+#define LOONGARCH_CSR_TMID           0x40 /* Timer ID */
+
+#define LOONGARCH_CSR_TCFG           0x41 /* Timer config */
+FIELD(CSR_TCFG, EN, 0, 1)
+FIELD(CSR_TCFG, PERIODIC, 1, 1)
+FIELD(CSR_TCFG, INIT_VAL, 2, 46)
+
+#define LOONGARCH_CSR_TVAL           0x42 /* Timer ticks remain */
+
+#define LOONGARCH_CSR_CNTC           0x43 /* Timer offset */
+
+#define LOONGARCH_CSR_TINTCLR        0x44 /* Timer interrupt clear */
+
+/* LLBCTL register */
+#define LOONGARCH_CSR_LLBCTL         0x60 /* LLBit control */
+
+/* Implement dependent */
+#define LOONGARCH_CSR_IMPCTL1        0x80 /* LoongArch config1 */
+
+#define LOONGARCH_CSR_IMPCTL2        0x81 /* LoongArch config2*/
+
+/* TLB Refill registers */
+#define LOONGARCH_CSR_TLBRENTRY      0x88 /* TLB refill exception address */
+#define LOONGARCH_CSR_TLBRBADV       0x89 /* TLB refill badvaddr */
+#define LOONGARCH_CSR_TLBRERA        0x8a /* TLB refill ERA */
+#define LOONGARCH_CSR_TLBRSAVE       0x8b /* KScratch for TLB refill */
+FIELD(CSR_TLBRERA, ISTLBR, 0, 1)
+FIELD(CSR_TLBRERA, PC, 2, 62)
+#define LOONGARCH_CSR_TLBRELO0       0x8c /* TLB refill entrylo0 */
+#define LOONGARCH_CSR_TLBRELO1       0x8d /* TLB refill entrylo1 */
+#define LOONGARCH_CSR_TLBREHI        0x8e /* TLB refill entryhi */
+FIELD(CSR_TLBREHI, PS, 0, 6)
+FIELD(CSR_TLBREHI, VPPN, 13, 35)
+#define LOONGARCH_CSR_TLBRPRMD       0x8f /* TLB refill mode info */
+FIELD(CSR_TLBRPRMD, PPLV, 0, 2)
+FIELD(CSR_TLBRPRMD, PIE, 2, 1)
+FIELD(CSR_TLBRPRMD, PWE, 4, 1)
+
+/* Machine Error registers */
+#define LOONGARCH_CSR_MERRCTL        0x90 /* ERRCTL */
+#define LOONGARCH_CSR_MERRINFO       0x91
+#define LOONGARCH_CSR_MERRINFO1      0x92
+#define LOONGARCH_CSR_MERRENT        0x93 /* MError exception base */
+#define LOONGARCH_CSR_MERRERA        0x94 /* MError exception PC */
+#define LOONGARCH_CSR_MERRSAVE       0x95 /* KScratch for error exception */
+
+#define LOONGARCH_CSR_CTAG           0x98 /* TagLo + TagHi */
+
+/* Direct map windows */
+#define LOONGARCH_CSR_DMWIN0         0x180 /* 64 direct map win0: MEM & IF */
+#define LOONGARCH_CSR_DMWIN1         0x181 /* 64 direct map win1: MEM & IF */
+#define LOONGARCH_CSR_DMWIN2         0x182 /* 64 direct map win2: MEM */
+#define LOONGARCH_CSR_DMWIN3         0x183 /* 64 direct map win3: MEM */
+#define  CSR_DMW_BASE_SH             48
+#define dmwin_va2pa(va) \
+    (va & (((unsigned long)1 << CSR_DMW_BASE_SH) - 1))
+
+/* Performance Counter registers */
+#define LOONGARCH_CSR_PERFCTRL0      0x200 /* 32 perf event 0 config */
+#define LOONGARCH_CSR_PERFCNTR0      0x201 /* 64 perf event 0 count value */
+#define LOONGARCH_CSR_PERFCTRL1      0x202 /* 32 perf event 1 config */
+#define LOONGARCH_CSR_PERFCNTR1      0x203 /* 64 perf event 1 count value */
+#define LOONGARCH_CSR_PERFCTRL2      0x204 /* 32 perf event 2 config */
+#define LOONGARCH_CSR_PERFCNTR2      0x205 /* 64 perf event 2 count value */
+#define LOONGARCH_CSR_PERFCTRL3      0x206 /* 32 perf event 3 config */
+#define LOONGARCH_CSR_PERFCNTR3      0x207 /* 64 perf event 3 count value */
+
+/* Debug registers */
+#define LOONGARCH_CSR_MWPC           0x300 /* data breakpoint config */
+#define LOONGARCH_CSR_MWPS           0x301 /* data breakpoint status */
+
+#define LOONGARCH_CSR_DB0ADDR        0x310 /* data breakpoint 0 address */
+#define LOONGARCH_CSR_DB0MASK        0x311 /* data breakpoint 0 mask */
+#define LOONGARCH_CSR_DB0CTL         0x312 /* data breakpoint 0 control */
+#define LOONGARCH_CSR_DB0ASID        0x313 /* data breakpoint 0 asid */
+
+#define LOONGARCH_CSR_DB1ADDR        0x318 /* data breakpoint 1 address */
+#define LOONGARCH_CSR_DB1MASK        0x319 /* data breakpoint 1 mask */
+#define LOONGARCH_CSR_DB1CTL         0x31a /* data breakpoint 1 control */
+#define LOONGARCH_CSR_DB1ASID        0x31b /* data breakpoint 1 asid */
+
+#define LOONGARCH_CSR_DB2ADDR        0x320 /* data breakpoint 2 address */
+#define LOONGARCH_CSR_DB2MASK        0x321 /* data breakpoint 2 mask */
+#define LOONGARCH_CSR_DB2CTL         0x322 /* data breakpoint 2 control */
+#define LOONGARCH_CSR_DB2ASID        0x323 /* data breakpoint 2 asid */
+
+#define LOONGARCH_CSR_DB3ADDR        0x328 /* data breakpoint 3 address */
+#define LOONGARCH_CSR_DB3MASK        0x329 /* data breakpoint 3 mask */
+#define LOONGARCH_CSR_DB3CTL         0x32a /* data breakpoint 3 control */
+#define LOONGARCH_CSR_DB3ASID        0x32b /* data breakpoint 3 asid */
+
+#define LOONGARCH_CSR_DB4ADDR        0x330 /* data breakpoint 4 address */
+#define LOONGARCH_CSR_DB4MASK        0x331 /* data breakpoint 4 maks */
+#define LOONGARCH_CSR_DB4CTL         0x332 /* data breakpoint 4 control */
+#define LOONGARCH_CSR_DB4ASID        0x333 /* data breakpoint 4 asid */
+
+#define LOONGARCH_CSR_DB5ADDR        0x338 /* data breakpoint 5 address */
+#define LOONGARCH_CSR_DB5MASK        0x339 /* data breakpoint 5 mask */
+#define LOONGARCH_CSR_DB5CTL         0x33a /* data breakpoint 5 control */
+#define LOONGARCH_CSR_DB5ASID        0x33b /* data breakpoint 5 asid */
+
+#define LOONGARCH_CSR_DB6ADDR        0x340 /* data breakpoint 6 address */
+#define LOONGARCH_CSR_DB6MASK        0x341 /* data breakpoint 6 mask */
+#define LOONGARCH_CSR_DB6CTL         0x342 /* data breakpoint 6 control */
+#define LOONGARCH_CSR_DB6ASID        0x343 /* data breakpoint 6 asid */
+
+#define LOONGARCH_CSR_DB7ADDR        0x348 /* data breakpoint 7 address */
+#define LOONGARCH_CSR_DB7MASK        0x349 /* data breakpoint 7 mask */
+#define LOONGARCH_CSR_DB7CTL         0x34a /* data breakpoint 7 control */
+#define LOONGARCH_CSR_DB7ASID        0x34b /* data breakpoint 7 asid */
+
+#define LOONGARCH_CSR_FWPC           0x380 /* instruction breakpoint config */
+#define LOONGARCH_CSR_FWPS           0x381 /* instruction breakpoint status */
+
+#define LOONGARCH_CSR_IB0ADDR        0x390 /* inst breakpoint 0 address */
+#define LOONGARCH_CSR_IB0MASK        0x391 /* inst breakpoint 0 mask */
+#define LOONGARCH_CSR_IB0CTL         0x392 /* inst breakpoint 0 control */
+#define LOONGARCH_CSR_IB0ASID        0x393 /* inst breakpoint 0 asid */
+
+#define LOONGARCH_CSR_IB1ADDR        0x398 /* inst breakpoint 1 address */
+#define LOONGARCH_CSR_IB1MASK        0x399 /* inst breakpoint 1 mask */
+#define LOONGARCH_CSR_IB1CTL         0x39a /* inst breakpoint 1 control */
+#define LOONGARCH_CSR_IB1ASID        0x39b /* inst breakpoint 1 asid */
+
+#define LOONGARCH_CSR_IB2ADDR        0x3a0 /* inst breakpoint 2 address */
+#define LOONGARCH_CSR_IB2MASK        0x3a1 /* inst breakpoint 2 mask */
+#define LOONGARCH_CSR_IB2CTL         0x3a2 /* inst breakpoint 2 control */
+#define LOONGARCH_CSR_IB2ASID        0x3a3 /* inst breakpoint 2 asid */
+
+#define LOONGARCH_CSR_IB3ADDR        0x3a8 /* inst breakpoint 3 address */
+#define LOONGARCH_CSR_IB3MASK        0x3a9 /* inst breakpoint 3 mask */
+#define LOONGARCH_CSR_IB3CTL         0x3aa /* inst breakpoint 3 control */
+#define LOONGARCH_CSR_IB3ASID        0x3ab /* inst breakpoint 3 asid */
+
+#define LOONGARCH_CSR_IB4ADDR        0x3b0 /* inst breakpoint 4 address */
+#define LOONGARCH_CSR_IB4MASK        0x3b1 /* inst breakpoint 4 mask */
+#define LOONGARCH_CSR_IB4CTL         0x3b2 /* inst breakpoint 4 control */
+#define LOONGARCH_CSR_IB4ASID        0x3b3 /* inst breakpoint 4 asid */
+
+#define LOONGARCH_CSR_IB5ADDR        0x3b8 /* inst breakpoint 5 address */
+#define LOONGARCH_CSR_IB5MASK        0x3b9 /* inst breakpoint 5 mask */
+#define LOONGARCH_CSR_IB5CTL         0x3ba /* inst breakpoint 5 control */
+#define LOONGARCH_CSR_IB5ASID        0x3bb /* inst breakpoint 5 asid */
+
+#define LOONGARCH_CSR_IB6ADDR        0x3c0 /* inst breakpoint 6 address */
+#define LOONGARCH_CSR_IB6MASK        0x3c1 /* inst breakpoint 6 mask */
+#define LOONGARCH_CSR_IB6CTL         0x3c2 /* inst breakpoint 6 control */
+#define LOONGARCH_CSR_IB6ASID        0x3c3 /* inst breakpoint 6 asid */
+
+#define LOONGARCH_CSR_IB7ADDR        0x3c8 /* inst breakpoint 7 address */
+#define LOONGARCH_CSR_IB7MASK        0x3c9 /* inst breakpoint 7 mask */
+#define LOONGARCH_CSR_IB7CTL         0x3ca /* inst breakpoint 7 control */
+#define LOONGARCH_CSR_IB7ASID        0x3cb /* inst breakpoint 7 asid */
+
+#define LOONGARCH_CSR_DBG            0x500 /* debug config */
+FIELD(CSR_DBG, DST, 0, 1)
+FIELD(CSR_DBG, DREV, 1, 7)
+FIELD(CSR_DBG, DEI, 8, 1)
+FIELD(CSR_DBG, DCL, 9, 1)
+FIELD(CSR_DBG, DFW, 10, 1)
+FIELD(CSR_DBG, DMW, 11, 1)
+FIELD(CSR_DBG, ECODE, 16, 6)
+
+#define LOONGARCH_CSR_DERA           0x501 /* Debug era */
+#define LOONGARCH_CSR_DESAVE         0x502 /* Debug save */
+
+#endif /* LOONGARCH_CPU_CSR_H */
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index ad11b98853..01a17d8221 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -156,6 +156,8 @@ static void loongarch_3a5000_initfn(Object *obj)
     data = FIELD_DP32(data, CPUCFG20, L3IU_WAYS, 0xf00f);
     data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 0x60);
     env->cpucfg[20] = data;
+
+    env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa);
 }
 
 static void loongarch_cpu_list_entry(gpointer data, gpointer user_data)
@@ -179,12 +181,22 @@ static void loongarch_cpu_reset(DeviceState *dev)
     LoongArchCPU *cpu = LOONGARCH_CPU(cs);
     LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(cpu);
     CPULoongArchState *env = &cpu->env;
+    uint64_t data;
 
     lacc->parent_reset(dev);
 
     env->fcsr0_mask = 0x1f1f031f;
     env->fcsr0 = 0x0;
 
+    /* Set direct mapping mode after reset */
+    data = FIELD_DP64(0, CSR_CRMD, PLV, 0);
+    data = FIELD_DP64(data, CSR_CRMD, IE, 0);
+    data = FIELD_DP64(data, CSR_CRMD, DA, 1);
+    data = FIELD_DP64(data, CSR_CRMD, PG, 0);
+    data = FIELD_DP64(data, CSR_CRMD, DATF, 1);
+    data = FIELD_DP64(data, CSR_CRMD, DATM, 1);
+    env->CSR_CRMD = data;
+
     restore_fp_status(env);
     cs->exception_index = EXCP_NONE;
 }
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 7509c77654..10fcd53104 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -11,6 +11,7 @@
 #include "exec/cpu-defs.h"
 #include "fpu/softfloat-types.h"
 #include "hw/registerfields.h"
+#include "cpu-csr.h"
 
 #define TCG_GUEST_DEFAULT_MO (0)
 
@@ -170,6 +171,132 @@ struct CPULoongArchState {
     uint64_t llval;
 
     uint64_t badaddr;
+
+    /* LoongArch CSR registers */
+    uint64_t CSR_CRMD;
+    uint64_t CSR_PRMD;
+    uint64_t CSR_EUEN;
+    uint64_t CSR_MISC;
+    uint64_t CSR_ECFG;
+    uint64_t CSR_ESTAT;
+    uint64_t CSR_ERA;
+    uint64_t CSR_BADV;
+    uint64_t CSR_BADI;
+    uint64_t CSR_EENTRY;
+    uint64_t CSR_TLBIDX;
+    uint64_t CSR_TLBEHI;
+    uint64_t CSR_TLBELO0;
+    uint64_t CSR_TLBELO1;
+    uint64_t CSR_ASID;
+    uint64_t CSR_PGDL;
+    uint64_t CSR_PGDH;
+    uint64_t CSR_PGD;
+    uint64_t CSR_PWCL;
+    uint64_t CSR_PWCH;
+    uint64_t CSR_STLBPS;
+    uint64_t CSR_RVACFG;
+    uint64_t CSR_CPUID;
+    uint64_t CSR_PRCFG1;
+    uint64_t CSR_PRCFG2;
+    uint64_t CSR_PRCFG3;
+    uint64_t CSR_SAVE0;
+    uint64_t CSR_SAVE1;
+    uint64_t CSR_SAVE2;
+    uint64_t CSR_SAVE3;
+    uint64_t CSR_SAVE4;
+    uint64_t CSR_SAVE5;
+    uint64_t CSR_SAVE6;
+    uint64_t CSR_SAVE7;
+    uint64_t CSR_TMID;
+    uint64_t CSR_TCFG;
+    uint64_t CSR_TVAL;
+    uint64_t CSR_CNTC;
+    uint64_t CSR_TINTCLR;
+    uint64_t CSR_LLBCTL;
+    uint64_t CSR_IMPCTL1;
+    uint64_t CSR_IMPCTL2;
+    uint64_t CSR_TLBRENTRY;
+    uint64_t CSR_TLBRBADV;
+    uint64_t CSR_TLBRERA;
+    uint64_t CSR_TLBRSAVE;
+    uint64_t CSR_TLBRELO0;
+    uint64_t CSR_TLBRELO1;
+    uint64_t CSR_TLBREHI;
+    uint64_t CSR_TLBRPRMD;
+    uint64_t CSR_MERRCTL;
+    uint64_t CSR_MERRINFO;
+    uint64_t CSR_MERRINFO1;
+    uint64_t CSR_MERRENT;
+    uint64_t CSR_MERRERA;
+    uint64_t CSR_MERRSAVE;
+    uint64_t CSR_CTAG;
+    uint64_t CSR_DMWIN0;
+    uint64_t CSR_DMWIN1;
+    uint64_t CSR_DMWIN2;
+    uint64_t CSR_DMWIN3;
+    uint64_t CSR_PERFCTRL0;
+    uint64_t CSR_PERFCNTR0;
+    uint64_t CSR_PERFCTRL1;
+    uint64_t CSR_PERFCNTR1;
+    uint64_t CSR_PERFCTRL2;
+    uint64_t CSR_PERFCNTR2;
+    uint64_t CSR_PERFCTRL3;
+    uint64_t CSR_PERFCNTR3;
+    uint64_t CSR_MWPC;
+    uint64_t CSR_MWPS;
+    uint64_t CSR_DB0ADDR;
+    uint64_t CSR_DB0MASK;
+    uint64_t CSR_DB0CTL;
+    uint64_t CSR_DB0ASID;
+    uint64_t CSR_DB1ADDR;
+    uint64_t CSR_DB1MASK;
+    uint64_t CSR_DB1CTL;
+    uint64_t CSR_DB1ASID;
+    uint64_t CSR_DB2ADDR;
+    uint64_t CSR_DB2MASK;
+    uint64_t CSR_DB2CTL;
+    uint64_t CSR_DB2ASID;
+    uint64_t CSR_DB3ADDR;
+    uint64_t CSR_DB3MASK;
+    uint64_t CSR_DB3CTL;
+    uint64_t CSR_DB3ASID;
+    uint64_t CSR_FWPC;
+    uint64_t CSR_FWPS;
+    uint64_t CSR_IB0ADDR;
+    uint64_t CSR_IB0MASK;
+    uint64_t CSR_IB0CTL;
+    uint64_t CSR_IB0ASID;
+    uint64_t CSR_IB1ADDR;
+    uint64_t CSR_IB1MASK;
+    uint64_t CSR_IB1CTL;
+    uint64_t CSR_IB1ASID;
+    uint64_t CSR_IB2ADDR;
+    uint64_t CSR_IB2MASK;
+    uint64_t CSR_IB2CTL;
+    uint64_t CSR_IB2ASID;
+    uint64_t CSR_IB3ADDR;
+    uint64_t CSR_IB3MASK;
+    uint64_t CSR_IB3CTL;
+    uint64_t CSR_IB3ASID;
+    uint64_t CSR_IB4ADDR;
+    uint64_t CSR_IB4MASK;
+    uint64_t CSR_IB4CTL;
+    uint64_t CSR_IB4ASID;
+    uint64_t CSR_IB5ADDR;
+    uint64_t CSR_IB5MASK;
+    uint64_t CSR_IB5CTL;
+    uint64_t CSR_IB5ASID;
+    uint64_t CSR_IB6ADDR;
+    uint64_t CSR_IB6MASK;
+    uint64_t CSR_IB6CTL;
+    uint64_t CSR_IB6ASID;
+    uint64_t CSR_IB7ADDR;
+    uint64_t CSR_IB7MASK;
+    uint64_t CSR_IB7CTL;
+    uint64_t CSR_IB7ASID;
+    uint64_t CSR_DBG;
+    uint64_t CSR_DERA;
+    uint64_t CSR_DESAVE;
 };
 
 /**
-- 
2.27.0



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

* [RFC PATCH v2 03/30] target/loongarch: Add basic vmstate description of CPU.
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
  2021-11-11  1:34 ` [RFC PATCH v2 01/30] target/loongarch: Update README Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 02/30] target/loongarch: Add CSR registers definition Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 13:30   ` Richard Henderson
  2021-11-11  1:35 ` [RFC PATCH v2 04/30] target/loongarch: Define exceptions for LoongArch Xiaojuan Yang
                   ` (27 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This patch introduces vmstate_loongarch_cpu

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/cpu.c       |   4 +
 target/loongarch/internals.h |   4 +
 target/loongarch/machine.c   | 154 +++++++++++++++++++++++++++++++++++
 target/loongarch/meson.build |   6 ++
 4 files changed, 168 insertions(+)
 create mode 100644 target/loongarch/machine.c

diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 01a17d8221..a53c8ebfb5 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -12,6 +12,7 @@
 #include "sysemu/qtest.h"
 #include "exec/exec-all.h"
 #include "qapi/qapi-commands-machine-target.h"
+#include "migration/vmstate.h"
 #include "cpu.h"
 #include "internals.h"
 #include "fpu/softfloat-helpers.h"
@@ -297,6 +298,9 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data)
     cc->has_work = loongarch_cpu_has_work;
     cc->dump_state = loongarch_cpu_dump_state;
     cc->set_pc = loongarch_cpu_set_pc;
+#ifndef CONFIG_USER_ONLY
+    dc->vmsd = &vmstate_loongarch_cpu;
+#endif
     cc->disas_set_info = loongarch_cpu_disas_set_info;
 #ifdef CONFIG_TCG
     cc->tcg_ops = &loongarch_tcg_ops;
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
index e9e63742c4..49ed6829d7 100644
--- a/target/loongarch/internals.h
+++ b/target/loongarch/internals.h
@@ -25,4 +25,8 @@ const char *loongarch_exception_name(int32_t exception);
 
 void restore_fp_status(CPULoongArchState *env);
 
+#ifndef CONFIG_USER_ONLY
+extern const VMStateDescription vmstate_loongarch_cpu;
+#endif
+
 #endif
diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c
new file mode 100644
index 0000000000..b628374814
--- /dev/null
+++ b/target/loongarch/machine.c
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU LoongArch machine State
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "migration/cpu.h"
+
+/* LoongArch CPU state */
+
+const VMStateDescription vmstate_loongarch_cpu = {
+    .name = "cpu",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+
+        VMSTATE_UINTTL_ARRAY(env.gpr, LoongArchCPU, 32),
+        VMSTATE_UINTTL(env.pc, LoongArchCPU),
+        VMSTATE_UINT64_ARRAY(env.fpr, LoongArchCPU, 32),
+        VMSTATE_UINT32(env.fcsr0, LoongArchCPU),
+
+        /* Remaining CSR registers */
+        VMSTATE_UINT64(env.CSR_CRMD, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PRMD, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_EUEN, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_MISC, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_ECFG, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_ESTAT, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_ERA, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_BADV, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_BADI, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_EENTRY, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TLBIDX, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TLBEHI, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TLBELO0, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TLBELO1, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PGDL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PGDH, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PGD, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PWCL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PWCH, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_STLBPS, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_RVACFG, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_CPUID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PRCFG1, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PRCFG2, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PRCFG3, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_SAVE0, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_SAVE1, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_SAVE2, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_SAVE3, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_SAVE4, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_SAVE5, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_SAVE6, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_SAVE7, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TMID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TCFG, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TVAL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_CNTC, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TINTCLR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_LLBCTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IMPCTL1, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IMPCTL2, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TLBRENTRY, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TLBRBADV, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TLBRERA, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TLBRSAVE, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TLBRELO0, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TLBRELO1, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TLBREHI, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_TLBRPRMD, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_MERRCTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_MERRINFO, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_MERRINFO1, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_MERRENT, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_MERRERA, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_MERRSAVE, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_CTAG, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DMWIN0, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DMWIN1, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DMWIN2, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DMWIN3, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PERFCTRL0, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PERFCNTR0, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PERFCTRL1, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PERFCNTR1, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PERFCTRL2, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PERFCNTR2, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PERFCTRL3, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_PERFCNTR3, LoongArchCPU),
+        /* debug */
+        VMSTATE_UINT64(env.CSR_MWPC, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_MWPS, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB0ADDR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB0MASK, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB0CTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB0ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB1ADDR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB1MASK, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB1CTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB1ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB2ADDR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB2MASK, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB2CTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB2ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB3ADDR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB3MASK, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB3CTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DB3ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_FWPC, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_FWPS, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB0ADDR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB0MASK, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB0CTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB0ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB1ADDR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB1MASK, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB1CTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB1ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB2ADDR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB2MASK, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB2CTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB2ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB3ADDR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB3MASK, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB3CTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB3ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB4ADDR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB4MASK, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB4CTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB4ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB5ADDR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB5MASK, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB5CTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB5ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB6ADDR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB6MASK, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB6CTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB6ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB7ADDR, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB7MASK, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB7CTL, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_IB7ASID, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DBG, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DERA, LoongArchCPU),
+        VMSTATE_UINT64(env.CSR_DESAVE, LoongArchCPU),
+
+        VMSTATE_END_OF_LIST()
+    },
+};
diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build
index bcb076e55f..103f36ee15 100644
--- a/target/loongarch/meson.build
+++ b/target/loongarch/meson.build
@@ -14,6 +14,12 @@ loongarch_tcg_ss.add(files(
 ))
 loongarch_tcg_ss.add(zlib)
 
+loongarch_softmmu_ss = ss.source_set()
+loongarch_softmmu_ss.add(files(
+  'machine.c',
+))
+
 loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss])
 
 target_arch += {'loongarch': loongarch_ss}
+target_softmmu_arch += {'loongarch': loongarch_softmmu_ss}
-- 
2.27.0



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

* [RFC PATCH v2 04/30] target/loongarch: Define exceptions for LoongArch.
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (2 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 03/30] target/loongarch: Add basic vmstate description of CPU Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 13:36   ` Richard Henderson
  2021-11-11  1:35 ` [RFC PATCH v2 05/30] target/loongarch: Implement qmp_query_cpu_definitions() Xiaojuan Yang
                   ` (26 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This patch introduces all possible exceptions.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/cpu.c | 13 +++++++++++++
 target/loongarch/cpu.h | 17 +++++++++++++++--
 2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index a53c8ebfb5..16443159cc 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -37,6 +37,19 @@ static const char * const excp_names[EXCP_LAST + 1] = {
     [EXCP_BREAK] = "Break",
     [EXCP_INE] = "Instruction Non-existent",
     [EXCP_FPE] = "Floating Point Exception",
+    [EXCP_IPE] = "Error privilege level access",
+    [EXCP_TLBL] = "TLB load",
+    [EXCP_TLBS] = "TLB store",
+    [EXCP_INST_NOTAVAIL] = "TLB inst not exist",
+    [EXCP_TLBM] = "TLB modify",
+    [EXCP_TLBPE] = "TLB priviledged error",
+    [EXCP_TLBNX] = "TLB execute-inhibit",
+    [EXCP_TLBNR] = "TLB read-inhibit",
+    [EXCP_EXT_INTERRUPT] = "Interrupt",
+    [EXCP_DBP] = "Debug breakpoint",
+    [EXCP_IBE] = "Instruction bus error",
+    [EXCP_DBE] = "Data bus error",
+    [EXCP_DINT] = "Debug interrupt",
 };
 
 const char *loongarch_exception_name(int32_t exception)
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 10fcd53104..399c4cb5e8 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -369,8 +369,21 @@ enum {
     EXCP_BREAK,
     EXCP_INE,
     EXCP_FPE,
-
-    EXCP_LAST = EXCP_FPE,
+    EXCP_IPE,
+    EXCP_TLBL,
+    EXCP_TLBS,
+    EXCP_INST_NOTAVAIL,
+    EXCP_TLBM,
+    EXCP_TLBPE,
+    EXCP_TLBNX,
+    EXCP_TLBNR,
+    EXCP_EXT_INTERRUPT,
+    EXCP_DBP,
+    EXCP_IBE,
+    EXCP_DBE,
+    EXCP_DINT,
+
+    EXCP_LAST = EXCP_DINT,
 };
 
 #define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU
-- 
2.27.0



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

* [RFC PATCH v2 05/30] target/loongarch: Implement qmp_query_cpu_definitions()
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (3 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 04/30] target/loongarch: Define exceptions for LoongArch Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 06/30] target/loongarch: Add stabletimer support Xiaojuan Yang
                   ` (25 subsequent siblings)
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This patch introduces qmp_query_cpu_definitions interface.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
 qapi/machine-target.json |  6 ++++--
 target/loongarch/cpu.c   | 28 ++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/qapi/machine-target.json b/qapi/machine-target.json
index f5ec4bc172..682dc86b42 100644
--- a/qapi/machine-target.json
+++ b/qapi/machine-target.json
@@ -324,7 +324,8 @@
                    'TARGET_ARM',
                    'TARGET_I386',
                    'TARGET_S390X',
-                   'TARGET_MIPS' ] } }
+                   'TARGET_MIPS',
+                   'TARGET_LOONGARCH64' ] } }
 
 ##
 # @query-cpu-definitions:
@@ -340,4 +341,5 @@
                    'TARGET_ARM',
                    'TARGET_I386',
                    'TARGET_S390X',
-                   'TARGET_MIPS' ] } }
+                   'TARGET_MIPS',
+                   'TARGET_LOONGARCH64' ] } }
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 16443159cc..c3e7c5dc98 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -342,3 +342,31 @@ static const TypeInfo loongarch_cpu_type_infos[] = {
 };
 
 DEFINE_TYPES(loongarch_cpu_type_infos)
+
+static void loongarch_cpu_add_definition(gpointer data, gpointer user_data)
+{
+    ObjectClass *oc = data;
+    CpuDefinitionInfoList **cpu_list = user_data;
+    CpuDefinitionInfo *info;
+    const char *typename;
+
+    typename = object_class_get_name(oc);
+    info = g_malloc0(sizeof(*info));
+    info->name = g_strndup(typename,
+                           strlen(typename) - strlen("-" TYPE_LOONGARCH_CPU));
+    info->q_typename = g_strdup(typename);
+
+    QAPI_LIST_PREPEND(*cpu_list, info);
+}
+
+CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
+{
+    CpuDefinitionInfoList *cpu_list = NULL;
+    GSList *list;
+
+    list = object_class_get_list(TYPE_LOONGARCH_CPU, false);
+    g_slist_foreach(list, loongarch_cpu_add_definition, &cpu_list);
+    g_slist_free(list);
+
+    return cpu_list;
+}
-- 
2.27.0



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

* [RFC PATCH v2 06/30] target/loongarch: Add stabletimer support
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (4 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 05/30] target/loongarch: Implement qmp_query_cpu_definitions() Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 14:34   ` Richard Henderson
  2021-11-11  1:35 ` [RFC PATCH v2 07/30] target/loongarch: Add MMU support for LoongArch CPU Xiaojuan Yang
                   ` (24 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/cpu.h         | 11 ++++++
 target/loongarch/meson.build   |  1 +
 target/loongarch/stabletimer.c | 70 ++++++++++++++++++++++++++++++++++
 3 files changed, 82 insertions(+)
 create mode 100644 target/loongarch/stabletimer.c

diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 399c4cb5e8..3dc0ef4cdf 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -148,6 +148,9 @@ FIELD(CPUCFG20, L3IU_SIZE, 24, 7)
 extern const char * const regnames[];
 extern const char * const fregnames[];
 
+#define N_IRQS      14
+#define IRQ_TIMER   11
+
 typedef struct CPULoongArchState CPULoongArchState;
 struct CPULoongArchState {
     uint64_t gpr[32];
@@ -297,6 +300,9 @@ struct CPULoongArchState {
     uint64_t CSR_DBG;
     uint64_t CSR_DERA;
     uint64_t CSR_DESAVE;
+
+    void *irq[N_IRQS];
+    QEMUTimer *timer; /* Internal timer */
 };
 
 /**
@@ -390,4 +396,9 @@ enum {
 #define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX
 #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
 
+void cpu_loongarch_clock_init(LoongArchCPU *cpu);
+uint64_t cpu_loongarch_get_stable_counter(CPULoongArchState *env);
+uint64_t cpu_loongarch_get_stable_timer_ticks(CPULoongArchState *env);
+void cpu_loongarch_store_stable_timer_config(CPULoongArchState *env,
+                                             uint64_t value);
 #endif /* LOONGARCH_CPU_H */
diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build
index 103f36ee15..bda9f47ae4 100644
--- a/target/loongarch/meson.build
+++ b/target/loongarch/meson.build
@@ -17,6 +17,7 @@ loongarch_tcg_ss.add(zlib)
 loongarch_softmmu_ss = ss.source_set()
 loongarch_softmmu_ss.add(files(
   'machine.c',
+  'stabletimer.c',
 ))
 
 loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss])
diff --git a/target/loongarch/stabletimer.c b/target/loongarch/stabletimer.c
new file mode 100644
index 0000000000..c7ecc300d4
--- /dev/null
+++ b/target/loongarch/stabletimer.c
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU LoongArch timer support
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/loongarch/loongarch.h"
+#include "qemu/timer.h"
+#include "cpu.h"
+
+#define TIMER_PERIOD                10 /* 10 ns period for 100 Mhz frequency */
+#define STABLETIMER_TICK_MASK       0xfffffffffffcUL
+#define STABLETIMER_ENABLE          0x1UL
+
+/* LoongArch timer */
+uint64_t cpu_loongarch_get_stable_counter(CPULoongArchState *env)
+{
+    return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD;
+}
+
+uint64_t cpu_loongarch_get_stable_timer_ticks(CPULoongArchState *env)
+{
+    uint64_t now, expire;
+
+    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    expire = timer_expire_time_ns(env->timer);
+
+    return (expire - now) / TIMER_PERIOD;
+}
+
+void cpu_loongarch_store_stable_timer_config(CPULoongArchState *env,
+                                             uint64_t value)
+{
+    uint64_t now, next;
+
+    env->CSR_TCFG = value;
+    if (value & STABLETIMER_ENABLE) {
+        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        next = now + (value & STABLETIMER_TICK_MASK) * TIMER_PERIOD;
+        timer_mod(env->timer, next);
+    }
+}
+
+static void loongarch_stable_timer_cb(void *opaque)
+{
+    CPULoongArchState *env;
+    uint64_t now, next;
+
+    env = opaque;
+    if (FIELD_EX64(env->CSR_TCFG, CSR_TCFG, PERIODIC)) {
+        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        next = now + (env->CSR_TCFG & STABLETIMER_TICK_MASK) * TIMER_PERIOD;
+        timer_mod(env->timer, next);
+    } else {
+        env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0);
+    }
+
+   qemu_irq_raise(env->irq[IRQ_TIMER]);
+}
+
+void cpu_loongarch_clock_init(LoongArchCPU *cpu)
+{
+    CPULoongArchState *env = &cpu->env;
+
+    env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+                              &loongarch_stable_timer_cb, env);
+}
-- 
2.27.0



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

* [RFC PATCH v2 07/30] target/loongarch: Add MMU support for LoongArch CPU.
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (5 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 06/30] target/loongarch: Add stabletimer support Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 15:53   ` Richard Henderson
  2021-11-11  1:35 ` [RFC PATCH v2 08/30] target/loongarch: Add LoongArch CSR/IOCSR instruction Xiaojuan Yang
                   ` (23 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This patch introduces basic TLB interfaces.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/cpu-param.h  |   3 +
 target/loongarch/cpu.c        |  36 ++++
 target/loongarch/cpu.h        |  57 ++++++
 target/loongarch/internals.h  |   7 +
 target/loongarch/machine.c    |  56 ++++++
 target/loongarch/meson.build  |   1 +
 target/loongarch/tlb_helper.c | 339 ++++++++++++++++++++++++++++++++++
 7 files changed, 499 insertions(+)
 create mode 100644 target/loongarch/tlb_helper.c

diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h
index 9a769b67e0..5a2147fb90 100644
--- a/target/loongarch/cpu-param.h
+++ b/target/loongarch/cpu-param.h
@@ -12,6 +12,9 @@
 #define TARGET_PHYS_ADDR_SPACE_BITS 48
 #define TARGET_VIRT_ADDR_SPACE_BITS 48
 
+#define TARGET_PHYS_MASK ((1UL << TARGET_PHYS_ADDR_SPACE_BITS) - 1)
+#define TARGET_VIRT_MASK ((1UL << TARGET_VIRT_ADDR_SPACE_BITS) - 1)
+
 #define TARGET_PAGE_BITS 14
 #define NB_MMU_MODES 4
 
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index c3e7c5dc98..7db6e21298 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -223,6 +223,10 @@ static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info)
 static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp)
 {
     CPUState *cs = CPU(dev);
+#ifndef CONFIG_USER_ONLY
+    LoongArchCPU *cpu = LOONGARCH_CPU(dev);
+    CPULoongArchState *env = &cpu->env;
+#endif
     LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev);
     Error *local_err = NULL;
 
@@ -232,6 +236,10 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp)
         return;
     }
 
+#ifndef CONFIG_USER_ONLY
+    loongarch_mmu_init(env);
+#endif
+
     cpu_reset(cs);
     qemu_init_vcpu(cs);
 
@@ -277,6 +285,21 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags)
         }
     }
 
+#ifndef CONFIG_USER_ONLY
+    qemu_fprintf(f, "EUEN            0x%lx\n", env->CSR_EUEN);
+    qemu_fprintf(f, "ESTAT           0x%lx\n", env->CSR_ESTAT);
+    qemu_fprintf(f, "ERA             0x%lx\n", env->CSR_ERA);
+    qemu_fprintf(f, "CRMD            0x%lx\n", env->CSR_CRMD);
+    qemu_fprintf(f, "PRMD            0x%lx\n", env->CSR_PRMD);
+    qemu_fprintf(f, "BadVAddr        0x%lx\n", env->CSR_BADV);
+    qemu_fprintf(f, "TLB refill ERA  0x%lx\n", env->CSR_TLBRERA);
+    qemu_fprintf(f, "TLB refill BadV 0x%lx\n", env->CSR_TLBRBADV);
+    qemu_fprintf(f, "EENTRY          0x%lx\n", env->CSR_EENTRY);
+    qemu_fprintf(f, "BadInstr        0x%lx\n", env->CSR_BADI);
+    qemu_fprintf(f, "PRCFG1    0x%lx\nPRCFG2     0x%lx\nPRCFG3     0x%lx\n",
+                 env->CSR_PRCFG1, env->CSR_PRCFG3, env->CSR_PRCFG3);
+#endif
+
     /* fpr */
     if (flags & CPU_DUMP_FPU) {
         for (i = 0; i < 32; i++) {
@@ -294,9 +317,21 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags)
 static struct TCGCPUOps loongarch_tcg_ops = {
     .initialize = loongarch_translate_init,
     .synchronize_from_tb = loongarch_cpu_synchronize_from_tb,
+
+#if !defined(CONFIG_USER_ONLY)
+    .tlb_fill = loongarch_cpu_tlb_fill,
+#endif /* !CONFIG_USER_ONLY */
 };
 #endif /* CONFIG_TCG */
 
+#ifndef CONFIG_USER_ONLY
+#include "hw/core/sysemu-cpu-ops.h"
+
+static const struct SysemuCPUOps loongarch_sysemu_ops = {
+    .get_phys_page_debug = loongarch_cpu_get_phys_page_debug,
+};
+#endif
+
 static void loongarch_cpu_class_init(ObjectClass *c, void *data)
 {
     LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c);
@@ -313,6 +348,7 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data)
     cc->set_pc = loongarch_cpu_set_pc;
 #ifndef CONFIG_USER_ONLY
     dc->vmsd = &vmstate_loongarch_cpu;
+    cc->sysemu_ops = &loongarch_sysemu_ops;
 #endif
     cc->disas_set_info = loongarch_cpu_disas_set_info;
 #ifdef CONFIG_TCG
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 3dc0ef4cdf..4881f18cf1 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -151,6 +151,53 @@ extern const char * const fregnames[];
 #define N_IRQS      14
 #define IRQ_TIMER   11
 
+/*
+ * LoongArch cpu has 4 priv level, now only 2 mode used.
+ * 0 for kernel mode, 3 for user mode.
+ */
+#define LOONGARCH_HFLAG_UM     0x3 /* user mode */
+#define LOONGARCH_HFLAG_KM     0x0 /* kernel mode */
+
+#define LOONGARCH_TLB_MAX      2112 /* 2048 STLB + 64 MTLB */
+
+/*
+ * define the ASID PS E VPPN field of TLB
+ *
+ * PS of stlb come from stlbps.ps
+ * PS of mtlb come from tlbidx.ps
+ */
+FIELD(TLB_MISC, E, 0, 1)
+FIELD(TLB_MISC, ASID, 1, 10)
+FIELD(TLB_MISC, G, 11, 1)
+FIELD(TLB_MISC, PS, 12, 6)
+FIELD(TLB_MISC, VPPN, 18, 35)
+
+/* Corresponding to CSR_TLBELO0/1 */
+FIELD(ENTRY0, V, 0, 1)
+FIELD(ENTRY0, D, 1, 1)
+FIELD(ENTRY0, NR, 2, 1)
+FIELD(ENTRY0, NX, 3, 1)
+FIELD(ENTRY0, MAT, 4, 2)
+FIELD(ENTRY0, PLV, 6, 2)
+FIELD(ENTRY0, RPLV, 8, 1)
+FIELD(ENTRY0, PPN, 9, 36)
+
+FIELD(ENTRY1, V, 0, 1)
+FIELD(ENTRY1, D, 1, 1)
+FIELD(ENTRY1, NR, 2, 1)
+FIELD(ENTRY1, NX, 3, 1)
+FIELD(ENTRY1, MAT, 4, 2)
+FIELD(ENTRY1, PLV, 6, 2)
+FIELD(ENTRY1, RPLV, 8, 1)
+FIELD(ENTRY1, PPN, 9, 36)
+
+struct loongarch_tlb {
+    uint64_t tlb_misc;
+    uint64_t tlb_entry0;
+    uint64_t tlb_entry1;
+};
+typedef struct loongarch_tlb loongarch_tlb;
+
 typedef struct CPULoongArchState CPULoongArchState;
 struct CPULoongArchState {
     uint64_t gpr[32];
@@ -303,6 +350,12 @@ struct CPULoongArchState {
 
     void *irq[N_IRQS];
     QEMUTimer *timer; /* Internal timer */
+
+#ifndef CONFIG_USER_ONLY
+    uint32_t      stlb_size; /* at most : 8 * 256 = 2048 */
+    uint32_t      mtlb_size; /* at most : 64 */
+    loongarch_tlb tlb[LOONGARCH_TLB_MAX];
+#endif
 };
 
 /**
@@ -345,7 +398,11 @@ struct LoongArchCPUClass {
 
 static inline int cpu_mmu_index(CPULoongArchState *env, bool ifetch)
 {
+#ifdef CONFIG_USER_ONLY
     return MMU_USER_IDX;
+#else
+    return FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV);
+#endif
 }
 
 static inline void cpu_get_tb_cpu_state(CPULoongArchState *env,
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
index 49ed6829d7..3f72492b91 100644
--- a/target/loongarch/internals.h
+++ b/target/loongarch/internals.h
@@ -27,6 +27,13 @@ void restore_fp_status(CPULoongArchState *env);
 
 #ifndef CONFIG_USER_ONLY
 extern const VMStateDescription vmstate_loongarch_cpu;
+
+bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+                            MMUAccessType access_type, int mmu_idx,
+                            bool probe, uintptr_t retaddr);
+
+void loongarch_mmu_init(CPULoongArchState *env);
+hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
 #endif
 
 #endif
diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c
index b628374814..ccac5b9ef3 100644
--- a/target/loongarch/machine.c
+++ b/target/loongarch/machine.c
@@ -8,6 +8,54 @@
 #include "qemu/osdep.h"
 #include "cpu.h"
 #include "migration/cpu.h"
+#include "internals.h"
+
+/* TLB state */
+static int get_tlb(QEMUFile *f, void *pv, size_t size,
+                   const VMStateField *field)
+{
+    loongarch_tlb *v = pv;
+
+    qemu_get_be64s(f, &v->tlb_misc);
+    qemu_get_be64s(f, &v->tlb_entry0);
+    qemu_get_be64s(f, &v->tlb_entry1);
+
+    return 0;
+}
+
+static int put_tlb(QEMUFile *f, void *pv, size_t size,
+                   const VMStateField *field, JSONWriter *vmdesc)
+{
+    loongarch_tlb *v = pv;
+
+    qemu_put_be64s(f, &v->tlb_misc);
+    qemu_put_be64s(f, &v->tlb_entry0);
+    qemu_put_be64s(f, &v->tlb_entry1);
+
+    return 0;
+}
+
+const VMStateInfo vmstate_info_tlb = {
+    .name = "tlb_entry",
+    .get  = get_tlb,
+    .put  = put_tlb,
+};
+
+#define VMSTATE_TLB_ARRAY_V(_f, _s, _n, _v)                     \
+    VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_tlb, loongarch_tlb)
+
+#define VMSTATE_TLB_ARRAY(_f, _s, _n)                           \
+    VMSTATE_TLB_ARRAY_V(_f, _s, _n, 0)
+
+const VMStateDescription vmstate_tlb = {
+    .name = "cpu/tlb",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_TLB_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX),
+        VMSTATE_END_OF_LIST()
+    }
+};
 
 /* LoongArch CPU state */
 
@@ -22,6 +70,10 @@ const VMStateDescription vmstate_loongarch_cpu = {
         VMSTATE_UINT64_ARRAY(env.fpr, LoongArchCPU, 32),
         VMSTATE_UINT32(env.fcsr0, LoongArchCPU),
 
+        /* TLB */
+        VMSTATE_UINT32(env.stlb_size, LoongArchCPU),
+        VMSTATE_UINT32(env.mtlb_size, LoongArchCPU),
+
         /* Remaining CSR registers */
         VMSTATE_UINT64(env.CSR_CRMD, LoongArchCPU),
         VMSTATE_UINT64(env.CSR_PRMD, LoongArchCPU),
@@ -151,4 +203,8 @@ const VMStateDescription vmstate_loongarch_cpu = {
 
         VMSTATE_END_OF_LIST()
     },
+    .subsections = (const VMStateDescription * []) {
+        &vmstate_tlb,
+        NULL
+    }
 };
diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build
index bda9f47ae4..935ffe2765 100644
--- a/target/loongarch/meson.build
+++ b/target/loongarch/meson.build
@@ -18,6 +18,7 @@ loongarch_softmmu_ss = ss.source_set()
 loongarch_softmmu_ss.add(files(
   'machine.c',
   'stabletimer.c',
+  'tlb_helper.c',
 ))
 
 loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss])
diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tlb_helper.c
new file mode 100644
index 0000000000..69c69ece0a
--- /dev/null
+++ b/target/loongarch/tlb_helper.c
@@ -0,0 +1,339 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU LoongArch TLB helpers for qemu
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include "cpu.h"
+#include "internals.h"
+#include "exec/exec-all.h"
+#include "exec/cpu_ldst.h"
+#include "exec/log.h"
+#include "cpu-csr.h"
+
+enum {
+    TLBRET_MATCH = 0,
+    TLBRET_BADADDR =1,
+    TLBRET_NOMATCH = 2,
+    TLBRET_INVALID = 3,
+    TLBRET_DIRTY = 4,
+    TLBRET_RI = 5,
+    TLBRET_XI = 6,
+    TLBRET_PE = 7,
+};
+
+/* TLB address map */
+static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical,
+                                   int *prot, target_ulong address,
+                                   int access_type, loongarch_tlb *tlb)
+{
+    uint64_t plv = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV);
+    uint8_t tlb_ps, n, tlb_v0, tlb_v1, tlb_d0, tlb_d1;
+    uint8_t tlb_nx0, tlb_nx1, tlb_nr0, tlb_nr1;
+    uint64_t tlb_ppn0, tlb_ppn1;
+    uint8_t tlb_rplv0, tlb_rplv1, tlb_plv0, tlb_plv1;
+
+    tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
+    n = (address >> tlb_ps) & 0x1;/* Odd or even */
+
+    tlb_v0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, V);
+    tlb_d0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, D);
+    tlb_plv0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, PLV);
+    tlb_ppn0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, PPN);
+    tlb_nx0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, NX);
+    tlb_nr0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, NR);
+    tlb_rplv0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, RPLV);
+
+    tlb_v1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, V);
+    tlb_d1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, D);
+    tlb_plv1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, PLV);
+    tlb_ppn1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, PPN);
+    tlb_nx1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, NX);
+    tlb_nr1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, NR);
+    tlb_rplv1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, RPLV);
+
+    /* Check access rights */
+    if (!(n ? tlb_v1 : tlb_v0)) {
+        return TLBRET_INVALID;
+    }
+
+    if (access_type == MMU_INST_FETCH && (n ? tlb_nx1 : tlb_nx0)) {
+        return TLBRET_XI;
+    }
+
+    if (access_type == MMU_DATA_LOAD && (n ? tlb_nr1 : tlb_nr0)) {
+        return TLBRET_RI;
+    }
+
+    if (n) {
+        if (((tlb_rplv1 == 0) && (plv > tlb_plv1)) ||
+            ((tlb_rplv1 == 1) && (plv != tlb_plv1))) {
+            return TLBRET_PE;
+        }
+    } else {
+        if (((tlb_rplv0 == 0) && (plv > tlb_plv0)) ||
+            ((tlb_rplv0 == 1) && (plv != tlb_plv0))) {
+            return TLBRET_PE;
+        }
+    }
+
+    if ((access_type == MMU_DATA_STORE) && !(n ? tlb_d1 : tlb_d0)) {
+        return TLBRET_DIRTY;
+    }
+
+    /*
+     *         PPN     address
+     *  4 KB: [47:13]   [12;0]
+     * 16 KB: [47:15]   [14:0]
+     */
+    if (n) {
+        *physical = (tlb_ppn1 << 12) | (address & ((1 << tlb_ps) - 1));
+    } else {
+        *physical = (tlb_ppn0 << 12) | (address & ((1 << tlb_ps) - 1));
+    }
+    *prot = PAGE_READ;
+    if (n ? tlb_d1 : tlb_d0) {
+        *prot |= PAGE_WRITE;
+    }
+    if (!(n ? tlb_nx1 : tlb_nx0)) {
+        *prot |= PAGE_EXEC;
+    }
+    return TLBRET_MATCH;
+}
+
+/* LoongArch 3A5000 -style MMU emulation */
+static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
+                                 int *prot,
+                                 target_ulong address,
+                                 MMUAccessType access_type)
+{
+    loongarch_tlb *tlb;
+    uint16_t csr_asid, tlb_asid, stlb_idx;
+    uint8_t tlb_e, stlb_ps, tlb_ps, tlb_g;
+    int i, stlb_size, mtlb_size;
+    uint64_t vpn, tlb_vppn;   /* Address to map */
+
+    stlb_size = env->stlb_size;
+    mtlb_size = env->mtlb_size;
+    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
+
+    /* Search MTLB */
+    for (i = stlb_size; i < stlb_size + mtlb_size; ++i) {
+        tlb = &env->tlb[i];
+        tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
+        tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
+
+        vpn = (address & TARGET_VIRT_MASK) >> (tlb_ps + 1);
+        tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
+        tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
+        tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
+
+        if ((tlb_g == 1 || tlb_asid == csr_asid) &&
+            (vpn == (tlb_vppn >> (tlb_ps + 1 - 13))) && tlb_e) {
+            return loongarch_map_tlb_entry(env, physical, prot,
+                                                   address, access_type, tlb);
+        }
+    }
+
+    /* Search STLB */
+    stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
+    vpn = (address & TARGET_VIRT_MASK) >> (stlb_ps + 1);
+
+    /* VA[ps+11 : ps+1] indicate the stlb index */
+    stlb_idx = vpn & 0xff; /* [0,255] */
+
+    for (i = 0; i < 8; ++i) {
+        tlb = &env->tlb[i * 256 + stlb_idx];
+        tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
+        tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
+        tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
+        tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
+
+        if ((tlb_g == 1 || tlb_asid == csr_asid) &&
+            (vpn == (tlb_vppn >> (stlb_ps + 1 - 13))) && tlb_e) {
+            return loongarch_map_tlb_entry(env, physical, prot,
+                                                   address, access_type, tlb);
+        }
+    }
+
+    return TLBRET_NOMATCH;
+}
+
+static int get_physical_address(CPULoongArchState *env, hwaddr *physical,
+                                int *prot, target_ulong real_address,
+                                MMUAccessType access_type, int mmu_idx)
+{
+    int user_mode = mmu_idx == LOONGARCH_HFLAG_UM;
+    int kernel_mode = !user_mode;
+    unsigned plv, base_c, base_v, tmp;
+    uint64_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG);
+
+    /* Effective address */
+    target_ulong address = real_address;
+
+    /* Check PG */
+    if (!pg) {
+        /* DA mode */
+        *physical = address & TARGET_PHYS_MASK;
+        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+        return TLBRET_MATCH;
+    }
+
+    plv = kernel_mode | (user_mode << 3);
+    base_v = address >> CSR_DMW_BASE_SH;
+    /* Check direct map window 0 */
+    base_c = env->CSR_DMWIN0 >> CSR_DMW_BASE_SH;
+    if ((plv & env->CSR_DMWIN0) && (base_c == base_v)) {
+        *physical = dmwin_va2pa(address);
+        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+        return TLBRET_MATCH;
+    }
+    /* Check direct map window 1 */
+    base_c = env->CSR_DMWIN1 >> CSR_DMW_BASE_SH;
+    if ((plv & env->CSR_DMWIN1) && (base_c == base_v)) {
+        *physical = dmwin_va2pa(address);
+        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+        return TLBRET_MATCH;
+    }
+    /* Check valid extension */
+    tmp = address >> (TARGET_VIRT_ADDR_SPACE_BITS - 1);
+    if (!(tmp == 0 || tmp == 0x1ffff)) {
+        return TLBRET_BADADDR;
+    }
+    /* Mapped address */
+    return loongarch_map_address(env, physical, prot, real_address,
+                                 access_type);
+}
+
+hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+    CPULoongArchState *env = &cpu->env;
+    hwaddr phys_addr;
+    int prot;
+
+    if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD,
+                             cpu_mmu_index(env, false)) != 0) {
+        return -1;
+    }
+    return phys_addr;
+}
+
+static void raise_mmu_exception(CPULoongArchState *env, target_ulong address,
+                                MMUAccessType access_type, int tlb_error)
+{
+    CPUState *cs = env_cpu(env);
+
+    switch (tlb_error) {
+    default:
+    case TLBRET_BADADDR:
+        cs->exception_index = EXCP_ADE;
+        break;
+    case TLBRET_NOMATCH:
+        /* No TLB match for a mapped address */
+        if (access_type == MMU_DATA_LOAD) {
+            cs->exception_index = EXCP_TLBL;
+        } else if (access_type == MMU_DATA_STORE) {
+            cs->exception_index = EXCP_TLBS;
+        } else if (access_type == MMU_INST_FETCH) {
+            cs->exception_index = EXCP_INST_NOTAVAIL;
+        }
+        env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 1);
+        break;
+    case TLBRET_INVALID:
+        /* TLB match with no valid bit */
+        if (access_type == MMU_DATA_LOAD) {
+            cs->exception_index = EXCP_TLBL;
+        } else if (access_type == MMU_DATA_STORE) {
+            cs->exception_index = EXCP_TLBS;
+        } else if (access_type == MMU_INST_FETCH) {
+            cs->exception_index = EXCP_INST_NOTAVAIL;
+        }
+        break;
+    case TLBRET_DIRTY:
+        /* TLB match but 'D' bit is cleared */
+        cs->exception_index = EXCP_TLBM;
+        break;
+    case TLBRET_XI:
+        /* Execute-Inhibit Exception */
+        cs->exception_index = EXCP_TLBNX;
+        break;
+    case TLBRET_RI:
+        /* Read-Inhibit Exception */
+        cs->exception_index = EXCP_TLBNR;
+        break;
+    case TLBRET_PE:
+        /* Privileged Exception */
+        cs->exception_index = EXCP_TLBPE;
+        break;
+    }
+
+    if (tlb_error == TLBRET_NOMATCH) {
+        env->CSR_TLBRBADV = address;
+        env->CSR_TLBREHI = address & (TARGET_PAGE_MASK << 1);
+    } else {
+        if (!FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) {
+            env->CSR_BADV = address;
+        }
+        env->CSR_TLBEHI = address & (TARGET_PAGE_MASK << 1);
+   }
+
+}
+
+void loongarch_mmu_init(CPULoongArchState *env)
+{
+    /* Number of MTLB */
+    env->mtlb_size = 64;
+
+    /* Number of STLB */
+    env->stlb_size = 2048;
+
+    /* For 16KB, ps = 14, compare the bit [47:15] */
+    for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
+        env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, TLB_MISC, E, 0);
+    }
+}
+
+bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+                            MMUAccessType access_type, int mmu_idx,
+                            bool probe, uintptr_t retaddr)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+    CPULoongArchState *env = &cpu->env;
+    hwaddr physical;
+    int prot;
+    int ret = TLBRET_BADADDR;
+
+    /* Data access */
+    /* XXX: put correct access by using cpu_restore_state() correctly */
+    ret = get_physical_address(env, &physical, &prot, address,
+                               access_type, mmu_idx);
+    switch (ret) {
+    case TLBRET_MATCH:
+        qemu_log_mask(CPU_LOG_MMU,
+                      "%s address=%" VADDR_PRIx " physical " TARGET_FMT_plx
+                      " prot %d\n", __func__, address, physical, prot);
+        break;
+    default:
+        qemu_log_mask(CPU_LOG_MMU,
+                      "%s address=%" VADDR_PRIx " ret %d\n", __func__, address,
+                      ret);
+        break;
+    }
+    if (ret == TLBRET_MATCH) {
+        tlb_set_page(cs, address & TARGET_PAGE_MASK,
+                     physical & TARGET_PAGE_MASK, prot,
+                     mmu_idx, TARGET_PAGE_SIZE);
+        return true;
+    }
+    if (probe) {
+        return false;
+    } else {
+        raise_mmu_exception(env, address, access_type, ret);
+        do_raise_exception(env, cs->exception_index, retaddr);
+    }
+}
-- 
2.27.0



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

* [RFC PATCH v2 08/30] target/loongarch: Add LoongArch CSR/IOCSR instruction
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (6 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 07/30] target/loongarch: Add MMU support for LoongArch CPU Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 17:43   ` Richard Henderson
  2021-11-11  1:35 ` [RFC PATCH v2 09/30] target/loongarch: Add TLB instruction support Xiaojuan Yang
                   ` (22 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This includes:
- CSRRD
- CSRWR
- CSRXCHG
- IOCSR{RD/WR}.{B/H/W/D}

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/csr_helper.c                | 322 ++++++++++++++
 target/loongarch/helper.h                    |  11 +
 target/loongarch/insn_trans/trans_core.c.inc | 437 +++++++++++++++++++
 target/loongarch/insns.decode                |  22 +
 target/loongarch/meson.build                 |   1 +
 target/loongarch/op_helper.c                 |  10 +
 target/loongarch/translate.c                 |   5 +
 7 files changed, 808 insertions(+)
 create mode 100644 target/loongarch/csr_helper.c
 create mode 100644 target/loongarch/insn_trans/trans_core.c.inc

diff --git a/target/loongarch/csr_helper.c b/target/loongarch/csr_helper.c
new file mode 100644
index 0000000000..c9e17bed20
--- /dev/null
+++ b/target/loongarch/csr_helper.c
@@ -0,0 +1,322 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch emulation helpers for csr registers
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "cpu.h"
+#include "internals.h"
+#include "qemu/host-utils.h"
+#include "exec/helper-proto.h"
+#include "exec/exec-all.h"
+#include "exec/cpu_ldst.h"
+#include "hw/irq.h"
+#include "cpu-csr.h"
+#include "hw/loongarch/loongarch.h"
+#include "tcg/tcg-ldst.h"
+
+target_ulong helper_csr_rdq(CPULoongArchState *env, uint64_t csr)
+{
+    int64_t v;
+
+    switch (csr) {
+    case LOONGARCH_CSR_PGD:
+        if (env->CSR_TLBRERA & 0x1) {
+            v = env->CSR_TLBRBADV;
+        } else {
+            v = env->CSR_BADV;
+        }
+
+        if ((v >> 63) & 0x1) {
+            v = env->CSR_PGDH;
+        } else {
+            v = env->CSR_PGDL;
+        }
+        v = v & TARGET_PHYS_MASK;
+        break;
+    case LOONGARCH_CSR_CPUID:
+        v = (env_cpu(env))->cpu_index;
+        break;
+    case LOONGARCH_CSR_TVAL:
+        v = cpu_loongarch_get_stable_timer_ticks(env);
+        break;
+    default:
+        assert(0);
+    }
+
+    return v;
+}
+
+target_ulong helper_csr_wrq(CPULoongArchState *env, target_ulong val,
+                            uint64_t csr)
+{
+    int64_t old_v;
+    old_v = -1;
+
+    switch (csr) {
+    case LOONGARCH_CSR_ASID:
+        old_v = env->CSR_ASID;
+        env->CSR_ASID = val;
+        if (old_v != val) {
+            tlb_flush(env_cpu(env));
+        }
+        break;
+    case LOONGARCH_CSR_TCFG:
+        old_v = env->CSR_TCFG;
+        cpu_loongarch_store_stable_timer_config(env, val);
+        break;
+    case LOONGARCH_CSR_TINTCLR:
+        old_v = 0;
+        qemu_irq_lower(env->irq[IRQ_TIMER]);
+        break;
+    default:
+        assert(0);
+    }
+
+    return old_v;
+}
+
+target_ulong helper_csr_xchgq(CPULoongArchState *env, target_ulong val,
+                              target_ulong mask, uint64_t csr)
+{
+    target_ulong tmp;
+    target_ulong v = val & mask;
+
+#define CASE_CSR_XCHGQ(csr)                                 \
+    case LOONGARCH_CSR_ ## csr:                             \
+    {                                                       \
+        val = env->CSR_ ## csr;                             \
+        env->CSR_ ## csr = (env->CSR_ ## csr) & (~mask);    \
+        env->CSR_ ## csr = (env->CSR_ ## csr) | v;          \
+        break;                                              \
+    };                                                      \
+
+    switch (csr) {
+    CASE_CSR_XCHGQ(CRMD)
+    CASE_CSR_XCHGQ(PRMD)
+    CASE_CSR_XCHGQ(EUEN)
+    CASE_CSR_XCHGQ(MISC)
+    CASE_CSR_XCHGQ(ECFG)
+    CASE_CSR_XCHGQ(ESTAT)
+    CASE_CSR_XCHGQ(ERA)
+    CASE_CSR_XCHGQ(BADV)
+    CASE_CSR_XCHGQ(BADI)
+    CASE_CSR_XCHGQ(EENTRY)
+    CASE_CSR_XCHGQ(TLBIDX)
+    CASE_CSR_XCHGQ(TLBEHI)
+    CASE_CSR_XCHGQ(TLBELO0)
+    CASE_CSR_XCHGQ(TLBELO1)
+    CASE_CSR_XCHGQ(ASID)
+    CASE_CSR_XCHGQ(PGDL)
+    CASE_CSR_XCHGQ(PGDH)
+    CASE_CSR_XCHGQ(PGD)
+    CASE_CSR_XCHGQ(PWCL)
+    CASE_CSR_XCHGQ(PWCH)
+    CASE_CSR_XCHGQ(STLBPS)
+    CASE_CSR_XCHGQ(RVACFG)
+    CASE_CSR_XCHGQ(CPUID)
+    CASE_CSR_XCHGQ(PRCFG1)
+    CASE_CSR_XCHGQ(PRCFG2)
+    CASE_CSR_XCHGQ(PRCFG3)
+    CASE_CSR_XCHGQ(SAVE0)
+    CASE_CSR_XCHGQ(SAVE1)
+    CASE_CSR_XCHGQ(SAVE2)
+    CASE_CSR_XCHGQ(SAVE3)
+    CASE_CSR_XCHGQ(SAVE4)
+    CASE_CSR_XCHGQ(SAVE5)
+    CASE_CSR_XCHGQ(SAVE6)
+    CASE_CSR_XCHGQ(SAVE7)
+    CASE_CSR_XCHGQ(TMID)
+    case LOONGARCH_CSR_TCFG:
+        val = env->CSR_TCFG;
+        tmp = val & ~mask;
+        tmp |= v;
+        cpu_loongarch_store_stable_timer_config(env, tmp);
+        break;
+    CASE_CSR_XCHGQ(TVAL)
+    CASE_CSR_XCHGQ(CNTC)
+    CASE_CSR_XCHGQ(TINTCLR)
+    CASE_CSR_XCHGQ(LLBCTL)
+    CASE_CSR_XCHGQ(IMPCTL1)
+    CASE_CSR_XCHGQ(IMPCTL2)
+    CASE_CSR_XCHGQ(TLBRENTRY)
+    CASE_CSR_XCHGQ(TLBRBADV)
+    CASE_CSR_XCHGQ(TLBRERA)
+    CASE_CSR_XCHGQ(TLBRSAVE)
+    CASE_CSR_XCHGQ(TLBRELO0)
+    CASE_CSR_XCHGQ(TLBRELO1)
+    CASE_CSR_XCHGQ(TLBREHI)
+    CASE_CSR_XCHGQ(TLBRPRMD)
+    CASE_CSR_XCHGQ(MERRCTL)
+    CASE_CSR_XCHGQ(MERRINFO)
+    CASE_CSR_XCHGQ(MERRINFO1)
+    CASE_CSR_XCHGQ(MERRENT)
+    CASE_CSR_XCHGQ(MERRERA)
+    CASE_CSR_XCHGQ(MERRSAVE)
+    CASE_CSR_XCHGQ(CTAG)
+    CASE_CSR_XCHGQ(DMWIN0)
+    CASE_CSR_XCHGQ(DMWIN1)
+    CASE_CSR_XCHGQ(DMWIN2)
+    CASE_CSR_XCHGQ(DMWIN3)
+    CASE_CSR_XCHGQ(PERFCTRL0)
+    CASE_CSR_XCHGQ(PERFCNTR0)
+    CASE_CSR_XCHGQ(PERFCTRL1)
+    CASE_CSR_XCHGQ(PERFCNTR1)
+    CASE_CSR_XCHGQ(PERFCTRL2)
+    CASE_CSR_XCHGQ(PERFCNTR2)
+    CASE_CSR_XCHGQ(PERFCTRL3)
+    CASE_CSR_XCHGQ(PERFCNTR3)
+    /* debug */
+    CASE_CSR_XCHGQ(MWPC)
+    CASE_CSR_XCHGQ(MWPS)
+    CASE_CSR_XCHGQ(DB0ADDR)
+    CASE_CSR_XCHGQ(DB0MASK)
+    CASE_CSR_XCHGQ(DB0CTL)
+    CASE_CSR_XCHGQ(DB0ASID)
+    CASE_CSR_XCHGQ(DB1ADDR)
+    CASE_CSR_XCHGQ(DB1MASK)
+    CASE_CSR_XCHGQ(DB1CTL)
+    CASE_CSR_XCHGQ(DB1ASID)
+    CASE_CSR_XCHGQ(DB2ADDR)
+    CASE_CSR_XCHGQ(DB2MASK)
+    CASE_CSR_XCHGQ(DB2CTL)
+    CASE_CSR_XCHGQ(DB2ASID)
+    CASE_CSR_XCHGQ(DB3ADDR)
+    CASE_CSR_XCHGQ(DB3MASK)
+    CASE_CSR_XCHGQ(DB3CTL)
+    CASE_CSR_XCHGQ(DB3ASID)
+    CASE_CSR_XCHGQ(FWPC)
+    CASE_CSR_XCHGQ(FWPS)
+    CASE_CSR_XCHGQ(IB0ADDR)
+    CASE_CSR_XCHGQ(IB0MASK)
+    CASE_CSR_XCHGQ(IB0CTL)
+    CASE_CSR_XCHGQ(IB0ASID)
+    CASE_CSR_XCHGQ(IB1ADDR)
+    CASE_CSR_XCHGQ(IB1MASK)
+    CASE_CSR_XCHGQ(IB1CTL)
+    CASE_CSR_XCHGQ(IB1ASID)
+    CASE_CSR_XCHGQ(IB2ADDR)
+    CASE_CSR_XCHGQ(IB2MASK)
+    CASE_CSR_XCHGQ(IB2CTL)
+    CASE_CSR_XCHGQ(IB2ASID)
+    CASE_CSR_XCHGQ(IB3ADDR)
+    CASE_CSR_XCHGQ(IB3MASK)
+    CASE_CSR_XCHGQ(IB3CTL)
+    CASE_CSR_XCHGQ(IB3ASID)
+    CASE_CSR_XCHGQ(IB4ADDR)
+    CASE_CSR_XCHGQ(IB4MASK)
+    CASE_CSR_XCHGQ(IB4CTL)
+    CASE_CSR_XCHGQ(IB4ASID)
+    CASE_CSR_XCHGQ(IB5ADDR)
+    CASE_CSR_XCHGQ(IB5MASK)
+    CASE_CSR_XCHGQ(IB5CTL)
+    CASE_CSR_XCHGQ(IB5ASID)
+    CASE_CSR_XCHGQ(IB6ADDR)
+    CASE_CSR_XCHGQ(IB6MASK)
+    CASE_CSR_XCHGQ(IB6CTL)
+    CASE_CSR_XCHGQ(IB6ASID)
+    CASE_CSR_XCHGQ(IB7ADDR)
+    CASE_CSR_XCHGQ(IB7MASK)
+    CASE_CSR_XCHGQ(IB7CTL)
+    CASE_CSR_XCHGQ(IB7ASID)
+    CASE_CSR_XCHGQ(DBG)
+    CASE_CSR_XCHGQ(DERA)
+    CASE_CSR_XCHGQ(DESAVE)
+    default :
+        assert(0);
+    }
+
+#undef CASE_CSR_XCHGQ
+    return val;
+}
+
+/*
+ * For per core address 0x10xx(IPI) 0x18xx(EXTIOI)
+ * need extra adjust the iocsr addr.
+ */
+uint64_t helper_iocsr_read(CPULoongArchState *env, target_ulong r_addr,
+                           uint32_t size)
+{
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
+    int cpuid = env_cpu(env)->cpu_index;
+
+    if (((r_addr & 0xff00) == 0x1000) || ((r_addr & 0xff00) == 0x1800)) {
+        r_addr = r_addr + ((target_ulong)(cpuid & 0x3) << 8);
+    }
+
+    if (size == 1) {
+        return address_space_ldub(lams->address_space_iocsr, r_addr,
+                                  MEMTXATTRS_UNSPECIFIED, NULL);
+    } else if (size == 2) {
+        return address_space_lduw(lams->address_space_iocsr, r_addr,
+                                  MEMTXATTRS_UNSPECIFIED, NULL);
+    } else if (size == 4) {
+        return address_space_ldl(lams->address_space_iocsr, r_addr,
+                                 MEMTXATTRS_UNSPECIFIED, NULL);
+    } else if (size == 8) {
+        return address_space_ldq(lams->address_space_iocsr, r_addr,
+                                 MEMTXATTRS_UNSPECIFIED, NULL);
+    }
+    return 0;
+}
+
+void helper_iocsr_write(CPULoongArchState *env, target_ulong w_addr,
+                        target_ulong val, uint32_t size)
+{
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
+    int cpuid = env_cpu(env)->cpu_index;
+    int mask, i;
+
+    /*
+     * For IPI send, Mail send, ANY send adjust addr and val
+     * according to their real meaning
+     */
+    if (w_addr == 0x1040) { /* IPI send */
+        cpuid = (val >> 16) & 0x3ff;
+        val = 1UL << (val & 0x1f);
+        w_addr = 0x1008;
+    } else if (w_addr == 0x1048) { /* Mail Send */
+        cpuid = (val >> 16) & 0x3ff;
+        w_addr = 0x1020 + (val & 0x1c);
+        val = val >> 32;
+        mask = (val >> 27) & 0xf;
+        size = 4;
+    } else if (w_addr == 0x1158) { /* ANY send */
+        cpuid = (val >> 16) & 0x3ff;
+        w_addr = val & 0xffff;
+        val = val >> 32;
+        mask = (val >> 27) & 0xf;
+        size = 1;
+
+        for (i = 0; i < 4; i++) {
+            if (!((mask >> i) & 1)) {
+                address_space_stb(lams->address_space_iocsr, w_addr,
+                                  val, MEMTXATTRS_UNSPECIFIED, NULL);
+            }
+            w_addr = w_addr + 1;
+            val = val >> 8;
+        }
+        return;
+    }
+
+    if (((w_addr & 0xff00) == 0x1000) || ((w_addr & 0xff00) == 0x1800)) {
+        w_addr = w_addr + ((target_ulong)(cpuid & 0x3) << 8);
+    }
+
+    if (size == 1) {
+        address_space_stb(lams->address_space_iocsr, w_addr,
+                          val, MEMTXATTRS_UNSPECIFIED, NULL);
+    } else if (size == 2) {
+        address_space_stw(lams->address_space_iocsr, w_addr,
+                          val, MEMTXATTRS_UNSPECIFIED, NULL);
+    } else if (size == 4) {
+        address_space_stl(lams->address_space_iocsr, w_addr,
+                          val, MEMTXATTRS_UNSPECIFIED, NULL);
+    } else if (size == 8) {
+        address_space_stq(lams->address_space_iocsr, w_addr,
+                          val, MEMTXATTRS_UNSPECIFIED, NULL);
+    }
+}
diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h
index 26ca6d2ae0..ad4fac4c5a 100644
--- a/target/loongarch/helper.h
+++ b/target/loongarch/helper.h
@@ -92,3 +92,14 @@ DEF_HELPER_2(frint_s, i64, env, i64)
 DEF_HELPER_2(frint_d, i64, env, i64)
 
 DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_RWG, void, env, i32)
+
+/*Core functions */
+#ifndef CONFIG_USER_ONLY
+DEF_HELPER_1(check_plv, void, env)
+
+DEF_HELPER_2(csr_rdq, i64, env, i64)
+DEF_HELPER_3(csr_wrq, i64, env, tl, i64)
+DEF_HELPER_4(csr_xchgq, i64, env, tl, tl, i64)
+DEF_HELPER_3(iocsr_read, i64, env, tl, i32)
+DEF_HELPER_4(iocsr_write, void, env, tl, tl, i32)
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target/loongarch/insn_trans/trans_core.c.inc b/target/loongarch/insn_trans/trans_core.c.inc
new file mode 100644
index 0000000000..a6ab2571d1
--- /dev/null
+++ b/target/loongarch/insn_trans/trans_core.c.inc
@@ -0,0 +1,437 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch translate functions for system mode
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ */
+
+/* Privileged instruction translation */
+
+#include "cpu-csr.h"
+
+#ifdef CONFIG_USER_ONLY
+
+#define GEN_FALSE_TRANS(name)   \
+static bool trans_##name(DisasContext *ctx, arg_##name * a)  \
+{   \
+    return false;   \
+}
+
+GEN_FALSE_TRANS(csrrd)
+GEN_FALSE_TRANS(csrwr)
+GEN_FALSE_TRANS(csrxchg)
+GEN_FALSE_TRANS(iocsrrd_b)
+GEN_FALSE_TRANS(iocsrrd_h)
+GEN_FALSE_TRANS(iocsrrd_w)
+GEN_FALSE_TRANS(iocsrrd_d)
+GEN_FALSE_TRANS(iocsrwr_b)
+GEN_FALSE_TRANS(iocsrwr_h)
+GEN_FALSE_TRANS(iocsrwr_w)
+GEN_FALSE_TRANS(iocsrwr_d)
+
+#else
+static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
+{
+    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+
+    gen_helper_check_plv(cpu_env);
+
+#define CASE_CSR_RDQ(csr)                                                     \
+    case LOONGARCH_CSR_ ## csr:                                               \
+    {                                                                         \
+        tcg_gen_ld_tl(dest, cpu_env, offsetof(CPULoongArchState, CSR_##csr)); \
+        break;                                                                \
+    };                                                                        \
+
+    switch (a->csr) {
+    CASE_CSR_RDQ(CRMD)
+    CASE_CSR_RDQ(PRMD)
+    CASE_CSR_RDQ(EUEN)
+    CASE_CSR_RDQ(MISC)
+    CASE_CSR_RDQ(ECFG)
+    CASE_CSR_RDQ(ESTAT)
+    CASE_CSR_RDQ(ERA)
+    CASE_CSR_RDQ(BADV)
+    CASE_CSR_RDQ(BADI)
+    CASE_CSR_RDQ(EENTRY)
+    CASE_CSR_RDQ(TLBIDX)
+    CASE_CSR_RDQ(TLBEHI)
+    CASE_CSR_RDQ(TLBELO0)
+    CASE_CSR_RDQ(TLBELO1)
+    CASE_CSR_RDQ(ASID)
+    CASE_CSR_RDQ(PGDL)
+    CASE_CSR_RDQ(PGDH)
+    CASE_CSR_RDQ(PWCL)
+    CASE_CSR_RDQ(PWCH)
+    CASE_CSR_RDQ(STLBPS)
+    CASE_CSR_RDQ(RVACFG)
+    CASE_CSR_RDQ(PRCFG1)
+    CASE_CSR_RDQ(PRCFG2)
+    CASE_CSR_RDQ(PRCFG3)
+    CASE_CSR_RDQ(SAVE0)
+    CASE_CSR_RDQ(SAVE1)
+    CASE_CSR_RDQ(SAVE2)
+    CASE_CSR_RDQ(SAVE3)
+    CASE_CSR_RDQ(SAVE4)
+    CASE_CSR_RDQ(SAVE5)
+    CASE_CSR_RDQ(SAVE6)
+    CASE_CSR_RDQ(SAVE7)
+    CASE_CSR_RDQ(TMID)
+    CASE_CSR_RDQ(TCFG)
+    CASE_CSR_RDQ(CNTC)
+    CASE_CSR_RDQ(TINTCLR)
+    CASE_CSR_RDQ(LLBCTL)
+    CASE_CSR_RDQ(IMPCTL1)
+    CASE_CSR_RDQ(IMPCTL2)
+    CASE_CSR_RDQ(TLBRENTRY)
+    CASE_CSR_RDQ(TLBRBADV)
+    CASE_CSR_RDQ(TLBRERA)
+    CASE_CSR_RDQ(TLBRSAVE)
+    CASE_CSR_RDQ(TLBRELO0)
+    CASE_CSR_RDQ(TLBRELO1)
+    CASE_CSR_RDQ(TLBREHI)
+    CASE_CSR_RDQ(TLBRPRMD)
+    CASE_CSR_RDQ(MERRCTL)
+    CASE_CSR_RDQ(MERRINFO)
+    CASE_CSR_RDQ(MERRINFO1)
+    CASE_CSR_RDQ(MERRENT)
+    CASE_CSR_RDQ(MERRERA)
+    CASE_CSR_RDQ(MERRSAVE)
+    CASE_CSR_RDQ(CTAG)
+    CASE_CSR_RDQ(DMWIN0)
+    CASE_CSR_RDQ(DMWIN1)
+    CASE_CSR_RDQ(DMWIN2)
+    CASE_CSR_RDQ(DMWIN3)
+    CASE_CSR_RDQ(PERFCTRL0)
+    CASE_CSR_RDQ(PERFCNTR0)
+    CASE_CSR_RDQ(PERFCTRL1)
+    CASE_CSR_RDQ(PERFCNTR1)
+    CASE_CSR_RDQ(PERFCTRL2)
+    CASE_CSR_RDQ(PERFCNTR2)
+    CASE_CSR_RDQ(PERFCTRL3)
+    CASE_CSR_RDQ(PERFCNTR3)
+    /* Debug */
+    CASE_CSR_RDQ(MWPC)
+    CASE_CSR_RDQ(MWPS)
+    CASE_CSR_RDQ(DB0ADDR)
+    CASE_CSR_RDQ(DB0MASK)
+    CASE_CSR_RDQ(DB0CTL)
+    CASE_CSR_RDQ(DB0ASID)
+    CASE_CSR_RDQ(DB1ADDR)
+    CASE_CSR_RDQ(DB1MASK)
+    CASE_CSR_RDQ(DB1CTL)
+    CASE_CSR_RDQ(DB1ASID)
+    CASE_CSR_RDQ(DB2ADDR)
+    CASE_CSR_RDQ(DB2MASK)
+    CASE_CSR_RDQ(DB2CTL)
+    CASE_CSR_RDQ(DB2ASID)
+    CASE_CSR_RDQ(DB3ADDR)
+    CASE_CSR_RDQ(DB3MASK)
+    CASE_CSR_RDQ(DB3CTL)
+    CASE_CSR_RDQ(DB3ASID)
+    CASE_CSR_RDQ(FWPC)
+    CASE_CSR_RDQ(FWPS)
+    CASE_CSR_RDQ(IB0ADDR)
+    CASE_CSR_RDQ(IB0MASK)
+    CASE_CSR_RDQ(IB0CTL)
+    CASE_CSR_RDQ(IB0ASID)
+    CASE_CSR_RDQ(IB1ADDR)
+    CASE_CSR_RDQ(IB1MASK)
+    CASE_CSR_RDQ(IB1CTL)
+    CASE_CSR_RDQ(IB1ASID)
+    CASE_CSR_RDQ(IB2ADDR)
+    CASE_CSR_RDQ(IB2MASK)
+    CASE_CSR_RDQ(IB2CTL)
+    CASE_CSR_RDQ(IB2ASID)
+    CASE_CSR_RDQ(IB3ADDR)
+    CASE_CSR_RDQ(IB3MASK)
+    CASE_CSR_RDQ(IB3CTL)
+    CASE_CSR_RDQ(IB3ASID)
+    CASE_CSR_RDQ(IB4ADDR)
+    CASE_CSR_RDQ(IB4MASK)
+    CASE_CSR_RDQ(IB4CTL)
+    CASE_CSR_RDQ(IB4ASID)
+    CASE_CSR_RDQ(IB5ADDR)
+    CASE_CSR_RDQ(IB5MASK)
+    CASE_CSR_RDQ(IB5CTL)
+    CASE_CSR_RDQ(IB5ASID)
+    CASE_CSR_RDQ(IB6ADDR)
+    CASE_CSR_RDQ(IB6MASK)
+    CASE_CSR_RDQ(IB6CTL)
+    CASE_CSR_RDQ(IB6ASID)
+    CASE_CSR_RDQ(IB7ADDR)
+    CASE_CSR_RDQ(IB7MASK)
+    CASE_CSR_RDQ(IB7CTL)
+    CASE_CSR_RDQ(IB7ASID)
+    CASE_CSR_RDQ(DBG)
+    CASE_CSR_RDQ(DERA)
+    CASE_CSR_RDQ(DESAVE)
+    case LOONGARCH_CSR_PGD:
+    case LOONGARCH_CSR_CPUID:
+    case LOONGARCH_CSR_TVAL:
+        gen_helper_csr_rdq(dest, cpu_env, tcg_constant_i64(a->csr));
+        break;
+    default:
+        assert(0);
+    }
+#undef CASE_CSR_RDQ
+
+    return true;
+}
+
+static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
+{
+    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+    TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE);
+    TCGv temp = tcg_temp_new();
+
+    gen_helper_check_plv(cpu_env);
+#define CASE_CSR_WRQ(csr)                                                     \
+    case LOONGARCH_CSR_ ## csr:                                               \
+    {                                                                         \
+        tcg_gen_ld_tl(temp, cpu_env, offsetof(CPULoongArchState, CSR_##csr)); \
+        tcg_gen_st_tl(src1, cpu_env, offsetof(CPULoongArchState, CSR_##csr)); \
+        tcg_gen_mov_tl(dest, temp);                                           \
+        tcg_temp_free(temp);                                                  \
+        break;                                                                \
+    };                                                                        \
+
+    switch (a->csr) {
+    case LOONGARCH_CSR_CRMD:
+        tcg_gen_ld_tl(temp, cpu_env, offsetof(CPULoongArchState, CSR_CRMD));
+        tcg_gen_st_tl(src1, cpu_env, offsetof(CPULoongArchState, CSR_CRMD));
+        tcg_gen_mov_tl(dest, temp);
+        tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
+        tcg_temp_free(temp);
+        ctx->base.is_jmp = DISAS_EXIT;
+        break;
+    case LOONGARCH_CSR_EUEN:
+        tcg_gen_ld_tl(temp, cpu_env, offsetof(CPULoongArchState, CSR_EUEN));
+        tcg_gen_st_tl(src1, cpu_env, offsetof(CPULoongArchState, CSR_EUEN));
+        tcg_gen_mov_tl(dest, temp);
+        tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
+        tcg_temp_free(temp);
+        ctx->base.is_jmp = DISAS_EXIT;
+        break;
+    CASE_CSR_WRQ(PRMD)
+    CASE_CSR_WRQ(MISC)
+    CASE_CSR_WRQ(ECFG)
+    CASE_CSR_WRQ(ESTAT)
+    CASE_CSR_WRQ(ERA)
+    CASE_CSR_WRQ(BADV)
+    CASE_CSR_WRQ(BADI)
+    CASE_CSR_WRQ(EENTRY)
+    CASE_CSR_WRQ(TLBIDX)
+    CASE_CSR_WRQ(TLBEHI)
+    CASE_CSR_WRQ(TLBELO0)
+    CASE_CSR_WRQ(TLBELO1)
+    CASE_CSR_WRQ(PGDL)
+    CASE_CSR_WRQ(PGDH)
+    CASE_CSR_WRQ(PWCL)
+    CASE_CSR_WRQ(PWCH)
+    CASE_CSR_WRQ(STLBPS)
+    CASE_CSR_WRQ(RVACFG)
+    CASE_CSR_WRQ(PRCFG1)
+    CASE_CSR_WRQ(PRCFG2)
+    CASE_CSR_WRQ(PRCFG3)
+    CASE_CSR_WRQ(SAVE0)
+    CASE_CSR_WRQ(SAVE1)
+    CASE_CSR_WRQ(SAVE2)
+    CASE_CSR_WRQ(SAVE3)
+    CASE_CSR_WRQ(SAVE4)
+    CASE_CSR_WRQ(SAVE5)
+    CASE_CSR_WRQ(SAVE6)
+    CASE_CSR_WRQ(SAVE7)
+    CASE_CSR_WRQ(TMID)
+    CASE_CSR_WRQ(TVAL)
+    CASE_CSR_WRQ(CNTC)
+    CASE_CSR_WRQ(LLBCTL)
+    CASE_CSR_WRQ(IMPCTL1)
+    CASE_CSR_WRQ(IMPCTL2)
+    CASE_CSR_WRQ(TLBRENTRY)
+    CASE_CSR_WRQ(TLBRBADV)
+    CASE_CSR_WRQ(TLBRERA)
+    CASE_CSR_WRQ(TLBRSAVE)
+    CASE_CSR_WRQ(TLBRELO0)
+    CASE_CSR_WRQ(TLBRELO1)
+    CASE_CSR_WRQ(TLBREHI)
+    CASE_CSR_WRQ(TLBRPRMD)
+    CASE_CSR_WRQ(MERRCTL)
+    CASE_CSR_WRQ(MERRINFO)
+    CASE_CSR_WRQ(MERRINFO1)
+    CASE_CSR_WRQ(MERRENT)
+    CASE_CSR_WRQ(MERRERA)
+    CASE_CSR_WRQ(MERRSAVE)
+    CASE_CSR_WRQ(CTAG)
+    CASE_CSR_WRQ(DMWIN0)
+    CASE_CSR_WRQ(DMWIN1)
+    CASE_CSR_WRQ(DMWIN2)
+    CASE_CSR_WRQ(DMWIN3)
+    CASE_CSR_WRQ(PERFCTRL0)
+    CASE_CSR_WRQ(PERFCNTR0)
+    CASE_CSR_WRQ(PERFCTRL1)
+    CASE_CSR_WRQ(PERFCNTR1)
+    CASE_CSR_WRQ(PERFCTRL2)
+    CASE_CSR_WRQ(PERFCNTR2)
+    CASE_CSR_WRQ(PERFCTRL3)
+    CASE_CSR_WRQ(PERFCNTR3)
+    /* Debug */
+    CASE_CSR_WRQ(MWPC)
+    CASE_CSR_WRQ(MWPS)
+    CASE_CSR_WRQ(DB0ADDR)
+    CASE_CSR_WRQ(DB0MASK)
+    CASE_CSR_WRQ(DB0CTL)
+    CASE_CSR_WRQ(DB0ASID)
+    CASE_CSR_WRQ(DB1ADDR)
+    CASE_CSR_WRQ(DB1MASK)
+    CASE_CSR_WRQ(DB1CTL)
+    CASE_CSR_WRQ(DB1ASID)
+    CASE_CSR_WRQ(DB2ADDR)
+    CASE_CSR_WRQ(DB2MASK)
+    CASE_CSR_WRQ(DB2CTL)
+    CASE_CSR_WRQ(DB2ASID)
+    CASE_CSR_WRQ(DB3ADDR)
+    CASE_CSR_WRQ(DB3MASK)
+    CASE_CSR_WRQ(DB3CTL)
+    CASE_CSR_WRQ(DB3ASID)
+    CASE_CSR_WRQ(FWPC)
+    CASE_CSR_WRQ(FWPS)
+    CASE_CSR_WRQ(IB0ADDR)
+    CASE_CSR_WRQ(IB0MASK)
+    CASE_CSR_WRQ(IB0CTL)
+    CASE_CSR_WRQ(IB0ASID)
+    CASE_CSR_WRQ(IB1ADDR)
+    CASE_CSR_WRQ(IB1MASK)
+    CASE_CSR_WRQ(IB1CTL)
+    CASE_CSR_WRQ(IB1ASID)
+    CASE_CSR_WRQ(IB2ADDR)
+    CASE_CSR_WRQ(IB2MASK)
+    CASE_CSR_WRQ(IB2CTL)
+    CASE_CSR_WRQ(IB2ASID)
+    CASE_CSR_WRQ(IB3ADDR)
+    CASE_CSR_WRQ(IB3MASK)
+    CASE_CSR_WRQ(IB3CTL)
+    CASE_CSR_WRQ(IB3ASID)
+    CASE_CSR_WRQ(IB4ADDR)
+    CASE_CSR_WRQ(IB4MASK)
+    CASE_CSR_WRQ(IB4CTL)
+    CASE_CSR_WRQ(IB4ASID)
+    CASE_CSR_WRQ(IB5ADDR)
+    CASE_CSR_WRQ(IB5MASK)
+    CASE_CSR_WRQ(IB5CTL)
+    CASE_CSR_WRQ(IB5ASID)
+    CASE_CSR_WRQ(IB6ADDR)
+    CASE_CSR_WRQ(IB6MASK)
+    CASE_CSR_WRQ(IB6CTL)
+    CASE_CSR_WRQ(IB6ASID)
+    CASE_CSR_WRQ(IB7ADDR)
+    CASE_CSR_WRQ(IB7MASK)
+    CASE_CSR_WRQ(IB7CTL)
+    CASE_CSR_WRQ(IB7ASID)
+    CASE_CSR_WRQ(DBG)
+    CASE_CSR_WRQ(DERA)
+    CASE_CSR_WRQ(DESAVE)
+    case LOONGARCH_CSR_ASID:
+    case LOONGARCH_CSR_TCFG:
+    case LOONGARCH_CSR_TINTCLR:
+        gen_helper_csr_wrq(dest, cpu_env, src1, tcg_constant_i64(a->csr));
+        break;
+    default:
+        assert(0);
+    }
+#undef CASE_CSR_WRQ
+
+    return true;
+}
+
+static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
+{
+    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+    TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE);
+    TCGv src2 = gpr_src(ctx, a->rj, EXT_NONE);
+
+    gen_helper_check_plv(cpu_env);
+    gen_helper_csr_xchgq(dest, cpu_env, src1, src2, tcg_constant_i64(a->csr));
+    return true;
+}
+
+static bool trans_iocsrrd_b(DisasContext *ctx, arg_iocsrrd_b *a)
+{
+    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+    TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+    gen_helper_check_plv(cpu_env);
+    gen_helper_iocsr_read(dest, cpu_env, src1, tcg_constant_i32(1));
+    return true;
+}
+
+static bool trans_iocsrrd_h(DisasContext *ctx, arg_iocsrrd_h *a)
+{
+    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+    TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+    gen_helper_check_plv(cpu_env);
+    gen_helper_iocsr_read(dest, cpu_env, src1, tcg_constant_i32(2));
+    return true;
+}
+
+static bool trans_iocsrrd_w(DisasContext *ctx, arg_iocsrrd_w *a)
+{
+    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+    TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+    gen_helper_check_plv(cpu_env);
+    gen_helper_iocsr_read(dest, cpu_env, src1, tcg_constant_i32(4));
+    return true;
+}
+
+static bool trans_iocsrrd_d(DisasContext *ctx, arg_iocsrrd_d *a)
+{
+    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+    TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+    gen_helper_check_plv(cpu_env);
+    gen_helper_iocsr_read(dest, cpu_env, src1, tcg_constant_i32(8));
+    return true;
+}
+
+static bool trans_iocsrwr_b(DisasContext *ctx, arg_iocsrwr_b *a)
+{
+    TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
+    TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+
+    gen_helper_check_plv(cpu_env);
+    gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(1));
+    return true;
+}
+
+static bool trans_iocsrwr_h(DisasContext *ctx, arg_iocsrwr_h *a)
+{
+    TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
+    TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+
+    gen_helper_check_plv(cpu_env);
+    gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(2));
+    return true;
+}
+
+static bool trans_iocsrwr_w(DisasContext *ctx, arg_iocsrwr_w *a)
+{
+    TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
+    TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+
+    gen_helper_check_plv(cpu_env);
+    gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(4));
+    return true;
+}
+
+static bool trans_iocsrwr_d(DisasContext *ctx, arg_iocsrwr_d *a)
+{
+    TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
+    TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
+
+    gen_helper_check_plv(cpu_env);
+    gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(8));
+    return true;
+}
+#endif
diff --git a/target/loongarch/insns.decode b/target/loongarch/insns.decode
index e28b843195..a867cb84d2 100644
--- a/target/loongarch/insns.decode
+++ b/target/loongarch/insns.decode
@@ -40,6 +40,7 @@
 %offs21  0:s5 10:16
 %offs16  10:s16
 %offs    0:s10 10:16
+%csr     10:14
 
 #
 # Argument sets
@@ -81,6 +82,8 @@
 &fmt_rdrjoffs16     rd rj offs16
 &fmt_offs           offs
 &fmt_rjrdoffs16     rj rd offs16
+&fmt_rdcsr          rd csr
+&fmt_rdrjcsr        rd rj csr
 
 #
 # Formats
@@ -122,6 +125,8 @@
 @fmt_rdrjoffs16      .... .. ................ ..... .....     &fmt_rdrjoffs16     %rd %rj %offs16
 @fmt_offs            .... .. ..........................       &fmt_offs           %offs
 @fmt_rjrdoffs16      .... .. ................ ..... .....     &fmt_rjrdoffs16     %rj %rd %offs16
+@fmt_rdcsr           .... .... .............. ..... .....     &fmt_rdcsr          %rd %csr
+@fmt_rdrjcsr         .... .... .............. ..... .....     &fmt_rdrjcsr        %rd %rj %csr
 
 #
 # Fixed point arithmetic operation instruction
@@ -477,3 +482,20 @@ blt              0110 00 ................ ..... .....     @fmt_rjrdoffs16
 bge              0110 01 ................ ..... .....     @fmt_rjrdoffs16
 bltu             0110 10 ................ ..... .....     @fmt_rjrdoffs16
 bgeu             0110 11 ................ ..... .....     @fmt_rjrdoffs16
+
+#
+# Core instructions
+#
+{
+  csrrd             0000 0100 .............. 00000 .....     @fmt_rdcsr
+  csrwr             0000 0100 .............. 00001 .....     @fmt_rdcsr
+  csrxchg           0000 0100 .............. ..... .....     @fmt_rdrjcsr
+}
+iocsrrd_b        0000 01100100 10000 00000 ..... .....    @fmt_rdrj
+iocsrrd_h        0000 01100100 10000 00001 ..... .....    @fmt_rdrj
+iocsrrd_w        0000 01100100 10000 00010 ..... .....    @fmt_rdrj
+iocsrrd_d        0000 01100100 10000 00011 ..... .....    @fmt_rdrj
+iocsrwr_b        0000 01100100 10000 00100 ..... .....    @fmt_rdrj
+iocsrwr_h        0000 01100100 10000 00101 ..... .....    @fmt_rdrj
+iocsrwr_w        0000 01100100 10000 00110 ..... .....    @fmt_rdrj
+iocsrwr_d        0000 01100100 10000 00111 ..... .....    @fmt_rdrj
diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build
index 935ffe2765..080d6297de 100644
--- a/target/loongarch/meson.build
+++ b/target/loongarch/meson.build
@@ -19,6 +19,7 @@ loongarch_softmmu_ss.add(files(
   'machine.c',
   'stabletimer.c',
   'tlb_helper.c',
+  'csr_helper.c',
 ))
 
 loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss])
diff --git a/target/loongarch/op_helper.c b/target/loongarch/op_helper.c
index fdd43032c4..20014ef07a 100644
--- a/target/loongarch/op_helper.c
+++ b/target/loongarch/op_helper.c
@@ -82,3 +82,13 @@ target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong rj)
 {
     return env->cpucfg[rj];
 }
+
+#ifndef CONFIG_USER_ONLY
+void helper_check_plv(CPULoongArchState *env)
+{
+    uint64_t plv = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV);
+    if (plv) {
+        do_raise_exception(env, EXCP_IPE, GETPC());
+    }
+}
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target/loongarch/translate.c b/target/loongarch/translate.c
index 437f33d522..3935b14163 100644
--- a/target/loongarch/translate.c
+++ b/target/loongarch/translate.c
@@ -26,6 +26,7 @@ TCGv_i32 cpu_fcsr0;
 TCGv_i64 cpu_fpr[32];
 
 #define DISAS_STOP       DISAS_TARGET_0
+#define DISAS_EXIT       DISAS_TARGET_1
 
 /*
  * LoongArch the upper 32 bits are undefined ("can be any value").
@@ -191,6 +192,7 @@ static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext)
 #include "insn_trans/trans_fmov.c.inc"
 #include "insn_trans/trans_fmemory.c.inc"
 #include "insn_trans/trans_branch.c.inc"
+#include "insn_trans/trans_core.c.inc"
 
 static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
 {
@@ -228,6 +230,9 @@ static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
         break;
     case DISAS_NORETURN:
         break;
+    case DISAS_EXIT:
+        tcg_gen_exit_tb(NULL, 0);
+        break;
     default:
         g_assert_not_reached();
     }
-- 
2.27.0



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

* [RFC PATCH v2 09/30] target/loongarch: Add TLB instruction support
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (7 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 08/30] target/loongarch: Add LoongArch CSR/IOCSR instruction Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 18:14   ` Richard Henderson
  2021-11-11  1:35 ` [RFC PATCH v2 10/30] target/loongarch: Add other core instructions support Xiaojuan Yang
                   ` (21 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This includes:
- TLBSRCH
- TLBRD
- TLBWR
- TLBFILL
- TLBCLR
- TLBFLUSH
- INVTLB

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/helper.h                    |   8 +
 target/loongarch/insn_trans/trans_core.c.inc |  71 +++
 target/loongarch/insns.decode                |  14 +
 target/loongarch/tlb_helper.c                | 478 +++++++++++++++++++
 4 files changed, 571 insertions(+)

diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h
index ad4fac4c5a..dea0087273 100644
--- a/target/loongarch/helper.h
+++ b/target/loongarch/helper.h
@@ -102,4 +102,12 @@ DEF_HELPER_3(csr_wrq, i64, env, tl, i64)
 DEF_HELPER_4(csr_xchgq, i64, env, tl, tl, i64)
 DEF_HELPER_3(iocsr_read, i64, env, tl, i32)
 DEF_HELPER_4(iocsr_write, void, env, tl, tl, i32)
+
+DEF_HELPER_1(tlbwr, void, env)
+DEF_HELPER_1(tlbfill, void, env)
+DEF_HELPER_1(tlbsrch, void, env)
+DEF_HELPER_1(tlbrd, void, env)
+DEF_HELPER_1(tlbclr, void, env)
+DEF_HELPER_1(tlbflush, void, env)
+DEF_HELPER_4(invtlb, void, env, tl, tl, tl)
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target/loongarch/insn_trans/trans_core.c.inc b/target/loongarch/insn_trans/trans_core.c.inc
index a6ab2571d1..c34163efec 100644
--- a/target/loongarch/insn_trans/trans_core.c.inc
+++ b/target/loongarch/insn_trans/trans_core.c.inc
@@ -28,6 +28,13 @@ GEN_FALSE_TRANS(iocsrwr_b)
 GEN_FALSE_TRANS(iocsrwr_h)
 GEN_FALSE_TRANS(iocsrwr_w)
 GEN_FALSE_TRANS(iocsrwr_d)
+GEN_FALSE_TRANS(tlbsrch)
+GEN_FALSE_TRANS(tlbrd)
+GEN_FALSE_TRANS(tlbwr)
+GEN_FALSE_TRANS(tlbfill)
+GEN_FALSE_TRANS(tlbclr)
+GEN_FALSE_TRANS(tlbflush)
+GEN_FALSE_TRANS(invtlb)
 
 #else
 static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
@@ -434,4 +441,68 @@ static bool trans_iocsrwr_d(DisasContext *ctx, arg_iocsrwr_d *a)
     gen_helper_iocsr_write(cpu_env, addr, val, tcg_constant_i32(8));
     return true;
 }
+
+static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a)
+{
+    gen_helper_check_plv(cpu_env);
+    gen_helper_tlbsrch(cpu_env);
+    return true;
+}
+
+static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
+{
+    gen_helper_check_plv(cpu_env);
+    gen_helper_tlbrd(cpu_env);
+    return true;
+}
+
+static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
+{
+    gen_helper_check_plv(cpu_env);
+    gen_helper_tlbwr(cpu_env);
+    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
+    ctx->base.is_jmp = DISAS_EXIT;
+    return true;
+}
+
+static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a)
+{
+    gen_helper_check_plv(cpu_env);
+    gen_helper_tlbfill(cpu_env);
+    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
+    ctx->base.is_jmp = DISAS_EXIT;
+    return true;
+}
+
+static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
+{
+    gen_helper_check_plv(cpu_env);
+    gen_helper_tlbclr(cpu_env);
+    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
+    ctx->base.is_jmp = DISAS_EXIT;
+    return true;
+}
+
+static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a)
+{
+    gen_helper_check_plv(cpu_env);
+    gen_helper_tlbflush(cpu_env);
+    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
+    ctx->base.is_jmp = DISAS_EXIT;
+    return true;
+}
+
+static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
+{
+    TCGv rj = gpr_src(ctx, a->rj, EXT_NONE);
+    TCGv rk = gpr_src(ctx, a->rk, EXT_NONE);
+    TCGv op = tcg_constant_tl(a->invop);
+
+    gen_helper_check_plv(cpu_env);
+    gen_helper_invtlb(cpu_env, rj, rk, op);
+    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
+    ctx->base.is_jmp = DISAS_EXIT;
+    return true;
+}
+
 #endif
diff --git a/target/loongarch/insns.decode b/target/loongarch/insns.decode
index a867cb84d2..f5031e089e 100644
--- a/target/loongarch/insns.decode
+++ b/target/loongarch/insns.decode
@@ -41,6 +41,9 @@
 %offs16  10:s16
 %offs    0:s10 10:16
 %csr     10:14
+%addr    10:5
+%info    5:5
+%invop   0:5
 
 #
 # Argument sets
@@ -84,6 +87,8 @@
 &fmt_rjrdoffs16     rj rd offs16
 &fmt_rdcsr          rd csr
 &fmt_rdrjcsr        rd rj csr
+&fmt_empty
+&fmt_invtlb         invop rj rk
 
 #
 # Formats
@@ -127,6 +132,8 @@
 @fmt_rjrdoffs16      .... .. ................ ..... .....     &fmt_rjrdoffs16     %rj %rd %offs16
 @fmt_rdcsr           .... .... .............. ..... .....     &fmt_rdcsr          %rd %csr
 @fmt_rdrjcsr         .... .... .............. ..... .....     &fmt_rdrjcsr        %rd %rj %csr
+@fmt_empty           .... ........ ..... ..... ..... .....    &fmt_empty
+@fmt_invtlb          ...... ...... ..... ..... ..... .....    &fmt_invtlb         %invop %rj %rk
 
 #
 # Fixed point arithmetic operation instruction
@@ -499,3 +506,10 @@ iocsrwr_b        0000 01100100 10000 00100 ..... .....    @fmt_rdrj
 iocsrwr_h        0000 01100100 10000 00101 ..... .....    @fmt_rdrj
 iocsrwr_w        0000 01100100 10000 00110 ..... .....    @fmt_rdrj
 iocsrwr_d        0000 01100100 10000 00111 ..... .....    @fmt_rdrj
+tlbsrch          0000 01100100 10000 01010 00000 00000    @fmt_empty
+tlbrd            0000 01100100 10000 01011 00000 00000    @fmt_empty
+tlbwr            0000 01100100 10000 01100 00000 00000    @fmt_empty
+tlbfill          0000 01100100 10000 01101 00000 00000    @fmt_empty
+tlbclr           0000 01100100 10000 01000 00000 00000    @fmt_empty
+tlbflush         0000 01100100 10000 01001 00000 00000    @fmt_empty
+invtlb           0000 01100100 10011 ..... ..... .....    @fmt_invtlb
diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tlb_helper.c
index 69c69ece0a..f36e379499 100644
--- a/target/loongarch/tlb_helper.c
+++ b/target/loongarch/tlb_helper.c
@@ -7,9 +7,11 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/guest-random.h"
 
 #include "cpu.h"
 #include "internals.h"
+#include "exec/helper-proto.h"
 #include "exec/exec-all.h"
 #include "exec/cpu_ldst.h"
 #include "exec/log.h"
@@ -284,6 +286,482 @@ static void raise_mmu_exception(CPULoongArchState *env, target_ulong address,
 
 }
 
+static void cpu_loongarch_tlb_flush(CPULoongArchState *env)
+{
+    /* Flush qemu's TLB and discard all shadowed entries. */
+    tlb_flush(env_cpu(env));
+}
+
+static void loongarch_invalidate_tlb_entry(CPULoongArchState *env,
+                                           loongarch_tlb *tlb)
+{
+    CPUState *cs = env_cpu(env);
+    target_ulong addr, end, mask;
+    int tlb_v0, tlb_v1;
+    uint64_t tlb_vppn;
+    uint8_t tlb_ps;
+
+    tlb_v0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, V);
+    tlb_v1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, V);
+    tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
+    tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
+    mask = (1 << (1 + tlb_ps)) - 1;
+
+    if (tlb_v0) {
+        addr = tlb_vppn & ~mask;    /* xxx...xxx[0]000..0000 */
+        end = addr | (mask >> 1);   /* xxx...xxx[0]111..1111 */
+        while (addr < end) {
+            tlb_flush_page(cs, addr);
+            addr += TARGET_PAGE_SIZE;
+        }
+    }
+
+    if (tlb_v1) {
+        /* xxx...xxx[1]000..0000 */
+        addr = (tlb_vppn & ~mask) | ((mask >> 1) + 1);
+        end = addr | mask;              /* xxx...xxx[1]111..1111 */
+        while (addr - 1 < end) {
+            tlb_flush_page(cs, addr);
+            addr += TARGET_PAGE_SIZE;
+        }
+    }
+}
+
+static void loongarch_invalidate_tlb(CPULoongArchState *env, int idx)
+{
+    loongarch_tlb *tlb;
+    uint16_t csr_asid, tlb_asid, tlb_g;
+
+    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
+    tlb = &env->tlb[idx];
+    tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
+    tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
+    if (tlb_g == 0 && tlb_asid != csr_asid) {
+        return;
+    }
+    loongarch_invalidate_tlb_entry(env, tlb);
+}
+
+static void loongarch_fill_tlb_entry(CPULoongArchState *env,
+                                     loongarch_tlb *tlb, int is_stlb)
+{
+    uint64_t lo0, lo1, csr_vppn;
+    uint16_t csr_asid;
+    uint8_t csr_g, stlb_ps, csr_ps;
+
+    if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
+        csr_ps = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS);
+        csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, VPPN);
+        lo0 = env->CSR_TLBRELO0;
+        lo1 = env->CSR_TLBRELO1;
+    } else {
+        csr_ps = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS);
+        csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI, VPPN);
+        lo0 = env->CSR_TLBELO0;
+        lo1 = env->CSR_TLBELO1;
+    }
+
+    if (csr_ps == 0) {
+        qemu_log_mask(CPU_LOG_MMU, "page size is 0\n");
+    }
+
+    /*
+     *       15-12  11-8   7-4    3-0
+     *  4KB: 0001   1111   1111   1111 // double 4KB  mask [12:0]
+     * 16KB: 0111   1111   1111   1111 // double 16KB mask [14:0]
+     */
+    if (is_stlb) {
+        stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
+        tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, stlb_ps);
+    } else {
+        tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps);
+    }
+
+    tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn);
+    tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1);
+    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
+    tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid);
+
+    csr_g = FIELD_EX64(env->CSR_TLBELO0, CSR_TLBELO0, G) &
+             FIELD_EX64(env->CSR_TLBELO1, CSR_TLBELO1, G);
+    tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, G, csr_g);
+
+    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, V,
+                                 FIELD_EX64(lo0, CSR_TLBELO0, V));/* [0] */
+    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, D,
+                                 FIELD_EX64(lo0, CSR_TLBELO0, D));/* [1] */
+    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, PLV,
+                                 FIELD_EX64(lo0, CSR_TLBELO0, PLV));/* [3:2] */
+    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, MAT,
+                                 FIELD_EX64(lo0, CSR_TLBELO0, MAT));/* [5:4] */
+    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, PPN,
+                                 FIELD_EX64(lo0, CSR_TLBELO0, PPN));/* [47:12] */
+    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, NR,
+                                 FIELD_EX64(lo0, CSR_TLBELO0, NR));/* [61] */
+    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, NX,
+                                 FIELD_EX64(lo0, CSR_TLBELO0, NX));/* [62] */
+    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, RPLV,
+                                 FIELD_EX64(lo0, CSR_TLBELO0, RPLV));/* [63] */
+
+    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, V,
+                                 FIELD_EX64(lo1, CSR_TLBELO1, V));/* [0] */
+    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, D,
+                                 FIELD_EX64(lo1, CSR_TLBELO1, D));/* [1] */
+    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, PLV,
+                                 FIELD_EX64(lo1, CSR_TLBELO1, PLV));/* [3:2] */
+    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, MAT,
+                                 FIELD_EX64(lo1, CSR_TLBELO1, MAT));/* [5:4] */
+    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, PPN,
+                                 FIELD_EX64(lo1, CSR_TLBELO1, PPN));/* [47:12] */
+    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, NR,
+                                 FIELD_EX64(lo1, CSR_TLBELO1, NR));/* [61] */
+    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, NX,
+                                 FIELD_EX64(lo1, CSR_TLBELO1, NX));/* [62] */
+    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, RPLV,
+                                 FIELD_EX64(lo1, CSR_TLBELO1, RPLV));/* [63] */
+}
+
+static void loongarch_fill_tlb(CPULoongArchState *env, int idx)
+{
+    loongarch_tlb *tlb;
+    tlb = &env->tlb[idx];
+
+    if (idx < 2048) {
+        loongarch_fill_tlb_entry(env, tlb, 1);
+    } else {
+        loongarch_fill_tlb_entry(env, tlb, 0);
+    }
+}
+
+/* Return random value in [low, high] */
+static uint32_t cpu_loongarch_get_random_loongarch_tlb(uint32_t low,
+                                                       uint32_t high)
+{
+    uint32_t val;
+
+    qemu_guest_getrandom_nofail(&val, sizeof(val));
+    return val % (high - low + 1) + low;
+}
+
+void helper_tlbsrch(CPULoongArchState *env)
+{
+    loongarch_tlb *tlb;
+    uint64_t vpn, tlb_vppn;
+    uint16_t csr_asid, tlb_asid, tlb_ps, tlb_e, tlb_g;
+
+    int stlb_size = env->stlb_size;
+    int mtlb_size = env->mtlb_size;
+    int i;
+    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
+
+    /* Search MTLB + STLB */
+    for (i = 0; i < stlb_size + mtlb_size; ++i) {
+        tlb = &env->tlb[i];
+        vpn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI, VPPN);
+        tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
+        tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
+        tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
+        tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
+        tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
+
+        if ((tlb_g == 1 || tlb_asid == csr_asid) &&
+            (vpn >> (tlb_ps + 1 - 13) == tlb_vppn >> (tlb_ps + 1 - 13)) && tlb_e) {
+            env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
+                                         INDEX, (i & 0xfff));
+            env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
+                                         PS, (tlb_ps & 0x3f));
+            return;
+        }
+    }
+
+    env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
+}
+
+void helper_tlbrd(CPULoongArchState *env)
+{
+    loongarch_tlb *tlb;
+    int idx;
+    uint16_t csr_asid, tlb_asid;
+    uint8_t tlb_ps, tlb_e, tlb_v0, tlb_v1, tlb_d0, tlb_d1;
+    uint8_t tlb_plv0, tlb_plv1, tlb_mat0, tlb_mat1, tlb_g;
+    uint64_t tlb_ppn0, tlb_ppn1;
+    uint8_t tlb_nr0, tlb_nr1, tlb_nx0, tlb_nx1, tlb_rplv0, tlb_rplv1;
+
+    idx = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
+    tlb = &env->tlb[idx];
+
+    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
+    tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
+    tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
+    tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
+    tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
+
+    tlb_v0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, V);
+    tlb_d0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, D);
+    tlb_plv0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, PLV);
+    tlb_mat0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, MAT);
+    tlb_ppn0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, PPN);
+    tlb_nr0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, NR);
+    tlb_nx0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, NX);
+    tlb_rplv0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, RPLV);
+
+    tlb_v1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, V);
+    tlb_d1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, D);
+    tlb_plv1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, PLV);
+    tlb_mat1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, MAT);
+    tlb_ppn1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, PPN);
+    tlb_nr1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, NR);
+    tlb_nx1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, NX);
+    tlb_rplv1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, RPLV);
+
+    if (csr_asid != tlb_asid) {
+        cpu_loongarch_tlb_flush(env);
+    }
+
+    if (!tlb_e) {
+        /* Invalid TLB entry */
+        env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
+        env->CSR_TLBEHI = 0;
+        env->CSR_TLBELO0 = 0;
+        env->CSR_TLBELO1 = 0;
+    } else {
+        /* Valid TLB entry */
+        env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
+                                     INDEX, (idx & 0xfff));
+        env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
+                                     PS, (tlb_ps & 0x3f));
+
+        env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) << 13;
+
+        env->CSR_TLBELO0 = FIELD_DP64(0, CSR_TLBELO0, V, tlb_v0);
+        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, D, tlb_d0);
+        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, PLV, tlb_plv0);
+        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, MAT, tlb_mat0);
+        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, G, tlb_g);
+        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, PPN, tlb_ppn0);
+        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, NR, tlb_nr0);
+        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, NX, tlb_nx0);
+        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, RPLV, tlb_rplv0);
+
+        env->CSR_TLBELO1 = FIELD_DP64(0, CSR_TLBELO1, V, tlb_v1);
+        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, D, tlb_d1);
+        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, PLV, tlb_plv1);
+        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, MAT, tlb_mat1);
+        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, G, tlb_g);
+        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, PPN, tlb_ppn1);
+        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, NR, tlb_nr1);
+        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, NX, tlb_nx1);
+        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, RPLV, tlb_rplv1);
+    }
+    env->CSR_ASID  = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, tlb_asid);
+}
+
+void helper_tlbwr(CPULoongArchState *env)
+{
+    int idx = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); /* 0-11 */
+
+    loongarch_invalidate_tlb(env, idx);
+
+    if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) {
+        env->tlb[idx].tlb_misc = FIELD_DP64(env->tlb[idx].tlb_misc,
+                                            TLB_MISC, E, 0);
+        return;
+    }
+
+    loongarch_fill_tlb(env, idx);
+}
+
+void helper_tlbfill(CPULoongArchState *env)
+{
+    uint64_t address, entryhi;
+    int idx, set, stlb_idx;
+    uint16_t pagesize, stlb_ps;
+
+    if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
+        entryhi = env->CSR_TLBREHI;
+        pagesize = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS);
+    } else {
+        entryhi = env->CSR_TLBEHI;
+        pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS);
+    }
+
+    uint32_t stlb_size = env->stlb_size;
+    uint32_t mtlb_size = env->mtlb_size;
+
+    stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
+
+    if (pagesize == stlb_ps && env->stlb_size > 0) {
+        /* Only write into STLB */
+        address = entryhi & 0xffffffffe000; /* [47:13] */
+
+        /* Choose one set ramdomly */
+        set = cpu_loongarch_get_random_loongarch_tlb(0, 7);
+
+        /* Index in one set */
+        stlb_idx = (address >> 15) & 0xff; /* [0,255] */
+
+        idx = set * 256 + stlb_idx;
+    } else {
+        /* Only write into MTLB */
+        idx = cpu_loongarch_get_random_loongarch_tlb(
+                stlb_size, stlb_size + mtlb_size - 1);
+    }
+
+    loongarch_invalidate_tlb(env, idx);
+    loongarch_fill_tlb(env, idx);
+}
+
+void helper_tlbclr(CPULoongArchState *env)
+{
+    loongarch_tlb *tlb;
+    int i;
+    uint16_t csr_asid, tlb_asid, tlb_g;
+    int msize, ssize, index;
+
+    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
+    msize = env->mtlb_size;
+    ssize = env->stlb_size;
+    index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
+
+    if (index < ssize) {
+        /* STLB. One line per operation */
+        for (i = 0; i < 8; i++) {
+            tlb = &env->tlb[i * 256 + (index % 256)];
+            tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
+            tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
+            if (!tlb_g && tlb_asid == csr_asid) {
+                tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
+            }
+        }
+    } else if (index < (ssize + msize)) {
+        /* MTLB. All entries */
+        for (i = ssize; i < ssize + msize; i++) {
+            tlb = &env->tlb[i];
+            tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
+            tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
+            if (!tlb_g && tlb_asid == csr_asid) {
+                tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
+            }
+        }
+    }
+
+    cpu_loongarch_tlb_flush(env);
+}
+
+void helper_tlbflush(CPULoongArchState *env)
+{
+    int i;
+    int msize, ssize, index;
+
+    msize = env->mtlb_size;
+    ssize = env->stlb_size;
+    index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
+
+    if (index < ssize) {
+        /* STLB. One line per operation */
+        for (i = 0; i < 8; i++) {
+            int idx = i * 256 + (index % 256);
+            env->tlb[idx].tlb_misc = FIELD_DP64(env->tlb[idx].tlb_misc,
+                                                TLB_MISC, E, 0);
+        }
+    } else if (index < (ssize + msize)) {
+        /* MTLB. All entries */
+        for (i = ssize; i < ssize + msize; i++) {
+            env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
+                                              TLB_MISC, E, 0);
+        }
+    }
+
+    cpu_loongarch_tlb_flush(env);
+}
+
+void helper_invtlb(CPULoongArchState *env, target_ulong addr,
+                   target_ulong info, target_ulong op)
+{
+    uint32_t csr_asid = info & 0x3ff;
+    uint16_t tlb_asid, tlb_g;
+    int i;
+
+    switch (op) {
+    case 0:
+    case 1:
+        for (i = 0; i < LOONGARCH_TLB_MAX; i++) {
+            env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
+                                              TLB_MISC, E, 0);
+        }
+        break;
+    case 2:
+        for (i = 0; i < LOONGARCH_TLB_MAX; i++) {
+            loongarch_tlb *tlb = &env->tlb[i];
+
+            if (FIELD_EX64(tlb->tlb_misc, TLB_MISC, G)) {
+                tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
+            }
+        }
+        break;
+    case 3:
+        for (i = 0; i < LOONGARCH_TLB_MAX; i++) {
+            loongarch_tlb *tlb = &env->tlb[i];
+
+            if (!FIELD_EX64(tlb->tlb_misc, TLB_MISC, G)) {
+                tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
+            }
+        }
+        break;
+    case 4:
+        for (i = 0; i < LOONGARCH_TLB_MAX; i++) {
+            loongarch_tlb *tlb = &env->tlb[i];
+            tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
+
+            if (!FIELD_EX64(tlb->tlb_misc, TLB_MISC, G) &&
+               (tlb_asid == csr_asid)) {
+                tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
+            }
+        }
+        break;
+    case 5:
+        for (i = 0; i < LOONGARCH_TLB_MAX; i++) {
+            loongarch_tlb *tlb = &env->tlb[i];
+            uint64_t vpn, tlb_vppn;
+            uint8_t tlb_ps;
+
+            tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
+            tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
+            tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
+            tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
+            vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
+
+            if (!tlb_g && tlb_asid == csr_asid &&
+               (vpn == (tlb_vppn >> (tlb_ps + 1 - 13)))) {
+                tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
+            }
+        }
+        break;
+    case 6:
+        for (i = 0; i < LOONGARCH_TLB_MAX; i++) {
+            loongarch_tlb *tlb = &env->tlb[i];
+            uint64_t vpn, tlb_vppn;
+            uint8_t tlb_ps;
+
+            tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
+            tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
+            tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
+            tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
+            vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
+
+            if ((tlb_g || tlb_asid == csr_asid) &&
+                (vpn == (tlb_vppn >> (tlb_ps + 1 - 13)))) {
+                tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
+            }
+        }
+        break;
+    default:
+        do_raise_exception(env, EXCP_INE, GETPC());
+    }
+
+    cpu_loongarch_tlb_flush(env);
+}
+
 void loongarch_mmu_init(CPULoongArchState *env)
 {
     /* Number of MTLB */
-- 
2.27.0



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

* [RFC PATCH v2 10/30] target/loongarch: Add other core instructions support
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (8 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 09/30] target/loongarch: Add TLB instruction support Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-14 10:19   ` Richard Henderson
  2021-11-11  1:35 ` [RFC PATCH v2 11/30] target/loongarch: Add LoongArch interrupt and exception handle Xiaojuan Yang
                   ` (20 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This includes:
-CACOP
-LDDIR
-LDPTE
-ERTN
-DBCL
-IDLE

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/cpu.h                       |  2 +
 target/loongarch/helper.h                    |  4 +
 target/loongarch/insn_trans/trans_core.c.inc | 62 +++++++++++++++
 target/loongarch/insns.decode                | 15 ++++
 target/loongarch/internals.h                 |  5 ++
 target/loongarch/op_helper.c                 | 44 ++++++++++
 target/loongarch/tlb_helper.c                | 84 ++++++++++++++++++++
 7 files changed, 216 insertions(+)

diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 4881f18cf1..6f7c13d366 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -449,6 +449,8 @@ enum {
     EXCP_LAST = EXCP_DINT,
 };
 
+#define CPU_INTERRUPT_WAKE CPU_INTERRUPT_TGT_INT_0
+
 #define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU
 #define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX
 #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h
index dea0087273..afb362c9c7 100644
--- a/target/loongarch/helper.h
+++ b/target/loongarch/helper.h
@@ -110,4 +110,8 @@ DEF_HELPER_1(tlbrd, void, env)
 DEF_HELPER_1(tlbclr, void, env)
 DEF_HELPER_1(tlbflush, void, env)
 DEF_HELPER_4(invtlb, void, env, tl, tl, tl)
+DEF_HELPER_4(lddir, tl, env, tl, tl, i32)
+DEF_HELPER_4(ldpte, void, env, tl, tl, i32)
+DEF_HELPER_1(ertn, void, env)
+DEF_HELPER_1(idle, void, env)
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target/loongarch/insn_trans/trans_core.c.inc b/target/loongarch/insn_trans/trans_core.c.inc
index c34163efec..37cae8c579 100644
--- a/target/loongarch/insn_trans/trans_core.c.inc
+++ b/target/loongarch/insn_trans/trans_core.c.inc
@@ -35,6 +35,12 @@ GEN_FALSE_TRANS(tlbfill)
 GEN_FALSE_TRANS(tlbclr)
 GEN_FALSE_TRANS(tlbflush)
 GEN_FALSE_TRANS(invtlb)
+GEN_FALSE_TRANS(cacop)
+GEN_FALSE_TRANS(ldpte)
+GEN_FALSE_TRANS(lddir)
+GEN_FALSE_TRANS(ertn)
+GEN_FALSE_TRANS(dbcl)
+GEN_FALSE_TRANS(idle)
 
 #else
 static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
@@ -442,6 +448,13 @@ static bool trans_iocsrwr_d(DisasContext *ctx, arg_iocsrwr_d *a)
     return true;
 }
 
+static bool trans_cacop(DisasContext *ctx, arg_cacop *a)
+{
+    /* Treat the cacop as a nop */
+    gen_helper_check_plv(cpu_env);
+    return true;
+}
+
 static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a)
 {
     gen_helper_check_plv(cpu_env);
@@ -505,4 +518,53 @@ static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
     return true;
 }
 
+static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a)
+{
+    TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
+    TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
+
+    gen_helper_check_plv(cpu_env);
+    gen_helper_ldpte(cpu_env, src1, tcg_constant_tl(a->seq), mem_idx);
+    return true;
+}
+
+static bool trans_lddir(DisasContext *ctx, arg_lddir *a)
+{
+    TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
+    TCGv src = gpr_src(ctx, a->rj, EXT_NONE);
+    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
+
+    gen_helper_check_plv(cpu_env);
+    gen_helper_lddir(dest, cpu_env, src, tcg_constant_tl(a->level), mem_idx);
+    return true;
+}
+
+static bool trans_ertn(DisasContext *ctx, arg_ertn *a)
+{
+    gen_helper_check_plv(cpu_env);
+    gen_helper_ertn(cpu_env);
+    ctx->base.is_jmp = DISAS_EXIT;
+    return true;
+}
+
+static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
+{
+    /*
+     * XXX: not clear which exception should be raised
+     *      when in debug mode...
+     */
+    gen_helper_check_plv(cpu_env);
+    generate_exception(ctx, EXCP_DBP);
+    return true;
+}
+
+static bool trans_idle(DisasContext *ctx, arg_idle *a)
+{
+    gen_helper_check_plv(cpu_env);
+
+    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
+    gen_helper_idle(cpu_env);
+    ctx->base.is_jmp = DISAS_NORETURN;
+    return true;
+}
 #endif
diff --git a/target/loongarch/insns.decode b/target/loongarch/insns.decode
index f5031e089e..bb6f0b912b 100644
--- a/target/loongarch/insns.decode
+++ b/target/loongarch/insns.decode
@@ -41,9 +41,12 @@
 %offs16  10:s16
 %offs    0:s10 10:16
 %csr     10:14
+%cop     0:5
 %addr    10:5
 %info    5:5
 %invop   0:5
+%level   10:8
+%seq     10:8
 
 #
 # Argument sets
@@ -87,8 +90,11 @@
 &fmt_rjrdoffs16     rj rd offs16
 &fmt_rdcsr          rd csr
 &fmt_rdrjcsr        rd rj csr
+&fmt_coprjsi12      cop rj si12
 &fmt_empty
 &fmt_invtlb         invop rj rk
+&fmt_rdrjlevel      rd rj level
+&fmt_rjseq          rj seq
 
 #
 # Formats
@@ -132,8 +138,11 @@
 @fmt_rjrdoffs16      .... .. ................ ..... .....     &fmt_rjrdoffs16     %rj %rd %offs16
 @fmt_rdcsr           .... .... .............. ..... .....     &fmt_rdcsr          %rd %csr
 @fmt_rdrjcsr         .... .... .............. ..... .....     &fmt_rdrjcsr        %rd %rj %csr
+@fmt_coprjsi12       .... ...... ............ ..... .....     &fmt_coprjsi12      %cop %rj %si12
 @fmt_empty           .... ........ ..... ..... ..... .....    &fmt_empty
 @fmt_invtlb          ...... ...... ..... ..... ..... .....    &fmt_invtlb         %invop %rj %rk
+@fmt_rdrjlevel       .... ........ .. ........ ..... .....    &fmt_rdrjlevel      %rd %rj %level
+@fmt_rjseq           .... ........ .. ........ ..... .....    &fmt_rjseq          %rj %seq
 
 #
 # Fixed point arithmetic operation instruction
@@ -506,6 +515,7 @@ iocsrwr_b        0000 01100100 10000 00100 ..... .....    @fmt_rdrj
 iocsrwr_h        0000 01100100 10000 00101 ..... .....    @fmt_rdrj
 iocsrwr_w        0000 01100100 10000 00110 ..... .....    @fmt_rdrj
 iocsrwr_d        0000 01100100 10000 00111 ..... .....    @fmt_rdrj
+cacop            0000 011000 ............ ..... .....     @fmt_coprjsi12
 tlbsrch          0000 01100100 10000 01010 00000 00000    @fmt_empty
 tlbrd            0000 01100100 10000 01011 00000 00000    @fmt_empty
 tlbwr            0000 01100100 10000 01100 00000 00000    @fmt_empty
@@ -513,3 +523,8 @@ tlbfill          0000 01100100 10000 01101 00000 00000    @fmt_empty
 tlbclr           0000 01100100 10000 01000 00000 00000    @fmt_empty
 tlbflush         0000 01100100 10000 01001 00000 00000    @fmt_empty
 invtlb           0000 01100100 10011 ..... ..... .....    @fmt_invtlb
+lddir            0000 01100100 00 ........ ..... .....    @fmt_rdrjlevel
+ldpte            0000 01100100 01 ........ ..... 00000    @fmt_rjseq
+ertn             0000 01100100 10000 01110 00000 00000    @fmt_empty
+idle             0000 01100100 10001 ...............      @fmt_whint
+dbcl             0000 00000010 10101 ...............      @fmt_code
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
index 3f72492b91..3177098337 100644
--- a/target/loongarch/internals.h
+++ b/target/loongarch/internals.h
@@ -13,6 +13,11 @@
 #define FCMP_UN   0x0100  /* unordered */
 #define FCMP_GT   0x1000  /* fp0 > fp1 */
 
+/* Global bit used for lddir/ldpte */
+#define LOONGARCH_PAGE_HUGE_SHIFT   6
+/* Global bit for huge page */
+#define LOONGARCH_HGLOBAL_SHIFT     12
+
 void loongarch_translate_init(void);
 
 void loongarch_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
diff --git a/target/loongarch/op_helper.c b/target/loongarch/op_helper.c
index 20014ef07a..e2a9fd9ad0 100644
--- a/target/loongarch/op_helper.c
+++ b/target/loongarch/op_helper.c
@@ -91,4 +91,48 @@ void helper_check_plv(CPULoongArchState *env)
         do_raise_exception(env, EXCP_IPE, GETPC());
     }
 }
+
+void helper_ertn(CPULoongArchState *env)
+{
+    uint64_t csr_pplv, csr_pie;
+    if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
+        csr_pplv = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV);
+        csr_pie = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE);
+
+        /* Clear Refill flag and set pc */
+        env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0);
+        env->pc = env->CSR_TLBRERA;
+        if (qemu_loglevel_mask(CPU_LOG_INT)) {
+            qemu_log("%s: TLBRERA 0x%lx\n", __func__, env->CSR_TLBRERA);
+        }
+    } else {
+        csr_pplv = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PPLV);
+        csr_pie = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PIE);
+
+        /* set pc*/
+        env->pc = env->CSR_ERA;
+        if (qemu_loglevel_mask(CPU_LOG_INT)) {
+            qemu_log("%s: ERA 0x%lx\n", __func__, env->CSR_ERA);
+        }
+    }
+    env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, csr_pplv);
+    env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, csr_pie);
+
+    env->lladdr = 1;
+}
+
+void helper_idle(CPULoongArchState *env)
+{
+    CPUState *cs = env_cpu(env);
+
+    cs->halted = 1;
+    cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE);
+    /*
+     * Last instruction in the block, PC was updated before
+     * - no need to recover PC and icount
+     */
+    do_raise_exception(env, EXCP_HLT, 0);
+}
+
+
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tlb_helper.c
index f36e379499..7253b10889 100644
--- a/target/loongarch/tlb_helper.c
+++ b/target/loongarch/tlb_helper.c
@@ -815,3 +815,87 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
         do_raise_exception(env, cs->exception_index, retaddr);
     }
 }
+
+target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
+                          target_ulong level, uint32_t mem_idx)
+{
+    CPUState *cs = env_cpu(env);
+    target_ulong badvaddr, index, phys, ret;
+    int shift;
+    uint64_t dir1_base, dir1_width;
+    uint64_t dir3_base, dir3_width;
+    bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1;
+
+    badvaddr = env->CSR_TLBRBADV;
+
+    /* 0:8B, 1:16B, 2:32B, 3:64B */
+    shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
+    shift = (shift + 1) * 3;
+
+    if (huge) {
+        return base;
+    }
+    switch (level) {
+    case 1:
+        dir1_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE);
+        dir1_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH);
+        index = (badvaddr >> dir1_base) & ((1 << dir1_width) - 1);
+        break;
+    case 3:
+        dir3_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE);
+        dir3_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH);
+        index = (badvaddr >> dir3_base) & ((1 << dir3_width) - 1);
+        break;
+    default:
+        do_raise_exception(env, EXCP_INE, GETPC());
+        return 0;
+    }
+
+    phys = base | index << shift;
+    ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
+    return ret;
+}
+
+void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
+                  uint32_t mem_idx)
+{
+    CPUState *cs = env_cpu(env);
+    target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv;
+    int shift;
+    bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1;
+    uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
+    uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
+
+    if (huge) {
+        /* Huge Page. base is paddr */
+        tmp0 = base ^ LOONGARCH_PAGE_HUGE_SHIFT;
+        /* Move Global bit */
+        tmp0 = (tmp0 >> LOONGARCH_HGLOBAL_SHIFT) << R_CSR_TLBELO0_G_SHIFT |
+               (tmp0 & (~(1 << R_CSR_TLBELO0_G_SHIFT)));
+        ps = ptbase + ptwidth - 1;
+        if (odd) {
+            tmp0 += (1 << ps);
+        }
+    } else {
+        /* 0:8B, 1:16B, 2:32B, 3:64B */
+        shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
+        shift = (shift + 1) * 3;
+        badv = env->CSR_TLBRBADV;
+
+        ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1);
+        ptindex = ptindex & ~0x1;   /* clear bit 0 */
+        ptoffset0 = ptindex << shift;
+        ptoffset1 = (ptindex + 1) << shift;
+
+        phys = base | (odd ? ptoffset1 : ptoffset0);
+        tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
+        ps = ptbase;
+    }
+
+    if (odd) {
+        env->CSR_TLBRELO1 = tmp0;
+    } else {
+        env->CSR_TLBRELO0 = tmp0;
+    }
+    env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps);
+}
-- 
2.27.0



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

* [RFC PATCH v2 11/30] target/loongarch: Add LoongArch interrupt and exception handle
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (9 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 10/30] target/loongarch: Add other core instructions support Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 12/30] target/loongarch: Add timer related instructions support Xiaojuan Yang
                   ` (19 subsequent siblings)
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This patch Add loongarch interrupt and exception handle.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/cpu.c | 279 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 279 insertions(+)

diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 7db6e21298..fa528d5510 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -80,6 +80,250 @@ static void loongarch_cpu_set_pc(CPUState *cs, vaddr value)
     env->pc = value;
 }
 
+#if !defined(CONFIG_USER_ONLY)
+static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env)
+{
+    bool ret = 0;
+
+    ret = (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE) &&
+          !(FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)));
+
+    return ret;
+}
+
+/* Check if there is pending and not masked out interrupt */
+static inline bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env)
+{
+    uint32_t pending;
+    uint32_t status;
+    bool r;
+
+    pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS);
+    status  = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE);
+
+    r = (pending & status) != 0;
+    return r;
+}
+
+static inline unsigned int get_vint_size(CPULoongArchState *env)
+{
+    uint64_t vs = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, VS);
+    uint64_t size = 0;
+
+    if (vs == 0) {
+        return 0;
+    }
+
+    if (vs < 8) {
+        size = 1 << (vs + 2);
+    }
+
+    if (vs > 8) {
+        qemu_log("%s: unexpected value", __func__);
+        assert(0);
+    }
+
+    return size;
+}
+
+static void loongarch_cpu_do_interrupt(CPUState *cs)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+    CPULoongArchState *env = &cpu->env;
+    bool update_badinstr = 0;
+    int cause = -1;
+    const char *name;
+    bool tlbfill = FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR);
+
+    if (qemu_loglevel_mask(CPU_LOG_INT)
+        && cs->exception_index != EXCP_EXT_INTERRUPT) {
+        if (cs->exception_index < 0 || cs->exception_index > EXCP_LAST) {
+            name = "unknown";
+        } else {
+            name = excp_names[cs->exception_index];
+        }
+
+        qemu_log("%s enter: pc " TARGET_FMT_lx " ERA " TARGET_FMT_lx
+                 " TLBRERA 0x%016lx" " %s exception\n", __func__,
+                 env->pc, env->CSR_ERA, env->CSR_TLBRERA, name);
+    }
+
+    switch (cs->exception_index) {
+    case EXCP_SYSCALL:
+        cause = EXCCODE_SYS;
+        update_badinstr = 1;
+        break;
+    case EXCP_BREAK:
+        cause = EXCCODE_BRK;
+        update_badinstr = 1;
+        break;
+    case EXCP_INE:
+        cause = EXCCODE_INE;
+        update_badinstr = 1;
+        break;
+    case EXCP_IPE:
+        cause = EXCCODE_IPE;
+        update_badinstr = 1;
+        break;
+    case EXCP_FPE:
+        cause = EXCCODE_FPE;
+        update_badinstr = 1;
+        break;
+    case EXCP_ADE:
+        cause = EXCCODE_ADE;
+        update_badinstr = 1;
+        break;
+    case EXCP_DBP:
+        env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DCL, 1);
+        env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, ECODE, 0xC);
+        env->CSR_DERA = env->pc;
+        env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DST, 1);
+        env->pc = env->CSR_EENTRY + 0x480;
+        break;
+    case EXCP_EXT_INTERRUPT:
+        cause = 0;
+        break;
+    case EXCP_TLBL:
+        cause = EXCCODE_PIL;
+        update_badinstr = 1;
+        break;
+    case EXCP_TLBS:
+        cause = EXCCODE_PIS;
+        update_badinstr = 1;
+        break;
+    case EXCP_INST_NOTAVAIL:
+        cause = EXCCODE_PIF;
+        break;
+    case EXCP_TLBM:
+        cause = EXCCODE_PME;
+        break;
+    case EXCP_TLBPE:
+        cause = EXCCODE_PPI;
+        break;
+    case EXCP_TLBNX:
+        cause = EXCCODE_PNX;
+        break;
+    case EXCP_TLBNR:
+        cause = EXCCODE_PNR;
+        update_badinstr = 1;
+        break;
+    case EXCP_IBE:
+        cause = EXCCODE_ADE;
+        break;
+    case EXCP_DBE:
+        cause = EXCCODE_ADE;
+        break;
+    default:
+        qemu_log("Error: exception(%d) '%s' has not been supported\n",
+                 cs->exception_index, excp_names[cs->exception_index]);
+        abort();
+    }
+
+    if (tlbfill) {
+        env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA,
+                                      PC, (env->pc >> 2));
+    } else {
+        env->CSR_ERA = env->pc;
+    }
+
+    if (update_badinstr) {
+        env->CSR_BADI = cpu_ldl_code(env, env->pc);
+    }
+
+    /* Save PLV and IE */
+    if (tlbfill) {
+        env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV,
+                                       FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV));
+        env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE,
+                                       FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE));
+    } else {
+        env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PPLV,
+                                   FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV));
+        env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PIE,
+                                   FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE));
+    }
+
+    env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0);
+    env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0);
+
+    uint32_t vec_size = get_vint_size(env);
+    env->pc = env->CSR_EENTRY;
+    env->pc += cause * vec_size;
+    if (tlbfill) {
+        /* TLB Refill */
+        env->pc = env->CSR_TLBRENTRY;
+    }
+    if  (cs->exception_index == EXCP_EXT_INTERRUPT) {
+        /* Interrupt */
+        uint32_t vector = 0;
+        uint32_t pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS);
+        pending &= FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE);
+
+        /* Find the highest-priority interrupt. */
+        while (pending >>= 1) {
+            vector++;
+        }
+        env->pc = env->CSR_EENTRY + (EXCODE_IP + vector) * vec_size;
+        if (qemu_loglevel_mask(CPU_LOG_INT)) {
+            qemu_log("%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx
+                     " cause %d\n" "    A " TARGET_FMT_lx " D "
+                     TARGET_FMT_lx " vector = %d ExC %08lx ExS %08lx\n",
+                     __func__, env->pc, env->CSR_ERA,
+                     cause, env->CSR_BADV, env->CSR_DERA, vector,
+                     env->CSR_ECFG, env->CSR_ESTAT);
+        }
+    }
+
+    /* Excode */
+    env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ECODE, cause);
+
+    if (qemu_loglevel_mask(CPU_LOG_INT) && cs->exception_index != EXCP_EXT_INTERRUPT) {
+        qemu_log("%s: PC " TARGET_FMT_lx " ERA 0x%08lx"  " cause %d%s\n"
+                 " ESTAT %08lx EXCFG 0x%08lx BADVA 0x%08lx BADI 0x%08lx \
+                 SYS_NUM %lu cpu %d asid 0x%lx" "\n",
+                 __func__, env->pc, tlbfill ? env->CSR_TLBRERA : env->CSR_ERA,
+                 cause, tlbfill ? "(refill)" : "", env->CSR_ESTAT, env->CSR_ECFG,
+                 tlbfill ? env->CSR_TLBRBADV : env->CSR_BADV, env->CSR_BADI,
+                 env->gpr[11], cs->cpu_index, env->CSR_ASID
+         );
+    }
+    cs->exception_index = EXCP_NONE;
+}
+
+static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
+                                    vaddr addr, unsigned size,
+                                    MMUAccessType access_type,
+                                    int mmu_idx, MemTxAttrs attrs,
+                                    MemTxResult response, uintptr_t retaddr)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+    CPULoongArchState *env = &cpu->env;
+
+    if (access_type == MMU_INST_FETCH) {
+        do_raise_exception(env, EXCP_IBE, retaddr);
+    } else {
+        do_raise_exception(env, EXCP_DBE, retaddr);
+    }
+}
+
+static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+    if (interrupt_request & CPU_INTERRUPT_HARD) {
+        LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+        CPULoongArchState *env = &cpu->env;
+
+        if (cpu_loongarch_hw_interrupts_enabled(env) &&
+            cpu_loongarch_hw_interrupts_pending(env)) {
+            /* Raise it */
+            cs->exception_index = EXCP_EXT_INTERRUPT;
+            loongarch_cpu_do_interrupt(cs);
+            return true;
+        }
+    }
+    return false;
+}
+#endif
+
 #ifdef CONFIG_TCG
 static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
                                               const TranslationBlock *tb)
@@ -93,7 +337,20 @@ static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
 
 static bool loongarch_cpu_has_work(CPUState *cs)
 {
+#ifdef CONFIG_USER_ONLY
     return true;
+#else
+    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+    CPULoongArchState *env = &cpu->env;
+    bool has_work = false;
+
+    if ((cs->interrupt_request & CPU_INTERRUPT_HARD) &&
+        cpu_loongarch_hw_interrupts_pending(env)) {
+        has_work = true;
+    }
+
+    return has_work;
+#endif
 }
 
 static void loongarch_3a5000_initfn(Object *obj)
@@ -211,6 +468,9 @@ static void loongarch_cpu_reset(DeviceState *dev)
     data = FIELD_DP64(data, CSR_CRMD, DATM, 1);
     env->CSR_CRMD = data;
 
+#ifndef CONFIG_USER_ONLY
+    env->pc = env->CSR_EENTRY;
+#endif
     restore_fp_status(env);
     cs->exception_index = EXCP_NONE;
 }
@@ -238,6 +498,7 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp)
 
 #ifndef CONFIG_USER_ONLY
     loongarch_mmu_init(env);
+    env->CSR_EENTRY = 0x1C000000;
 #endif
 
     cpu_reset(cs);
@@ -285,6 +546,21 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags)
         }
     }
 
+#ifndef CONFIG_USER_ONLY
+    qemu_fprintf(f, "EUEN            0x%lx\n", env->CSR_EUEN);
+    qemu_fprintf(f, "ESTAT           0x%lx\n", env->CSR_ESTAT);
+    qemu_fprintf(f, "ERA             0x%lx\n", env->CSR_ERA);
+    qemu_fprintf(f, "CRMD            0x%lx\n", env->CSR_CRMD);
+    qemu_fprintf(f, "PRMD            0x%lx\n", env->CSR_PRMD);
+    qemu_fprintf(f, "BadVAddr        0x%lx\n", env->CSR_BADV);
+    qemu_fprintf(f, "TLB refill ERA  0x%lx\n", env->CSR_TLBRERA);
+    qemu_fprintf(f, "TLB refill BadV 0x%lx\n", env->CSR_TLBRBADV);
+    qemu_fprintf(f, "EENTRY            0x%lx\n", env->CSR_EENTRY);
+    qemu_fprintf(f, "BadInstr        0x%lx\n", env->CSR_BADI);
+    qemu_fprintf(f, "PRCFG1    0x%lx\nPRCFG2     0x%lx\nPRCFG3     0x%lx\n",
+                 env->CSR_PRCFG1, env->CSR_PRCFG3, env->CSR_PRCFG3);
+#endif
+
 #ifndef CONFIG_USER_ONLY
     qemu_fprintf(f, "EUEN            0x%lx\n", env->CSR_EUEN);
     qemu_fprintf(f, "ESTAT           0x%lx\n", env->CSR_ESTAT);
@@ -320,6 +596,9 @@ static struct TCGCPUOps loongarch_tcg_ops = {
 
 #if !defined(CONFIG_USER_ONLY)
     .tlb_fill = loongarch_cpu_tlb_fill,
+    .cpu_exec_interrupt = loongarch_cpu_exec_interrupt,
+    .do_interrupt = loongarch_cpu_do_interrupt,
+    .do_transaction_failed = loongarch_cpu_do_transaction_failed,
 #endif /* !CONFIG_USER_ONLY */
 };
 #endif /* CONFIG_TCG */
-- 
2.27.0



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

* [RFC PATCH v2 12/30] target/loongarch: Add timer related instructions support.
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (10 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 11/30] target/loongarch: Add LoongArch interrupt and exception handle Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 13/30] target/loongarch: Add gdb support Xiaojuan Yang
                   ` (18 subsequent siblings)
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This includes:
-RDTIME{L/H}.W
-RDTIME.D

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/helper.h                     |  1 +
 target/loongarch/insn_trans/trans_extra.c.inc | 32 +++++++++++++++++++
 target/loongarch/op_helper.c                  |  4 +++
 target/loongarch/translate.c                  |  2 ++
 4 files changed, 39 insertions(+)

diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h
index afb362c9c7..fc4eaa1ce8 100644
--- a/target/loongarch/helper.h
+++ b/target/loongarch/helper.h
@@ -114,4 +114,5 @@ DEF_HELPER_4(lddir, tl, env, tl, tl, i32)
 DEF_HELPER_4(ldpte, void, env, tl, tl, i32)
 DEF_HELPER_1(ertn, void, env)
 DEF_HELPER_1(idle, void, env)
+DEF_HELPER_1(rdtime_d, i64, env)
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target/loongarch/insn_trans/trans_extra.c.inc b/target/loongarch/insn_trans/trans_extra.c.inc
index 76f0698da7..ab46331547 100644
--- a/target/loongarch/insn_trans/trans_extra.c.inc
+++ b/target/loongarch/insn_trans/trans_extra.c.inc
@@ -33,22 +33,54 @@ static bool trans_asrtgt_d(DisasContext *ctx, arg_asrtgt_d * a)
     return true;
 }
 
+#ifndef CONFIG_USER_ONLY
+static bool gen_rdtime(DisasContext *ctx, arg_rdtimel_w *a,
+                       bool word, bool high)
+{
+    TCGv dst1 = gpr_dst(ctx, a->rd, EXT_NONE);
+    TCGv dst2 = gpr_dst(ctx, a->rj, EXT_NONE);
+
+    if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
+        gen_io_start();
+    }
+    gen_helper_rdtime_d(dst1, cpu_env);
+    if (word) {
+        tcg_gen_sextract_tl(dst1, dst1, high ? 32 : 0, 32);
+    }
+    tcg_gen_ld_i64(dst2, cpu_env, offsetof(CPULoongArchState, CSR_TMID));
+
+    return true;
+}
+#endif
+
 static bool trans_rdtimel_w(DisasContext *ctx, arg_rdtimel_w *a)
 {
+#ifdef CONFIG_USER_ONLY
     tcg_gen_movi_tl(cpu_gpr[a->rd], 0);
     return true;
+#else
+    return gen_rdtime(ctx, a, 1, 0);
+#endif
 }
 
 static bool trans_rdtimeh_w(DisasContext *ctx, arg_rdtimeh_w *a)
 {
+#ifdef CONFIG_USER_ONLY
     tcg_gen_movi_tl(cpu_gpr[a->rd], 0);
     return true;
+#else
+    return gen_rdtime(ctx, a, 1, 1);
+#endif
 }
 
 static bool trans_rdtime_d(DisasContext *ctx, arg_rdtime_d *a)
 {
+#ifdef CONFIG_USER_ONLY
     tcg_gen_movi_tl(cpu_gpr[a->rd], 0);
     return true;
+#else
+    return gen_rdtime(ctx, a, 0, 0);
+#endif
 }
 
 static bool trans_cpucfg(DisasContext *ctx, arg_cpucfg *a)
diff --git a/target/loongarch/op_helper.c b/target/loongarch/op_helper.c
index e2a9fd9ad0..fb47914c87 100644
--- a/target/loongarch/op_helper.c
+++ b/target/loongarch/op_helper.c
@@ -134,5 +134,9 @@ void helper_idle(CPULoongArchState *env)
     do_raise_exception(env, EXCP_HLT, 0);
 }
 
+uint64_t helper_rdtime_d(CPULoongArchState *env)
+{
+     return cpu_loongarch_get_stable_counter(env);
+}
 
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target/loongarch/translate.c b/target/loongarch/translate.c
index 3935b14163..15276a240f 100644
--- a/target/loongarch/translate.c
+++ b/target/loongarch/translate.c
@@ -25,6 +25,8 @@ static TCGv cpu_lladdr, cpu_llval;
 TCGv_i32 cpu_fcsr0;
 TCGv_i64 cpu_fpr[32];
 
+#include "exec/gen-icount.h"
+
 #define DISAS_STOP       DISAS_TARGET_0
 #define DISAS_EXIT       DISAS_TARGET_1
 
-- 
2.27.0



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

* [RFC PATCH v2 13/30] target/loongarch: Add gdb support.
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (11 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 12/30] target/loongarch: Add timer related instructions support Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 14/30] target/loongarch: Implement privilege instructions disassembly Xiaojuan Yang
                   ` (17 subsequent siblings)
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 configs/targets/loongarch64-softmmu.mak |  1 +
 gdb-xml/loongarch-base64.xml            | 43 +++++++++++
 gdb-xml/loongarch-fpu64.xml             | 57 +++++++++++++++
 target/loongarch/cpu.c                  |  9 +++
 target/loongarch/gdbstub.c              | 97 +++++++++++++++++++++++++
 target/loongarch/internals.h            | 10 +++
 target/loongarch/meson.build            |  1 +
 7 files changed, 218 insertions(+)
 create mode 100644 configs/targets/loongarch64-softmmu.mak
 create mode 100644 gdb-xml/loongarch-base64.xml
 create mode 100644 gdb-xml/loongarch-fpu64.xml
 create mode 100644 target/loongarch/gdbstub.c

diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak
new file mode 100644
index 0000000000..f33fa1590b
--- /dev/null
+++ b/configs/targets/loongarch64-softmmu.mak
@@ -0,0 +1 @@
+TARGET_XML_FILES= gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu64.xml
diff --git a/gdb-xml/loongarch-base64.xml b/gdb-xml/loongarch-base64.xml
new file mode 100644
index 0000000000..f2af2a4b6e
--- /dev/null
+++ b/gdb-xml/loongarch-base64.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.loongarch.base">
+  <reg name="r0" bitsize="64" type="uint64" group="general"/>
+  <reg name="r1" bitsize="64" type="uint64" group="general"/>
+  <reg name="r2" bitsize="64" type="uint64" group="general"/>
+  <reg name="r3" bitsize="64" type="uint64" group="general"/>
+  <reg name="r4" bitsize="64" type="uint64" group="general"/>
+  <reg name="r5" bitsize="64" type="uint64" group="general"/>
+  <reg name="r6" bitsize="64" type="uint64" group="general"/>
+  <reg name="r7" bitsize="64" type="uint64" group="general"/>
+  <reg name="r8" bitsize="64" type="uint64" group="general"/>
+  <reg name="r9" bitsize="64" type="uint64" group="general"/>
+  <reg name="r10" bitsize="64" type="uint64" group="general"/>
+  <reg name="r11" bitsize="64" type="uint64" group="general"/>
+  <reg name="r12" bitsize="64" type="uint64" group="general"/>
+  <reg name="r13" bitsize="64" type="uint64" group="general"/>
+  <reg name="r14" bitsize="64" type="uint64" group="general"/>
+  <reg name="r15" bitsize="64" type="uint64" group="general"/>
+  <reg name="r16" bitsize="64" type="uint64" group="general"/>
+  <reg name="r17" bitsize="64" type="uint64" group="general"/>
+  <reg name="r18" bitsize="64" type="uint64" group="general"/>
+  <reg name="r19" bitsize="64" type="uint64" group="general"/>
+  <reg name="r20" bitsize="64" type="uint64" group="general"/>
+  <reg name="r21" bitsize="64" type="uint64" group="general"/>
+  <reg name="r22" bitsize="64" type="uint64" group="general"/>
+  <reg name="r23" bitsize="64" type="uint64" group="general"/>
+  <reg name="r24" bitsize="64" type="uint64" group="general"/>
+  <reg name="r25" bitsize="64" type="uint64" group="general"/>
+  <reg name="r26" bitsize="64" type="uint64" group="general"/>
+  <reg name="r27" bitsize="64" type="uint64" group="general"/>
+  <reg name="r28" bitsize="64" type="uint64" group="general"/>
+  <reg name="r29" bitsize="64" type="uint64" group="general"/>
+  <reg name="r30" bitsize="64" type="uint64" group="general"/>
+  <reg name="r31" bitsize="64" type="uint64" group="general"/>
+  <reg name="pc" bitsize="64" type="code_ptr" group="general"/>
+</feature>
diff --git a/gdb-xml/loongarch-fpu64.xml b/gdb-xml/loongarch-fpu64.xml
new file mode 100644
index 0000000000..e52cf89fbc
--- /dev/null
+++ b/gdb-xml/loongarch-fpu64.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.loongarch.fpu">
+
+  <union id="fpu64type">
+    <field name="f" type="ieee_single"/>
+    <field name="d" type="ieee_double"/>
+  </union>
+
+  <reg name="f0" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f1" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f2" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f3" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f4" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f5" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f6" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f7" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f8" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f9" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f10" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f11" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f12" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f13" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f14" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f15" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f16" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f17" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f18" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f19" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f20" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f21" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f22" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f23" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f24" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f25" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f26" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f27" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f28" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f29" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f30" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f31" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="fcc0" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc1" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc2" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc3" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc4" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc5" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc6" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc7" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcsr" bitsize="32" type="uint32" group="float"/>
+</feature>
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index fa528d5510..c789acaf2f 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -147,6 +147,10 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
                  " TLBRERA 0x%016lx" " %s exception\n", __func__,
                  env->pc, env->CSR_ERA, env->CSR_TLBRERA, name);
     }
+    if (cs->exception_index == EXCP_EXT_INTERRUPT &&
+        (FIELD_EX64(env->CSR_DBG, CSR_DBG, DST))) {
+        cs->exception_index = EXCP_DINT;
+    }
 
     switch (cs->exception_index) {
     case EXCP_SYSCALL:
@@ -173,9 +177,14 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
         cause = EXCCODE_ADE;
         update_badinstr = 1;
         break;
+    case EXCP_DINT:
+        env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DEI, 1);
+        goto set_DERA;
     case EXCP_DBP:
         env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DCL, 1);
         env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, ECODE, 0xC);
+        goto set_DERA;
+    set_DERA:
         env->CSR_DERA = env->pc;
         env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DST, 1);
         env->pc = env->CSR_EENTRY + 0x480;
diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c
new file mode 100644
index 0000000000..2fec9364de
--- /dev/null
+++ b/target/loongarch/gdbstub.c
@@ -0,0 +1,97 @@
+/*
+ * LOONGARCH gdb server stub
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "internals.h"
+#include "exec/gdbstub.h"
+#include "exec/helper-proto.h"
+
+int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+    CPULoongArchState *env = &cpu->env;
+
+    if (0 <= n && n < 32) {
+        return gdb_get_regl(mem_buf, env->gpr[n]);
+    } else if (n == 32) {
+        return gdb_get_regl(mem_buf, env->pc);
+    }
+    return 0;
+}
+
+int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+    CPULoongArchState *env = &cpu->env;
+    target_ulong tmp = ldtul_p(mem_buf);
+
+    if (0 <= n && n < 32) {
+        return env->gpr[n] = tmp, sizeof(target_ulong);
+    } else if (n == 32) {
+        return env->pc = tmp, sizeof(target_ulong);
+    }
+    return 0;
+}
+
+static int loongarch_gdb_get_fpu(CPULoongArchState *env,
+                                 GByteArray *mem_buf, int n)
+{
+    if (0 <= n && n < 32) {
+        return gdb_get_reg64(mem_buf, env->fpr[n]);
+    } else if (32 <= n && n < 40) {
+        return gdb_get_reg8(mem_buf, env->cf[n - 32]);
+    } else if (n == 40) {
+        return gdb_get_reg32(mem_buf, env->fcsr0);
+    }
+    return 0;
+}
+
+static int loongarch_gdb_set_fpu(CPULoongArchState *env,
+                                 uint8_t *mem_buf, int n)
+{
+    if (0 <= n && n < 32) {
+        return env->fpr[n] = ldq_p(mem_buf), 8;
+    } else if (32 <= n && n < 40) {
+        return env->cf[n - 32] = ldub_p(mem_buf), 1;
+    } else if (n == 40) {
+        return env->fcsr0 = ldl_p(mem_buf), 4;
+    }
+    return 0;
+}
+
+void loongarch_cpu_register_gdb_regs_for_features(CPUState *cs)
+{
+    gdb_register_coprocessor(cs, loongarch_gdb_get_fpu, loongarch_gdb_set_fpu,
+                             41, "loongarch-fpu64.xml", 0);
+}
+
+int loongarch_read_qxfer(CPUState *cs, const char *annex, uint8_t *read_buf,
+                         unsigned long offset, unsigned long len)
+{
+    if (strncmp(annex, "cpucfg", sizeof("cpucfg") - 1) == 0) {
+        if (offset % 4 != 0 || len % 4 != 0) {
+            return 0;
+        }
+
+        size_t i;
+        for (i = offset; i < offset + len; i += 4)
+            ((uint32_t *)read_buf)[(i - offset) / 4] =
+                helper_cpucfg(&(LOONGARCH_CPU(cs)->env), i / 4);
+        return 32 * 4;
+    }
+    return 0;
+}
+
+int loongarch_write_qxfer(CPUState *cs, const char *annex,
+                          const uint8_t *write_buf, unsigned long offset,
+                          unsigned long len)
+{
+    return 0;
+}
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
index 3177098337..a54e676d82 100644
--- a/target/loongarch/internals.h
+++ b/target/loongarch/internals.h
@@ -40,5 +40,15 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
 void loongarch_mmu_init(CPULoongArchState *env);
 hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
 #endif
+int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n);
+int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n);
+int loongarch_read_qxfer(CPUState *cs, const char *annex,
+                         uint8_t *read_buf,
+                         unsigned long offset, unsigned long len);
+int loongarch_write_qxfer(CPUState *cs, const char *annex,
+                          const uint8_t *write_buf,
+                          unsigned long offset, unsigned long len);
+
+void loongarch_cpu_register_gdb_regs_for_features(CPUState *cs);
 
 #endif
diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build
index 080d6297de..732f87e318 100644
--- a/target/loongarch/meson.build
+++ b/target/loongarch/meson.build
@@ -11,6 +11,7 @@ loongarch_tcg_ss.add(files(
   'fpu_helper.c',
   'op_helper.c',
   'translate.c',
+  'gdbstub.c',
 ))
 loongarch_tcg_ss.add(zlib)
 
-- 
2.27.0



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

* [RFC PATCH v2 14/30] target/loongarch: Implement privilege instructions disassembly
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (12 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 13/30] target/loongarch: Add gdb support Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 15/30] hw/pci-host: Add ls7a1000 PCIe Host bridge support for Loongson Platform Xiaojuan Yang
                   ` (16 subsequent siblings)
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

Signed-off-by: Song Gao <gaosong@loongson.cn>
Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
---
 target/loongarch/disas.c | 86 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)

diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c
index 1501462991..65aa0443bd 100644
--- a/target/loongarch/disas.c
+++ b/target/loongarch/disas.c
@@ -28,18 +28,28 @@ typedef enum {
     la_codec_2r_im16,
     la_codec_2r_im14,
     la_codec_2r_im12,
+    la_codec_2r_im8,
+    la_codec_r_im14,
     la_codec_r_cd,
     la_codec_r_cj,
     la_codec_code,
     la_codec_whint,
+    la_codec_invtlb,
     la_codec_r_ofs21,
     la_codec_cj_ofs21,
     la_codec_ofs26,
     la_codec_cond,
     la_codec_sel,
+    la_codec_empty,
+    la_codec_r_seq,
 
 } la_codec;
 
+#define la_fmt_empty           "nt"
+#define la_fmt_rd_csr          "nt0,x"
+#define la_fmt_rj_seq          "nt1,x"
+#define la_fmt_invtlb          "ntx,1,2"
+#define la_fmt_rd_rj_csr       "nt0,1,x"
 #define la_fmt_rd_rj           "nt0,1"
 #define la_fmt_rj_rk           "nt1,2"
 #define la_fmt_rd_si20         "nt0,i(x)"
@@ -68,6 +78,7 @@ typedef enum {
 #define la_fmt_d_cd_fj_fk      "K.dtH,4,5"
 #define la_fmt_fd_fj_fk_fa     "nt3,4,5,6"
 #define la_fmt_fd_fj_fk_ca     "nt3,4,5,L"
+#define la_fmt_cop_rj_si12     "ntM,1,i(x)"
 
 typedef struct {
     uint32_t pc;
@@ -88,6 +99,8 @@ const char * const fccregnames[8] = {
 };
 
 /* operand extractors */
+#define IM_5 5
+#define IM_8 8
 #define IM_12 12
 #define IM_14 14
 #define IM_15 15
@@ -170,6 +183,12 @@ static int32_t operand_im12(uint32_t insn)
     return imm > (1 << 11) ? imm - (1 << 12) : imm;
 }
 
+static int32_t operand_im8(uint32_t insn)
+{
+    int32_t imm = (int32_t)((insn >> 10) & 0xff);
+    return imm > (1 << 7) ? imm - (1 << 8) : imm;
+}
+
 static uint32_t operand_cd(uint32_t insn)
 {
     return insn & 0x7;
@@ -191,6 +210,12 @@ static int32_t operand_whint(uint32_t insn)
     return imm > (1 << 14) ? imm - (1 << 15) : imm;
 }
 
+static int32_t operand_invop(uint32_t insn)
+{
+    int32_t imm = (int32_t)(insn & 0x1f);
+    return imm > (1 << 4) ? imm - (1 << 5) : imm;
+}
+
 static int32_t operand_ofs21(uint32_t insn)
 {
     int32_t imm = (((int32_t)insn & 0x1f) << 16) |
@@ -220,6 +245,8 @@ static void decode_insn_operands(la_decode *dec)
 {
     uint32_t insn = dec->insn;
     switch (dec->codec) {
+    case la_codec_empty:
+        break;
     case la_codec_2r:
         dec->r1 = operand_r1(insn);
         dec->r2 = operand_r2(insn);
@@ -291,6 +318,17 @@ static void decode_insn_operands(la_decode *dec)
         dec->imm = operand_im12(insn);
         dec->bit = IM_12;
         break;
+    case la_codec_2r_im8:
+        dec->r1 = operand_r1(insn);
+        dec->r2 = operand_r2(insn);
+        dec->imm = operand_im8(insn);
+        dec->bit = IM_8;
+        break;
+    case la_codec_r_im14:
+        dec->r1 = operand_r1(insn);
+        dec->imm = operand_im14(insn);
+        dec->bit = IM_14;
+        break;
     case la_codec_r_cd:
         dec->r1 = operand_cd(insn);
         dec->r2 = operand_r2(insn);
@@ -299,6 +337,12 @@ static void decode_insn_operands(la_decode *dec)
         dec->r1 = operand_r1(insn);
         dec->r2 = operand_cj(insn);
         break;
+    case la_codec_r_seq:
+        dec->r1 = 0;
+        dec->r2 = operand_r1(insn);
+        dec->imm = operand_im8(insn);
+        dec->bit = IM_8;
+        break;
     case la_codec_code:
         dec->code = operand_code(insn);
         break;
@@ -306,6 +350,12 @@ static void decode_insn_operands(la_decode *dec)
         dec->imm = operand_whint(insn);
         dec->bit = IM_15;
         break;
+    case la_codec_invtlb:
+        dec->imm = operand_invop(insn);
+        dec->bit = IM_5;
+        dec->r2 = operand_r2(insn);
+        dec->r3 = operand_r3(insn);
+        break;
     case la_codec_r_ofs21:
         dec->imm = operand_ofs21(insn);
         dec->bit = IM_21;
@@ -499,6 +549,10 @@ static void format_insn(char *buf, size_t buflen,  const char* name,
         case 'L': /* ca */
             append(buf, fccregnames[dec->r4], buflen);
             break;
+        case 'M': /* cop */
+            snprintf(tmp, sizeof(tmp), "0x%x", (dec->imm2) & 0x1f);
+            append(buf, tmp, buflen);
+            break;
         case 'i': /* sixx d */
             snprintf(tmp, sizeof(tmp), "%d", dec->imm);
             append(buf, tmp, buflen);
@@ -509,6 +563,14 @@ static void format_insn(char *buf, size_t buflen,  const char* name,
             break;
         case 'x': /* sixx x */
             switch (dec->bit) {
+            case IM_5:
+                snprintf(tmp, sizeof(tmp), "0x%x", (dec->imm) & 0x1f);
+                append(buf, tmp, buflen);
+                break;
+            case IM_8:
+                snprintf(tmp, sizeof(tmp), "0x%x", (dec->imm) & 0xff);
+                append(buf, tmp, buflen);
+                break;
             case IM_12:
                 snprintf(tmp, sizeof(tmp), "0x%x", (dec->imm) & 0xfff);
                 append(buf, tmp, buflen);
@@ -916,3 +978,27 @@ INSN(blt,          la_fmt_rj_rd_offs16, la_codec_2r_im16)
 INSN(bge,          la_fmt_rj_rd_offs16, la_codec_2r_im16)
 INSN(bltu,         la_fmt_rj_rd_offs16, la_codec_2r_im16)
 INSN(bgeu,         la_fmt_rj_rd_offs16, la_codec_2r_im16)
+INSN(csrrd,        la_fmt_rd_csr,       la_codec_r_im14)
+INSN(csrwr,        la_fmt_rd_csr,       la_codec_r_im14)
+INSN(csrxchg,      la_fmt_rd_rj_csr,    la_codec_2r_im14)
+INSN(iocsrrd_b,    la_fmt_rd_rj,        la_codec_2r)
+INSN(iocsrrd_h,    la_fmt_rd_rj,        la_codec_2r)
+INSN(iocsrrd_w,    la_fmt_rd_rj,        la_codec_2r)
+INSN(iocsrrd_d,    la_fmt_rd_rj,        la_codec_2r)
+INSN(iocsrwr_b,    la_fmt_rd_rj,        la_codec_2r)
+INSN(iocsrwr_h,    la_fmt_rd_rj,        la_codec_2r)
+INSN(iocsrwr_w,    la_fmt_rd_rj,        la_codec_2r)
+INSN(iocsrwr_d,    la_fmt_rd_rj,        la_codec_2r)
+INSN(cacop,        la_fmt_rd_rj_si,     la_codec_2r_im12)
+INSN(tlbsrch,      la_fmt_empty,        la_codec_empty)
+INSN(tlbrd,        la_fmt_empty,        la_codec_empty)
+INSN(tlbwr,        la_fmt_empty,        la_codec_empty)
+INSN(tlbfill,      la_fmt_empty,        la_codec_empty)
+INSN(tlbclr,       la_fmt_empty,        la_codec_empty)
+INSN(tlbflush,     la_fmt_empty,        la_codec_empty)
+INSN(invtlb,       la_fmt_invtlb,       la_codec_invtlb)
+INSN(lddir,        la_fmt_rd_rj_si,     la_codec_2r_im8)
+INSN(ldpte,        la_fmt_rj_seq,       la_codec_r_seq)
+INSN(ertn,         la_fmt_empty,        la_codec_empty)
+INSN(idle,         la_fmt_whint,        la_codec_whint)
+INSN(dbcl,         la_fmt_code,         la_codec_code)
-- 
2.27.0



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

* [RFC PATCH v2 15/30] hw/pci-host: Add ls7a1000 PCIe Host bridge support for Loongson Platform
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (13 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 14/30] target/loongarch: Implement privilege instructions disassembly Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 13:17   ` Mark Cave-Ayland
  2021-11-11  1:35 ` [RFC PATCH v2 16/30] hw/loongarch: Add a virt LoongArch 3A5000 board support Xiaojuan Yang
                   ` (15 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This is a model of the PCIe Host Bridge found on a Loongson-5000
processor. It includes a interrupt controller, some interface for
pci and nonpci devices we only emulate part devices for tcg mode.
It support for MSI and MSIX interrupt sources.

For more detailed info about ls7a1000 you can see the doc at
https://github.com/loongson/LoongArch-Documentation/releases/latest/
download/Loongson-7A1000-usermanual-2.00-EN.pdf

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/pci-host/Kconfig        |   4 +
 hw/pci-host/ls7a.c         | 187 +++++++++++++++++++++++++++++++++++++
 hw/pci-host/meson.build    |   1 +
 include/hw/pci-host/ls7a.h |  47 ++++++++++
 4 files changed, 239 insertions(+)
 create mode 100644 hw/pci-host/ls7a.c
 create mode 100644 include/hw/pci-host/ls7a.h

diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig
index 2b5f7d58cc..b02a9d1454 100644
--- a/hw/pci-host/Kconfig
+++ b/hw/pci-host/Kconfig
@@ -77,3 +77,7 @@ config MV64361
     bool
     select PCI
     select I8259
+
+config PCI_EXPRESS_7A
+    bool
+    select PCI_EXPRESS
diff --git a/hw/pci-host/ls7a.c b/hw/pci-host/ls7a.c
new file mode 100644
index 0000000000..90b9fe4830
--- /dev/null
+++ b/hw/pci-host/ls7a.c
@@ -0,0 +1,187 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU Loongson 7A1000 North Bridge Emulation
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/pci/pci.h"
+#include "hw/pci/pcie_host.h"
+#include "qapi/error.h"
+#include "hw/irq.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_bus.h"
+#include "sysemu/reset.h"
+#include "hw/pci-host/ls7a.h"
+#include "migration/vmstate.h"
+
+static const VMStateDescription vmstate_ls7a_pcie = {
+    .name = "LS7A_PCIE",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, LS7APCIState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pci_ls7a_config_write(void *opaque, hwaddr addr,
+                                  uint64_t val, unsigned size)
+{
+    pci_data_write(opaque, addr, val, size);
+}
+
+static uint64_t pci_ls7a_config_read(void *opaque,
+                                     hwaddr addr, unsigned size)
+{
+    uint64_t val;
+
+    val = pci_data_read(opaque, addr, size);
+
+    return val;
+}
+
+static const MemoryRegionOps pci_ls7a_config_ops = {
+    .read = pci_ls7a_config_read,
+    .write = pci_ls7a_config_write,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void ls7a_pciehost_realize(DeviceState *dev, Error **errp)
+{
+    LS7APCIEHost *pciehost = LS7A_PCIE_HOST_BRIDGE(dev);
+    PCIExpressHost *e = PCIE_HOST_BRIDGE(dev);
+    PCIHostState *phb = PCI_HOST_BRIDGE(e);
+
+    phb->bus = pci_register_root_bus(dev, "pcie.0", NULL,
+                                     NULL, pciehost,
+                                     get_system_memory(), get_system_io(),
+                                     PCI_DEVFN(1, 0), 128, TYPE_PCIE_BUS);
+
+    memory_region_init_io(&pciehost->pci_conf, OBJECT(dev),
+                          &pci_ls7a_config_ops, phb->bus,
+                          "ls7a_pci_conf", HT1LO_PCICFG_SIZE);
+    memory_region_add_subregion(get_system_memory(), HT1LO_PCICFG_BASE,
+                                &pciehost->pci_conf);
+
+    /* Add ls7a pci-io */
+    memory_region_init_alias(&pciehost->pci_io, OBJECT(dev), "ls7a-pci-io",
+                             get_system_io(), 0, LS7A_PCI_IO_SIZE);
+    memory_region_add_subregion(get_system_memory(), LS7A_PCI_IO_BASE,
+                                &pciehost->pci_io);
+
+    pcie_host_mmcfg_update(e, true, LS_PCIECFG_BASE, LS_PCIECFG_SIZE);
+}
+
+PCIBus *ls7a_init(MachineState *machine, qemu_irq *pic)
+{
+    DeviceState *dev;
+    PCIHostState *phb;
+    LS7APCIState *pbs;
+    LS7APCIEHost *pciehost;
+    PCIDevice *pci_dev;
+    PCIExpressHost *e;
+
+    dev = qdev_new(TYPE_LS7A_PCIE_HOST_BRIDGE);
+    e = PCIE_HOST_BRIDGE(dev);
+    phb = PCI_HOST_BRIDGE(e);
+    pciehost = LS7A_PCIE_HOST_BRIDGE(dev);
+    pciehost->pic = pic;
+
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+    pci_dev = pci_new(PCI_DEVFN(0, 0), TYPE_LS7A_PCIE);
+    pbs = LS7A_PCIE(pci_dev);
+    pbs->pciehost = pciehost;
+    pbs->pciehost->pci_dev = pbs;
+
+    pci_realize_and_unref(pci_dev, phb->bus, &error_fatal);
+
+    return phb->bus;
+}
+
+static void ls7a_reset(DeviceState *qdev)
+{
+    uint64_t wmask;
+    wmask = ~(-1);
+    PCIDevice *dev = PCI_DEVICE(qdev);
+
+    pci_set_word(dev->config + PCI_STATUS, 0x0010);
+    pci_set_word(dev->wmask + PCI_STATUS, wmask & 0xffff);
+    pci_set_word(dev->cmask + PCI_STATUS, 0xffff);
+    pci_set_byte(dev->config + PCI_HEADER_TYPE, 0x1);
+    pci_set_byte(dev->wmask + PCI_HEADER_TYPE, wmask & 0xff);
+    pci_set_byte(dev->cmask + PCI_HEADER_TYPE, 0xff);
+    pci_set_word(dev->config + PCI_SUBSYSTEM_VENDOR_ID, 0x0014);
+    pci_set_word(dev->wmask + PCI_SUBSYSTEM_VENDOR_ID, wmask & 0xffff);
+    pci_set_word(dev->cmask + PCI_SUBSYSTEM_VENDOR_ID, 0xffff);
+    pci_set_word(dev->config + PCI_SUBSYSTEM_ID, 0x7a00);
+    pci_set_word(dev->wmask + PCI_SUBSYSTEM_ID, wmask & 0xffff);
+    pci_set_word(dev->cmask + PCI_SUBSYSTEM_ID, 0xffff);
+    pci_set_byte(dev->config + PCI_CAPABILITY_LIST, 0x40);
+    pci_set_byte(dev->wmask + PCI_CAPABILITY_LIST, wmask & 0xff);
+    pci_set_byte(dev->cmask + PCI_CAPABILITY_LIST, 0xff);
+}
+
+static void ls7a_pcie_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->vendor_id = 0x0014;
+    k->device_id = 0x7a00;
+    k->revision = 0x00;
+    k->class_id = PCI_CLASS_BRIDGE_HOST;
+    dc->reset = ls7a_reset;
+    dc->desc = "LS7A1000 PCIE Host bridge";
+    dc->vmsd = &vmstate_ls7a_pcie;
+    /*
+     * PCI-facing part of the host bridge, not usable without the
+     * host-facing part, which can't be device_add'ed, yet.
+     */
+    dc->user_creatable = false;
+}
+
+static const TypeInfo ls7a_pcie_device_info = {
+    .name          = TYPE_LS7A_PCIE,
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(LS7APCIState),
+    .class_init    = ls7a_pcie_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+        { },
+    },
+};
+
+static void ls7a_pciehost_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    dc->realize = ls7a_pciehost_realize;
+    dc->fw_name = "pci";
+    dc->user_creatable = false;
+}
+
+static const TypeInfo ls7a_pciehost_info = {
+    .name          = TYPE_LS7A_PCIE_HOST_BRIDGE,
+    .parent        = TYPE_PCIE_HOST_BRIDGE,
+    .instance_size = sizeof(LS7APCIEHost),
+    .class_init    = ls7a_pciehost_class_init,
+};
+
+static void ls7a_register_types(void)
+{
+    type_register_static(&ls7a_pciehost_info);
+    type_register_static(&ls7a_pcie_device_info);
+}
+
+type_init(ls7a_register_types)
diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build
index 4c4f39c15c..c4955455fd 100644
--- a/hw/pci-host/meson.build
+++ b/hw/pci-host/meson.build
@@ -11,6 +11,7 @@ pci_ss.add(when: 'CONFIG_PCI_SABRE', if_true: files('sabre.c'))
 pci_ss.add(when: 'CONFIG_XEN_IGD_PASSTHROUGH', if_true: files('xen_igd_pt.c'))
 pci_ss.add(when: 'CONFIG_REMOTE_PCIHOST', if_true: files('remote.c'))
 pci_ss.add(when: 'CONFIG_SH_PCI', if_true: files('sh_pci.c'))
+pci_ss.add(when: 'CONFIG_PCI_EXPRESS_7A', if_true: files('ls7a.c'))
 
 # PPC devices
 pci_ss.add(when: 'CONFIG_RAVEN_PCI', if_true: files('raven.c'))
diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h
new file mode 100644
index 0000000000..6b5ba3b442
--- /dev/null
+++ b/include/hw/pci-host/ls7a.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU LoongArch CPU
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef HW_LS7A_H
+#define HW_LS7A_H
+
+#include "hw/pci/pci.h"
+#include "hw/pci/pcie_host.h"
+#include "hw/pci-host/pam.h"
+#include "qemu/units.h"
+#include "qemu/range.h"
+#include "qom/object.h"
+
+#define HT1LO_PCICFG_BASE        0x1a000000
+#define HT1LO_PCICFG_SIZE        0x02000000
+
+#define LS_PCIECFG_BASE          0x20000000
+#define LS_PCIECFG_SIZE          0x08000000
+
+#define LS7A_PCI_IO_BASE        0x18000000UL
+#define LS7A_PCI_IO_SIZE        0x00010000
+typedef struct LS7APCIState LS7APCIState;
+typedef struct LS7APCIEHost {
+    PCIExpressHost parent_obj;
+    LS7APCIState *pci_dev;
+    qemu_irq *pic;
+    MemoryRegion pci_conf;
+    MemoryRegion pci_io;
+} LS7APCIEHost;
+
+struct LS7APCIState {
+    PCIDevice dev;
+    LS7APCIEHost *pciehost;
+};
+
+#define TYPE_LS7A_PCIE_HOST_BRIDGE "ls7a1000-pciehost"
+OBJECT_DECLARE_SIMPLE_TYPE(LS7APCIEHost, LS7A_PCIE_HOST_BRIDGE)
+
+#define TYPE_LS7A_PCIE "ls7a1000_pcie"
+OBJECT_DECLARE_SIMPLE_TYPE(LS7APCIState, LS7A_PCIE)
+
+PCIBus *ls7a_init(MachineState *machine, qemu_irq *irq);
+#endif /* HW_LS7A_H */
-- 
2.27.0



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

* [RFC PATCH v2 16/30] hw/loongarch: Add a virt LoongArch 3A5000 board support
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (14 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 15/30] hw/pci-host: Add ls7a1000 PCIe Host bridge support for Loongson Platform Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 14:17   ` Mark Cave-Ayland
  2021-11-11  1:35 ` [RFC PATCH v2 17/30] hw/loongarch: Add LoongArch cpu interrupt support(CPUINTC) Xiaojuan Yang
                   ` (14 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

LoongArch is a new RISC ISA, support 32bit mode
or 64bit mode. Now we only add 64bit support.

More detailed info you can see
https://github.com/loongson/LoongArch-Documentation

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 .../devices/loongarch64-softmmu/default.mak   |   3 +
 configs/targets/loongarch64-softmmu.mak       |   3 +
 hw/Kconfig                                    |   1 +
 hw/loongarch/Kconfig                          |   3 +
 hw/loongarch/ls3a5000_virt.c                  | 210 ++++++++++++++++++
 hw/loongarch/meson.build                      |   4 +
 hw/meson.build                                |   1 +
 include/exec/poison.h                         |   2 +
 include/hw/loongarch/loongarch.h              |  46 ++++
 include/sysemu/arch_init.h                    |   1 +
 qapi/machine.json                             |   2 +-
 target/Kconfig                                |   1 +
 target/loongarch/Kconfig                      |   2 +
 target/loongarch/cpu.c                        |   8 +
 target/loongarch/cpu.h                        |   4 +
 15 files changed, 290 insertions(+), 1 deletion(-)
 create mode 100644 configs/devices/loongarch64-softmmu/default.mak
 create mode 100644 hw/loongarch/Kconfig
 create mode 100644 hw/loongarch/ls3a5000_virt.c
 create mode 100644 hw/loongarch/meson.build
 create mode 100644 include/hw/loongarch/loongarch.h
 create mode 100644 target/loongarch/Kconfig

diff --git a/configs/devices/loongarch64-softmmu/default.mak b/configs/devices/loongarch64-softmmu/default.mak
new file mode 100644
index 0000000000..a6705b9e4a
--- /dev/null
+++ b/configs/devices/loongarch64-softmmu/default.mak
@@ -0,0 +1,3 @@
+# Default configuration for loongarch64-softmmu
+
+CONFIG_LOONGSON_3A5000=y
diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak
index f33fa1590b..7bc06c850c 100644
--- a/configs/targets/loongarch64-softmmu.mak
+++ b/configs/targets/loongarch64-softmmu.mak
@@ -1 +1,4 @@
+TARGET_ARCH=loongarch64
+TARGET_BASE_ARCH=loongarch
+TARGET_SUPPORTS_MTTCG=y
 TARGET_XML_FILES= gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu64.xml
diff --git a/hw/Kconfig b/hw/Kconfig
index ad20cce0a9..f71b2155ed 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -49,6 +49,7 @@ source avr/Kconfig
 source cris/Kconfig
 source hppa/Kconfig
 source i386/Kconfig
+source loongarch/Kconfig
 source m68k/Kconfig
 source microblaze/Kconfig
 source mips/Kconfig
diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
new file mode 100644
index 0000000000..720822f32c
--- /dev/null
+++ b/hw/loongarch/Kconfig
@@ -0,0 +1,3 @@
+config LOONGSON_3A5000
+    bool
+    select PCI_EXPRESS_7A
diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
new file mode 100644
index 0000000000..7c88d64795
--- /dev/null
+++ b/hw/loongarch/ls3a5000_virt.c
@@ -0,0 +1,210 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU loongson 3a5000 develop board emulation
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ */
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/units.h"
+#include "qemu/datadir.h"
+#include "qapi/error.h"
+#include "hw/boards.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+#include "sysemu/runstate.h"
+#include "sysemu/reset.h"
+#include "hw/loongarch/loongarch.h"
+#include "hw/pci-host/ls7a.h"
+
+CPULoongArchState *cpu_states[LOONGARCH_MAX_VCPUS];
+
+static void main_cpu_reset(void *opaque)
+{
+    LoongArchCPU *cpu = opaque;
+
+    cpu_reset(CPU(cpu));
+}
+
+static uint64_t loongarch_pm_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    return 0;
+}
+
+static void loongarch_pm_mem_write(void *opaque, hwaddr addr,
+                                   uint64_t val, unsigned size)
+{
+
+    if (addr != PM_CNT_MODE) {
+        return;
+    }
+
+    switch (val) {
+    case 0x00:
+        qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+        return;
+    case 0xff:
+        qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
+        return;
+    default:
+        return;
+    }
+}
+
+static const MemoryRegionOps loongarch_pm_ops = {
+    .read  = loongarch_pm_mem_read,
+    .write = loongarch_pm_mem_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+#define LOONGARCH_SIMPLE_MMIO_OPS(ADDR, NAME, SIZE) \
+({\
+     MemoryRegion *iomem = g_new(MemoryRegion, 1);\
+     memory_region_init_io(iomem, NULL, &loongarch_qemu_ops,\
+                           (void *)ADDR, NAME, SIZE);\
+     memory_region_add_subregion(lams->system_iocsr, ADDR, iomem);\
+})
+
+static void loongarch_qemu_write(void *opaque, hwaddr addr,
+                                 uint64_t val, unsigned size)
+{
+}
+
+static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size)
+{
+    uint64_t feature = 0UL;
+    addr = ((hwaddr)(long)opaque) + addr;
+
+    switch (addr) {
+    case FEATURE_REG:
+        feature |= 1UL << IOCSRF_MSI | 1UL << IOCSRF_EXTIOI |
+                   1UL << IOCSRF_CSRIPI;
+        return feature ;
+    case VENDOR_REG:
+        return *(uint64_t *)"Loongson-3A5000";
+    case CPUNAME_REG:
+        return *(uint64_t *)"3A5000";
+    }
+    return 0;
+}
+
+static const MemoryRegionOps loongarch_qemu_ops = {
+    .read = loongarch_qemu_read,
+    .write = loongarch_qemu_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 8,
+    },
+};
+
+static void ls3a5000_virt_init(MachineState *machine)
+{
+    const char *cpu_model = machine->cpu_type;
+    LoongArchCPU *cpu;
+    CPULoongArchState *env;
+    uint64_t lowram_size = 0, highram_size = 0;
+    MemoryRegion *lowmem = g_new(MemoryRegion, 1);
+    char *ramName = NULL;
+    ram_addr_t ram_size = machine->ram_size;
+    MemoryRegion *address_space_mem = get_system_memory();
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
+    int i;
+    MemoryRegion *iomem = NULL;
+
+    if (!cpu_model) {
+        cpu_model = LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000");
+    }
+    if (!strstr(cpu_model, "Loongson-3A5000")) {
+        error_report("LoongArch/TCG needs cpu type Loongson-3A5000");
+        exit(1);
+    }
+
+    lams->system_iocsr = g_new0(MemoryRegion, 1);
+    lams->address_space_iocsr = g_new0(AddressSpace, 1);
+    memory_region_init_io(lams->system_iocsr, NULL, NULL, lams, "iocsr", UINT64_MAX);
+    address_space_init(lams->address_space_iocsr, lams->system_iocsr, "IOCSR");
+
+    /* Init CPUs */
+    for (i = 0; i < machine->smp.cpus; i++) {
+        Object *cpuobj = NULL;
+        CPUState *cs;
+
+        cpuobj = object_new(machine->cpu_type);
+
+        cs = CPU(cpuobj);
+        cs->cpu_index = i;
+
+        qdev_realize(DEVICE(cpuobj), NULL, &error_fatal);
+        object_unref(cpuobj);
+
+        cpu = LOONGARCH_CPU(cs);
+        if (cpu == NULL) {
+            fprintf(stderr, "Unable to find CPU definition\n");
+            exit(1);
+        }
+        env = &cpu->env;
+        cpu_states[i] = env;
+
+        cpu_loongarch_clock_init(cpu);
+        qemu_register_reset(main_cpu_reset, cpu);
+    }
+
+    ramName = g_strdup_printf("loongarch.lowram");
+    lowram_size = MIN(ram_size, 256 * 0x100000);
+    memory_region_init_alias(lowmem, NULL, ramName, machine->ram,
+                             0, lowram_size);
+    memory_region_add_subregion(address_space_mem, 0, lowmem);
+
+    highram_size = ram_size > lowram_size ? ram_size - 256 * 0x100000 : 0;
+    if (highram_size > 0) {
+        MemoryRegion *highmem = g_new(MemoryRegion, 1);
+        ramName = g_strdup_printf("loongarch.highram");
+        memory_region_init_alias(highmem, NULL, ramName, machine->ram,
+                                 lowram_size, highram_size);
+        memory_region_add_subregion(address_space_mem, 0x90000000, highmem);
+    }
+
+    /* Add PM mmio memory for reboot and shutdown*/
+    iomem = g_new(MemoryRegion, 1);
+    memory_region_init_io(iomem, NULL, &loongarch_pm_ops, NULL,
+                          "loongarch_pm", PM_MMIO_SIZE);
+    memory_region_add_subregion(address_space_mem,
+                                PM_MMIO_ADDR, iomem);
+
+    LOONGARCH_SIMPLE_MMIO_OPS(FEATURE_REG, "loongarch_feature", 0x8);
+    LOONGARCH_SIMPLE_MMIO_OPS(VENDOR_REG, "loongarch_vendor", 0x8);
+    LOONGARCH_SIMPLE_MMIO_OPS(CPUNAME_REG, "loongarch_cpuname", 0x8);
+}
+
+static void loongarch_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->desc = "Loongson-5000 LS7A1000 machine";
+    mc->init = ls3a5000_virt_init;
+    mc->default_ram_size = 1 * GiB;
+    mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000");
+    mc->default_ram_id = "loongarch.ram";
+    mc->max_cpus = LOONGARCH_MAX_VCPUS;
+    mc->is_default = 1;
+    mc->default_kernel_irqchip_split = false;
+    mc->block_default_type = IF_VIRTIO;
+    mc->default_boot_order = "c";
+    mc->no_cdrom = 1;
+}
+
+static const TypeInfo loongarch_machine_types[] = {
+    {
+        .name           = TYPE_LOONGARCH_MACHINE,
+        .parent         = TYPE_MACHINE,
+        .instance_size  = sizeof(LoongArchMachineState),
+        .class_init     = loongarch_class_init,
+    }
+};
+
+DEFINE_TYPES(loongarch_machine_types)
diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build
new file mode 100644
index 0000000000..1e743cadb8
--- /dev/null
+++ b/hw/loongarch/meson.build
@@ -0,0 +1,4 @@
+loongarch_ss = ss.source_set()
+loongarch_ss.add(when: 'CONFIG_LOONGSON_3A5000', if_true: files('ls3a5000_virt.c'))
+
+hw_arch += {'loongarch': loongarch_ss}
diff --git a/hw/meson.build b/hw/meson.build
index b3366c888e..95202649b7 100644
--- a/hw/meson.build
+++ b/hw/meson.build
@@ -49,6 +49,7 @@ subdir('avr')
 subdir('cris')
 subdir('hppa')
 subdir('i386')
+subdir('loongarch')
 subdir('m68k')
 subdir('microblaze')
 subdir('mips')
diff --git a/include/exec/poison.h b/include/exec/poison.h
index 7ad4ad18e8..590bc305c7 100644
--- a/include/exec/poison.h
+++ b/include/exec/poison.h
@@ -14,6 +14,7 @@
 #pragma GCC poison TARGET_CRIS
 #pragma GCC poison TARGET_HEXAGON
 #pragma GCC poison TARGET_HPPA
+#pragma GCC poison TARGET_LOONGARCH64
 #pragma GCC poison TARGET_M68K
 #pragma GCC poison TARGET_MICROBLAZE
 #pragma GCC poison TARGET_MIPS
@@ -73,6 +74,7 @@
 #pragma GCC poison CONFIG_HPPA_DIS
 #pragma GCC poison CONFIG_I386_DIS
 #pragma GCC poison CONFIG_HEXAGON_DIS
+#pragma GCC poison CONFIG_LOONGARCH_DIS
 #pragma GCC poison CONFIG_M68K_DIS
 #pragma GCC poison CONFIG_MICROBLAZE_DIS
 #pragma GCC poison CONFIG_MIPS_DIS
diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
new file mode 100644
index 0000000000..edab069f76
--- /dev/null
+++ b/include/hw/loongarch/loongarch.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Definitions for loongarch board emulation.
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef HW_LOONGARCH_H
+#define HW_LOONGARCH_H
+
+#include "target/loongarch/cpu.h"
+#include "qemu-common.h"
+#include "hw/boards.h"
+#include "qemu/queue.h"
+
+#define LOONGARCH_MAX_VCPUS     4
+#define PM_MMIO_ADDR            0x10080000UL
+#define PM_MMIO_SIZE            0x100
+#define PM_CNT_MODE             0x10
+#define FEATURE_REG             0x8
+#define IOCSRF_TEMP             0
+#define IOCSRF_NODECNT          1
+#define IOCSRF_MSI              2
+#define IOCSRF_EXTIOI           3
+#define IOCSRF_CSRIPI           4
+#define IOCSRF_FREQCSR          5
+#define IOCSRF_FREQSCALE        6
+#define IOCSRF_DVFSV1           7
+#define IOCSRF_GMOD             9
+#define IOCSRF_VM               11
+
+#define VENDOR_REG              0x10
+#define CPUNAME_REG             0x20
+
+typedef struct LoongArchMachineState {
+    /*< private >*/
+    MachineState parent_obj;
+
+    AddressSpace *address_space_iocsr;
+    MemoryRegion *system_iocsr;
+} LoongArchMachineState;
+
+#define TYPE_LOONGARCH_MACHINE  MACHINE_TYPE_NAME("loongson7a")
+DECLARE_INSTANCE_CHECKER(LoongArchMachineState, LOONGARCH_MACHINE,
+                         TYPE_LOONGARCH_MACHINE)
+#endif
diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
index 70c579560a..3ac3634bbb 100644
--- a/include/sysemu/arch_init.h
+++ b/include/sysemu/arch_init.h
@@ -24,6 +24,7 @@ enum {
     QEMU_ARCH_RX = (1 << 20),
     QEMU_ARCH_AVR = (1 << 21),
     QEMU_ARCH_HEXAGON = (1 << 22),
+    QEMU_ARCH_LOONGARCH = (1 << 23),
 };
 
 extern const uint32_t arch_type;
diff --git a/qapi/machine.json b/qapi/machine.json
index 17794ef681..b23456ce98 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -30,7 +30,7 @@
 ##
 { 'enum' : 'SysEmuTarget',
   'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'cris', 'hppa', 'i386',
-             'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64',
+             'loongarch64', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64',
              'mips64el', 'mipsel', 'nios2', 'or1k', 'ppc',
              'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4',
              'sh4eb', 'sparc', 'sparc64', 'tricore',
diff --git a/target/Kconfig b/target/Kconfig
index ae7f24fc66..83da0bd293 100644
--- a/target/Kconfig
+++ b/target/Kconfig
@@ -4,6 +4,7 @@ source avr/Kconfig
 source cris/Kconfig
 source hppa/Kconfig
 source i386/Kconfig
+source loongarch/Kconfig
 source m68k/Kconfig
 source microblaze/Kconfig
 source mips/Kconfig
diff --git a/target/loongarch/Kconfig b/target/loongarch/Kconfig
new file mode 100644
index 0000000000..46b26b1a85
--- /dev/null
+++ b/target/loongarch/Kconfig
@@ -0,0 +1,2 @@
+config LOONGARCH64
+    bool
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index c789acaf2f..a6010deef0 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -11,6 +11,7 @@
 #include "qemu/module.h"
 #include "sysemu/qtest.h"
 #include "exec/exec-all.h"
+#include "hw/qdev-properties.h"
 #include "qapi/qapi-commands-machine-target.h"
 #include "migration/vmstate.h"
 #include "cpu.h"
@@ -534,6 +535,12 @@ static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model)
     return oc;
 }
 
+static Property loongarch_cpu_properties[] = {
+    DEFINE_PROP_INT32("core-id", LoongArchCPU, core_id, -1),
+    DEFINE_PROP_UINT32("id", LoongArchCPU, id, UNASSIGNED_CPU_ID),
+    DEFINE_PROP_END_OF_LIST()
+};
+
 void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags)
 {
     LoongArchCPU *cpu = LOONGARCH_CPU(cs);
@@ -629,6 +636,7 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data)
     device_class_set_parent_realize(dc, loongarch_cpu_realizefn,
                                     &lacc->parent_realize);
     device_class_set_parent_reset(dc, loongarch_cpu_reset, &lacc->parent_reset);
+    device_class_set_props(dc, loongarch_cpu_properties);
 
     cc->class_by_name = loongarch_cpu_class_by_name;
     cc->has_work = loongarch_cpu_has_work;
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 6f7c13d366..77afe9e26a 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -15,6 +15,8 @@
 
 #define TCG_GUEST_DEFAULT_MO (0)
 
+#define UNASSIGNED_CPU_ID 0xFFFFFFFF
+
 #define FCSR0_M1    0x1f         /* FCSR1 mask, Enables */
 #define FCSR0_M2    0x1f1f0000   /* FCSR2 mask, Cause and Flags */
 #define FCSR0_M3    0x300        /* FCSR3 mask, Round Mode */
@@ -371,6 +373,8 @@ struct LoongArchCPU {
 
     CPUNegativeOffsetState neg;
     CPULoongArchState env;
+    uint32_t id;
+    int32_t core_id;
 };
 
 #define TYPE_LOONGARCH_CPU "loongarch-cpu"
-- 
2.27.0



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

* [RFC PATCH v2 17/30] hw/loongarch: Add LoongArch cpu interrupt support(CPUINTC)
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (15 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 16/30] hw/loongarch: Add a virt LoongArch 3A5000 board support Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 14:22   ` Mark Cave-Ayland
  2021-11-11  1:35 ` [RFC PATCH v2 18/30] hw/loongarch: Add LoongArch ipi interrupt support(IPI) Xiaojuan Yang
                   ` (13 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

Loongson-3A5000 support 14 interrupts from 64 - 77(Timer->75 IPI->76)
Loongson-3A5000 and ls7a form a legacy model and extended model irq
hierarchy.Tcg mode emulate a simplified extended model which
has no Legacy I/O Interrupt Controller(LIOINTC) and LPC.
e.g:

 |    +-----+    +---------+     +-------+             |
 |    | IPI |--> | CPUINTC | <-- | Timer |             |
 |    +-----+    +---------+     +-------+             |
 |                    ^                                |
 |                    |                                |
 |               +---------+
 |               | EIOINTC |
 |               +---------+
 |                ^       ^                            |
 |                |       |                            |
 |         +---------+ +---------+                     |
 |         | PCH-PIC | | PCH-MSI |                     |
 |         +---------+ +---------+                     |
 |           ^     ^           ^                       |
 |           |     |           |                       |
 |   +---------+ +---------+ +---------+               |
 |   | UARTs | | Devices | | Devices |                 |
 |   +---------+ +---------+ +---------+               |
 |        ^                                            |

The following series patch will realize the interrupt
controller in this model.

More detailed info can be found at the kernel doc or manual
1.https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/
linux-loongson.git/tree/Documentation/loongarch?h=loongarch-next
2.https://github.com/loongson/LoongArch-Documentation

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/loongarch/loongarch_int.c     | 59 ++++++++++++++++++++++++++++++++
 hw/loongarch/ls3a5000_virt.c     |  2 ++
 hw/loongarch/meson.build         |  1 +
 include/hw/loongarch/loongarch.h |  2 ++
 4 files changed, 64 insertions(+)
 create mode 100644 hw/loongarch/loongarch_int.c

diff --git a/hw/loongarch/loongarch_int.c b/hw/loongarch/loongarch_int.c
new file mode 100644
index 0000000000..2502d056ac
--- /dev/null
+++ b/hw/loongarch/loongarch_int.c
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU LOONGARCH interrupt support
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "hw/irq.h"
+#include "hw/loongarch/loongarch.h"
+#include "cpu.h"
+
+static void cpu_loongarch_irq_request(void *opaque, int irq, int level)
+{
+    LoongArchCPU *cpu = opaque;
+    CPULoongArchState *env = &cpu->env;
+    CPUState *cs = CPU(cpu);
+    bool locked = false;
+
+    if (irq < 0 || irq > N_IRQS) {
+        return;
+    }
+
+    /* Make sure locking works even if BQL is already held by the caller */
+    if (!qemu_mutex_iothread_locked()) {
+        locked = true;
+        qemu_mutex_lock_iothread();
+    }
+
+    if (level) {
+        env->CSR_ESTAT |= 1 << irq;
+    } else {
+        env->CSR_ESTAT &= ~(1 << irq);
+    }
+
+    if (FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS)) {
+        cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+    } else {
+        cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+    }
+
+    if (locked) {
+        qemu_mutex_unlock_iothread();
+    }
+}
+
+void cpu_loongarch_init_irq(LoongArchCPU *cpu)
+{
+    CPULoongArchState *env = &cpu->env;
+    qemu_irq *qi;
+    int i;
+
+    qi = qemu_allocate_irqs(cpu_loongarch_irq_request, cpu, N_IRQS);
+    for (i = 0; i < N_IRQS; i++) {
+        env->irq[i] = qi[i];
+    }
+    g_free(qi);
+}
diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
index 7c88d64795..37d6b1ec88 100644
--- a/hw/loongarch/ls3a5000_virt.c
+++ b/hw/loongarch/ls3a5000_virt.c
@@ -150,6 +150,8 @@ static void ls3a5000_virt_init(MachineState *machine)
         env = &cpu->env;
         cpu_states[i] = env;
 
+        /* Init CPU internal devices */
+        cpu_loongarch_init_irq(cpu);
         cpu_loongarch_clock_init(cpu);
         qemu_register_reset(main_cpu_reset, cpu);
     }
diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build
index 1e743cadb8..a972210680 100644
--- a/hw/loongarch/meson.build
+++ b/hw/loongarch/meson.build
@@ -1,4 +1,5 @@
 loongarch_ss = ss.source_set()
+loongarch_ss.add(files('loongarch_int.c'))
 loongarch_ss.add(when: 'CONFIG_LOONGSON_3A5000', if_true: files('ls3a5000_virt.c'))
 
 hw_arch += {'loongarch': loongarch_ss}
diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
index edab069f76..8538697e5f 100644
--- a/include/hw/loongarch/loongarch.h
+++ b/include/hw/loongarch/loongarch.h
@@ -43,4 +43,6 @@ typedef struct LoongArchMachineState {
 #define TYPE_LOONGARCH_MACHINE  MACHINE_TYPE_NAME("loongson7a")
 DECLARE_INSTANCE_CHECKER(LoongArchMachineState, LOONGARCH_MACHINE,
                          TYPE_LOONGARCH_MACHINE)
+
+void cpu_loongarch_init_irq(LoongArchCPU *cpu);
 #endif
-- 
2.27.0



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

* [RFC PATCH v2 18/30] hw/loongarch: Add LoongArch ipi interrupt support(IPI)
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (16 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 17/30] hw/loongarch: Add LoongArch cpu interrupt support(CPUINTC) Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 14:28   ` Mark Cave-Ayland
  2021-11-11  1:35 ` [RFC PATCH v2 19/30] hw/intc: Add LoongArch ls7a interrupt controller support(PCH-PIC) Xiaojuan Yang
                   ` (12 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This patch realize the IPI interrupt controller.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/loongarch/ipi.c               | 144 +++++++++++++++++++++++++++++++
 hw/loongarch/ls3a5000_virt.c     |   1 +
 hw/loongarch/meson.build         |   2 +-
 include/hw/loongarch/gipi.h      |  37 ++++++++
 include/hw/loongarch/loongarch.h |   4 +
 target/loongarch/cpu.h           |   1 +
 6 files changed, 188 insertions(+), 1 deletion(-)
 create mode 100644 hw/loongarch/ipi.c
 create mode 100644 include/hw/loongarch/gipi.h

diff --git a/hw/loongarch/ipi.c b/hw/loongarch/ipi.c
new file mode 100644
index 0000000000..4902205ff5
--- /dev/null
+++ b/hw/loongarch/ipi.c
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch ipi interrupt support
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "exec/address-spaces.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/cpus.h"
+#include "cpu.h"
+#include "qemu/log.h"
+#include "hw/loongarch/loongarch.h"
+#include "migration/vmstate.h"
+
+static const VMStateDescription vmstate_gipi_core = {
+    .name = "gipi-single",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(status, gipi_core),
+        VMSTATE_UINT32(en, gipi_core),
+        VMSTATE_UINT32(set, gipi_core),
+        VMSTATE_UINT32(clear, gipi_core),
+        VMSTATE_UINT64_ARRAY(buf, gipi_core, MAX_GIPI_MBX_NUM),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_gipi = {
+    .name = "gipi",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT_ARRAY(core, gipiState, MAX_GIPI_CORE_NUM, 0,
+                             vmstate_gipi_core, gipi_core),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void gipi_writel(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    gipi_core *s = opaque;
+    void *pbuf;
+
+    if (size != 4) {
+        hw_error("size not 4");
+    }
+    addr &= 0xff;
+    switch (addr) {
+    case CORE_STATUS_OFF:
+        hw_error("CORE_SET_OFF Can't be write\n");
+        break;
+    case CORE_EN_OFF:
+        s->en = val;
+        break;
+    case CORE_SET_OFF:
+        s->status |= val;
+        if (s->status != 0) {
+            qemu_irq_raise(s->irq);
+        }
+        break;
+    case CORE_CLEAR_OFF:
+        s->status ^= val;
+        if (s->status == 0) {
+            qemu_irq_lower(s->irq);
+        }
+        break;
+    case CORE_BUF_20 ... CORE_BUF_38:
+        pbuf =  (void *)s->buf + (addr - 0x20);
+        *(unsigned int *)pbuf = val;
+        break;
+    default:
+        break;
+    }
+}
+
+static uint64_t gipi_readl(void *opaque, hwaddr addr, unsigned size)
+{
+    gipi_core *s = opaque;
+    uint64_t ret = 0;
+    void *pbuf;
+
+    addr &= 0xff;
+    if (size != 4) {
+        hw_error("size not 4 %d\n", size);
+    }
+    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:
+        pbuf =  (void *)s->buf + (addr - 0x20);
+        ret = *(unsigned int *)pbuf;
+        break;
+    default:
+        break;
+    }
+
+    return ret;
+}
+
+static const MemoryRegionOps gipi_ops = {
+    .read = gipi_readl,
+    .write = gipi_writel,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+int cpu_init_ipi(LoongArchMachineState *lams, qemu_irq parent, int cpu)
+{
+    int core_num = cpu % 4;
+    hwaddr addr;
+    MemoryRegion *region;
+    char str[32];
+
+    if (lams->gipi == NULL) {
+        lams->gipi = g_malloc0(sizeof(gipiState));
+        vmstate_register(NULL, 0, &vmstate_gipi, lams->gipi);
+    }
+
+    lams->gipi->core[cpu].irq = parent;
+
+    addr = SMP_GIPI_MAILBOX + core_num * 0x100;
+    region = g_new(MemoryRegion, 1);
+    sprintf(str, "gipi%d", cpu);
+    memory_region_init_io(region, NULL, &gipi_ops,
+                          &lams->gipi->core[cpu], str, 0x100);
+    memory_region_add_subregion(lams->system_iocsr, addr, region);
+    return 0;
+}
diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
index 37d6b1ec88..bd79df96df 100644
--- a/hw/loongarch/ls3a5000_virt.c
+++ b/hw/loongarch/ls3a5000_virt.c
@@ -153,6 +153,7 @@ static void ls3a5000_virt_init(MachineState *machine)
         /* Init CPU internal devices */
         cpu_loongarch_init_irq(cpu);
         cpu_loongarch_clock_init(cpu);
+        cpu_init_ipi(lams, env->irq[IRQ_IPI], i);
         qemu_register_reset(main_cpu_reset, cpu);
     }
 
diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build
index a972210680..1bd209c9eb 100644
--- a/hw/loongarch/meson.build
+++ b/hw/loongarch/meson.build
@@ -1,5 +1,5 @@
 loongarch_ss = ss.source_set()
 loongarch_ss.add(files('loongarch_int.c'))
-loongarch_ss.add(when: 'CONFIG_LOONGSON_3A5000', if_true: files('ls3a5000_virt.c'))
+loongarch_ss.add(when: 'CONFIG_LOONGSON_3A5000', if_true: files('ls3a5000_virt.c', 'ipi.c'))
 
 hw_arch += {'loongarch': loongarch_ss}
diff --git a/include/hw/loongarch/gipi.h b/include/hw/loongarch/gipi.h
new file mode 100644
index 0000000000..244d4e3ecf
--- /dev/null
+++ b/include/hw/loongarch/gipi.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch ipi interrupt header files
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef HW_LOONGARCH_GIPI_H
+#define HW_LOONGARCH_GIPI_H
+
+#define SMP_GIPI_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 MAX_GIPI_CORE_NUM      4
+#define MAX_GIPI_MBX_NUM       4
+
+typedef struct gipi_core {
+    uint32_t status;
+    uint32_t en;
+    uint32_t set;
+    uint32_t clear;
+    uint64_t buf[MAX_GIPI_MBX_NUM];
+    qemu_irq irq;
+} gipi_core;
+
+typedef struct gipiState {
+    gipi_core core[MAX_GIPI_CORE_NUM];
+} gipiState;
+
+#endif
diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
index 8538697e5f..54cc875e6d 100644
--- a/include/hw/loongarch/loongarch.h
+++ b/include/hw/loongarch/loongarch.h
@@ -12,6 +12,7 @@
 #include "qemu-common.h"
 #include "hw/boards.h"
 #include "qemu/queue.h"
+#include "hw/loongarch/gipi.h"
 
 #define LOONGARCH_MAX_VCPUS     4
 #define PM_MMIO_ADDR            0x10080000UL
@@ -38,6 +39,8 @@ typedef struct LoongArchMachineState {
 
     AddressSpace *address_space_iocsr;
     MemoryRegion *system_iocsr;
+
+    gipiState   *gipi;
 } LoongArchMachineState;
 
 #define TYPE_LOONGARCH_MACHINE  MACHINE_TYPE_NAME("loongson7a")
@@ -45,4 +48,5 @@ DECLARE_INSTANCE_CHECKER(LoongArchMachineState, LOONGARCH_MACHINE,
                          TYPE_LOONGARCH_MACHINE)
 
 void cpu_loongarch_init_irq(LoongArchCPU *cpu);
+int cpu_init_ipi(LoongArchMachineState *lams, qemu_irq irq, int cpu);
 #endif
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 77afe9e26a..b7ef0b8b3c 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -152,6 +152,7 @@ extern const char * const fregnames[];
 
 #define N_IRQS      14
 #define IRQ_TIMER   11
+#define IRQ_IPI     12
 
 /*
  * LoongArch cpu has 4 priv level, now only 2 mode used.
-- 
2.27.0



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

* [RFC PATCH v2 19/30] hw/intc: Add LoongArch ls7a interrupt controller support(PCH-PIC)
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (17 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 18/30] hw/loongarch: Add LoongArch ipi interrupt support(IPI) Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 14:37   ` Mark Cave-Ayland
  2021-11-11  1:35 ` [RFC PATCH v2 20/30] hw/intc: Add LoongArch ls7a msi interrupt controller support(PCH-MSI) Xiaojuan Yang
                   ` (11 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This patch realize the PCH-PIC interrupt controller.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/intc/Kconfig                     |   4 +
 hw/intc/loongarch_pch_pic.c         | 283 ++++++++++++++++++++++++++++
 hw/intc/meson.build                 |   1 +
 hw/loongarch/Kconfig                |   1 +
 include/hw/intc/loongarch_pch_pic.h |  49 +++++
 5 files changed, 338 insertions(+)
 create mode 100644 hw/intc/loongarch_pch_pic.c
 create mode 100644 include/hw/intc/loongarch_pch_pic.h

diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index 78aed93c45..3b7eca7b03 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -73,3 +73,7 @@ config GOLDFISH_PIC
 
 config M68K_IRQC
     bool
+
+config LOONGARCH_PCH_PIC
+    bool
+    select UNIMP
diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c
new file mode 100644
index 0000000000..96e4c46174
--- /dev/null
+++ b/hw/intc/loongarch_pch_pic.c
@@ -0,0 +1,283 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU Loongson 7A1000 I/O interrupt controller.
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "hw/irq.h"
+#include "hw/intc/loongarch_pch_pic.h"
+#include "migration/vmstate.h"
+
+#define DEBUG_LOONGARCH_PCH_PIC 0
+
+#define DPRINTF(fmt, ...) \
+do { \
+    if (DEBUG_LOONGARCH_PCH_PIC) { \
+        fprintf(stderr, "LOONGARCH_PCH_PIC: " fmt , ## __VA_ARGS__); \
+    } \
+} while (0)
+
+ #define for_each_set_bit(bit, addr, size) \
+         for ((bit) = find_first_bit((addr), (size));            \
+              (bit) < (size);                                    \
+              (bit) = find_next_bit((addr), (size), (bit) + 1))
+
+static void update_irq(loongarch_pch_pic *s, int mask, int level)
+{
+    int i;
+    uint64_t val;
+    val = mask & s->intirr & (~s->int_mask);
+
+    for_each_set_bit(i, &val, 32) {
+        if (level == 1) {
+            if ((s->intisr & (0x1ULL << i)) == 0) {
+                s->intisr |= 1ULL << i;
+                qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 1);
+            }
+        } else if (level == 0) {
+            if (s->intisr & (0x1ULL << i)) {
+                s->intisr &= ~(0x1ULL << i);
+                qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 0);
+            }
+        }
+    }
+}
+
+static void irq_handler(void *opaque, int irq, int level)
+{
+    loongarch_pch_pic *s = opaque;
+
+    assert(irq < 32);
+    uint32_t mask = 1ULL << irq;
+    DPRINTF("------ %s irq %d %d\n", __func__, irq, level);
+
+    if (s->intedge & mask) {
+        /* Edge triggered */
+        if (level) {
+            if ((s->last_intirr & mask) == 0) {
+                s->intirr |= mask;
+            }
+            s->last_intirr |= mask;
+        } else {
+            s->last_intirr &= ~mask;
+        }
+    } else {
+        /* Level triggered */
+        if (level) {
+            s->intirr |= mask;
+            s->last_intirr |= mask;
+        } else {
+            s->intirr &= ~mask;
+            s->last_intirr &= ~mask;
+        }
+
+    }
+    update_irq(s, mask, level);
+}
+
+static uint64_t loongarch_pch_pic_reg_read(void *opaque, hwaddr addr,
+                                           unsigned size)
+{
+    loongarch_pch_pic *s = opaque;
+    uint32_t val = 0;
+    uint32_t offset;
+    int32_t offset_tmp;
+    offset = addr & 0xfff;
+    if (4 == size) {
+        switch (offset) {
+        case PCH_PIC_INT_ID_OFFSET:
+            val = PCH_PIC_INT_ID_VAL;
+            break;
+        case PCH_PIC_INT_MASK_OFFSET:
+            val = s->int_mask;
+            break;
+        case PCH_PIC_INT_STATUS_OFFSET:
+            val = s->intisr & (~s->int_mask);
+            break;
+        case PCH_PIC_INT_EDGE_OFFSET:
+            val = s->intedge;
+            break;
+        case PCH_PIC_INT_POL_OFFSET:
+            val = s->int_polarity;
+            break;
+        case PCH_PIC_HTMSI_EN_OFFSET:
+            val = s->htmsi_en;
+            break;
+        case PCH_PIC_AUTO_CTRL0_OFFSET:
+        case PCH_PIC_AUTO_CTRL1_OFFSET:
+            break;
+        default:
+            break;
+        }
+    } else if (1 == size) {
+        if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) {
+            offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
+            if (offset_tmp >= 0 && offset_tmp < 32) {
+                val = s->htmsi_vector[offset_tmp];
+            }
+        } else if (offset >=  PCH_PIC_ROUTE_ENTRY_OFFSET) {
+            offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
+            if (offset_tmp >= 0 && offset_tmp < 32) {
+                val = s->route_entry[offset_tmp];
+            }
+        }
+    }
+
+    return val;
+}
+
+static void loongarch_pch_pic_reg_write(void *opaque, hwaddr addr,
+                                        uint64_t data, unsigned size)
+{
+    loongarch_pch_pic *s = opaque;
+    int32_t offset_tmp;
+    uint32_t offset, old;
+    offset = addr & 0xfff;
+
+    if (4 == size) {
+        switch (offset) {
+        case PCH_PIC_INT_MASK_OFFSET:
+            old = s->int_mask;
+            s->int_mask = data;
+            if (old & ~data) {
+                update_irq(s, (old & ~data), 1);
+            } else if (~old & data) {
+                update_irq(s, (~old & data), 0);
+            }
+            break;
+        case PCH_PIC_INT_STATUS_OFFSET:
+            s->intisr = data;
+            break;
+        case PCH_PIC_INT_EDGE_OFFSET:
+            s->intedge = data;
+            break;
+        case PCH_PIC_INT_CLEAR_OFFSET:
+            s->intirr &= (~(data & s->intedge));
+            update_irq(s, data, 0);
+            s->intisr &= (~data);
+            break;
+        case PCH_PIC_INT_POL_OFFSET:
+            s->int_polarity = data;
+            break;
+        case PCH_PIC_HTMSI_EN_OFFSET:
+            s->htmsi_en = data;
+            break;
+        case PCH_PIC_AUTO_CTRL0_OFFSET:
+        case PCH_PIC_AUTO_CTRL1_OFFSET:
+            break;
+        default:
+            break;
+        }
+    } else if (1 == size) {
+        if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) {
+            offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
+            if (offset_tmp >= 0 && offset_tmp < 32) {
+                s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
+            }
+        } else if (offset >=  PCH_PIC_ROUTE_ENTRY_OFFSET) {
+            offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
+            if (offset_tmp >= 0 && offset_tmp < 32) {
+                s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
+            }
+        }
+    }
+}
+
+static const MemoryRegionOps loongarch_pch_pic_ops = {
+    .read = loongarch_pch_pic_reg_read,
+    .write = loongarch_pch_pic_reg_write,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void loongarch_pch_pic_reset(DeviceState *d)
+{
+    loongarch_pch_pic *s = LOONGARCH_PCH_PIC(d);
+    int i;
+
+    s->int_id   = 0x0;
+    s->int_mask = 0xffffffff;
+    s->htmsi_en = 0x0;
+    s->intedge  = 0x0;
+    s->intclr   = 0x0;
+    s->auto_crtl0 = 0x0;
+    s->auto_crtl1 = 0x0;
+    for (i = 0; i < 32; i++) {
+        s->route_entry[i] = 0x1;
+        s->htmsi_vector[i] = 0x0;
+    }
+    s->intirr = 0x0;
+    s->intisr = 0x0;
+    s->last_intirr = 0x0;
+    s->int_polarity = 0x0;
+}
+
+static void loongarch_pch_pic_init(Object *obj)
+{
+    loongarch_pch_pic *s = LOONGARCH_PCH_PIC(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    int tmp;
+
+    memory_region_init_io(&s->iomem, obj, &loongarch_pch_pic_ops,
+                          s, TYPE_LOONGARCH_PCH_PIC, 0x1000);
+    sysbus_init_mmio(sbd, &s->iomem);
+
+    for (tmp = 0; tmp < 32; tmp++) {
+        sysbus_init_irq(sbd, &s->parent_irq[tmp]);
+    }
+    qdev_init_gpio_in(DEVICE(obj), irq_handler, 32);
+}
+
+static const VMStateDescription vmstate_loongarch_pch_pic = {
+    .name = TYPE_LOONGARCH_PCH_PIC,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(int_mask, loongarch_pch_pic),
+        VMSTATE_UINT32(htmsi_en, loongarch_pch_pic),
+        VMSTATE_UINT32(intedge, loongarch_pch_pic),
+        VMSTATE_UINT32(intclr, loongarch_pch_pic),
+        VMSTATE_UINT32(auto_crtl0, loongarch_pch_pic),
+        VMSTATE_UINT32(auto_crtl1, loongarch_pch_pic),
+        VMSTATE_UINT8_ARRAY(route_entry, loongarch_pch_pic, 32),
+        VMSTATE_UINT8_ARRAY(htmsi_vector, loongarch_pch_pic, 32),
+        VMSTATE_UINT32(last_intirr, loongarch_pch_pic),
+        VMSTATE_UINT32(intirr, loongarch_pch_pic),
+        VMSTATE_UINT32(intisr, loongarch_pch_pic),
+        VMSTATE_UINT32(int_polarity, loongarch_pch_pic),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = loongarch_pch_pic_reset;
+    dc->vmsd = &vmstate_loongarch_pch_pic;
+}
+
+static const TypeInfo loongarch_pch_pic_info = {
+    .name          = TYPE_LOONGARCH_PCH_PIC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(loongarch_pch_pic),
+    .instance_init = loongarch_pch_pic_init,
+    .class_init    = loongarch_pch_pic_class_init,
+};
+
+static void loongarch_pch_pic_register_types(void)
+{
+    type_register_static(&loongarch_pch_pic_info);
+}
+
+type_init(loongarch_pch_pic_register_types)
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index c89d2ca180..07b0627468 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -57,3 +57,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
 		if_true: files('spapr_xive_kvm.c'))
 specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c'))
 specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
+specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c'))
diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
index 720822f32c..c6d7ebcd5b 100644
--- a/hw/loongarch/Kconfig
+++ b/hw/loongarch/Kconfig
@@ -1,3 +1,4 @@
 config LOONGSON_3A5000
     bool
     select PCI_EXPRESS_7A
+    select LOONGARCH_PCH_PIC
diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h
new file mode 100644
index 0000000000..b1b3e24166
--- /dev/null
+++ b/include/hw/intc/loongarch_pch_pic.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch 7A1000 I/O interrupt controller definitions
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ */
+
+#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic"
+DECLARE_INSTANCE_CHECKER(struct loongarch_pch_pic, LOONGARCH_PCH_PIC,
+                         TYPE_LOONGARCH_PCH_PIC)
+
+#define PCH_PIC_ROUTE_ENTRY_OFFSET      0x100
+#define PCH_PIC_INT_ID_OFFSET           0x00
+#define PCH_PIC_INT_ID_VAL              0x7000000UL
+#define PCH_PIC_INT_ID_VER              0x1f0001UL
+#define PCH_PIC_INT_MASK_OFFSET         0x20
+#define PCH_PIC_INT_EDGE_OFFSET         0x60
+#define PCH_PIC_INT_CLEAR_OFFSET        0x80
+#define PCH_PIC_INT_STATUS_OFFSET       0x3a0
+#define PCH_PIC_INT_POL_OFFSET          0x3e0
+#define PCH_PIC_HTMSI_EN_OFFSET         0x40
+#define PCH_PIC_HTMSI_VEC_OFFSET        0x200
+#define PCH_PIC_AUTO_CTRL0_OFFSET       0xc0
+#define PCH_PIC_AUTO_CTRL1_OFFSET       0xe0
+
+typedef struct loongarch_pch_pic {
+    SysBusDevice parent_obj;
+    qemu_irq parent_irq[32];
+    uint32_t int_id;
+    uint32_t int_mask; /*0x020 interrupt mask register*/
+    uint32_t htmsi_en;/*0x040 1=msi*/
+    uint32_t intedge; /*0x060 edge=1 level  =0*/
+    uint32_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/
+    uint32_t auto_crtl0; /*0x0c0*/
+    uint32_t auto_crtl1; /*0x0e0*/
+    uint8_t route_entry[32]; /*0x100 - 0x120*/
+    uint8_t htmsi_vector[32]; /*0x200 - 0x220*/
+    uint32_t last_intirr;    /* edge detection */
+    uint32_t intirr; /* 0x380 interrupt request register */
+    uint32_t intisr; /* 0x3a0 interrupt service register */
+    /*
+     * 0x3e0 interrupt level polarity selection
+     * register 0 for high level trigger
+     */
+    uint32_t int_polarity;
+    MemoryRegion iomem;
+} loongarch_pch_pic;
+
+
-- 
2.27.0



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

* [RFC PATCH v2 20/30] hw/intc: Add LoongArch ls7a msi interrupt controller support(PCH-MSI)
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (18 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 19/30] hw/intc: Add LoongArch ls7a interrupt controller support(PCH-PIC) Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 14:40   ` Mark Cave-Ayland
  2021-11-11  1:35 ` [RFC PATCH v2 21/30] hw/intc: Add LoongArch extioi interrupt controller(EIOINTC) Xiaojuan Yang
                   ` (10 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This patch realize PCH-MSI interrupt controller.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/intc/Kconfig                     |  5 ++
 hw/intc/loongarch_pch_msi.c         | 73 +++++++++++++++++++++++++++++
 hw/intc/meson.build                 |  1 +
 hw/loongarch/Kconfig                |  1 +
 include/hw/intc/loongarch_pch_msi.h | 16 +++++++
 5 files changed, 96 insertions(+)
 create mode 100644 hw/intc/loongarch_pch_msi.c
 create mode 100644 include/hw/intc/loongarch_pch_msi.h

diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index 3b7eca7b03..c0dc12dfa0 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -77,3 +77,8 @@ config M68K_IRQC
 config LOONGARCH_PCH_PIC
     bool
     select UNIMP
+
+config LOONGARCH_PCH_MSI
+    select MSI_NONBROKEN
+    bool
+    select UNIMP
diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c
new file mode 100644
index 0000000000..1d8a3c1b21
--- /dev/null
+++ b/hw/intc/loongarch_pch_msi.c
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU Loongson 7A1000 msi interrupt controller.
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "hw/irq.h"
+#include "hw/intc/loongarch_pch_msi.h"
+#include "hw/pci/msi.h"
+#include "hw/misc/unimp.h"
+#include "migration/vmstate.h"
+
+#define DEBUG_LOONGARCH_PCH_MSI 0
+
+#define DPRINTF(fmt, ...) \
+do { \
+    if (DEBUG_LOONGARCH_PCH_MSI) { \
+        fprintf(stderr, "LOONGARCH_PCH_MSI: " fmt , ## __VA_ARGS__); \
+    } \
+} while (0)
+
+static uint64_t loongarch_msi_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    return 0;
+}
+
+static void loongarch_msi_mem_write(void *opaque, hwaddr addr,
+                                    uint64_t val, unsigned size)
+{
+    loongarch_pch_msi *s = opaque;
+    int irq_num = val & 0xff;
+
+    qemu_set_irq(s->pch_msi_irq[irq_num - 32], 1);
+}
+
+static const MemoryRegionOps loongarch_pch_msi_ops = {
+    .read  = loongarch_msi_mem_read,
+    .write = loongarch_msi_mem_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void loongarch_pch_msi_init(Object *obj)
+{
+    loongarch_pch_msi *s = LOONGARCH_PCH_MSI(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    int tmp;
+
+    memory_region_init_io(&s->msi_mmio, obj, &loongarch_pch_msi_ops,
+                          s, TYPE_LOONGARCH_PCH_MSI, 0x8);
+    sysbus_init_mmio(sbd, &s->msi_mmio);
+    msi_nonbroken = true;
+
+    for (tmp = 0; tmp < 224; tmp++) {
+        sysbus_init_irq(sbd, &s->pch_msi_irq[tmp]);
+    }
+}
+
+static const TypeInfo loongarch_pch_msi_info = {
+    .name          = TYPE_LOONGARCH_PCH_MSI,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(loongarch_pch_msi),
+    .instance_init = loongarch_pch_msi_init,
+};
+
+static void loongarch_pch_msi_register_types(void)
+{
+    type_register_static(&loongarch_pch_msi_info);
+}
+
+type_init(loongarch_pch_msi_register_types)
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index 07b0627468..e04abe2d56 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -58,3 +58,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
 specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c'))
 specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
 specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c'))
+specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c'))
diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
index c6d7ebcd5b..4500fd3a57 100644
--- a/hw/loongarch/Kconfig
+++ b/hw/loongarch/Kconfig
@@ -2,3 +2,4 @@ config LOONGSON_3A5000
     bool
     select PCI_EXPRESS_7A
     select LOONGARCH_PCH_PIC
+    select LOONGARCH_PCH_MSI
diff --git a/include/hw/intc/loongarch_pch_msi.h b/include/hw/intc/loongarch_pch_msi.h
new file mode 100644
index 0000000000..40f0575bb5
--- /dev/null
+++ b/include/hw/intc/loongarch_pch_msi.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch 7A1000 I/O interrupt controller definitions
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#define TYPE_LOONGARCH_PCH_MSI "loongarch_pch_msi"
+DECLARE_INSTANCE_CHECKER(struct loongarch_pch_msi, LOONGARCH_PCH_MSI,
+                         TYPE_LOONGARCH_PCH_MSI)
+
+typedef struct loongarch_pch_msi {
+    SysBusDevice parent_obj;
+    qemu_irq pch_msi_irq[224];
+    MemoryRegion msi_mmio;
+} loongarch_pch_msi;
-- 
2.27.0



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

* [RFC PATCH v2 21/30] hw/intc: Add LoongArch extioi interrupt controller(EIOINTC)
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (19 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 20/30] hw/intc: Add LoongArch ls7a msi interrupt controller support(PCH-MSI) Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 14:49   ` Mark Cave-Ayland
  2021-11-11  1:35 ` [RFC PATCH v2 22/30] hw/loongarch: Add irq hierarchy for the system Xiaojuan Yang
                   ` (9 subsequent siblings)
  30 siblings, 1 reply; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This patch realize the EIOINTC interrupt controller.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/intc/Kconfig                    |   3 +
 hw/intc/loongarch_extioi.c         | 570 +++++++++++++++++++++++++++++
 hw/intc/meson.build                |   1 +
 hw/loongarch/Kconfig               |   1 +
 include/hw/intc/loongarch_extioi.h |  99 +++++
 include/hw/loongarch/loongarch.h   |   1 +
 6 files changed, 675 insertions(+)
 create mode 100644 hw/intc/loongarch_extioi.c
 create mode 100644 include/hw/intc/loongarch_extioi.h

diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index c0dc12dfa0..a2d9efd5aa 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -82,3 +82,6 @@ config LOONGARCH_PCH_MSI
     select MSI_NONBROKEN
     bool
     select UNIMP
+
+config LOONGARCH_EXTIOI
+    bool
diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c
new file mode 100644
index 0000000000..592cd8d1e2
--- /dev/null
+++ b/hw/intc/loongarch_extioi.c
@@ -0,0 +1,570 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Loongson 3A5000 ext interrupt controller emulation
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/log.h"
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+#include "hw/loongarch/loongarch.h"
+#include "hw/qdev-properties.h"
+#include "exec/address-spaces.h"
+#include "hw/intc/loongarch_extioi.h"
+#include "migration/vmstate.h"
+
+#define DEBUG_APIC 0
+
+#define DPRINTF(fmt, ...) \
+do { \
+    if (DEBUG_APIC) { \
+        fprintf(stderr, "APIC: " fmt , ## __VA_ARGS__); \
+    } \
+} while (0)
+
+static void extioi_update_irq(void *opaque, int irq_num, int level)
+{
+    loongarch_extioi *s = opaque;
+    uint8_t  ipnum, cpu;
+    unsigned long found1, found2;
+
+    ipnum = s->sw_ipmap[irq_num];
+    cpu   = s->sw_coremap[irq_num];
+    if (level == 1) {
+        if (test_bit(irq_num, (void *)s->en_reg8) == false) {
+            return;
+        }
+        bitmap_set((void *)s->coreisr_reg8[cpu], irq_num, 1);
+        found1 = find_next_bit((void *)&(s->sw_ipisr[cpu][ipnum]),
+                               EXTIOI_IRQS, 0);
+        bitmap_set((void *)&(s->sw_ipisr[cpu][ipnum]), irq_num, 1);
+
+        if (found1 >= EXTIOI_IRQS) {
+            qemu_set_irq(s->parent_irq[cpu][ipnum], level);
+        }
+    } else {
+        bitmap_clear((void *)s->coreisr_reg8[cpu], irq_num, 1);
+        found1 = find_next_bit((void *)&(s->sw_ipisr[cpu][ipnum]),
+                               EXTIOI_IRQS, 0);
+        bitmap_clear((void *)&(s->sw_ipisr[cpu][ipnum]), irq_num, 1);
+        found2 = find_next_bit((void *)&(s->sw_ipisr[cpu][ipnum]),
+                               EXTIOI_IRQS, 0);
+
+        if ((found1 < EXTIOI_IRQS) && (found2 >= EXTIOI_IRQS)) {
+            qemu_set_irq(s->parent_irq[cpu][ipnum], level);
+        }
+    }
+}
+
+static void extioi_setirq(void *opaque, int irq, int level)
+{
+    loongarch_extioi *s = opaque;
+    extioi_update_irq(s, irq, level);
+}
+
+static void extioi_handler(void *opaque, int irq, int level)
+{
+    loongarch_extioi *extioi = (loongarch_extioi *)opaque;
+
+    qemu_set_irq(extioi->irq[irq], level);
+}
+
+static uint32_t extioi_readb(void *opaque, hwaddr addr)
+{
+    loongarch_extioi *state = opaque;
+    unsigned long offset, reg_count;
+    uint8_t ret;
+    int cpu;
+
+    offset = addr & 0xffff;
+
+    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
+        reg_count = (offset - EXTIOI_ENABLE_START);
+        ret = state->en_reg8[reg_count];
+    } else if ((offset >= EXTIOI_BOUNCE_START) &&
+               (offset < EXTIOI_BOUNCE_END)) {
+        reg_count = (offset - EXTIOI_BOUNCE_START);
+        ret = state->bounce_reg8[reg_count];
+    } else if ((offset >= EXTIOI_COREISR_START) &&
+               (offset < EXTIOI_COREISR_END)) {
+        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f);
+        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
+        ret = state->coreisr_reg8[cpu][reg_count];
+    } else if ((offset >= EXTIOI_IPMAP_START) &&
+               (offset < EXTIOI_IPMAP_END)) {
+        reg_count = (offset - EXTIOI_IPMAP_START);
+        ret = state->ipmap_reg8[reg_count];
+    } else if ((offset >= EXTIOI_COREMAP_START) &&
+               (offset < EXTIOI_COREMAP_END)) {
+        reg_count = (offset - EXTIOI_COREMAP_START);
+        ret = state->coremap_reg8[reg_count];
+    } else if ((offset >= EXTIOI_NODETYPE_START) &&
+               (offset < EXTIOI_NODETYPE_END)) {
+        reg_count = (offset - EXTIOI_NODETYPE_START);
+        ret = state->nodetype_reg8[reg_count];
+    }
+
+    DPRINTF("readb reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
+    return ret;
+}
+
+static uint32_t extioi_readw(void *opaque, hwaddr addr)
+{
+    loongarch_extioi *state = opaque;
+    unsigned long offset, reg_count;
+    uint32_t ret;
+    int cpu;
+
+    offset = addr & 0xffff;
+
+    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
+        reg_count = (offset - EXTIOI_ENABLE_START) / 4;
+        ret = state->en_reg32[reg_count];
+    } else if ((offset >= EXTIOI_BOUNCE_START) &&
+               (offset < EXTIOI_BOUNCE_END)) {
+        reg_count = (offset - EXTIOI_BOUNCE_START) / 4;
+        ret = state->bounce_reg32[reg_count];
+    } else if ((offset >= EXTIOI_COREISR_START) &&
+               (offset < EXTIOI_COREISR_END)) {
+        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 4;
+        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
+        ret = state->coreisr_reg32[cpu][reg_count];
+    } else if ((offset >= EXTIOI_IPMAP_START) &&
+               (offset < EXTIOI_IPMAP_END)) {
+        reg_count = (offset - EXTIOI_IPMAP_START) / 4;
+        ret = state->ipmap_reg32[reg_count];
+    } else if ((offset >= EXTIOI_COREMAP_START) &&
+               (offset < EXTIOI_COREMAP_END)) {
+        reg_count = (offset - EXTIOI_COREMAP_START) / 4;
+        ret = state->coremap_reg32[reg_count];
+    } else if ((offset >= EXTIOI_NODETYPE_START) &&
+               (offset < EXTIOI_NODETYPE_END)) {
+        reg_count = (offset - EXTIOI_NODETYPE_START) / 4;
+        ret = state->nodetype_reg32[reg_count];
+    }
+
+    DPRINTF("readw reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
+    return ret;
+}
+
+static uint64_t extioi_readl(void *opaque, hwaddr addr)
+{
+    loongarch_extioi *state = opaque;
+    unsigned long offset, reg_count;
+    uint64_t ret;
+    int cpu;
+
+    offset = addr & 0xffff;
+
+    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
+        reg_count = (offset - EXTIOI_ENABLE_START) / 8;
+        ret = state->en_reg64[reg_count];
+    } else if ((offset >= EXTIOI_BOUNCE_START) &&
+               (offset < EXTIOI_BOUNCE_END)) {
+        reg_count = (offset - EXTIOI_BOUNCE_START) / 8;
+        ret = state->bounce_reg64[reg_count];
+    } else if ((offset >= EXTIOI_COREISR_START) &&
+               (offset < EXTIOI_COREISR_END)) {
+        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 8;
+        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
+        ret = state->coreisr_reg64[cpu][reg_count];
+    } else if ((offset >= EXTIOI_IPMAP_START) &&
+               (offset < EXTIOI_IPMAP_END)) {
+        ret = state->ipmap_reg64;
+    } else if ((offset >= EXTIOI_COREMAP_START) &&
+               (offset < EXTIOI_COREMAP_END)) {
+        reg_count = (offset - EXTIOI_COREMAP_START) / 8;
+        ret = state->coremap_reg64[reg_count];
+    } else if ((offset >= EXTIOI_NODETYPE_START) &&
+               (offset < EXTIOI_NODETYPE_END)) {
+        reg_count = (offset - EXTIOI_NODETYPE_START) / 8;
+        ret = state->nodetype_reg64[reg_count];
+    }
+
+    DPRINTF("readl reg 0x" TARGET_FMT_plx " = %lx\n", addr, ret);
+    return ret;
+}
+
+static void extioi_writeb(void *opaque, hwaddr addr, uint32_t val)
+{
+    loongarch_extioi *state = opaque;
+    unsigned long offset, reg_count;
+    uint8_t old_data_u8;
+    int cpu, i, ipnum, level, mask, irqnum;
+
+    offset = addr & 0xffff;
+    val = val & 0xffUL;
+
+    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
+        reg_count = (offset - EXTIOI_ENABLE_START);
+        old_data_u8 = state->en_reg8[reg_count];
+        if (old_data_u8 != val) {
+            state->en_reg8[reg_count] = val;
+            old_data_u8 = old_data_u8 ^ val;
+            mask = 0x1;
+
+            for (i = 0; i < 8; i++) {
+                if (old_data_u8 & mask) {
+                    level = !!(val & (0x1 << i));
+                    extioi_update_irq(state, i + reg_count * 8, level);
+                }
+                mask = mask << 1;
+            }
+        }
+    } else if ((offset >= EXTIOI_BOUNCE_START) &&
+               (offset < EXTIOI_BOUNCE_END)) {
+        reg_count = (offset - EXTIOI_BOUNCE_START);
+        state->bounce_reg8[reg_count] = val;
+    } else if ((offset >= EXTIOI_ISR_START) && (offset < EXTIOI_ISR_END)) {
+        /* Can not be writen */
+        reg_count = (offset - EXTIOI_ISR_START) & 0x1f;
+        old_data_u8 = state->isr_reg8[reg_count];
+        state->isr_reg8[reg_count] = old_data_u8 & (~val);
+
+        mask = 0x1;
+        for (i = 0; i < 8; i++) {
+            if ((old_data_u8 & mask) && (val & mask)) {
+                extioi_update_irq(state, i + reg_count * 8, 0);
+            }
+            mask = mask << 1;
+        }
+    } else if ((offset >= EXTIOI_COREISR_START) &&
+               (offset < EXTIOI_COREISR_END)) {
+        reg_count = (offset - EXTIOI_COREISR_START) & 0x1f;
+        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
+
+        /* ext_isr */
+        old_data_u8 = state->isr_reg8[reg_count];
+        state->isr_reg8[reg_count] = old_data_u8 & (~val);
+
+        old_data_u8 = state->coreisr_reg8[cpu][reg_count];
+        state->coreisr_reg8[cpu][reg_count] = old_data_u8 & (~val);
+
+        if (old_data_u8 != state->coreisr_reg8[cpu][reg_count]) {
+            mask = 0x1;
+            for (i = 0; i < 8; i++) {
+                if ((old_data_u8 & mask) && (val & mask)) {
+                    extioi_update_irq(state, i + reg_count * 8, 0);
+                }
+                mask = mask << 1;
+            }
+        }
+    } else if ((offset >= EXTIOI_IPMAP_START) && (offset < EXTIOI_IPMAP_END)) {
+        /* Drop arch.core_ip_mask use state->ipmap */
+        reg_count = (offset - EXTIOI_IPMAP_START);
+        state->ipmap_reg8[reg_count] = val;
+
+        ipnum = 0;
+        for (i = 0; i < 4; i++) {
+            if (val & (0x1 << i)) {
+                ipnum = i;
+                break;
+            }
+        }
+
+        if (val) {
+            for (i = 0; i < 32; i++) {
+                irqnum = reg_count * 32 + i;
+                state->sw_ipmap[irqnum] = ipnum;
+            }
+        } else {
+            for (i = 0; i < 32; i++) {
+                irqnum = reg_count * 32 + i;
+                state->sw_ipmap[irqnum] = 0;
+            }
+        }
+    } else if ((offset >= EXTIOI_COREMAP_START) &&
+               (offset < EXTIOI_COREMAP_END)) {
+        reg_count = (offset - EXTIOI_COREMAP_START);
+        cpu = val & 0xf;
+
+        /* Node map different from kernel */
+        if (cpu) {
+            cpu = ctz32(cpu);
+            state->coremap_reg8[reg_count] = val;
+            state->sw_coremap[reg_count] = cpu;
+        }
+    } else if ((offset >= EXTIOI_NODETYPE_START) &&
+               (offset < EXTIOI_NODETYPE_END)) {
+        reg_count = (offset - EXTIOI_NODETYPE_START);
+        state->nodetype_reg8[reg_count] = val;
+    }
+
+    DPRINTF("writeb reg 0x" TARGET_FMT_plx " = %x\n", addr, val);
+}
+
+static void extioi_writew(void *opaque, hwaddr addr, uint32_t val)
+{
+    loongarch_extioi *state = opaque;
+    int cpu, level;
+    uint32_t offset, old_data_u32, reg_count, mask, i;
+
+    offset = addr & 0xffff;
+
+    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
+        reg_count = (offset - EXTIOI_ENABLE_START) / 4;
+        old_data_u32 = state->en_reg32[reg_count];
+        if (old_data_u32 != val) {
+            state->en_reg32[reg_count] = val;
+            old_data_u32 = old_data_u32 ^ val;
+
+            mask = 0x1;
+            for (i = 0; i < 8 * sizeof(old_data_u32); i++) {
+                if (old_data_u32 & mask) {
+                    level = !!(val & (0x1 << i));
+                    extioi_update_irq(state, i + reg_count * 32, level);
+                }
+                mask = mask << 1;
+            }
+        }
+    } else if ((offset >= EXTIOI_BOUNCE_START) &&
+               (offset < EXTIOI_BOUNCE_END)) {
+        reg_count = (offset - EXTIOI_BOUNCE_START) / 4;
+        state->bounce_reg32[reg_count] = val;
+    } else if ((offset >= EXTIOI_COREISR_START) &&
+               (offset < EXTIOI_COREISR_END)) {
+        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 4;
+        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
+
+        /* Ext_isr */
+        old_data_u32 = state->isr_reg32[reg_count];
+        state->isr_reg32[reg_count] = old_data_u32 & (~val);
+
+        /* Ext_core_ioisr */
+        old_data_u32 = state->coreisr_reg32[cpu][reg_count];
+        state->coreisr_reg32[cpu][reg_count] = old_data_u32 & (~val);
+
+        if (old_data_u32 != state->coreisr_reg32[cpu][reg_count]) {
+            mask = 0x1;
+            for (i = 0; i < 8 * sizeof(old_data_u32); i++) {
+                if ((old_data_u32 & mask) && (val & mask)) {
+                    extioi_update_irq(state, i + reg_count * 8, 0);
+                }
+                mask = mask << 1;
+            }
+        }
+    } else if ((offset >= EXTIOI_IPMAP_START) &&
+               (offset < EXTIOI_IPMAP_END)) {
+        extioi_writeb(opaque, addr, (val) & 0xff);
+        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+    } else if ((offset >= EXTIOI_COREMAP_START) &&
+               (offset < EXTIOI_COREMAP_END)) {
+        extioi_writeb(opaque, addr, (val) & 0xff);
+        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+    } else if ((offset >= EXTIOI_NODETYPE_START) &&
+               (offset < EXTIOI_NODETYPE_END)) {
+        reg_count = (offset - EXTIOI_NODETYPE_START) / 4;
+        state->nodetype_reg32[reg_count] = val;
+    }
+
+    DPRINTF("writew reg 0x" TARGET_FMT_plx " = %x\n", addr, val);
+}
+
+static void extioi_writel(void *opaque, hwaddr addr, uint64_t val)
+{
+    loongarch_extioi *state = (loongarch_extioi *)opaque;
+    int cpu, level;
+    uint64_t offset, old_data_u64, reg_count, mask, i;
+
+    offset = addr & 0xffff;
+
+    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
+        reg_count = (offset - EXTIOI_ENABLE_START) / 8;
+        old_data_u64 = state->en_reg64[reg_count];
+        if (old_data_u64 != val) {
+            state->en_reg64[reg_count] = val;
+            old_data_u64 = old_data_u64 ^ val;
+            mask = 0x1;
+            for (i = 0; i < 8 * sizeof(old_data_u64); i++) {
+                if (old_data_u64 & mask) {
+                    level = !!(val & (0x1 << i));
+                    extioi_update_irq(state, i + reg_count * 64, level);
+                }
+                mask = mask << 1;
+            }
+        }
+    } else if ((offset >= EXTIOI_BOUNCE_START) &&
+               (offset < EXTIOI_BOUNCE_END)) {
+        reg_count = (offset - EXTIOI_BOUNCE_START) / 8;
+        state->bounce_reg64[reg_count] = val;
+    } else if ((offset >= EXTIOI_COREISR_START) &&
+               (offset < EXTIOI_COREISR_END)) {
+        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 8;
+        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
+
+        /* core_ext_ioisr */
+        old_data_u64 = state->coreisr_reg64[cpu][reg_count];
+        state->coreisr_reg64[cpu][reg_count] = old_data_u64 & (~val);
+
+        if (old_data_u64 != state->coreisr_reg64[cpu][reg_count]) {
+            mask = 0x1;
+            for (i = 0; i < 8 * sizeof(old_data_u64); i++) {
+                if ((old_data_u64 & mask) && (val & mask)) {
+                    extioi_update_irq(state, i + reg_count * 64, 0);
+                }
+                mask = mask << 1;
+            }
+        }
+    } else if ((offset >= EXTIOI_IPMAP_START) &&
+               (offset < EXTIOI_IPMAP_END)) {
+        extioi_writeb(opaque, addr, (val) & 0xff);
+        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+        extioi_writeb(opaque, addr + 4, (val >> 32) & 0xff);
+        extioi_writeb(opaque, addr + 5, (val >> 40) & 0xff);
+        extioi_writeb(opaque, addr + 6, (val >> 48) & 0xff);
+        extioi_writeb(opaque, addr + 7, (val >> 56) & 0xff);
+    } else if ((offset >= EXTIOI_COREMAP_START) &&
+               (offset < EXTIOI_COREMAP_END)) {
+        extioi_writeb(opaque, addr, (val) & 0xff);
+        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+        extioi_writeb(opaque, addr + 4, (val >> 32) & 0xff);
+        extioi_writeb(opaque, addr + 5, (val >> 40) & 0xff);
+        extioi_writeb(opaque, addr + 6, (val >> 48) & 0xff);
+        extioi_writeb(opaque, addr + 7, (val >> 56) & 0xff);
+    } else if ((offset >= EXTIOI_NODETYPE_START) &&
+               (offset < EXTIOI_NODETYPE_END)) {
+        reg_count = (offset - EXTIOI_NODETYPE_START) / 8;
+        state->nodetype_reg64[reg_count] = val;
+    }
+
+    DPRINTF("writel reg 0x" TARGET_FMT_plx " = %lx\n", addr, val);
+}
+
+static uint64_t extioi_readfn(void *opaque, hwaddr addr, unsigned size)
+{
+    switch (size) {
+    case 1:
+        return extioi_readb(opaque, addr);
+    case 4:
+        return extioi_readw(opaque, addr);
+    case 8:
+        return extioi_readl(opaque, addr);
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void extioi_writefn(void *opaque, hwaddr addr,
+                           uint64_t value, unsigned size)
+{
+    switch (size) {
+    case 1:
+        extioi_writeb(opaque, addr, value);
+        break;
+    case 4:
+        extioi_writew(opaque, addr, value);
+        break;
+    case 8:
+        extioi_writel(opaque, addr, value);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static const MemoryRegionOps extioi_ops = {
+    .read = extioi_readfn,
+    .write = extioi_writefn,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 8,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 8,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void loongarch_extioi_realize(DeviceState *dev, Error **errp)
+{
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
+    MachineState *ms = MACHINE(lams);
+    loongarch_extioi *p = LOONGARCH_EXTIOI(dev);
+    int cpu, pin;
+
+    qdev_init_gpio_in(dev, extioi_setirq, EXTIOI_IRQS);
+
+    for (int i = 0; i < EXTIOI_IRQS; i++) {
+        sysbus_init_irq(SYS_BUS_DEVICE(dev), &p->irq[i]);
+    }
+
+    memory_region_init_io(&p->mmio, OBJECT(p), &extioi_ops, p,
+                          TYPE_LOONGARCH_EXTIOI, 0x900);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &p->mmio);
+
+    for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
+        for (pin = 0; pin < LS3A_INTC_IP; pin++) {
+            sysbus_init_irq(SYS_BUS_DEVICE(dev), &p->parent_irq[cpu][pin]);
+        }
+    }
+
+    /* 0-31 is for non msi device.32-256 for msi/msix device */
+    lams->pch_irq = qemu_allocate_irqs(extioi_handler, p, 256);
+}
+
+static const VMStateDescription vmstate_ext_sw_ipisr = {
+    .name = "ext_sw_ipisr",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8_ARRAY(irq, ext_sw_ipisr, EXTIOI_IRQS_BITMAP_SIZE),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_loongarch_extioi = {
+    .name = TYPE_LOONGARCH_EXTIOI,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8_ARRAY(en_reg8, loongarch_extioi, EXTIOI_IRQS_BITMAP_SIZE),
+        VMSTATE_UINT8_ARRAY(bounce_reg8, loongarch_extioi,
+                            EXTIOI_IRQS_BITMAP_SIZE),
+        VMSTATE_UINT8_ARRAY(isr_reg8, loongarch_extioi,
+                            EXTIOI_IRQS_BITMAP_SIZE),
+        VMSTATE_UINT8_2DARRAY(coreisr_reg8, loongarch_extioi, MAX_CORES,
+                              EXTIOI_IRQS_BITMAP_SIZE),
+        VMSTATE_UINT8_ARRAY(ipmap_reg8, loongarch_extioi,
+                            EXTIOI_IRQS_IPMAP_SIZE),
+        VMSTATE_UINT8_ARRAY(coremap_reg8, loongarch_extioi,
+                            EXTIOI_IRQS_COREMAP_SIZE),
+        VMSTATE_UINT16_ARRAY(nodetype_reg16, loongarch_extioi,
+                             EXTIOI_IRQS_NODETYPE_SIZE),
+        VMSTATE_UINT8_ARRAY(sw_ipmap, loongarch_extioi, EXTIOI_IRQS),
+        VMSTATE_UINT8_ARRAY(sw_coremap, loongarch_extioi, EXTIOI_IRQS),
+        VMSTATE_STRUCT_2DARRAY(sw_ipisr, loongarch_extioi, MAX_CORES,
+                               LS3A_INTC_IP, 1, vmstate_ext_sw_ipisr,
+                               ext_sw_ipisr),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void loongarch_extioi_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd = &vmstate_loongarch_extioi;
+    dc->realize = loongarch_extioi_realize;
+}
+
+static const TypeInfo loongarch_extioi_info = {
+    .name          = TYPE_LOONGARCH_EXTIOI,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(struct loongarch_extioi),
+    .class_init    = loongarch_extioi_class_init,
+};
+
+static void loongarch_extioi_register_types(void)
+{
+    type_register_static(&loongarch_extioi_info);
+}
+
+type_init(loongarch_extioi_register_types)
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index e04abe2d56..e8d3d46e3e 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -59,3 +59,4 @@ specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c'))
 specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
 specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c'))
 specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c'))
+specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c'))
diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
index 4500fd3a57..9212127701 100644
--- a/hw/loongarch/Kconfig
+++ b/hw/loongarch/Kconfig
@@ -3,3 +3,4 @@ config LOONGSON_3A5000
     select PCI_EXPRESS_7A
     select LOONGARCH_PCH_PIC
     select LOONGARCH_PCH_MSI
+    select LOONGARCH_EXTIOI
diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h
new file mode 100644
index 0000000000..f6381b6236
--- /dev/null
+++ b/include/hw/intc/loongarch_extioi.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch 3A5000 ext interrupt controller definitions
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "hw/sysbus.h"
+#include "hw/loongarch/loongarch.h"
+
+#ifndef LOONGARCH_EXTIOI_H
+#define LOONGARCH_EXTIOI_H
+
+#define LS3A_INTC_IP               8
+#define MAX_CORES                  LOONGARCH_MAX_VCPUS
+#define EXTIOI_IRQS                (256)
+#define EXTIOI_IRQS_BITMAP_SIZE    (256 / 8)
+/* map to ipnum per 32 irqs */
+#define EXTIOI_IRQS_IPMAP_SIZE     (256 / 32)
+#define EXTIOI_IRQS_COREMAP_SIZE   256
+#define EXTIOI_IRQS_NODETYPE_SIZE  16
+
+#define APIC_OFFSET                  0x400
+#define APIC_BASE                    (0x1000ULL + APIC_OFFSET)
+
+#define EXTIOI_NODETYPE_START        (0x4a0 - APIC_OFFSET)
+#define EXTIOI_NODETYPE_END          (0x4c0 - APIC_OFFSET)
+#define EXTIOI_IPMAP_START           (0x4c0 - APIC_OFFSET)
+#define EXTIOI_IPMAP_END             (0x4c8 - APIC_OFFSET)
+#define EXTIOI_ENABLE_START          (0x600 - APIC_OFFSET)
+#define EXTIOI_ENABLE_END            (0x620 - APIC_OFFSET)
+#define EXTIOI_BOUNCE_START          (0x680 - APIC_OFFSET)
+#define EXTIOI_BOUNCE_END            (0x6a0 - APIC_OFFSET)
+#define EXTIOI_ISR_START             (0x700 - APIC_OFFSET)
+#define EXTIOI_ISR_END               (0x720 - APIC_OFFSET)
+#define EXTIOI_COREISR_START         (0x800 - APIC_OFFSET)
+#define EXTIOI_COREISR_END           (0xB20 - APIC_OFFSET)
+#define EXTIOI_COREMAP_START         (0xC00 - APIC_OFFSET)
+#define EXTIOI_COREMAP_END           (0xD00 - APIC_OFFSET)
+
+#define TYPE_LOONGARCH_EXTIOI "loongarch.extioi"
+DECLARE_INSTANCE_CHECKER(struct loongarch_extioi, LOONGARCH_EXTIOI,
+                         TYPE_LOONGARCH_EXTIOI)
+
+typedef struct ext_sw_ipisr {
+    uint8_t irq[EXTIOI_IRQS_BITMAP_SIZE];
+} ext_sw_ipisr;
+
+typedef struct loongarch_extioi {
+    SysBusDevice parent_obj;
+    /* hardware state */
+    union {
+        uint64_t en_reg64[EXTIOI_IRQS_BITMAP_SIZE / 8];
+        uint32_t en_reg32[EXTIOI_IRQS_BITMAP_SIZE / 4];
+        uint8_t en_reg8[EXTIOI_IRQS_BITMAP_SIZE];
+    };
+    union {
+        uint64_t bounce_reg64[EXTIOI_IRQS_BITMAP_SIZE / 8];
+        uint32_t bounce_reg32[EXTIOI_IRQS_BITMAP_SIZE / 4];
+        uint8_t bounce_reg8[EXTIOI_IRQS_BITMAP_SIZE];
+    };
+    union {
+        uint64_t isr_reg64[EXTIOI_IRQS_BITMAP_SIZE / 8];
+        uint32_t isr_reg32[EXTIOI_IRQS_BITMAP_SIZE / 4];
+        uint8_t isr_reg8[EXTIOI_IRQS_BITMAP_SIZE];
+    };
+    union {
+        uint64_t coreisr_reg64[MAX_CORES][EXTIOI_IRQS_BITMAP_SIZE / 8];
+        uint32_t coreisr_reg32[MAX_CORES][EXTIOI_IRQS_BITMAP_SIZE / 4];
+        uint8_t coreisr_reg8[MAX_CORES][EXTIOI_IRQS_BITMAP_SIZE];
+    };
+    union {
+        uint64_t ipmap_reg64;
+        uint32_t ipmap_reg32[EXTIOI_IRQS_IPMAP_SIZE / 4];
+        uint8_t ipmap_reg8[EXTIOI_IRQS_IPMAP_SIZE];
+    };
+    union {
+        uint64_t coremap_reg64[EXTIOI_IRQS_COREMAP_SIZE / 8];
+        uint32_t coremap_reg32[EXTIOI_IRQS_COREMAP_SIZE / 4];
+        uint8_t coremap_reg8[EXTIOI_IRQS_COREMAP_SIZE];
+    };
+    union {
+        uint64_t nodetype_reg64[EXTIOI_IRQS_NODETYPE_SIZE / 4];
+        uint32_t nodetype_reg32[EXTIOI_IRQS_NODETYPE_SIZE / 2];
+        uint16_t nodetype_reg16[EXTIOI_IRQS_NODETYPE_SIZE];
+        uint8_t nodetype_reg8[EXTIOI_IRQS_NODETYPE_SIZE * 2];
+    };
+
+    /*software state */
+    uint8_t sw_ipmap[EXTIOI_IRQS];
+    uint8_t sw_coremap[EXTIOI_IRQS];
+    ext_sw_ipisr sw_ipisr[MAX_CORES][LS3A_INTC_IP];
+
+    qemu_irq parent_irq[MAX_CORES][LS3A_INTC_IP];
+    qemu_irq irq[EXTIOI_IRQS];
+    MemoryRegion mmio;
+} loongarch_extioi;
+
+#endif /* LOONGARCH_EXTIOI_H */
diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
index 54cc875e6d..a9690f63c1 100644
--- a/include/hw/loongarch/loongarch.h
+++ b/include/hw/loongarch/loongarch.h
@@ -41,6 +41,7 @@ typedef struct LoongArchMachineState {
     MemoryRegion *system_iocsr;
 
     gipiState   *gipi;
+    qemu_irq    *pch_irq;
 } LoongArchMachineState;
 
 #define TYPE_LOONGARCH_MACHINE  MACHINE_TYPE_NAME("loongson7a")
-- 
2.27.0



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

* [RFC PATCH v2 22/30] hw/loongarch: Add irq hierarchy for the system
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (20 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 21/30] hw/intc: Add LoongArch extioi interrupt controller(EIOINTC) Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 23/30] hw/loongarch: Add some devices support for 3A5000 Xiaojuan Yang
                   ` (8 subsequent siblings)
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This patch add the irq hierarchy for the virt board.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/loongarch/ls3a5000_virt.c | 64 ++++++++++++++++++++++++++++++++++++
 include/hw/pci-host/ls7a.h   |  4 +++
 2 files changed, 68 insertions(+)

diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
index bd79df96df..59a79807e0 100644
--- a/hw/loongarch/ls3a5000_virt.c
+++ b/hw/loongarch/ls3a5000_virt.c
@@ -15,6 +15,9 @@
 #include "sysemu/runstate.h"
 #include "sysemu/reset.h"
 #include "hw/loongarch/loongarch.h"
+#include "hw/intc/loongarch_extioi.h"
+#include "hw/intc/loongarch_pch_pic.h"
+#include "hw/intc/loongarch_pch_msi.h"
 #include "hw/pci-host/ls7a.h"
 
 CPULoongArchState *cpu_states[LOONGARCH_MAX_VCPUS];
@@ -102,6 +105,64 @@ static const MemoryRegionOps loongarch_qemu_ops = {
     },
 };
 
+static void sysbus_mmio_map_loongarch(SysBusDevice *dev, int n, hwaddr addr, MemoryRegion *iocsr)
+{
+    assert(n >= 0 && n < dev->num_mmio);
+
+    if (dev->mmio[n].addr == addr) {
+        /* ??? region already mapped here. */
+        return;
+    }
+    if (dev->mmio[n].addr != (hwaddr)-1) {
+        /* Unregister previous mapping. */
+        memory_region_del_subregion(iocsr, dev->mmio[n].memory);
+    }
+    dev->mmio[n].addr = addr;
+    memory_region_add_subregion(iocsr, addr, dev->mmio[n].memory);
+}
+
+static void ls3a5000_irq_init(MachineState *machine, CPULoongArchState *env[])
+{
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
+    DeviceState *extioi, *pch_pic, *pch_msi;
+    SysBusDevice *d;
+    int cpu, pin, i;
+
+    extioi = qdev_new(TYPE_LOONGARCH_EXTIOI);
+    d = SYS_BUS_DEVICE(extioi);
+    sysbus_realize_and_unref(d, &error_fatal);
+    sysbus_mmio_map_loongarch(d, 0, APIC_BASE, lams->system_iocsr);
+
+    for (i = 0; i < EXTIOI_IRQS; i++) {
+        sysbus_connect_irq(d, i, qdev_get_gpio_in(extioi, i));
+    }
+
+    for (cpu = 0; cpu < machine->smp.cpus; cpu++) {
+        /* cpu_pin[9:2] <= intc_pin[7:0] */
+        for (pin = 0; pin < LS3A_INTC_IP; pin++) {
+            sysbus_connect_irq(d, (EXTIOI_IRQS + cpu * 8 + pin),
+                               env[cpu]->irq[pin + 2]);
+        }
+    }
+
+    pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC);
+    d = SYS_BUS_DEVICE(pch_pic);
+    sysbus_realize_and_unref(d, &error_fatal);
+    sysbus_mmio_map(d, 0, LS7A_IOAPIC_REG_BASE);
+
+    for (int i = 0; i < 32; i++) {
+        sysbus_connect_irq(d, i, lams->pch_irq[i]);
+    }
+
+    pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI);
+    d = SYS_BUS_DEVICE(pch_msi);
+    sysbus_realize_and_unref(d, &error_fatal);
+    sysbus_mmio_map(d, 0, LS7A_PCH_MSI_ADDR_LOW);
+    for (i = 0; i < 224; i++) {
+        sysbus_connect_irq(d, i, lams->pch_irq[i + 32]);
+    }
+}
+
 static void ls3a5000_virt_init(MachineState *machine)
 {
     const char *cpu_model = machine->cpu_type;
@@ -179,6 +240,9 @@ static void ls3a5000_virt_init(MachineState *machine)
     memory_region_add_subregion(address_space_mem,
                                 PM_MMIO_ADDR, iomem);
 
+    /* Initialize the IO interrupt subsystem */
+    ls3a5000_irq_init(machine, cpu_states);
+
     LOONGARCH_SIMPLE_MMIO_OPS(FEATURE_REG, "loongarch_feature", 0x8);
     LOONGARCH_SIMPLE_MMIO_OPS(VENDOR_REG, "loongarch_vendor", 0x8);
     LOONGARCH_SIMPLE_MMIO_OPS(CPUNAME_REG, "loongarch_cpuname", 0x8);
diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h
index 6b5ba3b442..b57277f206 100644
--- a/include/hw/pci-host/ls7a.h
+++ b/include/hw/pci-host/ls7a.h
@@ -23,6 +23,10 @@
 
 #define LS7A_PCI_IO_BASE        0x18000000UL
 #define LS7A_PCI_IO_SIZE        0x00010000
+#define LS7A_PCH_REG_BASE       0x10000000UL
+#define LS7A_IOAPIC_REG_BASE    (LS7A_PCH_REG_BASE)
+#define LS7A_PCH_MSI_ADDR_LOW   0x2FF00000UL
+
 typedef struct LS7APCIState LS7APCIState;
 typedef struct LS7APCIEHost {
     PCIExpressHost parent_obj;
-- 
2.27.0



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

* [RFC PATCH v2 23/30] hw/loongarch: Add some devices support for 3A5000.
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (21 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 22/30] hw/loongarch: Add irq hierarchy for the system Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 24/30] hw/loongarch: Add LoongArch ls7a rtc device support Xiaojuan Yang
                   ` (7 subsequent siblings)
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

1.Add uart,virtio-net,vga and usb for 3A5000.
2.Add irq set and map for the pci host. Non pci device
use irq 0-16, pci device use 16-31.
3.Add some unimplented device to emulate guest unused
memory space.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/loongarch/Kconfig         |  8 ++++++
 hw/loongarch/ls3a5000_virt.c | 49 ++++++++++++++++++++++++++++++++++++
 hw/pci-host/ls7a.c           | 38 ++++++++++++++++++++++++++--
 include/hw/pci-host/ls7a.h   |  4 +++
 softmmu/qdev-monitor.c       |  3 ++-
 5 files changed, 99 insertions(+), 3 deletions(-)

diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
index 9212127701..e5f85d9c7f 100644
--- a/hw/loongarch/Kconfig
+++ b/hw/loongarch/Kconfig
@@ -1,5 +1,13 @@
 config LOONGSON_3A5000
     bool
+    imply VGA_PCI
+    imply VIRTIO_VGA
+    imply PARALLEL
+    imply PCI_DEVICES
+    select ISA_BUS
+    select SERIAL
+    select SERIAL_ISA
+    select VIRTIO_PCI
     select PCI_EXPRESS_7A
     select LOONGARCH_PCH_PIC
     select LOONGARCH_PCH_MSI
diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
index 59a79807e0..f780cca608 100644
--- a/hw/loongarch/ls3a5000_virt.c
+++ b/hw/loongarch/ls3a5000_virt.c
@@ -10,8 +10,11 @@
 #include "qemu/datadir.h"
 #include "qapi/error.h"
 #include "hw/boards.h"
+#include "hw/char/serial.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/qtest.h"
+#include "hw/irq.h"
+#include "net/net.h"
 #include "sysemu/runstate.h"
 #include "sysemu/reset.h"
 #include "hw/loongarch/loongarch.h"
@@ -19,6 +22,7 @@
 #include "hw/intc/loongarch_pch_pic.h"
 #include "hw/intc/loongarch_pch_msi.h"
 #include "hw/pci-host/ls7a.h"
+#include "hw/misc/unimp.h"
 
 CPULoongArchState *cpu_states[LOONGARCH_MAX_VCPUS];
 
@@ -150,6 +154,10 @@ static void ls3a5000_irq_init(MachineState *machine, CPULoongArchState *env[])
     sysbus_realize_and_unref(d, &error_fatal);
     sysbus_mmio_map(d, 0, LS7A_IOAPIC_REG_BASE);
 
+    serial_mm_init(get_system_memory(), LS7A_UART_BASE, 0,
+                   qdev_get_gpio_in(pch_pic, LS7A_UART_IRQ - 64),
+                   115200, serial_hd(0), DEVICE_NATIVE_ENDIAN);
+
     for (int i = 0; i < 32; i++) {
         sysbus_connect_irq(d, i, lams->pch_irq[i]);
     }
@@ -163,6 +171,22 @@ static void ls3a5000_irq_init(MachineState *machine, CPULoongArchState *env[])
     }
 }
 
+/* Network support */
+static void network_init(PCIBus *pci_bus)
+{
+    int i;
+
+    for (i = 0; i < nb_nics; i++) {
+        NICInfo *nd = &nd_table[i];
+
+        if (!nd->model) {
+            nd->model = g_strdup("virtio");
+        }
+
+        pci_nic_init_nofail(nd, pci_bus, nd->model, NULL);
+    }
+}
+
 static void ls3a5000_virt_init(MachineState *machine)
 {
     const char *cpu_model = machine->cpu_type;
@@ -176,6 +200,7 @@ static void ls3a5000_virt_init(MachineState *machine)
     LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
     int i;
     MemoryRegion *iomem = NULL;
+    PCIBus *pci_bus = NULL;
 
     if (!cpu_model) {
         cpu_model = LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000");
@@ -240,9 +265,33 @@ static void ls3a5000_virt_init(MachineState *machine)
     memory_region_add_subregion(address_space_mem,
                                 PM_MMIO_ADDR, iomem);
 
+    /*
+     * There are some invalid guest memory access.
+     * Create some unimplemented devices to emulate this.
+     */
+    create_unimplemented_device("ls7a-lpc", 0x10002000, 0x14);
+    create_unimplemented_device("pci-dma-cfg", 0x1001041c, 0x4);
+    create_unimplemented_device("node-bridge", 0xEFDFB000274, 0x4);
+    create_unimplemented_device("ls7a-lionlpc", 0x1fe01400, 0x38);
+    create_unimplemented_device("ls7a-node0", 0x0EFDFB000274, 0x4);
+    create_unimplemented_device("ls7a-node1", 0x1EFDFB000274, 0x4);
+    create_unimplemented_device("ls7a-node2", 0x2EFDFB000274, 0x4);
+    create_unimplemented_device("ls7a-node3", 0x3EFDFB000274, 0x4);
+
     /* Initialize the IO interrupt subsystem */
     ls3a5000_irq_init(machine, cpu_states);
 
+    /* Init the north bridge */
+    pci_bus = ls7a_init(machine, lams->pch_irq);
+
+    /* Network card */
+    network_init(pci_bus);
+
+    /* VGA setup. Don't bother loading the bios. */
+    pci_vga_init(pci_bus);
+
+    pci_create_simple(pci_bus, -1, "pci-ohci");
+
     LOONGARCH_SIMPLE_MMIO_OPS(FEATURE_REG, "loongarch_feature", 0x8);
     LOONGARCH_SIMPLE_MMIO_OPS(VENDOR_REG, "loongarch_vendor", 0x8);
     LOONGARCH_SIMPLE_MMIO_OPS(CPUNAME_REG, "loongarch_cpuname", 0x8);
diff --git a/hw/pci-host/ls7a.c b/hw/pci-host/ls7a.c
index 90b9fe4830..294715801f 100644
--- a/hw/pci-host/ls7a.c
+++ b/hw/pci-host/ls7a.c
@@ -27,6 +27,38 @@ static const VMStateDescription vmstate_ls7a_pcie = {
     }
 };
 
+static PCIINTxRoute ls7a_route_intx_pin_to_irq(void *opaque, int pin)
+{
+    PCIINTxRoute route;
+
+    route.irq = pin;
+    route.mode = PCI_INTX_ENABLED;
+    return route;
+}
+
+static int pci_ls7a_map_irq(PCIDevice *d, int irq_num)
+{
+    PCIBus *bus;
+    int irq;
+
+    bus = pci_get_bus(d);
+    if (bus->parent_dev) {
+        irq = pci_swizzle_map_irq_fn(d, irq_num);
+        return irq;
+     }
+
+     irq = 64 + 16 + ((PCI_SLOT(d->devfn) * 4 + irq_num) & 0xf);
+
+    return irq;
+}
+
+static void pci_ls7a_set_irq(void *opaque, int irq_num, int level)
+{
+    LS7APCIEHost *pciehost = opaque;
+
+    qemu_set_irq(pciehost->pic[irq_num - 64], level);
+}
+
 static void pci_ls7a_config_write(void *opaque, hwaddr addr,
                                   uint64_t val, unsigned size)
 {
@@ -63,11 +95,13 @@ static void ls7a_pciehost_realize(DeviceState *dev, Error **errp)
     PCIExpressHost *e = PCIE_HOST_BRIDGE(dev);
     PCIHostState *phb = PCI_HOST_BRIDGE(e);
 
-    phb->bus = pci_register_root_bus(dev, "pcie.0", NULL,
-                                     NULL, pciehost,
+    phb->bus = pci_register_root_bus(dev, "pcie.0", pci_ls7a_set_irq,
+                                     pci_ls7a_map_irq, pciehost,
                                      get_system_memory(), get_system_io(),
                                      PCI_DEVFN(1, 0), 128, TYPE_PCIE_BUS);
 
+    pci_bus_set_route_irq_fn(phb->bus, ls7a_route_intx_pin_to_irq);
+
     memory_region_init_io(&pciehost->pci_conf, OBJECT(dev),
                           &pci_ls7a_config_ops, phb->bus,
                           "ls7a_pci_conf", HT1LO_PCICFG_SIZE);
diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h
index b57277f206..96c9d22f33 100644
--- a/include/hw/pci-host/ls7a.h
+++ b/include/hw/pci-host/ls7a.h
@@ -27,6 +27,10 @@
 #define LS7A_IOAPIC_REG_BASE    (LS7A_PCH_REG_BASE)
 #define LS7A_PCH_MSI_ADDR_LOW   0x2FF00000UL
 
+#define LOONGARCH_PCH_IRQ_BASE  64
+#define LS7A_UART_IRQ           (LOONGARCH_PCH_IRQ_BASE + 2)
+#define LS7A_UART_BASE          0x1fe001e0
+
 typedef struct LS7APCIState LS7APCIState;
 typedef struct LS7APCIEHost {
     PCIExpressHost parent_obj;
diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c
index f8b3a4cd82..e2402d1b81 100644
--- a/softmmu/qdev-monitor.c
+++ b/softmmu/qdev-monitor.c
@@ -60,7 +60,8 @@ typedef struct QDevAlias
                               QEMU_ARCH_HPPA | QEMU_ARCH_I386 | \
                               QEMU_ARCH_MIPS | QEMU_ARCH_PPC |  \
                               QEMU_ARCH_RISCV | QEMU_ARCH_SH4 | \
-                              QEMU_ARCH_SPARC | QEMU_ARCH_XTENSA)
+                              QEMU_ARCH_SPARC | QEMU_ARCH_XTENSA | \
+                              QEMU_ARCH_LOONGARCH)
 #define QEMU_ARCH_VIRTIO_CCW (QEMU_ARCH_S390X)
 #define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K)
 
-- 
2.27.0



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

* [RFC PATCH v2 24/30] hw/loongarch: Add LoongArch ls7a rtc device support
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (22 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 23/30] hw/loongarch: Add some devices support for 3A5000 Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 25/30] hw/loongarch: Add default bios startup support Xiaojuan Yang
                   ` (6 subsequent siblings)
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

This patch add ls7a rtc device support.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/loongarch/Kconfig         |   1 +
 hw/loongarch/ls3a5000_virt.c |   3 +
 hw/rtc/Kconfig               |   3 +
 hw/rtc/ls7a_rtc.c            | 323 +++++++++++++++++++++++++++++++++++
 hw/rtc/meson.build           |   1 +
 include/hw/pci-host/ls7a.h   |   4 +
 6 files changed, 335 insertions(+)
 create mode 100644 hw/rtc/ls7a_rtc.c

diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
index e5f85d9c7f..fe100b01eb 100644
--- a/hw/loongarch/Kconfig
+++ b/hw/loongarch/Kconfig
@@ -12,3 +12,4 @@ config LOONGSON_3A5000
     select LOONGARCH_PCH_PIC
     select LOONGARCH_PCH_MSI
     select LOONGARCH_EXTIOI
+    select LS7A_RTC
diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
index f780cca608..c5e31080f0 100644
--- a/hw/loongarch/ls3a5000_virt.c
+++ b/hw/loongarch/ls3a5000_virt.c
@@ -158,6 +158,9 @@ static void ls3a5000_irq_init(MachineState *machine, CPULoongArchState *env[])
                    qdev_get_gpio_in(pch_pic, LS7A_UART_IRQ - 64),
                    115200, serial_hd(0), DEVICE_NATIVE_ENDIAN);
 
+    sysbus_create_simple("ls7a_rtc", LS7A_RTC_REG_BASE,
+                         qdev_get_gpio_in(pch_pic, LS7A_RTC_IRQ - 64));
+
     for (int i = 0; i < 32; i++) {
         sysbus_connect_irq(d, i, lams->pch_irq[i]);
     }
diff --git a/hw/rtc/Kconfig b/hw/rtc/Kconfig
index f06e133b8a..ba8f7bc202 100644
--- a/hw/rtc/Kconfig
+++ b/hw/rtc/Kconfig
@@ -25,3 +25,6 @@ config SUN4V_RTC
 
 config GOLDFISH_RTC
     bool
+
+config LS7A_RTC
+    bool
diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c
new file mode 100644
index 0000000000..0e65b83fa5
--- /dev/null
+++ b/hw/rtc/ls7a_rtc.c
@@ -0,0 +1,323 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Loongarch LS7A Real Time Clock emulation
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "hw/sysbus.h"
+#include "hw/irq.h"
+#include "include/hw/register.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "qemu/cutils.h"
+#include "qemu/log.h"
+#include "migration/vmstate.h"
+#include "hw/misc/unimp.h"
+
+#define SYS_TOYTRIM        0x20
+#define SYS_TOYWRITE0      0x24
+#define SYS_TOYWRITE1      0x28
+#define SYS_TOYREAD0       0x2C
+#define SYS_TOYREAD1       0x30
+#define SYS_TOYMATCH0      0x34
+#define SYS_TOYMATCH1      0x38
+#define SYS_TOYMATCH2      0x3C
+#define SYS_RTCCTRL        0x40
+#define SYS_RTCTRIM        0x60
+#define SYS_RTCWRTIE0      0x64
+#define SYS_RTCREAD0       0x68
+#define SYS_RTCMATCH0      0x6C
+#define SYS_RTCMATCH1      0x70
+#define SYS_RTCMATCH2      0x74
+
+/*
+ * Shift bits and filed mask
+ */
+#define TOY_MON_MASK   0x3f
+#define TOY_DAY_MASK   0x1f
+#define TOY_HOUR_MASK  0x1f
+#define TOY_MIN_MASK   0x3f
+#define TOY_SEC_MASK   0x3f
+#define TOY_MSEC_MASK  0xf
+
+#define TOY_MON_SHIFT  26
+#define TOY_DAY_SHIFT  21
+#define TOY_HOUR_SHIFT 16
+#define TOY_MIN_SHIFT  10
+#define TOY_SEC_SHIFT  4
+#define TOY_MSEC_SHIFT 0
+
+#define TOY_MATCH_YEAR_MASK  0x3f
+#define TOY_MATCH_MON_MASK   0xf
+#define TOY_MATCH_DAY_MASK   0x1f
+#define TOY_MATCH_HOUR_MASK  0x1f
+#define TOY_MATCH_MIN_MASK   0x3f
+#define TOY_MATCH_SEC_MASK   0x3f
+
+#define TOY_MATCH_YEAR_SHIFT 26
+#define TOY_MATCH_MON_SHIFT  22
+#define TOY_MATCH_DAY_SHIFT  17
+#define TOY_MATCH_HOUR_SHIFT 12
+#define TOY_MATCH_MIN_SHIFT  6
+#define TOY_MATCH_SEC_SHIFT  0
+
+#define TOY_ENABLE_BIT (1U << 11)
+
+#define TYPE_LS7A_RTC "ls7a_rtc"
+#define LS7A_RTC(obj) OBJECT_CHECK(LS7A_RTCState, (obj), TYPE_LS7A_RTC)
+
+typedef struct LS7A_RTCState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion iomem;
+    QEMUTimer *timer;
+    /*
+     * Needed to preserve the tick_count across migration, even if the
+     * absolute value of the rtc_clock is different on the source and
+     * destination.
+     */
+    int64_t offset;
+    int64_t data;
+    int64_t save_alarm_offset;
+    int tidx;
+    uint32_t toymatch[3];
+    uint32_t toytrim;
+    uint32_t cntrctl;
+    uint32_t rtctrim;
+    uint32_t rtccount;
+    uint32_t rtcmatch[3];
+    qemu_irq toy_irq;
+} LS7A_RTCState;
+
+enum {
+TOYEN = 1UL << 11,
+RTCEN = 1UL << 13,
+};
+
+static uint64_t ls7a_rtc_read(void *opaque, hwaddr addr, unsigned size)
+{
+    LS7A_RTCState *s = (LS7A_RTCState *)opaque;
+    struct tm tm;
+    unsigned int val;
+
+    val = 0;
+
+    switch (addr) {
+    case SYS_TOYREAD0:
+        qemu_get_timedate(&tm, s->offset);
+        val = (((tm.tm_mon + 1) & TOY_MON_MASK) << TOY_MON_SHIFT)
+        | (((tm.tm_mday) & TOY_DAY_MASK) << TOY_DAY_SHIFT)
+        | (((tm.tm_hour) & TOY_HOUR_MASK) << TOY_HOUR_SHIFT)
+        | (((tm.tm_min) & TOY_MIN_MASK) << TOY_MIN_SHIFT)
+        | (((tm.tm_sec) & TOY_SEC_MASK) << TOY_SEC_SHIFT) | 0x0;
+        break;
+    case SYS_TOYREAD1:
+        qemu_get_timedate(&tm, s->offset);
+        val = tm.tm_year;
+        break;
+    case SYS_TOYMATCH0:
+        val = s->toymatch[0];
+        break;
+    case SYS_TOYMATCH1:
+        val = s->toymatch[1];
+        break;
+    case SYS_TOYMATCH2:
+        val = s->toymatch[2];
+        break;
+    case SYS_RTCCTRL:
+        val = s->cntrctl;
+        break;
+    case SYS_RTCREAD0:
+        val = s->rtccount;
+        break;
+    case SYS_RTCMATCH0:
+        val = s->rtcmatch[0];
+        break;
+    case SYS_RTCMATCH1:
+        val = s->rtcmatch[1];
+        break;
+    case SYS_RTCMATCH2:
+        val = s->rtcmatch[2];
+        break;
+    default:
+        val = 0;
+        break;
+    }
+    return val;
+}
+
+static void ls7a_rtc_write(void *opaque, hwaddr addr,
+                           uint64_t val, unsigned size)
+{
+    LS7A_RTCState *s = (LS7A_RTCState *)opaque;
+    struct tm tm;
+    int64_t alarm_offset, year_diff, expire_time;
+
+    switch (addr) {
+    case SYS_TOYWRITE0:
+        qemu_get_timedate(&tm, s->offset);
+        tm.tm_sec = (val >> TOY_SEC_SHIFT) & TOY_SEC_MASK;
+        tm.tm_min = (val >> TOY_MIN_SHIFT) & TOY_MIN_MASK;
+        tm.tm_hour = (val >> TOY_HOUR_SHIFT) & TOY_HOUR_MASK;
+        tm.tm_mday = ((val >> TOY_DAY_SHIFT) & TOY_DAY_MASK);
+        tm.tm_mon = ((val >> TOY_MON_SHIFT) & TOY_MON_MASK) - 1;
+        s->offset = qemu_timedate_diff(&tm);
+        break;
+    case SYS_TOYWRITE1:
+        qemu_get_timedate(&tm, s->offset);
+        tm.tm_year = val;
+        s->offset = qemu_timedate_diff(&tm);
+        break;
+    case SYS_TOYMATCH0:
+        s->toymatch[0] = val;
+        qemu_get_timedate(&tm, s->offset);
+        tm.tm_sec = (val >> TOY_MATCH_SEC_SHIFT) & TOY_MATCH_SEC_MASK;
+        tm.tm_min = (val >> TOY_MATCH_MIN_SHIFT) & TOY_MATCH_MIN_MASK;
+        tm.tm_hour = ((val >> TOY_MATCH_HOUR_SHIFT) & TOY_MATCH_HOUR_MASK);
+        tm.tm_mday = ((val >> TOY_MATCH_DAY_SHIFT) & TOY_MATCH_DAY_MASK);
+        tm.tm_mon = ((val >> TOY_MATCH_MON_SHIFT) & TOY_MATCH_MON_MASK) - 1;
+        year_diff = ((val >> TOY_MATCH_YEAR_SHIFT) & TOY_MATCH_YEAR_MASK);
+        year_diff = year_diff - (tm.tm_year & TOY_MATCH_YEAR_MASK);
+        tm.tm_year = tm.tm_year + year_diff;
+        alarm_offset = qemu_timedate_diff(&tm) - s->offset;
+        if ((alarm_offset < 0) && (alarm_offset > -5)) {
+                alarm_offset = 0;
+        }
+        expire_time = qemu_clock_get_ms(rtc_clock);
+        expire_time += ((alarm_offset * 1000) + 100);
+        timer_mod(s->timer, expire_time);
+        break;
+    case SYS_TOYMATCH1:
+        s->toymatch[1] = val;
+        break;
+    case SYS_TOYMATCH2:
+        s->toymatch[2] = val;
+        break;
+    case SYS_RTCCTRL:
+        s->cntrctl = val;
+        break;
+    case SYS_RTCWRTIE0:
+        s->rtccount = val;
+        break;
+    case SYS_RTCMATCH0:
+        s->rtcmatch[0] = val;
+        break;
+    case SYS_RTCMATCH1:
+        val = s->rtcmatch[1];
+        break;
+    case SYS_RTCMATCH2:
+        val = s->rtcmatch[2];
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps ls7a_rtc_ops = {
+    .read = ls7a_rtc_read,
+    .write = ls7a_rtc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+
+};
+
+static void toy_timer(void *opaque)
+{
+    LS7A_RTCState *s = (LS7A_RTCState *) opaque;
+
+    if (s->cntrctl & TOY_ENABLE_BIT) {
+            qemu_irq_pulse(s->toy_irq);
+    }
+}
+
+static void ls7a_rtc_realize(DeviceState *dev, Error **errp)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    LS7A_RTCState *d = LS7A_RTC(sbd);
+    memory_region_init_io(&d->iomem, NULL, &ls7a_rtc_ops,
+                    (void *)d, "ls7a_rtc", 0x100);
+
+    sysbus_init_irq(sbd, &d->toy_irq);
+
+    sysbus_init_mmio(sbd, &d->iomem);
+    d->timer = timer_new_ms(rtc_clock, toy_timer, d);
+    timer_mod(d->timer, qemu_clock_get_ms(rtc_clock) + 100);
+    d->offset = 0;
+
+    create_unimplemented_device("mmio fallback 1", 0x10013ffc, 0x4);
+}
+
+static int ls7a_rtc_pre_save(void *opaque)
+{
+    LS7A_RTCState *s = (LS7A_RTCState *)opaque;
+    struct tm tm;
+    int64_t year_diff, value;
+
+    value = s->toymatch[0];
+    qemu_get_timedate(&tm, s->offset);
+    tm.tm_sec = (value >> TOY_MATCH_SEC_SHIFT) & TOY_MATCH_SEC_MASK;
+    tm.tm_min = (value >> TOY_MATCH_MIN_SHIFT) & TOY_MATCH_MIN_MASK;
+    tm.tm_hour = ((value >> TOY_MATCH_HOUR_SHIFT) & TOY_MATCH_HOUR_MASK);
+    tm.tm_mday = ((value >> TOY_MATCH_DAY_SHIFT) & TOY_MATCH_DAY_MASK);
+    tm.tm_mon = ((value >> TOY_MATCH_MON_SHIFT) & TOY_MATCH_MON_MASK) - 1;
+    year_diff = ((value >> TOY_MATCH_YEAR_SHIFT) & TOY_MATCH_YEAR_MASK);
+    year_diff = year_diff - (tm.tm_year & TOY_MATCH_YEAR_MASK);
+    tm.tm_year = tm.tm_year + year_diff;
+    s->save_alarm_offset = qemu_timedate_diff(&tm) - s->offset;
+
+    return 0;
+}
+
+static int ls7a_rtc_post_load(void *opaque, int version_id)
+{
+    LS7A_RTCState *s = (LS7A_RTCState *)opaque;
+    int64_t expire_time;
+
+    expire_time = qemu_clock_get_ms(rtc_clock) + (s->save_alarm_offset * 1000);
+    timer_mod(s->timer, expire_time);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_ls7a_rtc = {
+    .name = "ls7a_rtc",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .pre_save = ls7a_rtc_pre_save,
+    .post_load = ls7a_rtc_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT64(offset, LS7A_RTCState),
+        VMSTATE_INT64(save_alarm_offset, LS7A_RTCState),
+        VMSTATE_UINT32(toymatch[0], LS7A_RTCState),
+        VMSTATE_UINT32(cntrctl, LS7A_RTCState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ls7a_rtc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    dc->vmsd = &vmstate_ls7a_rtc;
+    dc->realize = ls7a_rtc_realize;
+    dc->desc = "ls7a rtc";
+}
+
+static const TypeInfo ls7a_rtc_info = {
+    .name          = TYPE_LS7A_RTC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(LS7A_RTCState),
+    .class_init    = ls7a_rtc_class_init,
+};
+
+static void ls7a_rtc_register_types(void)
+{
+    type_register_static(&ls7a_rtc_info);
+}
+
+type_init(ls7a_rtc_register_types)
diff --git a/hw/rtc/meson.build b/hw/rtc/meson.build
index 7cecdee5dd..dc33973384 100644
--- a/hw/rtc/meson.build
+++ b/hw/rtc/meson.build
@@ -11,6 +11,7 @@ softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_rtc.c'))
 softmmu_ss.add(when: 'CONFIG_SUN4V_RTC', if_true: files('sun4v-rtc.c'))
 softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_rtc.c'))
 softmmu_ss.add(when: 'CONFIG_GOLDFISH_RTC', if_true: files('goldfish_rtc.c'))
+softmmu_ss.add(when: 'CONFIG_LS7A_RTC', if_true: files('ls7a_rtc.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-rtc.c'))
 
 specific_ss.add(when: 'CONFIG_MC146818RTC', if_true: files('mc146818rtc.c'))
diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h
index 96c9d22f33..019f11c56a 100644
--- a/include/hw/pci-host/ls7a.h
+++ b/include/hw/pci-host/ls7a.h
@@ -30,6 +30,10 @@
 #define LOONGARCH_PCH_IRQ_BASE  64
 #define LS7A_UART_IRQ           (LOONGARCH_PCH_IRQ_BASE + 2)
 #define LS7A_UART_BASE          0x1fe001e0
+#define LS7A_RTC_IRQ            (LOONGARCH_PCH_IRQ_BASE + 3)
+#define LS7A_MISC_REG_BASE      (LS7A_PCH_REG_BASE + 0x00080000)
+#define LS7A_RTC_REG_BASE       (LS7A_MISC_REG_BASE + 0x00050100)
+#define LS7A_RTC_LEN            0x100
 
 typedef struct LS7APCIState LS7APCIState;
 typedef struct LS7APCIEHost {
-- 
2.27.0



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

* [RFC PATCH v2 25/30] hw/loongarch: Add default bios startup support.
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (23 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 24/30] hw/loongarch: Add LoongArch ls7a rtc device support Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 26/30] hw/loongarch: Add -kernel and -initrd options support Xiaojuan Yang
                   ` (5 subsequent siblings)
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/loongarch/Kconfig             |  4 +++
 hw/loongarch/fw_cfg.c            | 33 ++++++++++++++++++
 hw/loongarch/fw_cfg.h            | 15 ++++++++
 hw/loongarch/ls3a5000_virt.c     | 60 +++++++++++++++++++++++++-------
 hw/loongarch/meson.build         |  1 +
 include/hw/loongarch/loongarch.h |  5 +++
 6 files changed, 106 insertions(+), 12 deletions(-)
 create mode 100644 hw/loongarch/fw_cfg.c
 create mode 100644 hw/loongarch/fw_cfg.h

diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
index fe100b01eb..b59cd98a7a 100644
--- a/hw/loongarch/Kconfig
+++ b/hw/loongarch/Kconfig
@@ -13,3 +13,7 @@ config LOONGSON_3A5000
     select LOONGARCH_PCH_MSI
     select LOONGARCH_EXTIOI
     select LS7A_RTC
+    select FW_CFG_LOONGARCH
+
+config FW_CFG_LOONGARCH
+    bool
diff --git a/hw/loongarch/fw_cfg.c b/hw/loongarch/fw_cfg.c
new file mode 100644
index 0000000000..2a7f8ed0ce
--- /dev/null
+++ b/hw/loongarch/fw_cfg.c
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU fw_cfg helpers (LoongArch specific)
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "hw/loongarch/fw_cfg.h"
+#include "hw/loongarch/loongarch.h"
+#include "hw/nvram/fw_cfg.h"
+#include "sysemu/sysemu.h"
+
+static void fw_cfg_boot_set(void *opaque, const char *boot_device,
+                            Error **errp)
+{
+    fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+}
+
+FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms)
+{
+    FWCfgState *fw_cfg;
+    int max_cpus = ms->smp.max_cpus;
+    int smp_cpus = ms->smp.cpus;
+
+    fw_cfg = fw_cfg_init_mem_wide(FW_CFG_ADDR, FW_CFG_ADDR + 8, 8, 0, NULL);
+    fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
+    fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
+    fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
+
+    qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
+    return fw_cfg;
+}
diff --git a/hw/loongarch/fw_cfg.h b/hw/loongarch/fw_cfg.h
new file mode 100644
index 0000000000..7c0de4db4a
--- /dev/null
+++ b/hw/loongarch/fw_cfg.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU fw_cfg helpers (LoongArch specific)
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef HW_LOONGARCH_FW_CFG_H
+#define HW_LOONGARCH_FW_CFG_H
+
+#include "hw/boards.h"
+#include "hw/nvram/fw_cfg.h"
+
+FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms);
+#endif
diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
index c5e31080f0..85c8466d75 100644
--- a/hw/loongarch/ls3a5000_virt.c
+++ b/hw/loongarch/ls3a5000_virt.c
@@ -13,6 +13,8 @@
 #include "hw/char/serial.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/qtest.h"
+#include "hw/loader.h"
+#include "elf.h"
 #include "hw/irq.h"
 #include "net/net.h"
 #include "sysemu/runstate.h"
@@ -23,6 +25,9 @@
 #include "hw/intc/loongarch_pch_msi.h"
 #include "hw/pci-host/ls7a.h"
 #include "hw/misc/unimp.h"
+#include "hw/loongarch/fw_cfg.h"
+
+#define LOONGSON3_BIOSNAME "loongarch_bios.bin"
 
 CPULoongArchState *cpu_states[LOONGARCH_MAX_VCPUS];
 
@@ -195,8 +200,9 @@ static void ls3a5000_virt_init(MachineState *machine)
     const char *cpu_model = machine->cpu_type;
     LoongArchCPU *cpu;
     CPULoongArchState *env;
-    uint64_t lowram_size = 0, highram_size = 0;
+    uint64_t highram_size = 0;
     MemoryRegion *lowmem = g_new(MemoryRegion, 1);
+    MemoryRegion *highmem = g_new(MemoryRegion, 1);
     char *ramName = NULL;
     ram_addr_t ram_size = machine->ram_size;
     MemoryRegion *address_space_mem = get_system_memory();
@@ -204,6 +210,10 @@ static void ls3a5000_virt_init(MachineState *machine)
     int i;
     MemoryRegion *iomem = NULL;
     PCIBus *pci_bus = NULL;
+    int bios_size;
+    char *filename;
+    MemoryRegion *bios = g_new(MemoryRegion, 1);
+    ram_addr_t offset = 0;
 
     if (!cpu_model) {
         cpu_model = LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000");
@@ -246,21 +256,46 @@ static void ls3a5000_virt_init(MachineState *machine)
         qemu_register_reset(main_cpu_reset, cpu);
     }
 
+    if (ram_size < 1 * GiB) {
+        error_report("ram_size must be greater than 1G due to the bios memory layout");
+        exit(1);
+    }
+
     ramName = g_strdup_printf("loongarch.lowram");
-    lowram_size = MIN(ram_size, 256 * 0x100000);
     memory_region_init_alias(lowmem, NULL, ramName, machine->ram,
-                             0, lowram_size);
-    memory_region_add_subregion(address_space_mem, 0, lowmem);
-
-    highram_size = ram_size > lowram_size ? ram_size - 256 * 0x100000 : 0;
-    if (highram_size > 0) {
-        MemoryRegion *highmem = g_new(MemoryRegion, 1);
-        ramName = g_strdup_printf("loongarch.highram");
-        memory_region_init_alias(highmem, NULL, ramName, machine->ram,
-                                 lowram_size, highram_size);
-        memory_region_add_subregion(address_space_mem, 0x90000000, highmem);
+                             0, 256 * MiB);
+    memory_region_add_subregion(address_space_mem, offset, lowmem);
+    offset += 256 * MiB;
+
+    highram_size = ram_size - 256 * MiB;
+    ramName = g_strdup_printf("loongarch.highram");
+    memory_region_init_alias(highmem, NULL, ramName, machine->ram,
+                             offset, highram_size);
+    memory_region_add_subregion(address_space_mem, 0x90000000, highmem);
+    offset += highram_size;
+
+    /* load the BIOS image. */
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
+                              machine->firmware ?: LOONGSON3_BIOSNAME);
+    if (filename) {
+        bios_size = load_image_targphys(filename, LA_BIOS_BASE, LA_BIOS_SIZE);
+        lams->fw_cfg = loongarch_fw_cfg_init(ram_size, machine);
+        rom_set_fw(lams->fw_cfg);
+        g_free(filename);
+    } else {
+        bios_size = -1;
     }
 
+    if ((bios_size < 0 || bios_size > LA_BIOS_SIZE) && !qtest_enabled()) {
+        error_report("Could not load LOONGARCH bios '%s'", machine->firmware);
+        exit(1);
+    }
+
+    memory_region_init_ram(bios, NULL, "loongarch.bios",
+                           LA_BIOS_SIZE, &error_fatal);
+    memory_region_set_readonly(bios, true);
+    memory_region_add_subregion(get_system_memory(), LA_BIOS_BASE, bios);
+
     /* Add PM mmio memory for reboot and shutdown*/
     iomem = g_new(MemoryRegion, 1);
     memory_region_init_io(iomem, NULL, &loongarch_pm_ops, NULL,
@@ -311,6 +346,7 @@ static void loongarch_class_init(ObjectClass *oc, void *data)
     mc->default_ram_id = "loongarch.ram";
     mc->max_cpus = LOONGARCH_MAX_VCPUS;
     mc->is_default = 1;
+    mc->default_machine_opts = "firmware=loongarch_bios.bin";
     mc->default_kernel_irqchip_split = false;
     mc->block_default_type = IF_VIRTIO;
     mc->default_boot_order = "c";
diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build
index 1bd209c9eb..3fabfa72dc 100644
--- a/hw/loongarch/meson.build
+++ b/hw/loongarch/meson.build
@@ -1,5 +1,6 @@
 loongarch_ss = ss.source_set()
 loongarch_ss.add(files('loongarch_int.c'))
 loongarch_ss.add(when: 'CONFIG_LOONGSON_3A5000', if_true: files('ls3a5000_virt.c', 'ipi.c'))
+loongarch_ss.add(when: 'CONFIG_FW_CFG_LOONGARCH', if_true: files('fw_cfg.c'))
 
 hw_arch += {'loongarch': loongarch_ss}
diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
index a9690f63c1..2eb43e1263 100644
--- a/include/hw/loongarch/loongarch.h
+++ b/include/hw/loongarch/loongarch.h
@@ -33,6 +33,10 @@
 #define VENDOR_REG              0x10
 #define CPUNAME_REG             0x20
 
+#define FW_CFG_ADDR             0x1e020000
+#define LA_BIOS_BASE            0x1c000000
+#define LA_BIOS_SIZE            (4 * 1024 * 1024)
+
 typedef struct LoongArchMachineState {
     /*< private >*/
     MachineState parent_obj;
@@ -42,6 +46,7 @@ typedef struct LoongArchMachineState {
 
     gipiState   *gipi;
     qemu_irq    *pch_irq;
+    FWCfgState  *fw_cfg;
 } LoongArchMachineState;
 
 #define TYPE_LOONGARCH_MACHINE  MACHINE_TYPE_NAME("loongson7a")
-- 
2.27.0



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

* [RFC PATCH v2 26/30] hw/loongarch: Add -kernel and -initrd options support
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (24 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 25/30] hw/loongarch: Add default bios startup support Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 27/30] hw/loongarch: Add LoongArch smbios support Xiaojuan Yang
                   ` (4 subsequent siblings)
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/loongarch/ls3a5000_virt.c     | 81 ++++++++++++++++++++++++++++++++
 include/hw/loongarch/loongarch.h |  5 ++
 2 files changed, 86 insertions(+)

diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
index 85c8466d75..902a0c7630 100644
--- a/hw/loongarch/ls3a5000_virt.c
+++ b/hw/loongarch/ls3a5000_virt.c
@@ -29,8 +29,78 @@
 
 #define LOONGSON3_BIOSNAME "loongarch_bios.bin"
 
+static struct _loaderparams {
+    unsigned long ram_size;
+    const char *kernel_filename;
+    const char *kernel_cmdline;
+    const char *initrd_filename;
+} loaderparams;
+
 CPULoongArchState *cpu_states[LOONGARCH_MAX_VCPUS];
 
+static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr)
+{
+    return addr & 0x1fffffffll;
+}
+
+static void fw_cfg_add_kernel_info(FWCfgState *fw_cfg)
+{
+    int64_t kernel_entry, kernel_low, kernel_high, initrd_size = 0;
+    long kernel_size;
+    ram_addr_t initrd_offset = 0;
+    void *cmdline_buf;
+    int ret = 0;
+
+    kernel_size = load_elf(loaderparams.kernel_filename, NULL,
+                           cpu_loongarch_virt_to_phys, NULL,
+                           (uint64_t *)&kernel_entry, (uint64_t *)&kernel_low,
+                           (uint64_t *)&kernel_high, NULL, 0,
+                           EM_LOONGARCH, 1, 0);
+
+    if (kernel_size < 0) {
+        error_report("could not load kernel '%s': %s",
+                     loaderparams.kernel_filename,
+                     load_elf_strerror(kernel_size));
+        exit(1);
+    }
+
+    fw_cfg_add_i64(fw_cfg, FW_CFG_KERNEL_ENTRY, kernel_entry);
+
+    if (loaderparams.initrd_filename) {
+        initrd_size = get_image_size(loaderparams.initrd_filename);
+
+        if (initrd_size > 0) {
+            initrd_offset = MAX(INITRD_BASE,
+                                ROUND_UP(kernel_high, INITRD_PAGE_SIZE));
+            if (initrd_offset + initrd_size > 0x10000000) {
+                error_report("ramdisk '%s' is too big",
+                             loaderparams.initrd_filename);
+                exit(1);
+            }
+            initrd_size = load_image_targphys(loaderparams.initrd_filename,
+                                              initrd_offset,
+                                              loaderparams.ram_size - initrd_offset);
+        }
+        if (initrd_size == (target_ulong) -1) {
+            error_report("could not load initial ram disk '%s'",
+                         loaderparams.initrd_filename);
+            exit(1);
+        }
+    }
+
+    cmdline_buf = g_malloc0(COMMAND_LINE_SIZE);
+    if (initrd_size > 0)
+        ret = (1 + snprintf(cmdline_buf, COMMAND_LINE_SIZE,
+                "initrd=0x%lx,%li %s", initrd_offset,
+                initrd_size, loaderparams.kernel_cmdline));
+    else
+        ret = (1 + snprintf(cmdline_buf, COMMAND_LINE_SIZE, "%s",
+                loaderparams.kernel_cmdline));
+
+    fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, ret);
+    fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, (const char *)cmdline_buf);
+}
+
 static void main_cpu_reset(void *opaque)
 {
     LoongArchCPU *cpu = opaque;
@@ -198,6 +268,9 @@ static void network_init(PCIBus *pci_bus)
 static void ls3a5000_virt_init(MachineState *machine)
 {
     const char *cpu_model = machine->cpu_type;
+    const char *kernel_filename = machine->kernel_filename;
+    const char *kernel_cmdline = machine->kernel_cmdline;
+    const char *initrd_filename = machine->initrd_filename;
     LoongArchCPU *cpu;
     CPULoongArchState *env;
     uint64_t highram_size = 0;
@@ -291,6 +364,14 @@ static void ls3a5000_virt_init(MachineState *machine)
         exit(1);
     }
 
+    if (kernel_filename) {
+        loaderparams.ram_size = ram_size;
+        loaderparams.kernel_filename = kernel_filename;
+        loaderparams.kernel_cmdline = kernel_cmdline;
+        loaderparams.initrd_filename = initrd_filename;
+        fw_cfg_add_kernel_info(lams->fw_cfg);
+    }
+
     memory_region_init_ram(bios, NULL, "loongarch.bios",
                            LA_BIOS_SIZE, &error_fatal);
     memory_region_set_readonly(bios, true);
diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
index 2eb43e1263..dc7617ab64 100644
--- a/include/hw/loongarch/loongarch.h
+++ b/include/hw/loongarch/loongarch.h
@@ -37,6 +37,11 @@
 #define LA_BIOS_BASE            0x1c000000
 #define LA_BIOS_SIZE            (4 * 1024 * 1024)
 
+/* Kernels can be configured with 64KB pages */
+#define INITRD_PAGE_SIZE        (64 * KiB)
+#define INITRD_BASE             0x04000000
+#define COMMAND_LINE_SIZE       4096
+
 typedef struct LoongArchMachineState {
     /*< private >*/
     MachineState parent_obj;
-- 
2.27.0



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

* [RFC PATCH v2 27/30] hw/loongarch: Add LoongArch smbios support
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (25 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 26/30] hw/loongarch: Add -kernel and -initrd options support Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 28/30] hw/loongarch: Add LoongArch acpi support Xiaojuan Yang
                   ` (3 subsequent siblings)
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/loongarch/Kconfig             |  1 +
 hw/loongarch/ls3a5000_virt.c     | 41 ++++++++++++++++++++++++++++++++
 include/hw/loongarch/loongarch.h |  2 ++
 3 files changed, 44 insertions(+)

diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
index b59cd98a7a..595c19ad83 100644
--- a/hw/loongarch/Kconfig
+++ b/hw/loongarch/Kconfig
@@ -14,6 +14,7 @@ config LOONGSON_3A5000
     select LOONGARCH_EXTIOI
     select LS7A_RTC
     select FW_CFG_LOONGARCH
+    select SMBIOS
 
 config FW_CFG_LOONGARCH
     bool
diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
index 902a0c7630..bfe8dd4757 100644
--- a/hw/loongarch/ls3a5000_virt.c
+++ b/hw/loongarch/ls3a5000_virt.c
@@ -26,6 +26,7 @@
 #include "hw/pci-host/ls7a.h"
 #include "hw/misc/unimp.h"
 #include "hw/loongarch/fw_cfg.h"
+#include "hw/firmware/smbios.h"
 
 #define LOONGSON3_BIOSNAME "loongarch_bios.bin"
 
@@ -101,6 +102,43 @@ static void fw_cfg_add_kernel_info(FWCfgState *fw_cfg)
     fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, (const char *)cmdline_buf);
 }
 
+static void loongarch_build_smbios(LoongArchMachineState *lams)
+{
+    MachineState *ms = MACHINE(lams);
+    MachineClass *mc = MACHINE_GET_CLASS(lams);
+    uint8_t *smbios_tables, *smbios_anchor;
+    size_t smbios_tables_len, smbios_anchor_len;
+    const char *product = "QEMU Virtual Machine";
+    ms->smp.cores = 4;
+
+    if (!lams->fw_cfg) {
+        return;
+    }
+
+    product = "LoongArch-3A5K-7A1000-TCG";
+
+    smbios_set_defaults("QEMU", product, mc->name, false,
+                        true, SMBIOS_ENTRY_POINT_30);
+
+    smbios_get_tables(ms, NULL, 0, &smbios_tables, &smbios_tables_len,
+                      &smbios_anchor, &smbios_anchor_len, &error_fatal);
+
+    if (smbios_anchor) {
+        fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-tables",
+                        smbios_tables, smbios_tables_len);
+        fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-anchor",
+                        smbios_anchor, smbios_anchor_len);
+    }
+}
+
+static
+void loongarch_machine_done(Notifier *notifier, void *data)
+{
+    LoongArchMachineState *lams = container_of(notifier,
+                                        LoongArchMachineState, machine_done);
+    loongarch_build_smbios(lams);
+}
+
 static void main_cpu_reset(void *opaque)
 {
     LoongArchCPU *cpu = opaque;
@@ -377,6 +415,9 @@ static void ls3a5000_virt_init(MachineState *machine)
     memory_region_set_readonly(bios, true);
     memory_region_add_subregion(get_system_memory(), LA_BIOS_BASE, bios);
 
+    lams->machine_done.notify = loongarch_machine_done;
+    qemu_add_machine_init_done_notifier(&lams->machine_done);
+
     /* Add PM mmio memory for reboot and shutdown*/
     iomem = g_new(MemoryRegion, 1);
     memory_region_init_io(iomem, NULL, &loongarch_pm_ops, NULL,
diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
index dc7617ab64..b165b4fd07 100644
--- a/include/hw/loongarch/loongarch.h
+++ b/include/hw/loongarch/loongarch.h
@@ -49,6 +49,8 @@ typedef struct LoongArchMachineState {
     AddressSpace *address_space_iocsr;
     MemoryRegion *system_iocsr;
 
+    /* State for other subsystems/APIs: */
+    Notifier machine_done;
     gipiState   *gipi;
     qemu_irq    *pch_irq;
     FWCfgState  *fw_cfg;
-- 
2.27.0



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

* [RFC PATCH v2 28/30] hw/loongarch: Add LoongArch acpi support
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (26 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 27/30] hw/loongarch: Add LoongArch smbios support Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 29/30] hw/loongarch: Add machine->possible_cpus Xiaojuan Yang
                   ` (2 subsequent siblings)
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

Add a simple acpi model for LoongArch cpu
More complex functions will be added later

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/acpi/Kconfig                  |   4 +
 hw/acpi/ls7a.c                   | 349 +++++++++++++++++
 hw/acpi/meson.build              |   1 +
 hw/loongarch/Kconfig             |   2 +
 hw/loongarch/acpi-build.c        | 636 +++++++++++++++++++++++++++++++
 hw/loongarch/ls3a5000_virt.c     |  53 ++-
 hw/loongarch/meson.build         |   1 +
 hw/pci-host/ls7a.c               |   4 +-
 include/hw/acpi/ls7a.h           |  53 +++
 include/hw/loongarch/loongarch.h |   5 +
 include/hw/pci-host/ls7a.h       |   9 +-
 11 files changed, 1112 insertions(+), 5 deletions(-)
 create mode 100644 hw/acpi/ls7a.c
 create mode 100644 hw/loongarch/acpi-build.c
 create mode 100644 include/hw/acpi/ls7a.h

diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
index 622b0b50b7..30f887d479 100644
--- a/hw/acpi/Kconfig
+++ b/hw/acpi/Kconfig
@@ -11,6 +11,10 @@ config ACPI_X86
     select ACPI_PIIX4
     select ACPI_PCIHP
 
+config ACPI_LOONGARCH
+    bool
+    select ACPI
+
 config ACPI_X86_ICH
     bool
     select ACPI_X86
diff --git a/hw/acpi/ls7a.c b/hw/acpi/ls7a.c
new file mode 100644
index 0000000000..37cec6517d
--- /dev/null
+++ b/hw/acpi/ls7a.c
@@ -0,0 +1,349 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch ACPI implementation
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "sysemu/sysemu.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "sysemu/reset.h"
+#include "sysemu/runstate.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/ls7a.h"
+#include "hw/nvram/fw_cfg.h"
+#include "qemu/config-file.h"
+#include "qapi/opts-visitor.h"
+#include "qapi/qapi-events-run-state.h"
+#include "qapi/error.h"
+#include "hw/pci-host/ls7a.h"
+#include "hw/mem/pc-dimm.h"
+#include "hw/mem/nvdimm.h"
+#include "migration/vmstate.h"
+
+static void ls7a_pm_update_sci_fn(ACPIREGS *regs)
+{
+    LS7APCIPMRegs *pm = container_of(regs, LS7APCIPMRegs, acpi_regs);
+    acpi_update_sci(&pm->acpi_regs, pm->irq);
+}
+
+static uint64_t ls7a_gpe_readb(void *opaque, hwaddr addr, unsigned width)
+{
+    LS7APCIPMRegs *pm = opaque;
+    return acpi_gpe_ioport_readb(&pm->acpi_regs, addr);
+}
+
+static void ls7a_gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
+                            unsigned width)
+{
+    LS7APCIPMRegs *pm = opaque;
+    acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val);
+    acpi_update_sci(&pm->acpi_regs, pm->irq);
+}
+
+static const MemoryRegionOps ls7a_gpe_ops = {
+    .read = ls7a_gpe_readb,
+    .write = ls7a_gpe_writeb,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 8,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 1,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+#define VMSTATE_GPE_ARRAY(_field, _state)                            \
+ {                                                                   \
+     .name       = (stringify(_field)),                              \
+     .version_id = 0,                                                \
+     .num        = ACPI_GPE0_LEN,                                    \
+     .info       = &vmstate_info_uint8,                              \
+     .size       = sizeof(uint8_t),                                  \
+     .flags      = VMS_ARRAY | VMS_POINTER,                          \
+     .offset     = vmstate_offset_pointer(_state, _field, uint8_t),  \
+ }
+
+static uint64_t ls7a_reset_readw(void *opaque, hwaddr addr, unsigned width)
+{
+    return 0;
+}
+
+static void ls7a_reset_writew(void *opaque, hwaddr addr, uint64_t val,
+                              unsigned width)
+{
+    if (val & 1) {
+        qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+        return;
+    }
+}
+
+static const MemoryRegionOps ls7a_reset_ops = {
+    .read = ls7a_reset_readw,
+    .write = ls7a_reset_writew,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+const VMStateDescription vmstate_ls7a_pm = {
+    .name = "ls7a_pm",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT16(acpi_regs.pm1.evt.sts, LS7APCIPMRegs),
+        VMSTATE_UINT16(acpi_regs.pm1.evt.en, LS7APCIPMRegs),
+        VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, LS7APCIPMRegs),
+        VMSTATE_TIMER_PTR(acpi_regs.tmr.timer, LS7APCIPMRegs),
+        VMSTATE_INT64(acpi_regs.tmr.overflow_time, LS7APCIPMRegs),
+        VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, LS7APCIPMRegs),
+        VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, LS7APCIPMRegs),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static inline int64_t acpi_pm_tmr_get_clock(void)
+{
+    return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), PM_TIMER_FREQUENCY,
+                    NANOSECONDS_PER_SECOND);
+}
+
+static uint32_t acpi_pm_tmr_get(ACPIREGS *ar)
+{
+    uint32_t d = acpi_pm_tmr_get_clock();
+    return d & 0xffffff;
+}
+
+static void acpi_pm_tmr_timer(void *opaque)
+{
+    ACPIREGS *ar = opaque;
+    qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER, NULL);
+    ar->tmr.update_sci(ar);
+}
+
+static uint64_t acpi_pm_tmr_read(void *opaque, hwaddr addr, unsigned width)
+{
+    return acpi_pm_tmr_get(opaque);
+}
+
+static void acpi_pm_tmr_write(void *opaque, hwaddr addr, uint64_t val,
+                              unsigned width)
+{
+}
+
+static const MemoryRegionOps acpi_pm_tmr_ops = {
+    .read = acpi_pm_tmr_read,
+    .write = acpi_pm_tmr_write,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ls7a_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
+                             MemoryRegion *parent, uint64_t offset)
+{
+    ar->tmr.update_sci = update_sci;
+    ar->tmr.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, acpi_pm_tmr_timer, ar);
+    memory_region_init_io(&ar->tmr.io, memory_region_owner(parent),
+                          &acpi_pm_tmr_ops, ar, "acpi-tmr", 4);
+    memory_region_add_subregion(parent, offset, &ar->tmr.io);
+}
+
+static void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val)
+{
+    uint16_t pm1_sts = acpi_pm1_evt_get_sts(ar);
+    if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) {
+        /* if TMRSTS is reset, then compute the new overflow time */
+        acpi_pm_tmr_calc_overflow_time(ar);
+    }
+    ar->pm1.evt.sts &= ~val;
+}
+
+static uint64_t acpi_pm_evt_read(void *opaque, hwaddr addr, unsigned width)
+{
+    ACPIREGS *ar = opaque;
+    switch (addr) {
+    case 0:
+        return acpi_pm1_evt_get_sts(ar);
+    case 4:
+        return ar->pm1.evt.en;
+    default:
+        return 0;
+    }
+}
+
+static void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val)
+{
+    ar->pm1.evt.en = val;
+    qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC,
+                              val & ACPI_BITMASK_RT_CLOCK_ENABLE);
+    qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER,
+                              val & ACPI_BITMASK_TIMER_ENABLE);
+}
+
+static void acpi_pm_evt_write(void *opaque, hwaddr addr, uint64_t val,
+                              unsigned width)
+{
+    ACPIREGS *ar = opaque;
+    switch (addr) {
+    case 0:
+        acpi_pm1_evt_write_sts(ar, val);
+        ar->pm1.evt.update_sci(ar);
+        break;
+    case 4:
+        acpi_pm1_evt_write_en(ar, val);
+        ar->pm1.evt.update_sci(ar);
+        break;
+    }
+}
+
+static const MemoryRegionOps acpi_pm_evt_ops = {
+    .read = acpi_pm_evt_read,
+    .write = acpi_pm_evt_write,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ls7a_pm1_evt_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
+                              MemoryRegion *parent, uint64_t offset)
+{
+    ar->pm1.evt.update_sci = update_sci;
+    memory_region_init_io(&ar->pm1.evt.io, memory_region_owner(parent),
+                          &acpi_pm_evt_ops, ar, "acpi-evt", 8);
+    memory_region_add_subregion(parent, offset, &ar->pm1.evt.io);
+}
+
+static uint64_t acpi_pm_cnt_read(void *opaque, hwaddr addr, unsigned width)
+{
+    ACPIREGS *ar = opaque;
+    return ar->pm1.cnt.cnt;
+}
+
+/* ACPI PM1aCNT */
+static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val)
+{
+    ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
+
+    if (val & ACPI_BITMASK_SLEEP_ENABLE) {
+        /* Change suspend type */
+        uint16_t sus_typ = (val >> 10) & 7;
+        switch (sus_typ) {
+        /* Not support s3 s4 yet */
+        case 7: /* Soft power off */
+            qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+static void acpi_pm_cnt_write(void *opaque, hwaddr addr, uint64_t val,
+                              unsigned width)
+{
+    acpi_pm1_cnt_write(opaque, val);
+}
+
+static const MemoryRegionOps acpi_pm_cnt_ops = {
+    .read = acpi_pm_cnt_read,
+    .write = acpi_pm_cnt_write,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void acpi_notify_wakeup(Notifier *notifier, void *data)
+{
+    ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup);
+    WakeupReason *reason = data;
+
+    switch (*reason) {
+    case QEMU_WAKEUP_REASON_RTC:
+        ar->pm1.evt.sts |=
+            (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_RT_CLOCK_STATUS);
+        break;
+    case QEMU_WAKEUP_REASON_PMTIMER:
+        ar->pm1.evt.sts |=
+            (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS);
+        break;
+    case QEMU_WAKEUP_REASON_OTHER:
+        /*
+         * ACPI_BITMASK_WAKE_STATUS should be set on resume.
+         * Pretend that resume was caused by power button
+         */
+        ar->pm1.evt.sts |=
+            (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS);
+        break;
+    default:
+        break;
+    }
+}
+
+static void ls7a_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent,
+                              uint64_t offset)
+{
+    ar->wakeup.notify = acpi_notify_wakeup;
+    qemu_register_wakeup_notifier(&ar->wakeup);
+    memory_region_init_io(&ar->pm1.cnt.io, memory_region_owner(parent),
+                          &acpi_pm_cnt_ops, ar, "acpi-cnt", 4);
+    memory_region_add_subregion(parent, offset, &ar->pm1.cnt.io);
+}
+
+static void ls7a_pm_reset(void *opaque)
+{
+    LS7APCIPMRegs *pm = opaque;
+
+    acpi_pm1_evt_reset(&pm->acpi_regs);
+    acpi_pm1_cnt_reset(&pm->acpi_regs);
+    acpi_pm_tmr_reset(&pm->acpi_regs);
+    acpi_gpe_reset(&pm->acpi_regs);
+
+    acpi_update_sci(&pm->acpi_regs, pm->irq);
+}
+
+static void pm_powerdown_req(Notifier *n, void *opaque)
+{
+    LS7APCIPMRegs *pm = container_of(n, LS7APCIPMRegs, powerdown_notifier);
+
+    acpi_pm1_evt_power_down(&pm->acpi_regs);
+}
+
+void ls7a_pm_init(PCIDevice *pci_dev, LS7APCIPMRegs *pm, DeviceState *pch_pic)
+{
+    unsigned long base, gpe_len;
+
+    /*
+     * ls7a board acpi hardware info, including
+     * acpi system io base address
+     * acpi gpe length
+     * acpi sci irq number
+     */
+    base = ACPI_IO_BASE;
+    gpe_len = ACPI_GPE0_LEN;
+
+    pm->irq = qdev_get_gpio_in(pch_pic, (ACPI_SCI_IRQ - 64));
+    memory_region_init(&pm->iomem, OBJECT(pci_dev), "ls7a_pm", ACPI_IO_SIZE);
+    memory_region_add_subregion(get_system_memory(), base, &pm->iomem);
+
+    ls7a_pm_tmr_init(&pm->acpi_regs, ls7a_pm_update_sci_fn,
+                     &pm->iomem, LS7A_PM_TMR_BLK);
+    ls7a_pm1_evt_init(&pm->acpi_regs, ls7a_pm_update_sci_fn,
+                      &pm->iomem, LS7A_PM_EVT_BLK);
+    ls7a_pm1_cnt_init(&pm->acpi_regs, &pm->iomem, LS7A_PM_CNT_BLK);
+
+    acpi_gpe_init(&pm->acpi_regs, gpe_len);
+    memory_region_init_io(&pm->iomem_gpe, OBJECT(pci_dev), &ls7a_gpe_ops, pm,
+                          "acpi-gpe0", gpe_len);
+    memory_region_add_subregion(&pm->iomem, LS7A_GPE0_STS_REG, &pm->iomem_gpe);
+
+    memory_region_init_io(&pm->iomem_reset, OBJECT(pci_dev),
+                          &ls7a_reset_ops, pm, "acpi-reset", 4);
+    memory_region_add_subregion(&pm->iomem, LS7A_GPE0_RESET_REG,
+                                &pm->iomem_reset);
+
+    qemu_register_reset(ls7a_pm_reset, pm);
+
+    pm->powerdown_notifier.notify = pm_powerdown_req;
+    qemu_register_powerdown_notifier(&pm->powerdown_notifier);
+}
diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build
index adf6347bc4..52f851d52b 100644
--- a/hw/acpi/meson.build
+++ b/hw/acpi/meson.build
@@ -25,6 +25,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('ich9.c', 'tco.c'))
 acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: files('ipmi-stub.c'))
 acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c'))
 acpi_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c'))
+acpi_ss.add(when: 'CONFIG_ACPI_LOONGARCH', if_true: files('ls7a.c'))
 softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c'))
 softmmu_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss)
 softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c', 'aml-build-stub.c',
diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
index 595c19ad83..b99ac9f7ba 100644
--- a/hw/loongarch/Kconfig
+++ b/hw/loongarch/Kconfig
@@ -15,6 +15,8 @@ config LOONGSON_3A5000
     select LS7A_RTC
     select FW_CFG_LOONGARCH
     select SMBIOS
+    select ACPI_LOONGARCH
+    select ACPI_PCI
 
 config FW_CFG_LOONGARCH
     bool
diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c
new file mode 100644
index 0000000000..fda4d55f2e
--- /dev/null
+++ b/hw/loongarch/acpi-build.c
@@ -0,0 +1,636 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for generating ACPI tables and passing them to Guests
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/bitmap.h"
+#include "hw/pci/pci.h"
+#include "hw/core/cpu.h"
+#include "target/loongarch/cpu.h"
+#include "hw/acpi/acpi-defs.h"
+#include "hw/acpi/acpi.h"
+#include "hw/nvram/fw_cfg.h"
+#include "hw/acpi/bios-linker-loader.h"
+#include "migration/vmstate.h"
+#include "hw/mem/memory-device.h"
+#include "sysemu/reset.h"
+
+/* Supported chipsets: */
+#include "hw/pci-host/ls7a.h"
+#include "hw/loongarch/loongarch.h"
+#include "hw/acpi/aml-build.h"
+
+#include "hw/acpi/utils.h"
+#include "hw/acpi/pci.h"
+
+#include "qom/qom-qobject.h"
+
+#include "hw/acpi/ls7a.h"
+
+#define ACPI_BUILD_ALIGN_SIZE             0x1000
+#define ACPI_BUILD_TABLE_SIZE             0x20000
+
+#define DEBUG_ACPI_BUILD
+
+#ifdef DEBUG_ACPI_BUILD
+#define ACPI_BUILD_DPRINTF(fmt, ...)        \
+    do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define ACPI_BUILD_DPRINTF(fmt, ...)
+#endif
+
+static void init_common_fadt_data(AcpiFadtData *data)
+{
+    AmlAddressSpace as = AML_AS_SYSTEM_MEMORY;
+    uint64_t base = LS7A_ACPI_REG_BASE;
+    AcpiFadtData fadt = {
+        .rev = 3,
+        .flags =
+            (1 << ACPI_FADT_F_WBINVD) |
+            (1 << ACPI_FADT_F_PROC_C1) |
+            (1 << ACPI_FADT_F_SLP_BUTTON) |
+            (1 << ACPI_FADT_F_TMR_VAL_EXT) |
+            (1 << ACPI_FADT_F_RESET_REG_SUP) ,
+        .plvl2_lat = 0xfff /* C2 state not supported */,
+        .plvl3_lat = 0xfff /* C3 state not supported */,
+        .smi_cmd = 0x00,
+        .sci_int = ACPI_SCI_IRQ,
+        .acpi_enable_cmd = 0x00,
+        .acpi_disable_cmd = 0x00,
+        .pm1a_evt = { .space_id = as, .bit_width = 8 * 8,
+                      .address = base + LS7A_PM_EVT_BLK },
+        .pm1a_cnt = { .space_id = as, .bit_width = 4 * 8,
+                      .address = base + LS7A_PM_CNT_BLK },
+        .pm_tmr = { .space_id = as, .bit_width = 4 * 8,
+                    .address = base + LS7A_PM_TMR_BLK },
+        .gpe0_blk = { .space_id = as, .bit_width = 8 * 8,
+                      .address = base + LS7A_GPE0_STS_REG},
+        .reset_reg = { .space_id = as, .bit_width = 4 * 8,
+                       .address = base + LS7A_GPE0_RESET_REG},
+        .reset_val = 0x1,
+    };
+    *data = fadt;
+}
+
+static void acpi_align_size(GArray *blob, unsigned align)
+{
+    /*
+     * Align size to multiple of given size. This reduces the chance
+     * we need to change size in the future (breaking cross version migration).
+     */
+    g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
+}
+
+/*
+ * ACPI spec 1.0b,
+ * 5.2.6 Firmware ACPI Control Structure
+ */
+static void
+build_facs(GArray *table_data)
+{
+    const char *sig = "FACS";
+    const uint8_t reserved[40] = {};
+
+    g_array_append_vals(table_data, sig, 4); /* Signature */
+    build_append_int_noprefix(table_data, 64, 4); /* Length */
+    build_append_int_noprefix(table_data, 0, 4); /* Hardware Signature */
+    build_append_int_noprefix(table_data, 0, 4); /* Firmware Waking Vector */
+    build_append_int_noprefix(table_data, 0, 4); /* Global Lock */
+    build_append_int_noprefix(table_data, 0, 4); /* Flags */
+    g_array_append_vals(table_data, reserved, 40); /* Reserved */
+}
+
+static void
+build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams)
+{
+    MachineState *ms = MACHINE(lams);
+    int i;
+    AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lams->oem_id,
+                        .oem_table_id = lams->oem_table_id };
+
+    acpi_table_begin(&table, table_data);
+
+    /* Local APIC Address */
+    build_append_int_noprefix(table_data, 0, 4);
+    build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */
+
+    for (i = 0; i < ms->smp.cpus; i++) {
+        /* Rev 1.0b, Table 5-13 Processor Core Interrupt Controller Structure */
+        build_append_int_noprefix(table_data, 17, 1);       /* Type */
+        build_append_int_noprefix(table_data, 15, 1);       /* Length */
+        build_append_int_noprefix(table_data, 1, 1);       /* Version */
+        build_append_int_noprefix(table_data, i + 1, 4);     /* ACPI Processor ID */
+        build_append_int_noprefix(table_data, i, 4); /* Core ID */
+        build_append_int_noprefix(table_data, 1, 4); /* Flags */
+    }
+
+    /* Rev 1.0b, Table 5-13 Extend I/O Interrupt Controller Structure */
+    build_append_int_noprefix(table_data, 20, 1);       /* Type */
+    build_append_int_noprefix(table_data, 13, 1);        /* Length */
+    build_append_int_noprefix(table_data, 1, 1);        /* Version */
+    build_append_int_noprefix(table_data, 3, 1);        /* Cascade */
+    build_append_int_noprefix(table_data, 0, 1);        /* Node */
+    build_append_int_noprefix(table_data, 0xffff, 8);   /* Node map */
+
+    /* Rev 1.0b, Table 5-13 Bridge I/O Interrupt Controller Structure */
+    build_append_int_noprefix(table_data, 22, 1);       /* Type */
+    build_append_int_noprefix(table_data, 17, 1);       /* Length */
+    build_append_int_noprefix(table_data, 1, 1);        /* Version */
+    build_append_int_noprefix(table_data, 0x10000000, 8);        /* Address */
+    build_append_int_noprefix(table_data, 0x20, 2);        /* Size */
+    build_append_int_noprefix(table_data, 0, 2);        /* Id */
+    build_append_int_noprefix(table_data, 0x40, 2);   /* Base */
+
+    /* Rev 1.0b, Table 5-13 MSI Interrupt Controller Structure */
+    build_append_int_noprefix(table_data, 21, 1);       /* Type */
+    build_append_int_noprefix(table_data, 19, 1);       /* Length */
+    build_append_int_noprefix(table_data, 1, 1);        /* Version */
+    build_append_int_noprefix(table_data, 0x2ff00000, 8);        /* Address */
+    build_append_int_noprefix(table_data, 0x60, 4);        /* Start */
+    build_append_int_noprefix(table_data, 0xc0, 4);        /* Count */
+
+    acpi_table_end(linker, &table);
+}
+
+/*
+ * ACPI spec, Revision 3.0
+ * 5.2.15 System Resource Affinity Table (SRAT)
+ */
+static void
+build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
+{
+    uint64_t i, mem_len, mem_base;
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
+    MachineState *ms = MACHINE(lams);
+    AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lams->oem_id,
+                        .oem_table_id = lams->oem_table_id };
+
+    acpi_table_begin(&table, table_data);
+    build_append_int_noprefix(table_data, 1, 4); /* Reserved */
+    build_append_int_noprefix(table_data, 0, 8); /* Reserved */
+
+    for (i = 0; i < ms->smp.cpus; ++i) {
+        /* 5.2.15.1 Processor Local APIC/SAPIC Affinity Structure */
+        build_append_int_noprefix(table_data, 0, 1);  /* Type  */
+        build_append_int_noprefix(table_data, 16, 1); /* Length */
+        /* Proximity Domain [7:0] */
+        build_append_int_noprefix(table_data, 0, 1);
+        build_append_int_noprefix(table_data, i, 1); /* APIC ID */
+        /* Flags, Table 5-36 */
+        build_append_int_noprefix(table_data, 1, 4);
+        build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */
+        /* Proximity Domain [31:8] */
+        build_append_int_noprefix(table_data, 0, 3);
+        build_append_int_noprefix(table_data, 0, 4); /* Reserved */
+    }
+
+    mem_base = 0;
+    mem_len = 0x10000000;
+    build_srat_memory(table_data, mem_base, mem_len,
+                        0, MEM_AFFINITY_ENABLED);
+
+    mem_base = 0x90000000;
+    mem_len = machine->ram_size - 0x10000000;
+    build_srat_memory(table_data, mem_base, mem_len,
+                            0, MEM_AFFINITY_ENABLED);
+
+    acpi_table_end(linker, &table);
+}
+
+typedef
+struct AcpiBuildState {
+    /* Copy of table in RAM (for patching). */
+    MemoryRegion *table_mr;
+    /* Is table patched? */
+    uint8_t patched;
+    void *rsdp;
+    MemoryRegion *rsdp_mr;
+    MemoryRegion *linker_mr;
+} AcpiBuildState;
+
+static void build_ls7a_pci0_int(Aml *table)
+{
+    Aml *sb_scope = aml_scope("_SB");
+    Aml *pci0_scope = aml_scope("PCI0");
+    Aml *prt_pkg = aml_varpackage(128);
+    int slot, pin;
+
+    for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
+        for (pin = 0; pin < PCI_NUM_PINS; pin++) {
+            Aml *pkg = aml_package(4);
+            aml_append(pkg, aml_int((slot << 16) | 0xFFFF));
+            aml_append(pkg, aml_int(pin));
+            aml_append(pkg, aml_int(0));
+            aml_append(pkg, aml_int(80 + (slot * 4 + pin) % 16));
+            aml_append(prt_pkg, pkg);
+        }
+    }
+    aml_append(pci0_scope, aml_name_decl("_PRT", prt_pkg));
+    aml_append(sb_scope, pci0_scope);
+    aml_append(table, sb_scope);
+}
+
+static void build_dbg_aml(Aml *table)
+{
+    Aml *field;
+    Aml *method;
+    Aml *while_ctx;
+    Aml *scope = aml_scope("\\");
+    Aml *buf = aml_local(0);
+    Aml *len = aml_local(1);
+    Aml *idx = aml_local(2);
+
+    aml_append(scope,
+       aml_operation_region("DBG", AML_SYSTEM_IO, aml_int(0x0402), 0x01));
+    field = aml_field("DBG", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
+    aml_append(field, aml_named_field("DBGB", 8));
+    aml_append(scope, field);
+
+    method = aml_method("DBUG", 1, AML_NOTSERIALIZED);
+
+    aml_append(method, aml_to_hexstring(aml_arg(0), buf));
+    aml_append(method, aml_to_buffer(buf, buf));
+    aml_append(method, aml_subtract(aml_sizeof(buf), aml_int(1), len));
+    aml_append(method, aml_store(aml_int(0), idx));
+
+    while_ctx = aml_while(aml_lless(idx, len));
+    aml_append(while_ctx,
+        aml_store(aml_derefof(aml_index(buf, idx)), aml_name("DBGB")));
+    aml_append(while_ctx, aml_increment(idx));
+    aml_append(method, while_ctx);
+    aml_append(method, aml_store(aml_int(0x0A), aml_name("DBGB")));
+    aml_append(scope, method);
+    aml_append(table, scope);
+}
+
+static Aml *build_ls7a_osc_method(void)
+{
+    Aml *if_ctx;
+    Aml *if_ctx2;
+    Aml *else_ctx;
+    Aml *method;
+    Aml *a_cwd1 = aml_name("CDW1");
+    Aml *a_ctrl = aml_local(0);
+
+    method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
+    aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
+
+    if_ctx = aml_if(aml_equal(
+        aml_arg(0), aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766")));
+    aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2"));
+    aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
+    aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl));
+
+    /*
+     * Always allow native PME, AER (no dependencies)
+     * Allow SHPC (PCI bridges can have SHPC controller)
+     */
+    aml_append(if_ctx, aml_and(a_ctrl, aml_int(0x1F), a_ctrl));
+
+    if_ctx2 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1))));
+    /* Unknown revision */
+    aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x08), a_cwd1));
+    aml_append(if_ctx, if_ctx2);
+
+    if_ctx2 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl)));
+    /* Capabilities bits were masked */
+    aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x10), a_cwd1));
+    aml_append(if_ctx, if_ctx2);
+
+    /* Update DWORD3 in the buffer */
+    aml_append(if_ctx, aml_store(a_ctrl, aml_name("CDW3")));
+    aml_append(method, if_ctx);
+
+    else_ctx = aml_else();
+    /* Unrecognized UUID */
+    aml_append(else_ctx, aml_or(a_cwd1, aml_int(4), a_cwd1));
+    aml_append(method, else_ctx);
+
+    aml_append(method, aml_return(aml_arg(3)));
+    return method;
+}
+
+static void build_ls7a_uart_device_aml(Aml *table)
+{
+    Aml *dev;
+    Aml *crs;
+    Aml *pkg0, *pkg1, *pkg2;
+    uint32_t uart_irq = LS7A_UART_IRQ;
+
+    Aml *scope = aml_scope("_SB");
+    dev = aml_device("COMA");
+    aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501")));
+    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+    aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
+    crs = aml_resource_template();
+    aml_append(crs,
+        aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+                         AML_NON_CACHEABLE, AML_READ_WRITE,
+                         0, 0x1FE001E0, 0x1FE001E7, 0, 0x8));
+    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+                                  AML_SHARED, &uart_irq, 1));
+    aml_append(dev, aml_name_decl("_CRS", crs));
+    pkg0 = aml_package(0x2);
+    aml_append(pkg0, aml_int(0x05F5E100));
+    aml_append(pkg0, aml_string("clock-frenquency"));
+    pkg1 = aml_package(0x1);
+    aml_append(pkg1, pkg0);
+    pkg2 = aml_package(0x2);
+    aml_append(pkg2, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"));
+    aml_append(pkg2, pkg1);
+    aml_append(dev, aml_name_decl("_DSD", pkg2));
+    aml_append(scope, dev);
+    aml_append(table, scope);
+}
+
+static void
+build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine)
+{
+    Aml *dsdt, *sb_scope, *scope, *dev, *crs, *pkg;
+    uint64_t base = LS7A_ACPI_REG_BASE;
+    int root_bus_limit = 0xFF;
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
+    AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lams->oem_id,
+                        .oem_table_id = lams->oem_table_id };
+
+    acpi_table_begin(&table, table_data);
+
+    dsdt = init_aml_allocator();
+
+    build_dbg_aml(dsdt);
+
+    sb_scope = aml_scope("_SB");
+    dev = aml_device("PCI0");
+    aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08")));
+    aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03")));
+    aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
+    aml_append(dev, aml_name_decl("_BBN", aml_int(0)));
+    aml_append(dev, aml_name_decl("_UID", aml_int(1)));
+    aml_append(dev, build_ls7a_osc_method());
+    aml_append(sb_scope, dev);
+    aml_append(dsdt, sb_scope);
+
+    build_ls7a_pci0_int(dsdt);
+    build_ls7a_uart_device_aml(dsdt);
+
+    scope =  aml_scope("_GPE");
+    {
+        aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006")));
+    }
+    aml_append(dsdt, scope);
+
+    scope = aml_scope("\\_SB.PCI0");
+    /* Build PCI0._CRS */
+    crs = aml_resource_template();
+    aml_append(crs,
+        aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
+                            0x0000, 0x0, root_bus_limit,
+                            0x0000, root_bus_limit + 1));
+    aml_append(crs,
+        aml_dword_io(AML_MIN_FIXED, AML_MAX_FIXED,
+                    AML_POS_DECODE, AML_ENTIRE_RANGE,
+                    0x0000, 0x4000, 0xFFFF, 0x18000000, 0xC000));
+    aml_append(crs,
+        aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+                         AML_CACHEABLE, AML_READ_WRITE,
+                         0, 0x40000000, 0x7FFFFFFF, 0, 0x40000000));
+    aml_append(scope, aml_name_decl("_CRS", crs));
+
+    /* Reserve GPE0 block resources */
+    dev = aml_device("GPE0");
+    aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06")));
+    aml_append(dev, aml_name_decl("_UID", aml_string("GPE0 resources")));
+    /* Device present, functioning, decoding, not shown in UI */
+    aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
+    crs = aml_resource_template();
+    aml_append(crs,
+        aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+                        AML_CACHEABLE, AML_READ_WRITE,
+                        0, base + LS7A_GPE0_STS_REG,
+                        base + LS7A_GPE0_STS_REG + 0x3, 0, 0x4));
+    aml_append(dev, aml_name_decl("_CRS", crs));
+    aml_append(scope, dev);
+    aml_append(dsdt, scope);
+
+    scope = aml_scope("\\");
+    pkg = aml_package(4);
+    aml_append(pkg, aml_int(7)); /* PM1a_CNT.SLP_TYP */
+    aml_append(pkg, aml_int(7)); /* PM1b_CNT.SLP_TYP not impl. */
+    aml_append(pkg, aml_int(0)); /* Reserved */
+    aml_append(pkg, aml_int(0)); /* Reserved */
+    aml_append(scope, aml_name_decl("_S5", pkg));
+    aml_append(dsdt, scope);
+
+    /* Copy AML table into ACPI tables blob and patch header there */
+    g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
+    acpi_table_end(linker, &table);
+    free_aml_allocator();
+}
+
+static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
+{
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
+    GArray *table_offsets;
+    AcpiFadtData fadt_data;
+    unsigned facs, rsdt, fadt, dsdt;
+    uint8_t *u;
+    size_t aml_len = 0;
+    GArray *tables_blob = tables->table_data;
+
+    init_common_fadt_data(&fadt_data);
+
+    table_offsets = g_array_new(false, true /* Clear */,
+                                        sizeof(uint32_t));
+    ACPI_BUILD_DPRINTF("init ACPI tables\n");
+
+    bios_linker_loader_alloc(tables->linker,
+                             ACPI_BUILD_TABLE_FILE, tables_blob,
+                             64 /* Ensure FACS is aligned */,
+                             false /* High memory */);
+
+    /*
+     * FACS is pointed to by FADT.
+     * We place it first since it's the only table that has alignment
+     * requirements.
+     */
+    facs = tables_blob->len;
+    build_facs(tables_blob);
+
+    /* DSDT is pointed to by FADT */
+    dsdt = tables_blob->len;
+    build_dsdt(tables_blob, tables->linker, machine);
+
+    /*
+     * Count the size of the DSDT, we will need it for
+     * legacy sizing of ACPI tables.
+     */
+    aml_len += tables_blob->len - dsdt;
+
+    /* ACPI tables pointed to by RSDT */
+    fadt = tables_blob->len;
+    acpi_add_table(table_offsets, tables_blob);
+    fadt_data.facs_tbl_offset = &facs;
+    fadt_data.dsdt_tbl_offset = &dsdt;
+    fadt_data.xdsdt_tbl_offset = &dsdt;
+    build_fadt(tables_blob, tables->linker, &fadt_data,
+               lams->oem_id, lams->oem_table_id);
+    aml_len += tables_blob->len - fadt;
+
+    acpi_add_table(table_offsets, tables_blob);
+    build_madt(tables_blob, tables->linker, lams);
+
+    acpi_add_table(table_offsets, tables_blob);
+    build_srat(tables_blob, tables->linker, machine);
+
+    acpi_add_table(table_offsets, tables_blob);
+    {
+        AcpiMcfgInfo mcfg = {
+           .base = cpu_to_le64(LS7A_PCI_MEM_BASE),
+           .size = cpu_to_le64(LS7A_PCI_MEM_SIZE),
+        };
+        build_mcfg(tables_blob, tables->linker, &mcfg, lams->oem_id,
+                   lams->oem_table_id);
+    }
+
+    /* Add tables supplied by user (if any) */
+    for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
+        unsigned len = acpi_table_len(u);
+
+        acpi_add_table(table_offsets, tables_blob);
+        g_array_append_vals(tables_blob, u, len);
+    }
+
+    /* RSDT is pointed to by RSDP */
+    rsdt = tables_blob->len;
+    build_rsdt(tables_blob, tables->linker, table_offsets,
+               lams->oem_id, lams->oem_table_id);
+
+    /* RSDP is in FSEG memory, so allocate it separately */
+    {
+        AcpiRsdpData rsdp_data = {
+            .revision = 0,
+            .oem_id = lams->oem_id,
+            .xsdt_tbl_offset = NULL,
+            .rsdt_tbl_offset = &rsdt,
+        };
+        build_rsdp(tables->rsdp, tables->linker, &rsdp_data);
+    }
+
+    /*
+     * The align size is 128, warn if 64k is not enough therefore
+     * the align size could be resized.
+     */
+    if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) {
+        warn_report("ACPI table size %u exceeds %d bytes,"
+                    " migration may not work",
+                    tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2);
+        error_printf("Try removing CPUs, NUMA nodes, memory slots"
+                     " or PCI bridges.");
+    }
+
+    acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE);
+
+    /* Cleanup memory that's no longer used. */
+    g_array_free(table_offsets, true);
+}
+
+static void acpi_ram_update(MemoryRegion *mr, GArray *data)
+{
+    uint32_t size = acpi_data_len(data);
+
+    /*
+     * Make sure RAM size is correct - in case it got changed
+     * e.g. by migration
+     */
+    memory_region_ram_resize(mr, size, &error_abort);
+
+    memcpy(memory_region_get_ram_ptr(mr), data->data, size);
+    memory_region_set_dirty(mr, 0, size);
+}
+
+static void acpi_build_update(void *build_opaque)
+{
+    AcpiBuildState *build_state = build_opaque;
+    AcpiBuildTables tables;
+
+    /* No state to update or already patched? Nothing to do. */
+    if (!build_state || build_state->patched) {
+        return;
+    }
+    build_state->patched = 1;
+
+    acpi_build_tables_init(&tables);
+
+    acpi_build(&tables, MACHINE(qdev_get_machine()));
+
+    acpi_ram_update(build_state->table_mr, tables.table_data);
+    acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
+    acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob);
+
+    acpi_build_tables_cleanup(&tables, true);
+}
+
+static void acpi_build_reset(void *build_opaque)
+{
+    AcpiBuildState *build_state = build_opaque;
+    build_state->patched = 0;
+}
+
+static const VMStateDescription vmstate_acpi_build = {
+    .name = "acpi_build",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(patched, AcpiBuildState),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+void loongarch_acpi_setup(LoongArchMachineState *lams)
+{
+    AcpiBuildTables tables;
+    AcpiBuildState *build_state;
+
+    if (!lams->fw_cfg) {
+        ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
+        return;
+    }
+
+    if (!loongarch_is_acpi_enabled(lams)) {
+        ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n");
+        return;
+    }
+
+    build_state = g_malloc0(sizeof *build_state);
+
+    acpi_build_tables_init(&tables);
+    acpi_build(&tables, MACHINE(lams));
+
+    /* Now expose it all to Guest */
+    build_state->table_mr = acpi_add_rom_blob(acpi_build_update,
+                                              build_state, tables.table_data,
+                                              ACPI_BUILD_TABLE_FILE);
+    assert(build_state->table_mr != NULL);
+
+    build_state->linker_mr =
+        acpi_add_rom_blob(acpi_build_update, build_state,
+                          tables.linker->cmd_blob, ACPI_BUILD_LOADER_FILE);
+
+    build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update,
+                                             build_state, tables.rsdp,
+                                             ACPI_BUILD_RSDP_FILE);
+
+    qemu_register_reset(acpi_build_reset, build_state);
+    acpi_build_reset(build_state);
+    vmstate_register(NULL, 0, &vmstate_acpi_build, build_state);
+
+    /*
+     * Cleanup tables but don't free the memory: we track it
+     * in build_state.
+     */
+    acpi_build_tables_cleanup(&tables, false);
+}
diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
index bfe8dd4757..35222f70a4 100644
--- a/hw/loongarch/ls3a5000_virt.c
+++ b/hw/loongarch/ls3a5000_virt.c
@@ -27,6 +27,8 @@
 #include "hw/misc/unimp.h"
 #include "hw/loongarch/fw_cfg.h"
 #include "hw/firmware/smbios.h"
+#include "hw/acpi/aml-build.h"
+#include "qapi/qapi-visit-common.h"
 
 #define LOONGSON3_BIOSNAME "loongarch_bios.bin"
 
@@ -136,6 +138,7 @@ void loongarch_machine_done(Notifier *notifier, void *data)
 {
     LoongArchMachineState *lams = container_of(notifier,
                                         LoongArchMachineState, machine_done);
+    loongarch_acpi_setup(lams);
     loongarch_build_smbios(lams);
 }
 
@@ -238,7 +241,8 @@ static void sysbus_mmio_map_loongarch(SysBusDevice *dev, int n, hwaddr addr, Mem
     memory_region_add_subregion(iocsr, addr, dev->mmio[n].memory);
 }
 
-static void ls3a5000_irq_init(MachineState *machine, CPULoongArchState *env[])
+static DeviceState *ls3a5000_irq_init(MachineState *machine,
+                                    CPULoongArchState *env[])
 {
     LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
     DeviceState *extioi, *pch_pic, *pch_msi;
@@ -285,6 +289,7 @@ static void ls3a5000_irq_init(MachineState *machine, CPULoongArchState *env[])
     for (i = 0; i < 224; i++) {
         sysbus_connect_irq(d, i, lams->pch_irq[i + 32]);
     }
+    return pch_pic;
 }
 
 /* Network support */
@@ -325,6 +330,7 @@ static void ls3a5000_virt_init(MachineState *machine)
     char *filename;
     MemoryRegion *bios = g_new(MemoryRegion, 1);
     ram_addr_t offset = 0;
+    DeviceState *pch_pic;
 
     if (!cpu_model) {
         cpu_model = LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000");
@@ -439,10 +445,10 @@ static void ls3a5000_virt_init(MachineState *machine)
     create_unimplemented_device("ls7a-node3", 0x3EFDFB000274, 0x4);
 
     /* Initialize the IO interrupt subsystem */
-    ls3a5000_irq_init(machine, cpu_states);
+    pch_pic = ls3a5000_irq_init(machine, cpu_states);
 
     /* Init the north bridge */
-    pci_bus = ls7a_init(machine, lams->pch_irq);
+    pci_bus = ls7a_init(machine, pch_pic, lams->pch_irq);
 
     /* Network card */
     network_init(pci_bus);
@@ -457,6 +463,40 @@ static void ls3a5000_virt_init(MachineState *machine)
     LOONGARCH_SIMPLE_MMIO_OPS(CPUNAME_REG, "loongarch_cpuname", 0x8);
 }
 
+bool loongarch_is_acpi_enabled(LoongArchMachineState *lams)
+{
+    if (lams->acpi == ON_OFF_AUTO_OFF) {
+        return false;
+    }
+    return true;
+}
+
+static void loongarch_get_acpi(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
+{
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
+    OnOffAuto acpi = lams->acpi;
+
+    visit_type_OnOffAuto(v, name, &acpi, errp);
+}
+
+static void loongarch_set_acpi(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
+{
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
+
+    visit_type_OnOffAuto(v, name, &lams->acpi, errp);
+}
+
+static void loongarch_machine_initfn(Object *obj)
+{
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
+
+    lams->acpi = ON_OFF_AUTO_AUTO;
+    lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
+    lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
+}
+
 static void loongarch_class_init(ObjectClass *oc, void *data)
 {
     MachineClass *mc = MACHINE_CLASS(oc);
@@ -473,6 +513,12 @@ static void loongarch_class_init(ObjectClass *oc, void *data)
     mc->block_default_type = IF_VIRTIO;
     mc->default_boot_order = "c";
     mc->no_cdrom = 1;
+
+    object_class_property_add(oc, "acpi", "OnOffAuto",
+        loongarch_get_acpi, loongarch_set_acpi,
+        NULL, NULL);
+    object_class_property_set_description(oc, "acpi",
+        "Enable ACPI");
 }
 
 static const TypeInfo loongarch_machine_types[] = {
@@ -480,6 +526,7 @@ static const TypeInfo loongarch_machine_types[] = {
         .name           = TYPE_LOONGARCH_MACHINE,
         .parent         = TYPE_MACHINE,
         .instance_size  = sizeof(LoongArchMachineState),
+        .instance_init = loongarch_machine_initfn,
         .class_init     = loongarch_class_init,
     }
 };
diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build
index 3fabfa72dc..ebe6038fff 100644
--- a/hw/loongarch/meson.build
+++ b/hw/loongarch/meson.build
@@ -2,5 +2,6 @@ loongarch_ss = ss.source_set()
 loongarch_ss.add(files('loongarch_int.c'))
 loongarch_ss.add(when: 'CONFIG_LOONGSON_3A5000', if_true: files('ls3a5000_virt.c', 'ipi.c'))
 loongarch_ss.add(when: 'CONFIG_FW_CFG_LOONGARCH', if_true: files('fw_cfg.c'))
+loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c'))
 
 hw_arch += {'loongarch': loongarch_ss}
diff --git a/hw/pci-host/ls7a.c b/hw/pci-host/ls7a.c
index 294715801f..60a5bd265b 100644
--- a/hw/pci-host/ls7a.c
+++ b/hw/pci-host/ls7a.c
@@ -117,7 +117,7 @@ static void ls7a_pciehost_realize(DeviceState *dev, Error **errp)
     pcie_host_mmcfg_update(e, true, LS_PCIECFG_BASE, LS_PCIECFG_SIZE);
 }
 
-PCIBus *ls7a_init(MachineState *machine, qemu_irq *pic)
+PCIBus *ls7a_init(MachineState *machine, DeviceState *pch_pic, qemu_irq *pic)
 {
     DeviceState *dev;
     PCIHostState *phb;
@@ -141,6 +141,8 @@ PCIBus *ls7a_init(MachineState *machine, qemu_irq *pic)
 
     pci_realize_and_unref(pci_dev, phb->bus, &error_fatal);
 
+    ls7a_pm_init(pci_dev, &pbs->pm, pch_pic);
+
     return phb->bus;
 }
 
diff --git a/include/hw/acpi/ls7a.h b/include/hw/acpi/ls7a.h
new file mode 100644
index 0000000000..9c511f956b
--- /dev/null
+++ b/include/hw/acpi/ls7a.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU GMCH/LS7A PCI PM Emulation
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef HW_ACPI_LS7A_H
+#define HW_ACPI_LS7A_H
+
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/cpu_hotplug.h"
+#include "hw/acpi/cpu.h"
+#include "hw/acpi/memory_hotplug.h"
+#include "hw/acpi/acpi_dev_interface.h"
+#include "hw/acpi/tco.h"
+
+#define LS7A_ACPI_IO_BASE         0x800
+#define LS7A_ACPI_IO_SIZE         0x100
+#define LS7A_PM_EVT_BLK           (0x0C) /* 4 bytes */
+#define LS7A_PM_CNT_BLK           (0x14) /* 2 bytes */
+#define LS7A_GPE0_STS_REG         (0x28) /* 4 bytes */
+#define LS7A_GPE0_ENA_REG         (0x2C) /* 4 bytes */
+#define LS7A_GPE0_RESET_REG       (0x30) /* 4 bytes */
+#define LS7A_PM_TMR_BLK           (0x18) /* 4 bytes */
+#define LS7A_GPE0_LEN             (8)
+#define ACPI_IO_BASE              (LS7A_ACPI_REG_BASE)
+#define ACPI_GPE0_LEN             (LS7A_GPE0_LEN)
+#define ACPI_IO_SIZE              (LS7A_ACPI_IO_SIZE)
+#define ACPI_SCI_IRQ              (LS7A_SCI_IRQ)
+
+typedef struct LS7APCIPMRegs {
+    /*
+     * In ls7a spec says that pm1_cnt register is 32bit width and
+     * that the upper 16bits are reserved and unused.
+     * PM1a_CNT_BLK = 2 in FADT so it is defined as uint16_t.
+     */
+    ACPIREGS acpi_regs;
+
+    MemoryRegion iomem;
+    MemoryRegion iomem_gpe;
+    MemoryRegion iomem_reset;
+
+    qemu_irq irq;      /* SCI */
+
+    uint32_t pm_io_base;
+    Notifier powerdown_notifier;
+} LS7APCIPMRegs;
+
+void ls7a_pm_init(PCIDevice *pci_device, LS7APCIPMRegs *ls7a,
+                  DeviceState *pch_pic);
+extern const VMStateDescription vmstate_ls7a_pm;
+#endif /* HW_ACPI_LS7A_H */
diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
index b165b4fd07..ea80e9f9a1 100644
--- a/include/hw/loongarch/loongarch.h
+++ b/include/hw/loongarch/loongarch.h
@@ -54,6 +54,9 @@ typedef struct LoongArchMachineState {
     gipiState   *gipi;
     qemu_irq    *pch_irq;
     FWCfgState  *fw_cfg;
+    OnOffAuto   acpi;
+    char        *oem_id;
+    char        *oem_table_id;
 } LoongArchMachineState;
 
 #define TYPE_LOONGARCH_MACHINE  MACHINE_TYPE_NAME("loongson7a")
@@ -62,4 +65,6 @@ DECLARE_INSTANCE_CHECKER(LoongArchMachineState, LOONGARCH_MACHINE,
 
 void cpu_loongarch_init_irq(LoongArchCPU *cpu);
 int cpu_init_ipi(LoongArchMachineState *lams, qemu_irq irq, int cpu);
+bool loongarch_is_acpi_enabled(LoongArchMachineState *lams);
+void loongarch_acpi_setup(LoongArchMachineState *lams);
 #endif
diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h
index 019f11c56a..2ecdac768b 100644
--- a/include/hw/pci-host/ls7a.h
+++ b/include/hw/pci-host/ls7a.h
@@ -11,6 +11,7 @@
 #include "hw/pci/pci.h"
 #include "hw/pci/pcie_host.h"
 #include "hw/pci-host/pam.h"
+#include "hw/acpi/ls7a.h"
 #include "qemu/units.h"
 #include "qemu/range.h"
 #include "qom/object.h"
@@ -23,6 +24,9 @@
 
 #define LS7A_PCI_IO_BASE        0x18000000UL
 #define LS7A_PCI_IO_SIZE        0x00010000
+#define LS7A_PCI_MEM_BASE       0x20000000
+#define LS7A_PCI_MEM_SIZE       0x3fffffff
+
 #define LS7A_PCH_REG_BASE       0x10000000UL
 #define LS7A_IOAPIC_REG_BASE    (LS7A_PCH_REG_BASE)
 #define LS7A_PCH_MSI_ADDR_LOW   0x2FF00000UL
@@ -34,6 +38,8 @@
 #define LS7A_MISC_REG_BASE      (LS7A_PCH_REG_BASE + 0x00080000)
 #define LS7A_RTC_REG_BASE       (LS7A_MISC_REG_BASE + 0x00050100)
 #define LS7A_RTC_LEN            0x100
+#define LS7A_ACPI_REG_BASE      (LS7A_MISC_REG_BASE + 0x00050000)
+#define LS7A_SCI_IRQ            (LOONGARCH_PCH_IRQ_BASE + 4)
 
 typedef struct LS7APCIState LS7APCIState;
 typedef struct LS7APCIEHost {
@@ -47,6 +53,7 @@ typedef struct LS7APCIEHost {
 struct LS7APCIState {
     PCIDevice dev;
     LS7APCIEHost *pciehost;
+    LS7APCIPMRegs pm;
 };
 
 #define TYPE_LS7A_PCIE_HOST_BRIDGE "ls7a1000-pciehost"
@@ -55,5 +62,5 @@ OBJECT_DECLARE_SIMPLE_TYPE(LS7APCIEHost, LS7A_PCIE_HOST_BRIDGE)
 #define TYPE_LS7A_PCIE "ls7a1000_pcie"
 OBJECT_DECLARE_SIMPLE_TYPE(LS7APCIState, LS7A_PCIE)
 
-PCIBus *ls7a_init(MachineState *machine, qemu_irq *irq);
+PCIBus *ls7a_init(MachineState *machine, DeviceState *pch_pic, qemu_irq *irq);
 #endif /* HW_LS7A_H */
-- 
2.27.0



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

* [RFC PATCH v2 29/30] hw/loongarch: Add machine->possible_cpus
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (27 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 28/30] hw/loongarch: Add LoongArch acpi support Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11  1:35 ` [RFC PATCH v2 30/30] hw/loongarch: Add Numa support Xiaojuan Yang
  2021-11-11 14:58 ` [RFC PATCH v2 00/30] Add Loongarch softmmu support Mark Cave-Ayland
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

Use possible_cpus for storing possible topology info

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/loongarch/acpi-build.c    | 16 ++++++++++------
 hw/loongarch/ls3a5000_virt.c | 35 ++++++++++++++++++++++++++++++++++-
 2 files changed, 44 insertions(+), 7 deletions(-)

diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c
index fda4d55f2e..5f1d4070d8 100644
--- a/hw/loongarch/acpi-build.c
+++ b/hw/loongarch/acpi-build.c
@@ -107,7 +107,8 @@ build_facs(GArray *table_data)
 static void
 build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams)
 {
-    MachineState *ms = MACHINE(lams);
+    MachineClass *mc = MACHINE_GET_CLASS(lams);
+    const CPUArchIdList *core_ids = mc->possible_cpu_arch_ids(MACHINE(lams));
     int i;
     AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lams->oem_id,
                         .oem_table_id = lams->oem_table_id };
@@ -118,13 +119,14 @@ build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams)
     build_append_int_noprefix(table_data, 0, 4);
     build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */
 
-    for (i = 0; i < ms->smp.cpus; i++) {
+    for (i = 0; i < core_ids->len; i++) {
+        uint32_t core_id = core_ids->cpus[i].arch_id;
         /* Rev 1.0b, Table 5-13 Processor Core Interrupt Controller Structure */
         build_append_int_noprefix(table_data, 17, 1);       /* Type */
         build_append_int_noprefix(table_data, 15, 1);       /* Length */
         build_append_int_noprefix(table_data, 1, 1);       /* Version */
         build_append_int_noprefix(table_data, i + 1, 4);     /* ACPI Processor ID */
-        build_append_int_noprefix(table_data, i, 4); /* Core ID */
+        build_append_int_noprefix(table_data, core_id, 4); /* Core ID */
         build_append_int_noprefix(table_data, 1, 4); /* Flags */
     }
 
@@ -164,8 +166,9 @@ static void
 build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
 {
     uint64_t i, mem_len, mem_base;
+    MachineClass *mc = MACHINE_GET_CLASS(machine);
     LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
-    MachineState *ms = MACHINE(lams);
+    const CPUArchIdList *core_ids = mc->possible_cpu_arch_ids(machine);
     AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lams->oem_id,
                         .oem_table_id = lams->oem_table_id };
 
@@ -173,13 +176,14 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
     build_append_int_noprefix(table_data, 1, 4); /* Reserved */
     build_append_int_noprefix(table_data, 0, 8); /* Reserved */
 
-    for (i = 0; i < ms->smp.cpus; ++i) {
+    for (i = 0; i < core_ids->len; ++i) {
+        uint32_t core_id = cpu_to_le32(core_ids->cpus[i].arch_id);
         /* 5.2.15.1 Processor Local APIC/SAPIC Affinity Structure */
         build_append_int_noprefix(table_data, 0, 1);  /* Type  */
         build_append_int_noprefix(table_data, 16, 1); /* Length */
         /* Proximity Domain [7:0] */
         build_append_int_noprefix(table_data, 0, 1);
-        build_append_int_noprefix(table_data, i, 1); /* APIC ID */
+        build_append_int_noprefix(table_data, core_id, 1); /* APIC ID */
         /* Flags, Table 5-36 */
         build_append_int_noprefix(table_data, 1, 4);
         build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */
diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
index 35222f70a4..768af074f4 100644
--- a/hw/loongarch/ls3a5000_virt.c
+++ b/hw/loongarch/ls3a5000_virt.c
@@ -331,6 +331,8 @@ static void ls3a5000_virt_init(MachineState *machine)
     MemoryRegion *bios = g_new(MemoryRegion, 1);
     ram_addr_t offset = 0;
     DeviceState *pch_pic;
+    const CPUArchIdList *possible_cpus;
+    MachineClass *mc = MACHINE_GET_CLASS(machine);
 
     if (!cpu_model) {
         cpu_model = LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000");
@@ -346,15 +348,20 @@ static void ls3a5000_virt_init(MachineState *machine)
     address_space_init(lams->address_space_iocsr, lams->system_iocsr, "IOCSR");
 
     /* Init CPUs */
+    possible_cpus = mc->possible_cpu_arch_ids(machine);
     for (i = 0; i < machine->smp.cpus; i++) {
         Object *cpuobj = NULL;
         CPUState *cs;
 
-        cpuobj = object_new(machine->cpu_type);
+        cpuobj = object_new(possible_cpus->cpus[i].type);
+        object_property_set_uint(cpuobj, "id",
+                                 possible_cpus->cpus[i].arch_id, NULL);
 
         cs = CPU(cpuobj);
         cs->cpu_index = i;
 
+        machine->possible_cpus->cpus[i].cpu = cpuobj;
+
         qdev_realize(DEVICE(cpuobj), NULL, &error_fatal);
         object_unref(cpuobj);
 
@@ -497,12 +504,38 @@ static void loongarch_machine_initfn(Object *obj)
     lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
 }
 
+static const CPUArchIdList *loongarch_possible_cpu_arch_ids(MachineState *ms)
+{
+    int i;
+    unsigned int max_cpus = ms->smp.max_cpus;
+
+    if (ms->possible_cpus) {
+        /*
+         * make sure that max_cpus hasn't changed since the first use, i.e.
+         * -smp hasn't been parsed after it
+         */
+        assert(ms->possible_cpus->len == max_cpus);
+        return ms->possible_cpus;
+    }
+
+    ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
+                                  sizeof(CPUArchId) * max_cpus);
+    ms->possible_cpus->len = max_cpus;
+    for (i = 0; i < ms->possible_cpus->len; i++) {
+            ms->possible_cpus->cpus[i].type = ms->cpu_type;
+            ms->possible_cpus->cpus[i].vcpus_count = 1;
+            ms->possible_cpus->cpus[i].arch_id = i;
+    }
+    return ms->possible_cpus;
+}
+
 static void loongarch_class_init(ObjectClass *oc, void *data)
 {
     MachineClass *mc = MACHINE_CLASS(oc);
 
     mc->desc = "Loongson-5000 LS7A1000 machine";
     mc->init = ls3a5000_virt_init;
+    mc->possible_cpu_arch_ids = loongarch_possible_cpu_arch_ids;
     mc->default_ram_size = 1 * GiB;
     mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000");
     mc->default_ram_id = "loongarch.ram";
-- 
2.27.0



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

* [RFC PATCH v2 30/30] hw/loongarch: Add Numa support.
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (28 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 29/30] hw/loongarch: Add machine->possible_cpus Xiaojuan Yang
@ 2021-11-11  1:35 ` Xiaojuan Yang
  2021-11-11 14:58 ` [RFC PATCH v2 00/30] Add Loongarch softmmu support Mark Cave-Ayland
  30 siblings, 0 replies; 62+ messages in thread
From: Xiaojuan Yang @ 2021-11-11  1:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Song Gao

1.From Loongson-3A5000 4 cpus belongs to 1 node.
  Now support mostly 4 nodes 16 cpus.
2.Different nodes access different address spaces. All
  memory access should be handle correctly even nodes
  not assigned memory by numa parameters in the command
  line.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 hw/intc/loongarch_extioi.c         | 76 ++++++++++++++++-----------
 hw/loongarch/acpi-build.c          | 15 +++++-
 hw/loongarch/ipi.c                 |  2 +
 hw/loongarch/ls3a5000_virt.c       | 84 +++++++++++++++++++++++++++++-
 include/hw/intc/loongarch_extioi.h | 10 ++--
 include/hw/loongarch/loongarch.h   | 10 +++-
 target/loongarch/cpu.c             |  1 +
 target/loongarch/cpu.h             |  1 +
 target/loongarch/csr_helper.c      | 15 ++++--
 9 files changed, 174 insertions(+), 40 deletions(-)

diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c
index 592cd8d1e2..a48f4823ec 100644
--- a/hw/intc/loongarch_extioi.c
+++ b/hw/intc/loongarch_extioi.c
@@ -74,12 +74,14 @@ static void extioi_handler(void *opaque, int irq, int level)
 
 static uint32_t extioi_readb(void *opaque, hwaddr addr)
 {
-    loongarch_extioi *state = opaque;
+    loongarch_extioi **backref = (loongarch_extioi **)opaque;
+    loongarch_extioi *state = *backref;
     unsigned long offset, reg_count;
     uint8_t ret;
-    int cpu;
+    int cpu, node_id;
 
     offset = addr & 0xffff;
+    node_id = (backref - state->backref);
 
     if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
         reg_count = (offset - EXTIOI_ENABLE_START);
@@ -91,7 +93,7 @@ static uint32_t extioi_readb(void *opaque, hwaddr addr)
     } else if ((offset >= EXTIOI_COREISR_START) &&
                (offset < EXTIOI_COREISR_END)) {
         reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f);
-        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
+        cpu = node_id * 4 + (((offset - EXTIOI_COREISR_START) >> 8) & 0x3);
         ret = state->coreisr_reg8[cpu][reg_count];
     } else if ((offset >= EXTIOI_IPMAP_START) &&
                (offset < EXTIOI_IPMAP_END)) {
@@ -100,7 +102,7 @@ static uint32_t extioi_readb(void *opaque, hwaddr addr)
     } else if ((offset >= EXTIOI_COREMAP_START) &&
                (offset < EXTIOI_COREMAP_END)) {
         reg_count = (offset - EXTIOI_COREMAP_START);
-        ret = state->coremap_reg8[reg_count];
+        ret = state->coremap_reg8[node_id][reg_count];
     } else if ((offset >= EXTIOI_NODETYPE_START) &&
                (offset < EXTIOI_NODETYPE_END)) {
         reg_count = (offset - EXTIOI_NODETYPE_START);
@@ -113,12 +115,14 @@ static uint32_t extioi_readb(void *opaque, hwaddr addr)
 
 static uint32_t extioi_readw(void *opaque, hwaddr addr)
 {
-    loongarch_extioi *state = opaque;
+    loongarch_extioi **backref = (loongarch_extioi **)opaque;
+    loongarch_extioi *state = *backref;
     unsigned long offset, reg_count;
     uint32_t ret;
-    int cpu;
+    int cpu, node_id;
 
     offset = addr & 0xffff;
+    node_id = (backref - state->backref);
 
     if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
         reg_count = (offset - EXTIOI_ENABLE_START) / 4;
@@ -130,7 +134,7 @@ static uint32_t extioi_readw(void *opaque, hwaddr addr)
     } else if ((offset >= EXTIOI_COREISR_START) &&
                (offset < EXTIOI_COREISR_END)) {
         reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 4;
-        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
+        cpu = node_id * 4 + (((offset - EXTIOI_COREISR_START) >> 8) & 0x3);
         ret = state->coreisr_reg32[cpu][reg_count];
     } else if ((offset >= EXTIOI_IPMAP_START) &&
                (offset < EXTIOI_IPMAP_END)) {
@@ -139,7 +143,7 @@ static uint32_t extioi_readw(void *opaque, hwaddr addr)
     } else if ((offset >= EXTIOI_COREMAP_START) &&
                (offset < EXTIOI_COREMAP_END)) {
         reg_count = (offset - EXTIOI_COREMAP_START) / 4;
-        ret = state->coremap_reg32[reg_count];
+        ret = state->coremap_reg32[node_id][reg_count];
     } else if ((offset >= EXTIOI_NODETYPE_START) &&
                (offset < EXTIOI_NODETYPE_END)) {
         reg_count = (offset - EXTIOI_NODETYPE_START) / 4;
@@ -152,12 +156,14 @@ static uint32_t extioi_readw(void *opaque, hwaddr addr)
 
 static uint64_t extioi_readl(void *opaque, hwaddr addr)
 {
-    loongarch_extioi *state = opaque;
+    loongarch_extioi **backref = (loongarch_extioi **)opaque;
+    loongarch_extioi *state = *backref;
     unsigned long offset, reg_count;
     uint64_t ret;
-    int cpu;
+    int cpu, node_id;
 
     offset = addr & 0xffff;
+    node_id = (backref - state->backref);
 
     if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
         reg_count = (offset - EXTIOI_ENABLE_START) / 8;
@@ -169,7 +175,7 @@ static uint64_t extioi_readl(void *opaque, hwaddr addr)
     } else if ((offset >= EXTIOI_COREISR_START) &&
                (offset < EXTIOI_COREISR_END)) {
         reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 8;
-        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
+        cpu = node_id * 4 + (((offset - EXTIOI_COREISR_START) >> 8) & 0x3);
         ret = state->coreisr_reg64[cpu][reg_count];
     } else if ((offset >= EXTIOI_IPMAP_START) &&
                (offset < EXTIOI_IPMAP_END)) {
@@ -177,7 +183,7 @@ static uint64_t extioi_readl(void *opaque, hwaddr addr)
     } else if ((offset >= EXTIOI_COREMAP_START) &&
                (offset < EXTIOI_COREMAP_END)) {
         reg_count = (offset - EXTIOI_COREMAP_START) / 8;
-        ret = state->coremap_reg64[reg_count];
+        ret = state->coremap_reg64[node_id][reg_count];
     } else if ((offset >= EXTIOI_NODETYPE_START) &&
                (offset < EXTIOI_NODETYPE_END)) {
         reg_count = (offset - EXTIOI_NODETYPE_START) / 8;
@@ -190,13 +196,15 @@ static uint64_t extioi_readl(void *opaque, hwaddr addr)
 
 static void extioi_writeb(void *opaque, hwaddr addr, uint32_t val)
 {
-    loongarch_extioi *state = opaque;
+    loongarch_extioi **backref = (loongarch_extioi **)opaque;
+    loongarch_extioi *state = *backref;
     unsigned long offset, reg_count;
     uint8_t old_data_u8;
-    int cpu, i, ipnum, level, mask, irqnum;
+    int cpu, node_id, i, ipnum, level, mask, irqnum;
 
     offset = addr & 0xffff;
     val = val & 0xffUL;
+    node_id = (backref - state->backref);
 
     if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
         reg_count = (offset - EXTIOI_ENABLE_START);
@@ -234,7 +242,7 @@ static void extioi_writeb(void *opaque, hwaddr addr, uint32_t val)
     } else if ((offset >= EXTIOI_COREISR_START) &&
                (offset < EXTIOI_COREISR_END)) {
         reg_count = (offset - EXTIOI_COREISR_START) & 0x1f;
-        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
+        cpu = node_id * 4 + (((offset - EXTIOI_COREISR_START) >> 8) & 0x3);
 
         /* ext_isr */
         old_data_u8 = state->isr_reg8[reg_count];
@@ -284,8 +292,8 @@ static void extioi_writeb(void *opaque, hwaddr addr, uint32_t val)
         /* Node map different from kernel */
         if (cpu) {
             cpu = ctz32(cpu);
-            state->coremap_reg8[reg_count] = val;
-            state->sw_coremap[reg_count] = cpu;
+            state->coremap_reg8[node_id][reg_count] = val;
+            state->sw_coremap[reg_count] = node_id * 4 + cpu;
         }
     } else if ((offset >= EXTIOI_NODETYPE_START) &&
                (offset < EXTIOI_NODETYPE_END)) {
@@ -298,11 +306,13 @@ static void extioi_writeb(void *opaque, hwaddr addr, uint32_t val)
 
 static void extioi_writew(void *opaque, hwaddr addr, uint32_t val)
 {
-    loongarch_extioi *state = opaque;
-    int cpu, level;
+    loongarch_extioi **backref = (loongarch_extioi **)opaque;
+    loongarch_extioi *state = *backref;
+    int cpu, node_id, level;
     uint32_t offset, old_data_u32, reg_count, mask, i;
 
     offset = addr & 0xffff;
+    node_id = (backref - state->backref);
 
     if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
         reg_count = (offset - EXTIOI_ENABLE_START) / 4;
@@ -327,7 +337,7 @@ static void extioi_writew(void *opaque, hwaddr addr, uint32_t val)
     } else if ((offset >= EXTIOI_COREISR_START) &&
                (offset < EXTIOI_COREISR_END)) {
         reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 4;
-        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
+        cpu = node_id * 4 + (((offset - EXTIOI_COREISR_START) >> 8) & 0x3);
 
         /* Ext_isr */
         old_data_u32 = state->isr_reg32[reg_count];
@@ -369,11 +379,13 @@ static void extioi_writew(void *opaque, hwaddr addr, uint32_t val)
 
 static void extioi_writel(void *opaque, hwaddr addr, uint64_t val)
 {
-    loongarch_extioi *state = (loongarch_extioi *)opaque;
-    int cpu, level;
+    loongarch_extioi **backref = (loongarch_extioi **)opaque;
+    loongarch_extioi *state = *backref;
+    int cpu, node_id, level;
     uint64_t offset, old_data_u64, reg_count, mask, i;
 
     offset = addr & 0xffff;
+    node_id = (backref - state->backref);
 
     if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
         reg_count = (offset - EXTIOI_ENABLE_START) / 8;
@@ -397,7 +409,7 @@ static void extioi_writel(void *opaque, hwaddr addr, uint64_t val)
     } else if ((offset >= EXTIOI_COREISR_START) &&
                (offset < EXTIOI_COREISR_END)) {
         reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 8;
-        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
+        cpu = node_id * 4 + (((offset - EXTIOI_COREISR_START) >> 8) & 0x3);
 
         /* core_ext_ioisr */
         old_data_u64 = state->coreisr_reg64[cpu][reg_count];
@@ -488,7 +500,9 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp)
     LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
     MachineState *ms = MACHINE(lams);
     loongarch_extioi *p = LOONGARCH_EXTIOI(dev);
-    int cpu, pin;
+    int cpu, pin, id, nb_nodes;
+    char str[16];
+    nb_nodes = (ms->smp.cpus - 1) >> 2;
 
     qdev_init_gpio_in(dev, extioi_setirq, EXTIOI_IRQS);
 
@@ -496,9 +510,13 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp)
         sysbus_init_irq(SYS_BUS_DEVICE(dev), &p->irq[i]);
     }
 
-    memory_region_init_io(&p->mmio, OBJECT(p), &extioi_ops, p,
-                          TYPE_LOONGARCH_EXTIOI, 0x900);
-    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &p->mmio);
+    for (id = 0; id <= nb_nodes; id++) {
+        p->backref[id] = p;
+        sprintf(str, "extioi%d", id);
+        memory_region_init_io(&p->mmio[id], OBJECT(p), &extioi_ops,
+                              &p->backref[id], str, 0x900);
+        sysbus_init_mmio(SYS_BUS_DEVICE(dev), &p->mmio[id]);
+    }
 
     for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
         for (pin = 0; pin < LS3A_INTC_IP; pin++) {
@@ -534,8 +552,8 @@ static const VMStateDescription vmstate_loongarch_extioi = {
                               EXTIOI_IRQS_BITMAP_SIZE),
         VMSTATE_UINT8_ARRAY(ipmap_reg8, loongarch_extioi,
                             EXTIOI_IRQS_IPMAP_SIZE),
-        VMSTATE_UINT8_ARRAY(coremap_reg8, loongarch_extioi,
-                            EXTIOI_IRQS_COREMAP_SIZE),
+        VMSTATE_UINT8_2DARRAY(coremap_reg8, loongarch_extioi,
+                              LS3A_NODES, EXTIOI_IRQS_COREMAP_SIZE),
         VMSTATE_UINT16_ARRAY(nodetype_reg16, loongarch_extioi,
                              EXTIOI_IRQS_NODETYPE_SIZE),
         VMSTATE_UINT8_ARRAY(sw_ipmap, loongarch_extioi, EXTIOI_IRQS),
diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c
index 5f1d4070d8..3acc18f74b 100644
--- a/hw/loongarch/acpi-build.c
+++ b/hw/loongarch/acpi-build.c
@@ -182,7 +182,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
         build_append_int_noprefix(table_data, 0, 1);  /* Type  */
         build_append_int_noprefix(table_data, 16, 1); /* Length */
         /* Proximity Domain [7:0] */
-        build_append_int_noprefix(table_data, 0, 1);
+        build_append_int_noprefix(table_data, core_id >> 2, 1);
         build_append_int_noprefix(table_data, core_id, 1); /* APIC ID */
         /* Flags, Table 5-36 */
         build_append_int_noprefix(table_data, 1, 4);
@@ -202,6 +202,14 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
     build_srat_memory(table_data, mem_base, mem_len,
                             0, MEM_AFFINITY_ENABLED);
 
+    if (machine->numa_state) {
+        for (i = 1; i < machine->numa_state->num_nodes; ++i) {
+            mem_base = (i << LOONGARCH_NODE_SHIFT) + 0x80000000;
+            mem_len = machine->numa_state->nodes[i].node_mem;
+            build_srat_memory(table_data, mem_base, mem_len,
+                                i, MEM_AFFINITY_ENABLED);
+        }
+    }
     acpi_table_end(linker, &table);
 }
 
@@ -489,6 +497,11 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
 
     acpi_add_table(table_offsets, tables_blob);
     build_srat(tables_blob, tables->linker, machine);
+    if (machine->numa_state->have_numa_distance) {
+        acpi_add_table(table_offsets, tables_blob);
+        build_slit(tables_blob, tables->linker, machine, lams->oem_id,
+                   lams->oem_table_id);
+    }
 
     acpi_add_table(table_offsets, tables_blob);
     {
diff --git a/hw/loongarch/ipi.c b/hw/loongarch/ipi.c
index 4902205ff5..74f22cebba 100644
--- a/hw/loongarch/ipi.c
+++ b/hw/loongarch/ipi.c
@@ -122,6 +122,7 @@ static const MemoryRegionOps gipi_ops = {
 
 int cpu_init_ipi(LoongArchMachineState *lams, qemu_irq parent, int cpu)
 {
+    int node_id = cpu / 4;
     int core_num = cpu % 4;
     hwaddr addr;
     MemoryRegion *region;
@@ -135,6 +136,7 @@ int cpu_init_ipi(LoongArchMachineState *lams, qemu_irq parent, int cpu)
     lams->gipi->core[cpu].irq = parent;
 
     addr = SMP_GIPI_MAILBOX + core_num * 0x100;
+    addr = addr + ((unsigned long)node_id << LOONGARCH_NODE_SHIFT);
     region = g_new(MemoryRegion, 1);
     sprintf(str, "gipi%d", cpu);
     memory_region_init_io(region, NULL, &gipi_ops,
diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
index 768af074f4..b3381730b7 100644
--- a/hw/loongarch/ls3a5000_virt.c
+++ b/hw/loongarch/ls3a5000_virt.c
@@ -41,6 +41,38 @@ static struct _loaderparams {
 
 CPULoongArchState *cpu_states[LOONGARCH_MAX_VCPUS];
 
+struct la_memmap_entry {
+    uint64_t address;
+    uint64_t length;
+    uint32_t type;
+    uint32_t reserved;
+} ;
+
+static struct la_memmap_entry *la_memmap_table;
+static unsigned la_memmap_entries;
+
+static int la_memmap_add_entry(uint64_t address, uint64_t length, uint32_t type)
+{
+    int i;
+
+    for (i = 0; i < la_memmap_entries; i++) {
+        if (la_memmap_table[i].address == address) {
+            fprintf(stderr, "%s address:0x%lx length:0x%lx already exists\n",
+                     __func__, address, length);
+            return 0;
+        }
+    }
+
+    la_memmap_table = g_renew(struct la_memmap_entry, la_memmap_table,
+                                                      la_memmap_entries + 1);
+    la_memmap_table[la_memmap_entries].address = cpu_to_le64(address);
+    la_memmap_table[la_memmap_entries].length = cpu_to_le64(length);
+    la_memmap_table[la_memmap_entries].type = cpu_to_le32(type);
+    la_memmap_entries++;
+
+    return la_memmap_entries;
+}
+
 static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr)
 {
     return addr & 0x1fffffffll;
@@ -247,12 +279,17 @@ static DeviceState *ls3a5000_irq_init(MachineState *machine,
     LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
     DeviceState *extioi, *pch_pic, *pch_msi;
     SysBusDevice *d;
-    int cpu, pin, i;
+    int cpu, pin, i, id, node_id;
+    unsigned long base;
+    node_id = (machine->smp.cpus - 1) >> 2;
 
     extioi = qdev_new(TYPE_LOONGARCH_EXTIOI);
     d = SYS_BUS_DEVICE(extioi);
     sysbus_realize_and_unref(d, &error_fatal);
-    sysbus_mmio_map_loongarch(d, 0, APIC_BASE, lams->system_iocsr);
+    for (id = 0; id <= node_id; id++) {
+        base = APIC_BASE | (uint64_t)id << LOONGARCH_NODE_SHIFT;
+        sysbus_mmio_map_loongarch(d, id, base, lams->system_iocsr);
+    }
 
     for (i = 0; i < EXTIOI_IRQS; i++) {
         sysbus_connect_irq(d, i, qdev_get_gpio_in(extioi, i));
@@ -333,6 +370,7 @@ static void ls3a5000_virt_init(MachineState *machine)
     DeviceState *pch_pic;
     const CPUArchIdList *possible_cpus;
     MachineClass *mc = MACHINE_GET_CLASS(machine);
+    uint64_t phyAddr = 0;
 
     if (!cpu_model) {
         cpu_model = LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000");
@@ -362,6 +400,8 @@ static void ls3a5000_virt_init(MachineState *machine)
 
         machine->possible_cpus->cpus[i].cpu = cpuobj;
 
+        numa_cpu_pre_plug(&possible_cpus->cpus[i], DEVICE(cpuobj),
+                          &error_fatal);
         qdev_realize(DEVICE(cpuobj), NULL, &error_fatal);
         object_unref(cpuobj);
 
@@ -398,6 +438,22 @@ static void ls3a5000_virt_init(MachineState *machine)
     memory_region_add_subregion(address_space_mem, 0x90000000, highmem);
     offset += highram_size;
 
+    if (machine->numa_state) {
+        for (i = 1; i < machine->numa_state->num_nodes; i++) {
+            char *ramName = g_strdup_printf("loongarch.node%d.ram", i);
+            MemoryRegion *noderam = g_new(MemoryRegion, 1);
+            uint64_t node_size = machine->numa_state->nodes[i].node_mem;
+
+            memory_region_init_alias(noderam, NULL, ramName,
+                                    machine->ram, offset, node_size);
+            phyAddr = (((uint64_t)i) << LOONGARCH_NODE_SHIFT) + 0x80000000;
+            memory_region_add_subregion(address_space_mem,
+                                        phyAddr, noderam);
+            la_memmap_add_entry(phyAddr, node_size, SYSTEM_RAM);
+            offset += node_size;
+        }
+    }
+
     /* load the BIOS image. */
     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
                               machine->firmware ?: LOONGSON3_BIOSNAME);
@@ -423,6 +479,12 @@ static void ls3a5000_virt_init(MachineState *machine)
         fw_cfg_add_kernel_info(lams->fw_cfg);
     }
 
+    if (lams->fw_cfg != NULL) {
+        fw_cfg_add_file(lams->fw_cfg, "etc/memmap",
+                        la_memmap_table,
+                        sizeof(struct la_memmap_entry) * (la_memmap_entries));
+    }
+
     memory_region_init_ram(bios, NULL, "loongarch.bios",
                            LA_BIOS_SIZE, &error_fatal);
     memory_region_set_readonly(bios, true);
@@ -529,6 +591,22 @@ static const CPUArchIdList *loongarch_possible_cpu_arch_ids(MachineState *ms)
     return ms->possible_cpus;
 }
 
+static CpuInstanceProperties
+loongarch_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
+{
+    MachineClass *mc = MACHINE_GET_CLASS(ms);
+    const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
+
+    assert(cpu_index < possible_cpus->len);
+    return possible_cpus->cpus[cpu_index].props;
+}
+
+static int64_t loongarch_get_default_cpu_node_id(const MachineState *ms, int idx)
+{
+    int nb_numa_nodes = ms->numa_state->num_nodes;
+    return idx % nb_numa_nodes;
+}
+
 static void loongarch_class_init(ObjectClass *oc, void *data)
 {
     MachineClass *mc = MACHINE_CLASS(oc);
@@ -536,6 +614,8 @@ static void loongarch_class_init(ObjectClass *oc, void *data)
     mc->desc = "Loongson-5000 LS7A1000 machine";
     mc->init = ls3a5000_virt_init;
     mc->possible_cpu_arch_ids = loongarch_possible_cpu_arch_ids;
+    mc->cpu_index_to_instance_props = loongarch_cpu_index_to_props;
+    mc->get_default_cpu_node_id = loongarch_get_default_cpu_node_id;
     mc->default_ram_size = 1 * GiB;
     mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000");
     mc->default_ram_id = "loongarch.ram";
diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h
index f6381b6236..b3a046da96 100644
--- a/include/hw/intc/loongarch_extioi.h
+++ b/include/hw/intc/loongarch_extioi.h
@@ -13,6 +13,7 @@
 
 #define LS3A_INTC_IP               8
 #define MAX_CORES                  LOONGARCH_MAX_VCPUS
+#define LS3A_NODES                 4
 #define EXTIOI_IRQS                (256)
 #define EXTIOI_IRQS_BITMAP_SIZE    (256 / 8)
 /* map to ipnum per 32 irqs */
@@ -75,9 +76,9 @@ typedef struct loongarch_extioi {
         uint8_t ipmap_reg8[EXTIOI_IRQS_IPMAP_SIZE];
     };
     union {
-        uint64_t coremap_reg64[EXTIOI_IRQS_COREMAP_SIZE / 8];
-        uint32_t coremap_reg32[EXTIOI_IRQS_COREMAP_SIZE / 4];
-        uint8_t coremap_reg8[EXTIOI_IRQS_COREMAP_SIZE];
+        uint64_t coremap_reg64[LS3A_NODES][EXTIOI_IRQS_COREMAP_SIZE / 8];
+        uint32_t coremap_reg32[LS3A_NODES][EXTIOI_IRQS_COREMAP_SIZE / 4];
+        uint8_t coremap_reg8[LS3A_NODES][EXTIOI_IRQS_COREMAP_SIZE];
     };
     union {
         uint64_t nodetype_reg64[EXTIOI_IRQS_NODETYPE_SIZE / 4];
@@ -93,7 +94,8 @@ typedef struct loongarch_extioi {
 
     qemu_irq parent_irq[MAX_CORES][LS3A_INTC_IP];
     qemu_irq irq[EXTIOI_IRQS];
-    MemoryRegion mmio;
+    MemoryRegion mmio[LS3A_NODES];
+    struct loongarch_extioi *backref[LS3A_NODES];
 } loongarch_extioi;
 
 #endif /* LOONGARCH_EXTIOI_H */
diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
index ea80e9f9a1..eafe24716a 100644
--- a/include/hw/loongarch/loongarch.h
+++ b/include/hw/loongarch/loongarch.h
@@ -14,7 +14,7 @@
 #include "qemu/queue.h"
 #include "hw/loongarch/gipi.h"
 
-#define LOONGARCH_MAX_VCPUS     4
+#define LOONGARCH_MAX_VCPUS     16
 #define PM_MMIO_ADDR            0x10080000UL
 #define PM_MMIO_SIZE            0x100
 #define PM_CNT_MODE             0x10
@@ -42,6 +42,14 @@
 #define INITRD_BASE             0x04000000
 #define COMMAND_LINE_SIZE       4096
 
+#define LOONGARCH_NODE_SHIFT    44
+/* Memory types: */
+#define SYSTEM_RAM              1
+#define SYSTEM_RAM_RESERVED     2
+#define ACPI_TABLE              3
+#define ACPI_NVS                4
+#define SYSTEM_PMEM             5
+
 typedef struct LoongArchMachineState {
     /*< private >*/
     MachineState parent_obj;
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index a6010deef0..1c44a12049 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -538,6 +538,7 @@ static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model)
 static Property loongarch_cpu_properties[] = {
     DEFINE_PROP_INT32("core-id", LoongArchCPU, core_id, -1),
     DEFINE_PROP_UINT32("id", LoongArchCPU, id, UNASSIGNED_CPU_ID),
+    DEFINE_PROP_INT32("node-id", LoongArchCPU, node_id, CPU_UNSET_NUMA_NODE_ID),
     DEFINE_PROP_END_OF_LIST()
 };
 
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index b7ef0b8b3c..d72924295e 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -376,6 +376,7 @@ struct LoongArchCPU {
     CPULoongArchState env;
     uint32_t id;
     int32_t core_id;
+    int32_t node_id; /* NUMA node this CPU belongs to */
 };
 
 #define TYPE_LOONGARCH_CPU "loongarch-cpu"
diff --git a/target/loongarch/csr_helper.c b/target/loongarch/csr_helper.c
index c9e17bed20..1b21654f68 100644
--- a/target/loongarch/csr_helper.c
+++ b/target/loongarch/csr_helper.c
@@ -242,9 +242,13 @@ uint64_t helper_iocsr_read(CPULoongArchState *env, target_ulong r_addr,
 {
     LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
     int cpuid = env_cpu(env)->cpu_index;
+    target_ulong node_addr = (target_ulong)(cpuid & 0x3c) << 42;
+    
 
     if (((r_addr & 0xff00) == 0x1000) || ((r_addr & 0xff00) == 0x1800)) {
-        r_addr = r_addr + ((target_ulong)(cpuid & 0x3) << 8);
+        r_addr = r_addr + ((target_ulong)(cpuid & 0x3) << 8) + node_addr;
+    } else if (((r_addr & 0xf000) == 0x1000)) {
+        r_addr = r_addr + node_addr;
     }
 
     if (size == 1) {
@@ -269,6 +273,7 @@ void helper_iocsr_write(CPULoongArchState *env, target_ulong w_addr,
     LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
     int cpuid = env_cpu(env)->cpu_index;
     int mask, i;
+    target_ulong node_addr;
 
     /*
      * For IPI send, Mail send, ANY send adjust addr and val
@@ -290,10 +295,11 @@ void helper_iocsr_write(CPULoongArchState *env, target_ulong w_addr,
         val = val >> 32;
         mask = (val >> 27) & 0xf;
         size = 1;
+        node_addr = ((target_ulong)(cpuid & 0x3c) << 42);
 
         for (i = 0; i < 4; i++) {
             if (!((mask >> i) & 1)) {
-                address_space_stb(lams->address_space_iocsr, w_addr,
+                address_space_stb(lams->address_space_iocsr, w_addr + node_addr,
                                   val, MEMTXATTRS_UNSPECIFIED, NULL);
             }
             w_addr = w_addr + 1;
@@ -302,8 +308,11 @@ void helper_iocsr_write(CPULoongArchState *env, target_ulong w_addr,
         return;
     }
 
+    node_addr = (target_ulong)(cpuid & 0x3c) << 42;
     if (((w_addr & 0xff00) == 0x1000) || ((w_addr & 0xff00) == 0x1800)) {
-        w_addr = w_addr + ((target_ulong)(cpuid & 0x3) << 8);
+        w_addr = w_addr + ((target_ulong)(cpuid & 0x3) << 8) + node_addr;
+    } else if (((w_addr & 0xf000) == 0x1000)) {
+        w_addr = w_addr + node_addr;
     }
 
     if (size == 1) {
-- 
2.27.0



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

* Re: [RFC PATCH v2 01/30] target/loongarch: Update README
  2021-11-11  1:34 ` [RFC PATCH v2 01/30] target/loongarch: Update README Xiaojuan Yang
@ 2021-11-11 11:50   ` chen huacai
  2021-11-15  3:34     ` yangxiaojuan
  0 siblings, 1 reply; 62+ messages in thread
From: chen huacai @ 2021-11-11 11:50 UTC (permalink / raw)
  To: Xiaojuan Yang; +Cc: qemu-level, Song Gao

Hi, Xiaojuan,

On Thu, Nov 11, 2021 at 9:41 AM Xiaojuan Yang <yangxiaojuan@loongson.cn> wrote:
>
> Mainly introduce how to run the softmmu
>
> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao <gaosong@loongson.cn>
> ---
>  target/loongarch/README | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
>
> diff --git a/target/loongarch/README b/target/loongarch/README
> index 09f809cf80..6f64bde22f 100644
> --- a/target/loongarch/README
> +++ b/target/loongarch/README
> @@ -71,6 +71,26 @@
>        ./qemu-loongarch64  /opt/clfs/usr/bin/pwd
>        ...
>
> +- Softmmu emulation
> +
> +  Add support softmmu emulation support in the following series patches.
> +  Mainly emulate a virt 3A5000 board that is not exactly the same as the host.
> +  Kernel code is on the github and the uefi code will be opened in the near future.
> +  All required binaries can get from github for test.
> +
> +  1.Download kernel and the cross-tools.(vmlinux)
> +
> +      wget https://github.com/loongson/linux
This is a git repo URL, I think we cannot use wget to download.

> +      wget https://github.com/loongson/build-tools/releases/latest/download/loongarch64-clfs-20210831-cross-tools.tar.xz
> +
> +  2.Download the clfs-system and made a ramdisk with busybox.(ramdisk)
> +
> +  3.Run with command,eg:
> +
> +   ./build/qemu-system-loongarch64 -m 4G -smp 16 --cpu Loongson-3A5000 --machine loongson7a -kernel ./vmlinux -initrd ./ramdisk  -append "root=/dev/ram console=ttyS0,115200 rdinit=/sbin/init loglevel=8" -monitor tcp::4000,server,nowait -nographic
It isn't recommended to use "loongson7a" as the machine name. In my
opinion, we will have two types of machines run in qemu (One is an
emulated LS7A and the other is pure virtual). I think we can call them
"loongson3-ls7a" and "loongson3-virt".

Huacai
> +
> +The vmlinux, ramdisk and uefi binary loongarch_bios.bin can get from :
> +    git clone https://github.com/yangxiaojuan-loongson/qemu-binary
>
>  - Note.
>    We can get the latest LoongArch documents or LoongArch tools at https://github.com/loongson/
> --
> 2.27.0
>
>


-- 
Huacai Chen


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

* Re: [RFC PATCH v2 15/30] hw/pci-host: Add ls7a1000 PCIe Host bridge support for Loongson Platform
  2021-11-11  1:35 ` [RFC PATCH v2 15/30] hw/pci-host: Add ls7a1000 PCIe Host bridge support for Loongson Platform Xiaojuan Yang
@ 2021-11-11 13:17   ` Mark Cave-Ayland
  0 siblings, 0 replies; 62+ messages in thread
From: Mark Cave-Ayland @ 2021-11-11 13:17 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/2021 01:35, Xiaojuan Yang wrote:

Hi Xiaojuan,

> This is a model of the PCIe Host Bridge found on a Loongson-5000
> processor. It includes a interrupt controller, some interface for
> pci and nonpci devices we only emulate part devices for tcg mode.
> It support for MSI and MSIX interrupt sources.
> 
> For more detailed info about ls7a1000 you can see the doc at
> https://github.com/loongson/LoongArch-Documentation/releases/latest/
> download/Loongson-7A1000-usermanual-2.00-EN.pdf
> 
> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao <gaosong@loongson.cn>
> ---
>   hw/pci-host/Kconfig        |   4 +
>   hw/pci-host/ls7a.c         | 187 +++++++++++++++++++++++++++++++++++++
>   hw/pci-host/meson.build    |   1 +
>   include/hw/pci-host/ls7a.h |  47 ++++++++++
>   4 files changed, 239 insertions(+)
>   create mode 100644 hw/pci-host/ls7a.c
>   create mode 100644 include/hw/pci-host/ls7a.h
> 
> diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig
> index 2b5f7d58cc..b02a9d1454 100644
> --- a/hw/pci-host/Kconfig
> +++ b/hw/pci-host/Kconfig
> @@ -77,3 +77,7 @@ config MV64361
>       bool
>       select PCI
>       select I8259
> +
> +config PCI_EXPRESS_7A
> +    bool
> +    select PCI_EXPRESS
> diff --git a/hw/pci-host/ls7a.c b/hw/pci-host/ls7a.c
> new file mode 100644
> index 0000000000..90b9fe4830
> --- /dev/null
> +++ b/hw/pci-host/ls7a.c
> @@ -0,0 +1,187 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * QEMU Loongson 7A1000 North Bridge Emulation
> + *
> + * Copyright (C) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#include "qemu/osdep.h"
> +
> +#include "hw/pci/pci.h"
> +#include "hw/pci/pcie_host.h"
> +#include "qapi/error.h"
> +#include "hw/irq.h"
> +#include "hw/pci/pci_bridge.h"
> +#include "hw/pci/pci_bus.h"
> +#include "sysemu/reset.h"
> +#include "hw/pci-host/ls7a.h"
> +#include "migration/vmstate.h"
> +
> +static const VMStateDescription vmstate_ls7a_pcie = {
> +    .name = "LS7A_PCIE",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_PCI_DEVICE(dev, LS7APCIState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void pci_ls7a_config_write(void *opaque, hwaddr addr,
> +                                  uint64_t val, unsigned size)
> +{
> +    pci_data_write(opaque, addr, val, size);
> +}
> +
> +static uint64_t pci_ls7a_config_read(void *opaque,
> +                                     hwaddr addr, unsigned size)
> +{
> +    uint64_t val;
> +
> +    val = pci_data_read(opaque, addr, size);
> +
> +    return val;
> +}
> +
> +static const MemoryRegionOps pci_ls7a_config_ops = {
> +    .read = pci_ls7a_config_read,
> +    .write = pci_ls7a_config_write,
> +    .valid = {
> +        .min_access_size = 1,
> +        .max_access_size = 4,
> +    },
> +    .impl = {
> +        .min_access_size = 1,
> +        .max_access_size = 4,
> +    },
> +    .endianness = DEVICE_NATIVE_ENDIAN,

DEVICE_NATIVE_ENDIAN normally isn't the right thing to use: should this be 
DEVICE_LITTLE_ENDIAN or DEVICE_BIG_ENDIAN?

> +};
> +
> +static void ls7a_pciehost_realize(DeviceState *dev, Error **errp)
> +{
> +    LS7APCIEHost *pciehost = LS7A_PCIE_HOST_BRIDGE(dev);
> +    PCIExpressHost *e = PCIE_HOST_BRIDGE(dev);
> +    PCIHostState *phb = PCI_HOST_BRIDGE(e);
> +
> +    phb->bus = pci_register_root_bus(dev, "pcie.0", NULL,
> +                                     NULL, pciehost,
> +                                     get_system_memory(), get_system_io(),
> +                                     PCI_DEVFN(1, 0), 128, TYPE_PCIE_BUS);
> +
> +    memory_region_init_io(&pciehost->pci_conf, OBJECT(dev),
> +                          &pci_ls7a_config_ops, phb->bus,
> +                          "ls7a_pci_conf", HT1LO_PCICFG_SIZE);
> +    memory_region_add_subregion(get_system_memory(), HT1LO_PCICFG_BASE,
> +                                &pciehost->pci_conf);
> +
> +    /* Add ls7a pci-io */
> +    memory_region_init_alias(&pciehost->pci_io, OBJECT(dev), "ls7a-pci-io",
> +                             get_system_io(), 0, LS7A_PCI_IO_SIZE);
> +    memory_region_add_subregion(get_system_memory(), LS7A_PCI_IO_BASE,
> +                                &pciehost->pci_io);
> +
> +    pcie_host_mmcfg_update(e, true, LS_PCIECFG_BASE, LS_PCIECFG_SIZE);
> +}
> +
> +PCIBus *ls7a_init(MachineState *machine, qemu_irq *pic)
> +{
> +    DeviceState *dev;
> +    PCIHostState *phb;
> +    LS7APCIState *pbs;
> +    LS7APCIEHost *pciehost;
> +    PCIDevice *pci_dev;
> +    PCIExpressHost *e;
> +
> +    dev = qdev_new(TYPE_LS7A_PCIE_HOST_BRIDGE);
> +    e = PCIE_HOST_BRIDGE(dev);
> +    phb = PCI_HOST_BRIDGE(e);
> +    pciehost = LS7A_PCIE_HOST_BRIDGE(dev);
> +    pciehost->pic = pic;

pciehost->pic should be configured as a qdev gpio in the LS7A_PCIE_HOST_BRIDGE device 
.instance_init function using qdev_init_gpio_out() which is then wired up using 
qdev_connect_gpio_out().

> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +
> +    pci_dev = pci_new(PCI_DEVFN(0, 0), TYPE_LS7A_PCIE);
> +    pbs = LS7A_PCIE(pci_dev);
> +    pbs->pciehost = pciehost;

Here pbs->pciehost should be defined as a qdev link property for the LS7A_PCIE device 
(search for DEFINE_PROP_LINK for an example of this)

> +    pbs->pciehost->pci_dev = pbs;

... which means that this can be set in the ls7a_realize() function for the LS7A_PCIE 
device that should be added.

> +    pci_realize_and_unref(pci_dev, phb->bus, &error_fatal);
> +
> +    return phb->bus;
> +}

Global init functions such as ls7a_init() above shouldn't be used anymore since they 
tend to use board-specific logic and prevent future work towards allowing machine 
configuration from a separate configuration file (hence the reason for removing the 
use of pointers above).

> +static void ls7a_reset(DeviceState *qdev)
> +{
> +    uint64_t wmask;
> +    wmask = ~(-1);

Can you combine these onto a single line? And doesn't this fail on 32-bit hosts 
without being ~(-1ULL)?

> +    PCIDevice *dev = PCI_DEVICE(qdev);
> +
> +    pci_set_word(dev->config + PCI_STATUS, 0x0010);
> +    pci_set_word(dev->wmask + PCI_STATUS, wmask & 0xffff);
> +    pci_set_word(dev->cmask + PCI_STATUS, 0xffff);
> +    pci_set_byte(dev->config + PCI_HEADER_TYPE, 0x1);
> +    pci_set_byte(dev->wmask + PCI_HEADER_TYPE, wmask & 0xff);
> +    pci_set_byte(dev->cmask + PCI_HEADER_TYPE, 0xff);
> +    pci_set_word(dev->config + PCI_SUBSYSTEM_VENDOR_ID, 0x0014);
> +    pci_set_word(dev->wmask + PCI_SUBSYSTEM_VENDOR_ID, wmask & 0xffff);
> +    pci_set_word(dev->cmask + PCI_SUBSYSTEM_VENDOR_ID, 0xffff);
> +    pci_set_word(dev->config + PCI_SUBSYSTEM_ID, 0x7a00);
> +    pci_set_word(dev->wmask + PCI_SUBSYSTEM_ID, wmask & 0xffff);
> +    pci_set_word(dev->cmask + PCI_SUBSYSTEM_ID, 0xffff);
> +    pci_set_byte(dev->config + PCI_CAPABILITY_LIST, 0x40);
> +    pci_set_byte(dev->wmask + PCI_CAPABILITY_LIST, wmask & 0xff);
> +    pci_set_byte(dev->cmask + PCI_CAPABILITY_LIST, 0xff);
> +}
> +
> +static void ls7a_pcie_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> +
> +    k->vendor_id = 0x0014;
> +    k->device_id = 0x7a00;
> +    k->revision = 0x00;
> +    k->class_id = PCI_CLASS_BRIDGE_HOST;
> +    dc->reset = ls7a_reset;
> +    dc->desc = "LS7A1000 PCIE Host bridge";
> +    dc->vmsd = &vmstate_ls7a_pcie;
> +    /*
> +     * PCI-facing part of the host bridge, not usable without the
> +     * host-facing part, which can't be device_add'ed, yet.
> +     */
> +    dc->user_creatable = false;
> +}
> +
> +static const TypeInfo ls7a_pcie_device_info = {
> +    .name          = TYPE_LS7A_PCIE,
> +    .parent        = TYPE_PCI_DEVICE,
> +    .instance_size = sizeof(LS7APCIState),
> +    .class_init    = ls7a_pcie_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
> +        { },
> +    },
> +};
> +
> +static void ls7a_pciehost_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    dc->realize = ls7a_pciehost_realize;
> +    dc->fw_name = "pci";
> +    dc->user_creatable = false;
> +}
> +
> +static const TypeInfo ls7a_pciehost_info = {
> +    .name          = TYPE_LS7A_PCIE_HOST_BRIDGE,
> +    .parent        = TYPE_PCIE_HOST_BRIDGE,
> +    .instance_size = sizeof(LS7APCIEHost),
> +    .class_init    = ls7a_pciehost_class_init,
> +};
> +
> +static void ls7a_register_types(void)
> +{
> +    type_register_static(&ls7a_pciehost_info);
> +    type_register_static(&ls7a_pcie_device_info);
> +}
> +
> +type_init(ls7a_register_types)
> diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build
> index 4c4f39c15c..c4955455fd 100644
> --- a/hw/pci-host/meson.build
> +++ b/hw/pci-host/meson.build
> @@ -11,6 +11,7 @@ pci_ss.add(when: 'CONFIG_PCI_SABRE', if_true: files('sabre.c'))
>   pci_ss.add(when: 'CONFIG_XEN_IGD_PASSTHROUGH', if_true: files('xen_igd_pt.c'))
>   pci_ss.add(when: 'CONFIG_REMOTE_PCIHOST', if_true: files('remote.c'))
>   pci_ss.add(when: 'CONFIG_SH_PCI', if_true: files('sh_pci.c'))
> +pci_ss.add(when: 'CONFIG_PCI_EXPRESS_7A', if_true: files('ls7a.c'))
>   
>   # PPC devices
>   pci_ss.add(when: 'CONFIG_RAVEN_PCI', if_true: files('raven.c'))
> diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h
> new file mode 100644
> index 0000000000..6b5ba3b442
> --- /dev/null
> +++ b/include/hw/pci-host/ls7a.h
> @@ -0,0 +1,47 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * QEMU LoongArch CPU
> + *
> + * Copyright (c) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#ifndef HW_LS7A_H
> +#define HW_LS7A_H
> +
> +#include "hw/pci/pci.h"
> +#include "hw/pci/pcie_host.h"
> +#include "hw/pci-host/pam.h"
> +#include "qemu/units.h"
> +#include "qemu/range.h"
> +#include "qom/object.h"
> +
> +#define HT1LO_PCICFG_BASE        0x1a000000
> +#define HT1LO_PCICFG_SIZE        0x02000000
> +
> +#define LS_PCIECFG_BASE          0x20000000
> +#define LS_PCIECFG_SIZE          0x08000000
> +
> +#define LS7A_PCI_IO_BASE        0x18000000UL
> +#define LS7A_PCI_IO_SIZE        0x00010000
> +typedef struct LS7APCIState LS7APCIState;
> +typedef struct LS7APCIEHost {
> +    PCIExpressHost parent_obj;
> +    LS7APCIState *pci_dev;
> +    qemu_irq *pic;
> +    MemoryRegion pci_conf;
> +    MemoryRegion pci_io;
> +} LS7APCIEHost;
> +
> +struct LS7APCIState {
> +    PCIDevice dev;
> +    LS7APCIEHost *pciehost;
> +};
> +
> +#define TYPE_LS7A_PCIE_HOST_BRIDGE "ls7a1000-pciehost"
> +OBJECT_DECLARE_SIMPLE_TYPE(LS7APCIEHost, LS7A_PCIE_HOST_BRIDGE)
> +
> +#define TYPE_LS7A_PCIE "ls7a1000_pcie"
> +OBJECT_DECLARE_SIMPLE_TYPE(LS7APCIState, LS7A_PCIE)
> +
> +PCIBus *ls7a_init(MachineState *machine, qemu_irq *irq);
> +#endif /* HW_LS7A_H */


ATB,

Mark.


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

* Re: [RFC PATCH v2 02/30] target/loongarch: Add CSR registers definition
  2021-11-11  1:35 ` [RFC PATCH v2 02/30] target/loongarch: Add CSR registers definition Xiaojuan Yang
@ 2021-11-11 13:29   ` Richard Henderson
  2021-11-12  2:14     ` yangxiaojuan
  2021-11-11 13:33   ` Richard Henderson
  1 sibling, 1 reply; 62+ messages in thread
From: Richard Henderson @ 2021-11-11 13:29 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
> +#define LOONGARCH_CSR_MISC           0x3 /* Misc config */
> +

Missing bitfield definitions for misc.

> +#define  EXCODE_IP                   64

What's this?

> +#define  EXCCODE_INT                 0
> +#define  EXCCODE_PIL                 1
> +#define  EXCCODE_PIS                 2
> +#define  EXCCODE_PIF                 3
> +#define  EXCCODE_PME                 4
> +#define  EXCCODE_PNR                 5
> +#define  EXCCODE_PNX                 6
> +#define  EXCCODE_PPI                 7
> +#define  EXCCODE_ADE                 8

ADEF vs ADEM?

> +#define  EXCCODE_ALE                 9
> +#define  EXCCODE_BCE                 10
> +#define  EXCCODE_SYS                 11
> +#define  EXCCODE_BRK                 12
> +#define  EXCCODE_INE                 13
> +#define  EXCCODE_IPE                 14
> +#define  EXCCODE_FPD                 15
> +#define  EXCCODE_SXD                 16
> +#define  EXCCODE_ASXD                17
> +#define  EXCCODE_FPE                 18 /* Have different expsubcode */
> +#define  EXCCODE_VFPE                18
> +#define  EXCCODE_WPEF                19 /* Have different expsubcode */
> +#define  EXCCODE_WPEM                19
> +#define  EXCCODE_BTD                 20
> +#define  EXCCODE_BTE                 21

Missing BSPR, HVC, GCSC, GCHC.

> +#define LOONGARCH_CSR_ERA            0x6 /* ERA */

Not really helpful to name the acronym with the acronym.
"Exception return address".

> +#define LOONGARCH_CSR_TLBELO0        0x12 /* TLB EntryLo0 */
> +FIELD(CSR_TLBELO0, V, 0, 1)
> +FIELD(CSR_TLBELO0, D, 1, 1)
> +FIELD(CSR_TLBELO0, PLV, 2, 2)
> +FIELD(CSR_TLBELO0, MAT, 4, 2)
> +FIELD(CSR_TLBELO0, G, 6, 1)
> +FIELD(CSR_TLBELO0, PPN, 12, 36)
> +FIELD(CSR_TLBELO0, NR, 61, 1)
> +FIELD(CSR_TLBELO0, NX, 62, 1)
> +FIELD(CSR_TLBELO0, RPLV, 63, 1)
> +
> +#define LOONGARCH_CSR_TLBELO1        0x13 /* 64 TLB EntryLo1 */
> +FIELD(CSR_TLBELO1, V, 0, 1)
> +FIELD(CSR_TLBELO1, D, 1, 1)
> +FIELD(CSR_TLBELO1, PLV, 2, 2)
> +FIELD(CSR_TLBELO1, MAT, 4, 2)
> +FIELD(CSR_TLBELO1, G, 6, 1)
> +FIELD(CSR_TLBELO1, PPN, 12, 36)
> +FIELD(CSR_TLBELO1, NR, 61, 1)
> +FIELD(CSR_TLBELO1, NX, 62, 1)
> +FIELD(CSR_TLBELO1, RPLV, 63, 1)

Better to define the fields once, dropping the 0/1 from the name.

> +#define LOONGARCH_CSR_PWCL           0x1c /* PWCl */

"Page walk controller, low addr"

> +#define LOONGARCH_CSR_PWCH           0x1d /* PWCh */

"Page walk controller, high addr"

> +#define LOONGARCH_CSR_STLBPS     0x1e /* 64 */

64?  "STLB Page size".

> +#define LOONGARCH_CSR_RVACFG         0x1f

"Reduced virtual address config"

> +/* Save registers */
> +#define LOONGARCH_CSR_SAVE0            0x30
> +#define LOONGARCH_CSR_SAVE1            0x31
> +#define LOONGARCH_CSR_SAVE2            0x32
> +#define LOONGARCH_CSR_SAVE3            0x33
> +#define LOONGARCH_CSR_SAVE4            0x34
> +#define LOONGARCH_CSR_SAVE5            0x35
> +#define LOONGARCH_CSR_SAVE6            0x36
> +#define LOONGARCH_CSR_SAVE7            0x37

Might as well must define SAVE0, and comment that the count is in PRCFG1.SAVE_NUM.

> +#define  CSR_DMW_BASE_SH             48

What's this?  It looks like you should be using TARGET_VIRT_ADDR_SPACE_BITS anyway.

> +#define dmwin_va2pa(va) \
> +    (va & (((unsigned long)1 << CSR_DMW_BASE_SH) - 1))

Using unsigned long is wrong, breaking 32-bit hosts.
You want

     ((va) & MAKE_64BIT_MASK(0, TARGET_VIRT_ADDR_SPACE_BITS))

> +/* Performance Counter registers */
> +#define LOONGARCH_CSR_PERFCTRL0      0x200 /* 32 perf event 0 config */
> +#define LOONGARCH_CSR_PERFCNTR0      0x201 /* 64 perf event 0 count value */
> +#define LOONGARCH_CSR_PERFCTRL1      0x202 /* 32 perf event 1 config */
> +#define LOONGARCH_CSR_PERFCNTR1      0x203 /* 64 perf event 1 count value */
> +#define LOONGARCH_CSR_PERFCTRL2      0x204 /* 32 perf event 2 config */
> +#define LOONGARCH_CSR_PERFCNTR2      0x205 /* 64 perf event 2 count value */
> +#define LOONGARCH_CSR_PERFCTRL3      0x206 /* 32 perf event 3 config */
> +#define LOONGARCH_CSR_PERFCNTR3      0x207 /* 64 perf event 3 count value */

Perhaps better to define

#define LOONGARCH_CSR_PERFCTRL(N)  (0x200 + 2 * N)

etc.

> +#define LOONGARCH_CSR_DB0ADDR        0x310 /* data breakpoint 0 address */
> +#define LOONGARCH_CSR_DB0MASK        0x311 /* data breakpoint 0 mask */
> +#define LOONGARCH_CSR_DB0CTL         0x312 /* data breakpoint 0 control */
> +#define LOONGARCH_CSR_DB0ASID        0x313 /* data breakpoint 0 asid */

Likewise.

> +    uint64_t CSR_SAVE0;
> +    uint64_t CSR_SAVE1;
> +    uint64_t CSR_SAVE2;
> +    uint64_t CSR_SAVE3;
> +    uint64_t CSR_SAVE4;
> +    uint64_t CSR_SAVE5;
> +    uint64_t CSR_SAVE6;
> +    uint64_t CSR_SAVE7;

Better as an array.

> +    uint64_t CSR_PERFCTRL0;
> +    uint64_t CSR_PERFCNTR0;
> +    uint64_t CSR_PERFCTRL1;
> +    uint64_t CSR_PERFCNTR1;
> +    uint64_t CSR_PERFCTRL2;
> +    uint64_t CSR_PERFCNTR2;
> +    uint64_t CSR_PERFCTRL3;
> +    uint64_t CSR_PERFCNTR3;

Likewise.

> +    uint64_t CSR_DB0ADDR;
> +    uint64_t CSR_DB0MASK;
> +    uint64_t CSR_DB0CTL;
> +    uint64_t CSR_DB0ASID;
> +    uint64_t CSR_DB1ADDR;
> +    uint64_t CSR_DB1MASK;
> +    uint64_t CSR_DB1CTL;
> +    uint64_t CSR_DB1ASID;
> +    uint64_t CSR_DB2ADDR;
> +    uint64_t CSR_DB2MASK;
> +    uint64_t CSR_DB2CTL;
> +    uint64_t CSR_DB2ASID;
> +    uint64_t CSR_DB3ADDR;
> +    uint64_t CSR_DB3MASK;
> +    uint64_t CSR_DB3CTL;
> +    uint64_t CSR_DB3ASID;

Likewise.

> +    uint64_t CSR_IB0ADDR;
> +    uint64_t CSR_IB0MASK;
> +    uint64_t CSR_IB0CTL;
> +    uint64_t CSR_IB0ASID;
> +    uint64_t CSR_IB1ADDR;
> +    uint64_t CSR_IB1MASK;
> +    uint64_t CSR_IB1CTL;
> +    uint64_t CSR_IB1ASID;
> +    uint64_t CSR_IB2ADDR;
> +    uint64_t CSR_IB2MASK;
> +    uint64_t CSR_IB2CTL;
> +    uint64_t CSR_IB2ASID;
> +    uint64_t CSR_IB3ADDR;
> +    uint64_t CSR_IB3MASK;
> +    uint64_t CSR_IB3CTL;
> +    uint64_t CSR_IB3ASID;
> +    uint64_t CSR_IB4ADDR;
> +    uint64_t CSR_IB4MASK;
> +    uint64_t CSR_IB4CTL;
> +    uint64_t CSR_IB4ASID;
> +    uint64_t CSR_IB5ADDR;
> +    uint64_t CSR_IB5MASK;
> +    uint64_t CSR_IB5CTL;
> +    uint64_t CSR_IB5ASID;
> +    uint64_t CSR_IB6ADDR;
> +    uint64_t CSR_IB6MASK;
> +    uint64_t CSR_IB6CTL;
> +    uint64_t CSR_IB6ASID;
> +    uint64_t CSR_IB7ADDR;
> +    uint64_t CSR_IB7MASK;
> +    uint64_t CSR_IB7CTL;
> +    uint64_t CSR_IB7ASID;

Likewise.


r~


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

* Re: [RFC PATCH v2 03/30] target/loongarch: Add basic vmstate description of CPU.
  2021-11-11  1:35 ` [RFC PATCH v2 03/30] target/loongarch: Add basic vmstate description of CPU Xiaojuan Yang
@ 2021-11-11 13:30   ` Richard Henderson
  0 siblings, 0 replies; 62+ messages in thread
From: Richard Henderson @ 2021-11-11 13:30 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
> +++ b/target/loongarch/cpu.c
> @@ -12,6 +12,7 @@
>   #include "sysemu/qtest.h"
>   #include "exec/exec-all.h"
>   #include "qapi/qapi-commands-machine-target.h"
> +#include "migration/vmstate.h"

You don't need this include.

Otherwise,
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


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

* Re: [RFC PATCH v2 02/30] target/loongarch: Add CSR registers definition
  2021-11-11  1:35 ` [RFC PATCH v2 02/30] target/loongarch: Add CSR registers definition Xiaojuan Yang
  2021-11-11 13:29   ` Richard Henderson
@ 2021-11-11 13:33   ` Richard Henderson
  1 sibling, 0 replies; 62+ messages in thread
From: Richard Henderson @ 2021-11-11 13:33 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
> @@ -179,12 +181,22 @@ static void loongarch_cpu_reset(DeviceState *dev)
>       LoongArchCPU *cpu = LOONGARCH_CPU(cs);
>       LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(cpu);
>       CPULoongArchState *env = &cpu->env;
> +    uint64_t data;
>   
>       lacc->parent_reset(dev);
>   
>       env->fcsr0_mask = 0x1f1f031f;
>       env->fcsr0 = 0x0;
>   
> +    /* Set direct mapping mode after reset */
> +    data = FIELD_DP64(0, CSR_CRMD, PLV, 0);
> +    data = FIELD_DP64(data, CSR_CRMD, IE, 0);
> +    data = FIELD_DP64(data, CSR_CRMD, DA, 1);
> +    data = FIELD_DP64(data, CSR_CRMD, PG, 0);
> +    data = FIELD_DP64(data, CSR_CRMD, DATF, 1);
> +    data = FIELD_DP64(data, CSR_CRMD, DATM, 1);
> +    env->CSR_CRMD = data;

This is not all that is listed in 6.3 Reset.


r~


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

* Re: [RFC PATCH v2 04/30] target/loongarch: Define exceptions for LoongArch.
  2021-11-11  1:35 ` [RFC PATCH v2 04/30] target/loongarch: Define exceptions for LoongArch Xiaojuan Yang
@ 2021-11-11 13:36   ` Richard Henderson
  2021-11-12  2:24     ` yangxiaojuan
  0 siblings, 1 reply; 62+ messages in thread
From: Richard Henderson @ 2021-11-11 13:36 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
> +++ b/target/loongarch/cpu.h
> @@ -369,8 +369,21 @@ enum {
>       EXCP_BREAK,
>       EXCP_INE,
>       EXCP_FPE,
> -
> -    EXCP_LAST = EXCP_FPE,
> +    EXCP_IPE,
> +    EXCP_TLBL,
> +    EXCP_TLBS,
> +    EXCP_INST_NOTAVAIL,
> +    EXCP_TLBM,
> +    EXCP_TLBPE,
> +    EXCP_TLBNX,
> +    EXCP_TLBNR,
> +    EXCP_EXT_INTERRUPT,
> +    EXCP_DBP,
> +    EXCP_IBE,
> +    EXCP_DBE,
> +    EXCP_DINT,
> +
> +    EXCP_LAST = EXCP_DINT,

Surely this is (essentially) a duplicate of EXCCODE_*.
I think we should have only one copy of this.


r~


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

* Re: [RFC PATCH v2 16/30] hw/loongarch: Add a virt LoongArch 3A5000 board support
  2021-11-11  1:35 ` [RFC PATCH v2 16/30] hw/loongarch: Add a virt LoongArch 3A5000 board support Xiaojuan Yang
@ 2021-11-11 14:17   ` Mark Cave-Ayland
  0 siblings, 0 replies; 62+ messages in thread
From: Mark Cave-Ayland @ 2021-11-11 14:17 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/2021 01:35, Xiaojuan Yang wrote:

> LoongArch is a new RISC ISA, support 32bit mode
> or 64bit mode. Now we only add 64bit support.
> 
> More detailed info you can see
> https://github.com/loongson/LoongArch-Documentation
> 
> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao <gaosong@loongson.cn>
> ---
>   .../devices/loongarch64-softmmu/default.mak   |   3 +
>   configs/targets/loongarch64-softmmu.mak       |   3 +
>   hw/Kconfig                                    |   1 +
>   hw/loongarch/Kconfig                          |   3 +
>   hw/loongarch/ls3a5000_virt.c                  | 210 ++++++++++++++++++
>   hw/loongarch/meson.build                      |   4 +
>   hw/meson.build                                |   1 +
>   include/exec/poison.h                         |   2 +
>   include/hw/loongarch/loongarch.h              |  46 ++++
>   include/sysemu/arch_init.h                    |   1 +
>   qapi/machine.json                             |   2 +-
>   target/Kconfig                                |   1 +
>   target/loongarch/Kconfig                      |   2 +
>   target/loongarch/cpu.c                        |   8 +
>   target/loongarch/cpu.h                        |   4 +
>   15 files changed, 290 insertions(+), 1 deletion(-)
>   create mode 100644 configs/devices/loongarch64-softmmu/default.mak
>   create mode 100644 hw/loongarch/Kconfig
>   create mode 100644 hw/loongarch/ls3a5000_virt.c
>   create mode 100644 hw/loongarch/meson.build
>   create mode 100644 include/hw/loongarch/loongarch.h
>   create mode 100644 target/loongarch/Kconfig
> 
> diff --git a/configs/devices/loongarch64-softmmu/default.mak b/configs/devices/loongarch64-softmmu/default.mak
> new file mode 100644
> index 0000000000..a6705b9e4a
> --- /dev/null
> +++ b/configs/devices/loongarch64-softmmu/default.mak
> @@ -0,0 +1,3 @@
> +# Default configuration for loongarch64-softmmu
> +
> +CONFIG_LOONGSON_3A5000=y
> diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak
> index f33fa1590b..7bc06c850c 100644
> --- a/configs/targets/loongarch64-softmmu.mak
> +++ b/configs/targets/loongarch64-softmmu.mak
> @@ -1 +1,4 @@
> +TARGET_ARCH=loongarch64
> +TARGET_BASE_ARCH=loongarch
> +TARGET_SUPPORTS_MTTCG=y
>   TARGET_XML_FILES= gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu64.xml
> diff --git a/hw/Kconfig b/hw/Kconfig
> index ad20cce0a9..f71b2155ed 100644
> --- a/hw/Kconfig
> +++ b/hw/Kconfig
> @@ -49,6 +49,7 @@ source avr/Kconfig
>   source cris/Kconfig
>   source hppa/Kconfig
>   source i386/Kconfig
> +source loongarch/Kconfig
>   source m68k/Kconfig
>   source microblaze/Kconfig
>   source mips/Kconfig
> diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
> new file mode 100644
> index 0000000000..720822f32c
> --- /dev/null
> +++ b/hw/loongarch/Kconfig
> @@ -0,0 +1,3 @@
> +config LOONGSON_3A5000
> +    bool
> +    select PCI_EXPRESS_7A
> diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
> new file mode 100644
> index 0000000000..7c88d64795
> --- /dev/null
> +++ b/hw/loongarch/ls3a5000_virt.c
> @@ -0,0 +1,210 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * QEMU loongson 3a5000 develop board emulation
> + *
> + * Copyright (c) 2021 Loongson Technology Corporation Limited
> + */
> +#include "qemu/osdep.h"
> +#include "qemu-common.h"
> +#include "qemu/units.h"
> +#include "qemu/datadir.h"
> +#include "qapi/error.h"
> +#include "hw/boards.h"
> +#include "sysemu/sysemu.h"
> +#include "sysemu/qtest.h"
> +#include "sysemu/runstate.h"
> +#include "sysemu/reset.h"
> +#include "hw/loongarch/loongarch.h"
> +#include "hw/pci-host/ls7a.h"
> +
> +CPULoongArchState *cpu_states[LOONGARCH_MAX_VCPUS];

This doesn't look right - shouldn't this be in LoongArchMachineState?

> +static void main_cpu_reset(void *opaque)
> +{
> +    LoongArchCPU *cpu = opaque;
> +
> +    cpu_reset(CPU(cpu));
> +}
> +
> +static uint64_t loongarch_pm_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    return 0;
> +}
> +
> +static void loongarch_pm_mem_write(void *opaque, hwaddr addr,
> +                                   uint64_t val, unsigned size)
> +{
> +
> +    if (addr != PM_CNT_MODE) {
> +        return;
> +    }
> +
> +    switch (val) {
> +    case 0x00:
> +        qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
> +        return;
> +    case 0xff:
> +        qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
> +        return;
> +    default:
> +        return;
> +    }
> +}
> +
> +static const MemoryRegionOps loongarch_pm_ops = {
> +    .read  = loongarch_pm_mem_read,
> +    .write = loongarch_pm_mem_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,

This should likely be DEVICE_LITTLE_ENDIAN or DEVICE_BIG_ENDIAN.

> +};
> +
> +#define LOONGARCH_SIMPLE_MMIO_OPS(ADDR, NAME, SIZE) \
> +({\
> +     MemoryRegion *iomem = g_new(MemoryRegion, 1);\
> +     memory_region_init_io(iomem, NULL, &loongarch_qemu_ops,\
> +                           (void *)ADDR, NAME, SIZE);\
> +     memory_region_add_subregion(lams->system_iocsr, ADDR, iomem);\
> +})
> +
> +static void loongarch_qemu_write(void *opaque, hwaddr addr,
> +                                 uint64_t val, unsigned size)
> +{
> +}
> +
> +static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    uint64_t feature = 0UL;
> +    addr = ((hwaddr)(long)opaque) + addr;
> +
> +    switch (addr) {
> +    case FEATURE_REG:
> +        feature |= 1UL << IOCSRF_MSI | 1UL << IOCSRF_EXTIOI |
> +                   1UL << IOCSRF_CSRIPI;
> +        return feature ;
> +    case VENDOR_REG:
> +        return *(uint64_t *)"Loongson-3A5000";
> +    case CPUNAME_REG:
> +        return *(uint64_t *)"3A5000";
> +    }
> +    return 0;
> +}
> +
> +static const MemoryRegionOps loongarch_qemu_ops = {
> +    .read = loongarch_qemu_read,
> +    .write = loongarch_qemu_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,

Same comment here about DEVICE_NATIVE_ENDIAN.

> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 8,
> +    },
> +    .impl = {
> +        .min_access_size = 4,
> +        .max_access_size = 8,
> +    },
> +};
> +
> +static void ls3a5000_virt_init(MachineState *machine)
> +{
> +    const char *cpu_model = machine->cpu_type;
> +    LoongArchCPU *cpu;
> +    CPULoongArchState *env;
> +    uint64_t lowram_size = 0, highram_size = 0;
> +    MemoryRegion *lowmem = g_new(MemoryRegion, 1);
> +    char *ramName = NULL;
> +    ram_addr_t ram_size = machine->ram_size;
> +    MemoryRegion *address_space_mem = get_system_memory();
> +    LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
> +    int i;
> +    MemoryRegion *iomem = NULL;
> +
> +    if (!cpu_model) {
> +        cpu_model = LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000");
> +    }
> +    if (!strstr(cpu_model, "Loongson-3A5000")) {
> +        error_report("LoongArch/TCG needs cpu type Loongson-3A5000");
> +        exit(1);
> +    }
> +
> +    lams->system_iocsr = g_new0(MemoryRegion, 1);
> +    lams->address_space_iocsr = g_new0(AddressSpace, 1);

Heap allocations are not recommended because they disassociate the MemoryRegion from 
its owner. Here it would make sense to embed them directly in LoongArchMachineState 
directly i.e.

   typedef struct LoongArchMachineState {
       /*< private >*/
       MachineState parent_obj;

       AddressSpace address_space_iocsr;
       MemoryRegion system_iocsr;
   } LoongArchMachineState;

and then update the pointer references accordingly.

> +    memory_region_init_io(lams->system_iocsr, NULL, NULL, lams, "iocsr", UINT64_MAX);
> +    address_space_init(lams->address_space_iocsr, lams->system_iocsr, "IOCSR");
> +
> +    /* Init CPUs */
> +    for (i = 0; i < machine->smp.cpus; i++) {
> +        Object *cpuobj = NULL;
> +        CPUState *cs;
> +
> +        cpuobj = object_new(machine->cpu_type);
> +
> +        cs = CPU(cpuobj);
> +        cs->cpu_index = i;

Is this needed? I think cs->cpu_index gets set automatically.

> +        qdev_realize(DEVICE(cpuobj), NULL, &error_fatal);
> +        object_unref(cpuobj);
> +
> +        cpu = LOONGARCH_CPU(cs);
> +        if (cpu == NULL) {
> +            fprintf(stderr, "Unable to find CPU definition\n");
> +            exit(1);
> +        }
> +        env = &cpu->env;
> +        cpu_states[i] = env;

This looks wrong as per my comment above. Can you explain further what you're trying 
to do here?

> +        cpu_loongarch_clock_init(cpu);
> +        qemu_register_reset(main_cpu_reset, cpu);

The function is called main_cpu_reset however it seems to be used for all CPUs?

> +    }
> +
> +    ramName = g_strdup_printf("loongarch.lowram");

QEMU doesn't generally use camel-case for variable names.

> +    lowram_size = MIN(ram_size, 256 * 0x100000);
> +    memory_region_init_alias(lowmem, NULL, ramName, machine->ram,
> +                             0, lowram_size);
> +    memory_region_add_subregion(address_space_mem, 0, lowmem);

Missing a g_free(ramName).

> +    highram_size = ram_size > lowram_size ? ram_size - 256 * 0x100000 : 0;
> +    if (highram_size > 0) {
> +        MemoryRegion *highmem = g_new(MemoryRegion, 1);

Another heap allocation: should highmem also belong in LoongArchMachineState?

> +        ramName = g_strdup_printf("loongarch.highram");
> +        memory_region_init_alias(highmem, NULL, ramName, machine->ram,
> +                                 lowram_size, highram_size);
> +        memory_region_add_subregion(address_space_mem, 0x90000000, highmem);

Also missing a g_free(ramName) here.

> +    }
> +
> +    /* Add PM mmio memory for reboot and shutdown*/
> +    iomem = g_new(MemoryRegion, 1);

Another heap allocation: should iomem also belong in LoongArchMachineState?

> +    memory_region_init_io(iomem, NULL, &loongarch_pm_ops, NULL,
> +                          "loongarch_pm", PM_MMIO_SIZE);
> +    memory_region_add_subregion(address_space_mem,
> +                                PM_MMIO_ADDR, iomem);
> +
> +    LOONGARCH_SIMPLE_MMIO_OPS(FEATURE_REG, "loongarch_feature", 0x8);
> +    LOONGARCH_SIMPLE_MMIO_OPS(VENDOR_REG, "loongarch_vendor", 0x8);
> +    LOONGARCH_SIMPLE_MMIO_OPS(CPUNAME_REG, "loongarch_cpuname", 0x8);
> +}
> +
> +static void loongarch_class_init(ObjectClass *oc, void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    mc->desc = "Loongson-5000 LS7A1000 machine";
> +    mc->init = ls3a5000_virt_init;
> +    mc->default_ram_size = 1 * GiB;
> +    mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000");
> +    mc->default_ram_id = "loongarch.ram";
> +    mc->max_cpus = LOONGARCH_MAX_VCPUS;
> +    mc->is_default = 1;
> +    mc->default_kernel_irqchip_split = false;
> +    mc->block_default_type = IF_VIRTIO;
> +    mc->default_boot_order = "c";
> +    mc->no_cdrom = 1;
> +}
> +
> +static const TypeInfo loongarch_machine_types[] = {
> +    {
> +        .name           = TYPE_LOONGARCH_MACHINE,
> +        .parent         = TYPE_MACHINE,
> +        .instance_size  = sizeof(LoongArchMachineState),
> +        .class_init     = loongarch_class_init,
> +    }
> +};
> +
> +DEFINE_TYPES(loongarch_machine_types)
> diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build
> new file mode 100644
> index 0000000000..1e743cadb8
> --- /dev/null
> +++ b/hw/loongarch/meson.build
> @@ -0,0 +1,4 @@
> +loongarch_ss = ss.source_set()
> +loongarch_ss.add(when: 'CONFIG_LOONGSON_3A5000', if_true: files('ls3a5000_virt.c'))
> +
> +hw_arch += {'loongarch': loongarch_ss}
> diff --git a/hw/meson.build b/hw/meson.build
> index b3366c888e..95202649b7 100644
> --- a/hw/meson.build
> +++ b/hw/meson.build
> @@ -49,6 +49,7 @@ subdir('avr')
>   subdir('cris')
>   subdir('hppa')
>   subdir('i386')
> +subdir('loongarch')
>   subdir('m68k')
>   subdir('microblaze')
>   subdir('mips')
> diff --git a/include/exec/poison.h b/include/exec/poison.h
> index 7ad4ad18e8..590bc305c7 100644
> --- a/include/exec/poison.h
> +++ b/include/exec/poison.h
> @@ -14,6 +14,7 @@
>   #pragma GCC poison TARGET_CRIS
>   #pragma GCC poison TARGET_HEXAGON
>   #pragma GCC poison TARGET_HPPA
> +#pragma GCC poison TARGET_LOONGARCH64
>   #pragma GCC poison TARGET_M68K
>   #pragma GCC poison TARGET_MICROBLAZE
>   #pragma GCC poison TARGET_MIPS
> @@ -73,6 +74,7 @@
>   #pragma GCC poison CONFIG_HPPA_DIS
>   #pragma GCC poison CONFIG_I386_DIS
>   #pragma GCC poison CONFIG_HEXAGON_DIS
> +#pragma GCC poison CONFIG_LOONGARCH_DIS
>   #pragma GCC poison CONFIG_M68K_DIS
>   #pragma GCC poison CONFIG_MICROBLAZE_DIS
>   #pragma GCC poison CONFIG_MIPS_DIS
> diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
> new file mode 100644
> index 0000000000..edab069f76
> --- /dev/null
> +++ b/include/hw/loongarch/loongarch.h
> @@ -0,0 +1,46 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Definitions for loongarch board emulation.
> + *
> + * Copyright (C) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#ifndef HW_LOONGARCH_H
> +#define HW_LOONGARCH_H
> +
> +#include "target/loongarch/cpu.h"
> +#include "qemu-common.h"
> +#include "hw/boards.h"
> +#include "qemu/queue.h"
> +
> +#define LOONGARCH_MAX_VCPUS     4
> +#define PM_MMIO_ADDR            0x10080000UL
> +#define PM_MMIO_SIZE            0x100
> +#define PM_CNT_MODE             0x10
> +#define FEATURE_REG             0x8
> +#define IOCSRF_TEMP             0
> +#define IOCSRF_NODECNT          1
> +#define IOCSRF_MSI              2
> +#define IOCSRF_EXTIOI           3
> +#define IOCSRF_CSRIPI           4
> +#define IOCSRF_FREQCSR          5
> +#define IOCSRF_FREQSCALE        6
> +#define IOCSRF_DVFSV1           7
> +#define IOCSRF_GMOD             9
> +#define IOCSRF_VM               11
> +
> +#define VENDOR_REG              0x10
> +#define CPUNAME_REG             0x20
> +
> +typedef struct LoongArchMachineState {
> +    /*< private >*/
> +    MachineState parent_obj;
> +
> +    AddressSpace *address_space_iocsr;
> +    MemoryRegion *system_iocsr;
> +} LoongArchMachineState;
> +
> +#define TYPE_LOONGARCH_MACHINE  MACHINE_TYPE_NAME("loongson7a")
> +DECLARE_INSTANCE_CHECKER(LoongArchMachineState, LOONGARCH_MACHINE,
> +                         TYPE_LOONGARCH_MACHINE)
> +#endif
> diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
> index 70c579560a..3ac3634bbb 100644
> --- a/include/sysemu/arch_init.h
> +++ b/include/sysemu/arch_init.h
> @@ -24,6 +24,7 @@ enum {
>       QEMU_ARCH_RX = (1 << 20),
>       QEMU_ARCH_AVR = (1 << 21),
>       QEMU_ARCH_HEXAGON = (1 << 22),
> +    QEMU_ARCH_LOONGARCH = (1 << 23),
>   };
>   
>   extern const uint32_t arch_type;
> diff --git a/qapi/machine.json b/qapi/machine.json
> index 17794ef681..b23456ce98 100644
> --- a/qapi/machine.json
> +++ b/qapi/machine.json
> @@ -30,7 +30,7 @@
>   ##
>   { 'enum' : 'SysEmuTarget',
>     'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'cris', 'hppa', 'i386',
> -             'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64',
> +             'loongarch64', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64',
>                'mips64el', 'mipsel', 'nios2', 'or1k', 'ppc',
>                'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4',
>                'sh4eb', 'sparc', 'sparc64', 'tricore',
> diff --git a/target/Kconfig b/target/Kconfig
> index ae7f24fc66..83da0bd293 100644
> --- a/target/Kconfig
> +++ b/target/Kconfig
> @@ -4,6 +4,7 @@ source avr/Kconfig
>   source cris/Kconfig
>   source hppa/Kconfig
>   source i386/Kconfig
> +source loongarch/Kconfig
>   source m68k/Kconfig
>   source microblaze/Kconfig
>   source mips/Kconfig
> diff --git a/target/loongarch/Kconfig b/target/loongarch/Kconfig
> new file mode 100644
> index 0000000000..46b26b1a85
> --- /dev/null
> +++ b/target/loongarch/Kconfig
> @@ -0,0 +1,2 @@
> +config LOONGARCH64
> +    bool
> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
> index c789acaf2f..a6010deef0 100644
> --- a/target/loongarch/cpu.c
> +++ b/target/loongarch/cpu.c
> @@ -11,6 +11,7 @@
>   #include "qemu/module.h"
>   #include "sysemu/qtest.h"
>   #include "exec/exec-all.h"
> +#include "hw/qdev-properties.h"
>   #include "qapi/qapi-commands-machine-target.h"
>   #include "migration/vmstate.h"
>   #include "cpu.h"
> @@ -534,6 +535,12 @@ static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model)
>       return oc;
>   }
>   
> +static Property loongarch_cpu_properties[] = {
> +    DEFINE_PROP_INT32("core-id", LoongArchCPU, core_id, -1),
> +    DEFINE_PROP_UINT32("id", LoongArchCPU, id, UNASSIGNED_CPU_ID),
> +    DEFINE_PROP_END_OF_LIST()
> +};
> +
>   void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags)
>   {
>       LoongArchCPU *cpu = LOONGARCH_CPU(cs);
> @@ -629,6 +636,7 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data)
>       device_class_set_parent_realize(dc, loongarch_cpu_realizefn,
>                                       &lacc->parent_realize);
>       device_class_set_parent_reset(dc, loongarch_cpu_reset, &lacc->parent_reset);
> +    device_class_set_props(dc, loongarch_cpu_properties);
>   
>       cc->class_by_name = loongarch_cpu_class_by_name;
>       cc->has_work = loongarch_cpu_has_work;
> diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
> index 6f7c13d366..77afe9e26a 100644
> --- a/target/loongarch/cpu.h
> +++ b/target/loongarch/cpu.h
> @@ -15,6 +15,8 @@
>   
>   #define TCG_GUEST_DEFAULT_MO (0)
>   
> +#define UNASSIGNED_CPU_ID 0xFFFFFFFF
> +
>   #define FCSR0_M1    0x1f         /* FCSR1 mask, Enables */
>   #define FCSR0_M2    0x1f1f0000   /* FCSR2 mask, Cause and Flags */
>   #define FCSR0_M3    0x300        /* FCSR3 mask, Round Mode */
> @@ -371,6 +373,8 @@ struct LoongArchCPU {
>   
>       CPUNegativeOffsetState neg;
>       CPULoongArchState env;
> +    uint32_t id;
> +    int32_t core_id;
>   };
>   
>   #define TYPE_LOONGARCH_CPU "loongarch-cpu"


ATB,

Mark.


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

* Re: [RFC PATCH v2 17/30] hw/loongarch: Add LoongArch cpu interrupt support(CPUINTC)
  2021-11-11  1:35 ` [RFC PATCH v2 17/30] hw/loongarch: Add LoongArch cpu interrupt support(CPUINTC) Xiaojuan Yang
@ 2021-11-11 14:22   ` Mark Cave-Ayland
  0 siblings, 0 replies; 62+ messages in thread
From: Mark Cave-Ayland @ 2021-11-11 14:22 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/2021 01:35, Xiaojuan Yang wrote:

> Loongson-3A5000 support 14 interrupts from 64 - 77(Timer->75 IPI->76)
> Loongson-3A5000 and ls7a form a legacy model and extended model irq
> hierarchy.Tcg mode emulate a simplified extended model which
> has no Legacy I/O Interrupt Controller(LIOINTC) and LPC.
> e.g:
> 
>   |    +-----+    +---------+     +-------+             |
>   |    | IPI |--> | CPUINTC | <-- | Timer |             |
>   |    +-----+    +---------+     +-------+             |
>   |                    ^                                |
>   |                    |                                |
>   |               +---------+
>   |               | EIOINTC |
>   |               +---------+
>   |                ^       ^                            |
>   |                |       |                            |
>   |         +---------+ +---------+                     |
>   |         | PCH-PIC | | PCH-MSI |                     |
>   |         +---------+ +---------+                     |
>   |           ^     ^           ^                       |
>   |           |     |           |                       |
>   |   +---------+ +---------+ +---------+               |
>   |   | UARTs | | Devices | | Devices |                 |
>   |   +---------+ +---------+ +---------+               |
>   |        ^                                            |
> 
> The following series patch will realize the interrupt
> controller in this model.
> 
> More detailed info can be found at the kernel doc or manual
> 1.https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/
> linux-loongson.git/tree/Documentation/loongarch?h=loongarch-next
> 2.https://github.com/loongson/LoongArch-Documentation
> 
> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao <gaosong@loongson.cn>
> ---
>   hw/loongarch/loongarch_int.c     | 59 ++++++++++++++++++++++++++++++++
>   hw/loongarch/ls3a5000_virt.c     |  2 ++
>   hw/loongarch/meson.build         |  1 +
>   include/hw/loongarch/loongarch.h |  2 ++
>   4 files changed, 64 insertions(+)
>   create mode 100644 hw/loongarch/loongarch_int.c
> 
> diff --git a/hw/loongarch/loongarch_int.c b/hw/loongarch/loongarch_int.c
> new file mode 100644
> index 0000000000..2502d056ac
> --- /dev/null
> +++ b/hw/loongarch/loongarch_int.c
> @@ -0,0 +1,59 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * QEMU LOONGARCH interrupt support
> + *
> + * Copyright (C) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/main-loop.h"
> +#include "hw/irq.h"
> +#include "hw/loongarch/loongarch.h"
> +#include "cpu.h"
> +
> +static void cpu_loongarch_irq_request(void *opaque, int irq, int level)
> +{
> +    LoongArchCPU *cpu = opaque;
> +    CPULoongArchState *env = &cpu->env;
> +    CPUState *cs = CPU(cpu);
> +    bool locked = false;
> +
> +    if (irq < 0 || irq > N_IRQS) {
> +        return;
> +    }
> +
> +    /* Make sure locking works even if BQL is already held by the caller */
> +    if (!qemu_mutex_iothread_locked()) {
> +        locked = true;
> +        qemu_mutex_lock_iothread();
> +    }
> +
> +    if (level) {
> +        env->CSR_ESTAT |= 1 << irq;
> +    } else {
> +        env->CSR_ESTAT &= ~(1 << irq);
> +    }
> +
> +    if (FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS)) {
> +        cpu_interrupt(cs, CPU_INTERRUPT_HARD);
> +    } else {
> +        cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
> +    }
> +
> +    if (locked) {
> +        qemu_mutex_unlock_iothread();
> +    }
> +}
> +
> +void cpu_loongarch_init_irq(LoongArchCPU *cpu)
> +{
> +    CPULoongArchState *env = &cpu->env;
> +    qemu_irq *qi;
> +    int i;
> +
> +    qi = qemu_allocate_irqs(cpu_loongarch_irq_request, cpu, N_IRQS);
> +    for (i = 0; i < N_IRQS; i++) {
> +        env->irq[i] = qi[i];
> +    }
> +    g_free(qi);
> +}

TYPE_CPU is a child of TYPE_DEVICE so rather than use qemu_allocate_irqs() here you 
should declare these as qdev gpios in the LoongArchCPU device and wire them up using 
qdev_connect_gpio_out(). I suspect once you've done this then 
cpu_loongarch_init_irq() will no longer be required.

> diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
> index 7c88d64795..37d6b1ec88 100644
> --- a/hw/loongarch/ls3a5000_virt.c
> +++ b/hw/loongarch/ls3a5000_virt.c
> @@ -150,6 +150,8 @@ static void ls3a5000_virt_init(MachineState *machine)
>           env = &cpu->env;
>           cpu_states[i] = env;
>   
> +        /* Init CPU internal devices */
> +        cpu_loongarch_init_irq(cpu);
>           cpu_loongarch_clock_init(cpu);
>           qemu_register_reset(main_cpu_reset, cpu);
>       }
> diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build
> index 1e743cadb8..a972210680 100644
> --- a/hw/loongarch/meson.build
> +++ b/hw/loongarch/meson.build
> @@ -1,4 +1,5 @@
>   loongarch_ss = ss.source_set()
> +loongarch_ss.add(files('loongarch_int.c'))
>   loongarch_ss.add(when: 'CONFIG_LOONGSON_3A5000', if_true: files('ls3a5000_virt.c'))
>   
>   hw_arch += {'loongarch': loongarch_ss}
> diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
> index edab069f76..8538697e5f 100644
> --- a/include/hw/loongarch/loongarch.h
> +++ b/include/hw/loongarch/loongarch.h
> @@ -43,4 +43,6 @@ typedef struct LoongArchMachineState {
>   #define TYPE_LOONGARCH_MACHINE  MACHINE_TYPE_NAME("loongson7a")
>   DECLARE_INSTANCE_CHECKER(LoongArchMachineState, LOONGARCH_MACHINE,
>                            TYPE_LOONGARCH_MACHINE)
> +
> +void cpu_loongarch_init_irq(LoongArchCPU *cpu);
>   #endif


ATB,

Mark.


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

* Re: [RFC PATCH v2 18/30] hw/loongarch: Add LoongArch ipi interrupt support(IPI)
  2021-11-11  1:35 ` [RFC PATCH v2 18/30] hw/loongarch: Add LoongArch ipi interrupt support(IPI) Xiaojuan Yang
@ 2021-11-11 14:28   ` Mark Cave-Ayland
  0 siblings, 0 replies; 62+ messages in thread
From: Mark Cave-Ayland @ 2021-11-11 14:28 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/2021 01:35, Xiaojuan Yang wrote:

> This patch realize the IPI interrupt controller.
> 
> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao <gaosong@loongson.cn>
> ---
>   hw/loongarch/ipi.c               | 144 +++++++++++++++++++++++++++++++
>   hw/loongarch/ls3a5000_virt.c     |   1 +
>   hw/loongarch/meson.build         |   2 +-
>   include/hw/loongarch/gipi.h      |  37 ++++++++
>   include/hw/loongarch/loongarch.h |   4 +
>   target/loongarch/cpu.h           |   1 +
>   6 files changed, 188 insertions(+), 1 deletion(-)
>   create mode 100644 hw/loongarch/ipi.c
>   create mode 100644 include/hw/loongarch/gipi.h
> 
> diff --git a/hw/loongarch/ipi.c b/hw/loongarch/ipi.c
> new file mode 100644
> index 0000000000..4902205ff5
> --- /dev/null
> +++ b/hw/loongarch/ipi.c
> @@ -0,0 +1,144 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * LoongArch ipi interrupt support
> + *
> + * Copyright (C) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/units.h"
> +#include "qapi/error.h"
> +#include "exec/address-spaces.h"
> +#include "hw/hw.h"
> +#include "hw/irq.h"
> +#include "sysemu/sysemu.h"
> +#include "sysemu/cpus.h"
> +#include "cpu.h"
> +#include "qemu/log.h"
> +#include "hw/loongarch/loongarch.h"
> +#include "migration/vmstate.h"
> +
> +static const VMStateDescription vmstate_gipi_core = {
> +    .name = "gipi-single",
> +    .version_id = 0,
> +    .minimum_version_id = 0,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(status, gipi_core),
> +        VMSTATE_UINT32(en, gipi_core),
> +        VMSTATE_UINT32(set, gipi_core),
> +        VMSTATE_UINT32(clear, gipi_core),
> +        VMSTATE_UINT64_ARRAY(buf, gipi_core, MAX_GIPI_MBX_NUM),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static const VMStateDescription vmstate_gipi = {
> +    .name = "gipi",
> +    .version_id = 0,
> +    .minimum_version_id = 0,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_STRUCT_ARRAY(core, gipiState, MAX_GIPI_CORE_NUM, 0,
> +                             vmstate_gipi_core, gipi_core),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void gipi_writel(void *opaque, hwaddr addr, uint64_t val, unsigned size)
> +{
> +    gipi_core *s = opaque;
> +    void *pbuf;
> +
> +    if (size != 4) {
> +        hw_error("size not 4");
> +    }
> +    addr &= 0xff;
> +    switch (addr) {
> +    case CORE_STATUS_OFF:
> +        hw_error("CORE_SET_OFF Can't be write\n");
> +        break;
> +    case CORE_EN_OFF:
> +        s->en = val;
> +        break;
> +    case CORE_SET_OFF:
> +        s->status |= val;
> +        if (s->status != 0) {
> +            qemu_irq_raise(s->irq);
> +        }
> +        break;
> +    case CORE_CLEAR_OFF:
> +        s->status ^= val;
> +        if (s->status == 0) {
> +            qemu_irq_lower(s->irq);
> +        }
> +        break;
> +    case CORE_BUF_20 ... CORE_BUF_38:
> +        pbuf =  (void *)s->buf + (addr - 0x20);
> +        *(unsigned int *)pbuf = val;
> +        break;
> +    default:
> +        break;
> +    }
> +}
> +
> +static uint64_t gipi_readl(void *opaque, hwaddr addr, unsigned size)
> +{
> +    gipi_core *s = opaque;
> +    uint64_t ret = 0;
> +    void *pbuf;
> +
> +    addr &= 0xff;
> +    if (size != 4) {
> +        hw_error("size not 4 %d\n", size);
> +    }
> +    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:
> +        pbuf =  (void *)s->buf + (addr - 0x20);
> +        ret = *(unsigned int *)pbuf;
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static const MemoryRegionOps gipi_ops = {
> +    .read = gipi_readl,
> +    .write = gipi_writel,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +int cpu_init_ipi(LoongArchMachineState *lams, qemu_irq parent, int cpu)
> +{
> +    int core_num = cpu % 4;
> +    hwaddr addr;
> +    MemoryRegion *region;
> +    char str[32];
> +
> +    if (lams->gipi == NULL) {
> +        lams->gipi = g_malloc0(sizeof(gipiState));
> +        vmstate_register(NULL, 0, &vmstate_gipi, lams->gipi);
> +    }
> +
> +    lams->gipi->core[cpu].irq = parent;
> +
> +    addr = SMP_GIPI_MAILBOX + core_num * 0x100;
> +    region = g_new(MemoryRegion, 1);
> +    sprintf(str, "gipi%d", cpu);
> +    memory_region_init_io(region, NULL, &gipi_ops,
> +                          &lams->gipi->core[cpu], str, 0x100);
> +    memory_region_add_subregion(lams->system_iocsr, addr, region);
> +    return 0;
> +}

This doesn't look right at all. Shouldn't this be a qdev device? Also similar to my 
comments on previous patches, there shouldn't really be any heap allocation used.

> diff --git a/hw/loongarch/ls3a5000_virt.c b/hw/loongarch/ls3a5000_virt.c
> index 37d6b1ec88..bd79df96df 100644
> --- a/hw/loongarch/ls3a5000_virt.c
> +++ b/hw/loongarch/ls3a5000_virt.c
> @@ -153,6 +153,7 @@ static void ls3a5000_virt_init(MachineState *machine)
>           /* Init CPU internal devices */
>           cpu_loongarch_init_irq(cpu);
>           cpu_loongarch_clock_init(cpu);
> +        cpu_init_ipi(lams, env->irq[IRQ_IPI], i);
>           qemu_register_reset(main_cpu_reset, cpu);
>       }
>   
> diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build
> index a972210680..1bd209c9eb 100644
> --- a/hw/loongarch/meson.build
> +++ b/hw/loongarch/meson.build
> @@ -1,5 +1,5 @@
>   loongarch_ss = ss.source_set()
>   loongarch_ss.add(files('loongarch_int.c'))
> -loongarch_ss.add(when: 'CONFIG_LOONGSON_3A5000', if_true: files('ls3a5000_virt.c'))
> +loongarch_ss.add(when: 'CONFIG_LOONGSON_3A5000', if_true: files('ls3a5000_virt.c', 'ipi.c'))
>   
>   hw_arch += {'loongarch': loongarch_ss}
> diff --git a/include/hw/loongarch/gipi.h b/include/hw/loongarch/gipi.h
> new file mode 100644
> index 0000000000..244d4e3ecf
> --- /dev/null
> +++ b/include/hw/loongarch/gipi.h
> @@ -0,0 +1,37 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * LoongArch ipi interrupt header files
> + *
> + * Copyright (C) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#ifndef HW_LOONGARCH_GIPI_H
> +#define HW_LOONGARCH_GIPI_H
> +
> +#define SMP_GIPI_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 MAX_GIPI_CORE_NUM      4
> +#define MAX_GIPI_MBX_NUM       4
> +
> +typedef struct gipi_core {
> +    uint32_t status;
> +    uint32_t en;
> +    uint32_t set;
> +    uint32_t clear;
> +    uint64_t buf[MAX_GIPI_MBX_NUM];
> +    qemu_irq irq;
> +} gipi_core;
> +
> +typedef struct gipiState {
> +    gipi_core core[MAX_GIPI_CORE_NUM];
> +} gipiState;
> +
> +#endif
> diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
> index 8538697e5f..54cc875e6d 100644
> --- a/include/hw/loongarch/loongarch.h
> +++ b/include/hw/loongarch/loongarch.h
> @@ -12,6 +12,7 @@
>   #include "qemu-common.h"
>   #include "hw/boards.h"
>   #include "qemu/queue.h"
> +#include "hw/loongarch/gipi.h"
>   
>   #define LOONGARCH_MAX_VCPUS     4
>   #define PM_MMIO_ADDR            0x10080000UL
> @@ -38,6 +39,8 @@ typedef struct LoongArchMachineState {
>   
>       AddressSpace *address_space_iocsr;
>       MemoryRegion *system_iocsr;
> +
> +    gipiState   *gipi;
>   } LoongArchMachineState;
>   
>   #define TYPE_LOONGARCH_MACHINE  MACHINE_TYPE_NAME("loongson7a")
> @@ -45,4 +48,5 @@ DECLARE_INSTANCE_CHECKER(LoongArchMachineState, LOONGARCH_MACHINE,
>                            TYPE_LOONGARCH_MACHINE)
>   
>   void cpu_loongarch_init_irq(LoongArchCPU *cpu);
> +int cpu_init_ipi(LoongArchMachineState *lams, qemu_irq irq, int cpu);
>   #endif
> diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
> index 77afe9e26a..b7ef0b8b3c 100644
> --- a/target/loongarch/cpu.h
> +++ b/target/loongarch/cpu.h
> @@ -152,6 +152,7 @@ extern const char * const fregnames[];
>   
>   #define N_IRQS      14
>   #define IRQ_TIMER   11
> +#define IRQ_IPI     12
>   
>   /*
>    * LoongArch cpu has 4 priv level, now only 2 mode used.


ATB,

Mark.


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

* Re: [RFC PATCH v2 06/30] target/loongarch: Add stabletimer support
  2021-11-11  1:35 ` [RFC PATCH v2 06/30] target/loongarch: Add stabletimer support Xiaojuan Yang
@ 2021-11-11 14:34   ` Richard Henderson
  0 siblings, 0 replies; 62+ messages in thread
From: Richard Henderson @ 2021-11-11 14:34 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
> +    void *irq[N_IRQS];

qemu_irq, not void*.

> +    QEMUTimer *timer; /* Internal timer */

You can add this into the CPUArchState structure, rather than reference via pointer.  It 
is always present with the cpu.

Neither of these are needed for CONFIG_USER_ONLY.

> +void cpu_loongarch_store_stable_timer_config(CPULoongArchState *env,
> +                                             uint64_t value)
> +{
> +    uint64_t now, next;
> +
> +    env->CSR_TCFG = value;
> +    if (value & STABLETIMER_ENABLE) {
> +        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +        next = now + (value & STABLETIMER_TICK_MASK) * TIMER_PERIOD;
> +        timer_mod(env->timer, next);
> +    }

timer_mod_ns, when you use timer_new_ns.
timer_del when not enabled.

> +    env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
> +                              &loongarch_stable_timer_cb, env);

This would become

     timer_init_ns(&env->timer, ...)


r~


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

* Re: [RFC PATCH v2 19/30] hw/intc: Add LoongArch ls7a interrupt controller support(PCH-PIC)
  2021-11-11  1:35 ` [RFC PATCH v2 19/30] hw/intc: Add LoongArch ls7a interrupt controller support(PCH-PIC) Xiaojuan Yang
@ 2021-11-11 14:37   ` Mark Cave-Ayland
  0 siblings, 0 replies; 62+ messages in thread
From: Mark Cave-Ayland @ 2021-11-11 14:37 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/2021 01:35, Xiaojuan Yang wrote:

> This patch realize the PCH-PIC interrupt controller.
> 
> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao <gaosong@loongson.cn>
> ---
>   hw/intc/Kconfig                     |   4 +
>   hw/intc/loongarch_pch_pic.c         | 283 ++++++++++++++++++++++++++++
>   hw/intc/meson.build                 |   1 +
>   hw/loongarch/Kconfig                |   1 +
>   include/hw/intc/loongarch_pch_pic.h |  49 +++++
>   5 files changed, 338 insertions(+)
>   create mode 100644 hw/intc/loongarch_pch_pic.c
>   create mode 100644 include/hw/intc/loongarch_pch_pic.h
> 
> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> index 78aed93c45..3b7eca7b03 100644
> --- a/hw/intc/Kconfig
> +++ b/hw/intc/Kconfig
> @@ -73,3 +73,7 @@ config GOLDFISH_PIC
>   
>   config M68K_IRQC
>       bool
> +
> +config LOONGARCH_PCH_PIC
> +    bool
> +    select UNIMP
> diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c
> new file mode 100644
> index 0000000000..96e4c46174
> --- /dev/null
> +++ b/hw/intc/loongarch_pch_pic.c
> @@ -0,0 +1,283 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * QEMU Loongson 7A1000 I/O interrupt controller.
> + *
> + * Copyright (C) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/sysbus.h"
> +#include "hw/irq.h"
> +#include "hw/intc/loongarch_pch_pic.h"
> +#include "migration/vmstate.h"
> +
> +#define DEBUG_LOONGARCH_PCH_PIC 0
> +
> +#define DPRINTF(fmt, ...) \
> +do { \
> +    if (DEBUG_LOONGARCH_PCH_PIC) { \
> +        fprintf(stderr, "LOONGARCH_PCH_PIC: " fmt , ## __VA_ARGS__); \
> +    } \
> +} while (0)

Use of the DPRINTF() macro has been deprecated for new devices: please use 
trace-events instead.

> + #define for_each_set_bit(bit, addr, size) \
> +         for ((bit) = find_first_bit((addr), (size));            \
> +              (bit) < (size);                                    \
> +              (bit) = find_next_bit((addr), (size), (bit) + 1))
> +
> +static void update_irq(loongarch_pch_pic *s, int mask, int level)

I'd suggest adding a loongarch_pch_ prefix to this function name.

> +{
> +    int i;
> +    uint64_t val;
> +    val = mask & s->intirr & (~s->int_mask);
> +
> +    for_each_set_bit(i, &val, 32) {
> +        if (level == 1) {
> +            if ((s->intisr & (0x1ULL << i)) == 0) {
> +                s->intisr |= 1ULL << i;
> +                qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 1);
> +            }
> +        } else if (level == 0) {
> +            if (s->intisr & (0x1ULL << i)) {
> +                s->intisr &= ~(0x1ULL << i);
> +                qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 0);
> +            }
> +        }
> +    }
> +}
> +
> +static void irq_handler(void *opaque, int irq, int level)

And also add a loongarch_pch_ prefix to this function name.

> +{
> +    loongarch_pch_pic *s = opaque;

How about a QOM cast to ensure type safety:

   loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque);

> +    assert(irq < 32);
> +    uint32_t mask = 1ULL << irq;
> +    DPRINTF("------ %s irq %d %d\n", __func__, irq, level);
> +
> +    if (s->intedge & mask) {
> +        /* Edge triggered */
> +        if (level) {
> +            if ((s->last_intirr & mask) == 0) {
> +                s->intirr |= mask;
> +            }
> +            s->last_intirr |= mask;
> +        } else {
> +            s->last_intirr &= ~mask;
> +        }
> +    } else {
> +        /* Level triggered */
> +        if (level) {
> +            s->intirr |= mask;
> +            s->last_intirr |= mask;
> +        } else {
> +            s->intirr &= ~mask;
> +            s->last_intirr &= ~mask;
> +        }
> +
> +    }
> +    update_irq(s, mask, level);
> +}
> +
> +static uint64_t loongarch_pch_pic_reg_read(void *opaque, hwaddr addr,
> +                                           unsigned size)
> +{
> +    loongarch_pch_pic *s = opaque;

Similarly a QOM cast could be added here.

> +    uint32_t val = 0;
> +    uint32_t offset;
> +    int32_t offset_tmp;
> +    offset = addr & 0xfff;
> +    if (4 == size) {

if (size == 4)? I know swapping the parameters like this used to help detect bugs 
many years ago, but I'm sure that all modern compilers can cope with this.

> +        switch (offset) {
> +        case PCH_PIC_INT_ID_OFFSET:
> +            val = PCH_PIC_INT_ID_VAL;
> +            break;
> +        case PCH_PIC_INT_MASK_OFFSET:
> +            val = s->int_mask;
> +            break;
> +        case PCH_PIC_INT_STATUS_OFFSET:
> +            val = s->intisr & (~s->int_mask);
> +            break;
> +        case PCH_PIC_INT_EDGE_OFFSET:
> +            val = s->intedge;
> +            break;
> +        case PCH_PIC_INT_POL_OFFSET:
> +            val = s->int_polarity;
> +            break;
> +        case PCH_PIC_HTMSI_EN_OFFSET:
> +            val = s->htmsi_en;
> +            break;
> +        case PCH_PIC_AUTO_CTRL0_OFFSET:
> +        case PCH_PIC_AUTO_CTRL1_OFFSET:
> +            break;
> +        default:
> +            break;
> +        }
> +    } else if (1 == size) {

Same here.

> +        if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) {
> +            offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
> +            if (offset_tmp >= 0 && offset_tmp < 32) {
> +                val = s->htmsi_vector[offset_tmp];
> +            }
> +        } else if (offset >=  PCH_PIC_ROUTE_ENTRY_OFFSET) {
> +            offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
> +            if (offset_tmp >= 0 && offset_tmp < 32) {
> +                val = s->route_entry[offset_tmp];
> +            }
> +        }
> +    }
> +
> +    return val;
> +}
> +
> +static void loongarch_pch_pic_reg_write(void *opaque, hwaddr addr,
> +                                        uint64_t data, unsigned size)
> +{
> +    loongarch_pch_pic *s = opaque;

Another QOM cast.

> +    int32_t offset_tmp;
> +    uint32_t offset, old;
> +    offset = addr & 0xfff;
> +
> +    if (4 == size) {

Again please swap the parameters.

> +        switch (offset) {
> +        case PCH_PIC_INT_MASK_OFFSET:
> +            old = s->int_mask;
> +            s->int_mask = data;
> +            if (old & ~data) {
> +                update_irq(s, (old & ~data), 1);
> +            } else if (~old & data) {
> +                update_irq(s, (~old & data), 0);
> +            }
> +            break;
> +        case PCH_PIC_INT_STATUS_OFFSET:
> +            s->intisr = data;
> +            break;
> +        case PCH_PIC_INT_EDGE_OFFSET:
> +            s->intedge = data;
> +            break;
> +        case PCH_PIC_INT_CLEAR_OFFSET:
> +            s->intirr &= (~(data & s->intedge));
> +            update_irq(s, data, 0);
> +            s->intisr &= (~data);
> +            break;
> +        case PCH_PIC_INT_POL_OFFSET:
> +            s->int_polarity = data;
> +            break;
> +        case PCH_PIC_HTMSI_EN_OFFSET:
> +            s->htmsi_en = data;
> +            break;
> +        case PCH_PIC_AUTO_CTRL0_OFFSET:
> +        case PCH_PIC_AUTO_CTRL1_OFFSET:
> +            break;
> +        default:
> +            break;
> +        }
> +    } else if (1 == size) {

And here.

> +        if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) {
> +            offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
> +            if (offset_tmp >= 0 && offset_tmp < 32) {
> +                s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
> +            }
> +        } else if (offset >=  PCH_PIC_ROUTE_ENTRY_OFFSET) {
> +            offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
> +            if (offset_tmp >= 0 && offset_tmp < 32) {
> +                s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
> +            }
> +        }
> +    }
> +}
> +
> +static const MemoryRegionOps loongarch_pch_pic_ops = {
> +    .read = loongarch_pch_pic_reg_read,
> +    .write = loongarch_pch_pic_reg_write,
> +    .valid = {
> +        .min_access_size = 1,
> +        .max_access_size = 8,
> +    },
> +    .impl = {
> +        .min_access_size = 1,
> +        .max_access_size = 8,
> +    },
> +    .endianness = DEVICE_NATIVE_ENDIAN,

Should this be DEVICE_LITTLE_ENDIAN or DEVICE_BIG_ENDIAN?

> +};
> +
> +static void loongarch_pch_pic_reset(DeviceState *d)
> +{
> +    loongarch_pch_pic *s = LOONGARCH_PCH_PIC(d);
> +    int i;
> +
> +    s->int_id   = 0x0;
> +    s->int_mask = 0xffffffff;
> +    s->htmsi_en = 0x0;
> +    s->intedge  = 0x0;
> +    s->intclr   = 0x0;
> +    s->auto_crtl0 = 0x0;
> +    s->auto_crtl1 = 0x0;
> +    for (i = 0; i < 32; i++) {
> +        s->route_entry[i] = 0x1;
> +        s->htmsi_vector[i] = 0x0;
> +    }
> +    s->intirr = 0x0;
> +    s->intisr = 0x0;
> +    s->last_intirr = 0x0;
> +    s->int_polarity = 0x0;
> +}
> +
> +static void loongarch_pch_pic_init(Object *obj)
> +{
> +    loongarch_pch_pic *s = LOONGARCH_PCH_PIC(obj);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +    int tmp;

Can we use i instead of tmp for the loop counter?

> +    memory_region_init_io(&s->iomem, obj, &loongarch_pch_pic_ops,
> +                          s, TYPE_LOONGARCH_PCH_PIC, 0x1000);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +
> +    for (tmp = 0; tmp < 32; tmp++) {
> +        sysbus_init_irq(sbd, &s->parent_irq[tmp]);
> +    }
> +    qdev_init_gpio_in(DEVICE(obj), irq_handler, 32);
> +}
> +
> +static const VMStateDescription vmstate_loongarch_pch_pic = {
> +    .name = TYPE_LOONGARCH_PCH_PIC,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(int_mask, loongarch_pch_pic),
> +        VMSTATE_UINT32(htmsi_en, loongarch_pch_pic),
> +        VMSTATE_UINT32(intedge, loongarch_pch_pic),
> +        VMSTATE_UINT32(intclr, loongarch_pch_pic),
> +        VMSTATE_UINT32(auto_crtl0, loongarch_pch_pic),
> +        VMSTATE_UINT32(auto_crtl1, loongarch_pch_pic),
> +        VMSTATE_UINT8_ARRAY(route_entry, loongarch_pch_pic, 32),
> +        VMSTATE_UINT8_ARRAY(htmsi_vector, loongarch_pch_pic, 32),
> +        VMSTATE_UINT32(last_intirr, loongarch_pch_pic),
> +        VMSTATE_UINT32(intirr, loongarch_pch_pic),
> +        VMSTATE_UINT32(intisr, loongarch_pch_pic),
> +        VMSTATE_UINT32(int_polarity, loongarch_pch_pic),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->reset = loongarch_pch_pic_reset;
> +    dc->vmsd = &vmstate_loongarch_pch_pic;
> +}
> +
> +static const TypeInfo loongarch_pch_pic_info = {
> +    .name          = TYPE_LOONGARCH_PCH_PIC,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(loongarch_pch_pic),
> +    .instance_init = loongarch_pch_pic_init,
> +    .class_init    = loongarch_pch_pic_class_init,
> +};
> +
> +static void loongarch_pch_pic_register_types(void)
> +{
> +    type_register_static(&loongarch_pch_pic_info);
> +}
> +
> +type_init(loongarch_pch_pic_register_types)
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index c89d2ca180..07b0627468 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -57,3 +57,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
>   		if_true: files('spapr_xive_kvm.c'))
>   specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c'))
>   specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
> +specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c'))
> diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
> index 720822f32c..c6d7ebcd5b 100644
> --- a/hw/loongarch/Kconfig
> +++ b/hw/loongarch/Kconfig
> @@ -1,3 +1,4 @@
>   config LOONGSON_3A5000
>       bool
>       select PCI_EXPRESS_7A
> +    select LOONGARCH_PCH_PIC
> diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h
> new file mode 100644
> index 0000000000..b1b3e24166
> --- /dev/null
> +++ b/include/hw/intc/loongarch_pch_pic.h
> @@ -0,0 +1,49 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * LoongArch 7A1000 I/O interrupt controller definitions
> + *
> + * Copyright (c) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic"
> +DECLARE_INSTANCE_CHECKER(struct loongarch_pch_pic, LOONGARCH_PCH_PIC,
> +                         TYPE_LOONGARCH_PCH_PIC)
> +
> +#define PCH_PIC_ROUTE_ENTRY_OFFSET      0x100
> +#define PCH_PIC_INT_ID_OFFSET           0x00
> +#define PCH_PIC_INT_ID_VAL              0x7000000UL
> +#define PCH_PIC_INT_ID_VER              0x1f0001UL
> +#define PCH_PIC_INT_MASK_OFFSET         0x20
> +#define PCH_PIC_INT_EDGE_OFFSET         0x60
> +#define PCH_PIC_INT_CLEAR_OFFSET        0x80
> +#define PCH_PIC_INT_STATUS_OFFSET       0x3a0
> +#define PCH_PIC_INT_POL_OFFSET          0x3e0
> +#define PCH_PIC_HTMSI_EN_OFFSET         0x40
> +#define PCH_PIC_HTMSI_VEC_OFFSET        0x200
> +#define PCH_PIC_AUTO_CTRL0_OFFSET       0xc0
> +#define PCH_PIC_AUTO_CTRL1_OFFSET       0xe0
> +
> +typedef struct loongarch_pch_pic {
> +    SysBusDevice parent_obj;
> +    qemu_irq parent_irq[32];
> +    uint32_t int_id;
> +    uint32_t int_mask; /*0x020 interrupt mask register*/
> +    uint32_t htmsi_en;/*0x040 1=msi*/
> +    uint32_t intedge; /*0x060 edge=1 level  =0*/
> +    uint32_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/
> +    uint32_t auto_crtl0; /*0x0c0*/
> +    uint32_t auto_crtl1; /*0x0e0*/
> +    uint8_t route_entry[32]; /*0x100 - 0x120*/
> +    uint8_t htmsi_vector[32]; /*0x200 - 0x220*/
> +    uint32_t last_intirr;    /* edge detection */
> +    uint32_t intirr; /* 0x380 interrupt request register */
> +    uint32_t intisr; /* 0x3a0 interrupt service register */
> +    /*
> +     * 0x3e0 interrupt level polarity selection
> +     * register 0 for high level trigger
> +     */
> +    uint32_t int_polarity;
> +    MemoryRegion iomem;
> +} loongarch_pch_pic;
> +
> +


ATB,

Mark.


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

* Re: [RFC PATCH v2 20/30] hw/intc: Add LoongArch ls7a msi interrupt controller support(PCH-MSI)
  2021-11-11  1:35 ` [RFC PATCH v2 20/30] hw/intc: Add LoongArch ls7a msi interrupt controller support(PCH-MSI) Xiaojuan Yang
@ 2021-11-11 14:40   ` Mark Cave-Ayland
  0 siblings, 0 replies; 62+ messages in thread
From: Mark Cave-Ayland @ 2021-11-11 14:40 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/2021 01:35, Xiaojuan Yang wrote:

> This patch realize PCH-MSI interrupt controller.
> 
> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao <gaosong@loongson.cn>
> ---
>   hw/intc/Kconfig                     |  5 ++
>   hw/intc/loongarch_pch_msi.c         | 73 +++++++++++++++++++++++++++++
>   hw/intc/meson.build                 |  1 +
>   hw/loongarch/Kconfig                |  1 +
>   include/hw/intc/loongarch_pch_msi.h | 16 +++++++
>   5 files changed, 96 insertions(+)
>   create mode 100644 hw/intc/loongarch_pch_msi.c
>   create mode 100644 include/hw/intc/loongarch_pch_msi.h
> 
> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> index 3b7eca7b03..c0dc12dfa0 100644
> --- a/hw/intc/Kconfig
> +++ b/hw/intc/Kconfig
> @@ -77,3 +77,8 @@ config M68K_IRQC
>   config LOONGARCH_PCH_PIC
>       bool
>       select UNIMP
> +
> +config LOONGARCH_PCH_MSI
> +    select MSI_NONBROKEN
> +    bool
> +    select UNIMP
> diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c
> new file mode 100644
> index 0000000000..1d8a3c1b21
> --- /dev/null
> +++ b/hw/intc/loongarch_pch_msi.c
> @@ -0,0 +1,73 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * QEMU Loongson 7A1000 msi interrupt controller.
> + *
> + * Copyright (C) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/sysbus.h"
> +#include "hw/irq.h"
> +#include "hw/intc/loongarch_pch_msi.h"
> +#include "hw/pci/msi.h"
> +#include "hw/misc/unimp.h"
> +#include "migration/vmstate.h"
> +
> +#define DEBUG_LOONGARCH_PCH_MSI 0
> +
> +#define DPRINTF(fmt, ...) \
> +do { \
> +    if (DEBUG_LOONGARCH_PCH_MSI) { \
> +        fprintf(stderr, "LOONGARCH_PCH_MSI: " fmt , ## __VA_ARGS__); \
> +    } \
> +} while (0)

Again DPRINTF() shouldn't be used any more, please use trace-events.

> +static uint64_t loongarch_msi_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    return 0;
> +}
> +
> +static void loongarch_msi_mem_write(void *opaque, hwaddr addr,
> +                                    uint64_t val, unsigned size)
> +{
> +    loongarch_pch_msi *s = opaque;

I think a QOM cast would be useful here.

> +    int irq_num = val & 0xff;
> +
> +    qemu_set_irq(s->pch_msi_irq[irq_num - 32], 1);
> +}
> +
> +static const MemoryRegionOps loongarch_pch_msi_ops = {
> +    .read  = loongarch_msi_mem_read,
> +    .write = loongarch_msi_mem_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,

Same comment about endianness.

> +};
> +
> +static void loongarch_pch_msi_init(Object *obj)
> +{
> +    loongarch_pch_msi *s = LOONGARCH_PCH_MSI(obj);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +    int tmp;

Can we use i as a loop counter?

> +    memory_region_init_io(&s->msi_mmio, obj, &loongarch_pch_msi_ops,
> +                          s, TYPE_LOONGARCH_PCH_MSI, 0x8);
> +    sysbus_init_mmio(sbd, &s->msi_mmio);
> +    msi_nonbroken = true;
> +
> +    for (tmp = 0; tmp < 224; tmp++) {
> +        sysbus_init_irq(sbd, &s->pch_msi_irq[tmp]);
> +    }
> +}

224 seems like a magic number: how about using a #define for this value and using it 
where needed?

> +static const TypeInfo loongarch_pch_msi_info = {
> +    .name          = TYPE_LOONGARCH_PCH_MSI,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(loongarch_pch_msi),
> +    .instance_init = loongarch_pch_msi_init,
> +};
> +
> +static void loongarch_pch_msi_register_types(void)
> +{
> +    type_register_static(&loongarch_pch_msi_info);
> +}
> +
> +type_init(loongarch_pch_msi_register_types)
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index 07b0627468..e04abe2d56 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -58,3 +58,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
>   specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c'))
>   specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
>   specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c'))
> +specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c'))
> diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
> index c6d7ebcd5b..4500fd3a57 100644
> --- a/hw/loongarch/Kconfig
> +++ b/hw/loongarch/Kconfig
> @@ -2,3 +2,4 @@ config LOONGSON_3A5000
>       bool
>       select PCI_EXPRESS_7A
>       select LOONGARCH_PCH_PIC
> +    select LOONGARCH_PCH_MSI
> diff --git a/include/hw/intc/loongarch_pch_msi.h b/include/hw/intc/loongarch_pch_msi.h
> new file mode 100644
> index 0000000000..40f0575bb5
> --- /dev/null
> +++ b/include/hw/intc/loongarch_pch_msi.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * LoongArch 7A1000 I/O interrupt controller definitions
> + *
> + * Copyright (C) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#define TYPE_LOONGARCH_PCH_MSI "loongarch_pch_msi"
> +DECLARE_INSTANCE_CHECKER(struct loongarch_pch_msi, LOONGARCH_PCH_MSI,
> +                         TYPE_LOONGARCH_PCH_MSI)
> +
> +typedef struct loongarch_pch_msi {
> +    SysBusDevice parent_obj;
> +    qemu_irq pch_msi_irq[224];
> +    MemoryRegion msi_mmio;
> +} loongarch_pch_msi;
> 


ATB,

Mark.


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

* Re: [RFC PATCH v2 21/30] hw/intc: Add LoongArch extioi interrupt controller(EIOINTC)
  2021-11-11  1:35 ` [RFC PATCH v2 21/30] hw/intc: Add LoongArch extioi interrupt controller(EIOINTC) Xiaojuan Yang
@ 2021-11-11 14:49   ` Mark Cave-Ayland
  2021-11-25  8:20     ` yangxiaojuan
  0 siblings, 1 reply; 62+ messages in thread
From: Mark Cave-Ayland @ 2021-11-11 14:49 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/2021 01:35, Xiaojuan Yang wrote:

> This patch realize the EIOINTC interrupt controller.
> 
> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao <gaosong@loongson.cn>
> ---
>   hw/intc/Kconfig                    |   3 +
>   hw/intc/loongarch_extioi.c         | 570 +++++++++++++++++++++++++++++
>   hw/intc/meson.build                |   1 +
>   hw/loongarch/Kconfig               |   1 +
>   include/hw/intc/loongarch_extioi.h |  99 +++++
>   include/hw/loongarch/loongarch.h   |   1 +
>   6 files changed, 675 insertions(+)
>   create mode 100644 hw/intc/loongarch_extioi.c
>   create mode 100644 include/hw/intc/loongarch_extioi.h
> 
> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> index c0dc12dfa0..a2d9efd5aa 100644
> --- a/hw/intc/Kconfig
> +++ b/hw/intc/Kconfig
> @@ -82,3 +82,6 @@ config LOONGARCH_PCH_MSI
>       select MSI_NONBROKEN
>       bool
>       select UNIMP
> +
> +config LOONGARCH_EXTIOI
> +    bool
> diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c
> new file mode 100644
> index 0000000000..592cd8d1e2
> --- /dev/null
> +++ b/hw/intc/loongarch_extioi.c
> @@ -0,0 +1,570 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Loongson 3A5000 ext interrupt controller emulation
> + *
> + * Copyright (C) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/module.h"
> +#include "qemu/log.h"
> +#include "hw/irq.h"
> +#include "hw/sysbus.h"
> +#include "hw/loongarch/loongarch.h"
> +#include "hw/qdev-properties.h"
> +#include "exec/address-spaces.h"
> +#include "hw/intc/loongarch_extioi.h"
> +#include "migration/vmstate.h"
> +
> +#define DEBUG_APIC 0
> +
> +#define DPRINTF(fmt, ...) \
> +do { \
> +    if (DEBUG_APIC) { \
> +        fprintf(stderr, "APIC: " fmt , ## __VA_ARGS__); \
> +    } \
> +} while (0)

Again please use trace-events insead of DPRINTF().

> +static void extioi_update_irq(void *opaque, int irq_num, int level)
> +{
> +    loongarch_extioi *s = opaque;
> +    uint8_t  ipnum, cpu;
> +    unsigned long found1, found2;
> +
> +    ipnum = s->sw_ipmap[irq_num];
> +    cpu   = s->sw_coremap[irq_num];
> +    if (level == 1) {
> +        if (test_bit(irq_num, (void *)s->en_reg8) == false) {
> +            return;
> +        }
> +        bitmap_set((void *)s->coreisr_reg8[cpu], irq_num, 1);
> +        found1 = find_next_bit((void *)&(s->sw_ipisr[cpu][ipnum]),
> +                               EXTIOI_IRQS, 0);
> +        bitmap_set((void *)&(s->sw_ipisr[cpu][ipnum]), irq_num, 1);
> +
> +        if (found1 >= EXTIOI_IRQS) {
> +            qemu_set_irq(s->parent_irq[cpu][ipnum], level);
> +        }
> +    } else {
> +        bitmap_clear((void *)s->coreisr_reg8[cpu], irq_num, 1);
> +        found1 = find_next_bit((void *)&(s->sw_ipisr[cpu][ipnum]),
> +                               EXTIOI_IRQS, 0);
> +        bitmap_clear((void *)&(s->sw_ipisr[cpu][ipnum]), irq_num, 1);
> +        found2 = find_next_bit((void *)&(s->sw_ipisr[cpu][ipnum]),
> +                               EXTIOI_IRQS, 0);
> +
> +        if ((found1 < EXTIOI_IRQS) && (found2 >= EXTIOI_IRQS)) {
> +            qemu_set_irq(s->parent_irq[cpu][ipnum], level);
> +        }
> +    }
> +}
> +
> +static void extioi_setirq(void *opaque, int irq, int level)
> +{
> +    loongarch_extioi *s = opaque;
> +    extioi_update_irq(s, irq, level);
> +}
> +
> +static void extioi_handler(void *opaque, int irq, int level)
> +{
> +    loongarch_extioi *extioi = (loongarch_extioi *)opaque;
> +
> +    qemu_set_irq(extioi->irq[irq], level);
> +}
> +
> +static uint32_t extioi_readb(void *opaque, hwaddr addr)
> +{
> +    loongarch_extioi *state = opaque;

Add a QOM cast here.

> +    unsigned long offset, reg_count;
> +    uint8_t ret;
> +    int cpu;
> +
> +    offset = addr & 0xffff;
> +
> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
> +        reg_count = (offset - EXTIOI_ENABLE_START);
> +        ret = state->en_reg8[reg_count];
> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
> +               (offset < EXTIOI_BOUNCE_END)) {
> +        reg_count = (offset - EXTIOI_BOUNCE_START);
> +        ret = state->bounce_reg8[reg_count];
> +    } else if ((offset >= EXTIOI_COREISR_START) &&
> +               (offset < EXTIOI_COREISR_END)) {
> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f);
> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
> +        ret = state->coreisr_reg8[cpu][reg_count];
> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
> +               (offset < EXTIOI_IPMAP_END)) {
> +        reg_count = (offset - EXTIOI_IPMAP_START);
> +        ret = state->ipmap_reg8[reg_count];
> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
> +               (offset < EXTIOI_COREMAP_END)) {
> +        reg_count = (offset - EXTIOI_COREMAP_START);
> +        ret = state->coremap_reg8[reg_count];
> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
> +               (offset < EXTIOI_NODETYPE_END)) {
> +        reg_count = (offset - EXTIOI_NODETYPE_START);
> +        ret = state->nodetype_reg8[reg_count];
> +    }
> +
> +    DPRINTF("readb reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
> +    return ret;
> +}
> +
> +static uint32_t extioi_readw(void *opaque, hwaddr addr)
> +{
> +    loongarch_extioi *state = opaque;

And also a QOM cast here.

> +    unsigned long offset, reg_count;
> +    uint32_t ret;
> +    int cpu;
> +
> +    offset = addr & 0xffff;
> +
> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
> +        reg_count = (offset - EXTIOI_ENABLE_START) / 4;
> +        ret = state->en_reg32[reg_count];
> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
> +               (offset < EXTIOI_BOUNCE_END)) {
> +        reg_count = (offset - EXTIOI_BOUNCE_START) / 4;
> +        ret = state->bounce_reg32[reg_count];
> +    } else if ((offset >= EXTIOI_COREISR_START) &&
> +               (offset < EXTIOI_COREISR_END)) {
> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 4;
> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
> +        ret = state->coreisr_reg32[cpu][reg_count];
> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
> +               (offset < EXTIOI_IPMAP_END)) {
> +        reg_count = (offset - EXTIOI_IPMAP_START) / 4;
> +        ret = state->ipmap_reg32[reg_count];
> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
> +               (offset < EXTIOI_COREMAP_END)) {
> +        reg_count = (offset - EXTIOI_COREMAP_START) / 4;
> +        ret = state->coremap_reg32[reg_count];
> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
> +               (offset < EXTIOI_NODETYPE_END)) {
> +        reg_count = (offset - EXTIOI_NODETYPE_START) / 4;
> +        ret = state->nodetype_reg32[reg_count];
> +    }
> +
> +    DPRINTF("readw reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
> +    return ret;
> +}
> +
> +static uint64_t extioi_readl(void *opaque, hwaddr addr)
> +{
> +    loongarch_extioi *state = opaque;

A QOM cast here too.

> +    unsigned long offset, reg_count;
> +    uint64_t ret;
> +    int cpu;
> +
> +    offset = addr & 0xffff;
> +
> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
> +        reg_count = (offset - EXTIOI_ENABLE_START) / 8;
> +        ret = state->en_reg64[reg_count];
> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
> +               (offset < EXTIOI_BOUNCE_END)) {
> +        reg_count = (offset - EXTIOI_BOUNCE_START) / 8;
> +        ret = state->bounce_reg64[reg_count];
> +    } else if ((offset >= EXTIOI_COREISR_START) &&
> +               (offset < EXTIOI_COREISR_END)) {
> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 8;
> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
> +        ret = state->coreisr_reg64[cpu][reg_count];
> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
> +               (offset < EXTIOI_IPMAP_END)) {
> +        ret = state->ipmap_reg64;
> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
> +               (offset < EXTIOI_COREMAP_END)) {
> +        reg_count = (offset - EXTIOI_COREMAP_START) / 8;
> +        ret = state->coremap_reg64[reg_count];
> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
> +               (offset < EXTIOI_NODETYPE_END)) {
> +        reg_count = (offset - EXTIOI_NODETYPE_START) / 8;
> +        ret = state->nodetype_reg64[reg_count];
> +    }
> +
> +    DPRINTF("readl reg 0x" TARGET_FMT_plx " = %lx\n", addr, ret);
> +    return ret;
> +}
> +
> +static void extioi_writeb(void *opaque, hwaddr addr, uint32_t val)
> +{
> +    loongarch_extioi *state = opaque;

And another cast.

> +    unsigned long offset, reg_count;
> +    uint8_t old_data_u8;
> +    int cpu, i, ipnum, level, mask, irqnum;
> +
> +    offset = addr & 0xffff;
> +    val = val & 0xffUL;
> +
> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
> +        reg_count = (offset - EXTIOI_ENABLE_START);
> +        old_data_u8 = state->en_reg8[reg_count];
> +        if (old_data_u8 != val) {
> +            state->en_reg8[reg_count] = val;
> +            old_data_u8 = old_data_u8 ^ val;
> +            mask = 0x1;
> +
> +            for (i = 0; i < 8; i++) {
> +                if (old_data_u8 & mask) {
> +                    level = !!(val & (0x1 << i));
> +                    extioi_update_irq(state, i + reg_count * 8, level);
> +                }
> +                mask = mask << 1;
> +            }
> +        }
> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
> +               (offset < EXTIOI_BOUNCE_END)) {
> +        reg_count = (offset - EXTIOI_BOUNCE_START);
> +        state->bounce_reg8[reg_count] = val;
> +    } else if ((offset >= EXTIOI_ISR_START) && (offset < EXTIOI_ISR_END)) {
> +        /* Can not be writen */
> +        reg_count = (offset - EXTIOI_ISR_START) & 0x1f;
> +        old_data_u8 = state->isr_reg8[reg_count];
> +        state->isr_reg8[reg_count] = old_data_u8 & (~val);
> +
> +        mask = 0x1;
> +        for (i = 0; i < 8; i++) {
> +            if ((old_data_u8 & mask) && (val & mask)) {
> +                extioi_update_irq(state, i + reg_count * 8, 0);
> +            }
> +            mask = mask << 1;
> +        }
> +    } else if ((offset >= EXTIOI_COREISR_START) &&
> +               (offset < EXTIOI_COREISR_END)) {
> +        reg_count = (offset - EXTIOI_COREISR_START) & 0x1f;
> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
> +
> +        /* ext_isr */
> +        old_data_u8 = state->isr_reg8[reg_count];
> +        state->isr_reg8[reg_count] = old_data_u8 & (~val);
> +
> +        old_data_u8 = state->coreisr_reg8[cpu][reg_count];
> +        state->coreisr_reg8[cpu][reg_count] = old_data_u8 & (~val);
> +
> +        if (old_data_u8 != state->coreisr_reg8[cpu][reg_count]) {
> +            mask = 0x1;
> +            for (i = 0; i < 8; i++) {
> +                if ((old_data_u8 & mask) && (val & mask)) {
> +                    extioi_update_irq(state, i + reg_count * 8, 0);
> +                }
> +                mask = mask << 1;
> +            }
> +        }
> +    } else if ((offset >= EXTIOI_IPMAP_START) && (offset < EXTIOI_IPMAP_END)) {
> +        /* Drop arch.core_ip_mask use state->ipmap */
> +        reg_count = (offset - EXTIOI_IPMAP_START);
> +        state->ipmap_reg8[reg_count] = val;
> +
> +        ipnum = 0;
> +        for (i = 0; i < 4; i++) {
> +            if (val & (0x1 << i)) {
> +                ipnum = i;
> +                break;
> +            }
> +        }
> +
> +        if (val) {
> +            for (i = 0; i < 32; i++) {
> +                irqnum = reg_count * 32 + i;
> +                state->sw_ipmap[irqnum] = ipnum;
> +            }
> +        } else {
> +            for (i = 0; i < 32; i++) {
> +                irqnum = reg_count * 32 + i;
> +                state->sw_ipmap[irqnum] = 0;
> +            }
> +        }
> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
> +               (offset < EXTIOI_COREMAP_END)) {
> +        reg_count = (offset - EXTIOI_COREMAP_START);
> +        cpu = val & 0xf;
> +
> +        /* Node map different from kernel */
> +        if (cpu) {
> +            cpu = ctz32(cpu);
> +            state->coremap_reg8[reg_count] = val;
> +            state->sw_coremap[reg_count] = cpu;
> +        }
> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
> +               (offset < EXTIOI_NODETYPE_END)) {
> +        reg_count = (offset - EXTIOI_NODETYPE_START);
> +        state->nodetype_reg8[reg_count] = val;
> +    }
> +
> +    DPRINTF("writeb reg 0x" TARGET_FMT_plx " = %x\n", addr, val);
> +}
> +
> +static void extioi_writew(void *opaque, hwaddr addr, uint32_t val)
> +{
> +    loongarch_extioi *state = opaque;

And a QOM cast here.

> +    int cpu, level;
> +    uint32_t offset, old_data_u32, reg_count, mask, i;
> +
> +    offset = addr & 0xffff;
> +
> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
> +        reg_count = (offset - EXTIOI_ENABLE_START) / 4;
> +        old_data_u32 = state->en_reg32[reg_count];
> +        if (old_data_u32 != val) {
> +            state->en_reg32[reg_count] = val;
> +            old_data_u32 = old_data_u32 ^ val;
> +
> +            mask = 0x1;
> +            for (i = 0; i < 8 * sizeof(old_data_u32); i++) {
> +                if (old_data_u32 & mask) {
> +                    level = !!(val & (0x1 << i));
> +                    extioi_update_irq(state, i + reg_count * 32, level);
> +                }
> +                mask = mask << 1;
> +            }
> +        }
> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
> +               (offset < EXTIOI_BOUNCE_END)) {
> +        reg_count = (offset - EXTIOI_BOUNCE_START) / 4;
> +        state->bounce_reg32[reg_count] = val;
> +    } else if ((offset >= EXTIOI_COREISR_START) &&
> +               (offset < EXTIOI_COREISR_END)) {
> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 4;
> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
> +
> +        /* Ext_isr */
> +        old_data_u32 = state->isr_reg32[reg_count];
> +        state->isr_reg32[reg_count] = old_data_u32 & (~val);
> +
> +        /* Ext_core_ioisr */
> +        old_data_u32 = state->coreisr_reg32[cpu][reg_count];
> +        state->coreisr_reg32[cpu][reg_count] = old_data_u32 & (~val);
> +
> +        if (old_data_u32 != state->coreisr_reg32[cpu][reg_count]) {
> +            mask = 0x1;
> +            for (i = 0; i < 8 * sizeof(old_data_u32); i++) {
> +                if ((old_data_u32 & mask) && (val & mask)) {
> +                    extioi_update_irq(state, i + reg_count * 8, 0);
> +                }
> +                mask = mask << 1;
> +            }
> +        }
> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
> +               (offset < EXTIOI_IPMAP_END)) {
> +        extioi_writeb(opaque, addr, (val) & 0xff);
> +        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
> +        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
> +        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
> +               (offset < EXTIOI_COREMAP_END)) {
> +        extioi_writeb(opaque, addr, (val) & 0xff);
> +        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
> +        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
> +        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
> +               (offset < EXTIOI_NODETYPE_END)) {
> +        reg_count = (offset - EXTIOI_NODETYPE_START) / 4;
> +        state->nodetype_reg32[reg_count] = val;
> +    }
> +
> +    DPRINTF("writew reg 0x" TARGET_FMT_plx " = %x\n", addr, val);
> +}
> +
> +static void extioi_writel(void *opaque, hwaddr addr, uint64_t val)
> +{
> +    loongarch_extioi *state = (loongarch_extioi *)opaque;

And a QOM cast here.

> +    int cpu, level;
> +    uint64_t offset, old_data_u64, reg_count, mask, i;
> +
> +    offset = addr & 0xffff;
> +
> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
> +        reg_count = (offset - EXTIOI_ENABLE_START) / 8;
> +        old_data_u64 = state->en_reg64[reg_count];
> +        if (old_data_u64 != val) {
> +            state->en_reg64[reg_count] = val;
> +            old_data_u64 = old_data_u64 ^ val;
> +            mask = 0x1;
> +            for (i = 0; i < 8 * sizeof(old_data_u64); i++) {
> +                if (old_data_u64 & mask) {
> +                    level = !!(val & (0x1 << i));
> +                    extioi_update_irq(state, i + reg_count * 64, level);
> +                }
> +                mask = mask << 1;
> +            }
> +        }
> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
> +               (offset < EXTIOI_BOUNCE_END)) {
> +        reg_count = (offset - EXTIOI_BOUNCE_START) / 8;
> +        state->bounce_reg64[reg_count] = val;
> +    } else if ((offset >= EXTIOI_COREISR_START) &&
> +               (offset < EXTIOI_COREISR_END)) {
> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 8;
> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
> +
> +        /* core_ext_ioisr */
> +        old_data_u64 = state->coreisr_reg64[cpu][reg_count];
> +        state->coreisr_reg64[cpu][reg_count] = old_data_u64 & (~val);
> +
> +        if (old_data_u64 != state->coreisr_reg64[cpu][reg_count]) {
> +            mask = 0x1;
> +            for (i = 0; i < 8 * sizeof(old_data_u64); i++) {
> +                if ((old_data_u64 & mask) && (val & mask)) {
> +                    extioi_update_irq(state, i + reg_count * 64, 0);
> +                }
> +                mask = mask << 1;
> +            }
> +        }
> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
> +               (offset < EXTIOI_IPMAP_END)) {
> +        extioi_writeb(opaque, addr, (val) & 0xff);
> +        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
> +        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
> +        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
> +        extioi_writeb(opaque, addr + 4, (val >> 32) & 0xff);
> +        extioi_writeb(opaque, addr + 5, (val >> 40) & 0xff);
> +        extioi_writeb(opaque, addr + 6, (val >> 48) & 0xff);
> +        extioi_writeb(opaque, addr + 7, (val >> 56) & 0xff);
> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
> +               (offset < EXTIOI_COREMAP_END)) {
> +        extioi_writeb(opaque, addr, (val) & 0xff);
> +        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
> +        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
> +        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
> +        extioi_writeb(opaque, addr + 4, (val >> 32) & 0xff);
> +        extioi_writeb(opaque, addr + 5, (val >> 40) & 0xff);
> +        extioi_writeb(opaque, addr + 6, (val >> 48) & 0xff);
> +        extioi_writeb(opaque, addr + 7, (val >> 56) & 0xff);
> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
> +               (offset < EXTIOI_NODETYPE_END)) {
> +        reg_count = (offset - EXTIOI_NODETYPE_START) / 8;
> +        state->nodetype_reg64[reg_count] = val;
> +    }
> +
> +    DPRINTF("writel reg 0x" TARGET_FMT_plx " = %lx\n", addr, val);
> +}
> +
> +static uint64_t extioi_readfn(void *opaque, hwaddr addr, unsigned size)
> +{
> +    switch (size) {
> +    case 1:
> +        return extioi_readb(opaque, addr);
> +    case 4:
> +        return extioi_readw(opaque, addr);
> +    case 8:
> +        return extioi_readl(opaque, addr);
> +    default:
> +        g_assert_not_reached();
> +    }
> +}
> +
> +static void extioi_writefn(void *opaque, hwaddr addr,
> +                           uint64_t value, unsigned size)
> +{
> +    switch (size) {
> +    case 1:
> +        extioi_writeb(opaque, addr, value);
> +        break;
> +    case 4:
> +        extioi_writew(opaque, addr, value);
> +        break;
> +    case 8:
> +        extioi_writel(opaque, addr, value);
> +        break;
> +    default:
> +        g_assert_not_reached();
> +    }
> +}

Ooof. This pattern of using separate functions for different size accesses generally 
hasn't been needed since the memory API was introduced about 10 years ago. I suspect 
using a single function each for read and write would help simplify things enormously.

> +static const MemoryRegionOps extioi_ops = {
> +    .read = extioi_readfn,
> +    .write = extioi_writefn,
> +    .impl.min_access_size = 1,
> +    .impl.max_access_size = 8,
> +    .valid.min_access_size = 1,
> +    .valid.max_access_size = 8,
> +    .endianness = DEVICE_NATIVE_ENDIAN,

Again please choose an endianness.

> +};
> +
> +static void loongarch_extioi_realize(DeviceState *dev, Error **errp)
> +{
> +    LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
> +    MachineState *ms = MACHINE(lams);
> +    loongarch_extioi *p = LOONGARCH_EXTIOI(dev);
> +    int cpu, pin;
> +
> +    qdev_init_gpio_in(dev, extioi_setirq, EXTIOI_IRQS);
> +
> +    for (int i = 0; i < EXTIOI_IRQS; i++) {
> +        sysbus_init_irq(SYS_BUS_DEVICE(dev), &p->irq[i]);
> +    }

If EXTIOI_IRQS is constant you can do this in a .instance_init function.

> +    memory_region_init_io(&p->mmio, OBJECT(p), &extioi_ops, p,
> +                          TYPE_LOONGARCH_EXTIOI, 0x900);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &p->mmio);
> +
> +    for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
> +        for (pin = 0; pin < LS3A_INTC_IP; pin++) {
> +            sysbus_init_irq(SYS_BUS_DEVICE(dev), &p->parent_irq[cpu][pin]);
> +        }
> +    }

Similar comment about LS3A_INTC_IP.

> +    /* 0-31 is for non msi device.32-256 for msi/msix device */
> +    lams->pch_irq = qemu_allocate_irqs(extioi_handler, p, 256);

This doesn't look right: should these not be defined as qdev gpios belonging to a device?

> +}
> +
> +static const VMStateDescription vmstate_ext_sw_ipisr = {
> +    .name = "ext_sw_ipisr",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT8_ARRAY(irq, ext_sw_ipisr, EXTIOI_IRQS_BITMAP_SIZE),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static const VMStateDescription vmstate_loongarch_extioi = {
> +    .name = TYPE_LOONGARCH_EXTIOI,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT8_ARRAY(en_reg8, loongarch_extioi, EXTIOI_IRQS_BITMAP_SIZE),
> +        VMSTATE_UINT8_ARRAY(bounce_reg8, loongarch_extioi,
> +                            EXTIOI_IRQS_BITMAP_SIZE),
> +        VMSTATE_UINT8_ARRAY(isr_reg8, loongarch_extioi,
> +                            EXTIOI_IRQS_BITMAP_SIZE),
> +        VMSTATE_UINT8_2DARRAY(coreisr_reg8, loongarch_extioi, MAX_CORES,
> +                              EXTIOI_IRQS_BITMAP_SIZE),
> +        VMSTATE_UINT8_ARRAY(ipmap_reg8, loongarch_extioi,
> +                            EXTIOI_IRQS_IPMAP_SIZE),
> +        VMSTATE_UINT8_ARRAY(coremap_reg8, loongarch_extioi,
> +                            EXTIOI_IRQS_COREMAP_SIZE),
> +        VMSTATE_UINT16_ARRAY(nodetype_reg16, loongarch_extioi,
> +                             EXTIOI_IRQS_NODETYPE_SIZE),
> +        VMSTATE_UINT8_ARRAY(sw_ipmap, loongarch_extioi, EXTIOI_IRQS),
> +        VMSTATE_UINT8_ARRAY(sw_coremap, loongarch_extioi, EXTIOI_IRQS),
> +        VMSTATE_STRUCT_2DARRAY(sw_ipisr, loongarch_extioi, MAX_CORES,
> +                               LS3A_INTC_IP, 1, vmstate_ext_sw_ipisr,
> +                               ext_sw_ipisr),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void loongarch_extioi_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->vmsd = &vmstate_loongarch_extioi;
> +    dc->realize = loongarch_extioi_realize;
> +}
> +
> +static const TypeInfo loongarch_extioi_info = {
> +    .name          = TYPE_LOONGARCH_EXTIOI,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(struct loongarch_extioi),
> +    .class_init    = loongarch_extioi_class_init,
> +};
> +
> +static void loongarch_extioi_register_types(void)
> +{
> +    type_register_static(&loongarch_extioi_info);
> +}
> +
> +type_init(loongarch_extioi_register_types)
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index e04abe2d56..e8d3d46e3e 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -59,3 +59,4 @@ specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c'))
>   specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
>   specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c'))
>   specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c'))
> +specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c'))
> diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
> index 4500fd3a57..9212127701 100644
> --- a/hw/loongarch/Kconfig
> +++ b/hw/loongarch/Kconfig
> @@ -3,3 +3,4 @@ config LOONGSON_3A5000
>       select PCI_EXPRESS_7A
>       select LOONGARCH_PCH_PIC
>       select LOONGARCH_PCH_MSI
> +    select LOONGARCH_EXTIOI
> diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h
> new file mode 100644
> index 0000000000..f6381b6236
> --- /dev/null
> +++ b/include/hw/intc/loongarch_extioi.h
> @@ -0,0 +1,99 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * LoongArch 3A5000 ext interrupt controller definitions
> + *
> + * Copyright (C) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#include "hw/sysbus.h"
> +#include "hw/loongarch/loongarch.h"
> +
> +#ifndef LOONGARCH_EXTIOI_H
> +#define LOONGARCH_EXTIOI_H
> +
> +#define LS3A_INTC_IP               8
> +#define MAX_CORES                  LOONGARCH_MAX_VCPUS
> +#define EXTIOI_IRQS                (256)
> +#define EXTIOI_IRQS_BITMAP_SIZE    (256 / 8)
> +/* map to ipnum per 32 irqs */
> +#define EXTIOI_IRQS_IPMAP_SIZE     (256 / 32)
> +#define EXTIOI_IRQS_COREMAP_SIZE   256
> +#define EXTIOI_IRQS_NODETYPE_SIZE  16
> +
> +#define APIC_OFFSET                  0x400
> +#define APIC_BASE                    (0x1000ULL + APIC_OFFSET)
> +
> +#define EXTIOI_NODETYPE_START        (0x4a0 - APIC_OFFSET)
> +#define EXTIOI_NODETYPE_END          (0x4c0 - APIC_OFFSET)
> +#define EXTIOI_IPMAP_START           (0x4c0 - APIC_OFFSET)
> +#define EXTIOI_IPMAP_END             (0x4c8 - APIC_OFFSET)
> +#define EXTIOI_ENABLE_START          (0x600 - APIC_OFFSET)
> +#define EXTIOI_ENABLE_END            (0x620 - APIC_OFFSET)
> +#define EXTIOI_BOUNCE_START          (0x680 - APIC_OFFSET)
> +#define EXTIOI_BOUNCE_END            (0x6a0 - APIC_OFFSET)
> +#define EXTIOI_ISR_START             (0x700 - APIC_OFFSET)
> +#define EXTIOI_ISR_END               (0x720 - APIC_OFFSET)
> +#define EXTIOI_COREISR_START         (0x800 - APIC_OFFSET)
> +#define EXTIOI_COREISR_END           (0xB20 - APIC_OFFSET)
> +#define EXTIOI_COREMAP_START         (0xC00 - APIC_OFFSET)
> +#define EXTIOI_COREMAP_END           (0xD00 - APIC_OFFSET)
> +
> +#define TYPE_LOONGARCH_EXTIOI "loongarch.extioi"
> +DECLARE_INSTANCE_CHECKER(struct loongarch_extioi, LOONGARCH_EXTIOI,
> +                         TYPE_LOONGARCH_EXTIOI)
> +
> +typedef struct ext_sw_ipisr {
> +    uint8_t irq[EXTIOI_IRQS_BITMAP_SIZE];
> +} ext_sw_ipisr;
> +
> +typedef struct loongarch_extioi {
> +    SysBusDevice parent_obj;
> +    /* hardware state */
> +    union {
> +        uint64_t en_reg64[EXTIOI_IRQS_BITMAP_SIZE / 8];
> +        uint32_t en_reg32[EXTIOI_IRQS_BITMAP_SIZE / 4];
> +        uint8_t en_reg8[EXTIOI_IRQS_BITMAP_SIZE];
> +    };
> +    union {
> +        uint64_t bounce_reg64[EXTIOI_IRQS_BITMAP_SIZE / 8];
> +        uint32_t bounce_reg32[EXTIOI_IRQS_BITMAP_SIZE / 4];
> +        uint8_t bounce_reg8[EXTIOI_IRQS_BITMAP_SIZE];
> +    };
> +    union {
> +        uint64_t isr_reg64[EXTIOI_IRQS_BITMAP_SIZE / 8];
> +        uint32_t isr_reg32[EXTIOI_IRQS_BITMAP_SIZE / 4];
> +        uint8_t isr_reg8[EXTIOI_IRQS_BITMAP_SIZE];
> +    };
> +    union {
> +        uint64_t coreisr_reg64[MAX_CORES][EXTIOI_IRQS_BITMAP_SIZE / 8];
> +        uint32_t coreisr_reg32[MAX_CORES][EXTIOI_IRQS_BITMAP_SIZE / 4];
> +        uint8_t coreisr_reg8[MAX_CORES][EXTIOI_IRQS_BITMAP_SIZE];
> +    };
> +    union {
> +        uint64_t ipmap_reg64;
> +        uint32_t ipmap_reg32[EXTIOI_IRQS_IPMAP_SIZE / 4];
> +        uint8_t ipmap_reg8[EXTIOI_IRQS_IPMAP_SIZE];
> +    };
> +    union {
> +        uint64_t coremap_reg64[EXTIOI_IRQS_COREMAP_SIZE / 8];
> +        uint32_t coremap_reg32[EXTIOI_IRQS_COREMAP_SIZE / 4];
> +        uint8_t coremap_reg8[EXTIOI_IRQS_COREMAP_SIZE];
> +    };
> +    union {
> +        uint64_t nodetype_reg64[EXTIOI_IRQS_NODETYPE_SIZE / 4];
> +        uint32_t nodetype_reg32[EXTIOI_IRQS_NODETYPE_SIZE / 2];
> +        uint16_t nodetype_reg16[EXTIOI_IRQS_NODETYPE_SIZE];
> +        uint8_t nodetype_reg8[EXTIOI_IRQS_NODETYPE_SIZE * 2];
> +    };

Hmmm. I guess this is the fallout from having separate read/write functions for each 
different access size.

> +    /*software state */
> +    uint8_t sw_ipmap[EXTIOI_IRQS];
> +    uint8_t sw_coremap[EXTIOI_IRQS];
> +    ext_sw_ipisr sw_ipisr[MAX_CORES][LS3A_INTC_IP];
> +
> +    qemu_irq parent_irq[MAX_CORES][LS3A_INTC_IP];
> +    qemu_irq irq[EXTIOI_IRQS];
> +    MemoryRegion mmio;
> +} loongarch_extioi;
> +
> +#endif /* LOONGARCH_EXTIOI_H */
> diff --git a/include/hw/loongarch/loongarch.h b/include/hw/loongarch/loongarch.h
> index 54cc875e6d..a9690f63c1 100644
> --- a/include/hw/loongarch/loongarch.h
> +++ b/include/hw/loongarch/loongarch.h
> @@ -41,6 +41,7 @@ typedef struct LoongArchMachineState {
>       MemoryRegion *system_iocsr;
>   
>       gipiState   *gipi;
> +    qemu_irq    *pch_irq;
>   } LoongArchMachineState;
>   
>   #define TYPE_LOONGARCH_MACHINE  MACHINE_TYPE_NAME("loongson7a")

ATB,

Mark.


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

* Re: [RFC PATCH v2 00/30] Add Loongarch softmmu support.
  2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
                   ` (29 preceding siblings ...)
  2021-11-11  1:35 ` [RFC PATCH v2 30/30] hw/loongarch: Add Numa support Xiaojuan Yang
@ 2021-11-11 14:58 ` Mark Cave-Ayland
  2021-11-12  1:26   ` yangxiaojuan
  30 siblings, 1 reply; 62+ messages in thread
From: Mark Cave-Ayland @ 2021-11-11 14:58 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel

On 11/11/2021 01:34, Xiaojuan Yang wrote:

> Sorry only part of the v2 patch succeed. I consulted GNU sysadmin,
> He said our mail server was getting temporarily banned by fail2ban. Now the ban
> was removed. I resend the v2 series patch. For uefi is preparing to submit to
> the community only uefi binary can be provided now. All of the series patch
> add RFC title.
> 
> This series patch add softmmu support for LoongArch.
> Base on the linux-user emulation support V9 patch.
>    * https://patchew.org/QEMU/1630586467-22463-1-git-send-email-gaosong@loongson.cn/diff/1636340895-5255-1-git-send-email-gaosong@loongson.cn/
> 
> The latest kernel:
>    * https://github.com/loongson/linux/tree/loongarch-next
> The manual:
>    * https://github.com/loongson/LoongArch-Documentation/releases/tag/2021.10.11
> 
> Changes for v2:
> 1.Combine patch 2 and 3 into one.
> 2.Adjust the order of the patch.
> 3.Put all the binaries on the github.
> 4.Modify some emulate errors when use the kernel from the github.
> 5.Adjust some format problem and the Naming problem
> 6.Others mainly follow Richard's code review comments.
> 
> Please help review!
> 
> Thanks
> 
> Xiaojuan Yang (30):
>    target/loongarch: Update README
>    target/loongarch: Add CSR registers definition
>    target/loongarch: Add basic vmstate description of CPU.
>    target/loongarch: Define exceptions for LoongArch.
>    target/loongarch: Implement qmp_query_cpu_definitions()
>    target/loongarch: Add stabletimer support
>    target/loongarch: Add MMU support for LoongArch CPU.
>    target/loongarch: Add LoongArch CSR/IOCSR instruction
>    target/loongarch: Add TLB instruction support
>    target/loongarch: Add other core instructions support
>    target/loongarch: Add LoongArch interrupt and exception handle
>    target/loongarch: Add timer related instructions support.
>    target/loongarch: Add gdb support.
>    target/loongarch: Implement privilege instructions disassembly
>    hw/pci-host: Add ls7a1000 PCIe Host bridge support for Loongson
>      Platform
>    hw/loongarch: Add a virt LoongArch 3A5000 board support
>    hw/loongarch: Add LoongArch cpu interrupt support(CPUINTC)
>    hw/loongarch: Add LoongArch ipi interrupt support(IPI)
>    hw/intc: Add LoongArch ls7a interrupt controller support(PCH-PIC)
>    hw/intc: Add LoongArch ls7a msi interrupt controller support(PCH-MSI)
>    hw/intc: Add LoongArch extioi interrupt controller(EIOINTC)
>    hw/loongarch: Add irq hierarchy for the system
>    hw/loongarch: Add some devices support for 3A5000.
>    hw/loongarch: Add LoongArch ls7a rtc device support
>    hw/loongarch: Add default bios startup support.
>    hw/loongarch: Add -kernel and -initrd options support
>    hw/loongarch: Add LoongArch smbios support
>    hw/loongarch: Add LoongArch acpi support
>    hw/loongarch: Add machine->possible_cpus
>    hw/loongarch: Add Numa support.
> 
>   .../devices/loongarch64-softmmu/default.mak   |   3 +
>   configs/targets/loongarch64-softmmu.mak       |   4 +
>   gdb-xml/loongarch-base64.xml                  |  43 +
>   gdb-xml/loongarch-fpu64.xml                   |  57 ++
>   hw/Kconfig                                    |   1 +
>   hw/acpi/Kconfig                               |   4 +
>   hw/acpi/ls7a.c                                | 349 +++++++
>   hw/acpi/meson.build                           |   1 +
>   hw/intc/Kconfig                               |  12 +
>   hw/intc/loongarch_extioi.c                    | 588 ++++++++++++
>   hw/intc/loongarch_pch_msi.c                   |  73 ++
>   hw/intc/loongarch_pch_pic.c                   | 283 ++++++
>   hw/intc/meson.build                           |   3 +
>   hw/loongarch/Kconfig                          |  22 +
>   hw/loongarch/acpi-build.c                     | 653 +++++++++++++
>   hw/loongarch/fw_cfg.c                         |  33 +
>   hw/loongarch/fw_cfg.h                         |  15 +
>   hw/loongarch/ipi.c                            | 146 +++
>   hw/loongarch/loongarch_int.c                  |  59 ++
>   hw/loongarch/ls3a5000_virt.c                  | 647 +++++++++++++
>   hw/loongarch/meson.build                      |   7 +
>   hw/meson.build                                |   1 +
>   hw/pci-host/Kconfig                           |   4 +
>   hw/pci-host/ls7a.c                            | 223 +++++
>   hw/pci-host/meson.build                       |   1 +
>   hw/rtc/Kconfig                                |   3 +
>   hw/rtc/ls7a_rtc.c                             | 323 +++++++
>   hw/rtc/meson.build                            |   1 +
>   include/exec/poison.h                         |   2 +
>   include/hw/acpi/ls7a.h                        |  53 ++
>   include/hw/intc/loongarch_extioi.h            | 101 ++
>   include/hw/intc/loongarch_pch_msi.h           |  16 +
>   include/hw/intc/loongarch_pch_pic.h           |  49 +
>   include/hw/loongarch/gipi.h                   |  37 +
>   include/hw/loongarch/loongarch.h              |  78 ++
>   include/hw/pci-host/ls7a.h                    |  66 ++
>   include/sysemu/arch_init.h                    |   1 +
>   qapi/machine-target.json                      |   6 +-
>   qapi/machine.json                             |   2 +-
>   softmmu/qdev-monitor.c                        |   3 +-
>   target/Kconfig                                |   1 +
>   target/loongarch/Kconfig                      |   2 +
>   target/loongarch/README                       |  20 +
>   target/loongarch/cpu-csr.h                    | 334 +++++++
>   target/loongarch/cpu-param.h                  |   3 +
>   target/loongarch/cpu.c                        | 390 ++++++++
>   target/loongarch/cpu.h                        | 220 ++++-
>   target/loongarch/csr_helper.c                 | 331 +++++++
>   target/loongarch/disas.c                      |  86 ++
>   target/loongarch/gdbstub.c                    |  97 ++
>   target/loongarch/helper.h                     |  24 +
>   target/loongarch/insn_trans/trans_core.c.inc  | 570 +++++++++++
>   target/loongarch/insn_trans/trans_extra.c.inc |  32 +
>   target/loongarch/insns.decode                 |  51 +
>   target/loongarch/internals.h                  |  26 +
>   target/loongarch/machine.c                    | 210 ++++
>   target/loongarch/meson.build                  |  10 +
>   target/loongarch/op_helper.c                  |  58 ++
>   target/loongarch/stabletimer.c                |  70 ++
>   target/loongarch/tlb_helper.c                 | 901 ++++++++++++++++++
>   target/loongarch/translate.c                  |   7 +
>   61 files changed, 7410 insertions(+), 6 deletions(-)
>   create mode 100644 configs/devices/loongarch64-softmmu/default.mak
>   create mode 100644 configs/targets/loongarch64-softmmu.mak
>   create mode 100644 gdb-xml/loongarch-base64.xml
>   create mode 100644 gdb-xml/loongarch-fpu64.xml
>   create mode 100644 hw/acpi/ls7a.c
>   create mode 100644 hw/intc/loongarch_extioi.c
>   create mode 100644 hw/intc/loongarch_pch_msi.c
>   create mode 100644 hw/intc/loongarch_pch_pic.c
>   create mode 100644 hw/loongarch/Kconfig
>   create mode 100644 hw/loongarch/acpi-build.c
>   create mode 100644 hw/loongarch/fw_cfg.c
>   create mode 100644 hw/loongarch/fw_cfg.h
>   create mode 100644 hw/loongarch/ipi.c
>   create mode 100644 hw/loongarch/loongarch_int.c
>   create mode 100644 hw/loongarch/ls3a5000_virt.c
>   create mode 100644 hw/loongarch/meson.build
>   create mode 100644 hw/pci-host/ls7a.c
>   create mode 100644 hw/rtc/ls7a_rtc.c
>   create mode 100644 include/hw/acpi/ls7a.h
>   create mode 100644 include/hw/intc/loongarch_extioi.h
>   create mode 100644 include/hw/intc/loongarch_pch_msi.h
>   create mode 100644 include/hw/intc/loongarch_pch_pic.h
>   create mode 100644 include/hw/loongarch/gipi.h
>   create mode 100644 include/hw/loongarch/loongarch.h
>   create mode 100644 include/hw/pci-host/ls7a.h
>   create mode 100644 target/loongarch/Kconfig
>   create mode 100644 target/loongarch/cpu-csr.h
>   create mode 100644 target/loongarch/csr_helper.c
>   create mode 100644 target/loongarch/gdbstub.c
>   create mode 100644 target/loongarch/insn_trans/trans_core.c.inc
>   create mode 100644 target/loongarch/machine.c
>   create mode 100644 target/loongarch/stabletimer.c
>   create mode 100644 target/loongarch/tlb_helper.c

Hi Xiaojuan,

I've started to have a look at some of the devices, and replied with a few comments. 
It feels as if this code has been maintained in a fork for some time, using a lot of 
design patterns that are no longer recommended for QEMU development.

In particular:

- Use qdev GPIOs for IRQs and avoid allocating them with qemu_allocate_irq()

- Use qdev link properties for passing object references rather than manipulating 
pointers directly

- Avoid heap allocations (particularly for MemoryRegions) and instead store them 
within the relevant device

- Specify a specific endianness for devices using DEVICE_BIG_ENDIAN and 
DEVICE_LITTLE_ENDIAN instead of DEVICE_NATIVE_ENDIAN

- Having separate functions for each MMIO access size isn't generally required with 
the memory API

I've stopped reviewing for now as I think these issues will be present throughout the 
patch series, however I will do my best to answer any questions that you have and 
will take another look when you post a v3.


ATB,

Mark.


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

* Re: [RFC PATCH v2 07/30] target/loongarch: Add MMU support for LoongArch CPU.
  2021-11-11  1:35 ` [RFC PATCH v2 07/30] target/loongarch: Add MMU support for LoongArch CPU Xiaojuan Yang
@ 2021-11-11 15:53   ` Richard Henderson
  2021-11-17  6:37     ` yangxiaojuan
  0 siblings, 1 reply; 62+ messages in thread
From: Richard Henderson @ 2021-11-11 15:53 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
> This patch introduces basic TLB interfaces.
> 
> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao <gaosong@loongson.cn>
> ---
>   target/loongarch/cpu-param.h  |   3 +
>   target/loongarch/cpu.c        |  36 ++++
>   target/loongarch/cpu.h        |  57 ++++++
>   target/loongarch/internals.h  |   7 +
>   target/loongarch/machine.c    |  56 ++++++
>   target/loongarch/meson.build  |   1 +
>   target/loongarch/tlb_helper.c | 339 ++++++++++++++++++++++++++++++++++
>   7 files changed, 499 insertions(+)
>   create mode 100644 target/loongarch/tlb_helper.c
> 
> diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h
> index 9a769b67e0..5a2147fb90 100644
> --- a/target/loongarch/cpu-param.h
> +++ b/target/loongarch/cpu-param.h
> @@ -12,6 +12,9 @@
>   #define TARGET_PHYS_ADDR_SPACE_BITS 48
>   #define TARGET_VIRT_ADDR_SPACE_BITS 48
>   
> +#define TARGET_PHYS_MASK ((1UL << TARGET_PHYS_ADDR_SPACE_BITS) - 1)
> +#define TARGET_VIRT_MASK ((1UL << TARGET_VIRT_ADDR_SPACE_BITS) - 1)

As before, unsigned long is wrong; use MAKE_64BIT_MASK.

These do not belong in cpu-param.h anyway; probably only tlb_helper.c needs them.

> +#ifndef CONFIG_USER_ONLY
> +    qemu_fprintf(f, "EUEN            0x%lx\n", env->CSR_EUEN);
> +    qemu_fprintf(f, "ESTAT           0x%lx\n", env->CSR_ESTAT);
> +    qemu_fprintf(f, "ERA             0x%lx\n", env->CSR_ERA);
> +    qemu_fprintf(f, "CRMD            0x%lx\n", env->CSR_CRMD);
> +    qemu_fprintf(f, "PRMD            0x%lx\n", env->CSR_PRMD);
> +    qemu_fprintf(f, "BadVAddr        0x%lx\n", env->CSR_BADV);
> +    qemu_fprintf(f, "TLB refill ERA  0x%lx\n", env->CSR_TLBRERA);
> +    qemu_fprintf(f, "TLB refill BadV 0x%lx\n", env->CSR_TLBRBADV);
> +    qemu_fprintf(f, "EENTRY          0x%lx\n", env->CSR_EENTRY);
> +    qemu_fprintf(f, "BadInstr        0x%lx\n", env->CSR_BADI);
> +    qemu_fprintf(f, "PRCFG1    0x%lx\nPRCFG2     0x%lx\nPRCFG3     0x%lx\n",
> +                 env->CSR_PRCFG1, env->CSR_PRCFG3, env->CSR_PRCFG3);

%lx is wrong; use PRIx64.

> +#define LOONGARCH_TLB_MAX      2112 /* 2048 STLB + 64 MTLB */

Better to write (2048 + 64).

> +FIELD(TLB_MISC, E, 0, 1)
> +FIELD(TLB_MISC, ASID, 1, 10)
> +FIELD(TLB_MISC, G, 11, 1)
> +FIELD(TLB_MISC, PS, 12, 6)
> +FIELD(TLB_MISC, VPPN, 18, 35)
> +
> +/* Corresponding to CSR_TLBELO0/1 */
> +FIELD(ENTRY0, V, 0, 1)
> +FIELD(ENTRY0, D, 1, 1)
> +FIELD(ENTRY0, NR, 2, 1)
> +FIELD(ENTRY0, NX, 3, 1)
> +FIELD(ENTRY0, MAT, 4, 2)
> +FIELD(ENTRY0, PLV, 6, 2)
> +FIELD(ENTRY0, RPLV, 8, 1)
> +FIELD(ENTRY0, PPN, 9, 36)
> +
> +FIELD(ENTRY1, V, 0, 1)
> +FIELD(ENTRY1, D, 1, 1)
> +FIELD(ENTRY1, NR, 2, 1)
> +FIELD(ENTRY1, NX, 3, 1)
> +FIELD(ENTRY1, MAT, 4, 2)
> +FIELD(ENTRY1, PLV, 6, 2)
> +FIELD(ENTRY1, RPLV, 8, 1)
> +FIELD(ENTRY1, PPN, 9, 36)

Why are you duplicating the CSR_TLBELO* fields?

> +const VMStateInfo vmstate_info_tlb = {
> +    .name = "tlb_entry",
> +    .get  = get_tlb,
> +    .put  = put_tlb,
> +};

Better to use .fields.

> +#define VMSTATE_TLB_ARRAY_V(_f, _s, _n, _v)                     \
> +    VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_tlb, loongarch_tlb)
> +
> +#define VMSTATE_TLB_ARRAY(_f, _s, _n)                           \
> +    VMSTATE_TLB_ARRAY_V(_f, _s, _n, 0)

Don't need these.

> +
> +const VMStateDescription vmstate_tlb = {
> +    .name = "cpu/tlb",
> +    .version_id = 0,
> +    .minimum_version_id = 0,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_TLB_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX),

VMSTATE_STRUCT_ARRAY.

> +        VMSTATE_END_OF_LIST()
> +    }
> +};
>   
>   /* LoongArch CPU state */
>   
> @@ -22,6 +70,10 @@ const VMStateDescription vmstate_loongarch_cpu = {
>           VMSTATE_UINT64_ARRAY(env.fpr, LoongArchCPU, 32),
>           VMSTATE_UINT32(env.fcsr0, LoongArchCPU),
>   
> +        /* TLB */
> +        VMSTATE_UINT32(env.stlb_size, LoongArchCPU),
> +        VMSTATE_UINT32(env.mtlb_size, LoongArchCPU),

Might as well keep these in vmstate_tlb.

> +/* TLB address map */
> +static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical,
> +                                   int *prot, target_ulong address,
> +                                   int access_type, loongarch_tlb *tlb)
> +{
> +    uint64_t plv = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV);

Incorrect.  PLV associated with mmu_idx, so you need to use that.

> +    uint8_t tlb_ps, n, tlb_v0, tlb_v1, tlb_d0, tlb_d1;
> +    uint8_t tlb_nx0, tlb_nx1, tlb_nr0, tlb_nr1;
> +    uint64_t tlb_ppn0, tlb_ppn1;
> +    uint8_t tlb_rplv0, tlb_rplv1, tlb_plv0, tlb_plv1;
> +
> +    tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
> +    n = (address >> tlb_ps) & 0x1;/* Odd or even */

Surely you need to pass in tlb_ps, since it's not present in the STLB entries.

> +
> +    tlb_v0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, V);
> +    tlb_d0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, D);
> +    tlb_plv0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, PLV);
> +    tlb_ppn0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, PPN);
> +    tlb_nx0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, NX);
> +    tlb_nr0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, NR);
> +    tlb_rplv0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, RPLV);
> +
> +    tlb_v1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, V);
> +    tlb_d1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, D);
> +    tlb_plv1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, PLV);
> +    tlb_ppn1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, PPN);
> +    tlb_nx1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, NX);
> +    tlb_nr1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, NR);
> +    tlb_rplv1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, RPLV);

Better to check N first.

     entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0;
     v = FIELD_EX64(entry, TLBENTRY, V);

etc.

> +        *physical = (tlb_ppn1 << 12) | (address & ((1 << tlb_ps) - 1));

TARGET_PAGE_SIZE.

> +/* LoongArch 3A5000 -style MMU emulation */

There's no mention of 3A5000 in "LoongArch Reference Manual", which defines this.  I think 
you've copied this from MIPS, which talks about r4k here.

> +static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
> +                                 int *prot,
> +                                 target_ulong address,
> +                                 MMUAccessType access_type)
> +{
> +    loongarch_tlb *tlb;
> +    uint16_t csr_asid, tlb_asid, stlb_idx;
> +    uint8_t tlb_e, stlb_ps, tlb_ps, tlb_g;
> +    int i, stlb_size, mtlb_size;
> +    uint64_t vpn, tlb_vppn;   /* Address to map */
> +
> +    stlb_size = env->stlb_size;
> +    mtlb_size = env->mtlb_size;
> +    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
> +
> +    /* Search MTLB */
> +    for (i = stlb_size; i < stlb_size + mtlb_size; ++i) {

Bit of a shame to have a linear search.  I guess it's ok for a start, but you'll find that 
this function is critical to the emulation speed of qemu, so you should think about other 
ways to organize the data.

> +        tlb = &env->tlb[i];
> +        tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
> +        tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
> +
> +        vpn = (address & TARGET_VIRT_MASK) >> (tlb_ps + 1);

Why +1?

> +        tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
> +        tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
> +        tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
> +
> +        if ((tlb_g == 1 || tlb_asid == csr_asid) &&
> +            (vpn == (tlb_vppn >> (tlb_ps + 1 - 13))) && tlb_e) {

Surely extract and test the enable bit E before anything else, just to speed up the lookup.

The -13 is a bit of a magic number.  Surely TARGET_PAGE_BITS + 1.

> +static int get_physical_address(CPULoongArchState *env, hwaddr *physical,
> +                                int *prot, target_ulong real_address,
> +                                MMUAccessType access_type, int mmu_idx)
> +{
> +    int user_mode = mmu_idx == LOONGARCH_HFLAG_UM;
> +    int kernel_mode = !user_mode;

Incorrect.  PLV == mmu_idx, as defined by cpu_mmu_index.

> +    unsigned plv, base_c, base_v, tmp;
> +    uint64_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG);
> +
> +    /* Effective address */
> +    target_ulong address = real_address;
> +
> +    /* Check PG */
> +    if (!pg) {
> +        /* DA mode */
> +        *physical = address & TARGET_PHYS_MASK;
> +        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
> +        return TLBRET_MATCH;
> +    }

You need to define a fifth mmu_index for paging disabled, because the software tlb handler 
runs in this mode.  If you don't define that, you'll have to flush the softmmu tlb every 
time you have a tlb miss.

> +    plv = kernel_mode | (user_mode << 3);

plv_mask = 1 << plv;

> +    base_v = address >> CSR_DMW_BASE_SH;
> +    /* Check direct map window 0 */
> +    base_c = env->CSR_DMWIN0 >> CSR_DMW_BASE_SH;
> +    if ((plv & env->CSR_DMWIN0) && (base_c == base_v)) {
> +        *physical = dmwin_va2pa(address);
> +        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
> +        return TLBRET_MATCH;
> +    }
> +    /* Check direct map window 1 */
> +    base_c = env->CSR_DMWIN1 >> CSR_DMW_BASE_SH;
> +    if ((plv & env->CSR_DMWIN1) && (base_c == base_v)) {
> +        *physical = dmwin_va2pa(address);
> +        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
> +        return TLBRET_MATCH;
> +    }

There are more than 2 of these, which is why I suggested putting them in an array, so that 
you can loop.  Maybe pull this out to a separate function, like the tlb lookup?

> +    /* Check valid extension */
> +    tmp = address >> (TARGET_VIRT_ADDR_SPACE_BITS - 1);
> +    if (!(tmp == 0 || tmp == 0x1ffff)) {

Better to cast to int64_t first, so that this becomes 0 or -1, without knowing that 64 - 
TARGET_VIRT_ADDR_SPACE_BITS - 1 -> 0x1ffff.

> +void loongarch_mmu_init(CPULoongArchState *env)
> +{
> +    /* Number of MTLB */
> +    env->mtlb_size = 64;
> +
> +    /* Number of STLB */
> +    env->stlb_size = 2048;
> +
> +    /* For 16KB, ps = 14, compare the bit [47:15] */
> +    for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
> +        env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, TLB_MISC, E, 0);
> +    }

Just memset the whole thing?

> +bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
> +                            MMUAccessType access_type, int mmu_idx,
> +                            bool probe, uintptr_t retaddr)
> +{
> +    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
> +    CPULoongArchState *env = &cpu->env;
> +    hwaddr physical;
> +    int prot;
> +    int ret = TLBRET_BADADDR;
> +
> +    /* Data access */
> +    /* XXX: put correct access by using cpu_restore_state() correctly */
> +    ret = get_physical_address(env, &physical, &prot, address,
> +                               access_type, mmu_idx);
> +    switch (ret) {
> +    case TLBRET_MATCH:
> +        qemu_log_mask(CPU_LOG_MMU,
> +                      "%s address=%" VADDR_PRIx " physical " TARGET_FMT_plx
> +                      " prot %d\n", __func__, address, physical, prot);
> +        break;
> +    default:
> +        qemu_log_mask(CPU_LOG_MMU,
> +                      "%s address=%" VADDR_PRIx " ret %d\n", __func__, address,
> +                      ret);
> +        break;
> +    }
> +    if (ret == TLBRET_MATCH) {
> +        tlb_set_page(cs, address & TARGET_PAGE_MASK,
> +                     physical & TARGET_PAGE_MASK, prot,
> +                     mmu_idx, TARGET_PAGE_SIZE);
> +        return true;

Merge the above switch into this?

> +    }
> +    if (probe) {
> +        return false;
> +    } else {
> +        raise_mmu_exception(env, address, access_type, ret);
> +        do_raise_exception(env, cs->exception_index, retaddr);
> +    }
> +}
> 


r~


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

* Re: [RFC PATCH v2 08/30] target/loongarch: Add LoongArch CSR/IOCSR instruction
  2021-11-11  1:35 ` [RFC PATCH v2 08/30] target/loongarch: Add LoongArch CSR/IOCSR instruction Xiaojuan Yang
@ 2021-11-11 17:43   ` Richard Henderson
  2021-11-17  8:48     ` yangxiaojuan
  0 siblings, 1 reply; 62+ messages in thread
From: Richard Henderson @ 2021-11-11 17:43 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
> This includes:
> - CSRRD
> - CSRWR
> - CSRXCHG
> - IOCSR{RD/WR}.{B/H/W/D}

I think IOCSR should be in a separate patch.
It's completely unrelated to the other CSRs.

> +target_ulong helper_csr_rdq(CPULoongArchState *env, uint64_t csr)
> +{
> +    int64_t v;
> +
> +    switch (csr) {
> +    case LOONGARCH_CSR_PGD:
> +        if (env->CSR_TLBRERA & 0x1) {
> +            v = env->CSR_TLBRBADV;
> +        } else {
> +            v = env->CSR_BADV;
> +        }
> +
> +        if ((v >> 63) & 0x1) {
> +            v = env->CSR_PGDH;
> +        } else {
> +            v = env->CSR_PGDL;
> +        }
> +        v = v & TARGET_PHYS_MASK;

This csr is defined to be GRLEN bits; I this mask looks wrong.

> +    default:
> +        assert(0);

g_assert_not_reached.

> +    switch (csr) {
> +    case LOONGARCH_CSR_ASID:
> +        old_v = env->CSR_ASID;
> +        env->CSR_ASID = val;

Mask the write to the field; you don't want to corrupt ASIDBITS, or the other read-only bits.

> +    case LOONGARCH_CSR_TCFG:
> +        old_v = env->CSR_TCFG;
> +        cpu_loongarch_store_stable_timer_config(env, val);
> +        break;
> +    case LOONGARCH_CSR_TINTCLR:
> +        old_v = 0;
> +        qemu_irq_lower(env->irq[IRQ_TIMER]);

The interrupt is not documented to clear on any write; only writes of 1 to bit 0.

> +    default:
> +        assert(0);

g_assert_not_reached.

> +    }
> +
> +    return old_v;
> +}
> +
> +target_ulong helper_csr_xchgq(CPULoongArchState *env, target_ulong val,
> +                              target_ulong mask, uint64_t csr)
> +{
> +    target_ulong tmp;
> +    target_ulong v = val & mask;

I think it would be less confusing to name the input parameter new_val, and the local 
temporary old_val.

> +#define CASE_CSR_XCHGQ(csr)                                 \
> +    case LOONGARCH_CSR_ ## csr:                             \
> +    {                                                       \
> +        val = env->CSR_ ## csr;                             \
> +        env->CSR_ ## csr = (env->CSR_ ## csr) & (~mask);    \
> +        env->CSR_ ## csr = (env->CSR_ ## csr) | v;          \

   old_val = env->CSR_##csr;
   env->CSR_##csr = (old_val & ~mask) | (new_val & mask);


> +    switch (csr) {
> +    CASE_CSR_XCHGQ(CRMD)

I wonder if all of this would be better with a table of offsets, which could be shared 
with the translator.

#define CSR_OFF(X)  [LOONGARCH_CSR_##X] = offsetof(CPUArchState, CSR_##X)

static const int csr_offsets[] = {
     CSR_OFF(CRMD),
     ...
};

int cpu_csr_offset(unsigned csr_num)
{
     if (csr_num < ARRAY_SIZE(csr_offsets)) {
         return csr_offsets[csr_num];
     }
     return 0;
}

Which reduces this function to

     unsigned csr_offset = cpu_csr_offset(csr_num);
     if (csr_offset == 0) {
         /* CSR is undefined: read as 0, write ignored. */
         return 0;
     }

     uint64_t *csr = (void *)env + csr_offset;
     uint64_t old_val = *csr;

     new_val = (new_val & mask) | (old_val & ~mask);

     *csr = (old_val & ~mask) | (new_val & mask);

     if (csr_num == LOONGARCH_CSR_TCFG) {
         cpu_loongarch_store_stable_timer_config(env, new_val);
     } else {
         *csr = new_val;
     }
     return old_val;

> +uint64_t helper_iocsr_read(CPULoongArchState *env, target_ulong r_addr,
> +                           uint32_t size)
> +{
> +    LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
> +    int cpuid = env_cpu(env)->cpu_index;
> +
> +    if (((r_addr & 0xff00) == 0x1000) || ((r_addr & 0xff00) == 0x1800)) {
> +        r_addr = r_addr + ((target_ulong)(cpuid & 0x3) << 8);
> +    }

This looks to be something that should be controlled by the address space assigned to each 
cpu.

   But it's hard to tell.

Where is the documentation for this?  I didn't immediately find it in 3A5000 Technical 
Reference Manual, Chapter 10.

> +void helper_iocsr_write(CPULoongArchState *env, target_ulong w_addr,
> +                        target_ulong val, uint32_t size)
> +{
> +    LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
> +    int cpuid = env_cpu(env)->cpu_index;
> +    int mask, i;
> +
> +    /*
> +     * For IPI send, Mail send, ANY send adjust addr and val
> +     * according to their real meaning
> +     */
> +    if (w_addr == 0x1040) { /* IPI send */
> +        cpuid = (val >> 16) & 0x3ff;
> +        val = 1UL << (val & 0x1f);
> +        w_addr = 0x1008;

I don't see any interrupts actually being raised?

> +static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
> +{
> +    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
> +
> +    gen_helper_check_plv(cpu_env);

You don't need an external call.  PLV should be part of TB_FLAGS, so you can check this 
during translation.

> +    case LOONGARCH_CSR_TVAL:
> +        gen_helper_csr_rdq(dest, cpu_env, tcg_constant_i64(a->csr));
> +        break;
> +    default:
> +        assert(0);

The assert was definitely wrong, as it allows incorrect programs to crash qemu.  In 
addition, unknown csr read as 0.

> +    CASE_CSR_WRQ(MISC)

You don't actually support any of the MISC bits yet.
You should make this register read-only until you do.

How many more registers are read-only, or have read-only fields that you are not checking?


r~


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

* Re: [RFC PATCH v2 09/30] target/loongarch: Add TLB instruction support
  2021-11-11  1:35 ` [RFC PATCH v2 09/30] target/loongarch: Add TLB instruction support Xiaojuan Yang
@ 2021-11-11 18:14   ` Richard Henderson
  2021-11-17  7:29     ` yangxiaojuan
  0 siblings, 1 reply; 62+ messages in thread
From: Richard Henderson @ 2021-11-11 18:14 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
> +static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
> +{
> +    gen_helper_check_plv(cpu_env);
> +    gen_helper_tlbwr(cpu_env);
> +    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
> +    ctx->base.is_jmp = DISAS_EXIT;
> +    return true;
> +}

I think you can skip the EXIT if paging is disabled, which it usually will be in the 
software tlb handler.  You'd be able to tell with the mmu_idx being the one you use for 
paging disabled.

> +static void loongarch_invalidate_tlb_entry(CPULoongArchState *env,
> +                                           loongarch_tlb *tlb)
> +{
> +    CPUState *cs = env_cpu(env);
> +    target_ulong addr, end, mask;
> +    int tlb_v0, tlb_v1;
> +    uint64_t tlb_vppn;
> +    uint8_t tlb_ps;
> +
> +    tlb_v0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, V);
> +    tlb_v1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, V);
> +    tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
> +    tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
> +    mask = (1 << (1 + tlb_ps)) - 1;

MAKE_64BIT_MASK.

> +
> +    if (tlb_v0) {
> +        addr = tlb_vppn & ~mask;    /* xxx...xxx[0]000..0000 */
> +        end = addr | (mask >> 1);   /* xxx...xxx[0]111..1111 */
> +        while (addr < end) {
> +            tlb_flush_page(cs, addr);
> +            addr += TARGET_PAGE_SIZE;

tlb_flush_range_by_mmuidx.

> +    tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn);
> +    tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1);
> +    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
> +    tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid);
> +
> +    csr_g = FIELD_EX64(env->CSR_TLBELO0, CSR_TLBELO0, G) &
> +             FIELD_EX64(env->CSR_TLBELO1, CSR_TLBELO1, G);
> +    tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, G, csr_g);
> +
> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, V,
> +                                 FIELD_EX64(lo0, CSR_TLBELO0, V));/* [0] */
> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, D,
> +                                 FIELD_EX64(lo0, CSR_TLBELO0, D));/* [1] */
> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, PLV,
> +                                 FIELD_EX64(lo0, CSR_TLBELO0, PLV));/* [3:2] */
> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, MAT,
> +                                 FIELD_EX64(lo0, CSR_TLBELO0, MAT));/* [5:4] */
> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, PPN,
> +                                 FIELD_EX64(lo0, CSR_TLBELO0, PPN));/* [47:12] */
> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, NR,
> +                                 FIELD_EX64(lo0, CSR_TLBELO0, NR));/* [61] */
> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, NX,
> +                                 FIELD_EX64(lo0, CSR_TLBELO0, NX));/* [62] */
> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, RPLV,
> +                                 FIELD_EX64(lo0, CSR_TLBELO0, RPLV));/* [63] */
> +
> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, V,
> +                                 FIELD_EX64(lo1, CSR_TLBELO1, V));/* [0] */
> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, D,
> +                                 FIELD_EX64(lo1, CSR_TLBELO1, D));/* [1] */
> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, PLV,
> +                                 FIELD_EX64(lo1, CSR_TLBELO1, PLV));/* [3:2] */
> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, MAT,
> +                                 FIELD_EX64(lo1, CSR_TLBELO1, MAT));/* [5:4] */
> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, PPN,
> +                                 FIELD_EX64(lo1, CSR_TLBELO1, PPN));/* [47:12] */
> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, NR,
> +                                 FIELD_EX64(lo1, CSR_TLBELO1, NR));/* [61] */
> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, NX,
> +                                 FIELD_EX64(lo1, CSR_TLBELO1, NX));/* [62] */
> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, RPLV,
> +                                 FIELD_EX64(lo1, CSR_TLBELO1, RPLV));/* [63] */

The point of making the two values have the same field layout is so that you can just 
assign the whole value across, not extract and re-deposit each field.

> +void helper_tlbsrch(CPULoongArchState *env)
> +{
> +    loongarch_tlb *tlb;
> +    uint64_t vpn, tlb_vppn;
> +    uint16_t csr_asid, tlb_asid, tlb_ps, tlb_e, tlb_g;
> +
> +    int stlb_size = env->stlb_size;
> +    int mtlb_size = env->mtlb_size;
> +    int i;
> +    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
> +
> +    /* Search MTLB + STLB */
> +    for (i = 0; i < stlb_size + mtlb_size; ++i) {
> +        tlb = &env->tlb[i];
> +        vpn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI, VPPN);
> +        tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
> +        tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
> +        tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
> +        tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
> +        tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
> +
> +        if ((tlb_g == 1 || tlb_asid == csr_asid) &&
> +            (vpn >> (tlb_ps + 1 - 13) == tlb_vppn >> (tlb_ps + 1 - 13)) && tlb_e) {
> +            env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
> +                                         INDEX, (i & 0xfff));
> +            env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
> +                                         PS, (tlb_ps & 0x3f));
> +            return;
> +        }
> +    }
> +
> +    env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
> +}

Surely this should share code with loongarch_map_address.

> +
> +void helper_tlbrd(CPULoongArchState *env)
> +{
> +    loongarch_tlb *tlb;
> +    int idx;
> +    uint16_t csr_asid, tlb_asid;
> +    uint8_t tlb_ps, tlb_e, tlb_v0, tlb_v1, tlb_d0, tlb_d1;
> +    uint8_t tlb_plv0, tlb_plv1, tlb_mat0, tlb_mat1, tlb_g;
> +    uint64_t tlb_ppn0, tlb_ppn1;
> +    uint8_t tlb_nr0, tlb_nr1, tlb_nx0, tlb_nx1, tlb_rplv0, tlb_rplv1;
> +
> +    idx = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
> +    tlb = &env->tlb[idx];
> +
> +    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
> +    tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
> +    tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
> +    tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
> +    tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
> +
> +    tlb_v0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, V);
> +    tlb_d0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, D);
> +    tlb_plv0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, PLV);
> +    tlb_mat0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, MAT);
> +    tlb_ppn0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, PPN);
> +    tlb_nr0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, NR);
> +    tlb_nx0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, NX);
> +    tlb_rplv0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, RPLV);
> +
> +    tlb_v1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, V);
> +    tlb_d1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, D);
> +    tlb_plv1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, PLV);
> +    tlb_mat1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, MAT);
> +    tlb_ppn1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, PPN);
> +    tlb_nr1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, NR);
> +    tlb_nx1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, NX);
> +    tlb_rplv1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, RPLV);
> +
> +    if (csr_asid != tlb_asid) {
> +        cpu_loongarch_tlb_flush(env);

Why?  Surely the index selected should not have matched on the previous search?

> +    } else {
> +        /* Valid TLB entry */
> +        env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
> +                                     INDEX, (idx & 0xfff));
> +        env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
> +                                     PS, (tlb_ps & 0x3f));
> +
> +        env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) << 13;
> +
> +        env->CSR_TLBELO0 = FIELD_DP64(0, CSR_TLBELO0, V, tlb_v0);
> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, D, tlb_d0);
> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, PLV, tlb_plv0);
> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, MAT, tlb_mat0);
> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, G, tlb_g);
> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, PPN, tlb_ppn0);
> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, NR, tlb_nr0);
> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, NX, tlb_nx0);
> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, RPLV, tlb_rplv0);
> +
> +        env->CSR_TLBELO1 = FIELD_DP64(0, CSR_TLBELO1, V, tlb_v1);
> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, D, tlb_d1);
> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, PLV, tlb_plv1);
> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, MAT, tlb_mat1);
> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, G, tlb_g);
> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, PPN, tlb_ppn1);
> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, NR, tlb_nr1);
> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, NX, tlb_nx1);
> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, RPLV, 

Again, these should easily copy across.

> +    env->CSR_ASID  = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, tlb_asid);

The documentation for TLBRD does not mention modifications to the current ASID.

> +    default:
> +        do_raise_exception(env, EXCP_INE, GETPC());

You can detect this during translation, and dispatch to the appropriate invtlb sub-function.


r~


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

* Re: [RFC PATCH v2 00/30] Add Loongarch softmmu support.
  2021-11-11 14:58 ` [RFC PATCH v2 00/30] Add Loongarch softmmu support Mark Cave-Ayland
@ 2021-11-12  1:26   ` yangxiaojuan
  0 siblings, 0 replies; 62+ messages in thread
From: yangxiaojuan @ 2021-11-12  1:26 UTC (permalink / raw)
  To: Mark Cave-Ayland, qemu-devel

Hi, Mark,

On 11/11/2021 10:58 PM, Mark Cave-Ayland wrote:
> On 11/11/2021 01:34, Xiaojuan Yang wrote:
> 
>> Sorry only part of the v2 patch succeed. I consulted GNU sysadmin,
>> He said our mail server was getting temporarily banned by fail2ban. Now the ban
>> was removed. I resend the v2 series patch. For uefi is preparing to submit to
>> the community only uefi binary can be provided now. All of the series patch
>> add RFC title.
>>
>> This series patch add softmmu support for LoongArch.
>> Base on the linux-user emulation support V9 patch.
>>    * https://patchew.org/QEMU/1630586467-22463-1-git-send-email-gaosong@loongson.cn/diff/1636340895-5255-1-git-send-email-gaosong@loongson.cn/
>>
>> The latest kernel:
>>    * https://github.com/loongson/linux/tree/loongarch-next
>> The manual:
>>    * https://github.com/loongson/LoongArch-Documentation/releases/tag/2021.10.11
>>
>> Changes for v2:
>> 1.Combine patch 2 and 3 into one.
>> 2.Adjust the order of the patch.
>> 3.Put all the binaries on the github.
>> 4.Modify some emulate errors when use the kernel from the github.
>> 5.Adjust some format problem and the Naming problem
>> 6.Others mainly follow Richard's code review comments.
>>
>> Please help review!
>>
>> Thanks
>>
>> Xiaojuan Yang (30):
>>    target/loongarch: Update README
>>    target/loongarch: Add CSR registers definition
>>    target/loongarch: Add basic vmstate description of CPU.
>>    target/loongarch: Define exceptions for LoongArch.
>>    target/loongarch: Implement qmp_query_cpu_definitions()
>>    target/loongarch: Add stabletimer support
>>    target/loongarch: Add MMU support for LoongArch CPU.
>>    target/loongarch: Add LoongArch CSR/IOCSR instruction
>>    target/loongarch: Add TLB instruction support
>>    target/loongarch: Add other core instructions support
>>    target/loongarch: Add LoongArch interrupt and exception handle
>>    target/loongarch: Add timer related instructions support.
>>    target/loongarch: Add gdb support.
>>    target/loongarch: Implement privilege instructions disassembly
>>    hw/pci-host: Add ls7a1000 PCIe Host bridge support for Loongson
>>      Platform
>>    hw/loongarch: Add a virt LoongArch 3A5000 board support
>>    hw/loongarch: Add LoongArch cpu interrupt support(CPUINTC)
>>    hw/loongarch: Add LoongArch ipi interrupt support(IPI)
>>    hw/intc: Add LoongArch ls7a interrupt controller support(PCH-PIC)
>>    hw/intc: Add LoongArch ls7a msi interrupt controller support(PCH-MSI)
>>    hw/intc: Add LoongArch extioi interrupt controller(EIOINTC)
>>    hw/loongarch: Add irq hierarchy for the system
>>    hw/loongarch: Add some devices support for 3A5000.
>>    hw/loongarch: Add LoongArch ls7a rtc device support
>>    hw/loongarch: Add default bios startup support.
>>    hw/loongarch: Add -kernel and -initrd options support
>>    hw/loongarch: Add LoongArch smbios support
>>    hw/loongarch: Add LoongArch acpi support
>>    hw/loongarch: Add machine->possible_cpus
>>    hw/loongarch: Add Numa support.
>>
>>   .../devices/loongarch64-softmmu/default.mak   |   3 +
>>   configs/targets/loongarch64-softmmu.mak       |   4 +
>>   gdb-xml/loongarch-base64.xml                  |  43 +
>>   gdb-xml/loongarch-fpu64.xml                   |  57 ++
>>   hw/Kconfig                                    |   1 +
>>   hw/acpi/Kconfig                               |   4 +
>>   hw/acpi/ls7a.c                                | 349 +++++++
>>   hw/acpi/meson.build                           |   1 +
>>   hw/intc/Kconfig                               |  12 +
>>   hw/intc/loongarch_extioi.c                    | 588 ++++++++++++
>>   hw/intc/loongarch_pch_msi.c                   |  73 ++
>>   hw/intc/loongarch_pch_pic.c                   | 283 ++++++
>>   hw/intc/meson.build                           |   3 +
>>   hw/loongarch/Kconfig                          |  22 +
>>   hw/loongarch/acpi-build.c                     | 653 +++++++++++++
>>   hw/loongarch/fw_cfg.c                         |  33 +
>>   hw/loongarch/fw_cfg.h                         |  15 +
>>   hw/loongarch/ipi.c                            | 146 +++
>>   hw/loongarch/loongarch_int.c                  |  59 ++
>>   hw/loongarch/ls3a5000_virt.c                  | 647 +++++++++++++
>>   hw/loongarch/meson.build                      |   7 +
>>   hw/meson.build                                |   1 +
>>   hw/pci-host/Kconfig                           |   4 +
>>   hw/pci-host/ls7a.c                            | 223 +++++
>>   hw/pci-host/meson.build                       |   1 +
>>   hw/rtc/Kconfig                                |   3 +
>>   hw/rtc/ls7a_rtc.c                             | 323 +++++++
>>   hw/rtc/meson.build                            |   1 +
>>   include/exec/poison.h                         |   2 +
>>   include/hw/acpi/ls7a.h                        |  53 ++
>>   include/hw/intc/loongarch_extioi.h            | 101 ++
>>   include/hw/intc/loongarch_pch_msi.h           |  16 +
>>   include/hw/intc/loongarch_pch_pic.h           |  49 +
>>   include/hw/loongarch/gipi.h                   |  37 +
>>   include/hw/loongarch/loongarch.h              |  78 ++
>>   include/hw/pci-host/ls7a.h                    |  66 ++
>>   include/sysemu/arch_init.h                    |   1 +
>>   qapi/machine-target.json                      |   6 +-
>>   qapi/machine.json                             |   2 +-
>>   softmmu/qdev-monitor.c                        |   3 +-
>>   target/Kconfig                                |   1 +
>>   target/loongarch/Kconfig                      |   2 +
>>   target/loongarch/README                       |  20 +
>>   target/loongarch/cpu-csr.h                    | 334 +++++++
>>   target/loongarch/cpu-param.h                  |   3 +
>>   target/loongarch/cpu.c                        | 390 ++++++++
>>   target/loongarch/cpu.h                        | 220 ++++-
>>   target/loongarch/csr_helper.c                 | 331 +++++++
>>   target/loongarch/disas.c                      |  86 ++
>>   target/loongarch/gdbstub.c                    |  97 ++
>>   target/loongarch/helper.h                     |  24 +
>>   target/loongarch/insn_trans/trans_core.c.inc  | 570 +++++++++++
>>   target/loongarch/insn_trans/trans_extra.c.inc |  32 +
>>   target/loongarch/insns.decode                 |  51 +
>>   target/loongarch/internals.h                  |  26 +
>>   target/loongarch/machine.c                    | 210 ++++
>>   target/loongarch/meson.build                  |  10 +
>>   target/loongarch/op_helper.c                  |  58 ++
>>   target/loongarch/stabletimer.c                |  70 ++
>>   target/loongarch/tlb_helper.c                 | 901 ++++++++++++++++++
>>   target/loongarch/translate.c                  |   7 +
>>   61 files changed, 7410 insertions(+), 6 deletions(-)
>>   create mode 100644 configs/devices/loongarch64-softmmu/default.mak
>>   create mode 100644 configs/targets/loongarch64-softmmu.mak
>>   create mode 100644 gdb-xml/loongarch-base64.xml
>>   create mode 100644 gdb-xml/loongarch-fpu64.xml
>>   create mode 100644 hw/acpi/ls7a.c
>>   create mode 100644 hw/intc/loongarch_extioi.c
>>   create mode 100644 hw/intc/loongarch_pch_msi.c
>>   create mode 100644 hw/intc/loongarch_pch_pic.c
>>   create mode 100644 hw/loongarch/Kconfig
>>   create mode 100644 hw/loongarch/acpi-build.c
>>   create mode 100644 hw/loongarch/fw_cfg.c
>>   create mode 100644 hw/loongarch/fw_cfg.h
>>   create mode 100644 hw/loongarch/ipi.c
>>   create mode 100644 hw/loongarch/loongarch_int.c
>>   create mode 100644 hw/loongarch/ls3a5000_virt.c
>>   create mode 100644 hw/loongarch/meson.build
>>   create mode 100644 hw/pci-host/ls7a.c
>>   create mode 100644 hw/rtc/ls7a_rtc.c
>>   create mode 100644 include/hw/acpi/ls7a.h
>>   create mode 100644 include/hw/intc/loongarch_extioi.h
>>   create mode 100644 include/hw/intc/loongarch_pch_msi.h
>>   create mode 100644 include/hw/intc/loongarch_pch_pic.h
>>   create mode 100644 include/hw/loongarch/gipi.h
>>   create mode 100644 include/hw/loongarch/loongarch.h
>>   create mode 100644 include/hw/pci-host/ls7a.h
>>   create mode 100644 target/loongarch/Kconfig
>>   create mode 100644 target/loongarch/cpu-csr.h
>>   create mode 100644 target/loongarch/csr_helper.c
>>   create mode 100644 target/loongarch/gdbstub.c
>>   create mode 100644 target/loongarch/insn_trans/trans_core.c.inc
>>   create mode 100644 target/loongarch/machine.c
>>   create mode 100644 target/loongarch/stabletimer.c
>>   create mode 100644 target/loongarch/tlb_helper.c
> 
> Hi Xiaojuan,
> 
> I've started to have a look at some of the devices, and replied with a few comments. It feels as if this code has been maintained in a fork for some time, using a lot of design patterns that are no longer recommended for QEMU development.
> 
> In particular:
> 
> - Use qdev GPIOs for IRQs and avoid allocating them with qemu_allocate_irq()
> 
> - Use qdev link properties for passing object references rather than manipulating pointers directly
> 
> - Avoid heap allocations (particularly for MemoryRegions) and instead store them within the relevant device
> 
> - Specify a specific endianness for devices using DEVICE_BIG_ENDIAN and DEVICE_LITTLE_ENDIAN instead of DEVICE_NATIVE_ENDIAN
> 
> - Having separate functions for each MMIO access size isn't generally required with the memory API
> 
> I've stopped reviewing for now as I think these issues will be present throughout the patch series, however I will do my best to answer any questions that you have and will take another look when you post a v3.
> 
> 
> ATB,
> 
> Mark.
 
 Thank you for your review, I will read your comments carefully and then modify. Yes, All the work first started at the qemu3.1 then port to the master.
 I'm sorry for the unrecommended design patterns. I will try my best to modify. I believe the V3 version will be much better and look foward to your review again.

Thanks,
Xiaojuan 



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

* Re: [RFC PATCH v2 02/30] target/loongarch: Add CSR registers definition
  2021-11-11 13:29   ` Richard Henderson
@ 2021-11-12  2:14     ` yangxiaojuan
  2021-11-12  7:14       ` Richard Henderson
  0 siblings, 1 reply; 62+ messages in thread
From: yangxiaojuan @ 2021-11-12  2:14 UTC (permalink / raw)
  To: Richard Henderson, qemu-devel; +Cc: Song Gao

Hi, Richard,

On 11/11/2021 09:29 PM, Richard Henderson wrote:
> On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
>> +#define LOONGARCH_CSR_MISC           0x3 /* Misc config */
>> +
> 
> Missing bitfield definitions for misc.
> 

 At present, there is no read/write to each field of misc register so it is not defined.

>> +#define  EXCODE_IP                   64
> 
> What's this?
> 

The manual 6.1.3 says the exception number of an interrupt is the exception number plus an offset of 64.
This defines the offset, sorry, the name is bad, maybe change a name or just use 64 directly in the
exception handle is better.

>> +#define  EXCCODE_INT                 0
>> +#define  EXCCODE_PIL                 1
>> +#define  EXCCODE_PIS                 2
>> +#define  EXCCODE_PIF                 3
>> +#define  EXCCODE_PME                 4
>> +#define  EXCCODE_PNR                 5
>> +#define  EXCCODE_PNX                 6
>> +#define  EXCCODE_PPI                 7
>> +#define  EXCCODE_ADE                 8
> 
> ADEF vs ADEM?
> 
Yes, ADEF and ADEM have different esubcode, maybe I should add a description.

>> +#define  EXCCODE_ALE                 9
>> +#define  EXCCODE_BCE                 10
>> +#define  EXCCODE_SYS                 11
>> +#define  EXCCODE_BRK                 12
>> +#define  EXCCODE_INE                 13
>> +#define  EXCCODE_IPE                 14
>> +#define  EXCCODE_FPD                 15
>> +#define  EXCCODE_SXD                 16
>> +#define  EXCCODE_ASXD                17
>> +#define  EXCCODE_FPE                 18 /* Have different expsubcode */
>> +#define  EXCCODE_VFPE                18
>> +#define  EXCCODE_WPEF                19 /* Have different expsubcode */
>> +#define  EXCCODE_WPEM                19
>> +#define  EXCCODE_BTD                 20
>> +#define  EXCCODE_BTE                 21
> 
> Missing BSPR, HVC, GCSC, GCHC.
> 

 At present, these exceptions are not triggered, so they are not defined.
 They are used by kvm. 

>> +#define LOONGARCH_CSR_ERA            0x6 /* ERA */
> 
> Not really helpful to name the acronym with the acronym.
> "Exception return address".
> 

OK, I will use a more detailed description for all the registers.

>> +#define LOONGARCH_CSR_TLBELO0        0x12 /* TLB EntryLo0 */
>> +FIELD(CSR_TLBELO0, V, 0, 1)
>> +FIELD(CSR_TLBELO0, D, 1, 1)
>> +FIELD(CSR_TLBELO0, PLV, 2, 2)
>> +FIELD(CSR_TLBELO0, MAT, 4, 2)
>> +FIELD(CSR_TLBELO0, G, 6, 1)
>> +FIELD(CSR_TLBELO0, PPN, 12, 36)
>> +FIELD(CSR_TLBELO0, NR, 61, 1)
>> +FIELD(CSR_TLBELO0, NX, 62, 1)
>> +FIELD(CSR_TLBELO0, RPLV, 63, 1)
>> +
>> +#define LOONGARCH_CSR_TLBELO1        0x13 /* 64 TLB EntryLo1 */
>> +FIELD(CSR_TLBELO1, V, 0, 1)
>> +FIELD(CSR_TLBELO1, D, 1, 1)
>> +FIELD(CSR_TLBELO1, PLV, 2, 2)
>> +FIELD(CSR_TLBELO1, MAT, 4, 2)
>> +FIELD(CSR_TLBELO1, G, 6, 1)
>> +FIELD(CSR_TLBELO1, PPN, 12, 36)
>> +FIELD(CSR_TLBELO1, NR, 61, 1)
>> +FIELD(CSR_TLBELO1, NX, 62, 1)
>> +FIELD(CSR_TLBELO1, RPLV, 63, 1)
> 
> Better to define the fields once, dropping the 0/1 from the name.
OK, I will modify.
> 
>> +#define LOONGARCH_CSR_PWCL           0x1c /* PWCl */
> 
> "Page walk controller, low addr"
> 
>> +#define LOONGARCH_CSR_PWCH           0x1d /* PWCh */
> 
> "Page walk controller, high addr"
> 
>> +#define LOONGARCH_CSR_STLBPS     0x1e /* 64 */
> 
> 64?  "STLB Page size".
> 
>> +#define LOONGARCH_CSR_RVACFG         0x1f
> 
> "Reduced virtual address config"
> 
>> +/* Save registers */
>> +#define LOONGARCH_CSR_SAVE0            0x30
>> +#define LOONGARCH_CSR_SAVE1            0x31
>> +#define LOONGARCH_CSR_SAVE2            0x32
>> +#define LOONGARCH_CSR_SAVE3            0x33
>> +#define LOONGARCH_CSR_SAVE4            0x34
>> +#define LOONGARCH_CSR_SAVE5            0x35
>> +#define LOONGARCH_CSR_SAVE6            0x36
>> +#define LOONGARCH_CSR_SAVE7            0x37
> 
> Might as well must define SAVE0, and comment that the count is in PRCFG1.SAVE_NUM.
> 
>> +#define  CSR_DMW_BASE_SH             48
> 
> What's this?  It looks like you should be using TARGET_VIRT_ADDR_SPACE_BITS anyway.
Yes, TARGET_VIRT_ADDR_SPACE_BITS is better.
> 
>> +#define dmwin_va2pa(va) \
>> +    (va & (((unsigned long)1 << CSR_DMW_BASE_SH) - 1))
> 
> Using unsigned long is wrong, breaking 32-bit hosts.
> You want
> 
>     ((va) & MAKE_64BIT_MASK(0, TARGET_VIRT_ADDR_SPACE_BITS))
> 
>> +/* Performance Counter registers */
>> +#define LOONGARCH_CSR_PERFCTRL0      0x200 /* 32 perf event 0 config */
>> +#define LOONGARCH_CSR_PERFCNTR0      0x201 /* 64 perf event 0 count value */
>> +#define LOONGARCH_CSR_PERFCTRL1      0x202 /* 32 perf event 1 config */
>> +#define LOONGARCH_CSR_PERFCNTR1      0x203 /* 64 perf event 1 count value */
>> +#define LOONGARCH_CSR_PERFCTRL2      0x204 /* 32 perf event 2 config */
>> +#define LOONGARCH_CSR_PERFCNTR2      0x205 /* 64 perf event 2 count value */
>> +#define LOONGARCH_CSR_PERFCTRL3      0x206 /* 32 perf event 3 config */
>> +#define LOONGARCH_CSR_PERFCNTR3      0x207 /* 64 perf event 3 count value */
> 
> Perhaps better to define
> 
> #define LOONGARCH_CSR_PERFCTRL(N)  (0x200 + 2 * N)
> 
> etc.
> 
>> +#define LOONGARCH_CSR_DB0ADDR        0x310 /* data breakpoint 0 address */
>> +#define LOONGARCH_CSR_DB0MASK        0x311 /* data breakpoint 0 mask */
>> +#define LOONGARCH_CSR_DB0CTL         0x312 /* data breakpoint 0 control */
>> +#define LOONGARCH_CSR_DB0ASID        0x313 /* data breakpoint 0 asid */
> 
> Likewise.
> 
>> +    uint64_t CSR_SAVE0;
>> +    uint64_t CSR_SAVE1;
>> +    uint64_t CSR_SAVE2;
>> +    uint64_t CSR_SAVE3;
>> +    uint64_t CSR_SAVE4;
>> +    uint64_t CSR_SAVE5;
>> +    uint64_t CSR_SAVE6;
>> +    uint64_t CSR_SAVE7;
> 
> Better as an array.
> 
>> +    uint64_t CSR_PERFCTRL0;
>> +    uint64_t CSR_PERFCNTR0;
>> +    uint64_t CSR_PERFCTRL1;
>> +    uint64_t CSR_PERFCNTR1;
>> +    uint64_t CSR_PERFCTRL2;
>> +    uint64_t CSR_PERFCNTR2;
>> +    uint64_t CSR_PERFCTRL3;
>> +    uint64_t CSR_PERFCNTR3;
> 
> Likewise.
> 
>> +    uint64_t CSR_DB0ADDR;
>> +    uint64_t CSR_DB0MASK;
>> +    uint64_t CSR_DB0CTL;
>> +    uint64_t CSR_DB0ASID;
>> +    uint64_t CSR_DB1ADDR;
>> +    uint64_t CSR_DB1MASK;
>> +    uint64_t CSR_DB1CTL;
>> +    uint64_t CSR_DB1ASID;
>> +    uint64_t CSR_DB2ADDR;
>> +    uint64_t CSR_DB2MASK;
>> +    uint64_t CSR_DB2CTL;
>> +    uint64_t CSR_DB2ASID;
>> +    uint64_t CSR_DB3ADDR;
>> +    uint64_t CSR_DB3MASK;
>> +    uint64_t CSR_DB3CTL;
>> +    uint64_t CSR_DB3ASID;
> 
> Likewise.
> 
>> +    uint64_t CSR_IB0ADDR;
>> +    uint64_t CSR_IB0MASK;
>> +    uint64_t CSR_IB0CTL;
>> +    uint64_t CSR_IB0ASID;
>> +    uint64_t CSR_IB1ADDR;
>> +    uint64_t CSR_IB1MASK;
>> +    uint64_t CSR_IB1CTL;
>> +    uint64_t CSR_IB1ASID;
>> +    uint64_t CSR_IB2ADDR;
>> +    uint64_t CSR_IB2MASK;
>> +    uint64_t CSR_IB2CTL;
>> +    uint64_t CSR_IB2ASID;
>> +    uint64_t CSR_IB3ADDR;
>> +    uint64_t CSR_IB3MASK;
>> +    uint64_t CSR_IB3CTL;
>> +    uint64_t CSR_IB3ASID;
>> +    uint64_t CSR_IB4ADDR;
>> +    uint64_t CSR_IB4MASK;
>> +    uint64_t CSR_IB4CTL;
>> +    uint64_t CSR_IB4ASID;
>> +    uint64_t CSR_IB5ADDR;
>> +    uint64_t CSR_IB5MASK;
>> +    uint64_t CSR_IB5CTL;
>> +    uint64_t CSR_IB5ASID;
>> +    uint64_t CSR_IB6ADDR;
>> +    uint64_t CSR_IB6MASK;
>> +    uint64_t CSR_IB6CTL;
>> +    uint64_t CSR_IB6ASID;
>> +    uint64_t CSR_IB7ADDR;
>> +    uint64_t CSR_IB7MASK;
>> +    uint64_t CSR_IB7CTL;
>> +    uint64_t CSR_IB7ASID;
> 
> Likewise.
> 
> 
  I will modify the define. Thanks
> r~



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

* Re: [RFC PATCH v2 04/30] target/loongarch: Define exceptions for LoongArch.
  2021-11-11 13:36   ` Richard Henderson
@ 2021-11-12  2:24     ` yangxiaojuan
  0 siblings, 0 replies; 62+ messages in thread
From: yangxiaojuan @ 2021-11-12  2:24 UTC (permalink / raw)
  To: Richard Henderson, qemu-devel; +Cc: Song Gao

Hi Richard,

On 11/11/2021 09:36 PM, Richard Henderson wrote:
> On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
>> +++ b/target/loongarch/cpu.h
>> @@ -369,8 +369,21 @@ enum {
>>       EXCP_BREAK,
>>       EXCP_INE,
>>       EXCP_FPE,
>> -
>> -    EXCP_LAST = EXCP_FPE,
>> +    EXCP_IPE,
>> +    EXCP_TLBL,
>> +    EXCP_TLBS,
>> +    EXCP_INST_NOTAVAIL,
>> +    EXCP_TLBM,
>> +    EXCP_TLBPE,
>> +    EXCP_TLBNX,
>> +    EXCP_TLBNR,
>> +    EXCP_EXT_INTERRUPT,
>> +    EXCP_DBP,
>> +    EXCP_IBE,
>> +    EXCP_DBE,
>> +    EXCP_DINT,
>> +
>> +    EXCP_LAST = EXCP_DINT,
> 
> Surely this is (essentially) a duplicate of EXCCODE_*.
> I think we should have only one copy of this.
> 

OK, I will use EXCCODE_* directly. Thanks

Xiaojuan

> 
> r~



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

* Re: [RFC PATCH v2 02/30] target/loongarch: Add CSR registers definition
  2021-11-12  2:14     ` yangxiaojuan
@ 2021-11-12  7:14       ` Richard Henderson
  0 siblings, 0 replies; 62+ messages in thread
From: Richard Henderson @ 2021-11-12  7:14 UTC (permalink / raw)
  To: yangxiaojuan, qemu-devel; +Cc: Song Gao


On 11/12/21 3:14 AM, yangxiaojuan wrote:
>>> +#define  EXCODE_IP                   64
>>
>> What's this?
>>
> 
> The manual 6.1.3 says the exception number of an interrupt is the exception number plus an offset of 64. This defines the offset, sorry, the name is bad, maybe change a name or just use 64 directly in the exception handle is better.
Perhaps

#define EXCCODE_EXTERNAL_INT   64   /* plus external interrupt number */

?

r~


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

* Re: [RFC PATCH v2 10/30] target/loongarch: Add other core instructions support
  2021-11-11  1:35 ` [RFC PATCH v2 10/30] target/loongarch: Add other core instructions support Xiaojuan Yang
@ 2021-11-14 10:19   ` Richard Henderson
  0 siblings, 0 replies; 62+ messages in thread
From: Richard Henderson @ 2021-11-14 10:19 UTC (permalink / raw)
  To: Xiaojuan Yang, qemu-devel; +Cc: Song Gao

On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
> +        if (qemu_loglevel_mask(CPU_LOG_INT)) {
> +            qemu_log("%s: TLBRERA 0x%lx\n", __func__, env->CSR_TLBRERA);
> +        }

This is qemu_log_mask, two instances.  With that,

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

> +void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
> +                  uint32_t mem_idx)

It it is a bit disappointing that the manual does not describe this function in detail, 
only a high-level description.


r~


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

* Re: [RFC PATCH v2 01/30] target/loongarch: Update README
  2021-11-11 11:50   ` chen huacai
@ 2021-11-15  3:34     ` yangxiaojuan
  0 siblings, 0 replies; 62+ messages in thread
From: yangxiaojuan @ 2021-11-15  3:34 UTC (permalink / raw)
  To: chen huacai; +Cc: qemu-level, Song Gao



On 11/11/2021 07:50 PM, chen huacai wrote:
> Hi, Xiaojuan,
> 
> On Thu, Nov 11, 2021 at 9:41 AM Xiaojuan Yang <yangxiaojuan@loongson.cn> wrote:
>>
>> Mainly introduce how to run the softmmu
>>
>> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
>> Signed-off-by: Song Gao <gaosong@loongson.cn>
>> ---
>>  target/loongarch/README | 20 ++++++++++++++++++++
>>  1 file changed, 20 insertions(+)
>>
>> diff --git a/target/loongarch/README b/target/loongarch/README
>> index 09f809cf80..6f64bde22f 100644
>> --- a/target/loongarch/README
>> +++ b/target/loongarch/README
>> @@ -71,6 +71,26 @@
>>        ./qemu-loongarch64  /opt/clfs/usr/bin/pwd
>>        ...
>>
>> +- Softmmu emulation
>> +
>> +  Add support softmmu emulation support in the following series patches.
>> +  Mainly emulate a virt 3A5000 board that is not exactly the same as the host.
>> +  Kernel code is on the github and the uefi code will be opened in the near future.
>> +  All required binaries can get from github for test.
>> +
>> +  1.Download kernel and the cross-tools.(vmlinux)
>> +
>> +      wget https://github.com/loongson/linux
> This is a git repo URL, I think we cannot use wget to download.

oh, sorry, I will modify.

> 
>> +      wget https://github.com/loongson/build-tools/releases/latest/download/loongarch64-clfs-20210831-cross-tools.tar.xz
>> +
>> +  2.Download the clfs-system and made a ramdisk with busybox.(ramdisk)
>> +
>> +  3.Run with command,eg:
>> +
>> +   ./build/qemu-system-loongarch64 -m 4G -smp 16 --cpu Loongson-3A5000 --machine loongson7a -kernel ./vmlinux -initrd ./ramdisk  -append "root=/dev/ram console=ttyS0,115200 rdinit=/sbin/init loglevel=8" -monitor tcp::4000,server,nowait -nographic
> It isn't recommended to use "loongson7a" as the machine name. In my
> opinion, we will have two types of machines run in qemu (One is an
> emulated LS7A and the other is pure virtual). I think we can call them
> "loongson3-ls7a" and "loongson3-virt".
> 
> Huacai

Thank you for your advice,  I didn't think comprehensively. I will modify in the next versison.

Thanks,
Xiaojuan
 
>> +
>> +The vmlinux, ramdisk and uefi binary loongarch_bios.bin can get from :
>> +    git clone https://github.com/yangxiaojuan-loongson/qemu-binary
>>
>>  - Note.
>>    We can get the latest LoongArch documents or LoongArch tools at https://github.com/loongson/
>> --
>> 2.27.0
>>
>>
> 
> 



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

* Re: [RFC PATCH v2 07/30] target/loongarch: Add MMU support for LoongArch CPU.
  2021-11-11 15:53   ` Richard Henderson
@ 2021-11-17  6:37     ` yangxiaojuan
  2021-11-17  6:59       ` Richard Henderson
  0 siblings, 1 reply; 62+ messages in thread
From: yangxiaojuan @ 2021-11-17  6:37 UTC (permalink / raw)
  To: Richard Henderson, qemu-devel; +Cc: Song Gao

Hi, Richard:

On 11/11/2021 11:53 PM, Richard Henderson wrote:
> On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
>> This patch introduces basic TLB interfaces.
>>
>> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
>> Signed-off-by: Song Gao <gaosong@loongson.cn>
>> ---
>>   target/loongarch/cpu-param.h  |   3 +
>>   target/loongarch/cpu.c        |  36 ++++
>>   target/loongarch/cpu.h        |  57 ++++++
>>   target/loongarch/internals.h  |   7 +
>>   target/loongarch/machine.c    |  56 ++++++
>>   target/loongarch/meson.build  |   1 +
>>   target/loongarch/tlb_helper.c | 339 ++++++++++++++++++++++++++++++++++
>>   7 files changed, 499 insertions(+)
>>   create mode 100644 target/loongarch/tlb_helper.c
>>
>> diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h
>> index 9a769b67e0..5a2147fb90 100644
>> --- a/target/loongarch/cpu-param.h
>> +++ b/target/loongarch/cpu-param.h
>> @@ -12,6 +12,9 @@
>>   #define TARGET_PHYS_ADDR_SPACE_BITS 48
>>   #define TARGET_VIRT_ADDR_SPACE_BITS 48
>>   +#define TARGET_PHYS_MASK ((1UL << TARGET_PHYS_ADDR_SPACE_BITS) - 1)
>> +#define TARGET_VIRT_MASK ((1UL << TARGET_VIRT_ADDR_SPACE_BITS) - 1)
> 
> As before, unsigned long is wrong; use MAKE_64BIT_MASK.
> 
> These do not belong in cpu-param.h anyway; probably only tlb_helper.c needs them.
> 
>> +#ifndef CONFIG_USER_ONLY
>> +    qemu_fprintf(f, "EUEN            0x%lx\n", env->CSR_EUEN);
>> +    qemu_fprintf(f, "ESTAT           0x%lx\n", env->CSR_ESTAT);
>> +    qemu_fprintf(f, "ERA             0x%lx\n", env->CSR_ERA);
>> +    qemu_fprintf(f, "CRMD            0x%lx\n", env->CSR_CRMD);
>> +    qemu_fprintf(f, "PRMD            0x%lx\n", env->CSR_PRMD);
>> +    qemu_fprintf(f, "BadVAddr        0x%lx\n", env->CSR_BADV);
>> +    qemu_fprintf(f, "TLB refill ERA  0x%lx\n", env->CSR_TLBRERA);
>> +    qemu_fprintf(f, "TLB refill BadV 0x%lx\n", env->CSR_TLBRBADV);
>> +    qemu_fprintf(f, "EENTRY          0x%lx\n", env->CSR_EENTRY);
>> +    qemu_fprintf(f, "BadInstr        0x%lx\n", env->CSR_BADI);
>> +    qemu_fprintf(f, "PRCFG1    0x%lx\nPRCFG2     0x%lx\nPRCFG3     0x%lx\n",
>> +                 env->CSR_PRCFG1, env->CSR_PRCFG3, env->CSR_PRCFG3);
> 
> %lx is wrong; use PRIx64.
> 
>> +#define LOONGARCH_TLB_MAX      2112 /* 2048 STLB + 64 MTLB */
> 
> Better to write (2048 + 64).
> 
>> +FIELD(TLB_MISC, E, 0, 1)
>> +FIELD(TLB_MISC, ASID, 1, 10)
>> +FIELD(TLB_MISC, G, 11, 1)
>> +FIELD(TLB_MISC, PS, 12, 6)
>> +FIELD(TLB_MISC, VPPN, 18, 35)
>> +
>> +/* Corresponding to CSR_TLBELO0/1 */
>> +FIELD(ENTRY0, V, 0, 1)
>> +FIELD(ENTRY0, D, 1, 1)
>> +FIELD(ENTRY0, NR, 2, 1)
>> +FIELD(ENTRY0, NX, 3, 1)
>> +FIELD(ENTRY0, MAT, 4, 2)
>> +FIELD(ENTRY0, PLV, 6, 2)
>> +FIELD(ENTRY0, RPLV, 8, 1)
>> +FIELD(ENTRY0, PPN, 9, 36)
>> +
>> +FIELD(ENTRY1, V, 0, 1)
>> +FIELD(ENTRY1, D, 1, 1)
>> +FIELD(ENTRY1, NR, 2, 1)
>> +FIELD(ENTRY1, NX, 3, 1)
>> +FIELD(ENTRY1, MAT, 4, 2)
>> +FIELD(ENTRY1, PLV, 6, 2)
>> +FIELD(ENTRY1, RPLV, 8, 1)
>> +FIELD(ENTRY1, PPN, 9, 36)
> 
> Why are you duplicating the CSR_TLBELO* fields?
> 
>> +const VMStateInfo vmstate_info_tlb = {
>> +    .name = "tlb_entry",
>> +    .get  = get_tlb,
>> +    .put  = put_tlb,
>> +};
> 
> Better to use .fields.
> 
>> +#define VMSTATE_TLB_ARRAY_V(_f, _s, _n, _v)                     \
>> +    VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_tlb, loongarch_tlb)
>> +
>> +#define VMSTATE_TLB_ARRAY(_f, _s, _n)                           \
>> +    VMSTATE_TLB_ARRAY_V(_f, _s, _n, 0)
> 
> Don't need these.
> 
>> +
>> +const VMStateDescription vmstate_tlb = {
>> +    .name = "cpu/tlb",
>> +    .version_id = 0,
>> +    .minimum_version_id = 0,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_TLB_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX),
> 
> VMSTATE_STRUCT_ARRAY.
> 
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>>     /* LoongArch CPU state */
>>   @@ -22,6 +70,10 @@ const VMStateDescription vmstate_loongarch_cpu = {
>>           VMSTATE_UINT64_ARRAY(env.fpr, LoongArchCPU, 32),
>>           VMSTATE_UINT32(env.fcsr0, LoongArchCPU),
>>   +        /* TLB */
>> +        VMSTATE_UINT32(env.stlb_size, LoongArchCPU),
>> +        VMSTATE_UINT32(env.mtlb_size, LoongArchCPU),
> 
> Might as well keep these in vmstate_tlb.

All of the vmstate has been modified.

>> +/* TLB address map */
>> +static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical,
>> +                                   int *prot, target_ulong address,
>> +                                   int access_type, loongarch_tlb *tlb)
>> +{
>> +    uint64_t plv = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV);
> 
> Incorrect.  PLV associated with mmu_idx, so you need to use that.
> 
>> +    uint8_t tlb_ps, n, tlb_v0, tlb_v1, tlb_d0, tlb_d1;
>> +    uint8_t tlb_nx0, tlb_nx1, tlb_nr0, tlb_nr1;
>> +    uint64_t tlb_ppn0, tlb_ppn1;
>> +    uint8_t tlb_rplv0, tlb_rplv1, tlb_plv0, tlb_plv1;
>> +
>> +    tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
>> +    n = (address >> tlb_ps) & 0x1;/* Odd or even */
> 
> Surely you need to pass in tlb_ps, since it's not present in the STLB entries.
> 
>> +
>> +    tlb_v0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, V);
>> +    tlb_d0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, D);
>> +    tlb_plv0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, PLV);
>> +    tlb_ppn0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, PPN);
>> +    tlb_nx0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, NX);
>> +    tlb_nr0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, NR);
>> +    tlb_rplv0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, RPLV);
>> +
>> +    tlb_v1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, V);
>> +    tlb_d1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, D);
>> +    tlb_plv1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, PLV);
>> +    tlb_ppn1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, PPN);
>> +    tlb_nx1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, NX);
>> +    tlb_nr1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, NR);
>> +    tlb_rplv1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, RPLV);
> 
> Better to check N first.
> 
>     entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0;
>     v = FIELD_EX64(entry, TLBENTRY, V);
> 
> etc.

oh, yes , it is Much simpler when check N first. Your advice is always so appropriate. Thanks

> 
>> +        *physical = (tlb_ppn1 << 12) | (address & ((1 << tlb_ps) - 1));
> 
> TARGET_PAGE_SIZE.

Maybe TARGET_PAGE_SIZE is not fit for a huge page. MAKE_64BIT_MASK(0, tlb_ps) is ok?

> 
>> +/* LoongArch 3A5000 -style MMU emulation */
> 
> There's no mention of 3A5000 in "LoongArch Reference Manual", which defines this.  I think you've copied this from MIPS, which talks about r4k here.
> 
>> +static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
>> +                                 int *prot,
>> +                                 target_ulong address,
>> +                                 MMUAccessType access_type)
>> +{
>> +    loongarch_tlb *tlb;
>> +    uint16_t csr_asid, tlb_asid, stlb_idx;
>> +    uint8_t tlb_e, stlb_ps, tlb_ps, tlb_g;
>> +    int i, stlb_size, mtlb_size;
>> +    uint64_t vpn, tlb_vppn;   /* Address to map */
>> +
>> +    stlb_size = env->stlb_size;
>> +    mtlb_size = env->mtlb_size;
>> +    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
>> +
>> +    /* Search MTLB */
>> +    for (i = stlb_size; i < stlb_size + mtlb_size; ++i) {
> 
> Bit of a shame to have a linear search.  I guess it's ok for a start, but you'll find that this function is critical to the emulation speed of qemu, so you should think about other ways to organize the data.

The stlb search by the set, the mtlb uses the linear search, I have no other idea, do you have some advice? 

> 
>> +        tlb = &env->tlb[i];
>> +        tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
>> +        tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
>> +
>> +        vpn = (address & TARGET_VIRT_MASK) >> (tlb_ps + 1);
> 
> Why +1?

One tlb entry holds a adjacent odd/even pair, the vpn is the content of the virtual page number divided by 2.
So the compare vpn is bit[47:15] for 16KB page. while the vppn field in tlb entry contains bit[47:13], so need adjust.

> 
>> +        tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
>> +        tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
>> +        tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
>> +
>> +        if ((tlb_g == 1 || tlb_asid == csr_asid) &&
>> +            (vpn == (tlb_vppn >> (tlb_ps + 1 - 13))) && tlb_e) {
> 
> Surely extract and test the enable bit E before anything else, just to speed up the lookup.
> 
> The -13 is a bit of a magic number.  Surely TARGET_PAGE_BITS + 1.

OK, I will use the R_TLB_MISC_VPPN_SHIFT replace the magic 13.

> 
>> +static int get_physical_address(CPULoongArchState *env, hwaddr *physical,
>> +                                int *prot, target_ulong real_address,
>> +                                MMUAccessType access_type, int mmu_idx)
>> +{
>> +    int user_mode = mmu_idx == LOONGARCH_HFLAG_UM;
>> +    int kernel_mode = !user_mode;
> 
> Incorrect.  PLV == mmu_idx, as defined by cpu_mmu_index.
> 
>> +    unsigned plv, base_c, base_v, tmp;
>> +    uint64_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG);
>> +
>> +    /* Effective address */
>> +    target_ulong address = real_address;
>> +
>> +    /* Check PG */
>> +    if (!pg) {
>> +        /* DA mode */
>> +        *physical = address & TARGET_PHYS_MASK;
>> +        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
>> +        return TLBRET_MATCH;
>> +    }
> 
> You need to define a fifth mmu_index for paging disabled, because the software tlb handler runs in this mode.  If you don't define that, you'll have to flush the softmmu tlb every time you have a tlb miss.
> 
>> +    plv = kernel_mode | (user_mode << 3);
> 
> plv_mask = 1 << plv;
> 
>> +    base_v = address >> CSR_DMW_BASE_SH;
>> +    /* Check direct map window 0 */
>> +    base_c = env->CSR_DMWIN0 >> CSR_DMW_BASE_SH;
>> +    if ((plv & env->CSR_DMWIN0) && (base_c == base_v)) {
>> +        *physical = dmwin_va2pa(address);
>> +        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
>> +        return TLBRET_MATCH;
>> +    }
>> +    /* Check direct map window 1 */
>> +    base_c = env->CSR_DMWIN1 >> CSR_DMW_BASE_SH;
>> +    if ((plv & env->CSR_DMWIN1) && (base_c == base_v)) {
>> +        *physical = dmwin_va2pa(address);
>> +        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
>> +        return TLBRET_MATCH;
>> +    }
> 
> There are more than 2 of these, which is why I suggested putting them in an array, so that you can loop.  Maybe pull this out to a separate function, like the tlb lookup?
> 
>> +    /* Check valid extension */
>> +    tmp = address >> (TARGET_VIRT_ADDR_SPACE_BITS - 1);
>> +    if (!(tmp == 0 || tmp == 0x1ffff)) {
> 
> Better to cast to int64_t first, so that this becomes 0 or -1, without knowing that 64 - TARGET_VIRT_ADDR_SPACE_BITS - 1 -> 0x1ffff.
> 
>> +void loongarch_mmu_init(CPULoongArchState *env)
>> +{
>> +    /* Number of MTLB */
>> +    env->mtlb_size = 64;
>> +
>> +    /* Number of STLB */
>> +    env->stlb_size = 2048;
>> +
>> +    /* For 16KB, ps = 14, compare the bit [47:15] */
>> +    for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
>> +        env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, TLB_MISC, E, 0);
>> +    }
> 
> Just memset the whole thing?

yes, the tlb is empty at the start.

> 
>> +bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>> +                            MMUAccessType access_type, int mmu_idx,
>> +                            bool probe, uintptr_t retaddr)
>> +{
>> +    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
>> +    CPULoongArchState *env = &cpu->env;
>> +    hwaddr physical;
>> +    int prot;
>> +    int ret = TLBRET_BADADDR;
>> +
>> +    /* Data access */
>> +    /* XXX: put correct access by using cpu_restore_state() correctly */
>> +    ret = get_physical_address(env, &physical, &prot, address,
>> +                               access_type, mmu_idx);
>> +    switch (ret) {
>> +    case TLBRET_MATCH:
>> +        qemu_log_mask(CPU_LOG_MMU,
>> +                      "%s address=%" VADDR_PRIx " physical " TARGET_FMT_plx
>> +                      " prot %d\n", __func__, address, physical, prot);
>> +        break;
>> +    default:
>> +        qemu_log_mask(CPU_LOG_MMU,
>> +                      "%s address=%" VADDR_PRIx " ret %d\n", __func__, address,
>> +                      ret);
>> +        break;
>> +    }
>> +    if (ret == TLBRET_MATCH) {
>> +        tlb_set_page(cs, address & TARGET_PAGE_MASK,
>> +                     physical & TARGET_PAGE_MASK, prot,
>> +                     mmu_idx, TARGET_PAGE_SIZE);
>> +        return true;
> 
> Merge the above switch into this?
> 
>> +    }
>> +    if (probe) {
>> +        return false;
>> +    } else {
>> +        raise_mmu_exception(env, address, access_type, ret);
>> +        do_raise_exception(env, cs->exception_index, retaddr);
>> +    }
>> +}
>>
> 
> 
> r~



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

* Re: [RFC PATCH v2 07/30] target/loongarch: Add MMU support for LoongArch CPU.
  2021-11-17  6:37     ` yangxiaojuan
@ 2021-11-17  6:59       ` Richard Henderson
  0 siblings, 0 replies; 62+ messages in thread
From: Richard Henderson @ 2021-11-17  6:59 UTC (permalink / raw)
  To: yangxiaojuan, qemu-devel; +Cc: Song Gao

On 11/17/21 7:37 AM, yangxiaojuan wrote:
>>> +        *physical = (tlb_ppn1 << 12) | (address & ((1 << tlb_ps) - 1));
>>
>> TARGET_PAGE_SIZE.
> 
> Maybe TARGET_PAGE_SIZE is not fit for a huge page. MAKE_64BIT_MASK(0, tlb_ps) is ok?

I meant the first << 12.  But, yes, MAKE_64BIT_MASK is a good improvement.

>> Bit of a shame to have a linear search.  I guess it's ok for a start, but you'll find that this function is critical to the emulation speed of qemu, so you should think about other ways to organize the data.
> 
> The stlb search by the set, the mtlb uses the linear search, I have no other idea, do you have some advice?

Well, stlb are all the same page size, and duplicate matches are undefined.

A hash table of the valid entries might be workable, with the virtual page and asid taken 
into account.  I'd imagine that would get you down from 2048 comparisons to just a few.

The mtlb entries may all have different page size, so I don't immediately know how to 
search them more efficiently.  But there are only 64 of them.

It might be worth having a couple of bitsets that summarize the valid entries.  This could 
be used to search less than 64 mtlb entries.  It could also be used to influence the 
"random" selection of a new tlb entry: always select an empty tlb entry first, then evict 
a random entry.


r~


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

* Re: [RFC PATCH v2 09/30] target/loongarch: Add TLB instruction support
  2021-11-11 18:14   ` Richard Henderson
@ 2021-11-17  7:29     ` yangxiaojuan
  2021-11-17  8:22       ` Richard Henderson
  0 siblings, 1 reply; 62+ messages in thread
From: yangxiaojuan @ 2021-11-17  7:29 UTC (permalink / raw)
  To: Richard Henderson, qemu-devel; +Cc: Song Gao

Hi, Richard:

On 11/12/2021 02:14 AM, Richard Henderson wrote:
> On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
>> +static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
>> +{
>> +    gen_helper_check_plv(cpu_env);
>> +    gen_helper_tlbwr(cpu_env);
>> +    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
>> +    ctx->base.is_jmp = DISAS_EXIT;
>> +    return true;
>> +}
> 
> I think you can skip the EXIT if paging is disabled, which it usually will be in the software tlb handler.  You'd be able to tell with the mmu_idx being the one you use for paging disabled.

The paging disabled only enabled at the bios startup, we can get the phys address directly, tlbwr instruction will not be used when paging enabled.

> 
>> +static void loongarch_invalidate_tlb_entry(CPULoongArchState *env,
>> +                                           loongarch_tlb *tlb)
>> +{
>> +    CPUState *cs = env_cpu(env);
>> +    target_ulong addr, end, mask;
>> +    int tlb_v0, tlb_v1;
>> +    uint64_t tlb_vppn;
>> +    uint8_t tlb_ps;
>> +
>> +    tlb_v0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, V);
>> +    tlb_v1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, V);
>> +    tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
>> +    tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
>> +    mask = (1 << (1 + tlb_ps)) - 1;
> 
> MAKE_64BIT_MASK.
> 
>> +
>> +    if (tlb_v0) {
>> +        addr = tlb_vppn & ~mask;    /* xxx...xxx[0]000..0000 */
>> +        end = addr | (mask >> 1);   /* xxx...xxx[0]111..1111 */
>> +        while (addr < end) {
>> +            tlb_flush_page(cs, addr);
>> +            addr += TARGET_PAGE_SIZE;
> 
> tlb_flush_range_by_mmuidx.
> 
>> +    tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn);
>> +    tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1);
>> +    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
>> +    tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid);
>> +
>> +    csr_g = FIELD_EX64(env->CSR_TLBELO0, CSR_TLBELO0, G) &
>> +             FIELD_EX64(env->CSR_TLBELO1, CSR_TLBELO1, G);
>> +    tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, G, csr_g);
>> +
>> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, V,
>> +                                 FIELD_EX64(lo0, CSR_TLBELO0, V));/* [0] */
>> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, D,
>> +                                 FIELD_EX64(lo0, CSR_TLBELO0, D));/* [1] */
>> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, PLV,
>> +                                 FIELD_EX64(lo0, CSR_TLBELO0, PLV));/* [3:2] */
>> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, MAT,
>> +                                 FIELD_EX64(lo0, CSR_TLBELO0, MAT));/* [5:4] */
>> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, PPN,
>> +                                 FIELD_EX64(lo0, CSR_TLBELO0, PPN));/* [47:12] */
>> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, NR,
>> +                                 FIELD_EX64(lo0, CSR_TLBELO0, NR));/* [61] */
>> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, NX,
>> +                                 FIELD_EX64(lo0, CSR_TLBELO0, NX));/* [62] */
>> +    tlb->tlb_entry0 = FIELD_DP64(tlb->tlb_entry0, ENTRY0, RPLV,
>> +                                 FIELD_EX64(lo0, CSR_TLBELO0, RPLV));/* [63] */
>> +
>> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, V,
>> +                                 FIELD_EX64(lo1, CSR_TLBELO1, V));/* [0] */
>> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, D,
>> +                                 FIELD_EX64(lo1, CSR_TLBELO1, D));/* [1] */
>> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, PLV,
>> +                                 FIELD_EX64(lo1, CSR_TLBELO1, PLV));/* [3:2] */
>> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, MAT,
>> +                                 FIELD_EX64(lo1, CSR_TLBELO1, MAT));/* [5:4] */
>> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, PPN,
>> +                                 FIELD_EX64(lo1, CSR_TLBELO1, PPN));/* [47:12] */
>> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, NR,
>> +                                 FIELD_EX64(lo1, CSR_TLBELO1, NR));/* [61] */
>> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, NX,
>> +                                 FIELD_EX64(lo1, CSR_TLBELO1, NX));/* [62] */
>> +    tlb->tlb_entry1 = FIELD_DP64(tlb->tlb_entry1, ENTRY1, RPLV,
>> +                                 FIELD_EX64(lo1, CSR_TLBELO1, RPLV));/* [63] */
> 
> The point of making the two values have the same field layout is so that you can just assign the whole value across, not extract and re-deposit each field.

Yes, it is much simpler when use the same field layout.

> 
>> +void helper_tlbsrch(CPULoongArchState *env)
>> +{
>> +    loongarch_tlb *tlb;
>> +    uint64_t vpn, tlb_vppn;
>> +    uint16_t csr_asid, tlb_asid, tlb_ps, tlb_e, tlb_g;
>> +
>> +    int stlb_size = env->stlb_size;
>> +    int mtlb_size = env->mtlb_size;
>> +    int i;
>> +    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
>> +
>> +    /* Search MTLB + STLB */
>> +    for (i = 0; i < stlb_size + mtlb_size; ++i) {
>> +        tlb = &env->tlb[i];
>> +        vpn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI, VPPN);
>> +        tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
>> +        tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
>> +        tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
>> +        tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
>> +        tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
>> +
>> +        if ((tlb_g == 1 || tlb_asid == csr_asid) &&
>> +            (vpn >> (tlb_ps + 1 - 13) == tlb_vppn >> (tlb_ps + 1 - 13)) && tlb_e) {
>> +            env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
>> +                                         INDEX, (i & 0xfff));
>> +            env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
>> +                                         PS, (tlb_ps & 0x3f));
>> +            return;
>> +        }
>> +    }
>> +
>> +    env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
>> +}
> 
> Surely this should share code with loongarch_map_address.
> 
>> +
>> +void helper_tlbrd(CPULoongArchState *env)
>> +{
>> +    loongarch_tlb *tlb;
>> +    int idx;
>> +    uint16_t csr_asid, tlb_asid;
>> +    uint8_t tlb_ps, tlb_e, tlb_v0, tlb_v1, tlb_d0, tlb_d1;
>> +    uint8_t tlb_plv0, tlb_plv1, tlb_mat0, tlb_mat1, tlb_g;
>> +    uint64_t tlb_ppn0, tlb_ppn1;
>> +    uint8_t tlb_nr0, tlb_nr1, tlb_nx0, tlb_nx1, tlb_rplv0, tlb_rplv1;
>> +
>> +    idx = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
>> +    tlb = &env->tlb[idx];
>> +
>> +    csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
>> +    tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
>> +    tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
>> +    tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
>> +    tlb_g = FIELD_EX64(tlb->tlb_misc, TLB_MISC, G);
>> +
>> +    tlb_v0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, V);
>> +    tlb_d0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, D);
>> +    tlb_plv0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, PLV);
>> +    tlb_mat0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, MAT);
>> +    tlb_ppn0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, PPN);
>> +    tlb_nr0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, NR);
>> +    tlb_nx0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, NX);
>> +    tlb_rplv0 = FIELD_EX64(tlb->tlb_entry0, ENTRY0, RPLV);
>> +
>> +    tlb_v1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, V);
>> +    tlb_d1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, D);
>> +    tlb_plv1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, PLV);
>> +    tlb_mat1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, MAT);
>> +    tlb_ppn1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, PPN);
>> +    tlb_nr1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, NR);
>> +    tlb_nx1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, NX);
>> +    tlb_rplv1 = FIELD_EX64(tlb->tlb_entry1, ENTRY1, RPLV);
>> +
>> +    if (csr_asid != tlb_asid) {
>> +        cpu_loongarch_tlb_flush(env);
> 
> Why?  Surely the index selected should not have matched on the previous search?

yes, I will modify.

> 
>> +    } else {
>> +        /* Valid TLB entry */
>> +        env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
>> +                                     INDEX, (idx & 0xfff));
>> +        env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
>> +                                     PS, (tlb_ps & 0x3f));
>> +
>> +        env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) << 13;
>> +
>> +        env->CSR_TLBELO0 = FIELD_DP64(0, CSR_TLBELO0, V, tlb_v0);
>> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, D, tlb_d0);
>> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, PLV, tlb_plv0);
>> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, MAT, tlb_mat0);
>> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, G, tlb_g);
>> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, PPN, tlb_ppn0);
>> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, NR, tlb_nr0);
>> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, NX, tlb_nx0);
>> +        env->CSR_TLBELO0 = FIELD_DP64(env->CSR_TLBELO0, CSR_TLBELO0, RPLV, tlb_rplv0);
>> +
>> +        env->CSR_TLBELO1 = FIELD_DP64(0, CSR_TLBELO1, V, tlb_v1);
>> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, D, tlb_d1);
>> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, PLV, tlb_plv1);
>> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, MAT, tlb_mat1);
>> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, G, tlb_g);
>> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, PPN, tlb_ppn1);
>> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, NR, tlb_nr1);
>> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, NX, tlb_nx1);
>> +        env->CSR_TLBELO1 = FIELD_DP64(env->CSR_TLBELO1, CSR_TLBELO1, RPLV, 
> 
> Again, these should easily copy across.
> 
>> +    env->CSR_ASID  = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, tlb_asid);
> 
> The documentation for TLBRD does not mention modifications to the current ASID.
> 
>> +    default:
>> +        do_raise_exception(env, EXCP_INE, GETPC());
> 
> You can detect this during translation, and dispatch to the appropriate invtlb sub-function.
> 
oh, sorry, I don't quiet understand this. detect during the translation sees more complicated.

Xiaojuan,
Thanks
> 
> r~



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

* Re: [RFC PATCH v2 09/30] target/loongarch: Add TLB instruction support
  2021-11-17  7:29     ` yangxiaojuan
@ 2021-11-17  8:22       ` Richard Henderson
  2021-11-17  8:53         ` yangxiaojuan
  0 siblings, 1 reply; 62+ messages in thread
From: Richard Henderson @ 2021-11-17  8:22 UTC (permalink / raw)
  To: yangxiaojuan, qemu-devel; +Cc: Song Gao

On 11/17/21 8:29 AM, yangxiaojuan wrote:
> On 11/12/2021 02:14 AM, Richard Henderson wrote:
>> On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
>>> +static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
>>> +{
>>> +    gen_helper_check_plv(cpu_env);
>>> +    gen_helper_tlbwr(cpu_env);
>>> +    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
>>> +    ctx->base.is_jmp = DISAS_EXIT;
>>> +    return true;
>>> +}
>>
>> I think you can skip the EXIT if paging is disabled, which it usually will be in the software tlb handler.  You'd be able to tell with the mmu_idx being the one you use for paging disabled.
> 
> The paging disabled only enabled at the bios startup, we can get the phys address directly, tlbwr instruction will not be used when paging enabled.

Paging is also disabled during TLBRENTRY exception (see section 6.2.4 Hardware Exception 
Handling of TLB Refil Exception).  It is this routine that will usually use tlbwr most 
often (although the kernel at PRV 0 is not prevented from doing so).

>>> +    default:
>>> +        do_raise_exception(env, EXCP_INE, GETPC());
>>
>> You can detect this during translation, and dispatch to the appropriate invtlb sub-function.
>>
> oh, sorry, I don't quiet understand this. detect during the translation sees more complicated.

It is not more complex at all.  Less complex, I would say.

static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
{
     TCGv rj = gpr_src(ctx, a->rj, EXT_NONE);
     TCGv rk = gpr_src(ctx, a->rk, EXT_NONE);

     if (check_plv(ctx)) {
         return false;
     }

     switch (a->invop) {
     case 0:
     case 1:
         gen_helper_invtlb_all(cpu_env);
         break;
     case 2:
         gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(1));
         break;
     case 3:
         gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(0));
         break;
     case 4:
         gen_helper_invtlb_all_asid(cpu_env, rj);
         break;
     case 5:
         gen_helper_invtlb_page_asid(cpu_env, rj, rk);
         break;
     case 6:
         gen_helper_invtlb_page_asid_or_g(cpu_env, rj, rk);
         break;
     default:
         return false;
     }
     ctx->base.is_jmp = DISAS_STOP;
     return true;
}


r~


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

* Re: [RFC PATCH v2 08/30] target/loongarch: Add LoongArch CSR/IOCSR instruction
  2021-11-11 17:43   ` Richard Henderson
@ 2021-11-17  8:48     ` yangxiaojuan
  0 siblings, 0 replies; 62+ messages in thread
From: yangxiaojuan @ 2021-11-17  8:48 UTC (permalink / raw)
  To: Richard Henderson, qemu-devel; +Cc: Song Gao

Hi, Richard:

On 11/12/2021 01:43 AM, Richard Henderson wrote:
> On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
>> This includes:
>> - CSRRD
>> - CSRWR
>> - CSRXCHG
>> - IOCSR{RD/WR}.{B/H/W/D}
> 
> I think IOCSR should be in a separate patch.
> It's completely unrelated to the other CSRs.
> 
>> +target_ulong helper_csr_rdq(CPULoongArchState *env, uint64_t csr)
>> +{
>> +    int64_t v;
>> +
>> +    switch (csr) {
>> +    case LOONGARCH_CSR_PGD:
>> +        if (env->CSR_TLBRERA & 0x1) {
>> +            v = env->CSR_TLBRBADV;
>> +        } else {
>> +            v = env->CSR_BADV;
>> +        }
>> +
>> +        if ((v >> 63) & 0x1) {
>> +            v = env->CSR_PGDH;
>> +        } else {
>> +            v = env->CSR_PGDL;
>> +        }
>> +        v = v & TARGET_PHYS_MASK;
> 
> This csr is defined to be GRLEN bits; I this mask looks wrong.
> 
>> +    default:
>> +        assert(0);
> 
> g_assert_not_reached.
> 
>> +    switch (csr) {
>> +    case LOONGARCH_CSR_ASID:
>> +        old_v = env->CSR_ASID;
>> +        env->CSR_ASID = val;
> 
> Mask the write to the field; you don't want to corrupt ASIDBITS, or the other read-only bits.

Ok, all above have been modified.

> 
>> +    case LOONGARCH_CSR_TCFG:
>> +        old_v = env->CSR_TCFG;
>> +        cpu_loongarch_store_stable_timer_config(env, val);
>> +        break;
>> +    case LOONGARCH_CSR_TINTCLR:
>> +        old_v = 0;
>> +        qemu_irq_lower(env->irq[IRQ_TIMER]);
> 
> The interrupt is not documented to clear on any write; only writes of 1 to bit 0.

I think the manual has mentioned at 7.6.5 which says when 1 is written to this bit, the clock interrupt 
flag is cleared.

> 
>> +    default:
>> +        assert(0);
> 
> g_assert_not_reached.
> 
>> +    }
>> +
>> +    return old_v;
>> +}
>> +
>> +target_ulong helper_csr_xchgq(CPULoongArchState *env, target_ulong val,
>> +                              target_ulong mask, uint64_t csr)
>> +{
>> +    target_ulong tmp;
>> +    target_ulong v = val & mask;
> 
> I think it would be less confusing to name the input parameter new_val, and the local temporary old_val.
> 
>> +#define CASE_CSR_XCHGQ(csr)                                 \
>> +    case LOONGARCH_CSR_ ## csr:                             \
>> +    {                                                       \
>> +        val = env->CSR_ ## csr;                             \
>> +        env->CSR_ ## csr = (env->CSR_ ## csr) & (~mask);    \
>> +        env->CSR_ ## csr = (env->CSR_ ## csr) | v;          \
> 
>   old_val = env->CSR_##csr;
>   env->CSR_##csr = (old_val & ~mask) | (new_val & mask);
> 
> 
>> +    switch (csr) {
>> +    CASE_CSR_XCHGQ(CRMD)
> 
> I wonder if all of this would be better with a table of offsets, which could be shared with the translator.
> 
> #define CSR_OFF(X)  [LOONGARCH_CSR_##X] = offsetof(CPUArchState, CSR_##X)
> 
> static const int csr_offsets[] = {
>     CSR_OFF(CRMD),
>     ...
> };
> 
> int cpu_csr_offset(unsigned csr_num)
> {
>     if (csr_num < ARRAY_SIZE(csr_offsets)) {
>         return csr_offsets[csr_num];
>     }
>     return 0;
> }
> 
> Which reduces this function to
> 
>     unsigned csr_offset = cpu_csr_offset(csr_num);
>     if (csr_offset == 0) {
>         /* CSR is undefined: read as 0, write ignored. */
>         return 0;
>     }
> 
>     uint64_t *csr = (void *)env + csr_offset;
>     uint64_t old_val = *csr;
> 
>     new_val = (new_val & mask) | (old_val & ~mask);
> 
>     *csr = (old_val & ~mask) | (new_val & mask);
> 
>     if (csr_num == LOONGARCH_CSR_TCFG) {
>         cpu_loongarch_store_stable_timer_config(env, new_val);
>     } else {
>         *csr = new_val;
>     }
>     return old_val;
> 
>> +uint64_t helper_iocsr_read(CPULoongArchState *env, target_ulong r_addr,
>> +                           uint32_t size)
>> +{
>> +    LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
>> +    int cpuid = env_cpu(env)->cpu_index;
>> +
>> +    if (((r_addr & 0xff00) == 0x1000) || ((r_addr & 0xff00) == 0x1800)) {
>> +        r_addr = r_addr + ((target_ulong)(cpuid & 0x3) << 8);
>> +    }
> 
> This looks to be something that should be controlled by the address space assigned to each cpu.
> 
>   But it's hard to tell.
> 
> Where is the documentation for this?  I didn't immediately find it in 3A5000 Technical Reference Manual, Chapter 10.

Yes, most iocsr instructions introduced on 3A5000 Technical Reference Manual, Chapter 10. 
Table 10-2, 10-3, 10-4, 10-5 and 11-10 lists per core iocsr

> 
>> +void helper_iocsr_write(CPULoongArchState *env, target_ulong w_addr,
>> +                        target_ulong val, uint32_t size)
>> +{
>> +    LoongArchMachineState *lams = LOONGARCH_MACHINE(qdev_get_machine());
>> +    int cpuid = env_cpu(env)->cpu_index;
>> +    int mask, i;
>> +
>> +    /*
>> +     * For IPI send, Mail send, ANY send adjust addr and val
>> +     * according to their real meaning
>> +     */
>> +    if (w_addr == 0x1040) { /* IPI send */
>> +        cpuid = (val >> 16) & 0x3ff;
>> +        val = 1UL << (val & 0x1f);
>> +        w_addr = 0x1008;
> 
> I don't see any interrupts actually being raised?

I define the memory region ops at the IPI interrupt controller, the ipi write here will lead to the ops and raise the 
interrupt at the read/write function. I don't know if this is appropriate, but most of the iocsr read/write are closely
related to interrupt controller.

> 
>> +static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
>> +{
>> +    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
>> +
>> +    gen_helper_check_plv(cpu_env);
> 
> You don't need an external call.  PLV should be part of TB_FLAGS, so you can check this during translation.
> 
>> +    case LOONGARCH_CSR_TVAL:
>> +        gen_helper_csr_rdq(dest, cpu_env, tcg_constant_i64(a->csr));
>> +        break;
>> +    default:
>> +        assert(0);
> 
> The assert was definitely wrong, as it allows incorrect programs to crash qemu.  In addition, unknown csr read as 0.
> 
>> +    CASE_CSR_WRQ(MISC)
> 
> You don't actually support any of the MISC bits yet.
> You should make this register read-only until you do.
> 
> How many more registers are read-only, or have read-only fields that you are not checking?
>
Yes, I will check all of them.

Futher, Can you help review the remaining patches? since the board and machine code needs rewrite. A new version 
is need. The remaining patches are:

 0011-target-loongarch-Add-LoongArch-interrupt-and-excepti.patch
 0012-target-loongarch-Add-timer-related-instructions-supp.patch
 0013-target-loongarch-Add-gdb-support.patch
 0014-target-loongarch-Implement-privilege-instructions-di.patch

 Thank you for all your work.

Xiaojuan
 
> 
> r~



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

* Re: [RFC PATCH v2 09/30] target/loongarch: Add TLB instruction support
  2021-11-17  8:22       ` Richard Henderson
@ 2021-11-17  8:53         ` yangxiaojuan
  0 siblings, 0 replies; 62+ messages in thread
From: yangxiaojuan @ 2021-11-17  8:53 UTC (permalink / raw)
  To: Richard Henderson, qemu-devel; +Cc: Song Gao



On 11/17/2021 04:22 PM, Richard Henderson wrote:
> On 11/17/21 8:29 AM, yangxiaojuan wrote:
>> On 11/12/2021 02:14 AM, Richard Henderson wrote:
>>> On 11/11/21 2:35 AM, Xiaojuan Yang wrote:
>>>> +static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
>>>> +{
>>>> +    gen_helper_check_plv(cpu_env);
>>>> +    gen_helper_tlbwr(cpu_env);
>>>> +    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
>>>> +    ctx->base.is_jmp = DISAS_EXIT;
>>>> +    return true;
>>>> +}
>>>
>>> I think you can skip the EXIT if paging is disabled, which it usually will be in the software tlb handler.  You'd be able to tell with the mmu_idx being the one you use for paging disabled.
>>
>> The paging disabled only enabled at the bios startup, we can get the phys address directly, tlbwr instruction will not be used when paging enabled.
> 
> Paging is also disabled during TLBRENTRY exception (see section 6.2.4 Hardware Exception Handling of TLB Refil Exception).  It is this routine that will usually use tlbwr most often (although the kernel at PRV 0 is not prevented from doing so).

Sorry, I forgot this situation.

> 
>>>> +    default:
>>>> +        do_raise_exception(env, EXCP_INE, GETPC());
>>>
>>> You can detect this during translation, and dispatch to the appropriate invtlb sub-function.
>>>
>> oh, sorry, I don't quiet understand this. detect during the translation sees more complicated.
> 
> It is not more complex at all.  Less complex, I would say.
> 
> static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
> {
>     TCGv rj = gpr_src(ctx, a->rj, EXT_NONE);
>     TCGv rk = gpr_src(ctx, a->rk, EXT_NONE);
> 
>     if (check_plv(ctx)) {
>         return false;
>     }
> 
>     switch (a->invop) {
>     case 0:
>     case 1:
>         gen_helper_invtlb_all(cpu_env);
>         break;
>     case 2:
>         gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(1));
>         break;
>     case 3:
>         gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(0));
>         break;
>     case 4:
>         gen_helper_invtlb_all_asid(cpu_env, rj);
>         break;
>     case 5:
>         gen_helper_invtlb_page_asid(cpu_env, rj, rk);
>         break;
>     case 6:
>         gen_helper_invtlb_page_asid_or_g(cpu_env, rj, rk);
>         break;
>     default:
>         return false;
>     }
>     ctx->base.is_jmp = DISAS_STOP;
>     return true;
> }
> 
Thank you. I get it.
> 
> r~



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

* Re: [RFC PATCH v2 21/30] hw/intc: Add LoongArch extioi interrupt controller(EIOINTC)
  2021-11-11 14:49   ` Mark Cave-Ayland
@ 2021-11-25  8:20     ` yangxiaojuan
  2021-11-26  8:19       ` Mark Cave-Ayland
  0 siblings, 1 reply; 62+ messages in thread
From: yangxiaojuan @ 2021-11-25  8:20 UTC (permalink / raw)
  To: Mark Cave-Ayland, qemu-devel; +Cc: Song Gao

Hi Mark,

On 11/11/2021 10:49 PM, Mark Cave-Ayland wrote:
> On 11/11/2021 01:35, Xiaojuan Yang wrote:
> 
>> This patch realize the EIOINTC interrupt controller.
>>
>> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
>> Signed-off-by: Song Gao <gaosong@loongson.cn>
>> ---
>>   hw/intc/Kconfig                    |   3 +
>>   hw/intc/loongarch_extioi.c         | 570 +++++++++++++++++++++++++++++
>>   hw/intc/meson.build                |   1 +
>>   hw/loongarch/Kconfig               |   1 +
>>   include/hw/intc/loongarch_extioi.h |  99 +++++
>>   include/hw/loongarch/loongarch.h   |   1 +
>>   6 files changed, 675 insertions(+)
>>   create mode 100644 hw/intc/loongarch_extioi.c
>>   create mode 100644 include/hw/intc/loongarch_extioi.h
>>
>> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
>> index c0dc12dfa0..a2d9efd5aa 100644
>> --- a/hw/intc/Kconfig
>> +++ b/hw/intc/Kconfig
>> @@ -82,3 +82,6 @@ config LOONGARCH_PCH_MSI
>>       select MSI_NONBROKEN
>>       bool
>>       select UNIMP
>> +
>> +config LOONGARCH_EXTIOI
>> +    bool
>> diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c
>> new file mode 100644
>> index 0000000000..592cd8d1e2
>> --- /dev/null
>> +++ b/hw/intc/loongarch_extioi.c
>> @@ -0,0 +1,570 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Loongson 3A5000 ext interrupt controller emulation
>> + *
>> + * Copyright (C) 2021 Loongson Technology Corporation Limited
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu/module.h"
>> +#include "qemu/log.h"
>> +#include "hw/irq.h"
>> +#include "hw/sysbus.h"
>> +#include "hw/loongarch/loongarch.h"
>> +#include "hw/qdev-properties.h"
>> +#include "exec/address-spaces.h"
>> +#include "hw/intc/loongarch_extioi.h"
>> +#include "migration/vmstate.h"
>> +
>> +#define DEBUG_APIC 0
>> +
>> +#define DPRINTF(fmt, ...) \
>> +do { \
>> +    if (DEBUG_APIC) { \
>> +        fprintf(stderr, "APIC: " fmt , ## __VA_ARGS__); \
>> +    } \
>> +} while (0)
> 
> Again please use trace-events insead of DPRINTF().
> 
>> +static void extioi_update_irq(void *opaque, int irq_num, int level)
>> +{
>> +    loongarch_extioi *s = opaque;
>> +    uint8_t  ipnum, cpu;
>> +    unsigned long found1, found2;
>> +
>> +    ipnum = s->sw_ipmap[irq_num];
>> +    cpu   = s->sw_coremap[irq_num];
>> +    if (level == 1) {
>> +        if (test_bit(irq_num, (void *)s->en_reg8) == false) {
>> +            return;
>> +        }
>> +        bitmap_set((void *)s->coreisr_reg8[cpu], irq_num, 1);
>> +        found1 = find_next_bit((void *)&(s->sw_ipisr[cpu][ipnum]),
>> +                               EXTIOI_IRQS, 0);
>> +        bitmap_set((void *)&(s->sw_ipisr[cpu][ipnum]), irq_num, 1);
>> +
>> +        if (found1 >= EXTIOI_IRQS) {
>> +            qemu_set_irq(s->parent_irq[cpu][ipnum], level);
>> +        }
>> +    } else {
>> +        bitmap_clear((void *)s->coreisr_reg8[cpu], irq_num, 1);
>> +        found1 = find_next_bit((void *)&(s->sw_ipisr[cpu][ipnum]),
>> +                               EXTIOI_IRQS, 0);
>> +        bitmap_clear((void *)&(s->sw_ipisr[cpu][ipnum]), irq_num, 1);
>> +        found2 = find_next_bit((void *)&(s->sw_ipisr[cpu][ipnum]),
>> +                               EXTIOI_IRQS, 0);
>> +
>> +        if ((found1 < EXTIOI_IRQS) && (found2 >= EXTIOI_IRQS)) {
>> +            qemu_set_irq(s->parent_irq[cpu][ipnum], level);
>> +        }
>> +    }
>> +}
>> +
>> +static void extioi_setirq(void *opaque, int irq, int level)
>> +{
>> +    loongarch_extioi *s = opaque;
>> +    extioi_update_irq(s, irq, level);
>> +}
>> +
>> +static void extioi_handler(void *opaque, int irq, int level)
>> +{
>> +    loongarch_extioi *extioi = (loongarch_extioi *)opaque;
>> +
>> +    qemu_set_irq(extioi->irq[irq], level);
>> +}
>> +
>> +static uint32_t extioi_readb(void *opaque, hwaddr addr)
>> +{
>> +    loongarch_extioi *state = opaque;
> 
> Add a QOM cast here.
> 
>> +    unsigned long offset, reg_count;
>> +    uint8_t ret;
>> +    int cpu;
>> +
>> +    offset = addr & 0xffff;
>> +
>> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
>> +        reg_count = (offset - EXTIOI_ENABLE_START);
>> +        ret = state->en_reg8[reg_count];
>> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
>> +               (offset < EXTIOI_BOUNCE_END)) {
>> +        reg_count = (offset - EXTIOI_BOUNCE_START);
>> +        ret = state->bounce_reg8[reg_count];
>> +    } else if ((offset >= EXTIOI_COREISR_START) &&
>> +               (offset < EXTIOI_COREISR_END)) {
>> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f);
>> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
>> +        ret = state->coreisr_reg8[cpu][reg_count];
>> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
>> +               (offset < EXTIOI_IPMAP_END)) {
>> +        reg_count = (offset - EXTIOI_IPMAP_START);
>> +        ret = state->ipmap_reg8[reg_count];
>> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
>> +               (offset < EXTIOI_COREMAP_END)) {
>> +        reg_count = (offset - EXTIOI_COREMAP_START);
>> +        ret = state->coremap_reg8[reg_count];
>> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
>> +               (offset < EXTIOI_NODETYPE_END)) {
>> +        reg_count = (offset - EXTIOI_NODETYPE_START);
>> +        ret = state->nodetype_reg8[reg_count];
>> +    }
>> +
>> +    DPRINTF("readb reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
>> +    return ret;
>> +}
>> +
>> +static uint32_t extioi_readw(void *opaque, hwaddr addr)
>> +{
>> +    loongarch_extioi *state = opaque;
> 
> And also a QOM cast here.
> 
>> +    unsigned long offset, reg_count;
>> +    uint32_t ret;
>> +    int cpu;
>> +
>> +    offset = addr & 0xffff;
>> +
>> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
>> +        reg_count = (offset - EXTIOI_ENABLE_START) / 4;
>> +        ret = state->en_reg32[reg_count];
>> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
>> +               (offset < EXTIOI_BOUNCE_END)) {
>> +        reg_count = (offset - EXTIOI_BOUNCE_START) / 4;
>> +        ret = state->bounce_reg32[reg_count];
>> +    } else if ((offset >= EXTIOI_COREISR_START) &&
>> +               (offset < EXTIOI_COREISR_END)) {
>> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 4;
>> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
>> +        ret = state->coreisr_reg32[cpu][reg_count];
>> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
>> +               (offset < EXTIOI_IPMAP_END)) {
>> +        reg_count = (offset - EXTIOI_IPMAP_START) / 4;
>> +        ret = state->ipmap_reg32[reg_count];
>> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
>> +               (offset < EXTIOI_COREMAP_END)) {
>> +        reg_count = (offset - EXTIOI_COREMAP_START) / 4;
>> +        ret = state->coremap_reg32[reg_count];
>> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
>> +               (offset < EXTIOI_NODETYPE_END)) {
>> +        reg_count = (offset - EXTIOI_NODETYPE_START) / 4;
>> +        ret = state->nodetype_reg32[reg_count];
>> +    }
>> +
>> +    DPRINTF("readw reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
>> +    return ret;
>> +}
>> +
>> +static uint64_t extioi_readl(void *opaque, hwaddr addr)
>> +{
>> +    loongarch_extioi *state = opaque;
> 
> A QOM cast here too.
> 
>> +    unsigned long offset, reg_count;
>> +    uint64_t ret;
>> +    int cpu;
>> +
>> +    offset = addr & 0xffff;
>> +
>> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
>> +        reg_count = (offset - EXTIOI_ENABLE_START) / 8;
>> +        ret = state->en_reg64[reg_count];
>> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
>> +               (offset < EXTIOI_BOUNCE_END)) {
>> +        reg_count = (offset - EXTIOI_BOUNCE_START) / 8;
>> +        ret = state->bounce_reg64[reg_count];
>> +    } else if ((offset >= EXTIOI_COREISR_START) &&
>> +               (offset < EXTIOI_COREISR_END)) {
>> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 8;
>> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
>> +        ret = state->coreisr_reg64[cpu][reg_count];
>> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
>> +               (offset < EXTIOI_IPMAP_END)) {
>> +        ret = state->ipmap_reg64;
>> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
>> +               (offset < EXTIOI_COREMAP_END)) {
>> +        reg_count = (offset - EXTIOI_COREMAP_START) / 8;
>> +        ret = state->coremap_reg64[reg_count];
>> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
>> +               (offset < EXTIOI_NODETYPE_END)) {
>> +        reg_count = (offset - EXTIOI_NODETYPE_START) / 8;
>> +        ret = state->nodetype_reg64[reg_count];
>> +    }
>> +
>> +    DPRINTF("readl reg 0x" TARGET_FMT_plx " = %lx\n", addr, ret);
>> +    return ret;
>> +}
>> +
>> +static void extioi_writeb(void *opaque, hwaddr addr, uint32_t val)
>> +{
>> +    loongarch_extioi *state = opaque;
> 
> And another cast.
> 
>> +    unsigned long offset, reg_count;
>> +    uint8_t old_data_u8;
>> +    int cpu, i, ipnum, level, mask, irqnum;
>> +
>> +    offset = addr & 0xffff;
>> +    val = val & 0xffUL;
>> +
>> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
>> +        reg_count = (offset - EXTIOI_ENABLE_START);
>> +        old_data_u8 = state->en_reg8[reg_count];
>> +        if (old_data_u8 != val) {
>> +            state->en_reg8[reg_count] = val;
>> +            old_data_u8 = old_data_u8 ^ val;
>> +            mask = 0x1;
>> +
>> +            for (i = 0; i < 8; i++) {
>> +                if (old_data_u8 & mask) {
>> +                    level = !!(val & (0x1 << i));
>> +                    extioi_update_irq(state, i + reg_count * 8, level);
>> +                }
>> +                mask = mask << 1;
>> +            }
>> +        }
>> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
>> +               (offset < EXTIOI_BOUNCE_END)) {
>> +        reg_count = (offset - EXTIOI_BOUNCE_START);
>> +        state->bounce_reg8[reg_count] = val;
>> +    } else if ((offset >= EXTIOI_ISR_START) && (offset < EXTIOI_ISR_END)) {
>> +        /* Can not be writen */
>> +        reg_count = (offset - EXTIOI_ISR_START) & 0x1f;
>> +        old_data_u8 = state->isr_reg8[reg_count];
>> +        state->isr_reg8[reg_count] = old_data_u8 & (~val);
>> +
>> +        mask = 0x1;
>> +        for (i = 0; i < 8; i++) {
>> +            if ((old_data_u8 & mask) && (val & mask)) {
>> +                extioi_update_irq(state, i + reg_count * 8, 0);
>> +            }
>> +            mask = mask << 1;
>> +        }
>> +    } else if ((offset >= EXTIOI_COREISR_START) &&
>> +               (offset < EXTIOI_COREISR_END)) {
>> +        reg_count = (offset - EXTIOI_COREISR_START) & 0x1f;
>> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
>> +
>> +        /* ext_isr */
>> +        old_data_u8 = state->isr_reg8[reg_count];
>> +        state->isr_reg8[reg_count] = old_data_u8 & (~val);
>> +
>> +        old_data_u8 = state->coreisr_reg8[cpu][reg_count];
>> +        state->coreisr_reg8[cpu][reg_count] = old_data_u8 & (~val);
>> +
>> +        if (old_data_u8 != state->coreisr_reg8[cpu][reg_count]) {
>> +            mask = 0x1;
>> +            for (i = 0; i < 8; i++) {
>> +                if ((old_data_u8 & mask) && (val & mask)) {
>> +                    extioi_update_irq(state, i + reg_count * 8, 0);
>> +                }
>> +                mask = mask << 1;
>> +            }
>> +        }
>> +    } else if ((offset >= EXTIOI_IPMAP_START) && (offset < EXTIOI_IPMAP_END)) {
>> +        /* Drop arch.core_ip_mask use state->ipmap */
>> +        reg_count = (offset - EXTIOI_IPMAP_START);
>> +        state->ipmap_reg8[reg_count] = val;
>> +
>> +        ipnum = 0;
>> +        for (i = 0; i < 4; i++) {
>> +            if (val & (0x1 << i)) {
>> +                ipnum = i;
>> +                break;
>> +            }
>> +        }
>> +
>> +        if (val) {
>> +            for (i = 0; i < 32; i++) {
>> +                irqnum = reg_count * 32 + i;
>> +                state->sw_ipmap[irqnum] = ipnum;
>> +            }
>> +        } else {
>> +            for (i = 0; i < 32; i++) {
>> +                irqnum = reg_count * 32 + i;
>> +                state->sw_ipmap[irqnum] = 0;
>> +            }
>> +        }
>> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
>> +               (offset < EXTIOI_COREMAP_END)) {
>> +        reg_count = (offset - EXTIOI_COREMAP_START);
>> +        cpu = val & 0xf;
>> +
>> +        /* Node map different from kernel */
>> +        if (cpu) {
>> +            cpu = ctz32(cpu);
>> +            state->coremap_reg8[reg_count] = val;
>> +            state->sw_coremap[reg_count] = cpu;
>> +        }
>> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
>> +               (offset < EXTIOI_NODETYPE_END)) {
>> +        reg_count = (offset - EXTIOI_NODETYPE_START);
>> +        state->nodetype_reg8[reg_count] = val;
>> +    }
>> +
>> +    DPRINTF("writeb reg 0x" TARGET_FMT_plx " = %x\n", addr, val);
>> +}
>> +
>> +static void extioi_writew(void *opaque, hwaddr addr, uint32_t val)
>> +{
>> +    loongarch_extioi *state = opaque;
> 
> And a QOM cast here.
> 
>> +    int cpu, level;
>> +    uint32_t offset, old_data_u32, reg_count, mask, i;
>> +
>> +    offset = addr & 0xffff;
>> +
>> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
>> +        reg_count = (offset - EXTIOI_ENABLE_START) / 4;
>> +        old_data_u32 = state->en_reg32[reg_count];
>> +        if (old_data_u32 != val) {
>> +            state->en_reg32[reg_count] = val;
>> +            old_data_u32 = old_data_u32 ^ val;
>> +
>> +            mask = 0x1;
>> +            for (i = 0; i < 8 * sizeof(old_data_u32); i++) {
>> +                if (old_data_u32 & mask) {
>> +                    level = !!(val & (0x1 << i));
>> +                    extioi_update_irq(state, i + reg_count * 32, level);
>> +                }
>> +                mask = mask << 1;
>> +            }
>> +        }
>> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
>> +               (offset < EXTIOI_BOUNCE_END)) {
>> +        reg_count = (offset - EXTIOI_BOUNCE_START) / 4;
>> +        state->bounce_reg32[reg_count] = val;
>> +    } else if ((offset >= EXTIOI_COREISR_START) &&
>> +               (offset < EXTIOI_COREISR_END)) {
>> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 4;
>> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
>> +
>> +        /* Ext_isr */
>> +        old_data_u32 = state->isr_reg32[reg_count];
>> +        state->isr_reg32[reg_count] = old_data_u32 & (~val);
>> +
>> +        /* Ext_core_ioisr */
>> +        old_data_u32 = state->coreisr_reg32[cpu][reg_count];
>> +        state->coreisr_reg32[cpu][reg_count] = old_data_u32 & (~val);
>> +
>> +        if (old_data_u32 != state->coreisr_reg32[cpu][reg_count]) {
>> +            mask = 0x1;
>> +            for (i = 0; i < 8 * sizeof(old_data_u32); i++) {
>> +                if ((old_data_u32 & mask) && (val & mask)) {
>> +                    extioi_update_irq(state, i + reg_count * 8, 0);
>> +                }
>> +                mask = mask << 1;
>> +            }
>> +        }
>> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
>> +               (offset < EXTIOI_IPMAP_END)) {
>> +        extioi_writeb(opaque, addr, (val) & 0xff);
>> +        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
>> +        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
>> +        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
>> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
>> +               (offset < EXTIOI_COREMAP_END)) {
>> +        extioi_writeb(opaque, addr, (val) & 0xff);
>> +        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
>> +        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
>> +        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
>> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
>> +               (offset < EXTIOI_NODETYPE_END)) {
>> +        reg_count = (offset - EXTIOI_NODETYPE_START) / 4;
>> +        state->nodetype_reg32[reg_count] = val;
>> +    }
>> +
>> +    DPRINTF("writew reg 0x" TARGET_FMT_plx " = %x\n", addr, val);
>> +}
>> +
>> +static void extioi_writel(void *opaque, hwaddr addr, uint64_t val)
>> +{
>> +    loongarch_extioi *state = (loongarch_extioi *)opaque;
> 
> And a QOM cast here.
> 
>> +    int cpu, level;
>> +    uint64_t offset, old_data_u64, reg_count, mask, i;
>> +
>> +    offset = addr & 0xffff;
>> +
>> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
>> +        reg_count = (offset - EXTIOI_ENABLE_START) / 8;
>> +        old_data_u64 = state->en_reg64[reg_count];
>> +        if (old_data_u64 != val) {
>> +            state->en_reg64[reg_count] = val;
>> +            old_data_u64 = old_data_u64 ^ val;
>> +            mask = 0x1;
>> +            for (i = 0; i < 8 * sizeof(old_data_u64); i++) {
>> +                if (old_data_u64 & mask) {
>> +                    level = !!(val & (0x1 << i));
>> +                    extioi_update_irq(state, i + reg_count * 64, level);
>> +                }
>> +                mask = mask << 1;
>> +            }
>> +        }
>> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
>> +               (offset < EXTIOI_BOUNCE_END)) {
>> +        reg_count = (offset - EXTIOI_BOUNCE_START) / 8;
>> +        state->bounce_reg64[reg_count] = val;
>> +    } else if ((offset >= EXTIOI_COREISR_START) &&
>> +               (offset < EXTIOI_COREISR_END)) {
>> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 8;
>> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
>> +
>> +        /* core_ext_ioisr */
>> +        old_data_u64 = state->coreisr_reg64[cpu][reg_count];
>> +        state->coreisr_reg64[cpu][reg_count] = old_data_u64 & (~val);
>> +
>> +        if (old_data_u64 != state->coreisr_reg64[cpu][reg_count]) {
>> +            mask = 0x1;
>> +            for (i = 0; i < 8 * sizeof(old_data_u64); i++) {
>> +                if ((old_data_u64 & mask) && (val & mask)) {
>> +                    extioi_update_irq(state, i + reg_count * 64, 0);
>> +                }
>> +                mask = mask << 1;
>> +            }
>> +        }
>> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
>> +               (offset < EXTIOI_IPMAP_END)) {
>> +        extioi_writeb(opaque, addr, (val) & 0xff);
>> +        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
>> +        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
>> +        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
>> +        extioi_writeb(opaque, addr + 4, (val >> 32) & 0xff);
>> +        extioi_writeb(opaque, addr + 5, (val >> 40) & 0xff);
>> +        extioi_writeb(opaque, addr + 6, (val >> 48) & 0xff);
>> +        extioi_writeb(opaque, addr + 7, (val >> 56) & 0xff);
>> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
>> +               (offset < EXTIOI_COREMAP_END)) {
>> +        extioi_writeb(opaque, addr, (val) & 0xff);
>> +        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
>> +        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
>> +        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
>> +        extioi_writeb(opaque, addr + 4, (val >> 32) & 0xff);
>> +        extioi_writeb(opaque, addr + 5, (val >> 40) & 0xff);
>> +        extioi_writeb(opaque, addr + 6, (val >> 48) & 0xff);
>> +        extioi_writeb(opaque, addr + 7, (val >> 56) & 0xff);
>> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
>> +               (offset < EXTIOI_NODETYPE_END)) {
>> +        reg_count = (offset - EXTIOI_NODETYPE_START) / 8;
>> +        state->nodetype_reg64[reg_count] = val;
>> +    }
>> +
>> +    DPRINTF("writel reg 0x" TARGET_FMT_plx " = %lx\n", addr, val);
>> +}
>> +
>> +static uint64_t extioi_readfn(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    switch (size) {
>> +    case 1:
>> +        return extioi_readb(opaque, addr);
>> +    case 4:
>> +        return extioi_readw(opaque, addr);
>> +    case 8:
>> +        return extioi_readl(opaque, addr);
>> +    default:
>> +        g_assert_not_reached();
>> +    }
>> +}
>> +
>> +static void extioi_writefn(void *opaque, hwaddr addr,
>> +                           uint64_t value, unsigned size)
>> +{
>> +    switch (size) {
>> +    case 1:
>> +        extioi_writeb(opaque, addr, value);
>> +        break;
>> +    case 4:
>> +        extioi_writew(opaque, addr, value);
>> +        break;
>> +    case 8:
>> +        extioi_writel(opaque, addr, value);
>> +        break;
>> +    default:
>> +        g_assert_not_reached();
>> +    }
>> +}
> 
> Ooof. This pattern of using separate functions for different size accesses generally hasn't been needed since the memory API was introduced about 10 years ago. I suspect using a single function each for read and write would help simplify things enormously.
> 

 Thank you for your advice, Most of them have been modified. But for different size access I haven't thought of a good way yet. 
 Can we just define the readb/writeb and use Multiple readb/writeb or use if (size == 1/4/8) to handle separately in a big read/write func?

Thanks,
Xiaojuan



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

* Re: [RFC PATCH v2 21/30] hw/intc: Add LoongArch extioi interrupt controller(EIOINTC)
  2021-11-25  8:20     ` yangxiaojuan
@ 2021-11-26  8:19       ` Mark Cave-Ayland
  0 siblings, 0 replies; 62+ messages in thread
From: Mark Cave-Ayland @ 2021-11-26  8:19 UTC (permalink / raw)
  To: yangxiaojuan, qemu-devel; +Cc: Song Gao

On 25/11/2021 08:20, yangxiaojuan wrote:

> Hi Mark,
> 
> On 11/11/2021 10:49 PM, Mark Cave-Ayland wrote:
>> On 11/11/2021 01:35, Xiaojuan Yang wrote:
>>
>>> This patch realize the EIOINTC interrupt controller.
>>>
>>> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
>>> Signed-off-by: Song Gao <gaosong@loongson.cn>
>>> ---
>>>    hw/intc/Kconfig                    |   3 +
>>>    hw/intc/loongarch_extioi.c         | 570 +++++++++++++++++++++++++++++
>>>    hw/intc/meson.build                |   1 +
>>>    hw/loongarch/Kconfig               |   1 +
>>>    include/hw/intc/loongarch_extioi.h |  99 +++++
>>>    include/hw/loongarch/loongarch.h   |   1 +
>>>    6 files changed, 675 insertions(+)
>>>    create mode 100644 hw/intc/loongarch_extioi.c
>>>    create mode 100644 include/hw/intc/loongarch_extioi.h
>>>
>>> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
>>> index c0dc12dfa0..a2d9efd5aa 100644
>>> --- a/hw/intc/Kconfig
>>> +++ b/hw/intc/Kconfig
>>> @@ -82,3 +82,6 @@ config LOONGARCH_PCH_MSI
>>>        select MSI_NONBROKEN
>>>        bool
>>>        select UNIMP
>>> +
>>> +config LOONGARCH_EXTIOI
>>> +    bool
>>> diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c
>>> new file mode 100644
>>> index 0000000000..592cd8d1e2
>>> --- /dev/null
>>> +++ b/hw/intc/loongarch_extioi.c
>>> @@ -0,0 +1,570 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>> +/*
>>> + * Loongson 3A5000 ext interrupt controller emulation
>>> + *
>>> + * Copyright (C) 2021 Loongson Technology Corporation Limited
>>> + */
>>> +
>>> +#include "qemu/osdep.h"
>>> +#include "qemu/module.h"
>>> +#include "qemu/log.h"
>>> +#include "hw/irq.h"
>>> +#include "hw/sysbus.h"
>>> +#include "hw/loongarch/loongarch.h"
>>> +#include "hw/qdev-properties.h"
>>> +#include "exec/address-spaces.h"
>>> +#include "hw/intc/loongarch_extioi.h"
>>> +#include "migration/vmstate.h"
>>> +
>>> +#define DEBUG_APIC 0
>>> +
>>> +#define DPRINTF(fmt, ...) \
>>> +do { \
>>> +    if (DEBUG_APIC) { \
>>> +        fprintf(stderr, "APIC: " fmt , ## __VA_ARGS__); \
>>> +    } \
>>> +} while (0)
>>
>> Again please use trace-events insead of DPRINTF().
>>
>>> +static void extioi_update_irq(void *opaque, int irq_num, int level)
>>> +{
>>> +    loongarch_extioi *s = opaque;
>>> +    uint8_t  ipnum, cpu;
>>> +    unsigned long found1, found2;
>>> +
>>> +    ipnum = s->sw_ipmap[irq_num];
>>> +    cpu   = s->sw_coremap[irq_num];
>>> +    if (level == 1) {
>>> +        if (test_bit(irq_num, (void *)s->en_reg8) == false) {
>>> +            return;
>>> +        }
>>> +        bitmap_set((void *)s->coreisr_reg8[cpu], irq_num, 1);
>>> +        found1 = find_next_bit((void *)&(s->sw_ipisr[cpu][ipnum]),
>>> +                               EXTIOI_IRQS, 0);
>>> +        bitmap_set((void *)&(s->sw_ipisr[cpu][ipnum]), irq_num, 1);
>>> +
>>> +        if (found1 >= EXTIOI_IRQS) {
>>> +            qemu_set_irq(s->parent_irq[cpu][ipnum], level);
>>> +        }
>>> +    } else {
>>> +        bitmap_clear((void *)s->coreisr_reg8[cpu], irq_num, 1);
>>> +        found1 = find_next_bit((void *)&(s->sw_ipisr[cpu][ipnum]),
>>> +                               EXTIOI_IRQS, 0);
>>> +        bitmap_clear((void *)&(s->sw_ipisr[cpu][ipnum]), irq_num, 1);
>>> +        found2 = find_next_bit((void *)&(s->sw_ipisr[cpu][ipnum]),
>>> +                               EXTIOI_IRQS, 0);
>>> +
>>> +        if ((found1 < EXTIOI_IRQS) && (found2 >= EXTIOI_IRQS)) {
>>> +            qemu_set_irq(s->parent_irq[cpu][ipnum], level);
>>> +        }
>>> +    }
>>> +}
>>> +
>>> +static void extioi_setirq(void *opaque, int irq, int level)
>>> +{
>>> +    loongarch_extioi *s = opaque;
>>> +    extioi_update_irq(s, irq, level);
>>> +}
>>> +
>>> +static void extioi_handler(void *opaque, int irq, int level)
>>> +{
>>> +    loongarch_extioi *extioi = (loongarch_extioi *)opaque;
>>> +
>>> +    qemu_set_irq(extioi->irq[irq], level);
>>> +}
>>> +
>>> +static uint32_t extioi_readb(void *opaque, hwaddr addr)
>>> +{
>>> +    loongarch_extioi *state = opaque;
>>
>> Add a QOM cast here.
>>
>>> +    unsigned long offset, reg_count;
>>> +    uint8_t ret;
>>> +    int cpu;
>>> +
>>> +    offset = addr & 0xffff;
>>> +
>>> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
>>> +        reg_count = (offset - EXTIOI_ENABLE_START);
>>> +        ret = state->en_reg8[reg_count];
>>> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
>>> +               (offset < EXTIOI_BOUNCE_END)) {
>>> +        reg_count = (offset - EXTIOI_BOUNCE_START);
>>> +        ret = state->bounce_reg8[reg_count];
>>> +    } else if ((offset >= EXTIOI_COREISR_START) &&
>>> +               (offset < EXTIOI_COREISR_END)) {
>>> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f);
>>> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
>>> +        ret = state->coreisr_reg8[cpu][reg_count];
>>> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
>>> +               (offset < EXTIOI_IPMAP_END)) {
>>> +        reg_count = (offset - EXTIOI_IPMAP_START);
>>> +        ret = state->ipmap_reg8[reg_count];
>>> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
>>> +               (offset < EXTIOI_COREMAP_END)) {
>>> +        reg_count = (offset - EXTIOI_COREMAP_START);
>>> +        ret = state->coremap_reg8[reg_count];
>>> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
>>> +               (offset < EXTIOI_NODETYPE_END)) {
>>> +        reg_count = (offset - EXTIOI_NODETYPE_START);
>>> +        ret = state->nodetype_reg8[reg_count];
>>> +    }
>>> +
>>> +    DPRINTF("readb reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
>>> +    return ret;
>>> +}
>>> +
>>> +static uint32_t extioi_readw(void *opaque, hwaddr addr)
>>> +{
>>> +    loongarch_extioi *state = opaque;
>>
>> And also a QOM cast here.
>>
>>> +    unsigned long offset, reg_count;
>>> +    uint32_t ret;
>>> +    int cpu;
>>> +
>>> +    offset = addr & 0xffff;
>>> +
>>> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
>>> +        reg_count = (offset - EXTIOI_ENABLE_START) / 4;
>>> +        ret = state->en_reg32[reg_count];
>>> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
>>> +               (offset < EXTIOI_BOUNCE_END)) {
>>> +        reg_count = (offset - EXTIOI_BOUNCE_START) / 4;
>>> +        ret = state->bounce_reg32[reg_count];
>>> +    } else if ((offset >= EXTIOI_COREISR_START) &&
>>> +               (offset < EXTIOI_COREISR_END)) {
>>> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 4;
>>> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
>>> +        ret = state->coreisr_reg32[cpu][reg_count];
>>> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
>>> +               (offset < EXTIOI_IPMAP_END)) {
>>> +        reg_count = (offset - EXTIOI_IPMAP_START) / 4;
>>> +        ret = state->ipmap_reg32[reg_count];
>>> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
>>> +               (offset < EXTIOI_COREMAP_END)) {
>>> +        reg_count = (offset - EXTIOI_COREMAP_START) / 4;
>>> +        ret = state->coremap_reg32[reg_count];
>>> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
>>> +               (offset < EXTIOI_NODETYPE_END)) {
>>> +        reg_count = (offset - EXTIOI_NODETYPE_START) / 4;
>>> +        ret = state->nodetype_reg32[reg_count];
>>> +    }
>>> +
>>> +    DPRINTF("readw reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
>>> +    return ret;
>>> +}
>>> +
>>> +static uint64_t extioi_readl(void *opaque, hwaddr addr)
>>> +{
>>> +    loongarch_extioi *state = opaque;
>>
>> A QOM cast here too.
>>
>>> +    unsigned long offset, reg_count;
>>> +    uint64_t ret;
>>> +    int cpu;
>>> +
>>> +    offset = addr & 0xffff;
>>> +
>>> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
>>> +        reg_count = (offset - EXTIOI_ENABLE_START) / 8;
>>> +        ret = state->en_reg64[reg_count];
>>> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
>>> +               (offset < EXTIOI_BOUNCE_END)) {
>>> +        reg_count = (offset - EXTIOI_BOUNCE_START) / 8;
>>> +        ret = state->bounce_reg64[reg_count];
>>> +    } else if ((offset >= EXTIOI_COREISR_START) &&
>>> +               (offset < EXTIOI_COREISR_END)) {
>>> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 8;
>>> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
>>> +        ret = state->coreisr_reg64[cpu][reg_count];
>>> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
>>> +               (offset < EXTIOI_IPMAP_END)) {
>>> +        ret = state->ipmap_reg64;
>>> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
>>> +               (offset < EXTIOI_COREMAP_END)) {
>>> +        reg_count = (offset - EXTIOI_COREMAP_START) / 8;
>>> +        ret = state->coremap_reg64[reg_count];
>>> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
>>> +               (offset < EXTIOI_NODETYPE_END)) {
>>> +        reg_count = (offset - EXTIOI_NODETYPE_START) / 8;
>>> +        ret = state->nodetype_reg64[reg_count];
>>> +    }
>>> +
>>> +    DPRINTF("readl reg 0x" TARGET_FMT_plx " = %lx\n", addr, ret);
>>> +    return ret;
>>> +}
>>> +
>>> +static void extioi_writeb(void *opaque, hwaddr addr, uint32_t val)
>>> +{
>>> +    loongarch_extioi *state = opaque;
>>
>> And another cast.
>>
>>> +    unsigned long offset, reg_count;
>>> +    uint8_t old_data_u8;
>>> +    int cpu, i, ipnum, level, mask, irqnum;
>>> +
>>> +    offset = addr & 0xffff;
>>> +    val = val & 0xffUL;
>>> +
>>> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
>>> +        reg_count = (offset - EXTIOI_ENABLE_START);
>>> +        old_data_u8 = state->en_reg8[reg_count];
>>> +        if (old_data_u8 != val) {
>>> +            state->en_reg8[reg_count] = val;
>>> +            old_data_u8 = old_data_u8 ^ val;
>>> +            mask = 0x1;
>>> +
>>> +            for (i = 0; i < 8; i++) {
>>> +                if (old_data_u8 & mask) {
>>> +                    level = !!(val & (0x1 << i));
>>> +                    extioi_update_irq(state, i + reg_count * 8, level);
>>> +                }
>>> +                mask = mask << 1;
>>> +            }
>>> +        }
>>> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
>>> +               (offset < EXTIOI_BOUNCE_END)) {
>>> +        reg_count = (offset - EXTIOI_BOUNCE_START);
>>> +        state->bounce_reg8[reg_count] = val;
>>> +    } else if ((offset >= EXTIOI_ISR_START) && (offset < EXTIOI_ISR_END)) {
>>> +        /* Can not be writen */
>>> +        reg_count = (offset - EXTIOI_ISR_START) & 0x1f;
>>> +        old_data_u8 = state->isr_reg8[reg_count];
>>> +        state->isr_reg8[reg_count] = old_data_u8 & (~val);
>>> +
>>> +        mask = 0x1;
>>> +        for (i = 0; i < 8; i++) {
>>> +            if ((old_data_u8 & mask) && (val & mask)) {
>>> +                extioi_update_irq(state, i + reg_count * 8, 0);
>>> +            }
>>> +            mask = mask << 1;
>>> +        }
>>> +    } else if ((offset >= EXTIOI_COREISR_START) &&
>>> +               (offset < EXTIOI_COREISR_END)) {
>>> +        reg_count = (offset - EXTIOI_COREISR_START) & 0x1f;
>>> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
>>> +
>>> +        /* ext_isr */
>>> +        old_data_u8 = state->isr_reg8[reg_count];
>>> +        state->isr_reg8[reg_count] = old_data_u8 & (~val);
>>> +
>>> +        old_data_u8 = state->coreisr_reg8[cpu][reg_count];
>>> +        state->coreisr_reg8[cpu][reg_count] = old_data_u8 & (~val);
>>> +
>>> +        if (old_data_u8 != state->coreisr_reg8[cpu][reg_count]) {
>>> +            mask = 0x1;
>>> +            for (i = 0; i < 8; i++) {
>>> +                if ((old_data_u8 & mask) && (val & mask)) {
>>> +                    extioi_update_irq(state, i + reg_count * 8, 0);
>>> +                }
>>> +                mask = mask << 1;
>>> +            }
>>> +        }
>>> +    } else if ((offset >= EXTIOI_IPMAP_START) && (offset < EXTIOI_IPMAP_END)) {
>>> +        /* Drop arch.core_ip_mask use state->ipmap */
>>> +        reg_count = (offset - EXTIOI_IPMAP_START);
>>> +        state->ipmap_reg8[reg_count] = val;
>>> +
>>> +        ipnum = 0;
>>> +        for (i = 0; i < 4; i++) {
>>> +            if (val & (0x1 << i)) {
>>> +                ipnum = i;
>>> +                break;
>>> +            }
>>> +        }
>>> +
>>> +        if (val) {
>>> +            for (i = 0; i < 32; i++) {
>>> +                irqnum = reg_count * 32 + i;
>>> +                state->sw_ipmap[irqnum] = ipnum;
>>> +            }
>>> +        } else {
>>> +            for (i = 0; i < 32; i++) {
>>> +                irqnum = reg_count * 32 + i;
>>> +                state->sw_ipmap[irqnum] = 0;
>>> +            }
>>> +        }
>>> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
>>> +               (offset < EXTIOI_COREMAP_END)) {
>>> +        reg_count = (offset - EXTIOI_COREMAP_START);
>>> +        cpu = val & 0xf;
>>> +
>>> +        /* Node map different from kernel */
>>> +        if (cpu) {
>>> +            cpu = ctz32(cpu);
>>> +            state->coremap_reg8[reg_count] = val;
>>> +            state->sw_coremap[reg_count] = cpu;
>>> +        }
>>> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
>>> +               (offset < EXTIOI_NODETYPE_END)) {
>>> +        reg_count = (offset - EXTIOI_NODETYPE_START);
>>> +        state->nodetype_reg8[reg_count] = val;
>>> +    }
>>> +
>>> +    DPRINTF("writeb reg 0x" TARGET_FMT_plx " = %x\n", addr, val);
>>> +}
>>> +
>>> +static void extioi_writew(void *opaque, hwaddr addr, uint32_t val)
>>> +{
>>> +    loongarch_extioi *state = opaque;
>>
>> And a QOM cast here.
>>
>>> +    int cpu, level;
>>> +    uint32_t offset, old_data_u32, reg_count, mask, i;
>>> +
>>> +    offset = addr & 0xffff;
>>> +
>>> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
>>> +        reg_count = (offset - EXTIOI_ENABLE_START) / 4;
>>> +        old_data_u32 = state->en_reg32[reg_count];
>>> +        if (old_data_u32 != val) {
>>> +            state->en_reg32[reg_count] = val;
>>> +            old_data_u32 = old_data_u32 ^ val;
>>> +
>>> +            mask = 0x1;
>>> +            for (i = 0; i < 8 * sizeof(old_data_u32); i++) {
>>> +                if (old_data_u32 & mask) {
>>> +                    level = !!(val & (0x1 << i));
>>> +                    extioi_update_irq(state, i + reg_count * 32, level);
>>> +                }
>>> +                mask = mask << 1;
>>> +            }
>>> +        }
>>> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
>>> +               (offset < EXTIOI_BOUNCE_END)) {
>>> +        reg_count = (offset - EXTIOI_BOUNCE_START) / 4;
>>> +        state->bounce_reg32[reg_count] = val;
>>> +    } else if ((offset >= EXTIOI_COREISR_START) &&
>>> +               (offset < EXTIOI_COREISR_END)) {
>>> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 4;
>>> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
>>> +
>>> +        /* Ext_isr */
>>> +        old_data_u32 = state->isr_reg32[reg_count];
>>> +        state->isr_reg32[reg_count] = old_data_u32 & (~val);
>>> +
>>> +        /* Ext_core_ioisr */
>>> +        old_data_u32 = state->coreisr_reg32[cpu][reg_count];
>>> +        state->coreisr_reg32[cpu][reg_count] = old_data_u32 & (~val);
>>> +
>>> +        if (old_data_u32 != state->coreisr_reg32[cpu][reg_count]) {
>>> +            mask = 0x1;
>>> +            for (i = 0; i < 8 * sizeof(old_data_u32); i++) {
>>> +                if ((old_data_u32 & mask) && (val & mask)) {
>>> +                    extioi_update_irq(state, i + reg_count * 8, 0);
>>> +                }
>>> +                mask = mask << 1;
>>> +            }
>>> +        }
>>> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
>>> +               (offset < EXTIOI_IPMAP_END)) {
>>> +        extioi_writeb(opaque, addr, (val) & 0xff);
>>> +        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
>>> +        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
>>> +        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
>>> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
>>> +               (offset < EXTIOI_COREMAP_END)) {
>>> +        extioi_writeb(opaque, addr, (val) & 0xff);
>>> +        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
>>> +        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
>>> +        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
>>> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
>>> +               (offset < EXTIOI_NODETYPE_END)) {
>>> +        reg_count = (offset - EXTIOI_NODETYPE_START) / 4;
>>> +        state->nodetype_reg32[reg_count] = val;
>>> +    }
>>> +
>>> +    DPRINTF("writew reg 0x" TARGET_FMT_plx " = %x\n", addr, val);
>>> +}
>>> +
>>> +static void extioi_writel(void *opaque, hwaddr addr, uint64_t val)
>>> +{
>>> +    loongarch_extioi *state = (loongarch_extioi *)opaque;
>>
>> And a QOM cast here.
>>
>>> +    int cpu, level;
>>> +    uint64_t offset, old_data_u64, reg_count, mask, i;
>>> +
>>> +    offset = addr & 0xffff;
>>> +
>>> +    if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) {
>>> +        reg_count = (offset - EXTIOI_ENABLE_START) / 8;
>>> +        old_data_u64 = state->en_reg64[reg_count];
>>> +        if (old_data_u64 != val) {
>>> +            state->en_reg64[reg_count] = val;
>>> +            old_data_u64 = old_data_u64 ^ val;
>>> +            mask = 0x1;
>>> +            for (i = 0; i < 8 * sizeof(old_data_u64); i++) {
>>> +                if (old_data_u64 & mask) {
>>> +                    level = !!(val & (0x1 << i));
>>> +                    extioi_update_irq(state, i + reg_count * 64, level);
>>> +                }
>>> +                mask = mask << 1;
>>> +            }
>>> +        }
>>> +    } else if ((offset >= EXTIOI_BOUNCE_START) &&
>>> +               (offset < EXTIOI_BOUNCE_END)) {
>>> +        reg_count = (offset - EXTIOI_BOUNCE_START) / 8;
>>> +        state->bounce_reg64[reg_count] = val;
>>> +    } else if ((offset >= EXTIOI_COREISR_START) &&
>>> +               (offset < EXTIOI_COREISR_END)) {
>>> +        reg_count = ((offset - EXTIOI_COREISR_START) & 0x1f) / 8;
>>> +        cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
>>> +
>>> +        /* core_ext_ioisr */
>>> +        old_data_u64 = state->coreisr_reg64[cpu][reg_count];
>>> +        state->coreisr_reg64[cpu][reg_count] = old_data_u64 & (~val);
>>> +
>>> +        if (old_data_u64 != state->coreisr_reg64[cpu][reg_count]) {
>>> +            mask = 0x1;
>>> +            for (i = 0; i < 8 * sizeof(old_data_u64); i++) {
>>> +                if ((old_data_u64 & mask) && (val & mask)) {
>>> +                    extioi_update_irq(state, i + reg_count * 64, 0);
>>> +                }
>>> +                mask = mask << 1;
>>> +            }
>>> +        }
>>> +    } else if ((offset >= EXTIOI_IPMAP_START) &&
>>> +               (offset < EXTIOI_IPMAP_END)) {
>>> +        extioi_writeb(opaque, addr, (val) & 0xff);
>>> +        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
>>> +        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
>>> +        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
>>> +        extioi_writeb(opaque, addr + 4, (val >> 32) & 0xff);
>>> +        extioi_writeb(opaque, addr + 5, (val >> 40) & 0xff);
>>> +        extioi_writeb(opaque, addr + 6, (val >> 48) & 0xff);
>>> +        extioi_writeb(opaque, addr + 7, (val >> 56) & 0xff);
>>> +    } else if ((offset >= EXTIOI_COREMAP_START) &&
>>> +               (offset < EXTIOI_COREMAP_END)) {
>>> +        extioi_writeb(opaque, addr, (val) & 0xff);
>>> +        extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
>>> +        extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
>>> +        extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
>>> +        extioi_writeb(opaque, addr + 4, (val >> 32) & 0xff);
>>> +        extioi_writeb(opaque, addr + 5, (val >> 40) & 0xff);
>>> +        extioi_writeb(opaque, addr + 6, (val >> 48) & 0xff);
>>> +        extioi_writeb(opaque, addr + 7, (val >> 56) & 0xff);
>>> +    } else if ((offset >= EXTIOI_NODETYPE_START) &&
>>> +               (offset < EXTIOI_NODETYPE_END)) {
>>> +        reg_count = (offset - EXTIOI_NODETYPE_START) / 8;
>>> +        state->nodetype_reg64[reg_count] = val;
>>> +    }
>>> +
>>> +    DPRINTF("writel reg 0x" TARGET_FMT_plx " = %lx\n", addr, val);
>>> +}
>>> +
>>> +static uint64_t extioi_readfn(void *opaque, hwaddr addr, unsigned size)
>>> +{
>>> +    switch (size) {
>>> +    case 1:
>>> +        return extioi_readb(opaque, addr);
>>> +    case 4:
>>> +        return extioi_readw(opaque, addr);
>>> +    case 8:
>>> +        return extioi_readl(opaque, addr);
>>> +    default:
>>> +        g_assert_not_reached();
>>> +    }
>>> +}
>>> +
>>> +static void extioi_writefn(void *opaque, hwaddr addr,
>>> +                           uint64_t value, unsigned size)
>>> +{
>>> +    switch (size) {
>>> +    case 1:
>>> +        extioi_writeb(opaque, addr, value);
>>> +        break;
>>> +    case 4:
>>> +        extioi_writew(opaque, addr, value);
>>> +        break;
>>> +    case 8:
>>> +        extioi_writel(opaque, addr, value);
>>> +        break;
>>> +    default:
>>> +        g_assert_not_reached();
>>> +    }
>>> +}
>>
>> Ooof. This pattern of using separate functions for different size accesses generally hasn't been needed since the memory API was introduced about 10 years ago. I suspect using a single function each for read and write would help simplify things enormously.
>>
> 
>   Thank you for your advice, Most of them have been modified. But for different size access I haven't thought of a good way yet.
>   Can we just define the readb/writeb and use Multiple readb/writeb or use if (size == 1/4/8) to handle separately in a big read/write func?

In my experience you would generally only need this for old hardware which tends to 
have different behaviour for different access sizes: I'm not familiar with this 
particular device but modern hardware tends to behave more sensibly here.

The parts of your code that makes me think this isn't the correct approach are the 
sections like this in extioi_writel():

     } else if ((offset >= EXTIOI_IPMAP_START) &&
                (offset < EXTIOI_IPMAP_END)) {
         extioi_writeb(opaque, addr, (val) & 0xff);
         extioi_writeb(opaque, addr + 1, (val >> 8) & 0xff);
         extioi_writeb(opaque, addr + 2, (val >> 16) & 0xff);
         extioi_writeb(opaque, addr + 3, (val >> 24) & 0xff);
         extioi_writeb(opaque, addr + 4, (val >> 32) & 0xff);
         extioi_writeb(opaque, addr + 5, (val >> 40) & 0xff);
         extioi_writeb(opaque, addr + 6, (val >> 48) & 0xff);
         extioi_writeb(opaque, addr + 7, (val >> 56) & 0xff);
     }

On more modern hardware you can generally get away with tweaking extioi_ops:

     static const MemoryRegionOps extioi_ops = {
         .read = extioi_readfn,
         .write = extioi_writefn,
         .impl.min_access_size = 1,
         .impl.max_access_size = 8,
         .valid.min_access_size = 1,
         .valid.max_access_size = 8,
         .endianness = DEVICE_NATIVE_ENDIAN,
     };

If you set .impl.max_access_size to 1 then the QEMU memory subsystem will split 2/4/8 
byte access into multiple 1 byte accesses automatically for you. It feels like 
accesses between EXTIOI_IPMAP_START/EXTIOI_IPMAP_END and 
EXTIOI_COREMAP_START/EXTIOI_COREMAP_END could be better handled this way using 2 
separate MemoryRegionOps mapped at the relevant offset within the same device.


ATB,

Mark.


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

end of thread, other threads:[~2021-11-26  8:20 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-11  1:34 [RFC PATCH v2 00/30] Add Loongarch softmmu support Xiaojuan Yang
2021-11-11  1:34 ` [RFC PATCH v2 01/30] target/loongarch: Update README Xiaojuan Yang
2021-11-11 11:50   ` chen huacai
2021-11-15  3:34     ` yangxiaojuan
2021-11-11  1:35 ` [RFC PATCH v2 02/30] target/loongarch: Add CSR registers definition Xiaojuan Yang
2021-11-11 13:29   ` Richard Henderson
2021-11-12  2:14     ` yangxiaojuan
2021-11-12  7:14       ` Richard Henderson
2021-11-11 13:33   ` Richard Henderson
2021-11-11  1:35 ` [RFC PATCH v2 03/30] target/loongarch: Add basic vmstate description of CPU Xiaojuan Yang
2021-11-11 13:30   ` Richard Henderson
2021-11-11  1:35 ` [RFC PATCH v2 04/30] target/loongarch: Define exceptions for LoongArch Xiaojuan Yang
2021-11-11 13:36   ` Richard Henderson
2021-11-12  2:24     ` yangxiaojuan
2021-11-11  1:35 ` [RFC PATCH v2 05/30] target/loongarch: Implement qmp_query_cpu_definitions() Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 06/30] target/loongarch: Add stabletimer support Xiaojuan Yang
2021-11-11 14:34   ` Richard Henderson
2021-11-11  1:35 ` [RFC PATCH v2 07/30] target/loongarch: Add MMU support for LoongArch CPU Xiaojuan Yang
2021-11-11 15:53   ` Richard Henderson
2021-11-17  6:37     ` yangxiaojuan
2021-11-17  6:59       ` Richard Henderson
2021-11-11  1:35 ` [RFC PATCH v2 08/30] target/loongarch: Add LoongArch CSR/IOCSR instruction Xiaojuan Yang
2021-11-11 17:43   ` Richard Henderson
2021-11-17  8:48     ` yangxiaojuan
2021-11-11  1:35 ` [RFC PATCH v2 09/30] target/loongarch: Add TLB instruction support Xiaojuan Yang
2021-11-11 18:14   ` Richard Henderson
2021-11-17  7:29     ` yangxiaojuan
2021-11-17  8:22       ` Richard Henderson
2021-11-17  8:53         ` yangxiaojuan
2021-11-11  1:35 ` [RFC PATCH v2 10/30] target/loongarch: Add other core instructions support Xiaojuan Yang
2021-11-14 10:19   ` Richard Henderson
2021-11-11  1:35 ` [RFC PATCH v2 11/30] target/loongarch: Add LoongArch interrupt and exception handle Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 12/30] target/loongarch: Add timer related instructions support Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 13/30] target/loongarch: Add gdb support Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 14/30] target/loongarch: Implement privilege instructions disassembly Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 15/30] hw/pci-host: Add ls7a1000 PCIe Host bridge support for Loongson Platform Xiaojuan Yang
2021-11-11 13:17   ` Mark Cave-Ayland
2021-11-11  1:35 ` [RFC PATCH v2 16/30] hw/loongarch: Add a virt LoongArch 3A5000 board support Xiaojuan Yang
2021-11-11 14:17   ` Mark Cave-Ayland
2021-11-11  1:35 ` [RFC PATCH v2 17/30] hw/loongarch: Add LoongArch cpu interrupt support(CPUINTC) Xiaojuan Yang
2021-11-11 14:22   ` Mark Cave-Ayland
2021-11-11  1:35 ` [RFC PATCH v2 18/30] hw/loongarch: Add LoongArch ipi interrupt support(IPI) Xiaojuan Yang
2021-11-11 14:28   ` Mark Cave-Ayland
2021-11-11  1:35 ` [RFC PATCH v2 19/30] hw/intc: Add LoongArch ls7a interrupt controller support(PCH-PIC) Xiaojuan Yang
2021-11-11 14:37   ` Mark Cave-Ayland
2021-11-11  1:35 ` [RFC PATCH v2 20/30] hw/intc: Add LoongArch ls7a msi interrupt controller support(PCH-MSI) Xiaojuan Yang
2021-11-11 14:40   ` Mark Cave-Ayland
2021-11-11  1:35 ` [RFC PATCH v2 21/30] hw/intc: Add LoongArch extioi interrupt controller(EIOINTC) Xiaojuan Yang
2021-11-11 14:49   ` Mark Cave-Ayland
2021-11-25  8:20     ` yangxiaojuan
2021-11-26  8:19       ` Mark Cave-Ayland
2021-11-11  1:35 ` [RFC PATCH v2 22/30] hw/loongarch: Add irq hierarchy for the system Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 23/30] hw/loongarch: Add some devices support for 3A5000 Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 24/30] hw/loongarch: Add LoongArch ls7a rtc device support Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 25/30] hw/loongarch: Add default bios startup support Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 26/30] hw/loongarch: Add -kernel and -initrd options support Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 27/30] hw/loongarch: Add LoongArch smbios support Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 28/30] hw/loongarch: Add LoongArch acpi support Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 29/30] hw/loongarch: Add machine->possible_cpus Xiaojuan Yang
2021-11-11  1:35 ` [RFC PATCH v2 30/30] hw/loongarch: Add Numa support Xiaojuan Yang
2021-11-11 14:58 ` [RFC PATCH v2 00/30] Add Loongarch softmmu support Mark Cave-Ayland
2021-11-12  1:26   ` yangxiaojuan

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